BERT-base 级别的文本模型在 CPU 上单条推理几十毫秒,QPS 一上来机器成本直线上涨。压缩加速三板斧:蒸馏把知识搬进小模型、ONNX 把计算图优化掉、量化把位宽压下来——三者叠加常见 5~10 倍提速而指标只掉零点几个点。本文按顺序走通并给出回归方法。

前置准备

以文本分类任务为例,准备已训好的 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

T = 4.0 # 温度:越高 soft 分布信息越平滑
for batch in distill_loader:
with torch.no_grad():
t_logits = teacher(**batch).logits
s_logits = student(**batch).logits
loss_kd = F.kl_div(F.log_softmax(s_logits / T, dim=-1),
F.softmax(t_logits / T, dim=-1), reduction="batchmean") * T * T
loss_ce = F.cross_entropy(s_logits, batch["labels"])
(0.7 * loss_kd + 0.3 * loss_ce).backward()
opt.step(); opt.zero_grad()
  • 先在小验证集确认 student 硬标签基线,再开始蒸馏
  • 温度 T 从 2~4 试起;teacher 越强、数据越贴近线上,蒸馏收益越大

步骤二:导出 ONNX

把 student 导出为静态计算图,交给 ONNX Runtime 做算子融合优化:

python -m transformers.onnx --model=./student_distilled \
--feature=sequence-classification onnx/student.onnx
import onnxruntime as ort
sess = ort.InferenceSession("onnx/student.onnx", providers=["CPUExecutionProvider"])
out = sess.run(None, {"input_ids": ids, "attention_mask": mask})

导出后用同一批输入对比 PyTorch 与 ORT 的输出 logits,最大绝对差应 < 1e-4。

步骤三:INT8 动态量化

CPU 部署场景下动态量化收益最大,一行配置:

from onnxruntime.quantization import quantize_dynamic, QuantType
quantize_dynamic("onnx/student.onnx", "onnx/student_int8.onnx",
weight_type=QuantType.QUInt8)
  • 动态量化:权重 INT8、激活运行时量化,无需校准集
  • GPU 部署则考虑 ORT 的 TensorRT EP 或静态量化(需校准数据)

步骤四:延迟与指标双回归

建一张对比表,四个配置同机同批数据测量:

import time
def latency(sess_or_model, batches):
t0 = time.time()
for b in batches: predict(sess_or_model, b)
return round((time.time()-t0) / len(batches) * 1000, 2) # ms/条
配置 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 降图开销、量化降位宽,每一步之后都做”延迟 + 指标”双回归,掉点超门禁就停在该步回退。最终拿到的不只是一个快模型,而是一张可复用的”配置-延迟-指标”对照表——下次换模型时,它就是容量规划的依据。