【项目08】 迁移学习和恢复训练(蝴蝶分类)

作者:欧新宇(Xinyu OU)
开发平台:Paddle 2.1
运行环境:Intel Core i7-7700K CPU 4.2GHz, nVidia GeForce GTX 1080 Ti

本教案所涉及的数据集仅用于教学和交流使用,请勿用作商用。

最后更新:2021年8月17日


【实验目的】

  1. 学会调用paddle.vision.models库实现内置模型的调用,并能够实现预训练模型的载入
  2. 熟练使用各种不同的模型(AlexNet, ResNet, VGG, MobileNet等)进行训练、验证和推理
  3. 熟练对模型进行整体准确率测评和单样本预测
  4. 熟练使用logging函数进行日志输出和保存
  5. 熟练函数化编程方法

【实验要求】

  1. 能够调用paddle.vision.models库实现内置模型的调用,并能够实现预训练模型的载入
  2. 使用训练集训练模型,并在训练过程中输出验证集精度
  3. 使用训练好的模型在测试集上输出测试精度
  4. 对给定的测试样本进行预测,输出每一个样本的预测结果该类别的概率
  5. 尽力而为地在测试集上获得最优精度(除网络模型不能更改,其他参数均可修改)

迁移学习是深度神经网络训练策略的核心技术之一,它可以有效地提高训练效率,实现在较短的时间内训练出性能更好的模型。在本项目中,我们分别从数据的角度来对比使用迁移学习技术前后的预测结果的差异。

Projects0801Finetune

PS:如果需要直接使用预训练模型进行训练,需要确保:1. 模型的类别数量和目标数据集类别数量一致;2. 预训练模型中包含目标任务的类别。

【项目代码逻辑结构图】

深度学习的训练一般包括四种模式:

模型训练,可以生产两种模型:

Projcet07TrainingMethods

【实验一】 数据集准备

实验摘要: 对于模型训练的任务,需要数据预处理,将数据整理成为适合给模型训练使用的格式。蝴蝶识别数据集是一个包含有7个不同种类619个样本的数据集,不同种类的样本按照蝴蝶的类别各自放到了相应的文件夹。不同的样本具有不同的尺度,但总体都是规整的彩色图像。

实验目的:

  1. 学会观察数据集的文件结构,考虑是否需要进行数据清理,包括删除无法读取的样本、处理冗长不合规范的文件命名等
  2. 能够按照训练集、验证集、训练验证集、测试集四种子集对数据集进行划分,并生成数据列表
  3. 能够根据数据划分结果和样本的类别,生成包含数据集摘要信息下数据集信息文件 dataset_info.json
  4. 能简单展示和预览数据的基本信息,包括数据量,规模,数据类型和位深度等

1.0 处理数据集中样本命名的非法字符

原始的数据集的名字有可能会存在特殊的命名符号,从而导致在某些情况下无法正确识别。因此,可以通过批量改名的重命名方式来解决该问题。通过观察,本数据集相对规范,不需要进行数据清洗。

1.1 生产图像列表及类别标签

【实验二】 全局参数设置及数据基本处理

实验摘要: 蝴蝶种类识别是一个多分类问题,我们通过卷积神经网络来完成。这部分通过PaddlePaddle手动构造一个Alexnet卷积神经的网络来实现识别功能。本实验主要实现训练前的一些准备工作,包括:全局参数定义,数据集载入,数据预处理,可视化函数定义,日志输出函数定义。

实验目的:

  1. 学会使用配置文件定义全局参数
  2. 学会设置和载入数据集
  3. 学会对输入样本进行基本的预处理
  4. 学会定义可视化函数,可视化训练过程,同时输出可视化结果图和数据
  5. 学会使用logging定义日志输出函数,用于训练过程中的日志保持

2.1 导入依赖及全局参数配置

2.2 数据集定义及数据预处理

在Paddle 2.0+ 中,我们可使用paddle.io来构造标准的数据集类,用于通过数据列表读取样本,并对样本进行预处理。全新的paddle.vision.transforms可以轻松的实现样本的多种预处理功能,而不用手动去写数据预处理的函数,这大简化了代码的复杂性。

