主要目的是在没有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的安装命令

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的安装命令

python -m pip install paddlepaddle -i https://mirror.baidu.com/pypi/simple

  

2.2 PaddleDetection的安装

PaddleDetection的安装相对简单,没有分支。
我这里是在 D:\lusong\ppdetection\ 目录下操作。

 

安装

 

命令行

git clone https://gitee.com/paddlepaddle/PaddleDetection

cd PaddleDetection
pip install -r requirements.txt

python setup.py install

  

验证

命令行

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 数据集拆分

对数据集完成训练集,验证集,测试集的切分。下面是拆分代码:

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 训练

调用命令行:

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 继续训练

有时候会意外或主动中断,如需继续训练的话,调用命令行:

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 推理

中断训练后,项看当前训练的怎么样的话,调用命令行:

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,会时不时异常退出,浪费时间巨大。

 

贵阳正值疫情期间,心情有点慌张。

posted on 2022-09-12 20:20  EpicBlue  阅读(1157)  评论(0编辑  收藏  举报