本项目使用PaddleX搭建目标检测模块,在一个精选的数据集上进行初步训练,并在另一个老年人跌倒检测的数据集上进行参数微调,实现了迁移学习的目标检测项目。
1.项目介绍
迁移学习是非常有用的方法,在实际生活中由于场景多样,环境复杂,一些场景复杂或者人体姿态不一的数据集较少,因此直接训练的难度较大,且效果往往不如迁移学习。
2.项目流程
- 我们使用两个数据集,一个是精选的精选的跌倒检测数据集Fall detection Dataset,一个是老年人跌倒数据集,在界面左侧数据集标签页可看到。
- 检查标签名称,看是否满足我们的需要,如不满足则需要处理数据集将标签统一和格式化,并检查文件夹名称是否符合我们的规范。
- 本项目使用的格式是PascalVOC格式
具体流程如下:首先部署开发环境,安装PaddleX;切割数据集、构建数据读取器;搭建PPYolo模型;使用其他场景数据集训练、验证模型;使用跌倒数据集进行微调并验证;使用微调后的模型进行推理。
3.项目环境
本项目的实验环境如下表所示
名称 | 版本 | 功能说明 |
---|---|---|
AIStudio | BML Codelab | 代码实现的基础平台与环境 |
PaddlePaddle | 2.3.2 | 在新建项目时选择,2.4.0会出现bug |
python | 3.7 | 默认选择 |
PaddleX | 2.1.0 | PaddleX套件用于搭建模型 |
4.预训练阶段
利用跌倒检测数据集Fall detection Dataset得到预训练模型,为后续模型的微调做准备。
4.1 环境安装
PaddleX集成飞桨智能视觉领域图像分类、目标检测、语义分割、实例分割任务能力,将深度学习开发全流程从数据准备、模型训练与优化到多端部署端到端打通,并提供统一任务API接口。无需分别安装不同套件,以低代码的形式即可快速完成飞桨全流程开发。
本项目主要采用PaddleX来实现目标预测项目。
In [ ]
# 安装相关包,在持久化文件夹下安装
!pip install pycocotools -t /home/aistudio/external-libraries
!pip install cython -t /home/aistudio/external-libraries
!pip install pyproject -t /home/aistudio/external-libraries
!pip install --user --upgrade pyarrow==11.0.0
# 此处如果使用持久化路径会找不到paddlex命令
!pip install paddlex -i https://pypi.tuna.tsinghua.edu.cn/simple
4.2 数据集预处理
数据集存放在data文件夹下,解压相关数据集并处理成我们想要的格式。
In [ ]
# 解压数据集文件
!unzip -oq /home/aistudio/data/data182907/Fall_Dataset.zip -d Fall_Dataset/
!unzip -oq /home/aistudio/data/data94809/pp_fall.zip -d pp_fall/
此时数据集已解压到相关目录,但不符合PAscalVOC格式
Annotations
进行detection 任务时的 标签文件,xml文件形式ImageSets
存放数据集的分割文件,比如train,val,testJPEGImages
存放 .jpg格式的图片文件SegmentationClass
存放 按照class 分割的图片SegmentationObject
存放 按照 object 分割的图片
In [ ]
# 对文件结构做处理
%cd /home/aistudio/Fall_Dataset/Fall_Dataset
!mv /home/aistudio/Fall_Dataset/Fall_Dataset/ImageSets/Main/test.txt /home/aistudio/Fall_Dataset/Fall_Dataset/ImageSets
!mv /home/aistudio/Fall_Dataset/Fall_Dataset/ImageSets/Main/train.txt /home/aistudio/Fall_Dataset/Fall_Dataset/ImageSets
!mv /home/aistudio/Fall_Dataset/Fall_Dataset/ImageSets/Main/trainval.txt /home/aistudio/Fall_Dataset/Fall_Dataset/ImageSets
!mv /home/aistudio/Fall_Dataset/Fall_Dataset/ImageSets/Main/val.txt /home/aistudio/Fall_Dataset/Fall_Dataset/ImageSets
!mv /home/aistudio/Fall_Dataset/Fall_Dataset/train.txt /home/aistudio/Fall_Dataset/Fall_Dataset/train_list.txt
!mv /home/aistudio/Fall_Dataset/Fall_Dataset/trainval.txt /home/aistudio/Fall_Dataset/Fall_Dataset/val_list.txt
!rm -rf /home/aistudio/Fall_Dataset/Fall_Dataset/ImageSets/Main
!mv /home/aistudio/Fall_Dataset/Fall_Dataset/label_list.txt /home/aistudio/Fall_Dataset/Fall_Dataset/labels.txt
In [ ]
# 检查一下数据集里的object
from xml.etree import ElementTree as ET
from collections import defaultdict
import sys
import os
sys.path.append('/home/aistudio/external-libraries')
path = '/home/aistudio/Fall_Dataset/Fall_Dataset/Annotations/'
path_pp = '/home/aistudio/pp_fall/Annotations/'
name_dict = defaultdict(set)
for filename in os.listdir(path_pp):
if not os.path.isdir(path_pp + filename):
tree = ET.parse(path_pp + filename)
root = tree.getroot()
ets = root.findall('object')
for et in ets:
name = et.findall('name')[0].text
name_dict[name].add(filename)
# 如果有问题可以删除节点
# if name.text == 'XX': # 修改标签
# name.text='XXX'
# elif name.text == 'YY': # 删除无关标签
# root.remove(et)
print(name_dict.keys())
In [ ]
!mv /home/aistudio/pp_fall/images /home/aistudio/pp_fall/JPEGImages
# 使用paddlex切分数据集,根据37比例划分出验证集和训练集
# 注意,要确定符合PascalVOC格式才会成功裁剪
# Fall_Dataset无须此命令,可以直接用分割好的数据集
!paddlex --split_dataset --format VOC --dataset_dir /home/aistudio/pp_fall/ --val_value 0.3
我们得到了最终的数据集结构,Annotation和JPEGImages分别存放标签和图像文件;labels.txt存放标签名称;而train_list.txt和val_list.txt分别存放训练集和验证集,这两个txt在第4步搭建数据读取器中作为参数传入。
4.3 数据预处理
定义一些数据增强方法,可以使模型的泛化能力更强而不会过拟合。
数据增强方法有多种,具体选择哪几个需要根据任务特点、数据特征来确定。例如:图像竖直翻转之后会和实际严重不符,增加模型的学习难度而对实际表现没有任何帮助。
In [ ]
# 导入python库,如果遇到pyarrow报错,可以试试重启内核
import numpy as np
import paddlex as pdx
from paddlex import transforms as T
In [ ]
# 设置数据增强的方式
# API说明:https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/apis/transforms/transforms.md
train_transforms = T.Compose([
T.MixupImage(), # mixup增强
T.RandomDistort(), # 随机调整亮度、对比度、饱和度、色调
T.RandomExpand(), # 随机扩张图像
T.RandomCrop(), # 随机裁剪图像
T.RandomHorizontalFlip(), # 随机水平翻转
T.BatchRandomResize( # 训练批次图像的输入大小
target_sizes=[320, 352, 384, 416, 448, 480, 512, 544, 576, 608]), # 为了确保大目标和小目标都可以有效学习,我们设置了不通的输入大小
T.Normalize() # 对图像进行标准化
])
eval_transforms = T.Compose([
T.Resize(target_size=480), # 设置验证时输入大小,此处选择了480,平衡了目标大小和计算时间
T.Normalize() # 对图像进行标准化
])
4.4 构建数据读取器
利用准备好的数据构建之后要用的数据集变量,准备了训练集和验证集,所以定义了两个。
In [ ]
%cd
# 定义数据集
# 定义训练和验证所用的数据集
train_dataset = pdx.datasets.VOCDetection(
data_dir='pp_fall', # 数据集存放的路径
file_list='pp_fall/train_list.txt', # 训练集划分的txt文件
label_list='pp_fall/labels.txt', # 训练数据的类别文件
transforms=train_transforms, # 定义数据转换
shuffle=True) # 随机打乱数据集
# 验证数据集的构建和上面训练的一样
eval_dataset = pdx.datasets.VOCDetection(
data_dir='pp_fall', # 数据集存放的路径
file_list='pp_fall/val_list.txt', # 训练集划分的txt文件
label_list='pp_fall/labels.txt', # 训练数据的类别文件
transforms=eval_transforms, # 定义数据转换
shuffle=False) # 验证数据集没有必要打乱
4.5 构建模型
我们这里使用了PPYOLOv2,参考了其它的项目。
PPYOLOv2版本在COCO 2017 test-dev上的精度为49.5%;在640x640的输入尺寸下,FPS达到68.9FPS。PP-YOLOv2在同等速度下,精度超越YOLOv5。满足我们的需求,因此选择PPYOLOv2。
In [ ]
# 初始化模型,并进行训练
# 可使用VisualDL查看训练指标,参考https://github.com/PaddlePaddle/PaddleX/tree/release/2.0.0/tutorials/train#visualdl可视化训练指标
# 确定目标类别数
num_classes = len(train_dataset.labels)
# 定义PPYOLOv2模型
model = pdx.det.PPYOLOv2(num_classes=num_classes) # 我们只需要修改类别数即可
接下来可以开始训练了,使用PaddleX快速完成训练,具体参数使用请参考单元格,
In [ ]
# API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0.0/paddlex/cv/models/detector.py
# 各参数介绍与调整说明:https://paddlex.readthedocs.io/zh_CN/develop/appendix/parameters.html
model.train(
num_epochs=5, # 训练轮次
train_dataset=train_dataset, # 导入训练数据集读取器
eval_dataset=eval_dataset, # 导入验证数据集读取器
train_batch_size=16, # 批大小,在32G显存下可以用16,bs越大训练效果越好
learning_rate=0.00025, # 根据批大小和模型复杂度选择合理的学习率
warmup_steps=500, # 预热步数,学习率会逐渐增大。预热是为了训练过程能够更加稳定
warmup_start_lr=0.0, # 预热起始学习率
save_interval_epochs=1, # 每1轮次都保存一次,有验证数据时会进行评估
log_interval_steps=50, # 每迭代50次打印一次日志
lr_decay_epochs=[35, 65], # step学习率衰减
save_dir='output/pp_fall_PPYOLOv2', # 保存路径
use_vdl=False, # 用visuadl进行可视化训练记录,此处没有使用
# resume_checkpoint='output/PPYOLOv2/epoch_4/', # 重新训练时请把此处改为上一代的地址,并把下一行的pretrain_weights设为None
# pretrain_weights=None,
pretrain_weights='COCO', # 预训练权重
)
5.模型微调
接下来读取预训练相关模型进行微调,以达到良好的效果。
同样设置数据增强以及数据读取器。
In [ ]
# 设置数据增强的方式
# API说明:https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/apis/transforms/transforms.md
train_transforms = T.Compose([
T.MixupImage(), # mixup增强
T.RandomDistort(), # 随机调整亮度、对比度、饱和度、色调
T.RandomExpand(), # 随机扩张图像
T.RandomCrop(), # 随机裁剪图像
T.RandomHorizontalFlip(), # 随机水平翻转
T.BatchRandomResize( # 训练批次图像的输入大小
target_sizes=[320, 352, 384, 416, 448, 480, 512, 544, 576, 608]), # 为了确保大目标和小目标都可以有效学习,我们设置了不通的输入大小
T.Normalize() # 对图像进行标准化
])
eval_transforms = T.Compose([
T.Resize(target_size=480), # 设置验证时输入大小,此处选择了480,平衡了目标大小和计算时间
T.Normalize() # 对图像进行标准化
])
In [ ]
%cd
# 定义数据集
# 定义训练和验证所用的数据集
# API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0.0/paddlex/cv/datasets/voc.py
train_dataset = pdx.datasets.VOCDetection(
data_dir='Fall_Dataset/Fall_Dataset', # 数据集存放的路径
file_list='Fall_Dataset/Fall_Dataset/train_list.txt', # 训练集划分的txt文件
label_list='Fall_Dataset/Fall_Dataset/labels.txt', # 训练数据的类别文件
transforms=train_transforms, # 定义数据转换
shuffle=True) # 随机打乱数据集
# 验证数据集的构建和上面训练的一样
eval_dataset = pdx.datasets.VOCDetection(
data_dir='Fall_Dataset/Fall_Dataset', # 数据集存放的路径
file_list='Fall_Dataset/Fall_Dataset/val_list.txt', # 验证集划分的txt文件
label_list='Fall_Dataset/Fall_Dataset/labels.txt', # 验证数据的类别文件
transforms=eval_transforms, # 定义数据转换
shuffle=False) # 验证数据集没有必要打乱
微调之前可以看一下模型的初始效果。
In [ ]
# 先使用预训练模型进行测试
# 载入模型
model = pdx.load_model("output/pp_fall_PPYOLOv2/best_model/")
# 进行验证
model.evaluate(eval_dataset, batch_size=1)
接下来进行模型微调的训练。
此处warmup_steps=68最低设置为68.
In [ ]
# API说明:https://github.com/PaddlePaddle/PaddleX/blob/release/2.0.0/paddlex/cv/models/detector.py
# 各参数介绍与调整说明:https://paddlex.readthedocs.io/zh_CN/develop/appendix/parameters.html
model.train(
num_epochs=6, # 训练轮次
train_dataset=train_dataset, # 训练数据
eval_dataset=eval_dataset, # 验证数据
train_batch_size=16, # 批大小,在32G显存下可以用16,但是不稳定,容易内存溢出0
learning_rate=0.0002, # 学习率
warmup_steps=68, # 预热步数,学习率会逐渐增大
warmup_start_lr=0.0, # 预热起始学习率
save_interval_epochs=1, # 每1轮次都保存一次,有验证数据时会进行评估
log_interval_steps=50, # 每迭代50次打印一次日志
lr_decay_epochs=[4], # step学习率衰减 35, 65
save_dir='output/Finish_PPYOLOv2', # 保存路径
use_vdl=False, # 用visuadl进行可视化训练记录,此处没有使用
pretrain_weights='output/pp_fall_PPYOLOv2/best_model/model.pdparams', # 预训练权重
)
之后载入模型,并且在验证集上进行验证。
In [ ]
# 载入模型
model = pdx.load_model("output/Finish_PPYOLOv2/best_model/")
# 进行验证
model.evaluate(eval_dataset, batch_size=1)
6.模型预测
我们已经完成了模型预训练、微调、模型载入,接下来会进行模型预测。在实际生产中,部署完模型文件后,可以使用下面代码进行推理。
In [ ]
%cd
# 导入必要的库
import glob
import numpy as np
import threading
import time
import random
import os
import base64
import cv2
import json
import paddlex as pdx
# 待预测图片路径
# image_name = 'test/电力高架线路_31_31.jpg'
image_name = 'test/test-2.png'
# 预测模型加载
model = pdx.load_model('output/Finish_PPYOLOv2/best_model')
# 读取图片
img = cv2.imread(image_name)
# 预测图片并获得结果
result = model.predict(img)
# 解析预测结果,并保存到txt中
keep_results = []
areas = []
# 获取txt文件
f = open('result.txt', 'a')
# 计数
count = 0
for dt in np.array(result): # 遍历结果, 并将结果写入到result.txt
cname, bbox, score = dt['category'], dt['bbox'], dt['score'] # 获取目标框的种类、坐标、置信度
if score < 0.5: # 根据置信度过滤掉置信度较低的目标框
continue
keep_results.append(dt) # 将置信度符合的目标框保存下来
count += 1
f.write(str(dt) + '\n') # 写入txt中
f.write('\n')
print(count)
f.write("the total number is :" + str(int(count)))
f.close()
# 可视化保存
pdx.det.visualize(
image_name, result, threshold=0.5, save_dir='./output/PPYOLOv2')
生成的结果图片保存至./output/PPYOLOv2文件夹下,可进行查看。
7.总结提高
本项目基于PaddleX进行了目标检测的开发,且用了迁移学习以达到良好的效果,从自身体会上来说PaddleX确实是个挺好的框架,能够快速的开发想要的模型,强烈推荐!!!