代码最后给出了该了简单测试,输出两种不同模式的样本,进行数据预处理和不进行数据预处理。

2.3 设置数据提供器

结合paddle.io.DataLoader工具包,可以将读入的数据进行batch划分,确定是否进行随机打乱和是否丢弃最后的冗余样本。

  1. 一般来说,对于训练样本(包括train和trainvl),我们需要进行随机打乱,让每次输入到网络中的样本处于不同的组合形式,防止过拟合的产生;对于验证数据和测试数据,由于每次测试都需要对所有样本进行一次完整的遍历,并计算最终的平均值,因此是否进行打乱,并不影响最终的结果。
  2. 由于在最终输出的loss和accuracy的平均值时,会事先进行一次批内的平均,因此如果最后一个batch的数据并不能构成完整的一批,即实际样本数量小于batch_size,会导致最终计算精度产生一定的偏差。所以,当样本数较多的时候,可以考虑在训练时丢弃最后一个batch的数据。但值得注意的是,验证集和测试集不能进行丢弃,否则会有一部分样本无法进行测试。

2.4 定义过程可视化函数

定义训练过程中用到的可视化方法, 包括训练损失, 训练集批准确率, 测试集损失,测试机准确率等。 根据具体的需求,可以在训练后展示这些数据和迭代次数的关系. 值得注意的是, 训练过程中可以每个epoch绘制一个数据点,也可以每个batch绘制一个数据点,也可以每个n个batch或n个epoch绘制一个数据点。

下面的程序除了实现训练后自动可视化的函数,同时实现将可视化的图片和数据进行保存,保存的文件夹由 final_figures_path 指定。

2.5 定义日志输出函数

logging是一个专业的日志输出库,可以用于在屏幕上打印运行结果(和print()函数一致),也可以实现将这些运行结果保存到指定的文件夹中,用于后续的研究。

【实验三】 模型训练与评估

实验摘要: 蝴蝶种类识别是一个多分类问题,我们通过卷积神经网络来完成。这部分通过PaddlePaddle手动构造一个Alexnet卷积神经的网络来实现识别功能,最后一层采用Softmax激活函数完成分类任务。

实验目的:

  1. 掌握卷积神经网络的构建和基本原理
  2. 深刻理解训练集、验证集、训练验证集及测试集在模型训练中的作用
  3. 学会按照网络拓扑结构图定义神经网络类 (Paddle 2.0+)
  4. 学会在线测试和离线测试两种测试方法
  5. 学会定义多种优化方法,并在全局参数中进行定义选择

3.1 配置网络

从Alexnet开始,包括VGG,GoogLeNet,Resnet等模型都是层次较深的模型,如果按照逐层的方式进行设计,代码会变得非常繁琐。因此,我们可以考虑将相同结构的模型进行汇总和合成,例如Alexnet中,卷积层+激活+池化层就是一个完整的结构体。

如果使用的是标准的网络结构,我们可以直接从Paddle的模型库中进行下载,并启用预训练模型,载入Imagenet预训练参数。再实际应用中,预训练(迁移学习)是非常有效的提高性能和缩减训练时间的方法。在载入Paddle模型库的时候,我们不需要手动设计模型,只需要按照下面的方法来直接调用即可。

Paddle.vision.models 内置的模型库,请参考:paddle.vision

3.2 定义优化方法

学习率策略和优化方法是两个密不可分的模块,学习率策略定义了学习率的变化方法,常见的包括固定学习率、分段学习率、余弦退火学习率、指数学习率和多项式学习率等;优化策略是如何进行学习率的学习和变化,常见的包括动量SGD、RMS、SGD和Adam等。

3.3 定义验证函数

验证函数有两个功能,一是在训练过程中实时地对验证集进行测试(在线测试),二是在训练结束后对测试集进行测试(离线测试)。

