模型可视化方法

    1. Paddle推理模型可视化
    2. Lite子图方式下模型可视化

    Paddle用于推理的模型是通过这个API保存下来的,存储格式有两种,由save_inference_model接口中的 和 params_filename 变量控制:

    • non-combined形式:参数保存到独立的文件,如设置 model_filenameNone , params_filenameNone

    • combined形式:参数保存到同一个文件,如设置 model_filenamemodel , params_filenameparams

      1. $ ls -l recognize_digits_model_combined/
      2. total 160K
      3. -rw-r--r-- 1 root root 28K Sep 24 09:42 model # 模型文件
      4. -rw-r--r-- 1 root root 132K Sep 24 09:42 params # 权重文件

    通过以上方式保存下来的模型文件都可以通过Netron工具来打开查看模型的网络结构。

    注意:当前要求PaddlePaddle的保存模型文件名必须为__model__,否则无法识别。如果是通过第二种方式保存下来的combined形式的模型文件,需要将文件重命名为__model__

    Paddle Lite在执行模型推理之前需要使用模型优化工具opt来对模型进行优化,优化后的模型结构同样可以使用工具进行查看,但是必须保存为protobuf格式,而不是naive_buffer格式。

    注意: 为了减少第三方库的依赖、提高Lite预测框架的通用性,在移动端使用Lite API您需要准备Naive Buffer存储格式的模型(该模型格式是以.nb为后缀的单个文件)。但是Naive Buffer格式的模型为序列化模型,不支持可视化。

    这里以paddle_lite_opt工具为例:

    • 当模型输入为non-combined格式的Paddle模型时,需要通过--model_dir来指定模型文件夹

      1. $ paddle_lite_opt \
      2. --model_dir=./recognize_digits_model_non-combined/ \
      3. --valid_targets=arm \
      4. --optimize_out_type=protobuf \ # 注意:这里必须输出为protobuf格式
      5. --optimize_out=model_opt_dir_non-combined
    • 当模式输入为combined格式的Paddle模型时,需要同时输入--model_file--param_file来分别指定Paddle模型的模型文件和权重文件

      1. $ paddle_lite_opt \
      2. --model_file=./recognize_digits_model_combined/model \
      3. --param_file=./recognize_digits_model_combined/params \
      4. --valid_targets=arm \
      5. --optimize_out_type=protobuf \ # 注意:这里必须输出为protobuf格式
      6. --optimize_out=model_opt_dir_combined

      优化后的模型文件同样存储在由--optimize_out指定的输出文件夹下,格式相同

      1. ls -l model_opt_dir_combined/
      2. total 152K

    将通过以上步骤输出的优化后的模型文件model重命名为__model__,然后用工具打开即可查看优化后的模型结构。将优化前后的模型进行对比,即可发现优化后的模型比优化前的模型更轻量级,在推理任务中耗费资源更少且执行速度也更快。

    当模型优化的目标硬件平台为 华为NPU, , 瑞芯微NPU, 等通过子图方式接入的硬件平台时,得到的优化后的protobuf格式模型中运行在这些硬件平台上的算子都由subgraph算子包含,无法查看具体的网络结构。

    华为NPU为例,运行以下命令进行模型优化,得到输出文件夹下的model, params两个文件。

    将优化后的模型文件model重命名为__model__,然后用工具打开,只看到单个的subgraph算子,如下图所示:

    模型可视化方法 - 图2

    如果想要查看subgraph中的具体模型结构和算子信息需要打开Lite Debug Log,Lite在优化过程中会以.dot文本形式输出模型的拓扑结构,将.dot的文本内容复制到webgraphviz即可查看模型结构。

    1. $ export GLOG_v=5 # 注意:这里打开Lite中Level为5及以下的的Debug Log信息
    2. $ paddle_lite_opt \
    3. --model_dir=./recognize_digits_model_non-combined/ \
    4. --valid_targets=npu,arm \
    5. --optimize_out_type=protobuf \
    6. --optimize_out=model_opt_dir_npu > debug_log.txt 2>&1
    7. # 以上命令会将所有的debug log存储在debug_log.txt文件中
    1. I0924 10:50:12.715279 122828 optimizer.h:202] == Running pass: npu_subgraph_pass
    2. I0924 10:50:12.715335 122828 ssa_graph.cc:27] node count 33
    3. I0924 10:50:12.715412 122828 ssa_graph.cc:27] node count 33
    4. I0924 10:50:12.715438 122828 ssa_graph.cc:27] node count 33
    5. subgraphs: 1 # 注意:搜索subgraphs:这个关键词,
    6. digraph G {
    7. node_30[label="fetch"]
    8. node_29[label="fetch0" shape="box" style="filled" color="black" fillcolor="white"]
    9. node_28[label="save_infer_model/scale_0.tmp_0"]
    10. node_26[label="fc_0.tmp_1"]
    11. node_24[label="fc_0.w_0"]
    12. node_23[label="fc0_subgraph_0" shape="box" style="filled" color="black" fillcolor="red"]
    13. ...
    14. node_15[label="batch_norm_0.tmp_1"]
    15. node_17[label="conv2d1_subgraph_0" shape="box" style="filled" color="black" fillcolor="red"]
    16. node_19[label="conv2d_1.b_0"]
    17. node_1->node_0
    18. node_0->node_2
    19. node_2->node_3
    20. node_28->node_29
    21. node_29->node_30
    22. } // end G
    23. I0924 10:50:12.715745 122828 op_lite.h:62] valid places 0
    24. I0924 10:50:12.715770 122828 op_lite.cc:89] pick kernel for subgraph host/float/NCHW get 0 kernels

    将以上文本中以digraph G {开头和以} // end G结尾的这段文本复制粘贴到,即可看到子图中的具体模型结构,如下图。其中高亮的方形节点为算子,椭圆形节点为变量或张量。

    若模型中存在多个子图,以上方法同样可以得到所有子图的具体模型结构。

    同样以华为NPU和ARM平台混合调度为例,子图的产生往往是由于模型中存在部分算子无法运行在NPU平台上(比如NPU不支持的算子),这会导致整个模型被切分为多个子图,子图中包含的算子会运行在NPU平台上,而子图与子图之间的一个或多个算子则只能运行在ARM平台上。这里可以通过的自定义子图分割功能,将recognize_digits模型中的batch_norm设置为禁用NPU的算子,从而将模型分割为具有两个子图的模型:

    将执行以上命令之后,得到的优化后模型文件model重命名为__model__,然后用工具打开,就可以看到优化后的模型中存在2个subgraph算子,如左图所示,两个子图中间即为通过环境变量和配置文件指定的禁用NPU的batch_norm算子。

    打开新保存的debug_log.txt文件,搜索final program关键字,拷贝在这之后的以digraph G {开头和以} // end G结尾的文本用webgraphviz查看,也是同样的模型拓扑结构,存在subgraph1subgraph3两个子图,两个子图中间同样是被禁用NPU的batch_norm算子,如右图所示。

    模型可视化方法 - 图4

    之后继续在debug_log.txt文件中,搜索subgraphs关键字,可以得到所有子图的.dot格式内容如下:

    1. digraph G {
    2. node_30[label="fetch"]
    3. node_29[label="fetch0" shape="box" style="filled" color="black" fillcolor="white"]
    4. node_28[label="save_infer_model/scale_0.tmp_0"]
    5. node_26[label="fc_0.tmp_1"]
    6. node_24[label="fc_0.w_0"]
    7. ...
    8. node_17[label="conv2d1_subgraph_0" shape="box" style="filled" color="black" fillcolor="red"]
    9. node_19[label="conv2d_1.b_0"]
    10. node_0[label="feed0" shape="box" style="filled" color="black" fillcolor="white"]
    11. node_5[label="conv2d_0.b_0"]
    12. node_1[label="feed"]
    13. node_23[label="fc0_subgraph_0" shape="box" style="filled" color="black" fillcolor="red"]
    14. node_7[label="pool2d0_subgraph_1" shape="box" style="filled" color="black" fillcolor="green"]
    15. node_21[label="pool2d1_subgraph_0" shape="box" style="filled" color="black" fillcolor="red"]
    16. ...
    17. node_18[label="conv2d_1.w_0"]
    18. node_1->node_0
    19. node_0->node_2
    20. ...
    21. node_28->node_29
    22. } // end G

    将以上文本复制到查看,即可显示两个子图分别在整个模型中的结构,如下图所示。可以看到图中绿色高亮的方形节点的为subgraph1中的算子,红色高亮的方形节点为subgraph2中的算子,两个子图中间白色不高亮的方形节点即为被禁用NPU的算子。

    注意: 本章节用到的recognize_digits模型代码位于PaddlePaddle/book