【课后作业06】 迁移学习和恢复训练(十二生肖) 习题答案

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

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

最后更新:2021年8月19日


【实验目的】

  1. 学会调用paddle.vision.models库实现内置模型的调用,并能够实现预训练模型的载入
  2. 熟练使用各种不同的模型(AlexNet, ResNet, VGG, MobileNet等)进行训练、验证和推理
  3. 熟练使用logging函数进行日志输出和保存,熟练保存运行结果图,并能够反复调用输出
  4. 能够快速更改优化

【实验要求】

  1. 所有作业均在AIStudio上进行提交,提交时包含源代码和运行结果
  2. 补充完成下列ResNet50, ResNet18, Mobilenetv2, VGG16四个模型的结果统计表
  3. 对下列结果进行总结和分析

下面我们将在ResNet50, ResNet18, Mobilenetv2, VGG16四个模型对十二生肖数据集进行评估,所有模型设置batch_size=64。 数据集包含样本8500个,其中训练验证集样本7840个,训练集样本7190个, 验证集样本650个, 测试集样本660个, 共计8500个。

1. 实验结果

模型名称 Baseline模型 ImageNet预训练 learning_rate best_epoch top-1 acc top-5 acc loss test_top1_acc 单batch时间/总训练时间(s) 可训练参数/总参数
Zodiac_Alexnet Alexnet 0.001 3/10 0.48769 0.87231 0.02458 0.48485 9.06/1177.53 11,172,042/11,191,242
Zodiac_Resnet18 ResNet18
Zodiac_Resnet18_withoutPretrained ResNet18 0.01 50/50 0.60769 0.90769 0.02072 0.60909 0.26/374.98 11,172,042/11,191,242
Zodiac_Resnet50 ResNet50
Zodiac_Resnet50_withoutPretrained ResNet50 0.01 45/50 0.54154 0.89538 0.04756 0.55152 11.24/7868.96 23,479,500/23,585,740
Zodiac_VGG16 VGG16
Zodiac_VGG16_withoutPretrained VGG16 0.001 45/50 0.55231 0.90000 0.03685 0.86429 14.75/8727.87 134,309,708/134,309,708
Zodiac_Mobilenetv2 Mobilenetv2
Zodiac_Mobilenetv2_withoutPretrained Mobilenetv2 0.001 44/50 0.52615 0.87077 0.03536 0.51667 10.44/5838.82s 2,205,132/2,273,356

2. 实验结果分析

从实验结果可以得到以下几个结论:

【参考代码】


【实验一】 数据集准备

实验摘要: 对于模型训练的任务,需要数据预处理,将数据整理成为适合给模型训练使用的格式。蝴蝶识别数据集是一个包含有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模型

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

3.5 离线测试

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

【结果分析】

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

【实验结果及评价】

下面我们将在ResNet50, ResNet18, Mobilenetv2, VGG16四个模型对十二生肖数据集进行评估,所有模型设置batch_size=64。 数据集包含样本8500个,其中训练验证集样本7840个,训练集样本7190个, 验证集样本650个, 测试集样本660个, 共计8500个。

1. 实验结果

模型名称 Baseline模型 ImageNet预训练 learning_rate best_epoch top-1 acc top-5 acc loss test_top1_acc 单batch时间/总训练时间(s) 可训练参数/总参数
Zodiac_Alexnet Alexnet 0.001 3/10 0.48769 0.87231 0.02458 0.48485 9.06/1177.53 11,172,042/11,191,242
Zodiac_Resnet18 ResNet18 0.01 7/10 0.83385 0.97538 0.01093 0.81364 10.20/1389.07 11,172,042/11,191,242
Zodiac_Resnet18_withoutPretrained ResNet18 0.01 50/50 0.60769 0.90769 0.02072 0.60909 12.28/7220.98 11,172,042/11,191,242
Zodiac_Resnet50 ResNet50 0.01 8/10 0.94615 0.99846 0.00343 0.94545 11.67/1455.60 23,479,500/23,585,740
Zodiac_Resnet50_withoutPretrained ResNet50 0.01 45/50 0.54154 0.89538 0.04756 0.55152 11.24/7868.96 23,479,500/23,585,740
Zodiac_VGG16 VGG16 0.001 8/10 0.89385 0.98615 0.00581 0.86515 16.69/2243.58 134,309,708/134,309,708
Zodiac_VGG16_withoutPretrained VGG16 0.001 45/50 0.55231 0.90000 0.03685 0.86429 14.75/8727.87 134,309,708/134,309,708
Zodiac_Mobilenetv2 Mobilenetv2 0.001 9/10 0.92769 0.99692 0.00425 0.91212 10.18/1294.77 2,205,132/2,273,356
Zodiac_Mobilenetv2_withoutPretrained Mobilenetv2 0.001 44/50 0.52615 0.87077 0.03536 0.51667 10.44/5838.82s 2,205,132/2,273,356

2. 实验结果分析

从实验结果可以得到以下几个结论:

  1. 基于Imagenet预训练模型的微调训练(finetune)比从头训练收敛更快,且更容易获得较好的性能
  2. VGG16因为参数较多,因此被应用到简单的手势识别数据集时更容易发生过拟合,训练起来更困难;相似的ResNet50在该数据集上也趋向于过拟合
  3. VGG16和Alexnet模型因为参数较多,且没有类似残差结构的辅助,需要更低的学习率来进行训练
  4. 参数的数量训练时间有较大的影响
  5. 从损失loss和精确度来看,没有使用Imagenet预训练参数的模型的ResNet18还没有完全收敛。也证明了迁移学习在计算机视觉任务中的作用明显
  6. 同一种模型的参数数量是固定的,主要由其卷积层、全连接层等内部结构决定
  7. 由于任务并不复杂,因此四种模型的top5精度都比较高,说明CNN模型的判别能力还是很不错的
  8. 总体来看,ResNet50的性能是最好的,要优于ResNet18, VGG16和Mobilenet v2。一方面体现了残差网络的强大性能,另一方面也说明更深的模型对于识别精度是有积极意义的(Resnet50是50层,而Resnet18只有18层,VGG16也只有16层)。此外,Mobilenetv2也具有相当不错的性能,并且参数量只有ResNet50模型的10%,使其更适合部署在移动设备上。

值得注意的是,由于数据集比较简单,参数的初始化以及随机梯度下降算法对于训练的过程影响比较大。即每次训练的过程(甚至结果)可能会有一定的波动,但较好的超参数设置仍然有助于获得较好的性能。