验证函数的具体流程包括:

  1. 初始化输出变量,包括top1精度,top5精度和损失
  2. 基于批次batch的结构进行循环测试,具体包括:
    1). 定义输入层(image,label),图像输入维度 [batch, channel, Width, Height] (-1,imgChannel,imgSize,imgSize),标签输入维度 [batch, 1] (-1,1)
    2). 定义输出层:在paddle2.0+中,我们使用model.eval_batch([image],[label])进行评估验证,该函数可以直接输出精度和损失,但在运行前需要使用model.prepare()进行配置。值得注意的,在计算测试集精度的时候,需要对每个批次的精度/损失求取平均值。

在定义eval()函数的时候,我们需要为其指定两个必要参数:model是测试的模型,data_reader是迭代的数据读取器,取值为val_reader(), test_reader(),分别对验证集和测试集。此处验证集和测试集数据的测试过程是相同的,只是所使用的数据不同;此外,可选参数verbose用于定义是否在测试的时候输出过程

3.4 模型训练及在线测试

在Paddle 2.0+动态图模式下,动态图模式被作为默认进程,同时动态图守护进程 fluid.dygraph.guard(PLACE) 被取消。

训练部分的具体流程包括:

  1. 定义输入层(image, label): 图像输入维度 [batch, channel, Width, Height] (-1,imgChannel,imgSize,imgSize),标签输入维度 [batch, 1] (-1,1)
  2. 实例化网络模型: model = Alexnet()
  3. 定义学习率策略和优化算法
  4. 定义输出层,即模型准备函数model.prepare()
  5. 基于"周期-批次"两层循环进行训练
  6. 记录训练过程的结果,并定期输出模型。此处,我们分别保存用于调优和恢复训练的checkpoint_model模型和用于部署与预测的final_model模型

【实验结果分析】

  1. 训练完成,最终性能accuracy=0.71642(epoch=50), 总耗时398.28s, 已将其保存为:Butterfly_Alexnet_final
  2. 训练完成,最终性能accuracy=0.43284(epoch=39), 总耗时195.85s, 已将其保存为:Butterfly_Mobilenetv2_withoutPretrained_final
  3. 训练完成,最终性能accuracy=0.97015(epoch=8), 总耗时28.84s, 已将其保存为:Butterfly_Mobilenetv2_final

上面训练结果1是项目Project09Alexnet中使用Alexnet模型,训练50个epoch后,在验证集上获得的精度;结果2是本项目中使用一个更简单的MobileNet模型,从头开始训练获得的精度;结果3同样是使用MobileNet模型进行训练获得的精度,但是使用了Imagenet预训练参数作为初始化。

从结果可以看到,Alexnet的模型性能要比Mobilenet更好,但在经过Imagenet预训练之后,只需要2个epoch就可以达到Alexnet训练50个epoch才能达到的精度;更显著的是,经过10个epoch后,验证集的精度上升到0.97015。实验结果证明说明迁移学习(Imagenet)预训练带来的性能和效率提升是显著的。

训练完成后,建议将 *ExpResults* 文件夹的最终文件 **copy** 到 *ExpDeployments* 用于进行部署和应用。

3.5 离线测试

Paddle 2.0+的离线测试也抛弃了fluid方法。基本流程和训练是一致。

【结果分析】

需要注意的是此处的精度与训练过程中输出的测试精度是不相同的,因为训练过程中使用的是验证集, 而这里的离线测试使用的是测试集.

【实验四】 模型推理和预测(应用)

实验摘要: 对训练过的模型,我们通过测试集进行模型效果评估,并可以在实际场景中进行预测,查看模型的效果。

实验目的:

  1. 学会使用部署和推理模型进行测试
  2. 学会对测试样本使用基本预处理方法十重切割对样本进行预处理
  3. 对于测试样本,能够实现批量测试test()和单样本推理predict()

4.1 导入依赖库及全局参数配置

4.2 定义推理时的预处理函数

在预测之前,通常需要对图像进行预处理。此处的预处理方案和训练模型时所使用的预处理方案必须是一致的。对于彩色图,首先需要将图像resize为模型的输入尺度,其次需要将模型通道进行调整,转化[C,W,H]为[H,W,C],最后将像素色彩值归一化为[0,1]. 此外,按照Alexnet的基本要求,对于测试数据还需要进行十重切割,并在预测时求每个切片的预测结果的平均值作为最终的准确率。