内容安全分类这个业务对模型的要求是矛盾的:判断力得是大模型的,服务形态得是小模型的——前者要看得懂隐晦表达和上下文,后者要在单卡上以 GB 级显存、几十毫秒的延迟全天候常驻。蒸馏是标准解法,但把教科书方案落到一台国产卡服务器上,会遇到一连串教科书不写的问题。这篇把一套在海光 DCU 上完整跑过的蒸馏训练实现拆成九个问答,每个答案附带上"为什么这么做"。
问 1:什么样的任务适合这套做法?
特征是"判断密集、生成无关":输出是一个判断加若干理由类别,不需要模型写一个字。这类任务用小模型(0.6B 量级)加两个分类头就能服务,但小模型的判断力上限不够,于是让大模型(32B 量级)当教师,把判断力灌进学生。训练时师生同机,上线时只部署学生。
问 2:蒸馏选在线还是离线?
在线——每个 batch 现场让教师前向一次,教师全程陪跑。理由有三。第一,省一遍全量推理:几万条数据过 32B 教师不便宜,在线方式教师只对学生实际用到的样本出软标签,教师算力全花在有用处的地方。第二,软标签不过期:教师本身在持续迭代,离线预生成的软标签存在版本对齐问题,在线的永远新鲜。第三,实现简单到一行 no_grad,不需要软标签的存储和版本管理。代价是教师必须常驻显存,这正是问 6 要解决的。
问 3:软标签怎么做,是教科书里的 KL 加温度吗?
不是,这里用概率匹配替代。教科书方案(softmax 分布上的 KL 散度加温度缩放)是为互斥多分类设计的;这套任务的分类头是 sigmoid 型的二元判断加多标签,每个 logit 独立过 sigmoid,不存在可对齐的分布。所以软标签直接取教师 logits 过 sigmoid 后的概率,用 BCE 让学生拟合这个概率。价值在于:教师输出"87% 像正样本"比"是正样本"多携带一层不确定度信息,这正是软标签存在的意义。总损失的权重比取 1:1 的监督项对 0.5 的蒸馏项——硬标签是人工校对的真值,软标签是教师的意见,意见降权,免得教师的错误判断压过真值。
问 4:负样本要不要算多标签损失?
不要。理由类别只在二元判断为正时才有意义("不是问题内容"的样本谈不上属于哪个类别)。多标签头的监督损失要用二元标签做掩码,只在正样本子集上计算。这个开关不打开,负样本的全零多标签会教学生"遇事输出全零",把多标签头带歪。类似的细节还有:序列表示用带掩码的 mean pooling(padding 位置置零再平均,分母做除零保护);加载基座用 AutoModel 而不是 AutoModelForCausalLM——分类任务不需要语言模型头,扔掉它,学生的参数里就没有那块最大的输出投影。
问 5:教师的输出一定会是干净的吗?
一定会坏,要按会坏来设计。教师是 bf16 精度的 32B 模型,前向输出偶发 NaN 或正负无穷。一个 NaN 进了 loss,梯度、优化器状态跟着全部污染,整个训练静默报废,而且通常要在几个 step 之后 loss 变 NaN 才被发现,当前权重不可恢复。防御三行代码:nan_to_num 把异常值换成有界值;logit 侧 clamp 到正负 30(sigmoid 在这个范围已经是概率极限,超出的数值没有信息量);概率侧再 clamp 到 [1e-4, 1-1e-4],防止 BCE 在 0/1 目标上算出爆掉的对数。学生侧做同样处理——学生自己也会抽风。普适规则:任何把另一个模型的输出当输入的损失,都必须假设那个输出会坏。
问 6:32B 的教师加 0.6B 的学生,一台服务器怎么摆?
这是 DCU 平台直接塑造设计的部分。两个约束:其一,DCU 上没有可用的 bitsandbytes,教师做不了 int8/int4 量化加载,只能 bf16 全精度常驻——32B 对应约 65GB 权重,单张 64GB 的卡放不下;其二,学生只有 1GB 出头。于是布局是:教师用 device_map="balanced" 摊到多张卡上,学生固定在 0 号卡,同进程共享数据加载器和训练循环,不需要任何跨进程通信。教师占几张卡的躯干,学生占一张卡的角落。另外一个兼容性设计:教师加载函数同时支持 LoRA 形态(带 adapter 目录的旧产物)和全量形态(顶层完整权重的新产物),按 adapter_config.json 是否存在自动分支——教师升级换代,学生蒸馏不能因此断档。
问 7:训练超参怎么定?
保守起点,全部留 CLI 覆盖:学习率 2e-5,cosine 衰减加 3% warmup;每卡 batch 8、梯度累积 4,有效 batch 32;epochs 只跑 2——小模型小数据,多了过拟合;精度 bf16(DCU 原生支持);梯度裁剪 0.5,配合问 5 的数值防御;文本截断 384 token,按业务文本的实际长度分布定;每 400 步评估并存 checkpoint,保留最近 3 个。
问 8:怎么判断训好了,看 loss 吗?
不看 loss,看 F1。分类任务在正负样本不均衡时 loss 可以很好看,但那不是业务语言。评估逻辑要自己扩展:二元头看 bin F1,多标签头看 macro/micro F1 加每类 precision 和 recall,阈值 0.5。最优 checkpoint 按这两个指标选,不按 eval loss。这条纪律和问 5 的数值防御是同一种思想:模型给你的默认指标,未必是你业务需要的指标。
问 9:训练产物怎么接上线上服务?
训练结束时保存三样东西:学生骨干(safetensors)、tokenizer、两个分类头的权重文件。这三样就是一个完整的可部署分类器,线上服务(FastAPI 单卡常驻)直接加载这个目录,零转换。训练产物和部署格式同构,是整条流水线能自动迭代的前提——每轮训练结束产出一个可直接替换的模型目录,服务重启即上新。工程上还有两个小习惯:输出目录带时间戳命名,同前缀的旧目录自动清理,免得失败实验把磁盘填满;支持从历史 checkpoint 恢复双头权重继续训。
最后压缩成三句话:软标签按任务的实际形状构造,教科书公式不照抄;数值防御当默认配置,三行代码买断"教师毒死学生"的事故;产物即服务的形状,流水线才能无人值守地转。至于"数据不出机房"——判断力的生产和服役在同一台国产卡服务器上完成,这在某些场景里比任何性能数字都值钱。