• 关于近期轻量化部署任务的一个小结


    • 我只在windows平台做了尝试,因为较少涉及量化,所以精度几乎做到零损失
    • 涉及到 windows C++ 开发环境搭建,比如VS2019/cmake/ninja的windows配置,本文并未详细说明

    在这里插入图片描述

    故事要从PaddleSeg开始说起

    1. PaddleSeg

    PaddleSeg的链接:https://github.com/PaddlePaddle/PaddleSeg

    PaddleSeg 通过指定 config 文件来训练,这是 train.py 的部分其他参数含义:

    参数名用途是否必选项默认值
    iters训练迭代次数配置文件中指定值
    batch_size单卡batch size配置文件中指定值
    learning_rate初始学习率配置文件中指定值
    config配置文件-
    save_dir模型和visualdl日志文件的保存根路径output
    num_workers用于异步读取数据的进程数量, 大于等于1时开启子进程读取数据0
    use_vdl是否开启visualdl记录训练数据
    save_interval模型保存的间隔步数1000
    do_eval是否在保存模型时启动评估, 启动时将会根据mIoU保存最佳模型至best_model
    log_iters打印日志的间隔步数10
    resume_model恢复训练模型路径,如:output/iter_1000None
    keep_checkpoint_max最新模型保存个数5

    训练时,临时修改config文件,可以通过-o参数来指定 (文档找不到了,我记得之前有这个来着)

    在训练之后,得到模型参数文件 model.pdparams,可以用export.py 来将动态图模型导出为静态图模型

    2. 导出静态图

    关于PaddleSeg导出脚本 export.py 的使用分析:
    https://blog.csdn.net/HaoZiHuang/article/details/125761854

    这里插一句:

    什么是动态图静态图
    在深度学习模型构建上,飞桨框架支持动态图编程和静态图编程两种方式,其代码编写和执行方式均存在差异。

    动态图编程: 采用 Python 的编程风格,解析式地执行每一行网络代码,并同时返回计算结果。在 模型开发 章节中,介绍的都是动态图编程方式。

    静态图编程: 采用先编译后执行的方式。需先在代码中预定义完整的神经网络结构,飞桨框架会将神经网络描述为 Program 的数据结构,并对 Program 进行编译优化,再调用执行器获得计算结果。

    动态图编程体验更佳、更易调试,但是因为采用 Python 实时执行的方式,开销较大,在性能方面与 C++ 有一定差距;静态图调试难度大,但是将前端 Python 编写的神经网络预定义为 Program描述,转到 C++ 端重新解析执行,脱离了 Python 依赖,往往执行性能更佳,并且预先拥有完整网络结构也更利于全局优化。

    以上摘自Paddle官方文档:
    https://www.paddlepaddle.org.cn/documentation/docs/zh/guides/jit/index_cn.html

    PaddleSeg export.py 使用文档
    https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.6/docs/model_export_cn.md
    直接这样导出文档,没啥大问题:

    # 设置1张可用的卡
    export CUDA_VISIBLE_DEVICES=0
    # windows下请执行以下命令
    # set CUDA_VISIBLE_DEVICES=0
    
    python export.py \
           --config configs/bisenet/bisenet_cityscapes_1024x1024_160k.yml \
           --model_path bisenet/model.pdparams \
           --save_dir output
           --input_shape 1 3 512 1024
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    参数名用途是否必选项默认值
    config配置文件-
    model_path预训练权重的路径配置文件中指定的预训练权重路径
    save_dir保存预测模型的路径output
    input_shape设置导出模型的输入shape,比如传入--input_shape 1 3 1024 1024。如果不设置input_shape,默认导出模型的输入shape是[-1, 3, -1, -1]否( 如果预测shape固定,建议指定input_shape参数 )None
    with_softmax在网络末端添加softmax算子。由于PaddleSeg组网默认返回logits,如果想要部署模型获取概率值,可以置为TrueFalse
    without_argmax是否不在网络末端添加argmax算子。由于PaddleSeg组网默认返回logits,为部署模型可以直接获取预测结果,我们默认在网络末端添加argmax算子False

    这里需要主要的时候,without_argmax 是否需要添加,以及 with_softmax
    还有input_shape是否需要指定,这里留下一个伏笔,指定一个固定的输入有时很有必要

    导出之后的文件树:

    ./output
      ├── deploy.yaml            # 部署相关的配置文件,主要说明数据预处理的方式
      ├── model.pdmodel          # 预测模型的拓扑结构文件
      ├── model.pdiparams        # 预测模型的权重文件
      └── model.pdiparams.info   # 参数额外信息,一般无需关注
    
    • 1
    • 2
    • 3
    • 4
    • 5

    需要说明的是,在Paddle的其他套件或者版本

    • model.pdmodel 可能会被命名为 __model__
    • model.pdiparams 可能会被命名为 __param__

    3. paddle_infer

    导出静态图模型,就可以直接用 paddle inference 推理了
    在这里插入图片描述

    上图是我总结的Paddle Inference 中的关键API, 其实接下来可以看到,几乎所有轻量化框架都遵循这里流程:
    在这里插入图片描述
    具体的实验demo, 可以参考PaddleInference的官方demo
    https://paddle-inference.readthedocs.io/en/master/guides/quick_start/python_demo.html

    3. Paddle Lite

    我之前写过:
    win10 PaddleLite 编译以及代码跑通复盘:https://blog.csdn.net/HaoZiHuang/article/details/126040571

    跑 PaddleLite 一是环境怎么搭建,二是模型怎么转换,三是代码跑通,可直接看上文

    • 环境搭建部分,可以直接pip,上文也有写,前半部分也有编译部分,但是遇到了这个问题,github issue目前也木有回复:https://github.com/PaddlePaddle/Paddle-Lite/issues/9298,所以推荐用pip安装
    • 模型怎么转换? 在linux环境,可以直接用过其opt工具,windows环境需要用转换脚本,上文也有写,在其第五部分
    • 代码跑通,与之前的 Paddle Inference 类似,关键API设计类似:
      在这里插入图片描述

    到此,我在Paddle 部分的尝试就叙述完毕

    3. 将paddle模型转化为onnx

    使用工具paddle2onnx, 可以将静态图转化为onnx模型:
    https://github.com/PaddlePaddle/Paddle2ONNX

    paddle2onnx --model_dir saved_inference_model \
                --model_filename model.pdmodel \
                --params_filename model.pdiparams \
                --save_file model.onnx \
                --enable_dev_version True
    
    • 1
    • 2
    • 3
    • 4
    • 5
    参数参数说明
    –model_dir配置包含 Paddle 模型的目录路径
    –model_filename[可选] 配置位于 --model_dir 下存储网络结构的文件名
    –params_filename[可选] 配置位于 --model_dir 下存储模型参数的文件名称
    –save_file指定转换后的模型保存目录路径
    –opset_version[可选] 配置转换为 ONNX 的 OpSet 版本,目前支持 7~16 等多个版本,默认为 9
    –enable_dev_version[可选] 是否使用新版本 Paddle2ONNX(推荐使用),默认为 True
    –enable_onnx_checker[可选] 配置是否检查导出为 ONNX 模型的正确性, 建议打开此开关, 默认为 False
    –enable_auto_update_opset[可选] 是否开启 opset version 自动升级功能,当低版本 opset 无法转换时,自动选择更高版本的 opset进行转换, 默认为 True
    –input_shape_dict[可选] 配置输入的 shape, 默认为空; 此参数即将移除,如需要固定Paddle模型输入Shape,请使用此工具处理
    –deploy_backend[可选] 量化模型部署的推理引擎,支持 onnxruntime、tensorrt 或 others,当选择 others 时,所有的量化信息存储于 max_range.txt 文件中,默认为 onnxruntime
    –version[可选] 查看 paddle2onnx 版本

    注意 –input_shape_dict 参数即将移除

    这个转化工具需要注意的不多,可能是某些算子不支持,比如 adaptivepooling 不支持:

    解决方式,可以自定义一个来替换:

    AdaptiveAvgPool2D 不支持 onnx 导出,自定义一个类代替 AdaptiveAvgPool2D:https://blog.csdn.net/HaoZiHuang/article/details/125390041

    4. onnx 模型

    对于接触部署不多的同学,我觉得这张图这段话很有用,摘自mmdeploy官方文档:

    深度学习模型的结构通常比较庞大,需要大量的算力才能满足实时运行的需求。模型的运行效率需要优化。 因为这些难题的存在,模型部署不能靠简单的环境配置与安装完成。经过工业界和学术界数年的探索,模型部署有了一条流行的流水线:
    在这里插入图片描述
    为了让模型最终能够部署到某一环境上,开发者们可以使用任意一种深度学习框架来定义网络结构,并通过训练确定网络中的参数。之后,模型的结构和参数会被转换成一种只描述网络结构的中间表示,一些针对网络结构的优化会在中间表示上进行。最后,用面向硬件的高性能编程框架(如 CUDA,OpenCL)编写,能高效执行深度学习网络中算子的推理引擎会把中间表示转换成特定的文件格式,并在对应硬件平台上高效运行模型。

    这一条流水线解决了模型部署中的两大问题:使用对接深度学习框架和推理引擎的中间表示,开发者不必担心如何在新环境中运行各个复杂的框架;通过中间表示的网络结构优化和推理引擎对运算的底层优化,模型的运算效率大幅提升。

    onnx模型只是一个通用的中间表示

    ONNX (Open Neural Network Exchange)是 Facebook 和微软在2017年共同发布的,用于标准描述计算图的一种格式。目前,在数家机构的共同维护下,ONNX 已经对接了多种深度学习框架和多种推理引擎。因此,ONNX 被当成了深度学习框架到推理引擎的桥梁,就像编译器的中间语言一样。由于各框架兼容性不一,我们通常只用 ONNX 表示更容易部署的静态图。

    我们可以将 tf/torch/paddle的模型导出为onnx,使用Netron (开源的模型可视化工具) 来查看模型网络结构:
    https://netron.app/

    因为我们很少直接用 onnx api 直接创建模型,而是通过AI框架(Paddle/Torch/TF等)直接导出 onnx 模型,所以关于 onnx API 无需叙述,可参阅 onnx github:
    https://github.com/onnx/onnx

    可以用这二者对onnx模型做简化:
    https://github.com/daquexian/onnx-simplifier
    https://github.com/onnx/optimizer
    但是实验表明,速度反而变慢了…

    我们只需要关心 onnx模型怎么在跑起来,也就是 onnxruntime 的环境配置

    若要安装适用于 Python 的 onnxruntime ,请使用以下命令之一:

    pip install onnxruntime	      # CPU build
    pip install onnxruntime-gpu   # GPU build
    
    • 1
    • 2

    若要在 Python 脚本中调用 onnxruntime,请使用:

    import onnxruntime
    session = onnxruntime.InferenceSession("path to model")
    
    • 1
    • 2

    模型附带的文档通常会指示有关使用模型的输入和输出。 还可使用可视化工具查看模型。 onnxruntime 还可以查询模型元数据、输入和输出:

    session.get_modelmeta()
    first_input_name = session.get_inputs()[0].name
    first_output_name = session.get_outputs()[0].name
    
    • 1
    • 2
    • 3

    若要推理模型,请使用 run,并传入要返回的输出列表(如果需要所有输出,则保留为空)和输入值的映射。 结果是输出列表。

    results = session.run(["output1", "output2"], {
                          "input1": indata1, "input2": indata2})
    results = session.run([], {"input1": indata1, "input2": indata2})
    
    • 1
    • 2
    • 3

    有关完整的 Python API 参考,请参阅 onnxruntime 参考文档。

    https://onnxruntime.ai/docs/api/python/api_summary.html

    可以使用

    >>> import onnxruntime
    >>> onnxruntime.get_available_providers()
    ["CPUExecutionProvider", "CUDAExecutionProvider", "TensorrtExecutionProvider"]
    
    • 1
    • 2
    • 3

    来查看本机支持的推理provider

    若本机有CUDA环境,则会有 "CUDAExecutionProvider"
    若本机有TensorRT环境,则会有 "TensorrtExecutionProvider"

    5. mnn 模型

    我之前写了
    windows MNN 的使用流程(Python版): https://blog.csdn.net/HaoZiHuang/article/details/126146550

    mnn 之后的版本会提供 pymnn, 可以用 Python API 调用 opencl, vulkan 后端的编译的mnn.lib

    参考github issue:
    https://github.com/alibaba/MNN/issues/2010

    使用Python版本建议直接pip安装,使用C++版本建议自行根据文档进行编译

    还是那三个问题:

    • 怎么转换模型
    • 怎么部署环境
    • 怎么运行推理

    Python环境直接pip就好,转换模型pip之后会提供命令行工具mnnconvert, 可以用该工具进行转换:

    MNNConvert -f ONNX --modelFile output.onnx --MNNModel output.mnn --bizCode biz
    
    • 1

    其半精度操作也很简单,直接加上 --fp16 即可

    接下来就是怎么推理:
    https://blog.csdn.net/HaoZiHuang/article/details/126146550
    文中提供了验证demo,可以直接down下来测试

    然后就是原文也提供了convert 和 推理引擎的编译过程,供参考

    6. TensorRT 模型

    TensorRT是重头戏,我花费了最长的时间,首先是环境的部署:
    windows 版本:
    https://blog.csdn.net/HaoZiHuang/article/details/125795230

    linux 版本:
    https://blog.csdn.net/HaoZiHuang/article/details/126046171

    关于模型的转换,我尝试了 onnx 模型转换为 trt/engine 模型

    .trt / .engine 文件之后后缀不同,内容完全一样

    按照官方文档的Python API 通过 onnx parser 来转换,我尝试了,不太行
    我是用 trtexec 来进行转换的

    同时有些需要注意的地方:

    • 比如本机是否支持 int8 / float16 等之类的东西
    • 本机的 计算能力值

    等等,都可参考下文:
    https://blog.csdn.net/HaoZiHuang/article/details/125859167
    建议直接查看方法二

    此文引出了各种东西,同时 TensorRT 也有官方中文版,不过我看了一下,就是直接扔到谷歌翻译里的感觉,我甚至扔到谷歌翻译试了一下,确实是谷歌翻译翻译的hhh

    中文版:
    https://developer.nvidia.com/zh-cn/blog/author/ken-he/

    在这里插入图片描述
    英文版:
    https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#overview

  • 相关阅读:
    unity 接收和发送Udp消息
    8-5交换排序-快速排序
    计算机毕业设计ssm+vue基本微信小程序的健康管理系统
    es6模块+异步promise+async/await
    金融口译,口译中金融高频词有哪些
    时间轴_显示器
    【深度学习】详解 BEiT
    数值分析学习笔记——误差【华科B站教程版本】
    题目 1055: 二级C语言-进制转换
    三七互娱,oppo,快手25届暑期实习内推
  • 原文地址:https://blog.csdn.net/HaoZiHuang/article/details/126083086