erfnet

2 downloads 0 likes on ModelScope updated

Model card

ErfNet图像分割模型

2. 方案简介

2.1 算法流程介绍

1. 模型背景

ErfNet(Efficient Residual Factorized Network) 是一款专为高效语义分割任务设计的神经网络结构,由 西班牙巴利亚多利德大学(University of Valladolid) 的研究团队于 2017 年在论文《ErfNet: Efficient Residual Factorized ConvNet for Real-time Semantic Segmentation》中首次提出,并发表在 arXiv 上。该模型主要面向自动驾驶场景中的城市街景语义分割任务,强调在保持较高准确率的同时实现低延迟与低计算成本。

ErfNet 的核心创新点体现在其提出的两项关键设计:

  • 残差结构(Residual Connections):提高深层网络的可训练性,缓解梯度消失;
  • 卷积因式分解(Factorized Convolutions):将常规的 3×3 卷积分解为连续的 1×3 和 3×1 卷积,大幅减少参数与计算量。

此外,ErfNet 引入了高效的 非对称残差模块(Non-bottleneck-1D block),结合上述设计在语义表达能力与推理速度之间取得了良好平衡。

其结构包含:

  • 初始下采样层(Downsampling Blocks):快速压缩空间分辨率;
  • 编码器(Encoder):堆叠多个非对称残差模块提取深层语义信息;
  • 解码器(Decoder):通过反卷积逐步恢复空间分辨率;
  • 跳跃连接(Optional Skip Connections):增强低层细节。

该网络具备以下特点:

  • 所有卷积层均使用残差连接以确保信息流动顺畅;
  • 不采用传统的大型预训练主干网络(如 ResNet),而是从零设计轻量结构,适用于实时推理和嵌入式部署;
  • 不使用显式注意力机制,而是依靠结构本身完成特征融合与压缩。

2. 算法流程图

graph TD
    A[输入图像] --> B[前处理]
    B --> C[ErfNet模块]
    C --> D[后处理]
    

3. 前处理(Host端CPU处理)

前处理流程:

graph LR
    A[输入图像路径] --> B[图像加载]
    B --> C[尺寸变换 + Padding]
    C --> D[张量转换]
    D --> E[DMA准备]

    subgraph 前处理流程
        B -->|cv::IMREAD_COLOR| B1["示例:/data/street.png"]
        C --> C1[Resize长边1024px] --> C2["示例:1024x2048→512x1024"]
        C2 --> C3[rPad填充] --> C4["示例:补高后→1024x1024"]
        D --> D1["数据类型转换 uint8→float32"]
        D1 --> D2["归一化 /255.0"]
        D2 --> D3["通道转换 BGR→RGB"]
        D3 --> D4["添加Batch维度 [1,3,1024,1024]"]
        E --> E1["配置DMA参数"] --> E2["Host→NPU数据传输"]
    end

    style B fill:#F8CECC,stroke:#B85450
    style C fill:#D5E8D4,stroke:#82B366
    style D fill:#DAE8FC,stroke:#6C8EBF
    style E fill:#FFF2CC,stroke:#D6B656

关键操作:

PicPre img(img_path, cv::IMREAD_COLOR); // 图像加载
img.Resize({ netinfo.i_cubic[0].h, netinfo.i_cubic[0].w }, PicPre::LONG_SIDE).rPad(); // 调整尺寸 + Padding填充
Tensor img_tensor = CvMat2Tensor(img.dst_img, network); // OpenCV Mat转Tensor
dmaInit(run_sim, netinfo.ImageMake_on, img_tensor, device); // DMA传输初始化

