2.3 训练监控与评估、适配器部署与常见报错排查 上一节我们把训练跑起来了,也把超参数讲透了。但"跑起来"和"跑得好"是两码事。这一节我要回答的是真正决定你这趟微调值不值得的三个问题:训练过程我到底在看什么、模型练完我怎么验收、以及练好的适配器怎么真正用起来。我发现 90% 的初学者卡在第二步——以为 loss 掉下去就万事大吉,结果一上线全是胡说八道的回答。你读完这一节,应该能在训练中途就判断"该不该停",并掌握一套不依赖玄学的验收方法。 一、训练监控:你盯着的那个 loss 到底在说什么 先说一个扎心的真相:loss 下降只是"模型正在拟合这批数据"的证据,它既不代表模型学会了知识,也不代表它在你关心的任务上变强了。把 loss 当成唯一指标,是新手最容易犯的错。
上一节我们把训练跑起来了,也把超参数讲透了。但"跑起来"和"跑得好"是两码事。这一节我要回答的是真正决定你这趟微调值不值得的三个问题:训练过程我到底在看什么、模型练完我怎么验收、以及练好的适配器怎么真正用起来。我发现 90% 的初学者卡在第二步——以为 loss 掉下去就万事大吉,结果一上线全是胡说八道的回答。你读完这一节,应该能在训练中途就判断"该不该停",并掌握一套不依赖玄学的验收方法。
先说一个扎心的真相:loss 下降只是"模型正在拟合这批数据"的证据,它既不代表模型学会了知识,也不代表它在你关心的任务上变强了。把 loss 当成唯一指标,是新手最容易犯的错。
一个健康的 LoRA/QLoRA 训练曲线通常是这样的:前 10%~20% 的步数里 loss 断崖式下跌,这是模型在记住数据格式和语料风格;之后进入缓慢收敛区,loss 每一步只动一点点,伴随肉眼可见的震荡(因为 batch 里有易有难);最后进入平台区,loss 基本不动。你要关注的是从"缓慢收敛"到"平台"的过渡点,那附近往往藏着最佳检查点。
比只看 train loss 更关键的是盯住 eval loss(验证集损失)。如果你没划分验证集,现在就去划——哪怕只拿 5%~10% 的原始数据当验证集也好。train loss 一路向下、eval loss 却开始抬头,这就是过拟合的明确信号,说明模型在死记训练样本而不是学规律。在 LoRA 这种只动极少参数的场景里,过拟合来得比全量微调慢,但 QLoRA 因为 4-bit 量化本身带噪声,eval loss 的抬头往往更尖锐,更要警惕。
除了两条 loss 曲线,我还建议你顺手看三个量:grad_norm(梯度范数)、learning_rate(实际学习率)、trainable params(可训练参占比)。grad_norm 突然飙到几百上千,说明训练快崩了,先检查是不是学习率设大或没有梯度裁剪;learning_rate 应该和你的 scheduler 吻合,如果它一直是常数,说明 scheduler 没接上;trainable params 占比在 QLoRA 下通常只有 0.1%~1%,如果你看到 100%,那一定是把整个底座解冻了,等于在花 QLoRA 的显存做全量微调,纯亏。
loss 漂亮不等于模型好用。我见过太多案例:eval loss 低到 1.2,可拿业务问题一问,模型把训练数据里的样例原封不动背出来,换个问法就懵。所以练完第一件事,是做一次"越狱式"的人审验收。
验收要分三层,我把它叫"三重门":
第一重门是 格式遵循。垂直领域微调最常见的是把模型训成"输入→固定格式输出",比如医疗报告结构化、法律条款抽取。先看模型有没有稳定吐出你要的字段、标签、分隔符。这一层最容易过,但也不能跳——很多翻车是输出里多了个换行、少了个冒号,下游解析直接崩。
第二重门是 事实一致性。这是最难的。你拿验证集之外、业务真实会遇到的 20~30 个样本,人工判断回答里的关键事实对不对。LoRA 微调本质是"在底座能力上做风格/分布的偏移",它不会凭空给模型注入它本来不知道的知识。所以验收时如果发现模型编造了它训练数据里没有的专有名词,那不是你微调的问题,是底座本来就不知道——这种情况要么补数据,要么换更大的底座,微调救不了知识缺口。
第三重门是 泛化稳健性。把同一个问题用三种不同说法问模型,看它给的答案是否一致、是否都合理。能扛住问法扰动,才算真正学会了任务逻辑。
一个实操建议:把验收做成一张打分表,每个样本标记"格式/事实/稳健"三项,算出通过率。我给自己定的线是格式 100%、事实 ≥85%、稳健 ≥75% 才放行。低于这条线,别急着上线,回到 2.2 调超参或回炉补数据。验收不达标就上线,后面客服和投诉会替你验收,代价大得多。
LoRA/QLoRA 训练产出的不是一整个模型,而是一个几十到几百兆的"适配器文件"(通常是 adapter_model.bin 加 adapter_config.json)。这也是它省显存、好分享的核心。但你要在推理时用上它,有两条路:挂载加载,或者合并导出。
挂载加载最常用,也最省事。推理时加载原始底座,再把适配器挂上去:
from transformers import AutoModelForCausalLM from peft import PeftModel base = AutoModelForCausalLM.from_pretrained("基座路径", torch_dtype="auto", device_map="auto") model = PeftModel.from_pretrained(base, "适配器路径") model = model.merge_and_unload() # 可选:合并进显存里的权重
merge_and_unload() 这一步是把适配器的增量合并进当前加载的底座权重里,推理时不再需要单独读适配器,速度略快;但它只改内存里的权重,不会落盘成新模型。注意 QLoRA 训出来的适配器,挂载时底座仍建议用 4-bit 加载以省显存,但如果你要合并后做高精度推理,可以先把底座用 bf16 加载再合并,显存会涨但数值更干净。
合并导出适合要部署成独立模型的场景。思路是把适配器权重 merge 回底座,存成一个完整的、不再依赖 PEFT 的模型目录,这样任何标准推理框架都能直接加载,部署侧最省心:
from peft import PeftModel model = PeftModel.from_pretrained(base, "适配器路径") merged = model.merge_and_unload() merged.save_pretrained("合并后的模型路径")
这里有个坑必须提醒:QLoRA 的 4-bit 底座合并时,由于量化本身有信息损失,合并出的权重是"近似还原",不是数学上无损的。所以如果你对精度极其敏感(比如金融风控),建议评估阶段就用 bf16 底座 + LoRA 而非 QLoRA,QLoRA 留给"显存实在不够、且能接受小幅精度折损"的场合。这和第 1 章讲的"显存账"是同一笔账,只是你现在站在部署端再看一遍。
适配器的版本管理也值得说一句:每次训练出来的适配器建议带上超参标签,比如 glm52-lora-r64-a128-lr2e-4-ep3。否则一个月后你面对十个 adapter_model.bin,根本分不清哪个是"能用的那个"。
训练验收都过了,最后一步是把它变成能用的服务。最小可行的做法是直接用 transformers 的 pipeline 或 model.generate 包一层 HTTP 接口;追求吞吐的话,可以走 vLLM 这类支持 PEFT 适配器热加载的推理引擎,它允许同一个底座动态挂不同的适配器,多租户场景很香。
需要强调的合规点:GLM-5.2 是 MIT 开源协议,你可以商用,但请在产品里保留模型署名与协议声明,这是开源协议的基本要求,不是可选项。具体部署形态(本地服务、云函数、边缘端)取决于你的访问量和延迟要求,这里不替你下结论,但决策逻辑是:先量峰值 QPS,再选方案,别一上来就堆集群。
训练很少一帆风顺:半夜显存溢出、公司断电、你想换组数据接着训。所以“会不会存检查点、能不能续上”直接决定你的时间是不是打水漂。
PEFT 加 Trainer 默认会按 save_steps 或 save_strategy 等于 epoch 把适配器检查点写到输出目录,文件名形如 checkpoint-500、checkpoint-1000。我建议你不要只留最后一个,而是设置 save_total_limit 等于 3 左右,保留最近 3 个。原因有二:一是 eval loss 的最低点往往不在最后一个 step,留几个才能挑最优;二是万一最后一个检查点恰好处在梯度异常的那一步,你还有退路。
断点续训的写法是给 Trainer 传 resume_from_checkpoint 等于 True(或指定某个 checkpoint 路径)。但这里有个必须提醒的坑:QLoRA 场景里,如果你用的是 4-bit 量化加载的底座,底座本身不会被保存进检查点,检查点里只有适配器增量和 optimizer 与 scheduler 状态。续训时 Trainer 会重新加载底座再挂适配器,所以你续训命令里的底座路径必须和首次训练完全一致,否则 optimizer 状态对不上,续出来的是一盘乱账。
还有一类需求是“我有好几个不同任务的适配器,想融合成一个”。PEFT 提供了 WeightedAdapter 或 add_weighted_adapter 接口,可以按权重把多个 LoRA 线性叠加。我的经验是:融合适合任务相关、分布接近的适配器(比如同一个领域的不同子风格),不适合把八竿子打不着的任务硬揉在一起,融合后效果通常不如单独用其中一个。要不要融合,仍回到上一节的三重门验收说了算,别凭感觉。
这部分是我踩坑踩出来的,按"现象→原因→解法"列给你。注意:下面这些是真实高频问题,但具体报错文案会因库版本而异,遇到没见过的堆栈,先看最后一行报错类型,再去对应库的 issue 里搜,别盲猜。
| 现象 | 最可能的根因 | 解法 |
|---|---|---|
| CUDA out of memory 即便用了 QLoRA | 没开梯度检查点,或 batch_size 仍偏大 | 开 gradient_checkpointing,把 per_device_train_batch_size 降到 1~2,开 gradient_accumulation_steps 补步数 |
| 加载适配器报 size mismatch | 底座和训练时用的不是同一个,或 r/alpha 不一致 | 确认推理底座路径 == 训练底座路径,adapter_config 里的 r、lora_alpha 一致 |
| loss 是 NaN/Inf | 学习率过大,或数据里有空样本/坏字符 | 把 lr 降一个数量级,清洗数据里的空行和非法 token |
| 训练了几步就停,不报错 | max_steps 或 num_train_epochs 被设成极小值 | 检查总步数 = 样本数 / batch × epoch,确认不是被误设成 1 |
| 显存够但速度极慢 | 没用 device_map="auto",或没开 bf16 |
确认开启了混合精度和正确的设备映射 |
| 合并后精度明显变差 | QLoRA 4-bit 量化的固有折损 | 评估敏感场景改用 bf16 LoRA;或合并时用更高精度底座 |
| 输出全是重复 token | 数据格式混乱,模型没学到停止信号 | 在数据里明确加结束符,训练数据每条以统一分隔符收尾 |
表里前三条是我见得最多的。尤其第一个——很多人以为"用了 QLoRA 就一定能跑",结果忘了 QLoRA 省的是"存储与反向传播的权重精度",并不自动省掉"激活值显存",所以梯度检查点该开还得开。把这几条吃透,你基本能绕开 80% 的新手翻车。
回顾一下,这一节我们走完了"训练之后"的全链路:盯住 train/eval 双 loss 曲线来判断过拟合和早停时机;用"格式—事实—稳健"三重门做不玄学的验收;用挂载加载或合并导出把适配器真正用起来;并附了一份能拦下大部分翻车的报错手册。到下课为止,第 2 章核心模块的三节已经齐活——你已经具备"从零跑通一次训练、调好超参、验收并部署"的完整能力。下一章我们往原理深处走,搞清楚 LoRA 那几个矩阵到底在底座的什么位置起作用,以及为什么 QLoRA 能省那么多显存。