-
然而,为什么基于深度学习的方法在实测效果上并未比施易昌等的方法好多少呢?我认为主要有两个原因:深度学习方法需要大量数据来训练,而数据非常昂贵谭婧等所用的评判结果好坏的标准和人类的主观感受还无法完全一致我们先来看看评价方法和标准的缺陷,如下图所示,作者们用的评价方法是采样图像上的特定的线段,希望这些线段与Ground Truth之间的夹角越小越好。那么这里就可能存在这些采样的线段不足以描述人类的主观感受这样的问题了,比如是不是当发生畸变时,有更多的无法简单用线段的偏转来描述的特征?这是一个非常本质和深入的问题,可以留待将来进行更深入的研究。更现实的问题是,不管是用于训练,还是用于验证和评价,数据都非常宝贵。高质量的数据非常难于得到。几乎对每一张样本图像,都需要手工进行调整,以得到它去掉畸变后的完美图像——这实际上是一个非常主观的事情。昂贵的数据采集手段,使得谭婧等最终得到的是大概5000张图像的数据集。实际上,这些数据集很可能因为时间和精力原因,存在一定的瑕疵,影响最终的效果。那么,有没有可能降低对数据采集的需求呢?半监督学习是一个可能的方向,下面这篇旷视研究院的预印版文章(arxiv.org/abs/2109.0802)可供参考:作者们的想法也很简单,他们在网络训练过程中加入了一个分割的任务,使得分割任务去分割校正map中的各个部分(不同方向的大位移以及小位移)。那么分割结果和校正map之间就建立起了某种关系,另外分割结果、校正map与各自的Ground Truth之间也有约束关系。现在加入没有标注过的数据,这些数据经过网络推理后也会得到分割图和校正map。作者认为这两者之间的关系应该和有标注的数据对应的信息有相同的约束关系——就用这个约束去迭代优化网络。听起来,跟模型蒸馏颇有异曲同工之妙,文章中也展示了挺不错的效果,例如下图中的效果,确实相比谭婧和施易昌等人的方法有了提升。
-
网络结构如下图所示,其中LineNet用于进行镜头畸变校正,经过LineNet后直线被校直ShapeNet则用于进行人脸的球极畸变校正。通过两个注意力模块LAM(Line Attention Module)和FAM(Face Attention Module),图像中的直线和人脸被特意强调TM用于保持LineNet和ShapeNet在背景区域的一致性损失函数下面是LineNet和ShapeNet的损失函数,其中下标flow代表在计算map表的一致性,而下标proj和下标out则用于表示计算image的一致性。而2,s2则表示分别计算原始的L2 Loss,以及先对图像或map做Sobel计算,然后再计算L2 Loss。类似的,两个注意力模块LAM和FAM也有自己的损失函数:.这几个损失函数之和构成了最终的损失函数,用于约束整个网络的训练
-
为了评价算法的效果,作者引入了两个评价指标: LineAcc(评价直线的保直度),以及ShapeAcc(评价人脸校正的准确度)。简单说来,LineAcc计算的是已标注的线段在校正后图像上的像素点的平均夹角,夹角越小,直线越直。而ShapeAcc则是计算校正前后人脸上每个关键点与中心点形成的线段的夹角,夹角越小,人脸效果越接近Ground Truth。下图说明作者的最终输出在LineAcc和ShapeAcc两方面均超过了输入,且达到了较好的平衡。
-
谭婧等的方法总体上来说是一种监督型的训练方法,因此需要一个带有Ground Truth信息的数据集。然而以前从来没有广角畸变校正的标准数据集,因此作者用5个超广角的拍照手机,在各种场景下进行采集,制作了一个超过5000张图像的数据集,每张图像的人像分布在1到6人之间。由于有这几个相机的畸变参数,因此可以很容易的获得镜头畸变校正的map,作为LineNet训练时的Ground Truth。接下来,为了获得每张图像对应的透视畸变校正后的图像,作者们基于施易昌等的方法开发了一个自动化的校正工具,其中刻意在原方法的目标函数的基础上添加了一个人脸附近的直线约束,使得能够更好的保持人脸附近的直线。作者们还开发了一个微调工具,可以基于Mesh来对图像的局部进行微调。通过联合使用校正工具+微调工具,她们得到了一个效果很好的数据集,下图d是最终手动优化后的效果,可以看到它确实比施易昌等的方法结果好(图b),例如这里人脸附近的大楼阳台保持了直线。下面展示了数据集中需要包括的内容输入的图像(完全未经畸变校正)经过镜头畸变校正后的图像(此时直线被校直)经过透视畸变校正后的图像(此时背景直线校直,且人脸畸变被消除)人脸Mask直线标注信息镜头畸变校正Map透视畸变校正Map
-
在手机中的计算摄影4-超广角畸变校正中,我为你描述了广角镜头的镜头畸变校正和透视畸变校正,尤其是花了很多篇幅讲述施易昌等人的论文如何校正因为透视畸变导致的人脸拉伸现象。然而,正如我文章中所讲,这个方法依然有它的不足之处:没有准确的人脸Mask就无法进行校正校正只针对人脸,会出现头小身大的现象某些情况下无法保证直线不弯曲,有些时候还无法很好的校正人脸速度慢,单张图像需要接近1秒钟完成那么如何解决这些问题呢?今天要介绍的方法,就是尝试对上述缺陷进行改进。一. 第一个基于深度学习的畸变校正算法我们先来想想为什么施易昌等人的方法存在一定缺陷需要FOV信息作为输入,作者提到他们用到了图像的EXIF信息来获取焦距和传感器信息,但我们都知道很多图像在传输和保存过程中是丢失了这些信息的因为他们的算法只针对人脸区域采用球极投影进行校正,因此一定需要输入人脸的Mask,而这个Mask是通过专门训练的人脸分割网络计算出来的。很显然,这里带来了两个问题:算法需要额外的外部输入,增加了复杂度外部输入的质量高低决定了人脸校正的效果好坏3. 整个算法包含多个可调整的参数。但作者只在20张图像上进行了参数的调整,然后在167张图像上进行了仔细的验证,最后在4131张图像上观察是否有明显的错误。总体来说,真正用于检验并调整参数只有不到两百张图像,确实可能有泛化性问题。4. 另外目标函数也是非常复杂的,因此最优化也比较慢。针对这几个问题,潜在的解决方案有:完全不用相机内参数输入,将背景的镜头畸变校正也整合到流程中,用一些图像上的特征来约束镜头畸变校正将人脸校正所需的Mask的计算整合到整个流程中,不需要外部输入使用大量的图像来验证算法的泛化性和有效性旷视研究院的谭婧、赵姗等人在2021年CVPR发表的文章,就很好的把这几个方案整合到了一起:这篇文章展示了第一个用深度学习来完成的自动去畸变算法,其中用两个子网络分别完成镜头畸变的校正和透视畸变的校正,而且它不需要相机的畸变参数,效果据作者描述比施易昌等人的方法更好。这里,LineNet用于进行镜头畸变校正,经过LineNet后直线被校直。而ShapeNet则用于进行人脸的球极畸变校正。通过两个注意力模块LAM(Line Attention Module)和FAM(Face Attention Module),图像中的直线和人脸被特意强调。由于训练数据有人脸和直线的标注信息,因此训练出的网络不需要外部的相机参数及人脸Mask也能够进行很好的校正,最后得到了和施易昌等方法相当,甚至某些情况下更好的结果。
-
最大值池化最大值池化是最常见、也是用的最多的池化操作。最大值池化的核心代码可以描述为:// 摘选自caffe并稍加修改.top_data = -FLT_MAX; for (int h = hstart; h < hend; ++h) { for (int w = wstart; w < wend; ++w) { const int index = h * width_ + w; if (bottom_data[index] > top_data[pool_index]) { top_data = bottom_data[index]; } } }
-
1999年,一只企鹅出现在了电脑屏幕上,OICQ的出现让人们开始感受到了互联网的魅力。2001年以后,中国正式加入WTO,经济活力空前高涨,为互联网发展提供了成长的土壤。如果说抓住了互联网的风口,成就了无数经济大鳄量的积累,那么物联网的到来,则会让整个世界都产生质的飞跃。什么是物联网 物联网IoT(Internet of Things),即万物相连,是指通过信息传感设备,按约定的协议,将任何物体与网络相连接。物体通过信息传播媒介进行信息交换和通信,以实现智能化识别、定位、跟踪、监管等功能。据说物联网的诞生源于一只咖啡壶,那是在1991年,剑桥大学特洛伊计算机实验室的科学家们在工作时,要下两层楼梯到楼下看咖啡煮好了没有,但平平无奇的咖啡壶并不能成精,告诉楼上科学家们咖啡的实时情况,说起来也很奇怪,为啥不能把咖啡壶直接搬到楼上呢?天才的脑回路真不是凡人可以琢磨的。为了解决这个问题,聪明的科学家们编写了一套程序,在咖啡壶旁边安装了一个摄像头,利用终端摄像机的图像捕捉技术,以3帧/秒的速率传递到实验室的计算机上,这样科学家们便可随时查看咖啡是否煮好,没想到这项“懒人发明”成为了物联网最早的雏形。最终,这只著名的咖啡壶在EBay拍卖网站以7300美元的价格卖出!2008年,首届国际物联网大会在瑞士苏黎世举行,在这一年,物联网设备的数量首次超过了地球上的人口数量。2010年,中国政府将物联网列为关键技术,并宣布物联网是其长期发展规划的一部分。同年,Nest发布了一款智能恒温器,可以了解用户的生活习惯,自动调节房子的温度。Nest让“智能家居”的概念成为人们关注的焦点。物联网与传感设备上文所述,物联网是通过信息传感设备进行“万物互联”,那么,传感设备包括哪些呢?传感器作为物联网的“感知器官”可以采集和获取目标对象各种形态的信息,目前比较常见的包括红外传感器、激光雷达、摄像头、超声波和毫米波雷达。红外线传感器是一种能够感应目标辐射的红外线,利用红外线的物理性质来进行测量的传感器。激光雷达使用脉冲激光计算物体与地球表面的可变距离。这些光脉冲与机载系统收集到的信息结合在一起,可生成关于地球表面和目标物体的精确三维信息。摄像头一般具有视频摄像/传播和静态图像捕捉等基本功能,它是借由镜头采集图像后,由摄像头内的感光组件电路及控制组件对图像进行处理并转换成电脑所能识别的数字信号,然后借由并行端口或USB连接输入到电脑后由软件再进行图像还原。超声波是一种频率高于20000Hz(赫兹)的声波,它的方向性好,反射能力强,易于获得较集中的声能,在水中传播距离比空气中远,可用于测距、测速、清洗、焊接、碎石、杀菌消毒等。毫米波是指波长为1~10mm的电磁波,对应的频率范围为30~300GHz。毫米波雷达是工作在毫米波频段的雷达,它通过发射与接收高频电磁波来探测目标,后端信号处理模块利用回波信号计算出目标的距离、速度和角度等信息。毫米波雷达为啥“出圈”面对越来越广泛的应用需求和新场景的出现,物联网对传感器提出了更高的要求,毫米波雷达在其中算是备受关注的“千里马”。 传感器对比毫米波雷达与摄像头、红外线相比,不受目标物体形状颜色的干扰,而且不涉及隐私泄露的问题,与超声波相比不受大气紊流的影响,因而具有稳定的探测性能,环境适应性好。烟雾雨雪等环境都不会对毫米波雷达产生干扰,可全天候全天时工作,除此之外,毫米波雷达还有另一个其他四大传感器不能匹敌的就是测试距离远,最远能够达到 1000 米以上。毫米波雷达最广泛的应用市场,是车载雷达,目前主流的车载毫米波雷达按照频率不同,主要分为24GHz 毫米波雷达和 77GHz 毫米波雷达(欧洲是 79GHz),而在智能家居、智能穿戴、智能卫浴、安防等领域,则是24GHz毫米波雷达的主阵地。在目前的智能家居场景,以人体感应传感器为例,红外线、超声波和毫米波雷达各有千秋,但毫米波雷达在性能和性价比方面的表现更为出彩,尤其是毫米波雷达可隐藏在外壳中,无需钻孔,而且不用特意像红外线一样只能选择玻璃外壳材质,因此对追求产品“颜值”的消费者而言,无疑也有足够的吸引力。随着物联网的发展,智能场景的多样性呼声与日俱增,毫米波雷达在智能家居、智能卫浴、智能空调、停车场道闸、智能楼宇、安防辅助、养老健康、无人机、机器人、工业测量等领域将有更多更广泛的发展空间,让我们拭目以待,感受科技发展带来不可思议的生活体验。
-
MNIST手写体识别实验 —使用LeNet算法实现手写数字识别实验被誉为AI界的“hello world”,本文是在学习的过程中在对基于mindSpore框架训练代码的简单注释。(如有来理解错误,欢迎谈论)步骤1 查看原始数据集数据:from mindspore import context #导入context 模块,调用context的set_context方法import matplotlib.pyplot as pltimport matplotlibimport numpy as npimport mindspore.dataset as ds#上面是调用画图所需要的库# device_target 可选 CPU/GPU/Ascend, 当选择GPU时mindspore规格也需要切换到GPU device_target表示硬件信息,有三个选项(CPU、GPU、Ascend)context.set_context(mode=context.GRAPH_MODE, device_target="CPU") # device_id = int(os.getenv("DEVICE_ID"))# context.set_context(mode=context.GRAPH_MODE, device_target="Ascend", device_id=device_id)train_data_path = "./MNIST_Data/train" #训练数据集路径test_data_path = "./MNIST_Data/test" #测试数据集路径mnist_ds = ds.MnistDataset(train_data_path) print('The type of mnist_ds:', type(mnist_ds))print("Number of pictures contained in the mnist_ds:", mnist_ds.get_dataset_size()) dic_ds = mnist_ds.create_dict_iterator()item = next(dic_ds)# 字典类型数据img = item["image"].asnumpy()label = item["label"].asnumpy()print("The item of mnist_ds:", item.keys()) print("Tensor of image in item:", img.shape) print("The label of item:", label) plt.imshow(np.squeeze(img)) plt.title("number:%s"% item["label"].asnumpy()) plt.show() #显示图像步骤2 数据处理:数据集对于训练非常重要,好的数据集可以有效提高训练精度和效率,在加载数据集前,我们通常会对数据集进行一些处理。定义数据集及数据操作定义完成后,使用create_datasets对原始数据进行增强操作,并抽取一个batch的数据,查看数据增强后的变化。#定义一个creat_dataset函数,对原始数据进行增强(进行数据增强提升样本质量,避免局部最优等一些问题)import mindspore.dataset.vision.c_transforms as CVimport mindspore.dataset.transforms.c_transforms as Cfrom mindspore.dataset.vision import Interfrom mindspore import dtype as mstype def create_dataset(data_path, batch_size=32, repeat_size=1, num_parallel_workers=1): """ create dataset for train or test Args: data_path (str): Data path batch_size (int): The number of data records in each group repeat_size (int): The number of replicated data records num_parallel_workers (int): The number of parallel workers """ # define dataset mnist_ds = ds.MnistDataset(data_path) 将原始的Minist数据集加载进来 # define some parameters needed for data enhancement and rough justification定义数据增强和处理的一些参数 resize_height, resize_width = 32, 32 #图片大小32*32 rescale = 1.0 / 255.0 #图像缩放因子,使每个像素点在(0,255)范围内 shift = 0.0 #图像偏移因子 rescale_nml = 1 / 0.3081 shift_nml = -1 * 0.1307 / 0.3081 # according to the parameters, generate the corresponding data enhancement method resize_op = CV.Resize((resize_height, resize_width), interpolation=Inter.LINEAR) # 使用给定的插值模式将输入图像调整为一定大小 rescale_nml_op = CV.Rescale(rescale_nml, shift_nml)rescale_op = CV.Rescale(rescale, shift) #使用给定的缩放和移位消除图片在不同位置带来的影响hwc2chw_op = CV.HWC2CHW() #将输入图像从(H,W,C)转化为(C,H,W) type_cast_op = C.TypeCast(mstype.int32)#转化为Minist特定的数据类型mstype# using map to apply operations to a dataset#将数据增强处理方法映射到(使用)在对应数据集的相应部分(image,label) mnist_ds = mnist_ds.map(operations=type_cast_op, input_columns="label", num_parallel_workers=num_parallel_workers) mnist_ds = mnist_ds.map(operations=resize_op, input_columns="image", num_parallel_workers=num_parallel_workers) mnist_ds = mnist_ds.map(operations=rescale_op, input_columns="image", num_parallel_workers=num_parallel_workers) mnist_ds = mnist_ds.map(operations=rescale_nml_op, input_columns="image", num_parallel_workers=num_parallel_workers) mnist_ds = mnist_ds.map(operations=hwc2chw_op, input_columns="image", num_parallel_workers=num_parallel_workers) # process the generated dataset buffer_size = 10000 mnist_ds = mnist_ds.shuffle(buffer_size=buffer_size) #将数据集进行打乱 mnist_ds = mnist_ds.batch(batch_size, drop_remainder=True) # 数据集按照batch_size的大小分成若干批次,按照批次进行训练,drop_remainder=True是保留数据集整除batch_size的,多余的舍弃掉 mnist_ds = mnist_ds.repeat(repeat_size)#将数据集重复repeat_size次 return mnist_ds#展示数据集ms_dataset = create_dataset(train_data_path)print('Number of groups in the dataset:', ms_dataset.get_dataset_size()) # 查看一共分成多少组,将数据一共分成60000/32=1875个批次步骤3 进一步查看增强后的数据:从1875组数据中取出一组数据查看其数据张量及label。将张量数据和下标对应的值进行可视化。(请给出代码,并在每一句后面手写解释这一句代码实现的功能,在最后给出代码运行的输出结果)data = next(ms_dataset.create_dict_iterator(output_numpy=True)) 利用next获取样本并查看单个样本格式images = data["image"] labels = data["label"] #获取图像张量print('Tensor of image:', images.shape)print('labels:', labels)将张量数据和下标对应的值进行可视化。(请给出代码,并在每一句后面手写解释这一句代码实现的功能,在最后给出代码运行的输出结果)count = 1for i in images: plt.subplot(4, 8, count) plt.imshow(np.squeeze(i)) plt.title('num:%s'%labels[count-1]) plt.xticks([]) count += 1 plt.axis("off")plt.show()3、 定义模型在对手写字体识别上,通常采用卷积神经网络架构(CNN)进行学习预测,最经典的属1998年由Yann LeCun创建的LeNet5架构,结构示意如下图:import mindspore.nn as nnfrom mindspore.common.initializer import Normal class LeNet5(nn.Cell): """Lenet network structure.""" # define the operator required def __init__(self, num_class=10, num_channel=1): super(LeNet5, self).__init__() #继承父类nn.cell的__init__方法 self.conv1 = nn.Conv2d(num_channel, 6, 5, pad_mode='valid') self.conv2 = nn.Conv2d(6, 16, 5, pad_mode='valid') # pad_mode是卷积方式 ‘valid’是pad_mode = 0 self.fc1 = nn.Dense(16 * 5 * 5, 120, weight_init=Normal(0.02)) self.fc2 = nn.Dense(120, 84, weight_init=Normal(0.02)) self.fc3 = nn.Dense(84, num_class, weight_init=Normal(0.02))#nn.Dense为致密连接层,它的第一个参数为输入层的维度,第二个参数为输出的维度,第三个参数为神经网络可训练参数W权重矩阵的初始化方式,默认为normal self.relu = nn.ReLU() self.max_pool2d = nn.MaxPool2d(kernel_size=2, stride=2) # 将图片的宽度和高度都缩小一半 self.flatten = nn.Flatten() #nn.Flatten为输入展成平图层,即去掉那些空的维度 # use the preceding operators to construct networks def construct(self, x): # 使用前馈操作构建神经网络 x = self.max_pool2d(self.relu(self.conv1(x))) x = self.max_pool2d(self.relu(self.conv2(x))) x = self.flatten(x) x = self.relu(self.fc1(x)) x = self.relu(self.fc2(x)) x = self.fc3(x) return x network = LeNet5()print("layer conv1:", network.conv1)print("*"*40)print("layer fc1:", network.fc1)4、搭建训练网络并进行训练构建完成神经网络后,就可以着手进行训练网络的构建,模型训练函数为Model.train此步骤案例中使用epoch=1,使用CPU训练大概耗时15分钟,为实现快速训练,可选用更高规格的资源训练。import osfrom mindspore import Tensor, Modelfrom mindspore import load_checkpoint, load_param_into_netfrom mindspore.train.callback import ModelCheckpoint, CheckpointConfig, LossMonitorfrom mindspore.nn.metrics import Accuracyfrom mindspore.nn.loss import SoftmaxCrossEntropyWithLogits lr = 0.01 # learingrate,学习率,可以使梯度下降的幅度变小,从而可以更好的训练参数momentum = 0.9 epoch_size = 1 # 每个epoch需要遍历完成图片的batch数,这里是只要遍历一次mnist_path = "./MNIST_Data"model_path = "./models/ckpt/" # clean up old run files before in Linuxos.system('rm -f {}*.ckpt {}*.meta {}*.pb'.format(model_path, model_path, model_path)) #清理旧文件 # create the networknetwork = LeNet5() #创建神经网络 # define the optimizernet_opt = nn.Momentum(network.trainable_params(), lr, momentum) #使用了Momentum优化器进行优化 # define the loss functionnet_loss = SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')#相当于softmax分类器 sparse指定标签(label)是否使用稀疏模式,默认为false,reduction为损失的减少类型:mean表示平均值,一般情况下都是选择平均地减少# define the modelmodel = Model(network, net_loss, net_opt, metrics={"Accuracy": Accuracy()} )#调用Model高级API metrics 参数是指训练和测试期# save the network model and parameters for subsequence fine-tuningconfig_ck = CheckpointConfig(save_checkpoint_steps=375, keep_checkpoint_max=16) #保存训练好的模型参数的路径# group layers into an object with training and evaluation featuresckpoint_cb = ModelCheckpoint(prefix="checkpoint_lenet", directory=model_path, config=config_ck) #保存训练好的模型参数的路径 eval_dataset = create_dataset("./MNIST_Data/test") #测试数据集 step_loss = {"step": [], "loss_value": []} # 回调函数中的数据格式steps_eval = {"step": [], "acc": []} # 回调函数中的数据格式#(收集step对应模型精度值accuracy的信息)repeat_size = 1 ds_train = create_dataset(os.path.join(mnist_path, "train"), 32, repeat_size) model.train(epoch_size, ds_train, callbacks=[ckpoint_cb, LossMonitor(125)], dataset_sink_mode=False) #调用Model类的train方法进行训练,5、测试数据验证模型精度# testing relate modules def test_net(network, model, mnist_path): #测试神经网络函数 """Define the evaluation method.""" print("============== Starting Testing ==============") #开始测试 # load the saved model for evaluation param_dict = load_checkpoint("./models/ckpt/checkpoint_lenet-1_1875.ckpt") #加载保存的模型以进行评估 # load parameter to the network load_param_into_net(network, param_dict) #向网络加载参数 # load testing dataset ds_eval = create_dataset(os.path.join(mnist_path, "test")) #加载测试数据集 acc = model.eval(ds_eval, dataset_sink_mode=False) #获取测试模型的精确度 print("============== Accuracy:{} ==============".format(acc)) test_net(network, model, mnist_path)6、推理(训练后)ds_test = create_dataset(test_data_path).create_dict_iterator() #测试数据集data = next(ds_test) images = data["image"].asnumpy() #获取数据张量labels = data["label"].asnumpy() #获取数据labels标签 output = model.predict(Tensor(data['image']))pred = np.argmax(output.asnumpy(), axis=1)err_num = [] #错误数字index = 1for i in range(len(labels)): plt.subplot(4, 8, i+1) #图片按照4*8的方式摆放 color = 'blue' if pred[i] == labels[i] else 'red' #对于图片标签颜色的不同,预测正确是蓝色,错误是红色 plt.title("pre:{}".format(pred[i]), color=color) #输出预测标签 plt.imshow(np.squeeze(images[i])) #显示图片 plt.axis("off") if color == 'red': # 判断数字是否错误 index = 0 print("Row {}, column {} is incorrectly identified as {}, the correct value should be {}".format(int(i/8)+1, i%8+1, pred[i], labels[i]), '\n')if index: # 判断是否所有数字都预测正确 print("All the figures in this group are predicted correctly!") print(pred, "<--Predicted figures") # 预测目标数字标签print(labels, "<--The right number") #说明数字正确标签plt.show() #显示图像
-
# 确认当前环境的版本import mindsporeprint(mindspore.__version__)1. 数据集下载MNIST是一个手写数字数据集,训练集包含60000张手写数字,测试集包含10000张手写数字,共10类。从华为云OBS公共桶中下载。import osimport moxing as mox if not os.path.exists("./MNIST_Data.zip"): mox.file.copy("obs://modelarts-labs-bj4-v2/course/hwc_edu/python_module_framework/datasets/mindspore_data/MNIST_Data.zip", "./MNIST_Data.zip")下载Minist数据集!unzip -o MNIST_Data.zip -d ./!tree ./MNIST_Data/2、处理MNIST数据集由于我们后面会采用LeNet这样的卷积神经网络对数据集进行训练,而采用LeNet在训练数据时,对数据格式是有所要求的,所以接下来的工作需要我们先查看数据集内的数据是什么样的,这样才能构造一个针对性的数据转换函数,将数据集数据转换成符合训练要求的数据形式。 步骤1 查看原始数据集数据:from mindspore import context 导入context 模块,调用context的set_context方法import matplotlib.pyplot as pltimport matplotlibimport numpy as npimport mindspore.dataset as ds上面是调用画图所需要的库# device_target 可选 CPU/GPU/Ascend, 当选择GPU时mindspore规格也需要切换到GPU device_target表示硬件信息,有三个选项(CPU、GPU、Ascend)context.set_context(mode=context.GRAPH_MODE, device_target="CPU") # device_id = int(os.getenv("DEVICE_ID"))# context.set_context(mode=context.GRAPH_MODE, device_target="Ascend", device_id=device_id)train_data_path = "./MNIST_Data/train" 训练数据集路径test_data_path = "./MNIST_Data/test" 测试数据集路径mnist_ds = ds.MnistDataset(train_data_path) print('The type of mnist_ds:', type(mnist_ds))print("Number of pictures contained in the mnist_ds:", mnist_ds.get_dataset_size()) dic_ds = mnist_ds.create_dict_iterator()item = next(dic_ds) 字典类型数据img = item["image"].asnumpy()label = item["label"].asnumpy()print("The item of mnist_ds:", item.keys()) print("Tensor of image in item:", img.shape) print("The label of item:", label) plt.imshow(np.squeeze(img)) plt.title("number:%s"% item["label"].asnumpy()) plt.show() 显示图像步骤2 数据处理:数据集对于训练非常重要,好的数据集可以有效提高训练精度和效率,在加载数据集前,我们通常会对数据集进行一些处理。定义数据集及数据操作定义完成后,使用create_datasets对原始数据进行增强操作,并抽取一个batch的数据,查看数据增强后的变化。定义一个creat_dataset函数,对原始数据进行增强(进行数据增强提升样本质量,避免局部最优等一些问题)import mindspore.dataset.vision.c_transforms as CVimport mindspore.dataset.transforms.c_transforms as Cfrom mindspore.dataset.vision import Interfrom mindspore import dtype as mstype def create_dataset(data_path, batch_size=32, repeat_size=1, num_parallel_workers=1): """ create dataset for train or test Args: data_path (str): Data path batch_size (int): The number of data records in each group repeat_size (int): The number of replicated data records num_parallel_workers (int): The number of parallel workers """ # define dataset mnist_ds = ds.MnistDataset(data_path) 将原始的Minist数据集加载进来 # define some parameters needed for data enhancement and rough justification定义数据增强和处理的一些参数 resize_height, resize_width = 32, 32 图片大小32*32 rescale = 1.0 / 255.0 图像缩放因子,使每个像素点在(0,255)范围内 shift = 0.0 图像偏移因子 rescale_nml = 1 / 0.3081 shift_nml = -1 * 0.1307 / 0.3081 # according to the parameters, generate the corresponding data enhancement method resize_op = CV.Resize((resize_height, resize_width), interpolation=Inter.LINEAR) 使用给定的插值模式将输入图像调整为一定大小 rescale_nml_op = CV.Rescale(rescale_nml, shift_nml)rescale_op = CV.Rescale(rescale, shift) 使用给定的缩放和移位消除图片在不同位置带来的影响hwc2chw_op = CV.HWC2CHW() 将输入图像从(H,W,C)转化为(C,H,W) type_cast_op = C.TypeCast(mstype.int32)转化为Minist特定的数据类型mstype# using map to apply operations to a dataset将数据增强处理方法映射到(使用)在对应数据集的相应部分(image,label) mnist_ds = mnist_ds.map(operations=type_cast_op, input_columns="label", num_parallel_workers=num_parallel_workers) mnist_ds = mnist_ds.map(operations=resize_op, input_columns="image", num_parallel_workers=num_parallel_workers) mnist_ds = mnist_ds.map(operations=rescale_op, input_columns="image", num_parallel_workers=num_parallel_workers) mnist_ds = mnist_ds.map(operations=rescale_nml_op, input_columns="image", num_parallel_workers=num_parallel_workers) mnist_ds = mnist_ds.map(operations=hwc2chw_op, input_columns="image", num_parallel_workers=num_parallel_workers) # process the generated dataset buffer_size = 10000 mnist_ds = mnist_ds.shuffle(buffer_size=buffer_size) 将数据集进行打乱 mnist_ds = mnist_ds.batch(batch_size, drop_remainder=True) 数据集按照batch_size的大小分成若干批次,按照批次进行训练,drop_remainder=True是保留数据集整除batch_size的,多余的舍弃掉 mnist_ds = mnist_ds.repeat(repeat_size)将数据集重复repeat_size次 return mnist_ds展示数据集ms_dataset = create_dataset(train_data_path)print('Number of groups in the dataset:', ms_dataset.get_dataset_size()) 查看一共分成多少组,将数据一共分成60000/32=1875个批次步骤3 进一步查看增强后的数据:从1875组数据中取出一组数据查看其数据张量及label。将张量数据和下标对应的值进行可视化。data = next(ms_dataset.create_dict_iterator(output_numpy=True)) 利用next获取样本并查看单个样本格式images = data["image"] labels = data["label"] 获取图像张量print('Tensor of image:', images.shape)print('labels:', labels)将张量数据和下标对应的值进行可视化。count = 1for i in images: plt.subplot(4, 8, count) plt.imshow(np.squeeze(i)) plt.title('num:%s'%labels[count-1]) plt.xticks([]) count += 1 plt.axis("off")plt.show()3、 定义模型在对手写字体识别上,通常采用卷积神经网络架构(CNN)进行学习预测,最经典的属1998年由Yann LeCun创建的LeNet5架构,结构示意如下图: import mindspore.nn as nnfrom mindspore.common.initializer import Normal class LeNet5(nn.Cell): """Lenet network structure.""" # define the operator required def __init__(self, num_class=10, num_channel=1): super(LeNet5, self).__init__() #继承父类nn.cell的__init__方法 self.conv1 = nn.Conv2d(num_channel, 6, 5, pad_mode='valid') self.conv2 = nn.Conv2d(6, 16, 5, pad_mode='valid') pad_mode是卷积方式 ‘valid’是pad_mode = 0 self.fc1 = nn.Dense(16 * 5 * 5, 120, weight_init=Normal(0.02)) self.fc2 = nn.Dense(120, 84, weight_init=Normal(0.02)) self.fc3 = nn.Dense(84, num_class, weight_init=Normal(0.02))nn.Dense为致密连接层,它的第一个参数为输入层的维度,第二个参数为输出的维度,第三个参数为神经网络可训练参数W权重矩阵的初始化方式,默认为normal self.relu = nn.ReLU() self.max_pool2d = nn.MaxPool2d(kernel_size=2, stride=2) 将图片的宽度和高度都缩小一半 self.flatten = nn.Flatten() nn.Flatten为输入展成平图层,即去掉那些空的维度 # use the preceding operators to construct networks def construct(self, x): 使用前馈操作构建神经网络 x = self.max_pool2d(self.relu(self.conv1(x))) x = self.max_pool2d(self.relu(self.conv2(x))) x = self.flatten(x) x = self.relu(self.fc1(x)) x = self.relu(self.fc2(x)) x = self.fc3(x) return x network = LeNet5()print("layer conv1:", network.conv1)print("*"*40)print("layer fc1:", network.fc1)4、搭建训练网络并进行训练构建完成神经网络后,就可以着手进行训练网络的构建,模型训练函数为Model.train此步骤案例中使用epoch=1,使用CPU训练大概耗时15分钟,为实现快速训练,可选用更高规格的资源训练。import osfrom mindspore import Tensor, Modelfrom mindspore import load_checkpoint, load_param_into_netfrom mindspore.train.callback import ModelCheckpoint, CheckpointConfig, LossMonitorfrom mindspore.nn.metrics import Accuracyfrom mindspore.nn.loss import SoftmaxCrossEntropyWithLogits lr = 0.01 learingrate,学习率,可以使梯度下降的幅度变小,从而可以更好的训练参数momentum = 0.9 epoch_size = 1 每个epoch需要遍历完成图片的batch数,这里是只要遍历一次mnist_path = "./MNIST_Data"model_path = "./models/ckpt/" # clean up old run files before in Linuxos.system('rm -f {}*.ckpt {}*.meta {}*.pb'.format(model_path, model_path, model_path)) 清理旧文件 # create the networknetwork = LeNet5() 创建神经网络 # define the optimizernet_opt = nn.Momentum(network.trainable_params(), lr, momentum) 使用了Momentum优化器进行优化 # define the loss functionnet_loss = SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')相当于softmax分类器 sparse指定标签(label)是否使用稀疏模式,默认为false,reduction为损失的减少类型:mean表示平均值,一般情况下都是选择平均地减少# define the modelmodel = Model(network, net_loss, net_opt, metrics={"Accuracy": Accuracy()} )调用Model高级API metrics 参数是指训练和测试期# save the network model and parameters for subsequence fine-tuningconfig_ck = CheckpointConfig(save_checkpoint_steps=375, keep_checkpoint_max=16) 保存训练好的模型参数的路径# group layers into an object with training and evaluation featuresckpoint_cb = ModelCheckpoint(prefix="checkpoint_lenet", directory=model_path, config=config_ck) 保存训练好的模型参数的路径 eval_dataset = create_dataset("./MNIST_Data/test") 测试数据集 step_loss = {"step": [], "loss_value": []} 回调函数中的数据格式steps_eval = {"step": [], "acc": []} 回调函数中的数据格式(收集step对应模型精度值accuracy的信息)repeat_size = 1 ds_train = create_dataset(os.path.join(mnist_path, "train"), 32, repeat_size) model.train(epoch_size, ds_train, callbacks=[ckpoint_cb, LossMonitor(125)], dataset_sink_mode=False) 调用Model类的train方法进行训练5、测试数据验证模型精度# testing relate modules def test_net(network, model, mnist_path): 测试神经网络函数 """Define the evaluation method.""" print("============== Starting Testing ==============") 开始测试 # load the saved model for evaluation param_dict = load_checkpoint("./models/ckpt/checkpoint_lenet-1_1875.ckpt") 加载保存的模型以进行评估 # load parameter to the network load_param_into_net(network, param_dict) 向网络加载参数 # load testing dataset ds_eval = create_dataset(os.path.join(mnist_path, "test")) 加载测试数据集 acc = model.eval(ds_eval, dataset_sink_mode=False) 获取测试模型的精确度 print("============== Accuracy:{} ==============".format(acc)) test_net(network, model, mnist_path)6、推理(训练后)ds_test = create_dataset(test_data_path).create_dict_iterator() 测试数据集data = next(ds_test) images = data["image"].asnumpy() 获取数据张量labels = data["label"].asnumpy() 获取数据labels标签 output = model.predict(Tensor(data['image']))pred = np.argmax(output.asnumpy(), axis=1)err_num = [] 错误数字index = 1for i in range(len(labels)): plt.subplot(4, 8, i+1) 图片按照4*8的方式摆放 color = 'blue' if pred[i] == labels[i] else 'red' 对于图片标签颜色的不同,预测正确是蓝色,错误是红色 plt.title("pre:{}".format(pred[i]), color=color) 输出预测标签 plt.imshow(np.squeeze(images[i])) 显示图片 plt.axis("off") if color == 'red': 判断数字是否错误 index = 0 print("Row {}, column {} is incorrectly identified as {}, the correct value should be {}".format(int(i/8)+1, i%8+1, pred[i], labels[i]), '\n')if index: 判断是否所有数字都预测正确 print("All the figures in this group are predicted correctly!") print(pred, "<--Predicted figures") 预测目标数字标签print(labels, "<--The right number") 说明数字正确标签plt.show() 显示图像
-
-
天随着深度学习的不断发展,目标检测技术逐步从基于传统的手工检测方法向基于深度神经网络的检测方法转变。在众多基于深度学习的目标检测算法中,基于深度学习的单阶段目标检测算法因其网络结构较简单、运行速度较快以及具有更高的检测效率而被广泛运用。但现有的基于深度学习的单阶段目标检测方法由于小目标物体包含的特征信息较少、分辨率较低、背景信息较复杂、细节信息不明显以及定位精度要求较高等原因,导致在检测过程中对小目标物体的检测效果不理想,使得模型检测精度降低。针对目前基于深度学习的单阶段目标检测算法存在的问题,研究了大量基于深度学习的单阶段小目标检测技术。首先从单阶段目标检测方法的Anchor Box、网络结构、交并比函数以及损失函数等几个方面,系统地总结了针对小目标检测的优化方法;其次列举了常用的小目标检测数据集及其应用领域,并给出在各小目标检测数据集上的检测结果图;最后探讨了基于深度学习的单阶段小目标检测方法的未来研究方向。http://fcst.ceaj.org/CN/abstract/abstract2963.shtml
-
VGG原理VGG16相比AlexNet的一个改进是采用连续的几个3x3的卷积核代替AlexNet中的较大卷积核(11x11,7x7,5x5)。对于给定的感受野(与输出有关的输入图片的局部大小),采用堆积的小卷积核是优于采用大的卷积核,因为多层非线性层可以增加网络深度来保证学习更复杂的模式,而且代价还比较小(参数更少)。简单来说,在VGG中,使用了3个3x3卷积核来代替7x7卷积核,使用了2个3x3卷积核来代替5*5卷积核,这样做的主要目的是在保证具有相同感知野的条件下,提升了网络的深度,在一定程度上提升了神经网络的效果。3个步长为1的3x3卷积核的一层层叠加作用可看成一个大小为7的感受野(其实就表示3个3x3连续卷积相当于一个7x7卷积),其参数总量为 3x(9xC^2) ,如果直接使用7x7卷积核,其参数总量为 49xC^2 ,这里 C 指的是输入和输出的通道数。很明显,27xC^2小于49xC^2,即减少了参数;而且3x3卷积核有利于更好地保持图像性质。
-
昇思MindSpore Lite 1.5版本我们主要在异构推理、混合精度推理、端侧训练和混合比特权重量化等特性进行了重点优化,在推理性能、模型小型化和端侧训练易用性与性能等方面带来新的体验。下面就带大家快速浏览这些关键特性。 01 多硬件异构推理如何在有限的端侧硬件资源上获得最优推理性能是端侧AI推理框架的主要目标之一。而异构设备混合计算就能够充分利用CPU、GPU、NPU等异构硬件资源,充分利用资源的算力与内存,从而达到端侧推理的极致性能。本次我们在1.5版本中支持了多硬件异构推理的能力。用户可以设置对外开放的mindspore::Context使能不同后端异构硬件,包括CPU、GPU、NPU。并且用户可以根据需要设置各个硬件的优先级。实现了Mindspore Lite在多种异构硬件上的灵活部署,和高性能推理。早期版本Mindspore Lite只支持CPU+的模式,只能支持CPU与NPU或者CPU与GPU的异构执行,并且无法选择异构硬件的优先级。当前最新版本的多硬件异构推理特性,给了用户更多的异构硬件选择,增加了易用性,对异构硬件上的推理更加友好。多硬件异构推理特性使用的资料请参考:https://www.mindspore.cn/lite/docs/zh-CN/r1.5/use/runtime_cpp.html#id3 02 混合精度推理 我们经常发现用户在使用float16对模型进行推理的时候出现精度误差过大的问题,其原因可能是部分数据较小,使用float16能表达的精度较低导致误差过大,也可能是因为部分数据过大超出了float16的表达范围。 针对这类问题,我们在最新版本中提供了混合精度推理特性支持。用户可以指定模型中具体算子的推理精度,目前可选的精度有float32、float16两种。该特性可以解决部分模型无法整网使用float16进行推理的问题,用户可以将模型中因为数据原因导致的使用float16推理精度误差的部分算子,转为使用float32进行推理,从而确保整网推理精度。这样可以在使用高性能float16算子的基础上,保证了模型的推理精度提高。混合精度推理特性使用的资料请参考:https://www.mindspore.cn/lite/docs/zh-CN/r1.5/use/runtime_cpp.html#id13 03 端侧混合精度训练 为了在确保训练精度的前提下尽可能地提升端侧训练性能,我们在1.5版本中支持了端侧混合精度训练特性,实现了包括1)支持将fp32模型在线转成fp16混合精度模型,以及2)直接无缝运行MindSpore导出混合精度模型两种方案。 第一种方案,可以根据传入的traincfg实现自动混合精度,并提供了不同优化级别(O0不改变算子类型,O2将算子转成fp16模型,batchnorm和loss保持fp32,O3在O2基础上将batchnorm和loss转成fp16),也支持配置fp16算子自定义配置手动混合精度方案,提供了性能和精度之间的选择,更具灵活性。其计算流程如下: 可参考相关API:https://www.mindspore.cn/lite/api/zh-CN/r1.5/api_cpp/mindspore.html#traincfg第二种方案,支持将MindSpore导出的混合精度模型导出后在端侧直接进行增量训练,相比fp32训练内存、耗时均有明显提升,平均提升20-40%。 04 端侧训练支持resize 由于端侧内存限制原因,训练的模型batch不能过大,因此需要对输入模型的batch进行可配置,因此我们在这个版本中支持了将训练模型resize的能力。我们为训练算子增加了resize接口,同时在框架增加支持了训练内存的动态分配。使用resize接口只需要传入resize input shape。可参考相关API:https://www.mindspore.cn/lite/api/zh-CN/r1.5/api_cpp/mindspore.html#resize 05 端侧训练支持在线和离线融合 端侧训练大部分是finetune模型,其中大部分层都会被冻结,权重参数不会在训练过程中改变,因此可以通过在离线阶段对这些冻结层进行相关的融合优化来提升训练性能。我们通过分析模型节点的连接关系,实现了对冻结层进行动态识别融合优化点的方案,从而使训练性能得到提升显著。我们通过对Effnet网络训练进行试验,从结果看耗时可以节省约40%,训练内存开销降低30%。同时我们支持在训练结束后,在端侧训练结束导出推理模型时进行在线融合,无需额外进行离线优化,用户可以直接使用此模型进行在线推理,由于复用了离线融合pass,动态库仅增加76k,保证了端侧训练库的轻量性。 06 混合比特权重量化 针对模型大小有严格要求的场景,权重量化(weight quantization)无需数据集,即可直接将权重数据从浮点型映射到低比特的定点数据,方便模型的传输和存储。而传统的权重模型量化方法都是将整个模型的权重值都固定量化到某个特定的比特位,例如8比特量化是将Float32的浮点数映射到Int8的定点数,理论上模型可达到4倍压缩,但是这种方法存在一个问题:高比特的量化能保证高精度,但需要更大的存储空间占用;而低比特的存储空间占用更小,但会存在更大的精度损失。MindSpore Lite在1.5版本中支持了混合比特权重量化,根据神经网络的数据分布情况,自动搜索出最适合当前层的量化比特位,能够有效在压缩率和精度之间达到细粒度的trade-off,实现对模型的高效压缩。由于神经网络不同层对量化损失的敏感度不同,可以将损失敏感度较低的层用更低的比特位表示,而对损失敏感度较高的层用更高的比特位表示。MindSpore Lite的混合比特量化采用均方误差作为优化目标,自动搜索出最适合当前层的scale值。针对量化后的模型,同时采用有限状态熵(Finite State Entropy, FSE)对量化后的权重数据进行熵编码,可以进一步获得更大倍率的压缩,最高可达50+倍。具体功能使用请参考: https://www.mindspore.cn/lite/docs/zh-CN/r1.5/use/post_training_quantization.html#id9 MindSpore官方资料GitHub : https://github.com/mindspore-ai/mindsporeGitee : https : //gitee.com/mindspore/mindspore官方QQ群 : 871543426
-
一、实验目的 学会使用MindSpore进行简单卷积神经网络的开发。学会使用MindSpore进行CIFAR-10数据集分类任务的训练和测试。可视化学习到的特征表达器,和手工定义的特征进行分析和比较实验框架mindspore + pytorch(特征可视化)实验数据集CIFAR103.1 CIFAR10简介CIFAR-10数据集由10个类的60000个32x32彩色图像组成,每个类有6000个图像。10个类完全相互排斥,且类之间没有重叠,汽车和卡车之间没有重叠。“汽车”包括轿车,SUV等。“卡车”只包括大卡车,不包括皮卡车。以下是10个类别类的名字: airplane/automobile/bird/cat/deer/dog/frog/horse/ship/truck。 3.2 数据集下载根据mindspore官网提示使用命令下载并解压数据集到相应位置:wget -N https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/cifar-10-binary.tar.gz --no-check-certificatetar -zxvf cifar-10-binary.tar.gz -C ./datasets下载好的cifar-10-batches-bin文件包含一个操作说明readme.html,一个写有类别名称的batches.meta.txt,以及5个训练集和1个测试集bin文件 本次实验不单独划分验证集而直接使用测试集进行val操作。在cifar-10-batches-bin文件目录下创建两个文件夹分别命名为train和test,将训练集和测试机分别放入相应文件夹下,方便后续数据集加载与处理。3.3 数据集预处理与增强将数据预处理与增强操作写在函数def create_dataset(data_path, batch_size=32, repeat_size=1,num_parallel_workers=1, training=True)中调用相应数据集时将数据集目录传入函数data_path中,例如:# 加载训练数据集data_path = "./datasets/cifar-10-batches-bin"ds_train = create_dataset(os.path.join(data_path, "train"), 32, repeat_size, training=True)ds_test = create_dataset(os.path.join(data_path, "test"), 32, training=False)在函数create_dataset中首先定义数据集及数据增强参数大小。resize_height, resize_width分别为图像放大后的长和宽。在使用Lent训练时采用长和宽为32,其原因是将图片放大到224,224后,并不能提升Lenet的训练准确率,还使得训练时间增加。rescale操作是将原本像素值为0-255映射到0-1之间,符合网络训练对数据集的要求。# 定义数据集 cifar10_ds = ds.Cifar10Dataset(data_path) resize_height, resize_width = 32, 32 #resize_height, resize_width = 224, 224 rescale = 1.0 / 255.0 shift = 0.0 rescale_nml = 1 / 0.3081 shift_nml = -1 * 0.1307 / 0.3081进行数据增强操作需要引用两个头文件,具体的操作在注释中给出:import mindspore.dataset.transforms.c_transforms as Cimport mindspore.dataset.vision.c_transforms as CV#图像大小缩放 resize_op = CV.Resize((resize_height, resize_width), interpolation=Inter.LINEAR) #将给定图像随机裁剪为不同的大小和宽高比,然后缩放所裁剪得到的图像为制定的大小 crop_op = c_vision.RandomCrop((32, 32), (4, 4, 4, 4)) #像素值调整 rescale_nml_op = CV.Rescale(rescale_nml, shift_nml) rescale_op = CV.Rescale(rescale, shift) #归一化处理 normalize_op = CV.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)) #图片HWC格式转CHW格式 hwc2chw_op = CV.HWC2CHW() #标签数据类型由int8转换为int32 type_cast_op = C.TypeCast(mstype.int32)随后使用map映射函数,将数据操作应用到数据集。这里使用list的方式映射,使得代码更简洁。需要注意crop操作只需要对训练集进行,对传入的training进行判断:# 使用map映射函数,将数据操作应用到数据集 transforms_list=[] if training: transforms_list+=[crop_op] transforms_list+=[resize_op,rescale_nml_op,rescale_op,normalize_op,hwc2chw_op] cifar10_ds = cifar10_ds.map(operations=type_cast_op, input_columns="label", num_parallel_workers=num_parallel_workers) cifar10_ds = cifar10_ds.map(operations=transforms_list, input_columns="image", num_parallel_workers=num_parallel_workers)最后一部,对数据集打乱和分批,训练集/测试集数据就处理好了。ds.config.set_seed(58) # 进行shuffle、batch操作 buffer_size = 10000 # 随机打乱数据顺序 cifar10_ds = cifar10_ds.shuffle(buffer_size=buffer_size) # 对数据集进行分批cifar10_ds = cifar10_ds.batch(batch_size, drop_remainder=True)return cifar10_ds模型简述4.1 Lenet5LeNet-5出自论文Gradient-Based Learning Applied to Document Recognition,是一种用于手写体字符识别的卷积神经网络。LeNet5 的网络就今天而言虽然很小,但是包含了深度学习的基本模块有:卷积层,池化层,全链接层,是其他深度学习模型的基础。LeNet-5共有7层,其中有2个卷积层、2个下抽样层(池化层)、3个全连接层,每层都包含可训练参数;每个层有多个Feature Map,每个Feature Map有多个神经元并通过一种卷积滤波器提取输入的一种特征,结构如下 LeNet-5第一层:卷积层C1C1层是卷积层,形成6个特征图谱。卷积的输入区域大小是5x5,每个特征图谱内参数共享,即每个特征图谱内只使用一个共同卷积核,卷积核有5x5个连接参数加上1个偏置共26个参数。卷积区域每次滑动一个像素,这样卷积层形成的每个特征图谱大小是(32-5)/1+1=28x28。LeNet-5第二层:池化层S2S2层是一个下采样层,利用图像局部相关性的原理,对图像进行子抽样,减少数据处理量同时保留有用信息。C1层的6个28x28的特征图谱分别进行以2x2为单位的下抽样得到6个14x14的图。每个特征图谱使用一个下抽样核。LeNet-5第三层:卷积层C3C3层是一个卷积层,卷积核和C1相同,不同的是C3的每个节点与S2中的多个图相连。C3层有16个10x10的图,他采取的不对称的组合连接方式有利于提取多种组合特征。LeNet-5第四层:池化层S4S4是一个下采样层。C3层的16个10x10的图分别进行以2x2为单位的下抽样得到16个5x5的图。5x5x5x16=2000个连接。连接的方式与S2层类似。LeNet-5第五层:全连接层C5C5层是一个全连接层。由于S4层的16个图的大小为5x5,与卷积核的大小相同,所以卷积后形成的图的大小为1x1。这里形成120个卷积结果。每个都与上一层的16个图相连。LeNet-5第六层:全连接层F6F6层是全连接层。F6层有84个节点,对应于一个7x12的比特图。该层的训练参数和连接数是(120 + 1)x84=10164。LeNet-5第七层:全连接层OutputOutput层也是全连接层,共有10个节点,分别代表数字0到9。该层有84x10=840个参数和连接。4.2 resnet50resnet可以说是卷积神经网络结构的一次飞跃。其使用了Residual net(残差网络)。残差网络将靠前若干层的某一层数据输出直接跳过多层引入到后面数据层的输入部分。这意味着后面的特征层的内容会有一部分由其前面的某一层线性贡献。残差网络很好的解决了深度神经网络的退化问题,其结构如下: resnet使用的block结构使用多个小的卷积核代替大的卷积核的方法,使得输出尺寸不变的同时训练的时间大大缩短,这也是resenet的优势。resnet50有两个基本的块,分别名为Conv Block和Identity Block,其中Conv Block输入和输出的维度是不一样的,所以不能连续串联,它的作用是改变网络的维度;Identity Block输入维度和输出维度相同,可以串联,用于加深网络的。他们的结构如下: 训练过程与代码5.1 Lenet5模型代码根据mindspore官网的提示,我们可以较为容易地构建出Lenet5 网络结构模型代码:class LeNet5(nn.Cell): def __init__(self, num_class=10, num_channel=3): super(LeNet5, self).__init__() # 定义所需要的运算 self.conv1 = nn.Conv2d(num_channel, 6, 5, pad_mode='valid') self.conv2 = nn.Conv2d(6, 16, 5, pad_mode='valid') self.fc1 = nn.Dense(16 * 5 * 5, 120) self.fc2 = nn.Dense(120, 84) self.fc3 = nn.Dense(84, num_class) self.relu = nn.ReLU() self.max_pool2d = nn.MaxPool2d(kernel_size=2, stride=2) self.flatten = nn.Flatten() def construct(self, x): # 使用定义好的运算构建前向网络 x = self.conv1(x) x = self.relu(x) x = self.max_pool2d(x) x = self.conv2(x) x = self.relu(x) x = self.max_pool2d(x) x = self.flatten(x) x = self.fc1(x) x = self.relu(x) x = self.fc2(x) x = self.relu(x) x = self.fc3(x) return x5.2 训练代码在开始训练时,我们首先定义全局变量train_epoch为我们要训练的epoch,并将使用显卡训练的信息引入mindspore:#运行信息os.environ['CUDA_VISIBLE_DEVICES'] = '0'context.set_context(mode=context.GRAPH_MODE, device_target="GPU")context.set_context(device_id=0) train_epoch = 50随后在main函数中将Lenet5网络实例化,并定义算损失函数与优化器。在定义好数据地址与模型保存地址等信息后,调用train_net函数进行训练。学习率定为0.01,优化器选择了Momentum即动量优化器。if __name__ == '__main__': #网络实例化 net = LeNet5() # 定义损失函数 net_loss = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean') #定义优化器 lr = 0.01 momentum = 0.9 net_opt = nn.Momentum(net.trainable_params(), learning_rate=lr, momentum=momentum) dataset_size = 1 model = Model(net, net_loss, net_opt, metrics={"Accuracy": Accuracy()}) #数据处理 cifar10_path = "./datasets/cifar-10-batches-bin" model_path = "./models" # 设置模型保存参数 config_ck = CheckpointConfig(save_checkpoint_steps=1875, keep_checkpoint_max=10) # 应用模型保存参数 ckpoint = ModelCheckpoint(prefix="checkpoint_lenet",directory=model_path, config=config_ck) train_net(model, train_epoch, cifar10_path, dataset_size, ckpoint, False)在train_net函数中调用create_dataset函数创建数据集,随后将需要打印的信息传入继承了Callback类的EvalCallBack后,使用model.train语句开始训练:def train_net(model, epoch_size, data_path, repeat_size, ckpoint_cb, sink_mode): """定义训练的方法""" # 加载训练数据集 ds_train = create_dataset(os.path.join(data_path, "train"), 32, repeat_size, training=True) ds_test = create_dataset(os.path.join(data_path, "test"), 32, training=False) #打印信息 steps_loss = {"step": [], "loss_value": []} steps_eval = {"step": [], "acc": []} eval_param_dict = {"model":model,"dataset":ds_test,"metrics_name":"Accuracy"} step_loss_acc_info = EvalCallBack(apply_eval, eval_param_dict,model , ds_test, steps_loss, steps_eval) model.train(epoch_size, ds_train, callbacks=[ckpoint_cb, LossMonitor(125),step_loss_acc_info], dataset_sink_mode=sink_mode)EvalCallBack类继承了Callback类的__init__、step_end、epoch_end、end类函数,其作用分别是:__init__根据传入的参数初始化类;step_end在每一step完成后进行操作;epoch_end在每一epoch完成后进行操作;end在训练结束时进行操作。class EvalCallBack(Callback): def __init__(self, eval_function, eval_param_dict,model, eval_dataset, steps_loss, steps_eval,interval=1,eval_start_epoch=1,save_best_ckpt=True,ckpt_directory="./model/", besk_ckpt_name="best.ckpt", metrics_name="acc"): self.eval_param_dict = eval_param_dict self.eval_function = eval_function self.model = model self.eval_dataset = eval_dataset self.steps_loss = steps_loss self.steps_eval = steps_eval self.eval_start_epoch = eval_start_epoch self.interval = interval self.save_best_ckpt = save_best_ckpt self.best_res = 0 self.best_epoch = 0 if not os.path.isdir(ckpt_directory): os.makedirs(ckpt_directory) self.best_ckpt_path = os.path.join(ckpt_directory, besk_ckpt_name) self.metrics_name = metrics_name self.loss_d=[] self.res_d=[] def step_end(self, run_context): cb_params = run_context.original_args() cur_epoch = cb_params.cur_epoch_num cur_step = (cur_epoch-1)*1562 + cb_params.cur_step_num self.steps_loss["loss_value"].append(str(cb_params.net_outputs)) self.steps_loss["step"].append(str(cur_step)) # 删除ckpt文件 def remove_ckpoint_file(self, file_name): os.chmod(file_name, stat.S_IWRITE) os.remove(file_name) # 每一个epoch后,打印训练集的损失值和验证集的模型精度,并保存精度最好的ckpt文件 def epoch_end(self, run_context): cb_params = run_context.original_args() cur_epoch = cb_params.cur_epoch_num loss_epoch = cb_params.net_outputs if cur_epoch >= self.eval_start_epoch and (cur_epoch - self.eval_start_epoch) % self.interval == 0: res = self.eval_function(self.eval_param_dict) print('Epoch {}/{}'.format(cur_epoch, train_epoch)) print('-' * 10) print('train Loss: {}'.format(loss_epoch)) print('val Acc: {}'.format(res)) if res >= self.best_res: self.best_res = res self.best_epoch = cur_epoch if self.save_best_ckpt: if os.path.exists(self.best_ckpt_path): self.remove_ckpoint_file(self.best_ckpt_path) save_checkpoint(cb_params.train_network, self.best_ckpt_path) self.loss_d.append(loss_epoch.asnumpy()) self.res_d.append(res) # 训练结束后,打印最好的精度和对应的epoch def end(self, run_context): print("End training, the best {0} is: {1}, the best {0} epoch is {2}".format(self.metrics_name,self.best_res,self.best_epoch), flush=True) x=np.arange(1,train_epoch+1) _,ax1=plt.subplots() ax2=ax1.twinx() ax1.plot(x,self.loss_d, color="red",label='train loss') ax2.plot(x,self.res_d, color="blue",label='val Acc') ax1.set_xlabel('epoch') ax1.set_ylabel('train loss') ax2.set_ylabel('val Acc') ax1.legend(loc='best') ax2.legend(loc='best') plt.savefig(fname="lenet1_trainning.png")我们在train_net函数中向EvalCallBack传入的eval_function为:# 模型验证def apply_eval(eval_param): eval_model = eval_param['model'] eval_ds = eval_param['dataset'] metrics_name = eval_param['metrics_name'] res = eval_model.eval(eval_ds) return res[metrics_name]开始训练后,在执行一定的step后会返回实时的loss值;在每一个epoch完成后,会返回实时的训练loss值与验证集准确率;训练结束后会将训练过程中loss的变化曲线与准确率的变化曲线画成曲线图并保存。训练过程中的输出信息示例如下: 5.3 模型改进步骤一在原有Lenet的结构中将第一层的输入通道数改为3后直接训练,在训练到第10个epoch时准确率达到55%,随后继续训练即使loss一直在降低,准确率并没有上升甚至有所下滑。初步判定为Lenet网络结构较浅,对于三通道输入的图片并不能很好地提取特征。步骤二在原有Lenet的结构基础上进行模型的微调,依旧只使用卷积层、池化层、全连接层。将卷积层的网络结构加深,在第一个卷积层中将三通道的输入图片使用大小(32,3,3,3)的卷积核,输出32通道的特征,之后进一步加深网络。最终第一个全连接层的输出特征大小为(32*8*8)。卷积层个数在原有2个的基础上增加一个,各网络层的参数也相应变化。此方法由于加深了卷积层网络通道数,loss降低速度较步骤一Lenet5原有结构而言稍微缓慢,但由于深度的增加、卷积核参数增加,准确率在训练到30个epoch时达到了70%。但随之而来的问题是训练的继续进行,loss的进一步下降带来的却是准确率的飞速下降,并在训练到40个epoch时变为10%,而对于十分类而言10%的准确率说明出现了过拟合的现象,模型已经失效。class LeNet5(nn.Cell): def __init__(self, num_class=10, num_channel=3): super(LeNet5, self).__init__() # 定义所需要的运算 self.conv1 = nn.Conv2d(num_channel, 32, kernel_size=3, stride=1, pad_mode="same", weight_init='XavierUniform') self.conv2 = nn.Conv2d(32, 32, 3, stride=1, pad_mode="same") self.conv3 = nn.Conv2d(32, 32, 3, stride=1, pad_mode="same") self.fc1 = nn.Dense(32*8*8, 400) self.fc2 = nn.Dense(400, 120) self.fc3 = nn.Dense(120, num_class) self.relu = nn.ReLU() self.max_pool2d = nn.MaxPool2d(kernel_size=2, stride=2) self.flatten = nn.Flatten() def construct(self, x): # 使用定义好的运算构建前向网络 x = self.conv1(x) x = self.relu(x) x=self.conv2(x) x = self.relu(x) x = self.max_pool2d(x) x = self.conv3(x) x = self.relu(x) x = self.max_pool2d(x) x = self.flatten(x) x = self.fc1(x) x = self.relu(x) x = self.fc2(x) x = self.relu(x) x = self.fc3(x) return x步骤三随后由于出现过拟合现象,我们进一步增加卷积层与网络深度,但在增加了一个卷积层并将最终输出特征通道数定为64时,loss从训练开始遍无法下降,准确率也相应没有提升。为此我们初步采取使用不同的初始化手段如正态分布、Xavier对卷积层参数初始化,调整学习率和优化器参数等方法改变训练超参数,但都没有效果,loss依旧无法下降。于是我们打算从网络结构入手进行调整,由于考虑到网络无法有效训练可能是网络参数过多导致的,我们在引入BN(batch_norm)的基础上增加dropout层。这里需要注意的是mindspore的dropout层参数与pytorch不同的是,mindspore定义的dropout层参数是留下来的参数比例,这和pytorch是相反的,及mindspore中nn.Dropout(0.75)代表的是留下75%的参数。于是乎我们的网络结构也是在对比了其他信息最终我们采用的网络结构有4个卷积层,每个卷积层之间由BN层与Relu层连接,每两个卷积层后跟随着一个kernel_size=2的池化层使特征的size变为原本的四分之一,每个池化层后跟随着一个dropout层。BN层的加入可以使训练过程更加的稳定,对权重的初始化、梯度不会过于敏感,同时在不依赖dropout层的情况下减轻了过拟合的风险。准确率在网络训练的第一个epoch就达到了45%,在50个epoch训练结束后,准确率达到了80.3%网络训练过程的loss与准确率变化如下图所示。class LeNet5(nn.Cell): def __init__(self, num_class=10, num_channel=3): super(LeNet5, self).__init__() # 定义所需要的运算 self.conv1 = nn.Conv2d(num_channel, 32, kernel_size=3, stride=1, pad_mode="same", weight_init='XavierUniform') self.conv2 = nn.Conv2d(32, 32, 3, stride=1, pad_mode="same", weight_init='XavierUniform') self.conv3 = nn.Conv2d(32, 64, 3, stride=1, pad_mode="same", weight_init='XavierUniform') self.conv4 = nn.Conv2d(64, 64, 3, stride=1, pad_mode="same", weight_init='XavierUniform') self.conv5 = nn.Conv2d(64, 128, 3, stride=1, pad_mode="same", weight_init='XavierUniform') self.conv6 = nn.Conv2d(128, 128, 3, stride=1, pad_mode="same", weight_init='XavierUniform') self.fc1 = nn.Dense(128*28*28, 4096, weight_init='XavierUniform', bias_init='Uniform') self.fc2 = nn.Dense(4096, 400, weight_init='XavierUniform', bias_init='Uniform') self.fc3 = nn.Dense(400, num_class, weight_init='XavierUniform', bias_init='Uniform') self.relu = nn.ReLU() self.max_pool2d = nn.MaxPool2d(kernel_size=2, stride=2) self.flatten = nn.Flatten() self.bn1 = nn.BatchNorm2d(32, momentum=0.99, eps=0.00001, gamma_init='Uniform', beta_init=Tensor(np.zeros(32).astype(np.float32)), moving_mean_init=Tensor(np.zeros(32).astype(np.float32)), moving_var_init=Tensor(np.ones(32).astype(np.float32))) self.bn2 = nn.BatchNorm2d(32, momentum=0.99, eps=0.00001, gamma_init='Uniform', beta_init=Tensor(np.zeros(32).astype(np.float32)), moving_mean_init=Tensor(np.zeros(32).astype(np.float32)), moving_var_init=Tensor(np.ones(32).astype(np.float32))) self.bn3 = nn.BatchNorm2d(64, momentum=0.99, eps=0.00001, gamma_init='Uniform', beta_init=Tensor(np.zeros(64).astype(np.float32)), moving_mean_init=Tensor(np.zeros(64).astype(np.float32)), moving_var_init=Tensor(np.ones(64).astype(np.float32))) self.bn4 = nn.BatchNorm2d(64, momentum=0.99, eps=0.00001, gamma_init='Uniform', beta_init=Tensor(np.zeros(64).astype(np.float32)), moving_mean_init=Tensor(np.zeros(64).astype(np.float32)), moving_var_init=Tensor(np.ones(64).astype(np.float32))) self.bn5 = nn.BatchNorm2d(128, momentum=0.99, eps=0.00001, gamma_init='Uniform', beta_init=Tensor(np.zeros(128).astype(np.float32)), moving_mean_init=Tensor(np.zeros(128).astype(np.float32)), moving_var_init=Tensor(np.ones(128).astype(np.float32))) self.bn6 = nn.BatchNorm2d(128, momentum=0.99, eps=0.00001, gamma_init='Uniform', beta_init=Tensor(np.zeros(128).astype(np.float32)), moving_mean_init=Tensor(np.zeros(128).astype(np.float32)), moving_var_init=Tensor(np.ones(128).astype(np.float32))) self.dropout=nn.Dropout(0.75) def construct(self, x): # 使用定义好的运算构建前向网络 x = self.conv1(x) x=self.bn1(x) x = self.relu(x) x=self.conv2(x) x=self.bn2(x) x = self.relu(x) x = self.max_pool2d(x) x=self.dropout(x) x = self.conv3(x) x=self.bn3(x) x = self.relu(x) x = self.conv4(x) x=self.bn4(x) x = self.relu(x) x = self.max_pool2d(x) x=self.dropout(x) x = self.conv5(x) x=self.bn5(x) x = self.relu(x) x = self.conv6(x) x=self.bn6(x) x = self.relu(x) x = self.max_pool2d(x) x=self.dropout(x) x = self.flatten(x) x = self.fc1(x) x = self.relu(x) x = self.fc2(x) x = self.relu(x) x = self.fc3(x) return x 步骤四在尝到了一点甜头后,我们希望在加入了BN和dropout层的基础上进一步增加卷积层数量与网络深度。参照resnet对CIFAR10的做法,我们在数据预处理时将图像由原本的(32x32x3) resize为(224x224x3)。增加卷积层个数到6个并相应增加其他层的个数。加深网络深度,在最后一个卷积层输出通道数为256的特征图。然而此举并没有明显的准确率增加,带来的反而是准确率上升速率变慢,模型训练速率变慢。步骤三训练一个epoch的时间约为1秒而到了这里训练一个epoch需要2分钟。此过程可能还伴随着梯度等问题得不偿失,于是我们决定采用步骤三的网络结构进行之后的可视化操作。上述网络的训练loss与准确率变化图如下 步骤五步骤四说明了简单粗暴地对网络结构进行叠加和对网络深度进行加深并不能就此换来准确率地增加,其可能带来梯度衰减、顶层过拟合等种种问题。为了进一步提升准确率我们可以采取地操作有:1.增加训练集以及相应数据增强操作 2.使用更先进的网络结构在此我们希望采用resnet50的结构,引入block小卷积核的概念并使用残差网络。但此举和Lenet5的结构相差有点多且将resnet的结构生搬硬套以改变Lenet5的做法虽然对准确率的提高可能有所帮助但不太道德。为此我们在训练代码的模型引入阶段进入resnet50的网络,此网络并不包含预训练过程以及对CIFAR10相应的结构改善,我们使用和之前一样的数据增强和处理操作训练50个epoch后得到的准确率为90.3%,可以看出resnet确实是分类网络历史上智慧的一个飞跃,训练loss与准确率曲线如下,其训练一个epoch的时间与步骤四的结构训练时间相当约为2分钟。 可视化结果6.1 测试集预测结果与卷积核可视化在visualize.py中我们复用原有的数据集处理操作以及步骤三中的网络结构代码,并加载训练好的模型best.ckpt,对测试集进行结果预测与可视化并对模型best.ckpt的卷积核进行输出与可视化。函数visualize_model代码如下def visualize_model(best_ckpt_path,val_ds): # 定义网络并加载参数,对验证集进行预测 net = LeNet5() param_dict = load_checkpoint(best_ckpt_path) load_param_into_net(net, param_dict) loss = nn.SoftmaxCrossEntropyWithLogits(sparse=True,reduction='mean') model = Model(net, loss,metrics={"Accuracy":nn.Accuracy()}) data = next(val_ds.create_dict_iterator()) images = data["image"].asnumpy() labels = data["label"].asnumpy() with open(cifar10_path+"/batches.meta.txt","r",encoding="utf-8") as f: class_name = [name.replace("\n","") for name in f.readlines()] output = model.predict(Tensor(data['image'])) pred = np.argmax(output.asnumpy(),axis=1) # 可视化模型预测 for i in range(len(labels)): plt.subplot(4,8,i+1) color = 'blue' if pred[i] == labels[i] else 'red' plt.title('{}'.format(class_name[pred[i]]), color=color) picture_show = np.transpose(images[i],(1,2,0)) picture_show = picture_show/np.amax(picture_show) picture_show = np.clip(picture_show, 0, 1) plt.axis('off') plt.imshow(picture_show) plt.savefig(fname="./label/batch_"+str(i)+'.png') acc = model.eval(val_ds) print("acc:",acc) print(type(net.parameters_and_names())) for i, j in net.parameters_and_names(): wc = j wc=wc.flatten() pic_show=np.zeros(len(wc)) for q in range(len(wc)): pic_show[q]=wc[q].asnumpy() pic_show=np.resize(pic_show,(32,27)) pic_show=autoNorm(pic_show) print(pic_show) plt.matshow(pic_show, cmap=plt.cm.gray) plt.title(i) plt.colorbar() plt.savefig(fname="./conv/"+i+'.png') break其输出了一个测试集batch中图片的预测结果,其中红色的字体代表预测结果与图片label不同,蓝色则代表模型预测分类正确 下面是输出的训练好的模型各网络层包括卷积层、池化层等的结构与参数 对conv1.weight参数进行归一化后平铺为32x27的灰度图进行输出
推荐直播
-
HDC深度解读系列 - Serverless与MCP融合创新,构建AI应用全新智能中枢2025/08/20 周三 16:30-18:00
张昆鹏 HCDG北京核心组代表
HDC2025期间,华为云展示了Serverless与MCP融合创新的解决方案,本期访谈直播,由华为云开发者专家(HCDE)兼华为云开发者社区组织HCDG北京核心组代表张鹏先生主持,华为云PaaS服务产品部 Serverless总监Ewen为大家深度解读华为云Serverless与MCP如何融合构建AI应用全新智能中枢
回顾中 -
关于RISC-V生态发展的思考2025/09/02 周二 17:00-18:00
中国科学院计算技术研究所副所长包云岗教授
中科院包云岗老师将在本次直播中,探讨处理器生态的关键要素及其联系,分享过去几年推动RISC-V生态建设实践过程中的经验与教训。
回顾中 -
一键搞定华为云万级资源,3步轻松管理企业成本2025/09/09 周二 15:00-16:00
阿言 华为云交易产品经理
本直播重点介绍如何一键续费万级资源,3步轻松管理成本,帮助提升日常管理效率!
回顾中
热门标签