具体处理流程:

  1. 图像加载:通过 OpenCV 读取原始图像(cv::IMREAD_COLOR 保持 RGB 格式)
    • 示例:输入 img_path 为 "/data/street.png",图像尺寸为 1024×2048(高×宽)
  2. 尺寸变换与补边:
    • Resize:以 长边为基准(此处为宽 = 2048),将图像缩放,使其长边匹配目标尺寸(如输入为 512×1024,则长边为1024)
      • 缩放比例 = 1024 / 2048 = 0.5
      • 缩放后图像尺寸为 512×1024
    • rPad:在 短边方向(此处为高 = 512) 进行对称补零,补成最终输入尺寸(如目标为 512×1024,实际已满足则无需额外 padding)
    • 若目标为正方形输入(如 1024×1024),则:
      • 当前图像已为 512×1024,需要在高方向补齐至1024,高度补 512 像素(上下各补 256)
      • 最终尺寸为 1024×1024
  3. 张量转换:
    • CvMat2Tensor 完成以下处理:
      • 数据类型转换:uint8 → float32
      • 像素归一化(例如除以 255.0)
      • 通道顺序调整:如 BGR → RGB(若模型要求)
      • 添加 Batch 维度:转换后张量尺寸为 [1, 3, H, W],如 [1, 3, 1024, 1024]
  4. DMA准备:
    • dmaInit 函数配置 DMA 传输,将图像张量从主机内存拷贝至 NPU 设备端,用于硬件加速推理。

4. 后处理(Host端CPU处理)

后处理机制:

graph LR
    A[NPU输出张量] --> B[数据回传]
    B --> C[结果解析]
    C --> D[尺寸调整]
    D --> E[类别映射]
    E --> F[可视化处理]

    subgraph 后处理流程
        B --> B1["to(HostDevice::MemRegion())"]
        B1 --> B2["示例:shape=[1,512,512,19]"]
        C --> C1["(float*/int8_t*)host_tensor.data().cptr()"]
        D --> D1["resize(原图尺寸)"] --> D2["示例:512x512→1024x2048"]
        E --> E1["map_plot颜色映射"] --> E2["使用Cityscapes调色板"]
        F --> F1["cv::imwrite保存"] --> F2["示例:road→灰色,car→蓝色"]
    end

    style B fill:#F8CECC,stroke:#B85450
    style C fill:#D5E8D4,stroke:#82B366
    style D fill:#DAE8FC,stroke:#6C8EBF
    style E fill:#FFF2CC,stroke:#D6B656
    style F fill:#E1D5E7,stroke:#9673A6

关键操作:

// 数据回传
auto host_tensor = result_tensor[0].to(HostDevice::MemRegion());

// 结果解析(多通道float/单通道int8)
if (output_channel == 1) {
    auto tensor_data = (int8_t*)host_tensor.data().cptr();
    net_result_mat = cv::Mat(h,w,CV_8SC1,tensor_data); 
} else {
    auto tensor_data = (float*)host_tensor.data().cptr();
    net_result_mat = cv::Mat(h,w,CV_32FC19,tensor_data);
}

// 尺寸调整
cv::resize(net_result_mat, resultMat, cv::Size(orig_w,orig_h), cv::INTER_LINEAR);

// 类别映射与可视化
map_plot(resultMat, img_id_mat, img_color_mat, img.dst_img);

具体处理流程:

  1. 数据回传

    • to(HostDevice::MemRegion()) 将NPU输出的分割张量复制到Host内存
    • 示例输出shape为[1,512,512,19](Cityscapes的19类分割任务)
  2. 结果解析

    • 根据输出类型(float多通道/int8单通道)转换数据指针
    • 构建OpenCV矩阵:多通道用CV_32FC19,单通道用CV_8SC1
  3. 尺寸调整

    • 将网络输出resize到原始图像尺寸(如1024x2048)
    • 使用双线性插值保持边缘平滑
  4. 类别映射

    • map_plot函数核心操作:

      解释// 像素级类别索引→调色板颜色
      for(auto i : img_id){
          if(i >= 19) color = [0,0,0]; // 未知类别用黑色
          else color = PALETTE_CITYSCAPES[i]; // 按预设调色板着色
      }
      
  5. 结果保存

    • Windows/Linux路径差异化处理
    • 保存两种结果:
      • img_id_mat:单通道类别ID图(可用于量化分析)
      • img_color_mat:三通道伪彩色图(直观可视化)

