【AI工程师精讲】13:微调排查:训练损失很好看,效果却不行

一个很典型的场景:

  1. 训练脚本跑完,损失从 2.1 降到 0.4,曲线很漂亮;
  2. 测试集上跑一遍------指标没有提升,甚至不如直接用提示词的效果;
  3. 换个说法再测,还是不行。

于是开始怀疑:是不是要换更大的秩?是不是要加更多数据?是不是 LoRA 本来就不行?

这些方向都可能对,但它们不是首要嫌疑。

实践中,微调"没效果"的原因高度集中在少数几个地方 ,而且前两个原因占了绝大多数。

这篇文章给出一份按命中概率排序的排查清单。建议按顺序走,别跳------因为跳着排查的人,会在错误的基础上反复试验。


第一部分:排查第一步------训练与推理的输入格式是否一致

这是最高频的原因,也是最少被检查的一项。

1.1 具体错在哪

看一个真实的对照。

你训练时的数据长这样:

text 复制代码
指令:把以下文本分类为正面或负面。
文本:这家店服务很好。
答案:正面

而推理时,你的程序发给模型的是:

text 复制代码
这句话是好评还是差评?"这家店服务很好"

这两个是形态完全不同的任务。

模型学的是 "看到『指令:...文本:...答案:』这种模式时输出标签";而你让它做的是一个开放问句。

它当然表现不好------它没学过这个格式。

1.2 怎么检查

关键动作 :把推理时真实发送的那段文本 (不是你以为的模板)打印出来,和训练数据里的一条样本并排放在一起,逐字符对比。

重点检查四项:

| 检查项 | 具体看什么 |
|--------------|-----------------------------|----------|----------|
| 角色标签与分隔符 | 是否有相同的标签、分隔符、换行方式 |
| 系统提示 | 训练时用了系统提示,推理时必须也用 |
| 输出起始方式 | 训练时答案前有"答案:",推理时要给同样的引导 |
| 特殊标记 | 有没有模型特有的 token(如 `< | im_start | >`)被漏掉 |

1.3 为什么这一条如此高频

因为它有一个结构性的成因:

  • 训练数据的构造代码,通常写在训练脚本里;
  • 推理时的提示词,通常写在业务服务里。

这两份代码往往由不同的人、在不同的时间写,而且没有任何机制保证它们一致。

这一条能解释的失败案例,比后面九条加起来还多。

1.4 一个五分钟的验证

如果你只能做一件事,就做这件事:

python 复制代码
# 打印推理时真实发送的完整文本
print(repr(messages))          # 用 repr 显示所有空白字符

# 打印训练数据里的一条样本
print(repr(train_samples[0]))

并排看这两段字符串。 五分钟就能发现问题。

用 repr 而不是直接 print 的原因 :它能暴露看不到的空白字符差异(多余的空格、不同的换行符)。而这些恰好是最常见的坑。


第二部分:排查第二步------数据本身有没有问题

数据是第二个高发区。四类问题按严重程度排列。

2.1 问题一:标签/答案有错

尤其常见于自动生成的数据。

模型会忠实地学会错误。

做法:随机抽 30 到 50 条人工过一遍,估算错误率。

超过 5% 就该重新清洗。

2.2 问题二:样本高度重复

大量近似相同的样本,会让模型记住模板而不是学会泛化。

做法:做一次近似去重,看样本的多样性分布。

怎么判断"太重复"? 一个简单的方法:抽样 50 条,看看有多少条是"换个说法但内容一样"的。如果超过一半,重复度就偏高。

2.3 问题三:格式不统一

有些样本用中文标点,有些用英文;有些输出带解释,有些只给结论。

模型面对矛盾信号,只能学出一个"平均版本"。

这个"平均版本"的表现就是 :有时候带解释,有时候不带------看起来像"不稳定",实际上是它忠实反映了你给的矛盾。

2.4 问题四:数据量与任务不匹配

任务越复杂,需要的数据越多。

但要注意:质量比数量重要得多------几百条高质量样本常常打败上万条脏数据。

2.5 一个有用的判断标准

症状 更可能的原因
"学得会但学不稳、时好时坏" 数据一致性问题
"完全学不会" 格式或任务定义问题

这个区分能帮你快速定位到"数据"还是"链路"。


第三部分:排查第三步------这个任务适合微调吗

数据没问题,就要问:这个问题是不是根本不该用微调解决?

回到那条判据:微调擅长改"表达形态",不擅长注"新知识"或提"推理上限"。

3.1 三个不适合微调的信号

信号一:模型答错是因为"不知道"。

比如问一个私有的产品参数。

这是知识问题,应该用检索,用微调就是让模型硬猜。

"硬猜"的具体表现 :模型会输出一个看起来合理但实际错误的参数值。这在生产环境里比"拒答"危险得多。

