一、项目实践步骤
图 1 构建模型和完成训练的程序图
二、实验背景
2.1数据集介绍
AI识虫数据集结构如下:
- 提供了2183张图片,其中训练集1693张,验证集245,测试集245张。
- 包含7种昆虫,分别是Boerner、Leconte、Linnaeus、acuminatus、armandi、coleoptera和linnaeus。
图 2 AI识虫 数据集图像示例
2.2实验环境
本次实验,采用PaddlePaddle环境训练模型,利用PaddlePaddle的可视化插件VisualDL进行训练模型过程的可视化。
另附代码见附录和.ipynb 文件。
2.3实验设置
本次实验,采用YOLOV3作为AI识虫的基线模型,以COCO指标作为评估指标。
2.3.1 YOLOV3
R-CNN系列算法需要先产生候选区域,再对候选区域做分类和位置坐标的预测,这类算法被称为两阶段目标检测算法。近几年,很多研究人员相继提出一系列单阶段的检测算法,只需要一个网络即可同时产生候选区域并预测出物体的类别和位置坐标。
与R-CNN系列算法不同,YOLOv3使用单个网络结构,在产生候选区域的同时即可预测出物体类别和位置,不需要分成两阶段来完成检测任务。另外,YOLOv3算法产生的预测框数目比Faster R-CNN少很多。Faster R-CNN中每个真实框可能对应多个标签为正的候选区域,而YOLOv3里面每个真实框只对应一个正的候选区域。这些特性使得YOLOv3算法具有更快的速度,能到达实时响应的水平。
Joseph Redmon等人在2015年提出YOLO(You Only Look Once,YOLO)算法,通常也被称为YOLOv1;2016年,他们对算法进行改进,又提出YOLOv2版本;2018年发展出YOLOv3版本。
YOLOv3模型设计思想
YOLOv3算法的基本思想可以分成两部分:
按一定规则在图片上产生一系列的候选区域,然后根据这些候选区域与图片上物体真实框之间的位置关系对候选区域进行标注。跟真实框足够接近的那些候选区域会被标注为正样本,同时将真实框的位置作为正样本的位置目标。偏离真实框较大的那些候选区域则会被标注为负样本,负样本不需要预测位置或者类别。
使用卷积神经网络提取图片特征并对候选区域的位置和类别进行预测。这样每个预测框就可以看成是一个样本,根据真实框相对它的位置和类别进行了标注而获得标签值,通过网络模型预测其位置和类别,将网络预测值和标签值进行比较,就可以建立起损失函数。
YOLOv3算法训练过程的流程图如 图3 所示:
图3:YOLOv3算法训练流程图
图3 左边是输入图片,上半部分所示的过程是使用卷积神经网络对图片提取特征,随着网络不断向前传播,特征图的尺寸越来越小,每个像素点会代表更加抽象的特征模式,直到输出特征图,其尺寸减小为原图的1/32
图3 下半部分描述了生成候选区域的过程,首先将原图划分成多个小方块,每个小方块的大小是32×32,然后以每个小方块为中心分别生成一系列锚框,整张图片都会被锚框覆盖到。在每个锚框的基础上产生一个与之对应的预测框,根据锚框和预测框与图片上物体真实框之间的位置关系,对这些预测框进行标注。
将上方支路中输出的特征图与下方支路中产生的预测框标签建立关联,创建损失函数,开启端到端的训练过程。
三、项目流程
3.1 准备数据
使用pdx.dataset加载和处理数据集
train_dataset = pdx.datasets.VOCDetection(
data_dir='/home/aistudio/work/insects',
file_list='/home/aistudio/train_list.txt',
label_list='/home/aistudio/label.txt',
transforms=train_transforms,
shuffle=True)
eval_dataset = pdx.datasets.VOCDetection(
data_dir='/home/aistudio/work/insects',
file_list='/home/aistudio/eval_list.txt',
label_list='/home/aistudio/label.txt',
transforms=eval_transforms,
shuffle=False)
数据集处理
数据增强选择
训练预处理1(a1) | 验证预处理 |
MixupImage(mixup_epoch=250) | Resize( target_size=480, interp='CUBIC') |
RandomDistort() | Normalize() |
RandomExpand() | |
RandomCrop() | |
Resize(target_size=480, interp='RANDOM') | |
RandomHorizontalFlip() | |
Normalize() |
train_transforms = transforms.Compose([
transforms.MixupImage(mixup_epoch=250),
transforms.RandomDistort(),
transforms.RandomExpand(),
transforms.RandomCrop(),
transforms.Resize(
target_size=480, interp='RANDOM'),
transforms.RandomHorizontalFlip(),
transforms.Normalize(),
])
eval_transforms = transforms.Compose([
transforms.Resize(
target_size=480, interp='CUBIC'),
transforms.Normalize(),
])
3.2 搭建网络
本次实验基于 PaddlePaddle搭建了 yolov3,其详细代码如下:
num_classes = len(train_dataset.labels)
# API说明: https://paddlex.readthedocs.io/zh_CN/develop/apis/models/detection.html#paddlex-det-yolov3
model = pdx.det.YOLOv3(num_classes=num_classes, backbone='MobileNetV1')
3.3 训练配置
接下来,定义训练函数,为防止过拟合,达到更好效果,使用学习率衰减策略。
初始参数如下:
Epochs | Batch_size | warmup_start_lr | Shuffle | warmup_steps | lr_decay_epochs |
200 | 16 | 0.0 | True | 500 | [85, 135] |
model.train(
num_epochs=30, # 训练轮次
train_dataset=train_dataset, # 训练数据
eval_dataset=eval_dataset, # 验证数据
train_batch_size=16, # 批大小
pretrain_weights='COCO', # 预训练权重,刚开始训练的时候取消该注释,注释resume_checkpoint
learning_rate=0.005 / 12, # 学习率
warmup_steps=500, # 预热步数
warmup_start_lr=0.0, # 预热起始学习率
save_interval_epochs=5, # 每5个轮次保存一次,有验证数据时,自动评估
lr_decay_epochs=[85, 135], # step学习率衰减
save_dir='output/yolov3_mobilenetv1', # 保存路径
#resume_checkpoint='output/yolov3_mobilenetv1', # 断点继续训练
use_vdl=True) # 其用visuadl进行可视化训练记录
- num_epochs:训练轮次,模型将在训练数据上迭代多少个轮次。
- train_dataset:训练数据集对象,包含训练数据的路径、标签等信息。
- eval_dataset:验证数据集对象,包含验证数据的路径、标签等信息。
- train_batch_size:训练时每个批次的样本数量。
- learning_rate:学习率,控制模型参数更新的步长。
- warmup_steps:预热步数,模型在训练开始时将学习率逐渐增加到设定的学习率,以便更好地初始化模型权重。
- warmup_start_lr:预热起始学习率,预热步数开始时的学习率。
- save_interval_epochs:保存间隔轮次,每隔多少个轮次保存一次模型。
- lr_decay_epochs:学习率衰减轮次,指定在哪些轮次将学习率进行衰减。
- save_dir:保存路径,训练过程中模型的保存位置。
- resume_checkpoint:断点继续训练,如果之前已经训练过模型,并且保存了断点文件,可以指定断点文件的路径,以在之前训练的基础上继续训练。
- use_vdl:是否使用VisualDL进行可视化训练记录。VisualDL是一个可视化工具,用于分析和展示训练过程中的指标和结果。
综合来看,使用模型对安全帽数据集进行训练,通过VisualDL工具对训练过程进行可视化。训练过程中会保存分阶段的模型参数,并最终保存整个训练好的模型。
3.4模型优化(进阶)
精度提升 为了进一步提升模型的精度,可以通过coco_error_analysis
精度优化思路侧重在模型迭代过程中优化精度
(1) 基线模型选择
相较于二阶段检测模型,单阶段检测模型的精度略低但是速度更快。考虑到是部署到GPU端,本案例选择单阶段检测模型YOLOV3作为基线模型,其骨干网络选择MobileNetV1。训练完成后,模型在验证集上的精度如下:
模型 | map(Iou-0.5) | 识虫AP(Iou-0.5) |
baseline: YOLOv3 + MobileNetV1 + img_size(480) | 74.9958 | 94.58 |
从图中可以看到armandi和colepotera在AP上的表现不佳,AP值在76.97和74.05。
查看score-recall曲线可以看到linnaeus和acminatus在score上的数值并不高,训练效果并不够好。
使用PaddleX提供的paddlex.det.coco_error_analysis接口对模型在验证集上预测错误的原因进行分析,分析结果以图表的形式展示如下:
allclass | Boerner |
Leconte | Linnaeus |
acuminatus | armandi |
分析图表展示了6条Precision-Recall(PR)曲线,每一条曲线表示的Average Precision (AP)比它左边那条高,原因是逐步放宽了评估要求。以allclass为例,各条PR曲线的评估要求解释如下:
- C75: 在IoU设置为0.75时的PR曲线, AP为0.350。
- C50: 在IoU设置为0.5时的PR曲线,AP为0.737。C50与C75之间的白色区域面积代表将IoU从0.75放宽至0.5带来的AP增益。
- Loc: 在IoU设置为0.1时的PR曲线,AP为0.745。Loc与C50之间的蓝色区域面积代表将IoU从0.5放宽至0.1带来的AP增益。蓝色区域面积越大,表示越多的检测框位置不够精准。
- Sim: 在Loc的基础上,如果检测框与真值框的类别不相同,但两者同属于一个亚类,则不认为该检测框是错误的,在这种评估要求下的PR曲线, AP为0.843。Sim与Loc之间的红色区域面积越大,表示子类间的混淆程度越高。VOC格式的数据集所有的类别都属于同一个亚类。
- Oth: 在Sim的基础上,如果检测框与真值框的亚类不相同,则不认为该检测框是错误的,在这种评估要求下的PR曲线,AP为0.843。Oth与Sim之间的绿色区域面积越大,表示亚类间的混淆程度越高。VOC格式的数据集中所有的类别都属于同一个亚类,故不存在亚类间的混淆。
- BG: 在Oth的基础上,背景区域上的检测框不认为是错误的,在这种评估要求下的PR曲线,AP为0.849。BG与Oth之间的紫色区域面积越大,表示背景区域被误检的数量越多。
- FN: 在BG的基础上,漏检的真值框不认为是错误的,在这种评估要求下的PR曲线,AP为1.00。FN与BG之间的橙色区域面积越大,表示漏检的真值框数量越多。
从分析图表中可以看出,Boerner、Leconte两类检测效果较好,但仍然存在漏检的情况,特别是Boerner存在很大的漏检问题;此外,通过Boerner中C75指标可以看出,其相对于C50的0.946而言有些差了,因此定位性能有待进一步提高。
(2.1)通过尝试放大图片,不同的网络结构以及定位的优化策略
考虑到定位问题,通过尝试放大图片,不同的网络结构以及定位的优化策略: 利用cluster_yolo_anchor生成聚类的anchor或开启iou_aware。得到模型的精度如下:
模型 | map(Iou-0.5) |
YOLOv3 + MobileNetV1+img_size(608) | 81.5489 |
(2.2)尝试替换backbone: MobileNetV1-->ResNet50_vd_dcn --> DarkNet53
考虑到漏检问题,一般是特征学习不够,无法识别出物体,因此基于这个方向尝试替换backbone.
在指标上的提升如下:
模型 | map(Iou-0.5) |
YOLOv3 + ResNet50_vd_dcn +img_size(605) | 76.1519 |
YOLOv3 + DarkNet53 +img_size(605) | 73.7946 |
五、实验感想
在本次实验中,在加入了RandomHorizontalFlip、RandomDistort、RandomCrop、RandomExpand、BatchRandomResize、MixupImage这几种数据增强方法后,对模型的优化是有一定的积极作用了,在取消这些预处理后,模型性能会有一定的下降。
在训练初期都加上这些预处理方法,到后期模型超参数以及相关结构确定最优之后,再进行数据方面的再优化: 比如数据清洗,数据预处理方法筛选等。
中间使用paddlex还一直出现报错module 'pyarrow' has no attribute 'default_serialization_context'
找了很久之后找到了解决方法,是因为pyarrow版本在1.20以上就没有了这个模块,因此降低pyarrow到指定版本就能解决。
在一开始运行结果不好不用灰心,通过数据增强,更换backbone,调整学习率等超参数方法,一步一步将实验结果提高
总得来说,本次实验让我收获颇多。
六、 附录:python 代码
完整代码参见所交版本和.ipynb