2.2 硬件资源分配

1. 硬件映射策略

graph TD
    subgraph CPU前处理
        B1[图像加载: OpenCV读取] --> B2[尺寸变换: Resize]
        B2 --> B3[补边处理: rPad]
        B3 --> B4[张量转换: uint8转float32等]
    end
    
    subgraph ImageMake硬件加速
        C1[DMA传输初始化: 主机到NPU]
    end
    
    subgraph NPU推理
        D1[NPU推理计算]
    end
    
    subgraph CPU后处理
        E1[数据回传: NPU到主机] --> E2[结果解析: float/int8转OpenCV矩阵]
        E2 --> E3[尺寸调整: 调整到原始尺寸]
        E3 --> E4[类别映射: 像素索引到颜色]
    end
 
    A[输入图像] --> B[CPU前处理]
    B --> C[ImageMake硬件加速]
    C --> D[NPU推理]
    D --> E[detpost硬件加速]
    E --> F[CPU后处理]

2. 硬件资源分配方案总表

** 硬件分配** 代码/模块对应位置 关键变更说明
1. CPU前处理
- 图像加载 OpenCV读取图像文件 CPU cv::imread(img_path) 新增补边处理环节
- 尺寸变换 动态Resize(保持长宽比) CPU cv::resize(..., INTER_LINEAR) 支持非方形输入
- 补边处理 四周Padding填充(常量值) CPU cv::copyMakeBorder() 新增预处理步骤
- 张量转换 uint8转float32/归一化 CPU convertTo(CV_32F, 1/255.0) 归一化合并到转换
2. ImageMake加速
- DMA传输初始化 主机内存→NPU内存 FPGA dmaInit(host_ptr, npu_ptr, size) 硬件自动完成数据对齐
3. NPU推理
- 双分支卷积计算 细节分支+语义分支并行 NPU session.forward() 新增双分支支持
- BGALayer特征融合 动态加权融合双分支特征 NPU 编译器自动优化 新增硬件融合模块
4. CPU后处理
- 数据回传 NPU内存→主机内存 FPGA/CPU tensor.to(HostDevice::MemRegion()) 支持int8/float多格式
- 结果解析 原始数据转OpenCV矩阵 CPU cv::Mat(..., data_ptr) 自动识别单通道/多通道
- 尺寸调整 还原至原始图像尺寸 CPU cv::resize(..., orig_h, orig_w) 双线性插值保持边缘
- 类别映射 像素索引→Cityscapes调色板 CPU map_plot()中的PALETTE_CITYSCAPES 新增未知类别处理(填充黑色)
- 掩膜渲染 伪彩色结果叠加原始图 CPU cv::addWeighted() 可调节透明度

3. 硬件-软件交互示意图

sequenceDiagram
    participant Host as Host CPU
    participant NPU as NPU/PL
    participant DDR as 设备内存

    Host->>Host: 图像加载/Resize/Crop
    Host->>DDR: DMA传输预处理数据
    NPU->>NPU: 执行模型推理
    DDR->>Host: DMA回传输出张量
    Host->>Host: Softmax/TopK处理
    Host->>Host: 结果可视化与保存

3. 模型导出说明

ErfNet 主干

1)基础卷积算子:

  • 常规卷积:nn.Conv2d直接实现
  • 转置卷积(上采样):nn.ConvTranspose2d实现
  • 批归一化:nn.BatchNorm2d直接实现
  • 池化操作:nn.MaxPool2d实现下采样
  • 正则化:nn.Dropout2d实现通道随机失活

2)特殊卷积结构:

  • 非对称卷积分解:
    • 3x1卷积+1x3卷积:nn.Conv2d组合实现
    • 带膨胀率的非对称卷积:通过dilation参数实现
  • 残差连接:通过张量相加实现

