如何用知识蒸馏与 ONNX 压缩加速文本模型
BERT-base 级别的文本模型在 CPU 上单条推理几十毫秒,QPS 一上来机器成本直线上涨。压缩加速三板斧:蒸馏把知识搬进小模型、ONNX 把计算图优化掉、量化把位宽压下来——三者叠加常见 5~10 倍提速而指标只掉零点几个点。本文按顺序走通并给出回归方法。
graph LR
A[Teacher: BERT-base] -->|soft logits| B[Student: 4层小模型]
B --> C[导出 ONNX 计算图]
C --> D[INT8 动态量化]
D --> E{延迟与指标回归}
E -- 达标 --> F[替换线上推理]
E -- 不达标 --> G[回退 FP32 ONNX 或加大 Student]
前置准备
以文本分类任务为例,准备已训好的 teacher 与无标签蒸馏数据:
pip install transformers torch onnxruntime datasets |
- 蒸馏数据:线上真实请求日志脱敏 5~10 万条(比训练集分布更贴近服役分布)
- Student 结构:与 teacher 同族但层数减半(如 4 层 hidden 384)
步骤一:蒸馏训练
student 同时拟合硬标签与 teacher 的 soft logits(暗知识在 soft 分布里):
import torch.nn.functional as F |
- 先在小验证集确认 student 硬标签基线,再开始蒸馏
- 温度 T 从 2~4 试起;teacher 越强、数据越贴近线上,蒸馏收益越大
步骤二:导出 ONNX
把 student 导出为静态计算图,交给 ONNX Runtime 做算子融合优化:
python -m transformers.onnx --model=./student_distilled \ |
import onnxruntime as ort |
导出后用同一批输入对比 PyTorch 与 ORT 的输出 logits,最大绝对差应 < 1e-4。
步骤三:INT8 动态量化
CPU 部署场景下动态量化收益最大,一行配置:
from onnxruntime.quantization import quantize_dynamic, QuantType |
- 动态量化:权重 INT8、激活运行时量化,无需校准集
- GPU 部署则考虑 ORT 的 TensorRT EP 或静态量化(需校准数据)
步骤四:延迟与指标双回归
建一张对比表,四个配置同机同批数据测量:
import time |
| 配置 | ms/条(CPU) | F1 |
|---|---|---|
| PyTorch teacher | ~45 | 0.912 |
| ORT student fp32 | ~9 | 0.905 |
| ORT student int8 | ~5 | 0.901 |
门禁线:F1 掉点 ≤0.01 且 p99 延迟达标才允许替换线上模型。
常见问题
- 蒸馏后掉点超预期:蒸馏数据分布偏了(拿训练集蒸馏=二次过拟合),换线上日志数据
- ONNX 导出失败:自定义 head 含不支持算子——改写为等价基础算子或升级 opset
- 量化后个别类别崩坏:INT8 对离群权重敏感——该层回退 FP32(ORT 支持按节点排除)
- 线程数影响大:CPU 推理设置
sess.set_intra_op_num_threads(物理核数),超线程反而降速
小结
文本模型压缩是一条有序流水线:蒸馏降模型规模(收益最大)、ONNX 降图开销、量化降位宽,每一步之后都做”延迟 + 指标”双回归,掉点超门禁就停在该步回退。最终拿到的不只是一个快模型,而是一张可复用的”配置-延迟-指标”对照表——下次换模型时,它就是容量规划的依据。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 非鱼小站!
评论
WalineDisqus








