从零开始使用最新版Paddle【PaddleOCR系列】——第二部分:自建数据集 + 模型微调训练

发布于:2024-10-16 ⋅ 阅读:(14) ⋅ 点赞:(0)

       

目录

一、自建数据集

        1.官方数据集格式参考

        2.自建数据集txt文件编写代码

        3.数据集检验

二、模型训练

        1.模型配置yaml文件

        2.命令行指令训练


        在上一篇文章中,构建好了paddleOCR 运行必需的环境,并通过在线下载的方式,使用官方训练好的模型进行了简单的部署预测。但现实使用中,对于特定任务的数据集,如果想要提高精度表现,需要在原始模型的基础上进行训练微调。

        因此,本文记录如何构建符合paddlepaddle训练要求格式的数据集,以及如何使用官方提供的便利低代码量平台 PaddleX 进行模型训练。

        环境篇文章快速链接:https://blog.csdn.net/qq_58718853/article/details/142875253

        本文属于是记录性文档,斗胆抛砖引玉,详细参见官方文档地址:https://gitee.com/paddlepaddle/PaddleX/blob/release/3.0-beta1/docs/module_usage/tutorials/ocr_modules/text_detection.md

一、自建数据集

        1.官方数据集格式参考

        在上篇文章中介绍了官方测试数据的命令行快速下载和解压。首先下载官方数据,查看其数据集构建目录结构和内容编写规范。

## 下载解压数据集
wget https://paddle-model-ecology.bj.bcebos.com/paddlex/data/ocr_det_dataset_examples.tar -P ./dataset
tar -xf ./dataset/ocr_det_dataset_examples.tar -C ./dataset/

        目录结构上,在数据集目录下放 images子目录存放所有的图片,框和文字信息分为训练 train和 val 验证,分别存放在 txt 文件中。

         txt 文件编写上,文本文件每一行代表一张图片的所有信息——图片名和地址、所有框和文字信息的列表。具体来说,框信息和文字信息一起存放在字典结构中,一张图存在多个框和字组合,一起存放在一个列表中。

        图片地址和框信息中间用 ‘\t’ 分隔,中间不能出现空格。图片地址信息始终是字符串格式,需特别注意的是框和文字信息的编写,存在从字典列表结构写入txt 文本结构的转化细节,这在后续实操中会解释。

         综上,自建数据集实际需要操作的部分是构建这个两个关键 txt 文件,而编写文本文件中的关键就是后面框和文本信息的编写。下面我自己编写了一个简单的功能性脚本,可以完成对txt文件的编写。

        2.自建数据集txt文件编写代码

         代码总览:

                直接运行下述代码会在项目目录下新建示例文件 test.txt

import json
import os

class Paddle_Dataset_Creator:
    def __init__(self, savefile, input_dict, input_num=4, mode='only_det'):
        self.savefile = savefile    # paddle训练数据保存地址
        self.input_num = input_num   # 输入训练框数量信息:==4——左上、右下两点四坐标 or ==8——四顶点八坐标
        self.input_dict = input_dict   # 输入字典:键——文件名;值:框坐标列表

        self.mode = mode   # 选择模式:only_det——只进行检测框的训练集构建; ocr——检测框和区域识别字训练集
        self.pp_boxdict = None  # 存放格式正确的框字典

    # 四坐标转为八坐标,并保存paddle支持的框列表结构
    def check_box(self):
        # 标准格式——以一个框为例:[【xx,xx】,【xx,xx】,【xx,xx】,【xx,xx】],从左上顶点出发,顺时针一圈
        if self.input_num == 4:
            self.pp_boxdict = dict()
            for key in self.input_dict.keys():
                two_boxes_list = self.input_dict[key]  # 四坐标列表信息
                pp_box_list = []
                for two_box in two_boxes_list:
                    pp_dict = dict()
                    if self.mode == 'only_det':
                        l,t,r,b = two_box  # 左x_min,上y_min, 右x_max, 下y_max
                        pp_dict["transcription"] = "ZW"
                    elif self.mode == 'ocr':
                        l,t,r,b,text = two_box
                        pp_dict["transcription"] = str(text)
                    else:
                        raise ValueError('miss key value self.mode')
                    pp_box = [[min(l,r), min(t,b)], [max(l,r), min(t,b)], [max(l,r), max(t,b)], [min(l,r), max(t,b)]]
                    pp_dict["points"] = pp_box
                    pp_box_list.append(pp_dict)
                self.pp_boxdict[key] = pp_box_list
        elif self.input_num == 8:
            self.pp_boxdict = dict()
            for key in self.input_dict.keys():
                four_boxes_list = self.input_dict[key]  # 四坐标列表信息
                pp_box_list = []
                for four_box in four_boxes_list:
                    pp_dict = dict()
                    if self.mode == 'only_det':
                        pp_dict["transcription"] = "ZW"
                        pp_dict["points"] = four_box
                    elif self.mode == 'ocr':
                        pp_dict["transcription"] = str(four_box[4])
                        pp_dict["points"] = four_box[:4]
                    else:
                        raise ValueError('miss key value self.mode')
                    pp_box_list.append(pp_dict)
                self.pp_boxdict[key] = pp_box_list
        else:
            raise ValueError('cant process box_num, box_num must in [4,8]')

    # 创建paddle数据txt文件
    def create_pptext(self, mode='train'):
        savedir = os.path.join(self.savefile, mode+'.txt')  # 保存结果地址
        if self.pp_boxdict != None:
            for name in self.pp_boxdict.keys():
                filename = 'images/'+name     # 写入txt图片地址信息
                box_list = self.pp_boxdict[name] # 写入txt框和字信息
                json_box_list = json.dumps(box_list, ensure_ascii=False, separators=(',',':'))
                line_text = filename + '\t' + str(json_box_list)   # 写入一行完整信息,中间使用'\t'隔开
                with open(savedir, 'a') as f:
                    f.writelines(str(line_text))
                with open(savedir, 'a') as f:
                    f.write('\n')