3)下采样模块:

  • DownsamplerBlock:
    • 并行路径:
      • 卷积路径:nn.Conv2d+nn.BatchNorm2d
      • 池化路径:nn.MaxPool2d
    • 特征合并:通道维度拼接
    • 批归一化:nn.BatchNorm2d统一处理合并特征

4)上采样模块:

  • UpsamplerBlock:
    • 转置卷积:nn.ConvTranspose2d实现2倍上采样
    • 批归一化:nn.BatchNorm2d处理上采样结果

5)核心残差模块:

  • non_bottleneck_1d:
    • 双分支非对称卷积:
      • 第一组:3x1→1x3卷积+BN
      • 第二组:带膨胀率的3x1→1x3卷积+BN
    • 残差连接:输入输出直接相加
    • 正则化:nn.Dropout2d

6)编解码结构:

  • 编码器(Encoder):
    • 初始下采样块→多级下采样→系列non_bottleneck_1d
    • 输出卷积:1x1卷积降维
  • 解码器(Decoder):
    • 上采样块→non_bottleneck_1d→上采样→输出转置卷积

7)特殊参数配置:

  • 膨胀率设置:采用[1,2,4,8,16]的指数增长模式
  • 批归一化参数:固定eps=0.001,momentum=0.1
  • Dropout配置:编码器p=0.1,解码器p=0

8)维度变化处理:

  • 下采样时通道扩展:通过concat合并卷积和池化路径
  • 上采样时通道压缩:通过转置卷积同步调整空间和通道维度

5)ErfNet 与 Icraft 硬件支持算子对应表

ERFNet 功能组件 PyTorch 实现方式 iCraft 对应算子组合
基础卷积算子
常规卷积 nn.Conv2d Conv2d
转置卷积(上采样) nn.ConvTranspose2d ConvTranspose2d
批归一化 nn.BatchNorm2d BatchNorm2d
最大池化 nn.MaxPool2d MaxPool2d
Dropout正则化 nn.Dropout2d 不支持二维Dropout
下采样模块
DownsamplerBlock双路径 Conv2d+MaxPool2d并行 → 通道拼接 Conv2d + MaxPool2d + Concat
合并后批归一化 nn.BatchNorm2d BatchNorm2d
上采样模块
UpsamplerBlock ConvTranspose2d + BN ConvTranspose2d + BatchNorm2d
核心残差模块
non_bottleneck_1d 3x1→1x3非对称卷积组合 Conv2d(k=3x1) + Conv2d(k=1x3) ×2
带膨胀率的卷积 dilation=[1,2,4,8,16] Conv2d(dilation=*)
残差连接 张量相加 Add
编解码结构
编码器初始块 DownsamplerBlock 见下采样模块
编码器层级结构 交替使用下采样和non_bottleneck_1d 组合对应算子
解码器上采样块 UpsamplerBlock + non_bottleneck_1d 见上采样模块和核心残差模块
特殊操作
输出卷积 1x1卷积降维 Conv2d(k=1x1)
多尺度膨胀卷积 膨胀率指数增长 Conv2d(dilation=2^n)
特征维度处理 通道拼接/维度调整 Concat/Reshape
激活函数
ReLU(隐含) 默认激活 ReLU

导出过程和1_scripts中的脚本运行说明

直接加载模型,只导出ErfNet 主干,前后处理不保存在在导出模型中。