信号二:模型答错是因为"想不明白"。

比如需要多步推理的计算题,基座本身做不对。

微调改变不了能力上限。

信号三:需求是"每次都不一样"。

比如要求根据上下文灵活变通,而你的训练数据却在教一个固定套路。

这会降低灵活性。

这条比较隐蔽:你以为在"教它做好这件事",实际上是在"把它限制在一个模板里"。

3.2 一个很便宜的验证方法

手工构造 20 个测试样本,先验证"在训练数据里,模型是否 100% 学得会"。

观察 结论
连训练集都学不会 问题在数据或超参
训练集完美、验证集不行 泛化问题或任务不匹配

这个"训练集能不能拟合"的检查,是区分"管线问题"和"能力问题"的分水岭。

它的逻辑很简单:如果模型连它训练时见过的东西都学不会,那说明训练流程本身有问题;如果它学会了训练集但泛化不了,那是数据分布或任务定义的问题。


第四部分:排查第四步------超参是否合理

到了这一步,才轮到调参。

按影响从大到小:

4.1 学习率

情况 表现
太大 损失震荡不收敛、输出发散
太小 损失几乎不动、学不进去

LoRA 的学习率通常可以设得比全参大,但"大"是有上限的。

4.2 训练轮数

  • 太少:学不到位;
  • 太多:过拟合。

看损失曲线:验证损失回升就是过头的信号。

4.3 秩 r

情况 表现
太小 "损失降不下去",模型表达力不够
太大 容易过拟合

建议从小到大试,先 8 再到 16、32。

4.4 目标模块

只挂了注意力投影时效果不足,把前馈层加进来往往有明显提升------代价是参数量增加。

4.5 一个实用建议

用"训练集能否拟合"作为健全性检查。一个健康的配置应该能在训练集上把损失降到很低。

如果训练集都学不会,先别管泛化。

这句话的顺序很重要 :它把"能不能学"和"学得好不好"分成了两个独立的问题 。很多人直接跳到后者,但前者不成立时,后者的讨论都是空的。


第五部分:排查第五步------推理侧的配置是否同步

这一条非常隐蔽:训练对了,推理时却没配对。

5.1 四个检查点

检查点一:推理时是否加载了适配器。

听上去荒谬,但真实发生过------部署时忘了挂载。

具体表现 :模型表现得就像没微调过一样。而这一旦发生,你会花大量时间去怀疑训练本身。

检查点二:推理时的提示词是否还是旧版。

训练数据是按新提示词格式造的,但线上还是老提示词。

检查点三:是否残留了"引导串"。

训练数据里答案前有固定前缀,推理时也需要同样的前缀来触发,否则输出形态会不对。

举例 :如果训练数据都是 答案:正面,而推理时模型输出的第一句是 "这句话是正面的"------格式完全不对。 解决方法是在推理时也加上 答案: 作为引导。

检查点四:序列长度与截断策略。

如果推理时把输入截断了,模型看到的内容和训练分布不同。

5.2 排查方法

直接对比"训练样本"和"线上请求"的原始文本。这一步通常五分钟就能发现问题。

注意"原始文本"这四个字 :不要对比"你脑子里的模板",要对比实际发出去的字符串。


第六部分:一份十步排查清单

把上面的内容整理成可执行顺序,按命中概率排序。

步骤 检查项 命中概率 修复成本
1 训练与推理的输入格式是否一致(含系统提示) 极高 极低
2 推理时是否真的加载了适配器 高 极低
3 数据标签是否有错、是否重复、格式是否统一 高 低
4 任务是否适合微调(知识/推理类需求) 中高 低(改方案)
5 训练集能否被拟合(健全性检查) 中 低
6 学习率是否过大或过小 中 低
7 训练轮数是否不足或过多 中 低
8 秩 r 与目标模块是否够用 中 中(重训)
9 评测方法本身是否可靠(判分口径、样本量) 中 中
10 是否需要更强基座或拆分任务 低 高

6.1 为什么这个排序是这样的

排序依据是两个因素:

  1. 命中概率(有多常见);
  2. 修复成本(修起来有多麻烦)。

规律是:命中概率高的,修复成本都很低;修复成本高的,命中概率都很低。

这不是巧合 ------它反映了"高成本的错误通常会被及早发现":如果你花了大力气调超参,那你一定已经试过更简单的排查了。

6.2 一条硬性建议

前 5 步不解决,不要动第 8 步。

换超参之前先确认数据与格式没问题,否则你会在一个错误的基础上反复试验。

这句话对应一个很典型的时间浪费模式 :有人花了三天调各种 r 和学习率组合,最后发现是训练数据里多了一个空格。而这种浪费是可以避免的。


第七部分:训练前就该做的三件事

