PyTorch + OpenVINO 工具套件开发实战系列教程

3. 图象分类

通过前面两章的学习,我们已经了解 Pytorch 框架跟 OpenVINO™ 工具套件框架的用途,并且搭建好了开发环境,为本章学习做好了准备工作。本章介绍计算机视觉的基础任务之一图象分类的基本概念、深度学习对图象分类任务的推进与影响,常用的图象分类网络,基准数据集。Pytorch 框架自带的图象分类预训练模型库中模型、使用预训练模型实现图象分类、如何导出模型为 ONNX 格式,使用 OpenVINO™ 工具套件完成推理部署。最后通过自定义数据,基于 pytorch 的模型库实现迁移学习,实现一个自定义数据集的图象分类,并导出模型为 ONNX 格式使用 OpenVINO™ 工具套件完成部署。

本章内容有大量的代码演示环节,源代码也是本书内容的一部分,读者只有亲自动手才会理解与掌握相关的函数知识点,更好的理解本章内容。现在就让我们开启本章内容的学习…

3.1 图象分类概述

图象分类是计算机视觉基础任务之一,应用十分广泛,从手机中的图象自动分类、到各类数字图象归档、以图搜图的搜索引擎等应用中底层都依赖于图象分类算法,早期的图象分类算法都是手工提取特征数据,然后通过机器学习对特征数据分类从而实现图象分类,这类算法的缺点就是准确率比较低、很难在开放场景中大规模普及应用。直到2012年以AlexNet 为代表的深度学习技术应用在图象分类中取得了良好的效果,大幅度提升了图象分类的准确性与稳定性。在随后的几年中不同的深度学习模型不断刷新图象分类基准数据集(ImageNet)准确率与稳定性,得益于底层算法的有力支持,图象分类应用也呈现一片繁荣。

3.1.1 图象分类概念

图象分类的目标是自动归类图象到已知的标签类别,从算法类型上看有两种图象分类技术分别是监督分类与无监督分类。正常的图象分类算法分为两步,第一步是训练、第二步是测试,其中训练就通过提取图象特征,然后完成基于特征数据的标签分类;测试是对训练生成的模型通过测试数据验证模型有效性;只有根据测试数据上取得的效果如何才能判别训练方法的正确与否。

在深度学习出现与大规模应用之前,图象分类的训练过程很难实现完成自动化,在特征提取中主要是通过手工特征提取方法实现,这些方法主要包括 SIFT、HOG、以及它们组合构成 BOW(词袋)等传统图象特征提取方法,然后再通过机器学习方法对特征数据进行分类,预测不同的标签,完成图象分类。基于的深度学习方法则完全颠覆了传统图象分类技术,它基于卷积神经网络(CNN)实现自动特征提取,大大突破了传统手工特征提取的限制,提取到了更加全面与有效的特征信息,然后再基于特征数据完成分类预测,整个训练过程不需要认为干预而且相比传统图象分类方法,CNN 大幅提升图象分类准确率与稳定性。传统方法与 CNN 图象分类算法示意图如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第1张图片
图 3-1 中展示了传统算法与深度学习算法实现图象分类不同的处理过程主要在于特征提取部分,传统算法采用手动特征提取,而且深度学习基于 CNN 网络完成特征提取,最终都得到一维特征向量作为输入,经过 MLP(多层感知器 / 人工神经网络)预测分类,跟标签对比。如果预测结果跟标签差异较大则继续训练,直到预测结果跟标签接近(这个过程被称为训练收敛过程)。这样的到的模型可以在测试数据上进行测试,验证模型训练的效果。

3.1.2 ImageNet 图象分类竞赛

深度学习推动了图象分类算法精度不断提升,在这个过程中也出现了多种经典的卷积神经网络模型,说到这些经典卷积神经网络模型就不得不说它们训练与测试的基准数据集。这些早期的经典模型都通过参加 ImageNet 图象分类竞赛表现出卓越的精度,为业界熟知获得高度关注。

