INT8 量化推理
本章讲在 RK182X NPU 上配置 INT8 量化推理的完整流程:校准数据集准备、量化精度验证、混合精度策略、性能对比。
整体框图
训练框架模型(FP32 / FP16)
↓
准备校准集(50-200 张图片)
↓
RKNN Toolkit INT8 配置 + build
↓
板端 INT8 推理(C API / Python API)
↓
精度验证(accuracy_analysis)1. INT8 量化加速原理
NPU 的 INT8 乘加单元(MAC)面积和功耗远低于 FP16。RK182X 的 NPU 架构对 INT8 做了硬件级加速。
| 精度 | 相对速度 | 内存占用 | 适用场景 |
|---|---|---|---|
| FP32 | 1x | 100% | 训练、高精度推理 |
| FP16 | 2x | 50% | 通用推理 |
| INT8 | 3-4x | 25% | 量产部署首选 |
| INT4 | 6x | 12.5% | 极端压缩场景 |
量化公式:
Q = round(F / S) F ≈ Q × S其中 S(scale)通过校准数据集统计每层激活值的 min/max 确定。
2. INT8 量化完整流程
四步:Step1 准备校准集(50-200 张图片)→ Step2 RKNN Toolkit 配置 INT8 + build → Step3 板端 INT8 推理 → Step4 精度验证。
2.1 Step 1:准备校准数据集
import os, random
image_dir = '/path/to/dataset/train/images'
all_images = [os.path.join(image_dir, f) for f in os.listdir(image_dir)
if f.endswith(('.jpg', '.png'))]
random.seed(42)
calib_images = random.sample(all_images, 100)
with open('calib_list.txt', 'w') as f:
for img in calib_images:
f.write(img + '\n')
print(f'Calibration set: {len(calib_images)} images')2.2 Step 2:执行量化转换
from rknn.api import RKNN
rknn = RKNN(verbose=True)
# 实际合法值(rknn.py:177-178):
# target_platform = rv1103 / rv1103b / rv1106 / rv1106b / rk2118 /
# rk3562 / rk3566 / rk3568 / rk3576 / rk3588 / rk1820
# 实际合法值(rknn.py:173):
# quantized_dtype = w8a8 / w4a16(默认 w16a16)
rknn.config(
target_platform='rk1820',
mean_values=[[123.675, 116.28, 103.53]],
std_values=[[58.395, 57.12, 57.375]],
quantized_dtype='w8a8',
)
rknn.load_onnx('model.onnx')
# 实际签名 (rknn.py:291): build(do_quantization, dataset, rknn_batch_size, auto_hybrid)
rknn.build(
do_quantization=True,
dataset='calib_list.txt',
)
rknn.export_rknn('model_int8.rknn')
# 实际签名 (rknn.py:417): accuracy_analysis(inputs, output_dir, core_mask, target, device_id)
rknn.accuracy_analysis(
inputs='./test_set/',
output_dir='./quant_report/',
)
rknn.release()2.3 Step 3:板端 INT8 推理
#include <rknn3_api.h> /* 实际头文件不是 rknn_api.h */
rknn3_context ctx = 0;
rknn3_init_extend init_extend = {0};
int ret = rknn3_init(&ctx, &init_extend);
if (ret != RKNN3_SUCCESS) return -1;
/* rknn3_api.h:328 rknn3_tensor_attr */
rknn3_tensor_attr input_attr = {0};
input_attr.index = 0;
rknn3_query(ctx, RKNN3_QUERY_INPUT_ATTR, &input_attr, sizeof(input_attr));
rknn3_tensor_attr output_attr = {0};
output_attr.index = 0;
rknn3_query(ctx, RKNN3_QUERY_OUTPUT_ATTR, &output_attr, sizeof(output_attr));
/* INT8 量化模型的输入 / 输出张量 dtype 由 rknn3_tensor_attr.dtype 给出,
* 引用 rknn3_api.h:114 的 rknn3_tensor_type 枚举(UINT8 / INT8 / FLOAT16 / ...) */3. API 验证
头文件路径:
find /userdata/RK1820_RK1828_AI_SDK -name "rknn3_api.h" -type f输出:
/userdata/RK1820_RK1828_AI_SDK/rknn/rknn3-runtime/rknn3-api/include/rknn3_api.h
/userdata/RK1820_RK1828_AI_SDK/rknn/rknn3-model-zoo/3rdparty/rknpu3/include/rknn3_api.h头文件路径确认:
rknn3_api.h(不是rknn_api.h),主路径在rknn3-runtime/rknn3-api/include/。
rknn3_tensor_type 枚举定义:
sed -n '114,128p' /userdata/RK1820_RK1828_AI_SDK/rknn/rknn3-runtime/rknn3-api/include/rknn3_api.h输出:
typedef enum _rknn3_tensor_type
{
RKNN3_TENSOR_FLOAT32 = 0, /** < data type is float32. */
RKNN3_TENSOR_FLOAT16, /** < data type is float16. */
RKNN3_TENSOR_INT8, /** < data type is int8. */
RKNN3_TENSOR_UINT8, /** < data type is uint8. */
RKNN3_TENSOR_INT16, /** < data type is int16. */
RKNN3_TENSOR_UINT16, /** < data type is uint16. */
RKNN3_TENSOR_INT32, /** < data type is int32. */
RKNN3_TENSOR_UINT32, /** < data type is uint32. */
RKNN3_TENSOR_INT64, /** < data type is int64. */
RKNN3_TENSOR_UINT64, /** < data type is uint64. */
RKNN3_TENSOR_BOOL, /** < data type is boolean. */
RKNN3_TENSOR_INT4,
RKNN3_TENSOR_TYPE_MAX
} rknn3_tensor_type;rknn3_tensor_attr 结构定义:
sed -n '328,340p' /userdata/RK1820_RK1828_AI_SDK/rknn/rknn3-runtime/rknn3-api/include/rknn3_api.h输出:
typedef struct _rknn3_tensor_attr
{
uint32_t index; /** < input parameter, the index of input/output tensor,
need set before call rknn3_query. */
char name[RKNN3_MAX_NAME_LEN]; /** < the name of tensor. */
uint32_t n_dims; /** < the number of dimensions. */
uint32_t shape[RKNN3_MAX_DIMS]; /** < the valid dimensions array. */RKNN3_QUERY 常量:
grep -n "RKNN3_QUERY_INPUT_ATTR\|RKNN3_QUERY_OUTPUT_ATTR\|RKNN3_SUCCESS" /userdata/RK1820_RK1828_AI_SDK/rknn/rknn3-runtime/rknn3-api/include/rknn3_api.h | head -5输出:
28:#define RKNN3_SUCCESS 0 /** < execute succeed. */
81: RKNN3_QUERY_INPUT_ATTR = 1, /** < query the attribute of input tensor. */
82: RKNN3_QUERY_OUTPUT_ATTR = 2, /** < query the attribute of output tensor. */
326: * @brief The information for RKNN3_QUERY_INPUT_ATTR / RKNN3_QUERY_OUTPUT_ATTR.
1259: * @return Return RKNN3_SUCCESS on success, return error code on failure4. 混合精度策略
当 INT8 全量化精度不达标时,使用混合精度:敏感层保持 FP16,其余层 INT8。
RKNN3 工具链提供的开关(rknn.api.rknn:127 RKNN.config):
grep -n "auto_hybrid" /tmp/rknn/api/rknn.py输出:
146: auto_hybrid_cos_thresh=0.98,
147: auto_hybrid_euc_thresh=None,
192: :param auto_hybrid_cos_thresh: The thresholds of cosine distance in auto hybrid when model is quantizate. default is 0.98
193: :param auto_hybrid_euc_thresh: The thresholds of euclidean distance in auto hybrid when model is quantizate. default is None
291: def build(self, do_quantization=True, dataset=None, rknn_batch_size=None, auto_hybrid=False):
297: :param auto_hybrid: Whether to enable automatic hybrid quantization to adjust accuracy or overflow. default is False.rknn.config(
target_platform='rk1820',
quantized_dtype='w8a8',
# 量化算法 (rknn.py:174):normal / mmse / kl_divergence / gdq
quantized_algorithm='mmse',
# 量化方法 (rknn.py:175):layer / channel / group{32..256}
quantized_method='channel',
# 混合精度阈值 (rknn.py:146-147)
auto_hybrid_cos_thresh=0.98,
auto_hybrid_euc_thresh=None,
)
# build() 里的 auto_hybrid 是真正的自动混合精度开关 (rknn.py:291)
rknn.build(
do_quantization=True,
dataset='calib_list.txt',
auto_hybrid=True, # 工具链自动识别混合精度
)4.1 敏感层识别
rknn.accuracy_analysis(
inputs='./test_set/',
output_dir='./layer_wise_report/',
)5. 常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| INT8 全量化精度掉 | 敏感层被量化 | 改 quantized_method='channel' + auto_hybrid=True |
| Top-1 下降 >1% | 校准集覆盖不足 | 扩到 100+ 样本 |
quantized_dtype='w4a8' 报不支持 | RKNN3 不支持该组合 | 改成 'w8a8' 或 'w4a16' |
accuracy_analysis 找不到层 | 测试集路径错 | 用绝对路径 |
auto_hybrid=True 报 OOM | 模型太大 | 减 rknn_batch_size=1 |
