主要目的是在没有GPU的情况下,上手ppyolo的训练过程,看看paddlepaddle是不是顺手。纯代码实验。PaddleDetection在下文中简称ppdet。
1 基本环境
1.1 软件组成和版本
Windows>= 7
python=3.8
paddle.__version__ '2.3.2'
ppdet.__version__ '2.4.0'
1.2 数据集
HelmetDetection(VOC)
1.3 网络结构
ppyolo_r50vd_dcn
1.4 预训练模型
ResNet50_vd_ssld_pretrained.pdparams
2 环境的搭建
2.1 PaddlePaddle的安装
PaddleDetection依赖apddlepaddle,对于paddlepaddle来说,CPU分为支持avx和不支持avx(比如:Intel Core 2 Q8300),需要手动执行安装。
不支持avx的安装命令
1 | pip install https: //paddle-wheel .bj.bcebos.com /2 .3.0 /windows/windows-cpu-mkl-noavx/paddlepaddle-2 .3.0-cp38-cp38-win_amd64.whl |
支持avx的安装命令
1 | python -m pip install paddlepaddle -i https: //mirror .baidu.com /pypi/simple |
2.2 PaddleDetection的安装
PaddleDetection的安装相对简单,没有分支。
我这里是在 D:\lusong\ppdetection\ 目录下操作。
安装
命令行
1 2 3 4 5 6 | git clone https: //gitee .com /paddlepaddle/PaddleDetection cd PaddleDetection pip install -r requirements.txt python setup.py install |
验证
命令行
1 | python ppdet /modeling/tests/test_architectures .py |
2.3 数据集
数据集是偶然看到的VOC格式,下载地址为:
http://aistudio.baidu.com/aistudio/projectdetail/1059610
我自己解压缩到 D:\lusong\other\det\HelmetDetection目录。
2.4 预训练模型
模型为RestNet50,下载地址为:
https://paddledet.bj.bcebos.com/models/pretrained/ResNet50_vd_ssld_pretrained.pdparams
到此,下载部分结束,可以断开网络继续操作。
3 训练前处理
3.1 数据集拆分
对数据集完成训练集,验证集,测试集的切分。下面是拆分代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | import os.path as osp import random import xml.etree.ElementTree as ET # dataset_dir,数据集所在路径,文件夹层级如下: {dataset_dir} / images / xxx.jpg annotations / xxx.xml # val_percent,验证百分比 # test_percent,测试百分比 # save_dir,数据集保存到路径(格式:VOC),注意这里和标准的VOC格式是不一样的 {save_dir} / train_list.txt val_list.txt test_list.txt labels.txt # 注意,由上可见:本函数默认dataset_dir和save_dir是同一目录 def split_voc_dataset(dataset_dir, val_percent, test_percent, save_dir): # 注意图片目录和标注目录名已全部修改??? # 检查images目录和annotations目录是否存在 if not osp.exists(osp.join(dataset_dir, "images" )): logging.error( "\'images\' is not found in {}!" . format (dataset_dir)) if not osp.exists(osp.join(dataset_dir, "annotations" )): logging.error( "\'annotations\' is not found in {}!" . format ( dataset_dir)) # # 获取images目录下所有文件名(images目录及下一层目录) all_image_files = list_files(osp.join(dataset_dir, "images" )) # # 元素为 [image_file, anno_name] image_anno_list = list () # 元素为 anno_name文件中的所有object.name文本(去重) label_list = list () for image_file in all_image_files: if not is_pic(image_file): continue # 在annotations目录下,寻找image_file对应的anno_name文件,找到后加入image_anno_list anno_name = replace_ext(image_file, "xml" ) if osp.exists(osp.join(dataset_dir, "annotations" , anno_name)): image_anno_list.append([image_file, anno_name]) # 将anno_name文件中的object子节点的name节点找出来,加入label_list(去重) try : tree = ET.parse( osp.join(dataset_dir, "annotations" , anno_name)) except : raise Exception( "文件{}不是一个良构的xml文件,请检查标注文件" . format ( osp.join(dataset_dir, "annotations" , anno_name))) objs = tree.findall( "object" ) for i, obj in enumerate (objs): cname = obj.find( 'name' ).text if not cname in label_list: label_list.append(cname) else : logging.error( "The annotation file {} doesn't exist!" . format ( anno_name)) # # 打乱image_anno_list后,拆分成train_image_anno_list,val_image_anno_list,test_image_anno_list random.shuffle(image_anno_list) image_num = len (image_anno_list) val_num = int (image_num * val_percent) test_num = int (image_num * test_percent) train_num = image_num - val_num - test_num # train_image_anno_list = image_anno_list[:train_num] val_image_anno_list = image_anno_list[train_num:train_num + val_num] test_image_anno_list = image_anno_list[train_num + val_num:] # # 创建{save_dir}/train_list.txt 文件 with open ( osp.join(save_dir, 'train_list.txt' ), mode = 'w' , encoding = 'utf-8' ) as f: for x in train_image_anno_list: file = osp.join( "images" , x[ 0 ]) label = osp.join( "annotations" , x[ 1 ]) f.write( '{} {}\n' . format ( file , label)) # # 创建{save_dir}/val_list.txt 文件 with open ( osp.join(save_dir, 'val_list.txt' ), mode = 'w' , encoding = 'utf-8' ) as f: for x in val_image_anno_list: file = osp.join( "images" , x[ 0 ]) label = osp.join( "annotations" , x[ 1 ]) f.write( '{} {}\n' . format ( file , label)) # # 创建{save_dir}/test_list.txt 文件 if len (test_image_anno_list): with open ( osp.join(save_dir, 'test_list.txt' ), mode = 'w' , encoding = 'utf-8' ) as f: for x in test_image_anno_list: file = osp.join( "images" , x[ 0 ]) label = osp.join( "annotations" , x[ 1 ]) f.write( '{} {}\n' . format ( file , label)) # # 创建{save_dir}/labels.txt 文件 with open ( osp.join(save_dir, 'labels.txt' ), mode = 'w' , encoding = 'utf-8' ) as f: for l in sorted (label_list): f.write( '{}\n' . format (l)) # return train_num, val_num, test_num if __name__ = = "__main__" : # 切分数据集 split_voc_dataset( 'PaddleDetection/dataset/MyDataset' , 0.2 , 0.1 , 'PaddleDetection/dataset/MyDataset' ) |
运行上面代码完成后,训练集、验证集、测试集分别记录在文件:train_list.txt,val_list.txt,test_list.txt文件中。
3.2 数据集的配置
数据集的配置主要是写明训练集/验证集/测试集的文件位置(ppdet和数据集路径见上文所述)。
D:\lusong\ppdetection\PaddleDetection\configs\datasets\voc.yml
dataset_dir路径
该路径下包括anno_path所指文件。无需标准的VOC的两级目录:VOCdevkit/VOC2007(或VOC2012)。
代码(yml)
metric: VOC
map_type: 11point
num_classes: 4
TrainDataset:
!VOCDataSet
dataset_dir: D:\lusong\other\det\HelmetDetection
anno_path: train_list.txt
label_list: labels.txt
data_fields: ['image', 'gt_bbox', 'gt_class', 'difficult']
EvalDataset:
!VOCDataSet
dataset_dir: D:\lusong\other\det\HelmetDetection
anno_path: val_list.txt
label_list: labels.txt
data_fields: ['image', 'gt_bbox', 'gt_class', 'difficult']
TestDataset:
!ImageFolder
anno_path: dataset/voc/test_list.txt
4 训练
调用命令行:
1 2 3 4 5 6 7 | python -u tools /train .py -c D:\lusong\ppdetection\PaddleDetection\configs\ppyolo\ppyolo_r50vd_dcn_voc_1.yml -o use_gpu= false pretrain_weights=D:\lusong\other\det\model\ResNet50_vd_ssld_pretrained.pdparams -- eval |
4.1 继续训练
有时候会意外或主动中断,如需继续训练的话,调用命令行:
1 2 3 4 5 | python -u tools /train .py \ -c D:\lusong\ppdetection\PaddleDetection\configs\ppyolo\ppyolo_r50vd_dcn_voc_1.yml \ -o use_gpu= false \ -r output /ppyolo_r50vd_dcn_voc_1/best_model \ -- eval |
5 推理
中断训练后,项看当前训练的怎么样的话,调用命令行:
1 2 3 4 | python -u tools /infer .py -c D:\lusong\ppdetection\PaddleDetection\configs\ppyolo\ppyolo_r50vd_dcn_voc_1.yml -o use_gpu= false weights=D:\lusong\ppdetection\PaddleDetection\output\ppyolo_r50vd_dcn_voc_1\best_model.pdparams --infer_img=D:\lusong\other\det\HelmetDetection\images\hard_hat_workers0.png |
至此,可以启动训练,并在成功保存模型的情况下,执行推理。
6 对训练时长的缩减
6.1 修改ppyolo_r50vd_dcn_voc.yml,缩减:训练轮数,batch尺寸,保存时间间隔(轮数)。减少整个训练时长。
snapshot_epoch: 1
TrainReader:
batch_size: 2
epoch: 10
6.2 修改数据集文件,缩减:单轮训练图片数量,单轮验证图片数量。减少单轮训练时长。
缩减单轮训练图片数量:编辑train_list.txt文件,一行一个训练样本,根据训练时长整行删除即可。
缩减单轮验证图片数量:同上编辑val_list.txt文件。
7 小结
1 简单感受了一下VOC格式,划分的训练集,ppdet的配置方式,调用方式。
2 训练代码还是比较简单,很容易上手。
3 纯时间耗费不起(原训练配置ppdet估计时长为140天左右)。
4 ppdet=2.6有bug,会时不时异常退出,浪费时间巨大。
贵阳正值疫情期间,心情有点慌张。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)