if __name__=='__main__':
    example1 = {'abc.jpg':[[1,2,3,4],[5,6,7,8]], 'def.jpg':[[10,20,30,40],[50,60,70,80]]}
    example2 = {'abc.jpg':[[1,2,3,4,'a'],[5,6,7,8,'b']], 'def.jpg':[[10,20,30,40,'c'],[50,60,70,80,'d']]}
    paddle_creator = Paddle_Dataset_Creator(savefile='./', input_dict=example1, input_num=4, mode='only_det')
    paddle_creator.check_box()
    paddle_creator.create_pptext(mode='test')
    print(paddle_creator.pp_boxdict)

使用方法:

        必要条件:1.框信息是顶点的绝对值坐标信息构建的(如果是归一化坐标——YOLO,或者是左上点坐标信息加框的长宽,需要先做好数据转化)

                          2.输入数据格式必须是字典,且键为对应图片文件名,值为该图所有框和字信息

                          3.如果存在文字信息,请放在对应框列表的最后;如果没有文字信息,则默认为只训练框的检测模型。

        具体使用:

        具体使用的时候,只需将此类嵌入文件处理的过程中,使用相应的功能函数即可。

        ①初始化 self.__init__:

        初始化类有四个需要输入的参数——savefile:你需要txt文件保存的地址;input_dict:框和文字信息字典(按必要条件要求组织);input_num:框信息坐标数量,默认是4,代表左上右下两点四坐标;mode:是做仅框的检测模型训练 'only_det' ,还是带上识别模型的文本训练'ocr'

        ②框信息规范 self.check_box:

        直接训练该函数会将输入的框信息标准化为paddle需要的格式

        ③写入txt文件 self.create_pptext(mode):

        传入mode参数代表当前编写的是训练集train的txt还验证val的txt文件。

        3.数据集检验

        PaddleX 中自备了检验数据集是否构建成功的命令行指令。

        使用时只需将 xxx.yaml 修改为自建模型配置文件地址,将dataset_dir=xxx 改为自建数据集地址即可。

# 效验官方数据集
python main.py -c paddlex/configs/text_detection/PP-OCRv4_mobile_det.yaml -o Global.mode=check_dataset -o Global.dataset_dir=./dataset/ocr_det_dataset_examples

# 效验自建数据集
python main.py -c xxx.yaml -o Global.mode=check_dataset -o Global.dataset_dir=xxx

        运行出现以下绿字则说明数据集构建成功。

 

二、模型训练

        这里模型训练仅以文本检测 ——text_detection 模型为例,实际上大部分其他模型的训练方式逻辑和本例类似。两步走:一、找到对应模型配置文件;二、命令行指令运行训练。

        1.模型配置yaml文件

        首先需要找到需要的模型配置 yaml 文件,在之前git克隆下载好的 PaddleX 源码/paddlex/configs目录下就可以查询到 paddle 所有模型的配置文件。

        打开本例需要的PP-OCRv4_server_det.yaml文件,按下图修改一些参数即可。(实际上,不一定非要修改,在命令行指令运行中也可以传入修改对应参数,如在命令代码后加上-o Global.dataset_dir=./dataset/ocr_det_dataset_examples,也可以修改数据集目录)

        2.命令行指令训练

        命令行训练需要配置几个所必需的参数,下面逐一记录。

        一个官方例子如下图。

基础命令参数配置:

        ① -c model.yaml:这是模型配置yaml文件地址

        ②-o Global.mode=train:当前模式选择,可以是训练train模式也可以是验证或者预测模式

        ③-o Global.dataset_dir=./:之前构建的训练数据集目录地址

        ④-o Global.device=gpu:0,1:指定训练设备,例中表示使用双卡训练

        ⑤-o Global.output=./:结果保存地址

训练参数配置:

        ⑥-o Train.epochs_iters=100:训练轮次

        ⑦-o Train.num_classes=1:数据集中的类别数,对于仅框检测来说,类别为1

        ⑧-o Train.batch_size=16:训练批量大小

        ⑨-o Train.pretrain_weight_path:预训练模型参数地址,第一次训练会下载在本地c盘

        参数配置上的详细内容参考官方文档地址:https://gitee.com/paddlepaddle/PaddleX/blob/release/3.0-beta1/docs/module_usage/instructions/config_parameters_common.md

        训练中会打印过程结果。       

        经过训练结果保存目录会得到以下结果。

        可以在best_model下找到训练最优模型参数,即可将微调模型参数用于部署任务。

        由于服务器CUDA版本环境存在一定冲突,虽然之前在yolo训练时没有报过错,但paddle会有警告。不过似乎并特别影响正常训练,训练还是能跑起来的。下一章将集中精力解决这些环境报错,并深入研究其训练输出目录下结果的使用,还有模型的测试评估和最终的部署。