恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
ncnn 后训练 int8 量化工具链实战:ncnn2table / ncnn2int8 / ncnnllm2table 完整指南
首页
资讯中心
/
ncnn 后训练 int8 量化工具链实战:ncnn2table / ncnn2int8 / ncnnllm2table 完整指南
ncnn 后训练 int8 量化工具链实战:ncnn2table / ncnn2int8 / ncnnllm2table 完整指南
发布时间:2026/9/20 5:35:01
ncnn 后训练 int8 量化工具链实战ncnn2table / ncnn2int8 / ncnnllm2table 完整指南【免费下载链接】ncnnncnn is a high-performance neural network inference framework optimized for the mobile platform项目地址: https://gitcode.com/gh_mirrors/nc/ncnn导读本文围绕 ncnn 官方提供的后训练量化Post Training Quantization, PTQ工具链展开系统讲解如何把 float32 模型转换为可在移动端低延迟部署的 int8 模型。文章以 tools/quantize/README.md 及其指向的 docs/how-to-use-and-FAQ/quantized-int8-inference.md 为骨架完整覆盖模型优化、校准表生成图片 / npy 两种数据源、int8 转换、LLM 分块量化ncnnllm2table / ncnnllm2int、int8 推理接入与混合精度控制等全部步骤并结合tools/quantize/下各工具的源码实现深入解释每个命令行参数在底层是如何被解析和使用的。读完本文你将掌握从 float32 模型到可部署 int8 模型的完整实战流程并理解校准算法KL / ACIQ / EQ与 block 量化的底层原理。一、工具链总览与构建前提1.1 工具组成ncnn 的量化工具位于 tools/quantize/包含以下可执行程序工具作用输入输出ncnnoptimize模型优化flag 指定存储类型float32 param/bin优化后的 param/binncnn2table生成 int8 校准表优化后模型 校准数据.table校准表ncnn2int8按校准表转换权重优化后模型 校准表int8 param/binncnnllm2tableLLM 模型 block 量化校准表模型 可选校准数据.llm.tablencnnllm2intLLM 模型 block 量化转换模型 校准表int4/int6/int8 param/bin1.2 构建前提CMake 开关从 tools/quantize/CMakeLists.txt 可以确认各工具的编译条件ncnn2table与ncnn2int8依赖NCNN_INT8开关其中ncnn2table额外依赖NCNN_PIXEL用于图像读取且其图像解码有三种实现路径系统 OpenCVfind_package(OpenCV ...)、内置NCNN_SIMPLEOCV、或本地 imreadwrite.cppUSE_LOCAL_IMREADWRITE。也就是说即使没有安装 OpenCV只要开启NCNN_SIMPLEOCV也能编译出完整的ncnn2table。ncnnllm2table与ncnnllm2int依赖NCNN_WEIGHT_QUANT开关。工具通过ncnn_install_tool()安装并归入tools/optimization虚拟工程组。构建示例在 ncnn 源码根目录mkdir build cd build cmake -DNCNN_INT8ON -DNCNN_PIXELON -DNCNN_WEIGHT_QUANTON -DNCNN_SIMPLEOCVON .. make -j$(nproc)二、三步完成 CNN 模型的 int8 量化官方文档以 mobilenet 为例给出了完整的“三步走”流程。下面逐步展开并结合源码解释每一步的内部行为。2.1 第一步优化模型注意如果模型是通过 pnnx 转换得到的请跳过此步。./ncnnoptimize mobilenet.param mobilenet.bin mobilenet-opt.param mobilenet-opt.bin 0ncnnoptimize的完整用法为ncnnoptimize [inparam] [inbin] [outparam] [outbin] [flag] [cutstart] [cutend]其中flag决定存储类型。从 tools/ncnnoptimize.cpp 的源码可以看出flag 65536或flag 1storage_type 1fp16 存储flag 2storage_type 2其他取值含示例中的0storage_type 0即 float32 存储是后训练量化推荐的前置选项。优化过程会做算子融合、常量折叠等图优化得到更利于量化的计算图。使用cutstart/cutend还可以截取子图。2.2 第二步生成校准表calibration table2.2.1 基于图片数据校准推荐官方建议使用验证集verification dataset作为校准数据数量建议超过 5000 张图片以保证激活值分布统计的充分性。先生成图片列表再运行ncnn2tablefind images/ -type f imagelist.txt ./ncnn2table mobilenet-opt.param mobilenet-opt.bin imagelist.txt mobilenet.table mean[104,117,123] norm[0.017,0.017,0.017] shape[224,224,3] pixelBGR thread8 methodkl各keyvalue参数的含义与源码中main()的参数解析逻辑一一对应见 tools/quantize/ncnn2table.cpp参数说明对应源码mean均值即训练时传给Mat::substract_mean_normalize()的值parse_comma_float_array_listnorm归一化系数同样是substract_mean_normalize()使用的值parse_comma_float_array_listshape模型输入 blob 形状[w,h]或[w,h,c]parse_comma_int_array_listpixel像素格式支持RAW/RGB/BGR/GRAY/RGBA/BGRA图像会先转换到该格式再喂给Extractor::input()parse_comma_pixel_type_list源码中 RAW 对应 -233其余对应ncnn::Mat::PIXEL_*常量thread并行推理使用的 CPU 线程数默认取ncnn::get_cpu_count()QuantNet构造函数method后训练量化算法支持kl、aciq源码中还实现了eqquantize_KL() / quantize_ACIQ() / quantize_EQ()type输入数据类型0表示图片1表示 npynet.file_typeshape的缩放规则与 read_and_resize_image 的实现一致若 w 和 h 都为正数图像被精确缩放到该尺寸若 w 和 h 都为 0 或负数图像不缩放直接按原图送入若仅 h 为 0 或负数以 w 为基准等比缩放保持宽高比若仅 w 为 0 或负数以 h 为基准等比缩放。2.2.2 多输入模型如果模型有多个输入节点可以用逗号分隔多个 list 文件及多组参数例如./ncnn2table mobilenet-opt.param mobilenet-opt.bin imagelist-bgr.txt,imagelist-depth.txt mobilenet.table mean[104,117,123],[128] norm[0.017,0.017,0.017],[0.0078125] shape[224,224,3],[224,224,1] pixelBGR,GRAY thread8 methodkl注意分组数量必须与模型 Input 层数量一致。从源码的 sanity checkncnn2table.cpp可以看到工具会严格校验listspaths.size()、means.size()、norms.size()、shapes.size()、type_to_pixels.size()是否等于输入 blob 数量不匹配会直接报错退出。2.2.3 基于 npy 数据校准对于非图像模型如文本、语音特征建议使用验证集/开发集。预处理方式需与训练时完全一致以batchsize1为例每个输入向量单独存为一个 npy 文件存储时去掉 batch 维度有 n 个输入就对应 n 个 npy 文件。测试网络示例形状为 NCHW 描述但没有N维in0, shape[512] in1, shape[2, 1, 64] in2, shape[2, 1, 64]对应的三个 list 文件# filelist_in0.txt 0_in0.npy 1_in0.npy 2_in0.npy ...# filelist_in1.txt 0_in1.npy 1_in1.npy 2_in1.npy ...# filelist_in2.txt 0_in2.npy 1_in2.npy 2_in2.npy ...运行./ncnn2table test.param test.bin filelist_in0.txt,filelist_in1.txt,filelist_in2.txt test.table shape[512],[64,1,2],[64,1,2] thread8 methodkl type1这里shape的维度顺序是 WHC即[w,h,c]与ncnn::Mat的构造函数参数顺序一致。源码 read_npy 会逐维度校验 npy 文件的形状npy 文件内存储维度顺序与命令行 shape 相反代码用npy_shape[dims - 1 - i]与shape[i]比较并支持 1~4 维输入超过 4 维会报dims:%d illegal!并退出。2.2.4 无校准数据仅权重静态量化对于 RNN、GRU、LSTM、MultiHeadAttention 和 Embed 这类层ncnn2table可以不提供校准数据集仅根据权重的绝对值最大值生成静态权重 scale./ncnn2table rnn.param rnn.bin rnn.table methodkl对应源码 initialize_static_weight_scales对Embed逐元素求absmax对MultiHeadAttention分别计算 q/k/v/out 四组权重的逐输出通道absmax对 RNN/LSTM/GRU 计算weight_xc与weight_hc两组 scale。每个 scale 的计算公式均为127 / absmaxabsmax 0时取 1 兜底。另外从 main() 可以看到若模型含 Convolution 等层但没有提供校准数据工具会打印警告并退化为“仅权重”校准表跳过激活校准。2.3 第三步转换为 int8 模型./ncnn2int8 mobilenet-opt.param mobilenet-opt.bin mobilenet-int8.param mobilenet-int8.bin mobilenet.tablencnn2int8的调用形式为ncnn2int8 [inparam] [inbin] [outparam] [outbin] [int8scale_table]table 是可选的ncnn2int8.cpp 中argc 6 ? argv[5] : NULL。ncnn2int8通过 read_int8scale_table 读取校准表将其中的权重 scalelayer_param_0 ...与激活 blob scalelayer ...分别装入weight_int8scale_table与blob_int8scale_table随后check_int8scale_table_requirement会逐个检查可量化层若需要静态权重量化而表中缺少对应项会提示“run ncnn2table to generate weight scales and pass the table to ncnn2int8”。2.4 校准表文件格式ncnn2table生成的 table 是纯文本内容包含两类行见 save_table权重 scale 行层名_param_0 w0 w1 ...激活blobscale 行层名 s0 s1 ...。对于MultiHeadAttention会输出_param_0q、_param_1k、_param_2v、_param_3out四行RNN/LSTM/GRU 输出_param_0weight_xc与_param_1weight_hc两行Embed 输出_param_0一行。生成成功后工具会打印ncnn int8 calibration table create success, best wish for your int8 inference has a low accuracy loss...。同时print_quant_info会打印每个量化层的max / threshold / scale统计信息scale 为127 / threshold。三、三种校准算法KL、ACIQ 与 EQ文档明确支持kl与aciq两种方法源码中三种方法均已实现ncnn2table.cpp方法全称特点klKL 散度熵校准统计激活值直方图通过最小化 float32 与 int8 分布的 KL 散度选取阈值。源码中QuantBlobStat保存histogramstd::vectoruint64_t与histogram_normed与文档提到的“entropy calibration”实现相对应aciqAnalytical Clipping for Integer Quantization解析式方法基于量化误差理论直接推导最优截断点无需反复迭代。QuantBlobStat中的total字段即为 ACIQ 统计所用eq均方误差MSE法源码中quantize_EQ()会先调用quantize_KL()ncnn2table.cpp可理解为在 KL 基础上再做误差精修命令行默认methodkl传入未知方法时工具会打印unknown method %s, expect kl / aciq / eq并返回错误。四、LLM 分块量化Gemm 与 MultiHeadAttention针对 LLM 场景ncnn 提供了一套独立于上述 PTQ int8 流程的分块量化block quantization方案支持 4-bit / 6-bit / 8-bit 三种位宽。4.1 三种位宽的执行模式4-bit 与 6-bitweight-only权重按有符号 int4/int6 分块存储激活与输出保持 fp32是纯权重量化8-bitCPU 动态 W8A8 逐块常量权重存为有符号 int8每次前向时把每行/每块的 fp32 激活动态量化为 int8块内以 int32 累加 int8 点积结果在每个块边界施加激活与权重的反量化descale最终输出 fp328-bit 模式没有 W8A32 兼容路径。4.2 标准工作流与 ncnn2table / ncnn2int8 流程一致./ncnnllm2table in.param in.bin model.llm.table methodminmax bits6 block64 ./ncnnllm2int in.param in.bin out.param out.bin model.llm.table参数说明methodminmax、mseclip、awq、gptqbits4、6、8。bits4与bits6走 weight-only 执行bits8走 CPU 动态 W8A8 逐块执行block32、64、128threadCPU 线程数。4.3 awq 与 gptq 需要校准数据awq与gptq方法与ncnn2table的 npy 校准方式一致需要提供校准数据./ncnnllm2table in.param in.bin calib.list awq.llm.table methodawq bits4 block64 type1 shape[...] ./ncnnllm2int in.param in.bin awq.param awq.bin awq.llm.table./ncnnllm2table in.param in.bin calib.list gptq.llm.table methodgptq bits4 block128 type1 shape[...] ./ncnnllm2int in.param in.bin gptq.param gptq.bin gptq.llm.table校准列表格式与ncnn2table相同。4.4 生成的校准表格式以mseclip方法为例table 中每行对应一个量化层的权重 scalegemm_name_param_1 bits4 block64 methodmseclip scale0 scale1 ... mha_name_param_0 bits4 block64 methodmseclip scale0 scale1 ... mha_name_param_1 bits4 block64 methodmseclip scale0 scale1 ... mha_name_param_2 bits4 block64 methodmseclip scale0 scale1 ... mha_name_param_3 bits4 block64 methodmseclip scale0 scale1 ...对于MultiHeadAttention_param_0/_param_1/_param_2/_param_3依次对应 q/k/v/out 四组权重。awq方法额外会生成输入 scale 行gemm_name_param_1_input_scale methodawq scale0 scale1 ... mha_name_param_0_input_scale methodawq scale0 scale1 ...gptq方法使用固定对称分块 scale 与标准 GPTQ 误差补偿会写出打包的 qweight 文件并在 table 中记录gemm_name_param_1 bits4 block128 methodgptq qweightgemm.qweight scale0 scale1 ...4.5 表的可编辑性与约束table 是纯文本转换前可以直接编辑。约束规则缺失的Gemm行会被跳过MultiHeadAttention的 q/k/v/out 四行必须同时存在未使用的行会被拒绝至少需要量化一个层。4.6 quantize_term 编码规则生成的层quantize_term编码为bits * 100 input_scale * 10 block_code其中block_code的取值0/1/2分别对应block 32/64/128。4.7 快速转换免表直转如果不想保存 tablencnnllm2int也可以直接计算 scale 完成转换./ncnnllm2int in.param in.bin out.param out.bin methodminmax bits6 block64该格式采用**有符号对称、仅 scalesigned symmetric scale-only**的量化方式不使用 zero point。五、在代码中启用 int8 推理转换完成后int8 推理完全自动生效——业务代码无需任何改动。ncnn 库在加载 int8 param/bin 后会自动走 int8 计算路径ncnn::Net mobilenet; mobilenet.load_param(mobilenet-int8.param); mobilenet.load_model(mobilenet-int8.bin);从 ncnn2int8.cpp 的实现看转换阶段已把各量化层的权重替换为 int8 数据并写入校准表对应的 scale因此运行时不再需要额外的量化配置。六、混合精度推理mixed precision inference如果希望某个层保持 float32 计算只需在量化前注释掉校准表中该层的权重 scale 行。例如保留conv1层为 float32conv1_param_0 156.639840536改为#conv1_param_0 156.639840536被注释掉的行所对应的层在ncnn2int8转换时不会做权重量化从而在 int8 模型中保留 fp32 精度。这一机制特别适合在精度敏感层如检测头、关键输出分支上做“选择性量化”在精度与性能之间取平衡。七、质量验证与调优建议校准数据量图片校准建议使用 5000 张以上的验证集npy 校准建议使用验证集/开发集且预处理必须与训练完全一致method 选择默认kl在多数 CNN 上表现稳健若追求更低精度损失可尝试aciq或eq并通过ncnn2table打印的max / threshold / scale统计观察各层分布多输入检查多输入模型务必保证 list 文件、mean/norm/shape/pixel 的分组顺序与 Input 层顺序一致否则会被 sanity check 拒绝LLM 场景bits4/bits6是 weight-only适合显存/带宽受限场景bits8是动态 W8A8 逐块量化适合在 CPU 上追求更高精度的部署awq/gptq需要校准数据且精度通常更优精度排查若 int8 精度异常可优先检查校准数据与训练预处理是否一致mean/norm 是否与Mat::substract_mean_normalize()完全匹配再尝试用混合精度方式保留敏感层的 fp32 计算。八、参考链接关联文档入口tools/quantize/README.md完整量化指南docs/how-to-use-and-FAQ/quantized-int8-inference.md校准表生成工具源码tools/quantize/ncnn2table.cppint8 转换工具源码tools/quantize/ncnn2int8.cppLLM 分块量化工具tools/quantize/ncnnllm2table.cpp、tools/quantize/ncnnllm2int.cpp工具构建配置tools/quantize/CMakeLists.txt模型优化工具tools/ncnnoptimize.cpp【免费下载链接】ncnnncnn is a high-performance neural network inference framework optimized for the mobile platform项目地址: https://gitcode.com/gh_mirrors/nc/ncnn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考