与其事后排查,不如事前设计。 这三件事能让排查成本降低一个数量级。

7.1 第一件:造一份"极小验证集"

20 到 50 条,人工确认答案正确。

每次改动都在这份集子上跑一遍。它不需要全面,只需要灵敏。

注意"灵敏"这个要求 :它不需要覆盖所有场景,但必须能在你改动有效时立刻反映出来。

为什么是 20~50 条? 因为:

  • 太少(如 5 条)→ 噪声大,看不出趋势;
  • 太多(如 500 条)→ 跑一次太慢,你会懒得跑。

20~50 条的量级,跑一遍只要几分钟,你会愿意每次都跑。

7.2 第二件:做一次"格式一致性检查"

写一个脚本,把训练样本的构造函数和推理时的提示词构造函数指向同一个来源。

让它们共用代码,就不会不一致。

这是根治第一步问题的唯一办法。

具体实现:

python 复制代码
# 关键:只有一个地方定义格式
def build_prompt(task_input, system_prompt=SYSTEM_PROMPT):
    return [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": f"指令:{task_input}\n答案:"}
    ]

# 训练数据构造时用它
train_samples = [build_prompt(x) for x in raw_train]

# 推理时也用同一个函数
messages = build_prompt(user_input)

共用代码的收益是结构性的 :你不必"记得检查一致性",因为不一致在结构上不可能发生。

7.3 第三件:先跑一个"过拟合实验"

用 50 条数据、较大学习率、多训几轮,看模型能不能把训练集背下来。

  • 能背下来 → 说明链路是通的,再扩展到全量数据做泛化;
  • 背不下来 → 问题在管线,不在能力。

这一步能区分"管线问题"和"能力问题"。

7.4 三件事的总成本

三件事加起来不到一天,但能省掉后面几天的瞎试。

这个投入产出比很划算 ------尤其是当你考虑到"瞎试"往往是按周计的。


常见误区

误区一:"效果不好就加大数据量。"

数据量是最后考虑的杠杆之一。

先确认格式、标签、任务适配性。

原因 :如果格式不一致,再加一万条数据也没用------训练集越大,模型学那个错误格式学得越牢。

误区二:"损失降得低就说明训得好。"

训练损失低只说明"拟合了训练集"。

要同时看验证损失和实际任务指标。

一个具体的反例 :训练损失 0.1(很低),验证损失 2.5(在上升)------这是典型的过拟合,但只看训练损失会误以为"训得很好"。

误区三:"微调没效果,说明要用全参。"

全参不会修复格式不一致或数据错误。

同样的错误在全参上通常表现得更彻底。

"更彻底"的意思是 :全参的容量更大,所以它能更精准地记住那个错误的格式。问题会被放大,而不是被修复。

误区四:"多训几轮总会有提升。"

训练轮数与效果不是单调关系。

超过最优点之后,每一轮都在加重过拟合与遗忘。

误区五:"评测结果不好,一定是模型的问题。"

评测方法本身也可能是问题:

  • 判分口径太严(格式差异被算作错误);
  • 样本量太小(噪声大,结论不可靠);
  • 评测集与训练集有重叠(虚高)。

所以第 9 步要检查评测本身 ------在你怀疑模型之前,先确认尺子是准的。

误区六:"训练集指标好就够了。"

训练集指标只是必要条件,不是充分条件。

它只能说明"链路通了",而不能说明"模型学会了泛化"。

真正该看的是验证集和业务指标。


实战问答

Q1:微调完全没有效果,第一步应该做什么?

A :打印推理时真实发送的文本,和一条训练样本并排比对。

python 复制代码
print(repr(actual_request_text))
print(repr(train_sample_text))

如果格式不一致,问题就找到了。

这一步五分钟,命中率最高。

注意用 repr------它能暴露不可见的空白字符差异。


Q2:怎么快速判断是"数据问题"还是"超参问题"?

A :做过拟合实验。

拿 50 条干净数据,用较大学习率多训几轮。

结果 结论
能拟合 链路是通的,问题在数据泛化或规模
不能拟合 问题在超参、秩或数据错误

这个实验的价值在于它是一次"二分" :把问题空间一刀切成两半,然后你就知道该往哪个方向查了。


Q3:训练集指标很好,但线上没变化,怎么办?

A :八成是推理侧配置问题。

依次确认:

  1. 适配器是否加载(最容易漏);
  2. 提示词是否与训练一致;
  3. 是否有必要的引导串;
  4. 输入是否被截断。

"训练集好但线上差"这个组合本身就是线索------它说明训练是有效的,问题出在从训练到上线的这一段。


Q4:怎么避免这类问题反复出现?

A :把一致性做成机制,而不是靠人记得检查。

具体两件事:

① 让训练与推理共用同一套提示词构造逻辑(同一份模板、同一个函数);

