昆虫目标检测---Paddlex目标检测

用PaddleX快速实现目标检测训练

比赛的链接:aistudio.baidu.com/aistudio/co…

PaddleX 1.3 版本文档:paddlex.readthedocs.io/zh_CN/relea…

直接使用paddle进行测试

验证飞桨安装

python -c "import paddle;paddle.utils.run_check()"
复制代码

获得下图,则安装成功 image.png

准备工作

用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__)"
复制代码

获得下图,则安装成功

image.png

飞桨 - PaddleX 是一套更加简明易懂的API,并配套一键下载安装的图形化开发客户端。用PaddleX实现图像分类训练非常快速,代码量也小。

数据的处理

image.png

!unzip -oq /home/aistudio/data/data19638/insects.zip -d ./data
复制代码

解压完成,我们进入data/inserts中查看下数据

image.png

构造需要的数据

使用下面这个脚本完成数据的

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 (主要是类别标签的名称)

image.png

制造train_list.txt和val_list.txt

image.png

  • train_list.txt (训练图片的路径和标签)

image.png

  • val_list.txt (验证图片的路径和标签)

image.png

数据展示

我们以其中一个数据为例进行展示

  • 图片数据

image.png

  • 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,以最小的粒度进行检索,不放过任何一个最优解,这样既能节约空间,又能获得最优模型,一举两得

image.png

我们能看到的信息是这是101个epoch训练完,Epoch 101 finished, loss=21.205597, lr=0.00125,在训练集的损失已经很小了。

第一百零五个epoch

image.png

我们能看到的信息是这是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

image.png

我们能看到的信息是这是第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)
复制代码

比较好的测试结果

image.png

image.png

但是对于位置较近的识别效果较差

image.png

image.png

image.png

おすすめ

転載: juejin.im/post/7073365077402845221
おすすめ