这里不得不首先介绍一下 ImageNet 基准数据集,该数据集是在 2009 年正式发布提供给外界使用,同步发布的还有基于该数据集的一系列计算机视觉任务竞赛,这其中就包括了图象分类比赛。ImageNet 数据集在 2009 年发布时图象总计有5247 个不同类别总计超过 320 万张图片,后续两年数据集规模超过了 500 万张,至今 ImageNet 数据集仍然是计算机视觉领域最重要的基准数据集之一。

ImageNet 相关的图象分类竞赛总计举办八届,图象分类预测的错误率(top-5)也从最初2009你那开始的28.2%逐年下降,2017 年更是达到惊人的 2.25% 错误率。竞赛已经完全达到了最初设立提升图象分类准确率的目标,而且远超人类的 5%的上限值,再也没有继续举办下去的必要的。ImageNet 图象分类竞赛与历年的冠军模型预测错误率图示如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第2张图片
图 3-2 中 Human 5% 表示人类的认知水平,图象分类预测top-5 错误率在 5%,2015 年以前的模型都没有超过人类的认知水平、而 2015 年极其之后的模型都已经远超人类的认知水平。

3.1.3 图象分类模型

ImageNet 图象分类竞赛产生了很多经典的模型,这些模型背后是一些典型的模型设计思想,其中两个最常用的经典模型系列是 VGG 模型系列与 ResNet 模型系列。

●VGG 系列模型

VGG 系列模型是 2014 年 ImageNet 图象分类竞赛表现杰出的模型之一,它在模型设计跟模型深度方面较 2014 年之前的模型都有创新,特别是大量改用 3x3 卷积,有效减少了模型参数与计算量,从而网络权重层数比之前的模型更多,在当年也取得了良好的效果。VGG 系列根据权重层数目的不同可以分为 VGG11 ~VGG19,作为主干网络常被采用的是 VGG16 与VGG19 两个模型,VGG 系列网络的权重层数与结构如下图:
PyTorch + OpenVINO 工具套件开发实战系列教程_第3张图片
图 3-3 中 conv3 表 示 卷 积 3x3、conv1 表 示 卷 积 1x1、conv3-512 表示输出通道 512 个,其它依此类推,FC-4096表示全链接层 4096 个神经元节点。

●ResNet 系列模型

ResNet 系列模型是 ImageNet 图象分类竞赛后期出现的重要基础模型之一,它的残差结构设计思想至今仍然可以从很多新出现的模型中找到踪迹。此外 ResNet 产生的一系列变种卷积神经网络不仅在图象分类中精度提升明显,还在其它视觉任务中也表现良好。ResNet 系列网络图示如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第4张图片
图 3-4 中 18-layers 表示权重层(参数计算)数目、FLOPs 表示每列对应模型的浮点数计算量、ResNet-18 表示 18 个权重层数目的残差网络模型的简写、权重层中在这里插入图片描述
表示残差block 中含有两个卷积层分别为 3x3、64 个通道的结构、图示如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第5张图片
其它常见的基础模型还包括 InceptionV2 系列模型、MobileNet系列模型、EfficientNet 系列模型、DenseNet、ResNext 等,感兴趣的读者可以自己阅读相关资料进一步拓展。

3.2 Pytorch 图象分类模型使用

通过前面一节内容我们已经了解图象分类的基本概念,常用模型与基准数据集。本节我们将使用Pytorch框架中基于ImageNet 数据集的预训练模型库中的模型,来完成图象分类的代码演示,同时学习如何使用 Pytorch 预训练模型库中的图像分类模型。

Pytorch 中 torchvision 组件下的 models 包含大多数当前常用的基于 ImageNet 数据集训练的图象分类模型,这些模型主要包括:

AlexNet
VGG
ResNet
SqueezeNet
DenseNet
Inception v3