├── 0_infer.py       >框架下推断脚本
├── 1_save.py        >trace模型保存onnx/pt脚本
├── 2_save_infer.py  >onnx/pt推断脚本
  • 0_infer.py

    • 可以推理一张图(或其他格式数据)并得到最终结果,目的是为了快速跑通推理代码,为后续剥离/移植前后处理做准备。为了实现快速不能用整个数据集作为输入。
    • 用法: python 0_infer.py
  • 1_save.py

    • 保存模型,导出的模型默认存放至2_compile/fmodel路径下,如../2_compile/fmodel/erfnet_512x1048.pt

    • 用法: python 1_save.py

    • 导出过程修改点

      • (1)因为icraft不支持nn.Dropout2d,原本在non_bottleneck_1d.forward中会使用nn.Dropout2d,直接去掉这个算子,生成一个新的forward。

        # 1_save.py文件中
        
        # -------- 修改 non_bottleneck_1d 的 forward --------
        def new_forward(self, input):
            output = self.conv3x1_1(input)
            output = F.relu(output)
            output = self.conv1x3_1(output)
            output = self.bn1(output)
            output = F.relu(output)
        
            output = self.conv3x1_2(output)
            output = F.relu(output)
            output = self.conv1x3_2(output)
            output = self.bn2(output)
        
            return F.relu(output + input)
        
        non_bottleneck_1d.forward = new_forward
        
      • (2)分析parsed后的模型最后一层,可以看到最后一层是Conv2dTranspose算子,因为要使用segpost FPGA硬算子加速,要求最后一层是普通卷积。所以在Conv2dTranspose后再加一个恒等卷积。

        • # -------- ERFNet + 非分组恒等卷积 --------
          class ERFNetWithIdentityConv(nn.Module):
              def __init__(self, base_model, n_classes):
                  super().__init__()
                  self.base = base_model
                  self.final_conv = nn.Conv2d(n_classes, n_classes, kernel_size=1, bias=False)  # 默认 groups=1
          
                  # 初始化为恒等变换 (单位矩阵形式)
                  with torch.no_grad():
                      self.final_conv.weight.zero_()  # 所有权重先清零
                      for i in range(n_classes):
                          self.final_conv.weight[i, i, 0, 0] = 1.0  # 对角线设置为1
          
              def forward(self, x):
                  out = self.base(x)
                  out = self.final_conv(out)
                  return out
          
        • 最后一层是一个恒等卷积(不需训练)。

  • 2_save_infer.py

    • 用修改后保存的模型做前向推理,目的是为了与0_infer的结果对比,确保导出静态模型的正确性。
    • 用法: python 2_save_infer.py

4. cpu处理部分c++工程移植说明

4.1 cpu环节实现的功能

CPU(Host 端)承担了模型部署与推理流程中大量关键的前处理、后处理和控制逻辑。

1)配置与初始化(控制逻辑)

  1. 读取 YAML 配置文件:
    • 包括模型路径、仿真与实卡运行开关、是否可视化输出、是否保存结果等。
    • 加载图像路径、类别标签文件、网络输入尺寸等信息。
  2. 构建模型路径、打开设备并加载模型:
    • 根据配置动态拼接 JSON 模型路径和 RAW 参数路径。
    • 判断是否在仿真(sim)还是实际设备(实卡)上运行。
    • 加载网络结构并初始化 Session 和 Device。

2)前处理(Preprocess)

这是典型的 在 CPU 上运行的图像前处理操作,目标是将原始图片转为模型输入格式:

  1. 图像读取:
    • 使用 OpenCV 加载图像为 BGR 格式。
  2. 尺寸变换与补边:
    • Resize:以 长边为基准(此处为宽 = 2048),将图像缩放,使其长边匹配目标尺寸(如输入为 512×1024,则长边为1024)
      • 缩放比例 = 1024 / 2048 = 0.5
      • 缩放后图像尺寸为 512×1024
    • rPad:在 短边方向(此处为高 = 512) 进行对称补零,补成最终输入尺寸(如目标为 512×1024,实际已满足则无需额外 padding)
    • 若目标为正方形输入(如 1024×1024),则:
      • 当前图像已为 512×1024,需要在高方向补齐至1024,高度补 512 像素(上下各补 256)
      • 最终尺寸为 1024×1024
  3. 图像转换为张量:
    • 将裁剪后的图像转为 NCHW 格式、float 类型张量,用于输入模型(CvMat2Tensor())。
  4. 输入 DMA 传输:
    • 将 CPU 中的图像张量传输给设备端执行推理(dmaInit())。

