文章目录
一、前言
云计算发展迅速,移动云作为业界领先的云服务商,一直致力于推动云计算技术的创新应用,赋能各领域的开发者,构建蓬勃发展的云生态。“移动云”是基于5g技术,由中国移动打造的品牌,针对云计算、大数据、人工智能
等产品的服务,为客户提供专业的解决方案。
移动云的产品特点如下:
云计算服务:
基于互联网和虚拟化技术,将计算、存储、网络等资源以服务的形式提供给用户。用户可以根据需要获取和使用这些资源,而无需购买和维护硬件设备。高效性:
通过云计算技术实现资源的动态分配和弹性扩展,可以根据用户的实际需求进行资源的快速调整。这有助于提高资源的利用率和效率。安全性:
注重用户数据的安全性和隐私保护,采用多种安全措施和技术手段来保障用户数据的安全性。灵活性和可扩展性:
可以根据用户需求灵活地进行扩展和缩减,使企业能够根据实际情况调整资源规模。成本效益:
采用按需付费的模式,可以降低企业的IT成本。同时,移动云服务还能帮助企业节约硬件和软件的成本,提高资源利用率。高可用性和灾备性:
通常具有多地域部署和备份功能,能够提供高可用性和灾备性,确保服务持续运行。跨平台兼容性:
可以在各种不同的移动设备和操作系统上使用。
移动云云原生产品非常广泛,不仅包括云原生计算、应用管理、安全等产品线,还囊括了人工智能产品线、数据库以及大数据等产品线,为企业和个人提供了极大的便利。
而我作为一名人工智能领域的开发者,那么移动云的人工智能产品线则是我最常使用的,它所带的九天人工智能平台
以及各种OCR识别、语音识别和合成
的API也在我的工作中起到了很重要的作用。
二、九天人工智能平台介绍
说起九天人工智能平台,我依稀记得去年移动云举办的第二届移动云杯比赛,由于去年开设了许多的人工智能赛题,所以我也就欣然而往,报名参加了人工智能相关的医疗赛道。在比赛期间,我也是借助了九天人工智能平台来帮我解决了比赛期间数据处理、数据分析以及模型训练与部署的难题,同时,每天签到的话,平台还会赠予相应的算力豆,可以供我们进行人工智能计算使用。
九天人工智能平台
提供人工智能算力、算法、数据
,汇聚优秀AI能力,打造从智算基础设施、核心算法能力到智能化应用的全栈人工智能服务,全面支持自智网络等多样化运营商智慧运营需求,为工业、医疗、政务、教育、金融等行业客户构建创新解决方案。
而九天人工智能平台又分为基础开发平台
和行业应用平台
,同时分别对应九天·自动建模平台AutoX
和九天毕昇平台
。
2.1 九天·自动建模平台AutoX
九天·自动建模平台AutoX
作为一站式人工智能学习和实战平台,为AI学习者提供充沛的GPU算力、丰富的数据和学习实战资源,服务课程学习、比赛打榜、工作求职等全流程场景,并面向高校提供在线教学、科研开发的一站式解决方案,助力高校培养复合型AI人才、支持高校人工智能技术创新。
🌟直达链接:九天·自动建模平台AutoX
我们点击上面的立即使用
可以跳转到自动建模平台,不过貌似最近平台在维护,目前访问不了。不过该平台和百度的EasyDL功能类似,不同之处在于,九天自动建模平台看起来更加简便而且对新手友好,它不仅操作简单而且还有完善的使用文档可以供我们新人进行学习体验。最主要的是,该平台最大的特点
在于不仅可以实现视觉方面的模型一键全流程训练,而且还可以实现NLP方面的一键全流程训练。这对于上课不好好听讲而迫切想要完成老师布置的作业的大学生简直是一大福音。当然,对于一些平时比较忙碌但同时又要兼顾模型训练的人工智能研究人员也是一大好助手。
我们通过项目管理可以清楚看到项目的进展,包括数据集的导入、模型的训练以及模型的验证和发布
。
同时,如果我们点击 “数据导入”,就可以查看项目的数据集列表,包括数据名称、状态、场景、上传路径、创建时间、任务进度,支持查看数据集详情。
此外,我们也可以新建数据集,上传自己需要用到的任何数据集,其中包括自己采集的以及网上查找的。
如果我们有多个数据集,但是该数据集属于同一类型,那么我们可以点击 “合并数据” 按钮,可以选择2-6个完成状态的数据集进行合并操作,生成新的数据集,新数据集和原有的数据集彼此独立,如有重名图片会保留其中一份。
此外,平台还为第三方系统提供了数据回流接口,将数据推送到交换区,输入数据名称、描述、文件格式(VOC、YOLO或COCO)、数据文件地址。回流完成之后可以查看的图片和检测标注情况。
在数据列表中点击数据名称,可查看数据详情。
通过上面图片我们可以清晰看到,我们上传数据集以后,还可以对数据集进行分析、筛选甚至标注。不再需要本地下载工具进行标注,标注完成以后也可以直接进行训练,不需要我们进一步处理,可谓是一站式服务!轻松方便的很嘞~
模型训练也是很简单,点击“模型训练”,查看项目的模型训练列表,包括模型训练名称、状态、运行时长、创建时间、任务进度、准确率、是否可用模型,支持查看模型训练详情。
要知道,人工智能是一门交叉学科,不仅学习该专业的人经常用到,其它各行各业的人也经常需要使用到它。但是模型的训练对与专业的人来看虽然是小菜一碟,但是对于那些外行人来讲,他们可谓是一窍不通,环境的配置和训练前的bug就足以让他们“痛不欲生”。使用九天 · 自动建模平台AutoX
则可以很好的解决该问题,让小白也能够轻松训练属于自己的模型。
如果我们想要训练一个新模型,只需要点击 “新建任务”,新建AutoCV建模项目的模型训练任务,设置基本信息、训练参数并配置资源。模型训练任务新建完毕后,可在模型训练列表查看。
如果需要对已有模型进行模型迭代,可以打开增量训练开关,在“选择基准任务”输入框选择需要迭代的历史任务。也可以从模型详情页点击“模型迭代”按钮,平台将自动带入历史任务。
使用数据上,可以在数据集列表中选择带有标注的物体检测训练数据,增量训练选择增量的训练数据集。
增量训练开关开启后,平台会自动带入基准任务的算法和网络信息,系统将自动生成优化后的模型迭代超参和高级超参,一般按照该参数即可训练出不错的模型,如果后续对训练结果不理想,我们当然也可以进行参数调优。然后就是选择运行环境,我们可以按照自己任务来自行选择算力规模。
在模型训练列表中点击指定模型训练的名称,可查看模型训练详情,包括训练任务中各个模型的评分指标(mAP50、精准率、召回率)、超参数设置情况及日志、各标签识别情况、各标签错例分析。
模型训练详情-各标签识别情况界面可以点击“放大”按钮,进行查看识别情况。
同时模型训练详情-各标签错例分析界面,点击“放大”按钮,可以放大查看错误分析,左边图是验证集上的标注框和标签。右边图是正确识别、错误识别、漏识别的框。
点击“在线预测”标签,可以启动模型校验服务,上传图片,查看预测结果,预测结果列表中显示物体检测的位置框、类别标签、置信度。
以上就是AutoCV部分的使用,顾名思义,这就是计算机视觉cv领域一站式流程,当然还是 AutoNLP,对自然语言处理NLP领域的一站式流程服务,点击可以自行进行探索。概括为一点就是,九天·自动建模平台
提供给我们低门槛、零代码的自动化建模服务,预置预训练模型,深度优化的算法,用户只需提供业务目标和数据,即可轻松完成AI模型训练、调优和上线应用。AutoNLP模块当前支持单句分类、文本相似度、序列标注及智能文档理解任务。 不过该平台不知道最近为何无法访问,可能是我电脑问题,也可能该平台正在维护,如果是在维护,希望可以尽早重新让大家伙体验到该强大便利功能。
2.2 九天毕昇平台
点击立即使用
按钮,该平台就会立即跳转,进入到九天·毕昇体验版
页面。九天毕昇平台是一站式人工智能学习和实战平台
,为AI学习者提供充沛的GPU算力、丰富的数据和学习实战资源,服务课程学习、比赛打榜、工作求职等全流程场景,并面向高校提供在线教学、科研开发的一站式解决方案,助力高校培养复合型AI人才、支持高校人工智能技术创新。
🌟直达链接:九天·毕昇人工智能平台
九天·毕昇是一款新的AI学习和训练平台
,体验过百度飞桨AI Studio星河社区的伙伴肯定能发现,该平台跟百度飞桨的星河社区一样,不仅有各种AI实战教程,而且还有很多的人工智能学习课程,可谓是应用尽有,是一款名副其实的一站式人工智能学习和实战平台。同时它还具有百度星河社区没有的优势
,那就是平台可以使用各种深度学习框架,而不像百度星河社区一样局限于百度飞桨自研的Paddle框架。
点击控制台,可以看到该平台不仅可以签到领取算力豆,而且还给我们用户了高达200GB的存储配额,足够我们进行任意的基础深度模型训练。同时,我看还可以发现该平台左边还有数据管理功能,人工智能相关领域的伙伴肯定都知道,我们要做深度学习任务的前提之一就是寻找数据并进行数据处理,而该平台可以很好地帮助我们解决这一问题。
我们可以点击新建数据,来创建属于我们自己的数据集,用于模型训练。这里我们可以上传多种数据类型的数据集,比如图像视频、语音、文本、结构化数据甚至其它数据
。由此可见,该平台可以实现任何领域的深度学习计算。
三、九天毕昇初体验
对于九天毕昇平台,我们来简单进行一个小小的案例体验下它,由于只是体验,所以我就直接使用平台自带的一些流行数据集,不再自己从本地上传。
首先,我们点击单机训练,新建一个实例:
然后我就随便输入个实例名称进行下该实例的备注,以防我们后面忘记这个实例的任务是干嘛的。
数据集的话我们可以进行点击选择,可以发现有很多的常用数据集,而我比较常用的数据集就是CIFAR 10数据集,该数据集是由 Hinton 的学生 Alex Krizhevsky 和 Ilya Sutskever 整理的一个用于识别普适物体的小型数据集。CIFAR-10数据集包含60000幅32x32的彩色图像,分为10个类,每类6000幅图像。训练图像50000张,测试图像10000张。
这是我们入门计算机视觉领域常用的一个数据集,当初我作为小白入门计算机视觉的时候也是常用这个数据集进行训练学习。这里我们明显可以挂载最多三个数据集,这里我们作为体验,就先挂载一个数据集。
资源的话看来我们来晚了,只能够选择V100的2核CPU,好在它的内存有16g,够我们跑基础任务。
接下来我们就可以点击确定进行实例的创建,实例的创建需要一定的时间,耐心等待一下即可创建成功。
创建成功以后我们可以看到,我们可以选择使用jupyter或者vscode的方式启动该实例,而我由于经常使用jupyter,所以这里就用jupyter打开实例,习惯用vscode的朋友可以使用vscode打开实例。
jupyter notebook打开界面如下:
进来以后,印入眼帘的是各种各样的深度学习框架以及开发环境,这里有国外流行的Pytorch、TensorFlow,也有国内出名的Paddle和MindSpore。这里由于大家比较流行使用pytorch,所以我们就选取pytorch最为此次体验的深度学习框架。
vscode打开界面如下,可以看到和我们本地的vscode十分相像,我们可以点击右上角自主选择内核进行使用。:
接下来,我们就要开始体验了。首先,我们先选取一个图像分类模型,由于我们使用的数据集是CIFAR 10,所以分类模型方面也选取一个比较出名的模型ResNet。
残差神经网络(ResNet)
是由微软研究院的何恺明、张祥雨、任少卿、孙剑等人提出的。ResNet 在2015 年的ILSVRC(ImageNet Large Scale Visual Recognition Challenge)中取得了冠军。
残差神经网络的主要贡献是发现了“退化现象(Degradation)”
,并针对退化现象发明了 “快捷连接(Shortcut connection)”
,极大的消除了深度过大的神经网络训练困难问题。神经网络的“深度”首次突破了100层、最大的神经网络甚至超过了1000层。
ResNet核心残差结构如下:
3.1 构建模型
""" pytorch实现ResNet50、ResNet101和ResNet152: """ import torch import torch.nn as nn import torchvision import torch.nn.functional as F # conv1 7 x 7 64 stride=2 def Conv1(channel_in, channel_out, stride=2): return nn.Sequential( nn.Conv2d( channel_in, channel_out, kernel_size=7, stride=stride, padding=3, bias=False ), nn.BatchNorm2d(channel_out), # 会改变输入数据的值 # 节省反复申请与释放内存的空间与时间 # 只是将原来的地址传递,效率更好 nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=3, stride=stride, padding=1) ) # 构建ResNet18-34的网络基础模块 class BasicBlock(nn.Module): expansion = 1 def __init__(self, in_planes, planes, stride=1): super(BasicBlock, self).__init__() self.conv1 = nn.Conv2d( in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(planes) self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(planes) self.shortcut = nn.Sequential() if stride != 1 or in_planes != self.expansion * planes: self.shortcut = nn.Sequential( nn.Conv2d(in_planes, self.expansion * planes, kernel_size=1, stride=stride, bias=False), nn.BatchNorm2d(self.expansion * planes) ) def forward(self, x): out = F.relu(self.bn1(self.conv1(x))) out = self.bn2(self.conv2(out)) out += self.shortcut(x) out = F.relu(out) return out # 构建ResNet50-101-152的网络基础模块 class Bottleneck(nn.Module): expansion = 4 def __init__(self, in_planes, planes, stride=1): super(Bottleneck, self).__init__() # 构建 1x1, 3x3, 1x1的核心卷积块 self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=1, bias=False) self.bn1 = nn.BatchNorm2d(planes) self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(planes) self.conv3 = nn.Conv2d(planes, self.expansion * planes, kernel_size=1, bias=False) self.bn3 = nn.BatchNorm2d(self.expansion * planes) # 采用1x1的kernel,构建shout cut # 注意这里除了第一个bottleblock之外,都需要下采样,所以步长要设置为stride=2 self.shortcut = nn.Sequential() if stride != 1 or in_planes != self.expansion * planes: self.shortcut = nn.Sequential( nn.Conv2d(in_planes, self.expansion * planes, kernel_size=1, stride=stride, bias=False), nn.BatchNorm2d(self.expansion * planes) ) def forward(self, x): out = F.relu(self.bn1(self.conv1(x))) out = F.relu(self.bn2(self.conv2(out))) out = self.bn3(self.conv3(out)) out += self.shortcut(x) out = F.relu(out) return out # 搭建ResNet模板块 class ResNet(nn.Module): def __init__(self, block, num_blocks, num_classes=10): super(ResNet, self).__init__() self.in_planes = 64 self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(64) # 逐层搭建ResNet self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=1) self.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2) self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2) self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2) self.linear = nn.Linear(512 * block.expansion, num_classes) # 参数初始化 # for m in self.modules(): # if isinstance(m, nn.Conv2d): # nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') # elif isinstance(m, nn.BatchNorm2d): # nn.init.constant_(m.weight, 1) # nn.init.constant_(m.bias, 0) def _make_layer(self, block, planes, num_blocks, stride): strides = [stride] + [1] * (num_blocks - 1) # layers = [ ] 是一个列表 # 通过下面的for循环遍历配置列表,可以得到一个由 卷积操作、池化操作等 组成的一个列表layers # return nn.Sequential(*layers),即通过nn.Sequential函数将列表通过非关键字参数的形式传入(列表layers前有一个星号) layers = [] for stride in strides: layers.append(block(self.in_planes, planes, stride)) self.in_planes = planes * block.expansion return nn.Sequential(*layers) def forward(self, x): out = F.relu(self.bn1(self.conv1(x))) out = self.layer1(out) out = self.layer2(out) out = self.layer3(out) out = self.layer4(out) out = F.avg_pool2d(out, 4) out = out.view(out.size(0), -1) out = self.linear(out) return out def ResNet18(): return ResNet(BasicBlock, [2, 2, 2, 2]) def ResNet34(): return ResNet(BasicBlock, [3, 4, 6, 3]) def ResNet50(): return ResNet(Bottleneck, [3, 4, 6, 3]) def ResNet101(): return ResNet(Bottleneck, [3, 4, 23, 3]) def ResNet152(): return ResNet(Bottleneck, [3, 8, 36, 3]) # 测试 if __name__ == '__main__': model = ResNet18() print(model)
我们可以先打印下模型结构看下,这里我们选取ResNet18作为我们的模型,结果输出如下:
ResNet( (conv1): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (layer1): Sequential( (0): BasicBlock( (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (shortcut): Sequential() ) (1): BasicBlock( (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (shortcut): Sequential() ) ) (layer2): Sequential( (0): BasicBlock( (conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (shortcut): Sequential( (0): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2), bias=False) (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): BasicBlock( (conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (shortcut): Sequential() ) ) (layer3): Sequential( (0): BasicBlock( (conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (shortcut): Sequential( (0): Conv2d(128, 256, kernel_size=(1, 1), stride=(2, 2), bias=False) (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): BasicBlock( (conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (shortcut): Sequential() ) ) (layer4): Sequential( (0): BasicBlock( (conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False) (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (shortcut): Sequential( (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False) (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) ) ) (1): BasicBlock( (conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (shortcut): Sequential() ) ) (linear): Linear(in_features=512, out_features=10, bias=True) )
3.2 加载数据集
接下来,就是数据集读取和处理了,数据处理代码如下:
import os,PIL,random,pathlib data_dir = './dataset' data_dir = pathlib.Path(data_dir) data_paths = list(data_dir.glob('*')) classeNames = [str(path).split("\\")[1] for path in data_paths] # 关于transforms.Compose的更多介绍可以参考:https://blog.csdn.net/qq_38251616/article/details/124878863 train_transforms = transforms.Compose([ transforms.Resize([32, 32]), # 将输入图片resize成统一尺寸 # transforms.RandomHorizontalFlip(), # 随机水平翻转 transforms.ToTensor(), # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间 transforms.Normalize( # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛 mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。 ]) test_transform = transforms.Compose([ transforms.Resize([32, 32]), # 将输入图片resize成统一尺寸 transforms.ToTensor(), # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间 transforms.Normalize( # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛 mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。 ]) total_data = datasets.ImageFolder("./dataset/",transform=train_transforms) # 划分数据集 train_size = int(0.8 * len(total_data)) test_size = len(total_data) - train_size train_dataset, test_dataset = torch.utils.data.random_split(total_data, [train_size, test_size]) train_dataset, test_dataset
3.3 编写训练函数
# 训练循环 def train(dataloader, model, loss_fn, optimizer): size = len(dataloader.dataset) # 训练集的大小 num_batches = len(dataloader) # 批次数目, (size/batch_size,向上取整) print('num_batches:', num_batches) train_loss, train_acc = 0, 0 # 初始化训练损失和正确率 for X, y in dataloader: # 获取图片及其标签 X, y = X.to(device), y.to(device) # 计算预测误差 pred = model(X) # 网络输出 loss = loss_fn(pred, y) # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失 # 反向传播 optimizer.zero_grad() # grad属性归零 loss.backward() # 反向传播 optimizer.step() # 每一步自动更新 # 记录acc与loss train_acc += (pred.argmax(1) == y).type(torch.float).sum().item() train_loss += loss.item() train_acc /= size train_loss /= num_batches return train_acc, train_loss
3.4 编写测试函数
测试函数和训练函数大致相同,但是由于不进行梯度下降对网络权重进行更新,所以不需要传入优化器
def test (dataloader, model, loss_fn): size = len(dataloader.dataset) # 测试集的大小 num_batches = len(dataloader) # 批次数目, (size/batch_size,向上取整) test_loss, test_acc = 0, 0 # 当不进行训练时,停止梯度更新,节省计算内存消耗 with torch.no_grad(): for imgs, target in dataloader: imgs, target = imgs.to(device), target.to(device) # 计算loss target_pred = model(imgs) loss = loss_fn(target_pred, target) test_loss += loss.item() test_acc += (target_pred.argmax(1) == target).type(torch.float).sum().item() test_acc /= size test_loss /= num_batches return test_acc, test_loss
3.5 正式训练
import copy optimizer = torch.optim.Adam(ResNet18.parameters(), lr= 1e-2) loss_fn = nn.CrossEntropyLoss() # 创建损失函数 epochs = 60 train_loss1 = [] train_acc1 = [] test_loss1 = [] test_acc1 = [] best_acc = 0 # 设置一个最佳准确率,作为最佳模型的判别指标 for epoch in range(epochs): resnet28.train() epoch_train_acc, epoch_train_loss = train(train_dl, resnet28, loss_fn, optimizer) resnet28.eval() epoch_test_acc, epoch_test_loss = test(test_dl, resnet28, loss_fn) # 保存最佳模型到 best_model if epoch_test_acc > best_acc: best_acc = epoch_test_acc best_model = copy.deepcopy(resnet28) train_acc1.append(epoch_train_acc) train_loss1.append(epoch_train_loss) test_acc1.append(epoch_test_acc) test_loss1.append(epoch_test_loss) # 获取当前的学习率 lr = optimizer.state_dict()['param_groups'][0]['lr'] template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f}, Lr:{:.2E}') print(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss, epoch_test_acc*100, epoch_test_loss, lr)) # 保存最佳模型到文件中 PATH = './best_resnet.pth' # 保存的参数文件名 torch.save(best_model.state_dict(), PATH) print('Done')
训练结果如下:
3.6 结果可视化
import matplotlib.pyplot as plt #隐藏警告 import warnings warnings.filterwarnings("ignore") #忽略警告信息 plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 plt.rcParams['figure.dpi'] = 100 #分辨率 epochs_range = range(epochs) plt.figure(figsize=(12, 3)) plt.subplot(1, 2, 1) plt.plot(epochs_range, train_acc1, label='Training Accuracy') plt.plot(epochs_range, test_acc1, label='Test Accuracy') plt.legend(loc='lower right') plt.title('Training and Validation Accuracy') plt.subplot(1, 2, 2) plt.plot(epochs_range, train_loss1, label='Training Loss') plt.plot(epochs_range, test_loss1, label='Test Loss') plt.legend(loc='upper right') plt.title('Training and Validation Loss') plt.show()
四、总结
移动云采用了先进的“一云多池”架构体系,其中五大资源池构成核心层,省公司资源池作为边缘计算节点构成接入层。这种架构体系使得移动云能够提供安全、可信、高效的云服务。在产品方面,移动云已经打造了包括硬件、IaaS、PaaS、SaaS、安全、管理平台在内的六大产品线,并引入了超过2500款合作SaaS产品。此外,移动云还自研了超过210款产品,其中IaaS产品数量已经跻身国内第一阵营。这些产品在操作系统、弹性计算、存储、云管平台等方面均已实现了软硬一体可控,为核心产品提供了高达99.98%的可用性。
九天毕昇
是中国移动推出的一站式人工智能学习和实战平台
,该平台在人工智能模型训练`方面具有显著优势。以下是对九天毕昇平台在人工智能模型训练方面的详细介绍:
- 强大的训练能力:九天毕昇平台提供了强大的CPU和GPU训练能力,使得用户能够高效地进行深度学习模型的训练。这不仅可以加快模型的训练速度,还可以提高模型的准确性,但是目前平台一直没有开放GPU资源,希望以后可以开通下。
- 丰富的模型库和算法库:九天毕昇平台拥有丰富的模型库和算法库,涵盖了各种主流的人工智能算法和模型。这使得用户能够轻松地选择适合自己的模型和算法,进行高效的模型训练。
- 灵活的配置和扩展性:九天毕昇平台提供了灵活的配置和扩展性,用户可以根据自己的需求进行定制化的配置和扩展。这不仅可以满足用户的不同需求,还可以提高平台的灵活性和可扩展性。
- 易于使用的界面和工具:九天毕昇平台提供了易于使用的界面和工具,使得用户能够轻松地进行模型训练和实验。用户可以通过简单的操作完成模型的配置、训练、评估和部署等操作,无需进行复杂的编程和调试。