② 把"极小验证集"纳入训练脚本自动跑。

第二件事的意思是 :训练完成后自动跑一遍小验证集,如果指标没有改善,就直接报警------而不是等人去测。

这两件事都是"一次性投入、长期收益",而且它们的成本都不高。


Q5:训练数据的标签错了 5%,影响有多大?

A :影响比大多数人以为的大。

原因 :模型会忠实地学习你给的所有信号,包括错误的那些。它不是"从错误中过滤出正确规律",而是"在噪声中拟合出一个平均结果"。

具体表现:

错误率 典型表现
1%~2% 通常影响不大
5% 明显拉低效果上限,且会出现"时好时坏"
10%+ 可能完全学不出正确模式

所以"超过 5% 就该重新清洗"这条建议,是有依据的。

而且要注意 :自动生成的数据里,错误往往不是随机分布 的------它可能集中在某几类难例上,而这几类恰恰是你最关心的。


Q6:怎么判断该不该扩大训练数据?

A :先看当前数据能不能拟合。

text 复制代码
训练集学不会 → 不是数据量的问题,是格式/超参/数据质量问题
训练集学会了、验证集不行 → 可能是数据量不足或分布太单一
训练集好、验证集也好、业务指标不行 → 可能是评测或任务定义问题

这个顺序很重要 :在"训练集都学不会"的情况下加数据量,是最常见的浪费。

只有在"训练集能学会、但泛化不好"的情况下,加数据(尤其是加多样性)才是对症的。


Q7:我们的微调效果"有时候好有时候坏",怎么查?

A :"时好时坏"这个模式本身就是线索 ------ 它指向数据一致性问题,而不是格式问题。

排查顺序:

检查项 说明
样本格式是否统一 有的带解释、有的不带 → 模型学出"随机选择"
标点/空格是否统一 中英文标点混用
同一类输入的答案是否矛盾 同一问题两种答案 → 模型学到随机
是否有重复样本 重复样本会放大其影响

特别注意"矛盾样本" :如果同一个输入在数据里出现了两次、但答案不同,模型会学到"在这种情况下可以二选一" 。这会直接导致输出不稳定。

而这类问题在不做数据检查时很难发现,因为单看每条样本都是"对的"。


术语表

术语 含义
输入格式一致性 训练样本与推理请求在角色标签、分隔符、系统提示上完全对齐
过拟合实验 用小数据集验证训练链路是否通畅的健全性检查
引导串 训练时出现在答案前的固定前缀,推理时需同样提供以触发期望输出形态
极小验证集 20~50 条人工确认的样本,用于快速判断每次改动是否有效
拟合能力 模型在训练集上达到低损失的能力,用于区分链路问题与泛化问题
目标模块 LoRA 作用的层集合,决定改动范围与效果上限

结语

把这篇压缩成三句话:

第一,微调失败的排查顺序是:

text 复制代码
① 对比训练与推理的输入格式(命中率最高,修复成本最低)
② 检查数据质量与任务适配性
③ 最后才是超参

注意"最后才是超参" ------大多数人第一反应是调参,而调参排在第 6 到第 8 步。

第二,"训练集能不能拟合"是最有信息量的一个检查。 它一次就把问题空间切成两半:能拟合 → 查泛化;不能拟合 → 查管线。

第三,也是最重要的: 避免这一切的最好方式,是让训练和推理共用同一套提示词构造逻辑。

把一致性做成机制,而不是靠人记得检查。

而如果只允许带走一个动作,那就是:在怀疑模型之前,先打印出推理时真实发送的那段字符串,和训练样本并排看一遍。 五分钟,命中率最高。

相关推荐
晨间晓1 小时前
图片里的细小噪点怎么平滑?用华为云码道开发 OpenCV 图像滤波对比工具
人工智能·opencv·华为云
知寻智能1 小时前
把 AI 回答的来源列表拆成三层:引用位置、URL、域名
人工智能
Java内核笔记1 小时前
Spring AI 生产级 Chat 接口:6 个开关、13 个坑,其中 2 个开关根本没生效
java·人工智能·后端
AI原生B2B产品架构师1 小时前
两张表对不上:先查重复与缺项,再让AI解释异常
人工智能
猛犸象限1 小时前
【CJMP】用仓颉跨端做游戏(下):94 关能过、6 关卡住,问题最后出在我们自己这边
ai编程
程序员大狐狸2 小时前
Ultra X7 358H 笔记本电脑,本地三个大模型接入 DeepSeek Harness 桌面版的过程与方法
人工智能·llm
W***25922 小时前
深度解读Work Agent长程任务拆解与执行机制
人工智能
付威20232 小时前
Provider 不属于 Agent 核心:rpi 的分层理由与收益
人工智能