在 Pytorch 中引用上述列表模型并初始化的代码如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第6张图片
其中第一行代码是导入模型库支持,剩下的代码则是分别初始化各个预训练模型、其中 pretrained=True 表示加载该模型的预训练版本。当你在 Pycharm 中第一次执行上述代码时候,你会发现它运行非常慢,原因是第一次执行它会把模型下载本地,这个是一个绝对耗时的过程,需要一点时间等待。一旦完成模型加载,就可以通过读入一张图象,然后进行预处理之后,作为输入调用模型推理实现图象类别预测。以ResNet18为例,完整代码演示分为如下几个部分:

●模型加载

Torchvision.models 包含了 ResNet 系列模型,这里我们以ResNet18 模型为例,首先需要完成模型库导入与加载初始化,代码如下:
在这里插入图片描述
上述代码实现了模型加载并通过 print 功能打印模型模型结构,可以很清楚的对应上图 3-4 所示的模型结构说明,感兴趣的读者可以自行对照验证。

●图象预处理

在加载 ResNet18 模型之后,还需要准备测试图象,完成对测试图象的预处理,才可以作为输入张量数据给模型,然后推理预测分类。所以输入图象预处理非常重要,测试图象预处理必须遵循跟训练图象数据集预处理保持一致原则。ResNet18 模型的输入要求如下:

PyTorch + OpenVINO 工具套件开发实战系列教程_第7张图片
图 3-6 中输入格式 NCHW 中 N 表示图象数目、C 表示每张图象的通道数目、H 跟 W 分别表示图象的宽与高。NCHW=1x3x244x244表示输入图象是三通道的大小为 224x244,这里需要注意的是 torchvision 模型库中的预训练模型在训练时候输入图象数据集的通道顺序都是 RGB 顺序,而 OpenCV 读入图象默认顺序是 RGB,所以需要转换为 RGB;此外输入图象数据值被归一化到 0~1 之间,而且被均值与方差归一化处理,这些必须都要在图象预处理中完成,完成之后转换为 NCHW 格式输入数据。图象预处理部分的演示代码如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第8张图片
其中 transforms 来自第一章中介绍过的 torchvision 的组件,它是pytorch中图象预处理的神器,低版本只接受来自 PIL 库读入的图象数据,pytorch1.7 以上版本支持直接读取 OpenCV 图象作为输入数据。toTensor() 函数会把图象数据从字节类型转换为 0~1 之间的浮点数张量、Normalize 表示归一化,三个通道减去 [0.485, 0.456, 0.406] 对应均值,然后除以 [0.229, 0.224, 0.225] 对应方差;注意这里均值跟方差都是基于整个数据集计算生成的。最后 Resize 到指定的224x224 大小。

●推理预测

首先检测是否支持 GPU 运行,如果支持则转换为 GPU 数据,然后运行,否则CPU运行推理。推理完成以后会得到预测结果,我们使用 top-5 的模式来显示预测结果,通过 OpenCV 绘制文本显示预测结果到图象上。这部分的演示代码如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第9张图片
在推理开始之前,对加载的模型首先要执行 eval() 操作,然后才可以使用,eval() 操作是针对模型推理的调整,目的是保证推理输出结果的一致性。得到预测结果 pred,它的显示格式如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第10张图片
ImageNet 数据集是支持 1000 个类别分类的图象数据集,所以输出格式为 Nx1000=1x1000,其中 N=1 表示图象数目。使用 topk 函数获取前五个最可能预测类别,然后根据索引转换为对应标签文字,运行结果如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第11张图片
图 3-8 中显示的是 top-5 的预测结果 ( top-5 准确率意思是预测五个结果有一个是正确结果 )

3.3 迁移学习介绍

深度学习在训练环节严重依赖大规模数据集与硬件计算能力,很多知名的业界模型只有顶级人工智能公司有资金购买算力资源跟数据资源支持,实现从零开始的模型训练。对大多数中小型的人工智能公司来说训练超大规模的模型是一件极其困难的事情,这个困难不是技术困难,而是没有足够的资金持续支持模型训练与开发,顶层的人工智能公司资金雄厚,通用开发模型垄断的通用应用场景,在细分领域确少足够数据跟资金的中小型人工智能公司的出路在哪里?答案就是迁移学习。

