【项目07】基于AlexNet的图像分类(Fluid)(蝴蝶分类)

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

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

最后更新:2021年8月15日


【实验目的】

  1. 学会基于Paddle 1.8版实现卷积神经网络
  2. 学会自己设计AlexNet的类结构,并基于AlexNet模型进行训练、验证和推理
  3. 学会对模型进行整体准确率测评和单样本预测
  4. 熟练函数化编程方法

【实验要求】

  1. 按照给定的网络体系结构图设计卷积神经网络
  2. 使用训练集训练模型,并在训练过程中输出验证集精度
  3. 使用训练好的模型在测试集上输出测试精度
  4. 对给定的测试样本进行预测,输出每一个样本的预测结果该类别的概率
  5. 尽力而为地在测试集上获得最优精度(除网络模型不能更改,其他参数均可修改)

【项目逻辑结构图】

建设中...

【实验一】 数据集准备

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

实验目的:

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

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

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

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

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

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

实验目的:

  1. 学会使用配置文件定义全局参数
  2. 学会设置和载入数据集
  3. 学会对输入样本进行基本的预处理
  4. 学会定义可视化函数,可视化训练过程

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

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

2.3 设置数据提供器

2.4 定义过程可视化函数

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

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

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

实验目的:

  1. 掌握卷积神经网络的构建和基本原理
  2. 深刻理解训练集、验证集、训练验证集及测试集在模型训练中的作用
  3. 学会按照网络拓扑结构图定义神经网络类 (Paddle 1.8)
  4. 学会在线测试和离线测试两种测试方法

3.1 配置网络

3.1.1 网络拓扑结构图

Ch04assign001

需要注意的是,在Alexnet中实际输入的尺度会被Crop为$3×227×227$

3.1.2 网络参数配置表

Layer Input Kernels_num Kernels_size Stride Padding PoolingType Output Parameters
Input 3×227×227
Conv1 3×227×227 96 3×11×11 4 0 96×55×55 (3×11×11+1)×96=34944
Pool1 96×55×55 96 96×3×3 2 0 max 96×27×27 0
Conv2 96×27×27 256 96×5×5 1 2 256×27×27 (96×5×5+1)×256=614656
Pool2 256×27×27 256 256×3×3 2 0 max 256×13×13 0
Conv3 256×13×13 384 256×3×3 1 1 384×13×13 (256×3×3+1)×384=885120
Conv4 384×13×13 384 384×3×3 1 1 384×13×13 (384×3×3+1)×384=1327488
Conv5 384×13×13 256 384×3×3 1 1 256×13×13 (384×3×3+1)×256=884992
Pool5 256×13×13 256 256×3×3 2 0 max 256×6×6 0
FC6 (256×6×6)×1 4096×1 (9216+1)×4096=37752832
FC7 4096×1 4096×1 (4096+1)×4096=16781312
FC8 4096×1 1000×1 (4096+1)×1000=4097000
Output 1000×1
Total = 62378344

其中卷积层参数:3747200,占总参数的6%。

3.1.3 定义神经网络类

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

3.2 定义测试函数

测试部分的具体流程包括:

  1. 设置模型运行模式为验证模式model.eval()
  2. 基于周期epoch-批次batch的结构进行两层循环训练,具体包括:
    1). 定义输入层(image,label),图像输入维度 [batch, channel, Width, Height] (-1,imgChannel,imgSize,imgSize),标签输入维度 [batch, 1] (-1,1)
    2). 定义输出层,包括前向传播的输出predict=model(image)及精度accuracy。如果需要,还可以输出针对测试集的损失loss。
    值得注意的,在计算测试集精度的时候,需要对每个批次的精度/损失求取平均值。

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

3.3 定义训练函数

在动态图模式下,所有的训练测试代码都需要基于动态图守护进程fluid.dygraph.guard(PLACE)

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

  1. 模型实例化,并设置为训练模式model.train()
  2. 定义优化器optimizer
  3. 基于周期epoch-批次batch的结构进行两层循环训练,具体包括:
    1). 定义输入层(image,label),图像输入维度 [batch, channel, Width, Height] (-1,imgChannel,imgSize,imgSize),标签输入维度 [batch, 1] (-1,1)
    2). 定义输出层,包括前向传播的输出predict=model(image),损失loss及平均损失,精度accuracy。
    3). 执行反向传播,并将损失最小化,清除梯度

在训练过程中,可以将周期,批次,损失及精度等信息打印到屏幕。

值得注意的是,在每一轮的训练中,每100个batch之后会输出一次平均训练误差和准确率。每一轮训练之后,使用测试集进行一次测试,在每轮测试中,均打输出一次平均测试误差和准确率。

【注意】注意在下列的代码中,我们每个epoch都执行一次模型保存,这种方式一般应用在复杂的模型和大型数据集上。这种经常性的模型保存,有利于我们执行EarlyStopping策略,当我们发现运行曲线不再继续收敛时,就可以结束训练,并选择之前保存的最好的一个模型作为最终的模型。FinalModel

值得注意的是,因为训练数据样本较少(210),而Batch_Size=128,因此每个周期的batch数只有不足2个批次。因此无法按照批次来进行print训练过程,只能按照epoch进行print。而基于epoch的print,需要将其缩进往前移一个层次。

3.5 执行训练主函数

3.6 离线测试

离线测试同样要基于动态守护框架fluid.dygraph.guard()。测试过程与训练过程中的在线测试流程基本一致,只需要提前实现载入已保存的模型即可,载入模型使用fluid.load_dygraph()方法。

【结果分析】

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

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

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

实验目的:

  1. 学会使用部署和推理模型进行测试
  2. 学会对测试样本进行基本数据预处理
  3. 对于测试样本能够实现单样本推理

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

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

在预测之前,通常需要对图像进行预处理。此处的预处理方案和训练模型时所使用的预处理方案必须是一致的。对于彩色图,首先需要将图像resize为模型的输入尺度,其次需要将模型通道进行调整,转化[C,W,H]为[H,W,C],最后将像素色彩值归一化为[0,1].

4.3 数据推理