记录一下那天晚上的排查过程。1200 条样本每条都带 800 字 system 说明,训练 loss 降到 0.4 看着很漂亮,上线后回答开始复读 system 里的原句,比微调之前还生硬。

刚开始盯着「assistant 回答普遍很短,模型学到的是用 system 语气敷衍」,折腾了半个多小时,排掉了。

这类报错背后常见的原因有几处,逐个排除就行。长 system 在每条样本里权重固定,模型倾向于复制它的句式。也可能是system 内容每轮完全相同,模型把注意力放在记住它而不是学任务。

另外,学习率偏高,几个 epoch 之后过拟合到固定前缀。

第一件事是统计 system 与 assistant 的平均字符数

python -c "import json;ls=[json.loads(l) for l in open('train.jsonl')];print(sum(len(m['content']) for d in ls for m in d['messages'] if m['role']=='system')//len(ls))"

随后统计 assistant 单条平均长度做对照

python -c "import json;ls=[json.loads(l) for l in open('train.jsonl')];print(sum(len(m['content']) for d in ls for m in d['messages'] if m['role']=='assistant')//len(ls))"

然后把 system 压成一句话重建训练集

python -c "import json;print(json.dumps({'role':'system','content':'你是客服助手,只回答订单问题'},ensure_ascii=False))"

剩下的就是用同一批问题对比基础模型与微调模型

python eval_compare.py --base gpt-4o-mini --ft ft:gpt-4o-mini:myorg:abc123:xyz --questions holdout.jsonl

怎么算真的好了,看的是同一批现象。微调后在留出问题上复读 system 句子的比例下降到个位数,回答长度接近人工样本均值,验证集 loss 与训练 loss 差距不再扩大。

system 是背景不是训练目标,把规则塞进样本会让模型背规则,真正想让它学的样式应该体现在 assistant 那一侧。这类问题最好一次改到位,反复小改反而容易把原因搅混。

作者 9823189