3)模型执行控制

  1. 执行模型前向推理:
    • 由 CPU 调用 session.forward() 启动一次推理流程,控制输入和获取输出。
    • 返回的是在设备端计算完成的结果张量。
  2. 设备端输出转回 Host 内存:
    • 将设备端输出张量(float*)搬运回 CPU 内存空间供后处理使用(to(HostDevice::MemRegion()))。

4)后处理(Postprocess)

这些操作也是由 CPU 进行的,主要包括:

  1. 数据回传
    • to(HostDevice::MemRegion()) 将NPU输出的分割张量复制到Host内存。
    • 这里使用segpost FPGA硬算子加速。
    • 示例输出shape为[1,512,1024,19](Cityscapes的19类分割任务)
  2. 结果解析
    • 根据输出类型(float多通道/int8单通道)转换数据指针
    • 构建OpenCV矩阵:多通道用CV_32FC19,单通道用CV_8SC1
  3. 尺寸调整
    • 将网络输出resize到原始图像尺寸(如1024x2048)
    • 使用双线性插值保持边缘平滑
  4. 类别映射
    • 将每个类别用不同颜色表示。

4.2 各环节耗时

命令行输入icraft run ./config/ErfNet_8.toml,以仿真int8量化后的模型为例,单一输入图像对理时间输如下:

# ErfNet_8.toml文件中
[run]
log_time = true
log_io = true
dump_format = "SFB"
json = "../3_deploy/modelzoo/ErfNet/imodel/8/ErfNet_BY.json"
raw = "../3_deploy/modelzoo/ErfNet/imodel/8/ErfNet_BY.raw"
input = "./qtset/cityscapes/frankfurt_000000_000294_leftImg8bit.png"
log_path = "./logs/"
D:\dtpw\Desktop\icraft_workplace\erfnet\2_compile>icraft run ./config/ErfNet_8.toml
[06/30/25 17:55:49.661] [I] ImageMake Version is 3.8.1.0
SegPost Version is 3.8.1.0
[06/30/25 17:55:49.741] [W] Turn on both dump and calctime will get wrong timing results.
Running     : [██████████████████████████████████████████████████ ] 100% [00m:22s]
Input     0    In:                                                              Out: (0|-1,-1,-1,-1)
Resize    1    In: (0|-1,-1,-1,-1)                                              Out: (1|1,512,1024,3)
ImageMake 609  In: (1|1,512,1024,3)                                             Out: (1295|1,1,512,1024,4)
HardOp    902  In: (1295|1,1,512,1024,4)                                        Out: (443|1,1,512,1024,32)
SegPost   610  In: (443|1,1,512,1024,32)                                        Out: (1296|1,512,1024,1)
Output    215  In: (1296|1,512,1024,1)                                          Out:
[06/30/25 17:56:12.117] [I] network wall:   0.0162 ms
Load Json Time:   43.718 ms
Load Raw Time:   32.475 ms
opId: 0    opType: Input           wall:   32.332 ms  mem:    0.000 ms  hard:    0.000 ms  other:   32.332 ms
opId: 1    opType: Resize          wall:   50.862 ms  mem:    0.000 ms  hard:    0.000 ms  other:   50.862 ms
opId: 609  opType: ImageMake       wall:  130.088 ms  mem:    0.000 ms  hard:    0.000 ms  other:  130.088 ms
opId: 902  opType: HardOp          wall: 19747.800 ms  mem:    0.000 ms  hard:    0.000 ms  other: 19747.800 ms
opId: 610  opType: SegPost         wall: 2015.161 ms  mem:    0.000 ms  hard:    0.000 ms  other: 2015.161 ms
opId: 215  opType: Output          wall:    0.013 ms  mem:    0.000 ms  hard:    0.000 ms  other:    0.013 ms


