用PaddleX快速实现目标检测训练
比赛的链接:aistudio.baidu.com/aistudio/co…
PaddleX 1.3 版本文档:paddlex.readthedocs.io/zh_CN/relea…
直接使用paddle进行测试
验证飞桨安装
python -c "import paddle;paddle.utils.run_check()" 复制代码
获得下图,则安装成功
准备工作
用pip install安装paddlex
pip install paddlex==1.3.7 -i https://mirror.baidu.com/pypi/simple
复制代码
用pip install安装pycocotools
pip install pycocotools -i https://mirror.baidu.com/pypi/simple
复制代码
验证PaddleX安装
python -c "import paddlex as pdx;print(pdx.__version__)"
复制代码
获得下图,则安装成功
飞桨 – PaddleX
是一套更加简明易懂的API,并配套一键下载安装的图形化开发客户端。用PaddleX实现图像分类训练非常快速,代码量也小。
数据的处理
!unzip -oq /home/aistudio/data/data19638/insects.zip -d ./data
复制代码
解压完成,我们进入data/inserts中查看下数据
构造需要的数据
使用下面这个脚本完成数据的
import os
import pandas as pd
train_list_path = 'data/insects/train_list.txt'
val_list_path = 'data/insects/val_list.txt'
label_list_path = 'data/insects/labels.txt'
# 清空内容,第一次运行先注释掉会报错
# with open(train_list_path,'r+') as f:
# f.seek(0)
# f.truncate()
# with open(val_list_path,'r+') as f:
# f.seek(0)
# f.truncate()
# with open(label_list_path,'r+') as f:
# f.seek(0)
# f.truncate()
#训练集
train_list = os.listdir('data/insects/train/annotations/xmls')
for filename in train_list:
name= filename.split('.')
if name[1] == 'xml':
annotation_path = os.path.join('annotations/xmls',filename)
image_path = os.path.join('images',name[0]+'.jpeg')
with open(train_list_path,'a') as f:
f.write(image_path+' '+annotation_path+'\n')
#验证集
train_list = os.listdir('data/insects/val/annotations/xmls')
for filename in train_list:
name= filename.split('.')
if name[1] == 'xml':
annotation_path = os.path.join('annotations/xmls',filename)
image_path = os.path.join('images',name[0]+'.jpeg')
with open(val_list_path,'a') as f:
f.write(image_path+' '+annotation_path+'\n')
#标签
with open(label_list_path,'w') as f:
f.write('Boerner\n')
f.write('Leconte\n')
f.write('Linnaeus\n')
f.write('acuminatus\n')
f.write('armandi\n')
f.write('coleoptera\n')
f.write('linnaeus')
复制代码
labels.txt (主要是类别标签的名称)
制造train_list.txt和val_list.txt
- train_list.txt (训练图片的路径和标签)
- val_list.txt (验证图片的路径和标签)
数据展示
我们以其中一个数据为例进行展示
- 图片数据
- xml标签数据
<annotation>
<folder>石霄宇</folder>
<filename>654.jpeg</filename>
<path>/home/sxy/已拍摄图片/石霄宇/石霄宇/石霄宇/654.jpeg</path>
<source>
<database>Unknown</database>
</source>
<size>
<width>1232</width>
<height>1232</height>
<depth>3</depth>
</size>
<segmented>0</segmented>
<object>
<name>Leconte</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>534</xmin>
<ymin>406</ymin>
<xmax>716</xmax>
<ymax>554</ymax>
</bndbox>
</object>
<object>
<name>Boerner</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>742</xmin>
<ymin>471</ymin>
<xmax>825</xmax>
<ymax>586</ymax>
</bndbox>
</object>
<object>
<name>Linnaeus</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>448</xmin>
<ymin>646</ymin>
<xmax>534</xmax>
<ymax>713</ymax>
</bndbox>
</object>
<object>
<name>armandi</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>615</xmin>
<ymin>761</ymin>
<xmax>698</xmax>
<ymax>830</ymax>
</bndbox>
</object>
<object>
<name>coleoptera</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>798</xmin>
<ymin>694</ymin>
<xmax>848</xmax>
<ymax>762</ymax>
</bndbox>
</object>
</annotation>
复制代码
PaddleX完成目标检测
- 程序运行的主路径是
data_sets
,在此目录下新建一个output的目录
下面是程序的代码
# 加载paddlex,设置GPU使用环境,如果非GPU环境会自动使用CPU
import paddlex as pdx
import os
os.environ['CUDA_VISIBLE_DEVICE'] = '0'
from paddlex.det import transforms
train_transforms = transforms.Compose([
transforms.RandomDistort(),
transforms.RandomExpand(),
transforms.RandomCrop(),
transforms.Resize(target_size=608,interp='RANDOM'),
transforms.RandomHorizontalFlip(),
transforms.Normalize(),
])
eval_transforms = transforms.Compose([
transforms.Resize(target_size=608,interp='CUBIC'),
transforms.Normalize(),
])
train_dataset = pdx.datasets.VOCDetection(
data_dir = 'data/insects/train',
file_list='data/insects/train_list.txt',
label_list = 'data/insects/labels.txt',
transforms = train_transforms,
shuffle = True
)
eval_dataset = pdx.datasets.VOCDetection(
data_dir='data/insects/val',
file_list = 'data/insects/val_list.txt',
label_list = 'data/insects/labels.txt',
transforms = eval_transforms,
)
# 使用PPYOLO模型,这是Box MMAP指标最高的模型,同时也是模型大小最大的,训练有点慢。
num_classes = len(train_dataset.labels)
model = pdx.det.PPYOLO(num_classes=num_classes)
model.train(
num_epochs=270,
train_dataset=train_dataset,
train_batch_size=32,
eval_dataset=eval_dataset,
learning_rate=0.00125,
lr_decay_epochs=[210,240],
save_interval_epochs=5,
save_dir='output/ppyolo',
# resume_checkpoint可设置恢复训练,传入想要恢复的模型参数文件夹即可
resume_checkpoint='output/ppyolo/epoch_100',
use_vdl=True)
复制代码
观察训练过程
中间出现了一个小插曲,就是我们的train的参数在进行配置的时候,save_interval_epochs=1,我们每训练一个epoch就会进行一轮数据的保存,一个epoch的数据保存的大小差不多500~600M,这样我们的硬盘的实际大小是100G,当我们训练到160多轮次的时候,硬盘存储直接爆掉了,所以后期将这个值设置成save_interval_epochs=5,在前一百轮迭代的时候,每隔5轮保存一次,比较节约空间,我们的上面的代码实际上resume_checkpoint=’output/ppyolo/epoch_100’,是从我们已经训练好的100轮的模型中读取参数作为接下来训练的预训练参数,这样我们的空间就能运用的起来了
- 优点: 我们节约了空间,每隔5轮进行一轮参数的保存
- 缺点:我们每次保留的数据,并不一定是这五轮训练中最优的数据,所以我们取的是近似最优解
一个比较好的优化方案:在前期我们的模型是在不断的迭代上升的阶段,这个时候的模型保存不保存其实意义不大,我们可以先进行100轮的训练,将save_interval_epochs=10,设置的跨度大一点,然中间100轮,我们可以减小这个参数save_interval_epochs=5,可以将近似的最优保存下来,粒度比之前要小,然后最后100轮,我们可以设置save_interval_epochs=1,以最小的粒度进行检索,不放过任何一个最优解,这样既能节约空间,又能获得最优模型,一举两得
我们能看到的信息是这是101个epoch训练完,Epoch 101 finished, loss=21.205597, lr=0.00125,在训练集的损失已经很小了。
第一百零五个epoch
我们能看到的信息是这是105个epoch中,训练完,[EVAL] Finished, Epoch=105, bbox_map=77.154663
,在验证集上我们的bbox_map已经可以达到77了,已经是很高的分数了,Model saved in output/ppyolo/best_model.Model saved in output/ppyolo/epoch_105.Current evaluated best model in eval_dataset is epoch_105, bbox_map=77.1546633102041
模型被存入了最好的模型中了
第二百七十个epoch
我们能看到的信息是这是第270个epoch中的,训练完 [EVAL] Finished, Epoch=270, bbox_map=73.998861
,第270轮模型验证完的bbox_map是73.99,Model saved in output/ppyolo/epoch_270. 2022-03-10 12:49:34 [INFO] Current evaluated best model in eval_dataset is epoch_165, bbox_map=81.40644180327331
模型被放入到了epoch270中,最好的模型还是epoch_165,并且bbox_map的大小是81.41。
验证代码
# 模型加载
model_dir = 'output/ppyolo/best_model'
model = pdx.load_model(model_dir)
# 在验证集上进行评估
eval_result = model.evaluate(eval_dataset, batch_size=1, return_details=False)
print('eval result:\n',eval_result)
复制代码
在测试集进行验证
验证部分的代码如下:
# 预测test数据集,结果保存到all_result中
import cv2
test_path = 'data/insects/test/images/'
save_path = 'all_result/'
!rm -rf all_result
os.makedirs(save_path)
font = cv2.FONT_HERSHEY_SIMPLEX
def Picture_frame(image,box_list):
for item in box_list:
# 接受阈值
if(item['score']>0.4):
x = int(item['bbox'][0])
y = int(item['bbox'][1])
w = x +int(item['bbox'][2])
h = y +int(item['bbox'][3])
cv2.rectangle(image, (x,y), (w, h), (0, 255, 0), 2)
text = item['category']+str(round(item['score'],3))
cv2.putText(img, text, (x, y), font, 0.9, (0, 0, 255), 2)
return image
for frame in os.listdir(test_path):
img_path = os.path.join(test_path,frame)
img = cv2.imread(img_path)
result = model.predict(img)
img = Picture_frame(img,result)
cv2.imwrite(os.path.join(save_path,frame),img)
复制代码
比较好的测试结果
但是对于位置较近的识别效果较差