3.3.1 迁移学习概念

古语有云:“青,取之于蓝,而胜于蓝;冰,水为之,而寒于水”意思是说学习者是可以达到或者超过被学习的对象,前提是基于相似的素材。迁移学习的思想跟这句古语非常接近,当我们想用一个模型,但是有受限于数据集样本数目,计算能力的限制,无法重零开始训练一个模型时,只要有一个超大规模数据集上训练好的模型,在这个模型的基础上,利用我们有限的数据跟算力,复用跟微调模型权重参数,进行有限条件的训练模型,从而在自定义的数据集上面让模型达到或者超过之前的预测精度,这个就是迁移学习。标准模型训练与迁移学习之间的对比如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第12张图片
迁移学习不是深度学习之后才有的概念,它是机器学习的分支研究领域之一,从不同的维度跟层次可以分为不同方式的迁移学习,这里我们主要关注的是基于深度学习模型的迁移学习。

迁移学习在计算机视觉领域的应用十分广泛,从图象分类、风格迁移、对象检测、语义分割等视觉任务中都会用到,而且效果良好。针对卷积神经网络不同任务的模型,迁移学习以基于 MS-COCO/ImageNet 等大规模数据集训练生成的模型为基础,通过采集少量的自定义数据集并标注,使用有限的计算资源进行训练,得到模型在大规模数据集上训练之后相同或者相近的测试精度与稳定性要求。在 Pytorch 深度学习框架中,torchvison 是计算机视觉的迁移学习框架,支持对象检测、图象分类、语义分割等模型的迁移学习。

3.3.2 特征迁移方式

这里的迁移学习方式主要是针对计算机视觉领域的卷积神经网络模型来说,它的迁移学习方式可以分为两种,第一种方式冻结大多数卷积层,只训练微调最后几层;第二种方式则是全部权重层都参与训练,整个模型的参数微调训练。下图是深度神经网络迁移学习方式对比:
PyTorch + OpenVINO 工具套件开发实战系列教程_第13张图片
作者把 ImageNet 数据集分为两部分 dataA 与 DataB,分别对应 baseA 与 baseB 两个单独的模型,然后训练得到结果;第三行是采用模型 baseB,前三层冻结参数不变后续层随机初始化参数训练,使用 dataB 训练得到 B3B;然后对前三层也不冻结参与微调,后续层随机初始化得到 B3B+;第四行则采用模型 baseA,然后采取冻结前三层与前三层微调,后续层随机初始化使用训练数据 dataB 训练分别得到 A3B 与 A3B+。它们实验准确率对比结果如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第14张图片
图 3-10 中可以看到采用全链路参数微调的 AnB+ 不管 n 等于几,它的结果总是很好,在同等条件下,冻结的层数越多,迁移学习效果越差,参见 AnB 模型准确率。上述实验有力的说明深度神经网络模型的特征可以在不同数据集上迁移,通过微调达到更好的效果。

3.4 自定义图象分类

本节我们将在 pytorch 框架中使用迁移学习实现自定义图象数据分类,同时对训练之后保存的模型转换为 OpenVINO™ 工具套件的 IR 格式,实现 CPU 部署与推理,完成自定义图象分类训练与部署全过程,迈出实战化学习的第一步。

3.4.1 数据集说明与使用

作者选择了一个公开的迁移学习数据集 flower_photos,是一个花卉图像分类数据集,包含五个分类标签表示五种不同的花卉,分别是雏菊、蒲公英、玫瑰、向日葵、郁金香,总计包含2934 张训练图象,736 张测试图象,图象格式均为 JPG 格式。五种类别花卉图示如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第15张图片
在 Pytorch 中制作数据集主要是通过 torch.utils.data.Dataset这个类来完成,实现一个自定义的数据集类。这个其中最重要的做好图像的预处理,要把图象处理成为 224x224x3 大小的彩色图像,RGB 顺序,值在 0~1 之间的 tensor 数据,这样就跟 Pytroch 的 ResNet18 预训练模型的预处理的输入格式要求一致了。