################# SUMMARY #################

OpNum 1   OpType HardOp           total_wall: 19747.800 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other: 19747.800 ms
OpNum 1   OpType ImageMake        total_wall:  130.088 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other:  130.088 ms
OpNum 1   OpType Input            total_wall:   32.332 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other:   32.332 ms
OpNum 1   OpType Output           total_wall:    0.013 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other:    0.013 ms
OpNum 1   OpType Resize           total_wall:   50.862 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other:   50.862 ms
OpNum 1   OpType SegPost          total_wall: 2015.161 ms  total_mem:    0.000 ms  total_hard:    0.000 ms  total_other: 2015.161 ms
total_time: 0.016 ms, total_hard_time: 0.000 ms
ImageMake: 0.000 ms
SegPost: 0.000 ms

当前仿真系统的cpu配置情况(不同配置运行速度不同,只作参考)

D:\dtpw\Desktop\icraft_workplace\ErfNet\2_compile>wmic cpu get name,numberofcores,numberoflogicalprocessors,maxclockspeed
MaxClockSpeed  Name                                    NumberOfCores  NumberOfLogicalProcessors
1800           AMD Ryzen 7 4800U with Radeon Graphics  8              16

分析

  • 核心推理耗时在 HardOp,即模型主干部分(19747.800 ms),这是真正的前向计算阶段;
  • 前处理部分(Resize + ImageMake)耗时约 130.088ms;
  • 类型转换和输出阶段使用SegPost耗时2015.161 ms;

6. ICRAFT编译

  • 不支持算子(但通过前面所述手段绕过)

    在icraft logs中找到不支持的算子序列、在源码中定位对应的源码、实现的功能

    算子序列 对应源码 实现的功能
    aten::feature_dropout nn.Dropout2d 整个通道置零

