onnx之fp16转换,int8量化
FP16 vs INT8
一、概述与本质区别
1.1 核心差异对比表
| 维度 | FP16 转换 | INT8 量化 |
|---|---|---|
| 本质 | 精度转换(浮点→浮点) | 数据压缩(浮点→整数) |
| 是否需要校准 | ❌ 不需要 | ✅ 必须需要 |
| 是否需要缩放因子 | ❌ 不需要 | ✅ 需要(scale + zero_point) |
| 数据范围 | ~6e-8 到 65504 | -128 到 127 或 0 到 255 |
| 精度损失 | 小(0.1-1%) | 中等(1-3%) |
| 模型大小压缩比 | 2x | 4x |
| 推理速度提升 | 1.5-2x | 2-4x |
| 硬件要求 | 支持FP16的GPU | 支持INT8的NPU/GPU |
二、FP16 转换完整过程
数学原理
FP16 转换的本质是IEEE 754 标准的精度降级:
FP32 格式(32位): ┌───────┬─────────┬────────────────────┐ │ 符号位 │ 指数位 │ 尾数位 │ │ 1位 │ 8位 │ 23位 │ └───────┴─────────┴────────────────────┘ FP16 格式(16位): ┌───────┬─────────┬────────────────────┐ │ 符号位 │ 指数位 │ 尾数位 │ │ 1位 │ 5位 │ 10位 │ └───────┴─────────┴────────────────────┘ 转换公式: FP16 = round(FP32) 到最近的 FP16 可表示值
三、INT8 量化完整过程
数学原理
INT8 量化需要将浮点数映射到整数空间:
量化公式: q = round((x / scale) + zero_point) 反量化公式: x_approx = (q - zero_point) * scale 其中: - x: FP32 值 - q: INT8 量化值(-128 到 127 或 0 到 255) - scale: 缩放因子(浮点数) - zero_point: 零点(整数,对应 FP32 的 0)
四、FP16 vs INT8 关键差异对比
# FP16 转换数据流(简单直接) FP32 → 位转换 → FP16 (无中间步骤) # INT8 量化数据流(需要校准) FP32 → [校准阶段] → 确定 scale/zero_point → 量化 → INT8 ↓ 收集统计信息 ↓ 计算最优参数 ↓ KL散度优化
五、选择指南
5.1 何时选择 FP16
def choose_fp16(): """FP16 适用场景""" conditions = [ "✓ GPU 支持 FP16 计算", "✓ 需要保持高精度(1% 以内精度损失)", "✓ 不需要校准数据", "✓ 快速实现(一行代码)", "✓ 模型较小或对速度要求不是极致" ] for cond in conditions: print(cond) print("\n推荐场景: 计算机视觉分类、检测等,追求精度优先")5.2 何时选择 INT8
def choose_int8(): """INT8 适用场景""" conditions = [ "✓ 需要极致压缩(4x 压缩比)", "✓ 有代表性校准数据", "✓ 可以接受 1-3% 精度损失", "✓ 部署到 NPU 或支持 INT8 的硬件", "✓ 需要最大推理速度" ] for cond in conditions: print(cond) print("\n推荐场景: 移动端部署、边缘设备、实时推理")六、总结
| 特性 | FP16 转换 | INT8 量化 |
|---|---|---|
| 转换方式 | 直接位转换 | 量化映射 |
| 校准需求 | 无 | 必须有 |
| 数据依赖 | 无 | 依赖校准数据分布 |
| 参数优化 | 无 | scale, zero_point, 截断阈值 |
| 算法复杂度 | O(1) | O(n) 需要遍历数据 |
| 精度损失 | 位宽截断 | 量化 + 截断 |
| 压缩比 | 2x | 4x |
| 速度提升 | 1.5-2x | 2-4x |
| 实现难度 | 极低 | 中等 |
一句话总结:FP16 是无校准的精度降级,INT8 是需要校准数据的压缩量化。FP16 像把照片从高清转为标清,INT8 像把照片转为黑白像素画。
