让模型懂一个行业,有两条路:继续预训练(灌大量行业文本, 学说法)和指令微调(喂几千条问答,学任务)。两条路补的东西不一样,混着用才对。 这一节用字符级 n-gram 真做一次配比实验:行业语料从 0% 加到 100%, 看行业困惑度和通用困惑度怎么此消彼长。
上一节把模型立起来了,这一节改它。 第 3 章 3.3 节讲过全参微调与 LoRA 的账,这一节讲的是行业模型特有的那个问题: 灌了行业数据之后,通用能力掉多少、值不值。
一、两条路,补的东西不一样
继续预训练(领域预训练):拿海量无标注行业文本接着训。 它补的是「说法」——模型开始用行业里的词、行业里的句式。 指令微调:拿几千条问答对训。它补的是「任务」——模型知道被问什么该怎么答。 两条路的成本差两个数量级:领域预训练要几亿 token、几十张卡、几天; 指令微调几千条数据、一张卡、几小时。
所以顺序是先指令微调。 大部分行业应用,指令微调加上检索就够了,领域预训练是「说法实在不像」时才做的事。
二、配比:留多少通用语料
只用行业语料训,行业困惑度最低,通用困惑度最高——模型把通用语言忘了。 这就是灾难性遗忘。演示里行业占 100% 时行业困惑度 51.7、通用 93.7;行业占 80% 时是 55.6 和 81.4。 换算一下:20% 的通用语料,换来行业困惑度涨 3.9,通用困惑度降 12.3。这笔买卖很划算。
常见经验是行业语料占七到九成。这一两成通用语料不是浪费,它买的是「模型还记得怎么说话」。 更细的做法是分层配比:通用语料里再挑一部分「跟行业相关但不属于行业」的,比如通用科普、通用问答, 比纯文学语料更有效。
三、灾难性遗忘长什么样
在小模型上它表现为困惑度暴涨; 在真模型上它更隐蔽——不是「不会说人话了」,而是某些通用能力悄悄掉了:常识问答变差、指令跟随变差、 格式稳定性变差。 最容易被发现的是格式:微调之后回答开始不带标点、或者每条都强行套行业模板。 这是「通用指令跟随能力」被行业格式覆盖了。 对策有三条:混通用语料、降学习率、少训几轮。三条都是从「别让它改太多」出发的。
还有一条最实用:微调时把通用能力也放进评测集。不测就发现不了,发现了也已经上线了。
四、微调策略:LoRA 挂哪、学率多少、训几轮
行业微调一般先上 LoRA,秩 8 到 16,挂 q、k、v、o 四路。 理由是全参微调的成本高十倍,而行业微调通常不需要改太多。 学习率比全参高一个量级,万分之一到万分之二。轮数一到三轮,行业数据量小,训多了就背下来了。 数据量够大(几千条以上)再考虑加前馈三块。挂得越多参数越多,效果上限也越高。
有一个反直觉的经验: 要「注入知识」,LoRA 的秩要往上涨;要「改格式」,低秩就够。因为知识是分散的,格式是集中的。
五、知识进不去,是微调最常见的天花板
微调改不了模型不知道的事实。它只会让模型把不知道的东西说得更像真的——这叫幻觉,不叫学习。 判断标准很简单:这条知识在训练数据里出现过吗?出现过,微调有用;没出现过,微调没用。 要补新知识,用检索:把文档切块、建索引、问的时候捞三段进上下文。 改库即生效,不用重训,还能溯源。
所以行业模型的典型架构是三层:基座(会说人话)+ 微调(会说行业话)+ 检索(知道具体事实)。
六、动手 Demo:三种语料配比,看行业困惑度和通用困惑度怎么换
用字符级三阶 n-gram 当替身:它小、能真训、结论方向和大模型一致。 两份语料各八十句——一份日常词、一份行业词,句子都由术语拼成。 三种配比各训一个,在行业测试集和通用测试集上各算一次困惑度。 再把行业占比从 0% 扫到 100%,看两条曲线怎么交叉。
VOCAB = sorted(set(u''.join(COM_TERMS + IND_TERMS)))
NV = len(VOCAB) + 2 # 三种配比共用同一个词表
def build(train, order=3):
table = {}
for s in train:
pad = u'^' * (order - 1) + s + u'$'
for i in range(len(pad) - order + 1):
key, nxt = pad[i:i + order - 1], pad[i + order - 1]
d = table.setdefault(key, {})
d[nxt] = d.get(nxt, 0) + 1
return table
# 加一平滑:没见过的一半算未知,一半靠 +1 兜
p = (d.get(nxt, 0) + 1.0) / (sum(d.values()) + NV)
实跑输出:
--------------------------------------------------------------------
三种语料配比,看行业困惑度和通用困惑度怎么换
--------------------------------------------------------------------
① 语料与测试集
--------------------------------------------------------------------
| 分块 | 条数 | 规模 |
|---|---|---:|
| 通用训练语料 | 80 句 | 950 字 |
| 行业训练语料 | 80 句 | 956 字 |
| 行业测试集 | 20 句 | 242 字 |
| 通用测试集 | 20 句 | 228 字 |
语料是造的:从字表里随机取字拼句。所以这里量的是「字与字的搭配」学得怎么样,不是真实语言能力。
要看的是三种配比之间的相对差别。
! 真实领域预训练也用这套方法验配比:拿两份小语料训几个小模型,比困惑度,再放大。
先在小规模上把配比试出来,比直接烧一次大的便宜两个数量级。
② 三种配比
--------------------------------------------------------------------
| 配比 | 通用条数 / 行业条数 | 比例 |
|---|:---:|:---:|
| 只用通用 | 80 / 0 | 100% / 0% |
| 只用行业 | 0 / 80 | 0% / 100% |
| 八二混合 | 16 / 64 | 20% / 80% |
三种配比参数量相同(都是三阶字符模型),差别只在看到过什么。
③ 四种困惑度
--------------------------------------------------------------------
| 训练语料 | 行业困惑度 | 通用困惑度 |
|---|---:|---:|
| 只用通用 | 93.5 | 53.7 |
| 只用行业 | 51.7 | 93.7 |
| 八二混合 | 55.6 | 81.4 |
只用行业语的模型,行业困惑度最低,通用困惑度最高——它把通用语言忘了。
! 这就是灾难性遗忘的最小演示。
真模型上一个数量级的差别往往表现得更隐蔽:不是「不会说人话了」,而是「某些通用能力悄悄掉了」。
④ 混合比例怎么影响两头
--------------------------------------------------------------------
| 配比 | 行业困惑度 | 通用困惑度 |
|---|---:|---:|
| 行业占 0% | 93.5 | 53.7 |
| 行业占 20% | 78.7 | 58.1 |
| 行业占 40% | 67.2 | 65.7 |
| 行业占 60% | 61.6 | 69.9 |
| 行业占 80% | 55.6 | 81.4 |
| 行业占 100% | 51.7 | 93.7 |
行业占比越高,行业困惑度越低、通用困惑度越高。没有两头都最优的点,只有折中。
! 常见经验是行业语料占七到九成,留一两成通用语料「压着不让它忘」。
这一两成不是浪费——它买的是「模型还记得怎么说话」。
⑤ 微调与领域预训练的分工
--------------------------------------------------------------------
| 手段 | 吃什么 | 补什么 | 代价 |
|---|---|---|---|
| 领域预训练 | 海量无标注行业文本 | 补知识、学说法 | 贵,要几亿 token |
| 指令微调 | 几千条问答对 | 学格式、学任务 | 便宜,一天能跑完 |
| 检索增强 | 行业文档库 | 补事实、可更新 | 不训练,改库即生效 |
三者不是替代关系。知识要新就用检索,说法要像就用领域预训练,格式要对就用指令微调。
! 最容易做错的是「什么都想用微调解决」。
微调改不了模型不知道的事实——它只会让模型把不知道的东西说得更像真的。
⑥ 真机上的配比参数
--------------------------------------------------------------------
[配置原样 · 本沙箱不执行]
| # 继续预训练,混一点通用语料防遗忘
| train_files:
| - path: data/industry.jsonl
| weight: 0.8
| - path: data/general.jsonl
| weight: 0.2
这是训练框架的数据配置片段,本沙箱没有训练框架,不执行。
①语料是造的:由术语拼成句子,每条五到七个词。 为什么不像上一版那样直接随机取字?因为均匀随机的字之间没有搭配,三阶模型什么都学不到, 配比实验就测不出差别。 真实领域预训练也是这么验配比的:拿两份小语料训几个小模型,比困惑度,再放大。 先在小规模上把配比试出来,比直接烧一次大的便宜两个数量级。 ②三种配比共用同一个词表。这一点很重要——真实训练时词表在训练前就定死了,不会因为语料换了而缩水。 词表不一样,困惑度就没法横向比。 ③结果很清楚:只用通用语料的模型,通用困惑度 53.7、行业 93.5; 只用行业语料的正好反过来,行业 51.7、通用 93.7。各自只认得自己那一半。 八二混合:行业 55.6、通用 81.4。行业那边只差 3.9,通用那边好了 12.3。 20% 的通用语料买回了三分之二的通用能力。 ④扫一遍配比看趋势:行业占比从 0 涨到 100,行业困惑度单调降(93.5 到 51.7), 通用困惑度单调升(53.7 到 93.7)。 没有两头都最优的点,只有折中。折中点的选择取决于业务:如果只做行业问答,行业占九成也行; 如果还要处理通用闲聊,八成是底线。 ⑤三种手段的分工要分清:知识要新用检索,说法要像用领域预训练,格式要对用指令微调。 最容易做错的是「什么都想用微调解决」。 微调改不了模型不知道的事实——它只会让模型把不知道的东西说得更像真的。
30 秒实验
- 把八二混合改成「通用 8 条 + 行业 72 条」(一成通用),重跑第③步。通用困惑度会涨到多少? 这一成通用还值不值?
- 把三阶改成二阶,重跑。两阶模型的困惑度整体会变差,但配比的趋势有没有变?这决定结论稳不稳。
- 把
NV从「共用词表」改成「各自语料的字符集」,重跑第③步。数值会怎么变? 这说明为什么困惑度不能跨词表比较。
常见误区
- 以为微调能灌知识。微调改的是「怎么说」,不是「知道什么」。要补事实,用检索; 检索不生效了,才轮到重训。
- 只用行业语料训。行业困惑度确实最低,但通用能力掉得厉害,表现是回答开始不像人话、格式开始飘。
- 拿困惑度跨模型比较。 困惑度跟词表、分词粒度、测试集都绑定,只能在同一套设置下比相对高低, 不能拿「我的模型 PPL 8,他的 12」当结论。
自测三问(点开对答案)
1. 领域预训练和指令微调,各自补什么? —— 领域预训练补「说法」——模型开始用行业词汇和句式; 指令微调补「任务」——模型知道被问什么该怎么答。前者贵两个数量级,所以先做后者。 2. 为什么行业占比 80% 时要留 20% 通用语料? —— 因为那 20% 买的是「模型还记得怎么说人话」。 演示里它只让行业困惑度涨 3.9,却让通用困惑度降了 12.3——这笔交换很划算。 3. 灾难性遗忘在真模型上为什么不容易发现? —— 因为它不表现为「突然不会说话」,而是通用能力悄悄下滑:常识变差、指令跟随变差、 格式稳定性变差。不把通用能力放进评测集,就永远发现不了。模型改完了,最后一节:上线前怎么验,验不过怎么改。
- ☐ (本篇完,下一篇 5.5 模型测试、评估和优化)
相关笔记
- 微调策略:全参微调与 LoRA 的账
- 评估与优化:评测怎么分维度
- 41 课 图解深度学习:迁移学习与灾难性遗忘
- 🦙 Llama 大模型实践 · 索引