●自定义 Dataset 类

在 Pytorch 中官方提供的数据集都已经封装好了 Dataset 类,比如 MNIST 数据集、FashionMNIST 数据集等。其它的非官方提供的,特别是开发者自己制作的数据集,都需要通过集成Dataset 类,实现它的三个方法:
在这里插入图片描述
其中 init 是初始化方法,只是在创建自定义数据集实例时候运行一次,一般是根据路径加载数据集,定义数据预处理流程等操作。getitem 方法是根据索引返回数据集中对应的样本、对图象来说,它会完成图象预处理,转换为 tensor,返回图象的 tensor 数据与对应的标注信息(分类标签)。花卉数据集的自定义 Dataset 类如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第16张图片
需要注意的是在 init 中的变量 self.transform 是一个组合变换操作,第一步转换为 tensor 跟后续操作顺序绝对不能调换,否则你会遇到如下错误:

raise TypeError(‘img should be PIL Image. Got {}’.format(type(img)))TypeError: img should be PIL Image. Got

原因是因为早期的 pytorch 中 transforms 图象预处理包接受的输入图象类型是 PIL 格式的,不支持 OpenCV 的 numpy 数据格式数据,但是 transforms 中的 toTensor 支持把 Numpy数据转换为 tensor 变量,而 transforms 中所有的操作都支持tensor 数据,所以在数据集中当使用 OpenCV 读取图象时候必须把 toTensor 放在第一个操作。自定义数据集类的代码基本结构在本书的后续章节中会反复用到,请读者熟悉并理解上述代码。

●数据集加载

自定义数据集完成之后,可以通过 DataLoader 类完成数据集的加载,DataLoader 有两个最重要的参数分别是 batchSize参数与 shuffle 参数,batchSize=64 表示迭代每次输出 64 个样本数据,shuffle=True 表示对原有数据打乱顺序输出。使用 DataLoader 加速自定义的花卉数据集并迭代输出样本的代码演示如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第17张图片
运行结果(部分截图)如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第18张图片
3.4.2 模型构建

这里笔者选择 ResNet 系列模型来构建迁移学习模型,首先需要把模型的全链接层的最后的输入从预测 ImageNet 的 1000个类别改为我们自定义数据指定的类别数目 5。当采用全链路参数微调时,模型的构建的代码如下:
在这里插入图片描述
当采用冻结全部卷积层参数的方式迁移学习时,模型构建的代码如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第19张图片
这样就完成模型的构建,这部分的完整源代码参考 flower_cnn.py 这个 python 源代码文件,这里就不再贴出全部源码。

3.4.3 模型训练

前面已经准备好了训练数据,构建了数据集类与模型类的代码,为模型训练做好了一切准备,指定训练批次参数为 4,训练 epoch 为 15,选择交叉熵损失函数与 Adam 优化器,这部分的代码如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第20张图片
关于 DataLoader 中设置批次参数时需要注意整个训练集数据的多少,数据集数据最好是能被批次整除的,这样最后一个批次就不会出现数据不足。当出现最后一个批次数据不足时,DataLoader 类支持通过参数 drop_last 来决定是否丢掉,默认是不丢掉。 然后就可以根据设置 epoch 开始训练模型,代码如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第21张图片
PyTorch + OpenVINO 工具套件开发实战系列教程_第22张图片
上述代码在训练过程中,整个数据集循环一次叫做一个epoch,数据集中每次完成一个批次训练就会计算损失,更新模型参数。完成指定的 epoch 数目之后,就可以保存模型,Pytorch 中保存模型有两种典型不同方式,一种方式是保存检查点,目的是方便以后继续训练,可以直接从检查点恢复参数开始训练,这样可以大大节省后续训练的时间与精力;另外一种方式保存为字典形式的 Python 串行化字节,方便后续模型推理使用。这两种方式分别的分别说明与代码演示如下:

●保存与加载检查点模式

保存加持点代码如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第23张图片
其中 PATH 表示的是保存路径。加载检查点的代码示例如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第24张图片然后设置模型为训练模式就可以从检查点继续训练。

●保存与加载推理模式

保存代码如下:
在这里插入图片描述
加载代码如下:
在这里插入图片描述
注意:在加载之后必须调用模型的 eval() 方法设置 dropout层与 BN 层参数为推理模式,然后才可以使用模型去推理,否则很可能导致模型训练与推理运行结果不一致。

3.4.4 模型部署

前面已经完成了迁移学习图象分类模型的训练与模型保存,现在我们需要使用模型来进行预测,这个过程我们称为模型推理。一个训练好的模型是既可以使用原来训练的深度学习框架来部署,也可以使用第三方模型部署框架进行部署。这里我们将分别演示 Pytroch 原生推理与 OpenVINO™ 工具套件转换推理部署两种方式。

●Pytorch 依赖的推理部署

首先需要加载训练好的模型,然后加载测试图象,对测试图象进行预处理,预处理的方式必须与训练时对图象数据集的处理方式一致。这里需要注意的时候加载模型之后必须先 eval() 才可以后续使用,加载模型的代码如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第25张图片
然后遍历测试图象进行测试,并显示测试结果,代码如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第26张图片
运行结果如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第27张图片
●OpenVINO™ 工具套件的推理部署

OpenVINO™ 工具套件支持 ResNet 系列的图象分类模型部署,当前 OpenVINO™ 工具套件只支持两种模型格式文件的读取与加载,一种是基于 IR 格式 (.xml 与 .bin);另外一种基于开放神经网络交换格式 (Open Neural Network Exchange 简称ONNX)。Pytorch 训练的模型可以很方便的通过几行代码转换为 ONNX 格式,然后就可以脱离 pytorch 依赖,直接使用OpenVINO™ 工具套件进行部署并推理。将上面迁移学习训练生成的模型转换为 ONNX 格式的代码如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第28张图片
转换为 ONNX 格式的模型文件,可以通过 OpenVINO™ 工具套件的相关函数直接加载,加载 ONNX 格式文件并设置输入与输出格式代码如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第29张图片
上面的代码中 en、ec、eh、ew 表示输入图象的数目、通道、高度、宽度,可以简化表示为 NCHW=[1x3x224x224]。输出是1x5的二维数据,其中1表示输出图象数目,5表示分类数目,这里花卉是 5 个类别。然后就可以使用模型进行推理,并解析输出结果,这部分的代码实现如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第30张图片
其中 224 表示图象大小、除以 255 是为了归一化到 0~1 之间。运行预测结果如下:
PyTorch + OpenVINO 工具套件开发实战系列教程_第31张图片
完整的演示代码读者可以自行下载运行。

本小节把训练生成的模型分别在依赖 pytorch 框架下运行与导出 ONNX 格式无 pytorch 依赖直接通过 OpenVINO™ 工具套件部署 CPU 推理预测,演示了 pytorch 图象分类模型的不同部署方法。

3.5 小结

本章通过图象分类得概念介绍、支持的常用模型与数据集介绍,了解了图象分类的历史与发展,当前 Pytorch 中支持的图象分类模型,迁移学习的概念等相关知识。通过一个图象分类的示例代码演示了使用 pytorch 预训练模型进行图象分类预测,同时完成了基于迁移学习实现自定义图象分类模型的数据处理、训练、部署全流程的代码演示,帮助读者掌握 pytorch 框架下如何快速完成自定义图象分类模型的训练与部署。

你可能感兴趣的:(OpenVINO,pytorch,openvino,深度学习)