编译通过

  • 使用运行时仿真得到的仿真效果截图或数据,需提供表格

    比赛报告中要求有:框架动态图推理、导出静态模型推理、解析组件仿真、量化组件仿真、指令组件仿真的仿真效果截图或数据。对于解析组件仿真、量化组件仿真、指令组件仿真等中间过程仿真该怎么做?文档中并没写明,只能参考[docs/Part 2_2 icraft tools.md · spiger/TUTORIAL - 码云 - 开源中国](https://gitee.com/mxh-spiger/tutorial-runtime/blob/tt3.7.1/docs/Part 2_2 icraft tools.md#34-结果分析)中的icraft run工作简单输出每个组件的仿真。

1. 框架动态图推理

  • 输入图片

  • 框架推理

    • python 0_infer.py

    • 输出结果:

2. 导出静态模型推理

  • 输入图片:同上

  • 导出静态模型推理

    • python 2_save_infer.py

3. 解析组件仿真(icraft run命令)

  • 命令:

    • # ErfNet_8.toml文件中
      [run]
      log_time = true
      log_io = true
      dump_format = "SFB"
      json = "../3_deploy/modelzoo/ErfNet/imodel/8/ErfNet_parsed.json"  					
      raw = "../3_deploy/modelzoo/ErfNet/imodel/8/ErfNet_parsed.raw"                      
      input = "./qtset/cityscapes/frankfurt_000000_000294_leftImg8bit.png"
      backends = "Host"
      
    • 命令行运行\ErfNet\2_compile>icraft run ./config/ErfNet_8.toml

  • 输出截图(使用仿真命令icraft run)

4. 量化组件仿真(icraft run命令)

  • 命令:

    • # ErfNet_8.toml文件中
      [run]
      log_time = true
      log_io = true
      dump_format = "SFB"
      json = "../3_deploy/modelzoo/ErfNet/imodel/8/ErfNet_quantized.json"  					
      raw = "../3_deploy/modelzoo/ErfNet/imodel/8/ErfNet_quantized.raw"                      
      input = "./qtset/cityscapes/frankfurt_000000_000294_leftImg8bit.png"
      backends = "Host"
      
    • 命令行运行\ErfNet\2_compile>icraft run ./config/ErfNet_8.toml

  • 输出截图

5. 指令组件仿真(icraft run命令)

  • 命令:

    • # ErfNet_8.toml文件中
      [run]
      log_time = true
      log_io = true
      dump_format = "SFB"
      json = "../3_deploy/modelzoo/ErfNet/imodel/8/ErfNet_BY.json"  					
      raw = "../3_deploy/modelzoo/ErfNet/imodel/8/ErfNet_BY.raw"                      
      input = "./qtset/cityscapes/frankfurt_000000_000294_leftImg8bit.png"
      backends = "Host"
      
    • 命令行运行\Bisenet\2_compile>icraft run ./config/ErfNet_8.toml

  • 输出截图

6. 完整的c++工程仿真输出(c++ runtime)

  • 在3_deploy/modelzoo/ErfNet/build_win目录下运行:

    Release\ErfNet_psin.exe ..\cfg\ErfNet_8.yaml  #注意,windows中一般用\,而/可以用但有些时假会报错,仿真时分割任务时间长,请等待。
    
    Release\ErfNet_psin.exe ..\cfg\ErfNet_16.yaml
    
    Release\ErfNet_psin.exe ..\cfg\ErfNet_mix.yaml
    
  • 输出截图

7. 8bit,16bit,混合精度的精度测试结果

  • 分别运行以下命令,得到8bit,16bit,混合精度模型在CITYSCAPES验证集图像上的分类结果。
Release\ErfNet_psin.exe ..\cfg\ErfNet_test_8.yaml  # 注意,windows中一般用\,而/可以用但有些时假会报错

Release\ErfNet_psin.exe ..\cfg\ErfNet_test_16.yaml

Release\ErfNet_psin.exe ..\cfg\ErfNet_test_mix.yaml

结果分别保存在

3_deploy\modelzoo\ErfNet\io\output\8\val_5000
3_deploy\modelzoo\ErfNet\io\output\16\val_5000
3_deploy\modelzoo\ErfNet\io\output\mix\val_5000
  • 运行精度测试命令\Release\ErfNet_psin.exe ..\cfg\ErfNet_test_8.yaml 后,使用metrics测试脚本,根据保存的测试结果计算出精度指标。

    metrics测试脚本使用与配置说明:

    • 精度测试脚本存放于:https://gitee.com/mxh-spiger/benchmark
    • 因为该模型是在512x1024的输入和输出下训练的,CITYSCAPES验证集的GT大小都为1024x2028,所以可使用我提供的erfnet\3_deploy\modelzoo\ErfNet\io\output\8\leftImg8bit\val\myresizeGT.py的这个脚本resize到1024x2028大小,再进行精度测试。
  • 精度结果

    • ErfNet input shape 精度
      int8 [1, 3, 512,1024] allAcc/mAcc/mIoU 93.11%/71.78%/61.67%
      int16 [1, 3, 512,1024] allAcc/mAcc/mIoU 94.94%/81.27%/71.23%
      mix(混合精度) [1, 3, 512,1024] allAcc/mAcc/mIoU 93.43%/70.61%/63.32%

8. 编译过程的一些特殊说明

  • 注意,windows中的命令行中,一般用左,右/可以用但有些时假会报错

  • 编译命令注意事项

    • cd 3_deploy\modelzoo\ErfNet
      mkdir -p build_win
      cd build_win
      
      cmake -G "Visual Studio 17 2022" -A x64 ..    # 不要直接cmake ..
      cmake --build . --config Release
      
    • 不能直接使用cmake ..命令,因为如果系统上多种编译器,且默认cmake使用的编译器不是visualstudio2022会出错,所以使用-G "Visual Studio 17 2022" -A x64命令,让cmake使用visualstudio2022编译器编译。