【飞桨开发者说】侯继旭,海南师范大学本三自动化专业在读,人工智能开发爱好者,曾获2019中国高校计算机大赛-人工智能创意赛海南省一等奖、2019年度海南省高等学校科学研究“人工智能”优秀成果奖
本项目以ssd_mobilenet_v1_voc算法为例,详细介绍了从准备数据集、到模型训练,并将模型部署到树莓派的全过程。缺训练数据的痛苦,相信做过模型训练的小伙伴们都深有感触。为了便于大家实战操作,训练数据统统提供!无需任何准备工作就可以体验全流程哈。
项目用到的开源工具包括百度的深度学习平台飞桨以及模型开发套件PaddleDetection、端侧部署工具Paddle Lite、百度一站式AI开发平台AI Studio和树莓派4B。可通过以下方式进行在线或者本地体验:
在线体验:
项目已在AI Studio上公开,包括数据集在内已经打包上传,代码可在线跑通,欢迎Fork!链接:https://aistudio.baidu.com/aistudio/projectdetail/331209
本地体验:
项目涉及的全部资料也都打包放在百度网盘(PaddleDetection、Paddle Lite Demo、Paddle Lite、opt),可下载到本地体验。
链接:https://pan.baidu.com/s/1IKT-ByVN9BaVxfqQC1VaMw 提取码:mdd1
数据集准备
import os
import random
trainval_percent = 0.95 #训练集验证集总占比
train_percent = 0.9 #训练集在trainval_percent里的train占比
xmlfilepath = 'F:/Cola/Annotations'
txtsavepath = 'F:/Cola/ImageSets/Main'
total_xml = os.listdir(xmlfilepath)
num=len(total_xml)
list=range(num)
tv=int(num*trainval_percent)
tr=int(tv*train_percent)
trainval= random.sample(list,tv)
train=random.sample(trainval,tr)
ftrainval = open('F:/Cola/ImageSets/Main/trainval.txt', 'w')
ftest = open('F:/Cola/ImageSets/Main/test.txt', 'w')
ftrain = open('F:/Cola/ImageSets/Main/train.txt', 'w')
fval = open('F:/Cola/ImageSets/Main/val.txt', 'w')for i in list:
name=total_xml[i][:-4]+'\n'if i in trainval:
ftrainval.write(name)if i in train:
ftrain.write(name)else:
fval.write(name)else:
ftest.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest .close()
以下代码可根据在Main文件夹中划分好的数据集进行位置索引,生成含有图像及对应的XML文件的地址信息的文件。
import os
import re
import random
devkit_dir = './'
output_dir = './'
def get_dir(devkit_dir, type):return os.path.join(devkit_dir, type)
def walk_dir(devkit_dir):
filelist_dir = get_dir(devkit_dir, 'ImageSets/Main')
annotation_dir = get_dir(devkit_dir, 'Annotations')
img_dir = get_dir(devkit_dir, 'JPEGImages')
trainval_list = []
train_list = []
val_list = []
test_list = []
added = set()for _, _, files in os.walk(filelist_dir):for fname in files:print(fname)
img_ann_list = []if re.match('trainval.txt', fname):
img_ann_list = trainval_list
elif re.match('train.txt', fname):
img_ann_list = train_list
elif re.match('val.txt', fname):
img_ann_list = val_list
elif re.match('test.txt', fname):
img_ann_list = test_listelse:
continue
fpath = os.path.join(filelist_dir, fname)for line in open(fpath):
name_prefix = line.strip().split()[0]print(name_prefix)
added.add(name_prefix)
#ann_path = os.path.join(annotation_dir, name_prefix + '.xml')
ann_path = annotation_dir + '/' + name_prefix + '.xml'print(ann_path)
#img_path = os.path.join(img_dir, name_prefix + '.jpg')
img_path = img_dir + '/' + name_prefix + '.jpg'assert os.path.isfile(ann_path), 'file %s not found.' % ann_pathassert os.path.isfile(img_path), 'file %s not found.' % img_path
img_ann_list.append((img_path, ann_path))print(img_ann_list)return trainval_list, train_list, val_list, test_list
def prepare_filelist(devkit_dir, output_dir):
trainval_list = []
train_list = []
val_list = []
test_list = []
trainval, train, val, test = walk_dir(devkit_dir)
trainval_list.extend(trainval)
train_list.extend(train)
val_list.extend(val)
test_list.extend(test)
#print(trainval)
with open(os.path.join(output_dir, 'trainval.txt'), 'w') as ftrainval:for item in trainval_list:
ftrainval.write(item[0] + ' ' + item[1] + '\n')
with open(os.path.join(output_dir, 'train.txt'), 'w') as ftrain:for item in train_list:
ftrain.write(item[0] + ' ' + item[1] + '\n')
with open(os.path.join(output_dir, 'val.txt'), 'w') as fval:for item in val_list:
fval.write(item[0] + ' ' + item[1] + '\n')
with open(os.path.join(output_dir, 'test.txt'), 'w') as ftest:for item in test_list:
ftest.write(item[0] + ' ' + item[1] + '\n')if __name__ == '__main__':
prepare_filelist(devkit_dir, output_dir)
最终创建完成的VOC数据集如下:
将整个文件拷贝至 ./PaddleDetection/dataset/voc 下 以上全部完成后,还需要修改两个地方,ssd
mobilenetv1_voc源码中是以20类目标为准设计的,本项目的目标仅为两类 1. 找到 ./PaddleDetection/configs/ssd/ssd
mobilenetv1
voc.yml文件,修改第12行的numclasses,3代表2个标签加一个背景
num_classes: 3
2. 找到 ./PaddleDetection/ppdet/data/source/voc.py文件,修改167行的pascalvoc
label()函数,按照前面设定的labellist.txt文件里的标签顺序依次修改,并将多余的内容删掉
def pascalvoc_label(with_background=True):
labels_map = {
'PepsiCola': 1,'CocaCola': 2
}if not with_background:
labels_map = {k: v - 1 for k, v in labels_map.items()}return labels_map
至此,整个数据集制作及配置完成。
创建项目
环境配置
#安装Python依赖库
!pip install -r requirements.txt#测试项目环境
!export PYTHONPATH=`pwd`:$PYTHONPATH
!python ppdet/modeling/tests/test_architectures.py
1. 设置环境变量
%env PYTHONPATH=/home/aistudio/PaddleDetection
2. 找到报错的文件添加以下代码
import sys
DIR = '/home/aistudio/PaddleDetection'
sys.path.append(DIR)
测试环境通过后,就可以开始训练了
开始训练
%cd home/aistudio/PaddleDetection/
!python -u tools/train.py -c configs/ssd/ssd_mobilenet_v1_voc.yml --use_tb=True --eval
训练完成后输出的模型保存在 ./PaddleDetection/output/ssdmobilenetv1voc 文件夹下,本次训练总轮数默认为28000轮,每隔2000轮保存一次模型,以轮次命名的均为阶段性模型,modelfinal为训练结束时保存的模型,best_model是每次评估后的最佳mAP模型
#测试,查看模型效果
%cd home/aistudio/PaddleDetection/
!python tools/infer.py -c configs/ssd/ssd_mobilenet_v1_voc.yml --infer_img=/home/aistudio/2001.jpg#infer_img输入需要预测图片的路径,看一下效果
模型转换
!python -u tools/export_model.py -c configs/ssd/ssd_mobilenet_v1_voc.yml --output_dir=./inference_model_final
生成的预测模型保存在 ./PaddleDetection/inference
model final/ssd
mobilenet v1_voc 文件夹下,会生成两个文件,模型文件名和参数文件名分别为
model 和
params。
部署到树莓派4B上需要使用Paddle Lite,而飞桨的原生模型需要经过 opt 工具转化为Paddle Lite可以支持的naive_buffer格式。
%cd /home/aistudio/#复制opt文件到相应目录下
!cp opt /home/aistudio/PaddleDetection/inference_model_final/ssd_mobilenet_v1_voc#进入预测模型文件夹%cd /home/aistudio/PaddleDetection/inference_model_final/ssd_mobilenet_v1_voc#下载opt文件#!wget https://github.com/PaddlePaddle/Paddle-Lite/releases/download/v2.3.0/opt#给opt加上可执行权限
!chmod +x opt#使用opt进行模型转化,将__model__和__params__转化为model.nb
!./opt --model_file=__model__ --param_file=__params__ --optimize_out_type=naive_buffer --optimize_out=./model
!ls
这个opt自己下载实在是太慢了,因此我在网盘里已经准备好了opt文件,可以直接上传至AI Studio操作,最终结果如下图所示:
到目前为止,在AI Studio上的所有内容已经完成,生成了这个model.nb文件,就可将其部署在树莓派4B上使用。
预测库编译
# 1. Install basic software
apt update
apt-get install -y --no-install-recomends \
gcc g++ make wget python unzip# 2. install cmake 3.10 or above
wget https://www.cmake.org/files/v3.10/cmake-3.10.3.tar.gz
tar -zxvf cmake-3.10.3.tar.gz
cd cmake-3.10.3
./configure
make
sudo make install
此环境树莓派应该是会有的,可以自行检查,没有的包安装上即可。至此完成所有的编译环境配置。
cd /home/pi/Paddle/Paddle-Lite
sudo ./lite/tools/build.sh \--build_extra=OFF \--arm_os=armlinux \--arm_abi=armv7hf \--arm_lang=gcc \
tiny_publish
虽然树莓派4B已经是 ARMv8 的CPU架构,但官方系统为32位,还是需要使用ARMv7架构的编译方式
编译结束,结果如下:
文件结构搭建
模型部署
#!/bin/bash# configure#TARGET_ARCH_ABI=armv8 # for RK3399, set to default arch abi
TARGET_ARCH_ABI=armv7hf # for Raspberry Pi 3B
PADDLE_LITE_DIR=/home/pi/Desktop/Paddle-Lite-Demo/PaddleLite-armlinux-demo/object_detection_demo/Paddle-Liteif [ "x$1" != "x" ]; then
TARGET_ARCH_ABI=$1fi# build
rm -rf build
mkdir buildcd build
cmake -DPADDLE_LITE_DIR=${PADDLE_LITE_DIR} -DTARGET_ARCH_ABI=${TARGET_ARCH_ABI} ..
make#run
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${PADDLE_LITE_DIR}/libs/${TARGET_ARCH_ABI} ./object_detection_demo ../models/model.nb ../labels/pascalvoc_label_list ../images/2001.jpg ./result.jpg
修改完run.sh文件后,就算是完成了所有的配置内容,可以开始放心的 RUN 了!!
/home/pi/Desktop/Paddle-Lite-Demo/PaddleLite-armlinux-demo/object_detection_demo/code
sudo ./run.sh
最后的输出结果如下:
图片的预测结果就是这样了,虽然一个类别只有300张图,但是总的来说结果还算不错!
关于视频流的实时监测,在源代码的主函数中可以看到
if (argc > 3) {
WARMUP_COUNT = 1;
REPEAT_COUNT = 5;std::string input_image_path = argv[3];std::string output_image_path = argv[4];
cv::Mat input_image = cv::imread(input_image_path);
cv::Mat output_image = process(input_image, word_labels, predictor);
cv::imwrite(output_image_path, output_image);
cv::imshow("Object Detection Demo", output_image);
cv::waitKey(0);
} else {
cv::VideoCapture cap(-1);
cap.set(cv::CAP_PROP_FRAME_WIDTH, 640);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480);if (!cap.isOpened()) {
return -1;
}while (1) {
cv::Mat input_image;
cap >> input_image;
cv::Mat output_image = process(input_image, word_labels, predictor);
cv::imshow("Object Detection Demo", output_image);if (cv::waitKey(1) == char('q')) {
break;
}
}
cap.release();
cv::destroyAllWindows();
}
当我们在 `run.sh` 文件中设置小于三个参数时,即可使用视频流实时监测。 在这里注释 掉图片路径和输出路径即可。
#runLD_LIBRARY_PATH=$LD_LIBRARY_PATH:${PADDLE_LITE_DIR}/libs/${TARGET_ARCH_ABI} ./object_detection_demo ../models/ssd_mobilenet_v1_pascalvoc_for_cpu/best.nb ../labels/pascalvoc_label_list #../images/2.jpg ./result.jpg
注:如果有用 Opencv-4.1.0 版本的,可能在编译 object
detectiondemo.cc 时在 267、268 行会报错。
cap.set(CV_CAP_PROP_FRAME_WIDTH, 640);
cap.set(CV_CAP_PROP_FRAME_HEIGHT, 480);
由于新版本的API发生了变化。需要修改为如下代码:
cap.set(cv::CAP_PROP_FRAME_WIDTH, 640);cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480);
至此,整个项目就完成了。 参考资料
有奖调研
为了更好的了解您在深度学习领域的需求及对飞桨的建议反馈,希望您可以抽出3分钟的宝贵时间填写问卷,您的建议和反馈,将会支持我们进行产品的迭代和优化!调研结束后,我们将选出5️⃣名最佳贡献者,5️⃣名特别关注者,以及再随机抽取?名用户感恩回馈,送上精选礼品???感谢大家对我们的支持和关注!问卷链接(点击 “阅读原文” 打开): https://iwenjuan.baidu.com/?code=ciynjt 如在使用过程中有问题,可加入飞桨官方QQ群进行交流: 703252161 。 如果您想详细了解更多飞桨的相关内容,请参阅以下文档。 官网地址: https://www.paddlepaddle.org.cn 飞桨开源框架项目地址: GitHub: https://github.com/PaddlePaddle/Paddle Gitee: https://gitee.com/paddlepaddle/PaddleEND