504 lines
14 KiB
Markdown
504 lines
14 KiB
Markdown
# Netrans Cookbook - 实用指南
|
||
|
||
本文档提供 Netrans 的速查表、配置说明、场景示例和故障排查。
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
1. [快速开始](#快速开始)
|
||
2. [速查表](#速查表)
|
||
3. [数据集格式](#数据集格式)
|
||
4. [导入格式兼容性](#导入格式兼容性)
|
||
5. [配置字段说明](#配置字段说明)
|
||
6. [场景示例](#场景示例)
|
||
7. [故障排查](#故障排查)
|
||
|
||
---
|
||
|
||
## 快速开始
|
||
|
||
## 安装指南
|
||
|
||
```bash
|
||
# 克隆仓库并创建 Python 3.10 环境后,在源码根目录执行
|
||
make install
|
||
source ~/.bashrc
|
||
```
|
||
|
||
请使用项目提供的 Makefile 完成安装;在源码根目录执行 `make install` 即可安装 Netrans 及所需组件。环境创建和日常更新步骤见本项目的介绍与安装页(仓库中为 `README.md`)。
|
||
|
||
### 基础转换流程
|
||
|
||
```bash
|
||
# 1. 加载模型
|
||
netrans load ./model --mean 0 0 0 --std 255
|
||
|
||
# 2. 量化
|
||
netrans quantize ./model asymu8 --algorithm 1 --iterations 1
|
||
|
||
# 3. 导出
|
||
netrans export ./model asymu8 --platform pnna
|
||
```
|
||
|
||
### Python API 完整流程
|
||
|
||
```python
|
||
from netrans import Netrans
|
||
|
||
model = Netrans()
|
||
model.load('./model', mean=[0, 0, 0], std=255)
|
||
model.quantize('asymu8')
|
||
model.export('asymu8', platform='pnna', preprocess=True, postprocess=True)
|
||
```
|
||
|
||
---
|
||
|
||
## 速查表
|
||
|
||
### 量化类型
|
||
|
||
| 类型 | 激活 | 权重 | 说明 |
|
||
|------|------|------|------|
|
||
| `asymu8` | uint8 | uint8 | 非对称8位 |
|
||
| `symi8` | int8 | int8 | 对称8位 |
|
||
| `symi16` | int16 | int16 | 对称16位 |
|
||
| `dfpi16` | int16 | int16 | 动态定点16位 |
|
||
| `fp16` | float16 | float16 | 半精度浮点 |
|
||
|
||
**注意事项**:
|
||
|
||
- **FP16**: 支持将预处理和后处理嵌入推理计算图
|
||
- **symi16**: 不支持将预处理嵌入推理节点,导出时需设置 `preprocess=False`,或改用 `dfpi16`
|
||
|
||
### 量化算法
|
||
|
||
| 值 | 算法 | 说明 | 适用场景 |
|
||
|----|------|------|---------|
|
||
| `0` | normal | 直接统计 min/max | 快速测试 |
|
||
| `1` | KL | KL散度(**推荐**) | 平衡精度和速度 |
|
||
| `2` | moving_average | 移动平均 | 动态范围大的数据 |
|
||
| `3` | auto | 自动选择 | 最高精度,最慢 |
|
||
|
||
### 预处理参数
|
||
|
||
#### 自动广播
|
||
|
||
mean/std 参数支持自动广播,无需手动填写所有通道值:
|
||
|
||
```python
|
||
# 显式指定每个通道
|
||
model.load('./model', mean=[128, 128, 128], std=[255, 255, 255])
|
||
|
||
# 使用自动广播
|
||
model.load('./model', mean=128, std=255) # 单值自动广播到所有通道
|
||
model.load('./model', mean=[128], std=[255]) # 单元素列表也自动广播
|
||
model.load('./model', mean=[128, 128, 128], std=255) # std 单值广播
|
||
```
|
||
|
||
**广播规则**:
|
||
- 单值(如 `mean=128`)→ 自动广播到所有通道
|
||
- 单元素列表(如 `mean=[128]`)→ 自动广播到所有通道
|
||
- 多值列表(如 `mean=[128, 128, 128]`)→ 原样使用,长度必须匹配通道数
|
||
|
||
常见模型的 mean/std 配置:
|
||
|
||
| 模型 | mean | std | 说明 |
|
||
|------|------|-------|------|
|
||
| YOLOv5/v8 | `0` 或 `[0, 0, 0]` | `255` | 归一化到 [0,1] |
|
||
| ResNet/ImageNet | `[123.675, 116.28, 103.53]` | `[58.395, 57.12, 57.375]` | ImageNet 统计值 |
|
||
| MobileNet | `127.5` | `127.5` | 归一化到 [-1,1] |
|
||
| 自定义 | 根据训练配置 | 根据训练配置 | 与训练时一致 |
|
||
|
||
`std` 是归一化除数;生成的 `_inputmeta.yml` 固定使用 `scale` 字段,其值为 `1 / std`。
|
||
|
||
### 平台与多核
|
||
|
||
| 平台 | 多核支持 | 说明 |
|
||
|------|----------|------|
|
||
| `pnna` | 不支持 | 默认平台,单核架构 |
|
||
| `pnna2` | 支持 1-4 核 | 多核架构 |
|
||
|
||
**多核配置**(仅 pnna2):
|
||
|
||
| 配置值 | 说明 |
|
||
|--------|------|
|
||
| `1core` / `1` | 单核模式 |
|
||
| `2core` / `2` | 双核模式 |
|
||
| `3core` / `3` | 三核模式 |
|
||
| `4core` / `4` | 四核模式 |
|
||
|
||
---
|
||
|
||
## 数据集格式
|
||
|
||
Netrans 量化支持以下数据集格式用于校准:
|
||
|
||
### 1. 文本列表 (.txt)
|
||
|
||
每行一个图像路径:
|
||
```
|
||
/path/to/image1.jpg
|
||
/path/to/image2.jpg
|
||
/path/to/image3.jpg
|
||
```
|
||
|
||
### 2. NumPy 数组 (.npy)
|
||
|
||
预处理后的数组数据,形状为 `[N, C, H, W]`。
|
||
|
||
**注意**:使用 `.npy` 作为输入时,Netrans **不会触发预处理**(mean/std 等操作),请确保数组数据已经完成预处理。
|
||
|
||
### 3. 自动生成(默认)
|
||
|
||
当未提供数据集时,Netrans 会自动生成随机数据用于量化。
|
||
|
||
**注意**:随机数据会影响量化精度,生产环境建议使用真实数据。
|
||
|
||
---
|
||
|
||
## 导入格式兼容性
|
||
|
||
| 格式 | 支持版本/说明 |
|
||
|------|--------------|
|
||
| **ONNX** | opset 7-17(ONNX 1.2-1.12)|
|
||
| **TensorFlow** | 1.4.x, 2.0.x, 2.3.x, 2.6.x, 2.8.x, 2.10.x, 2.12.x, 2.15.x |
|
||
| **TFLite** | schema 2.15.0(TensorFlow 2.15.0)|
|
||
| **PyTorch** | 1.5.1-2.2.2(`.pt` 通过 ONNX 后端导入)|
|
||
| **Caffe** | 标准协议及部分扩展协议,具体取决于模型层和算子 |
|
||
| **Darknet** | 标准模型,具体取决于模型层和算子 |
|
||
| **Keras** | TensorFlow 2.0.x - 2.15.x 生成的模型 |
|
||
|
||
---
|
||
|
||
## 配置字段说明
|
||
|
||
### _inputmeta.yml 重点字段
|
||
|
||
```yaml
|
||
input_meta:
|
||
databases:
|
||
- ports:
|
||
- lid: input_0 # 输入层 ID
|
||
shape: [1, 3, 640, 640] # [batch, channels, height, width]
|
||
preprocess:
|
||
mean: [0, 0, 0] # 通道均值
|
||
scale: [0.00392, 0.00392, 0.00392] # 缩放系数
|
||
preproc_node_params:
|
||
add_preproc_node: true # 是否嵌入预处理节点到网络
|
||
preproc_dtype_converter: # 数据类型转换(定点量化时)
|
||
qtype: uint8
|
||
quantizer: asymmetric_affine
|
||
scale: 0.965
|
||
zero_point: 0
|
||
```
|
||
|
||
**关键字段**:
|
||
- `mean`: 与 `load()` 的 `mean` 对应;`scale` 是 `_inputmeta.yml` 的固定字段,值为 `1 / std`,不是 `load()` 的参数名
|
||
- `add_preproc_node`: 设为 `true` 将预处理嵌入网络图
|
||
- `preproc_dtype_converter`: 定点量化时的输入数据类型转换
|
||
|
||
### _postprocess_file.yml 重点字段
|
||
|
||
```yaml
|
||
app_postprocs:
|
||
- lid: output_0 # 输出层 ID
|
||
postproc_params:
|
||
add_postproc_node: true # 是否嵌入后处理节点
|
||
force_float32: true # 强制输出为 float32
|
||
```
|
||
|
||
**关键字段**:
|
||
- `add_postproc_node`: 设为 `true` 将反量化等后处理嵌入网络图
|
||
- `force_float32`: 强制输出数据类型为 float32
|
||
|
||
---
|
||
|
||
## 场景示例
|
||
|
||
### 场景1:YOLOv5s 完整转换
|
||
|
||
```python
|
||
#!/usr/bin/env python3
|
||
"""YOLOv5s 模型转换完整示例"""
|
||
from netrans import Netrans
|
||
|
||
model = Netrans()
|
||
model.load('./yolov5s', mean=[0, 0, 0], std=255)
|
||
model.quantize('asymu8', algorithm=1)
|
||
model.export('asymu8', platform='pnna', preprocess=True, postprocess=True)
|
||
print("✅ 转换完成: wksp/yolov5s_asymu8_nbg_unify/network_binary.nb")
|
||
```
|
||
|
||
### 场景2:ResNet50 ImageNet 模型
|
||
|
||
```python
|
||
#!/usr/bin/env python3
|
||
"""ResNet50 ImageNet 模型转换"""
|
||
from netrans import Netrans
|
||
|
||
model = Netrans()
|
||
model.load(
|
||
'./resnet50',
|
||
mean=[123.675, 116.28, 103.53],
|
||
std=[58.395, 57.12, 57.375]
|
||
)
|
||
model.quantize('asymu8', algorithm=1, iterations=5)
|
||
model.export('asymu8', platform='pnna')
|
||
```
|
||
|
||
### 场景3:混合量化
|
||
|
||
```python
|
||
#!/usr/bin/env python3
|
||
"""YOLO 模型混合量化 - 检测头高精度"""
|
||
from netrans import Netrans
|
||
import os
|
||
|
||
model_dir = './yolov5s'
|
||
model = Netrans()
|
||
model.load(model_dir, mean=[0, 0, 0], std=255)
|
||
|
||
# 创建混合量化配置
|
||
config_file = os.path.join(model_dir, 'cust_qnt_layers.txt')
|
||
with open(config_file, 'w') as f:
|
||
f.write('Conv_245\nConv_269\nConv_293\n')
|
||
|
||
model.quantize_hybrid('asymu8', cust_qnt_layers=config_file)
|
||
model.export('asymu8', platform='pnna', use_hybrid=True)
|
||
```
|
||
|
||
### 场景4:PNNA2 多核导出
|
||
|
||
```python
|
||
#!/usr/bin/env python3
|
||
"""PNNA2 平台 4 核导出"""
|
||
from netrans import Netrans
|
||
|
||
model = Netrans()
|
||
model.load('./model', mean=[0, 0, 0], std=255)
|
||
model.quantize('asymu8')
|
||
model.export('asymu8', platform='pnna2', core_num='4core')
|
||
```
|
||
|
||
### 场景5:批量转换
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
MODELS=("yolov5s" "yolov5m" "yolov5l")
|
||
|
||
for model in "${MODELS[@]}"; do
|
||
echo "=== 转换 $model ==="
|
||
netrans load ./$model --mean 0 0 0 --std 255
|
||
netrans quantize ./$model asymu8 --algorithm 1
|
||
netrans export ./$model asymu8 --platform pnna
|
||
echo "✅ $model 完成"
|
||
done
|
||
```
|
||
|
||
### 场景6:精度验证
|
||
|
||
```python
|
||
#!/usr/bin/env python3
|
||
"""精度验证 - 对比浮点和量化模型"""
|
||
from netrans import Netrans
|
||
|
||
model = Netrans()
|
||
model.load('./model', mean=[0, 0, 0], std=255)
|
||
|
||
# 浮点推理
|
||
model.inference('float32', iterations=1)
|
||
|
||
# 量化并推理
|
||
model.quantize('asymu8')
|
||
model.inference('asymu8', iterations=1)
|
||
|
||
# 对比 golden 目录输出
|
||
# wksp/model_float32/golden/ vs wksp/model_asymu8/golden/
|
||
```
|
||
|
||
### 场景7:复用 json/data 多量化类型导出
|
||
|
||
```python
|
||
#!/usr/bin/env python3
|
||
"""一次 load,多量化类型连续导出"""
|
||
from netrans import Netrans
|
||
|
||
model = Netrans()
|
||
model.load('./model', mean=[0, 0, 0], std=255)
|
||
|
||
# 同一实例上连续量化不同类型 — 安全,无需反复 load
|
||
for qtype in ['asymu8', 'symi8', 'fp16']:
|
||
model.quantize(qtype)
|
||
model.export(qtype, platform='pnna')
|
||
|
||
# 原因: nn.quantize(net, rebuild=True) 会重建网络,
|
||
# 输入 net 是否已被上一次量化不影响最终结果。
|
||
# 经验证,此方式与"每次重新 load"产生的 NBG 大小完全一致。
|
||
```
|
||
|
||
### 场景8:自定义层量化类型(set_ioq)
|
||
|
||
`lid` + `in_out_quantized` 参数组合允许为模型的输入/输出层指定与主体不同的量化类型。这在以下场景中很有用:
|
||
|
||
- 输入层需要更高精度以保留细节(如 `dfpi16`)
|
||
- 需要验证不同 ioq 类型对精度的影响
|
||
|
||
```python
|
||
#!/usr/bin/env python3
|
||
"""为特定输入/输出层指定不同的量化类型"""
|
||
from netrans import Netrans
|
||
|
||
model = Netrans()
|
||
model.load('./model', mean=[0, 0, 0], std=255)
|
||
|
||
# 全模型 asymu8,但输入层 input_0 使用 dfpi16(更高精度)
|
||
model.quantize('asymu8', lid='input_0', in_out_quantized='dfpi16')
|
||
model.export('asymu8', platform='pnna')
|
||
|
||
# 同时指定多个层,逗号分隔
|
||
model.quantize('asymu8', lid='input_0,output_0', in_out_quantized='symi16')
|
||
model.export('asymu8', platform='pnna')
|
||
```
|
||
|
||
**注意事项:**
|
||
|
||
- `lid` 和 `in_out_quantized` 必须同时使用,单独指定其中之一不会生效
|
||
- 层名可以通过 `netrans dump` 查看各层名称
|
||
- 多输入模型 + 有符号整数(symi8/symi16)+ `in_out_quantized` + `preprocess=True` 可能触发 SDK 报 Error 65280,这是 SDK 侧限制
|
||
|
||
> 完整可运行示例: [examples/custom_layer_quant/](../examples/custom_layer_quant/)
|
||
|
||
### 场景9:计算模型计算量
|
||
|
||
```python
|
||
#!/usr/bin/env python3
|
||
"""统计模型 FLOPs 和参数量"""
|
||
from netrans import Netrans
|
||
|
||
model = Netrans()
|
||
model.load('./yolov5s', mean=0, std=255)
|
||
|
||
# 浮点模型计算量
|
||
model.measure('float32')
|
||
# 输出:wksp/yolov5s_float32/
|
||
|
||
# 量化后模型计算量
|
||
model.quantize('asymu8')
|
||
model.measure('asymu8')
|
||
# 输出:wksp/yolov5s_asymu8/
|
||
|
||
# Hybrid 量化模型计算量
|
||
model.quantize_hybrid('asymu8', cust_qnt_layers='layers.txt')
|
||
model.measure('asymu8', use_hybrid=True)
|
||
# 输出:wksp/yolov5s_asymu8_hy/
|
||
```
|
||
|
||
```bash
|
||
# CLI 方式
|
||
netrans measure ./yolov5s float32
|
||
netrans measure ./yolov5s asymu8
|
||
netrans measure ./yolov5s asymu8 --use-hybrid
|
||
```
|
||
|
||
`quant_type` 省略时默认为 `float32`。非 `float32` 模式需要先生成对应的 `.quantize` 文件;Hybrid 模式需要先完成 `quantize_hybrid` 并同时指定 `--use-hybrid`。输出位于模型目录下的 `wksp/<model>_<quant_type>/`(Hybrid 为 `wksp/<model>_<quant_type>_hy/`)。
|
||
|
||
> 完整示例和前置产物说明: [examples/measure/](../examples/measure/)
|
||
|
||
### 场景10:多输入模型转换
|
||
|
||
多输入模型(如双分支网络、Siamese 网络等)的转换流程与单输入基本一致,关键差异在于 `dataset.txt` 格式和 `channel_mean_value.txt` 配置。
|
||
|
||
```python
|
||
#!/usr/bin/env python3
|
||
"""多输入 Caffe 模型转换"""
|
||
from netrans import Netrans
|
||
|
||
model = Netrans()
|
||
model.load('./multi_input', mean=[0, 0, 0], std=1.0)
|
||
model.quantize('asymu8')
|
||
model.add_pre_post('asymu8', preprocess=True, postprocess=True)
|
||
model.export('asymu8')
|
||
```
|
||
|
||
```bash
|
||
# CLI 方式
|
||
netrans load ./multi_input --mean 0 0 0 --std 1.0
|
||
netrans quantize ./multi_input asymu8
|
||
netrans add_pre_post ./multi_input asymu8 --preprocess --postprocess
|
||
netrans export ./multi_input asymu8
|
||
```
|
||
|
||
**多输入关键点:**
|
||
|
||
| 要点 | 说明 |
|
||
|------|------|
|
||
| `dataset<N>.txt` 格式 | 每个输入端口单独一个文件(`dataset0.txt`、`dataset1.txt`...),文件内为图片路径 |
|
||
| `channel_mean_value.txt` | 当前只接受一组共享的 `mean`/`std`,会应用到所有输入端口;暂不支持每个输入端口独立配置 |
|
||
| `inputs_outputs.txt` | 多输出时用 `--outputs` 指定输出端口名 |
|
||
| 前后处理节点嵌入 | 同时作用于所有输入/输出端口 |
|
||
| `--lid` + `--in_out_quantized` | 多输入模型 + 有符号整数 + ioq=Y + pre=T 可能触发 SDK Error 65280 |
|
||
|
||
> 完整可运行示例: [examples/multi_input/](../examples/multi_input/)
|
||
|
||
---
|
||
|
||
## 故障排查
|
||
|
||
### 错误速查表
|
||
|
||
| 错误信息 | 可能原因 | 解决方案 |
|
||
|----------|----------|----------|
|
||
| `xxx.quantize file does not exist` | 未执行 quantize 或类型不匹配 | 执行 `netrans quantize` 并确保类型一致 |
|
||
| `Warning: @{lid}:<suffix> not found` | hybrid 层名不匹配 | 使用 `netrans dump` 查看实际层名 |
|
||
| 模型性能分析需求 | 需要统计 FLOPs/参数量 | 使用 `netrans measure` 统计计算量 |
|
||
| NBG 文件大小异常 | 量化未生效 | 确保 `quantize()` 后调用 `export()`,不要重新创建实例 |
|
||
| 预处理参数不生效 | 配置未正确加载 | 检查 `channel_mean_value.txt` 格式和 `_inputmeta.yml` |
|
||
|
||
### 问题1: NBG 文件大小异常
|
||
|
||
**症状**: 量化后的 NBG 文件大小与浮点模型相近
|
||
|
||
**原因**: Python API 使用时未更新网络对象,或重新加载了浮点模型
|
||
|
||
**解决**:
|
||
```python
|
||
# ✅ 正确流程
|
||
model.quantize('asymu8') # 量化
|
||
model.export('asymu8') # 导出(使用同一实例)
|
||
|
||
# ❌ 错误:不要重新创建实例
|
||
model1 = Netrans()
|
||
model1.load('./model')
|
||
model1.quantize('asymu8')
|
||
|
||
model2 = Netrans() # 新实例!
|
||
model2.load('./model') # 加载的是浮点模型
|
||
model2.export('asymu8') # 导出的是浮点 NBG
|
||
```
|
||
|
||
### 问题2: 预处理参数不生效
|
||
|
||
**症状**: 模型输出与预期不符
|
||
|
||
**排查步骤**:
|
||
```bash
|
||
# 1. 检查 channel_mean_value.txt 格式
|
||
cat channel_mean_value.txt
|
||
# 应该是: "mean1 mean2 mean3 std"
|
||
|
||
# 2. 检查 _inputmeta.yml
|
||
cat *_inputmeta.yml | grep -A 10 "preprocess:"
|
||
|
||
# 3. 确认 add_preproc_node 设置
|
||
grep "add_preproc_node" *_inputmeta.yml
|
||
```
|
||
|
||
---
|
||
|
||
## 相关文档
|
||
|
||
- [API 参考](netrans_api.md) - Python API 详细说明
|
||
- [CLI 参考](netrans_cli.md) - 命令行工具参考
|
||
- [版本记录](release.md) - 版本发布记录
|