【飞浆AI实战】桃子分类系统部署:手把手带你入门PaddleClas全家桶

慈云数据 2024-03-12 技术支持 117 0

前言

本次分享将带领大家从0到1完成一个图像分类任务的模型训练评估和推理部署全流程,项目将采用以PaddleClas为核心的飞浆深度学习套装进行开发,并总结开发过程中踩过的一些坑,希望对有类似项目需求的同学提供一点帮助。

项目背景和目标

背景:

  • 图像分类是计算机视觉的基础,也是其他计算机复杂任务的基础。本次选用的案例来自智慧农业,实现对不同质量桃子的自动分类,从而减轻人工挑拣的负担。

    目标:

    • 掌握如何基于paddlepaddle深度学习框架完成一个图像分类任务;
    • 掌握如何完成模型的训练、评估、预测和部署等深度学习工作过程;

      百度AI Studio平台

      本次实验将采用AI Studio实训平台中的免费GPU资源,在平台注册账号后,点击创建项目-选择NoteBook任务,然后添加数据集,如下图所示,完成项目创建。启动环境可以自行选择CPU资源 or GPU资源,创建任务每天有8点免费算力,推荐大家使用GPU资源进行模型训练,这样会大幅减少模型训练时长。

      数据集介绍

      本次实验使用的数据集是四个种类桃子,这些桃子被分在四个文件夹中,每一个文件夹的名字就对应着一类桃子。

      观察发现,这些桃子基本是按照大小、颜色来划分的,本次实验任务就是通过飞浆提供的套件完成一个图像分类任务。

      飞浆深度学习开发框架介绍

      PaddlePaddle百度提供的开源深度学习框架,其中文名是“飞桨”,致力于为开发者和企业提供最好的深度学习研发体验,国产框架中绝对的榜一大哥!其核心优势是生态完善,目前集成了各种开发套件,覆盖了数据处理、模型训练、模型验证、模型部署等各个阶段的工具。下面简要介绍一下本项目用到的几个核心组件:

      • PaddleClas:一个图像识别和图像分类任务的工具集,集成了主流和百度自研的视觉模型和模型训练测试API。
      • PaddleServing:将模型部署成一个在线预测服务的库,支持服务端和客户端之间的高并发和高效通信。
      • PaddleLite:将模型转换成可以端侧推理的库,比如将模型部署到手机端进行推理。

        从零开始开发

        PaddleClas完成模型训练

        安装PaddleClas

        在项目中打开终端,然后运行如下命令:

        # (可选)最好在虚拟环境中安装,环境会持久保存在项目中
        pip install virtualenv # 安装虚拟环境管理包
        virtualenv --python=python3.7 --system venv # 新建虚拟环境
        source venv/bin/activate # 切换到新建的虚拟环境
        # 安装paddlepaddle,根据云端环境选择cpu版本和gpu版本
        pip install paddlepaddle -i https://mirror.baidu.com/pypi/simple 
        pip install paddlepaddle-gpu -i https://mirror.baidu.com/pypi/simple
        # 克隆PaddleClas仓库
        git clone https://gitee.com/paddlepaddle/PaddleClas.git
        # 安装其他依赖
        cd PaddleClas/
        pip install --upgrade -r requirements.txt -i https://mirror.baidu.com/pypi/simple
        # 编译安装paddleclas
        python setup.py install
        

        数据准备

        我们需要制作符合PaddleClas框架要求的数据集格式:参考github.com,格式要求如下:

        PaddleClas使用 txt 格式文件指定训练集和测试集,以 ImageNet1k 数据集为例,其中 train_list.txt 和 val_list.txt 的格式形如:
        # 每一行采用"空格"分隔图像路径与标注
        # 下面是 train_list.txt 中的格式样例
        train/n01440764/n01440764_10026.JPEG 0
        ...
        # 下面是 val_list.txt 中的格式样例
        val/ILSVRC2012_val_00000001.JPEG 65
        ...
        

        为此我们需要将桃子分类数据集制作上如上的格式:

        # 首先将data目录下的数据解压到PaddleClas/dataset目录下
        unzip /home/aistudio/data/data103593/data.zip -d /home/aistudio/PaddleClas/dataset/peach_data
        cd dataset/peach_data
        # 编写generate_dataset.py
        python generate_dataset.py # 生成指定格式的train_list.txt val_list.txt test_list.txt
        cd ../.. # 返回PaddleClas主目录
        

        其中generate_dataset.py中的代码如下:

        import os
        train_dir = 'train'
        test_dir = 'test'
        class_names = os.listdir(train_dir)
        id2names = {}
        for i, cls in enumerate(class_names):
            id2names[cls] =  i
        # 制作标签对应关系
        with open('label_list.txt', 'w') as f:
            f.writelines(f'{id} {name}\n' for name, id in id2names.items())
        # 制作训练集和验证集
        traindata_list = []
        valdata_list = []
        for name in class_names:
            class_images = os.listdir(os.path.join(train_dir, name))
            for i, img in enumerate(class_images):
                label = id2names[name]
                if i%8 == 0:
                    valdata_list.append(f'{train_dir}/{name}/{img} {label}')
                else:
                    traindata_list.append(f'{train_dir}/{name}/{img} {label}')
        with open('train_list.txt', 'w') as f:
            f.writelines(f'{data}\n' for data in traindata_list)
        with open('val_list.txt', 'w') as f:
            f.writelines(f'{data}\n' for data in valdata_list)
        # 制作测试集
        testdata_list = []
        for name in class_names:
            class_images = os.listdir(os.path.join(train_dir, name))
            for i, img in enumerate(class_images):
                label = id2names[name]
                testdata_list.append(f'{test_dir}/{name}/{img} {label}')
        with open('test_list.txt', 'w') as f:
            f.writelines(f'{data}\n' for data in testdata_list)
        

        如上,我们便完成了数据的准备工作,接下来将选用一个model完成模型在该数据集上的训练。

        模型训练和评估:

        这里我们以选用百度自研的PP-LCNetV2为例,有关该模型的介绍可参考官方文档。

        • 下载预训练权重:让模型参数有个好的初始化
          wget https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNetV2_base_ssld_pretrained.pdparams
          mv PPLCNetV2_base_ssld_pretrained.pdparams output/ # 移到output文件夹
          
          • 修改配置文件:参考ppcls/configs/ImageNet/PPLCNetV2/PPLCNetV2_base.yaml
            mkdir ppcls/configs/peach
            # 复制一份配置文件并根据数据集对应修改
            cp ppcls/configs/ImageNet/PPLCNetV2/PPLCNetV2_base.yaml ppcls/configs/peach/ 
            # 需要修改的地方如下
            pretrained_model: ./output/PPLCNetV2_base_ssld_pretrained
            class_num: 4
            Train:
                dataset:
                  name: MultiScaleDataset
                  image_root: ./dataset/peach_data/
                  cls_label_path: ./dataset/peach_data/train_list.txt
            Eval:
                dataset: 
                  name: ImageNetDataset
                  image_root: ./dataset/peach_data/
                  cls_label_path: ./dataset/peach_data/val_list.txt
            PostProcess:
                name: Topk
                topk: 1
                class_id_map_file: ./dataset/peach_data/label_list.txt
            
            • 开启训练
              # 这里需要使用云端GPU环境,cpu环境因为只有2core,训练跑不起来
              python tools/train.py -c ppcls/configs/peach/PPLCNetV2_base.yaml 
              

              一般2-3个epoch就会有不错的效果。

              • 模型评估

                -o Global.pretrained_model 指定推理的权重位置

                python tools/eval.py -c ppcls/configs/peach/PPLCNetV2_base.yaml -o Global.pretrained_model=output/PPLCNetV2_base/best_model
                
                • 模型预测

                  -o Infer.infer_imgs指定预测的图片位置

                  python tools/infer.py -c ppcls/configs/peach/PPLCNetV2_base.yaml -o Global.pretrained_model=output/PPLCNetV2_base/best_model -o Infer.infer_imgs=./dataset/peach_data/test/M2/1800.png
                  

                  模型推理部署

                  0 推理模型准备

                  其目的是为了得到用于inference的模型文件,最终存放位置由配置文件的Global.save_inference_dir指定

                  python tools/export_model.py -c ppcls/configs/peach/PPLCNetV2_base.yaml -o Global.pretrained_model=output/PPLCNetV2_base/best_model
                  # 在./deploy/models/PPLCNetV2_base_infer文件下得到三个文件
                  - inference.pdiparams
                  - inference.pdiparams.info
                  - inference.pdmodel
                  

                  1 基于python预测引擎推理

                  测试一下上述得到的用于inference的模型有没有问题

                  cd deploy
                  # 复制一份inference_cls.yaml到inference_cls_peach.yaml并做相应修改,针对我们的任务需要修改的地方如下:
                  infer_imgs: "../dataset/peach_data/test/R0/0.png"
                  inference_model_dir: "./models/PPLCNetV2_base_infer"
                  class_id_map_file: "../dataset/peach_data/label_list.txt"
                  # 单张图像推理,默认使用gpu
                  python python/predict_cls.py -c configs/inference_cls.yaml
                  # 文件夹中所有图像推理,指定文件夹路径
                  python python/predict_cls.py -c configs/inference_cls_peach.yaml -o Global.infer_imgs=../dataset/peach_data/test/R0
                  

                  2 服务化部署

                  这个部分的目的是将我们的模型部署成一个服务,客户端就可以通过http或rpc进行,飞浆已对上述需求所需要的功能实现进行了封装,细节可参考官方文档。具体执行命令如下:

                  # 第一步:安装必要的包
                  pip install paddle-serving-client==0.7.0
                  pip install paddle-serving-app==0.7.0
                  # 若为CPU部署环境:
                  pip install paddle-serving-server==0.7.0
                  # 若为GPU部署环境:
                  pip install paddle-serving-server-gpu==0.7.0.post102 
                  # 第二步:模型转换成server需要的文件
                  python -m paddle_serving_client.convert --dirname ../models/PPLCNetV2_base_infer/ --model_filename inference.pdmodel --params_filename inference.pdiparams
                  ## 运行完会在当前目录下生成两个文件夹,serving_server和serving_client
                  # 第三步:准备配置文件
                  cd paddleserving/
                  ## 修改classification_web_service.py中依赖的配置文件config.yml和imagenet.label
                    ## 其中config.yml中需要指定model_config: serving_server,也即我们在第二步生成的serving_server文件夹
                    ## 其中imagenet.label是ImageNet的标签文件,需要修改成我们数据集的标签,可以copy一份并命名为peach.label,并将4个标签填入
                  ## 修改classification_web_service.py中preprocess函数中{"inputs": input_imgs}=>{"x": input_imgs}
                    ## 注意这里的"x"是因为需要和我们模型输入的名称对应,可查看serving_server/serving_server_conf.prototxt
                  ## 修改serving_client/serving_client_conf.prototxt和serving_server/serving_server_conf.prototxt中fetch_var的alias_name,具体而言:
                  feed_var {
                    name: "x"
                    alias_name: "x"
                    is_lod_tensor: false
                    feed_type: 1
                    shape: 3
                    shape: 224
                    shape: 224
                  }
                  fetch_var {
                    name: "softmax_1.tmp_0"
                    alias_name: "prediction"
                    is_lod_tensor: false
                    fetch_type: 1
                    shape: 4
                  }
                  # 第四步:启动服务
                  # 启动服务并放到后台运行,运行日志保存在 log.txt
                  python classification_web_service.py &>log.txt &
                  # 第五步:客户端调用(发送请求)
                  python pipeline_http_client.py # http请求
                  python pipeline_rpc_client.py # rpc请求
                  

                  3 端侧部署

                  这个部分的目的是将我们的模型部署到移动端(比如手机),这样就不用依赖云端服务器来进行推理了,飞浆已对上述需求所需要的功能实现进行了封装,主要体现在PaddleLite这个组件上,细节可参考官方文档。端侧部署相对稍微复杂一些,主要可以分为以下几个步骤进行:

                  3.1 模型优化

                  考虑到端侧对推理耗时要求比较高,故需要采用paddlelite对inference模型做进一步优化

                  # 安装最新版的paddlelite==2.12, 2.10转换pplcnet时会报错-Check failed: it != attrs().end(): No attributes called fix_seed found for dropout Aborted (core dumped)
                  pip install paddlelite==2.12
                  # 模型转换
                  cd PaddleClas/deploy
                  paddle_lite_opt --model_file=models/PPLCNetV2_base_infer/inference.pdmodel --param_file=models/PPLCNetV2_base_infer/inference.pdiparams --optimize_out=models/pplcnet
                  #上述代码会在models/下生成pplcnet.nb文件
                  

                  成功生成pplcnet.nb

                  3.2 执行编译

                  这一步会得到手机端的可执行文件clas_system,下面以华为Mate30为例,其cpu是armv8架构,如果选用其他手机,需要查看其处理器架构是armv8还是armv7。

                  cd PaddleClas/deploy/lite/
                  # 克隆 Autolog 代码库,以便获取自动化日志
                  git clone https://github.com/LDOUBLEV/AutoLog.git
                  # 如果国内下载不了,加上镜像https://mirror.ghproxy.com/
                  git clone https://mirror.ghproxy.com/https://github.com/LDOUBLEV/AutoLog.git
                  # 下载 linux-x86_64 版本的 Android NDK, 并添加系统环境变量
                  wget https://dl.google.com/android/repository/android-ndk-r17c-linux-x86_64.zip
                  unzip android-ndk-r17c-linux-x86_64.zip
                  export NDK_ROOT=/home/aistudio/android-ndk-r17c # 注意路径
                  # 下载paddlelite的交叉编译库-注意这里的版本2.12要和上面的paddlelite==2.12对应上
                  wget https://github.com/PaddlePaddle/Paddle-Lite/releases/download/v2.12/inference_lite_lib.android.armv8.clang.c++_static.with_extra.with_cv.tar.gz
                  tar -xf inference_lite_lib.android.armv8.clang.c++_static.with_extra.with_cv.tar.gz
                  # 修改makefile,指定LITE_ROOT=./inference_lite_lib.android.armv8.clang.c++_static.with_extra.with_cv
                  # 执行编译命令
                  make
                  ## 编译成功后,会在当前目录生成 clas_system 可执行文件,该文件用于手机端推理
                  
                  3.3 和手机联调

                  将可执行文件、模型文件和测试图片推送到手机上,进行联调。

                  # 第一步:windows安装adb
                  谷歌的安卓平台下载ADB软件包进行安装:链接
                  安装好后adb.exe 一般保存在C:\Users\用户名xx\AppData\Local\Android\Sdk\platform-tools
                  需要将其加到系统环境变量中:
                  -- 以win11为例,系统-系统信息-高级系统设置-环境变量
                  -- 新建AndroidHome=C:\Users\用户名xx\AppData\Local\Android\Sdk
                  -- Path-新建:%AndroidHome%\platform-tools
                  # 第二步:手机连接电脑
                  华为Mate30手机为例:
                  - 开启开发者模式:设置-关于手机-点击“版本号”多次直到提示“您已进入开发者模式”
                  - 打开USB调试:设置-系统与更新-开发人员选项-USB调试-选择USB配置(多媒体传输)
                  # 第三步:文件传输到手机
                  - 将所需文件下载到D:\Downloads\litedemo
                     - 10.png # 测试图像
                     - clas_system # 可执行文件
                     - config_pplcnet.txt # config文件
                     - label_list.txt # 标签映射文件
                     - libpaddle_light_api_shared.so
                     - pplcnet.nb # 模型权重文件
                  - 在D:\Downloads\litedemo下打开终端:
                  adb devices # 显示设备
                  adb shell ls # 查看手机系统目录
                  # 使用adb push命令将文件夹中所有文件传输到手机上:
                  adb shell mkdir -p /data/local/tmp/arm_cpu/
                  adb push clas_system /data/local/tmp/arm_cpu/
                  adb push config_pplcnet.txt /data/local/tmp/arm_cpu/ 
                  adb push label_list.txt /data/local/tmp/arm_cpu/ 
                  adb push libpaddle_light_api_shared.so /data/local/tmp/arm_cpu/ 
                  adb push pplcnet.nb /data/local/tmp/arm_cpu/
                  adb shell chmod +x /data/local/tmp/arm_cpu//clas_system # 需要加可执行权限
                  adb shell 'export LD_LIBRARY_PATH=/data/local/tmp/arm_cpu/; /data/local/tmp/arm_cpu/clas_system /data/local/tmp/arm_cpu/config_pplcnet.txt /data/local/tmp/arm_cpu/10.png'
                  

                  预测成功,看到如下结果:

                  端侧预测结果

                  测试成功后,可将文件夹删除adb shell rm -r /data/local/tmp/arm_cpu/

                  总结

                  本文通过一个计算机视觉领域中最基础的任务之图像分类,带领大家熟悉百度Paddle深度学习框架中的各种组件。案例选自智慧农业场景,有现实场景应用需求,本系列的后续文章将沿袭这一思路,继续分享更多采用Paddle深度学习框架服务更多产业应用的案例。

微信扫一扫加客服

微信扫一扫加客服

点击启动AI问答
Draggable Icon