书生浦语第三次作业:大模型微调、合并、量化部署与评测全链路

发布时间:2026/10/26 3:10:46
书生浦语第三次作业:大模型微调、合并、量化部署与评测全链路 第三次作业在整个训练营里属于一个明显的分水岭前两次多半是跑通环境、读懂脚本到这一关才真正开始把一个大模型从头到尾“盘”一遍——准备数据、配置微调、盯着 loss 曲线、合并权重、量化部署、再拿评测集回去证明它确实变好了。很多人在这里第一次遇到显存不够、loss 不降、合并后效果塌方这些实打实的问题也第一次意识到“能跑通 demo”和“能交付一个可用模型”之间隔着的不是一行命令而是一整套工程判断。下面这些内容就是围绕书生浦语这套训练营的第三次作业展开的把任务拆解、算力账本、数据格式、LoRA 参数取舍、排查链路、部署与评测这几个环节完整走一遍。它适合已经跑通过一次训练、但卡在中途不知道下一步该调什么的人也适合想拿这套流程迁移到自己业务数据上的开发者。1. 第三次作业的考核重点它要的是完整交付链先把这一关的任务性质说清楚否则后面的每一步都会做偏。第三次作业通常不是“写一个脚本”而是要求你交付一条完整的链路基于给定的基座模型用自己构造或整理的数据做一次参数高效微调把训练产物合并、量化、部署成可对话的服务最后用一套评测集给出微调前后的对比结论。这四件事任何一环断掉作业都是不完整的。1.1 四个交付物各自意味着什么我把它拆成四块看每一块的验收点其实不一样交付物表面要求实际考察点微调配置一份能跑起来的配置文件你是否理解 batch size、梯度累积、学习率之间的换算关系训练日志loss 曲线与最终指标你是否能判断训练是“真的收敛”还是“看起来收敛”合并与部署能对话的服务或接口你是否处理了 chat template、量化精度、推理参数评测对比微调前后的分数表你是否选了与任务匹配的评测口径而不是随便跑个总分第一项最容易糊弄也最容易翻车。很多人的配置文件是从示例里抄来的改了模型路径就开跑结果 batch size 设成 4 却只有一张 24G 的卡第一轮迭代就 OOM还有一种相反的情况为了让显存够用把 batch size 压到 1却忘了同步提高梯度累积步数等效批量变得极小训了三个 epoch 模型基本没学到东西。1.2 为什么作业要求“自己造数据”而不是给现成的这是第三次作业和前面最本质的区别。给你一份干净的数据集训一遍你学到的只是命令行让你自己去构造几十到几百条样本你才会碰到真实业务里那些讨厌的细节——样本长度分布不均、回答风格不一致、有些样本的 output 是空的、有些样本的 input 里混着全角空格和不可见字符。这些问题在示例数据里都被清洗过了在你自己的数据里一条都不会少。我的建议是数据量不要贪多。做第三次作业200 到 500 条高质量样本远比 5000 条脏数据有价值因为 LoRA 在小数据量下的表现更容易观察你调一个参数就能看出差别而数据一多训练一轮要几十分钟你的迭代节奏会被彻底拖死。1.3 一个容易被忽略的前置判断这次该用全量微调还是 LoRA作业里通常允许你选但选哪个不是看“哪个高级”而是看你手里的卡。判断逻辑很简单把模型参数按 bf16 折算成显存占用全量微调的训练峰值大致是“权重 × 4 激活”LoRA 则只需要“权重 × 2 少量优化器状态 激活”。下面这张表是我自己踩过之后整理的经验值可以直接拿来估模型规模权重(bf16)LoRA 训练峰值全量微调峰值单卡可行性1.8B约 3.6G8–10G30G 以上LoRA 单卡 24G 可行7B约 14G22–26G80G 以上LoRA 需 40G 或双卡20B约 40G60–70G多卡才谈得上多卡 80G这张表的意义在于帮你在动手之前就排除掉不可能的方案。我见过有人拿着单张 24G 的卡硬上 7B 全量微调折腾两天最后放弃其实一开始算一下就知道走不通。如果显存确实紧张还有两个兜底手段开启梯度检查点时间换显存慢 20% 左右以及开启序列打包把多条短样本拼成一条长序列吞吐能提不少但对 attention mask 的实现有要求后文会细说。2. 环境和算力的账必须在开跑前算清环境这一步看起来最没技术含量实际上是第三次作业里返工率最高的环节。原因不在于装不上而在于版本组合的隐性冲突框架、驱动、算子库三者之间只要有一处对不上往往不报错只是在训练到某个 batch 时突然给你一个莫名其妙的非法内存访问。2.1 版本对齐的顺序不能反正确的顺序是先定显卡驱动支持的 CUDA 上限再定 PyTorch最后让微调框架去适配 PyTorch。反过来做也就是先 pip 装最新框架再回头找匹配的 torch十有八九会掉进依赖地狱。我的常规操作是建一个独立环境锁定 Python 3.10然后装 CUDA 11.8 或 12.1 对应的 torch 2.1/2.2 版本装完立刻验证python -c import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_available(), torch.cuda.device_count())这一行输出里如果is_available()是 False别急着往下装任何东西先回去查驱动。很多人是在装了三个框架之后才发现显卡根本没被识别白折腾。验证通过后再装微调工具链。书生浦语生态里常用的是 XTuner 做微调、LMDeploy 做部署、OpenCompass 做评测这三个的版本最好一次性对齐因为它们都依赖 transformers 的某些接口版本跨度太大就会在加载模型时报unexpected keyword argument之类的错。2.2 那几个看起来无害却会卡住一整天的细节有几个点我单独拎出来说因为每一个都让我浪费过大半天torch.cuda.is_available()为 True 但训练报 NCCL 错误。多半是多卡通信的网卡绑定问题单卡训练时把 distributed 相关参数直接关掉即可不用死磕。装完 flash-attention 后 import 报 undefined symbol。这是典型的编译环境和运行环境不一致重装时确保CUDA_HOME指向的版本和 torch 编译时用的版本一致别用系统里另一个 CUDA 去编。pip install时悄悄把 torch 降级了。装框架时务必加--no-deps先看依赖或者装完再跑一次上面的验证命令确认 torch 没被换掉。注意环境验证不要等到训练脚本跑起来才做。装完就写一个三行的脚本加载模型、跑一次前向、打印输出形状能过再进入下一步。这三分钟能省掉后面三小时。2.3 磁盘空间是被低估的资源一个 7B 模型的 fp16 权重约 14G加上分词器、配置文件、原始下载包一个模型目录轻松 30G 起步。训练过程中每个 checkpoint 又是二十多 G如果你设置每 500 步存一次、跑三个 epoch磁盘两百 G 都不一定够。我的习惯是保存策略改成只存 adapter 权重几十兆到几百兆checkpoint 数量限制在 3 个以内中间产物定期清理。这个习惯在大模型训练里不是节省是保命。3. 数据格式那点事一行 JSON 决定训练成败数据是第三次作业里最能拉开差距的地方。同样的配置、同样的卡两个人的 loss 曲线可以完全不同原因往往就在数据上。3.1 对话类数据的字段结构书生浦语这套体系里常见的对话数据是 JSONL 格式一行一条样本核心字段是一个 conversation 列表列表里每一项包含 system、input、output 三个键{conversation: [{system: 你是一个严谨的技术助手。, input: 解释一下什么是梯度累积。, output: 梯度累积是指...}]}这里有几个必须注意的点全都是踩出来的system 字段可以为空但键最好保留某些旧版本的转换脚本对缺失键容错不好。input 和 output 都不能是空字符串。output 为空会导致这一条样本的标签全是 paddingloss 计算时整条归零如果这种样本占比高loss 曲线会难看得让你以为模型坏了。多轮对话要严格交替一问一答、再问再答别出现连续两条 output 中间没有 input 的情况否则模板拼接出来的格式是错的模型学到的是错误的对齐关系。3.2 长度分布比数据总量更重要我在做第三次作业时做过一次统计500 条样本里长度中位数是 320 个 token但有 12 条超过了 4000 token。这 12 条会带来两个麻烦——如果 max_length 设成 2048它们会被截断而截断往往正好切在 output 中间模型学到的回答是残缺的如果不截断这几条样本会撑爆显存让你误以为是 batch size 设太大了。处理方式我推荐两步走先统计长度分布把超过 max_length 的样本单独拎出来要么人工精简要么把长答案拆成多轮然后在数据预处理时加一道过滤超过阈值的直接丢弃并在日志里计数。别指望模型能自己消化截断样本它只会学歪。3.3 模板一致性最隐蔽也最致命的一个坑训练时用什么模板拼接对话推理时就必须用什么模板一字不差。我一开始没在意这个训练用的是工具链默认的模板推理时图省事自己手写了一个用户 query \n助手的拼接结果模型回答得驴唇不对马嘴还一度怀疑是微调失败了。后来把推理端的输入按训练模板原样拼回去效果立刻正常。判断方法很简单训练前打印一条样本经过 map_fn 处理后的完整 input_ids 解码结果肉眼看一下格式对不对部署后再打印一次推理服务实际收到的 prompt两边做字符级对比。这两次打印值得养成习惯。另外中文数据里全角空格、零宽字符、从网页复制来的不间断空格都需要在预处理阶段清洗掉。这些字符在分词后会变成意料之外的 token模型没见过几次就会开始输出一些奇怪的符号。4. LoRA 的参数不是玄学是有换算关系的到了配置这一步绝大多数困惑集中在几个参数上rank 取多少、alpha 怎么配、学习率给多大、batch size 和梯度累积怎么折算。这些不是拍脑袋定的互相之间有明确的关系。4.1 rank 与 alpha 的配比逻辑LoRA 的本质是在原权重旁边挂一个小矩阵用低秩分解去近似这次微调需要的权重变化量。rank 就是那个低秩的维度alpha 是一个缩放系数实际生效的缩放是alpha / rank。这就解释了一个常见现象把 rank 从 8 提到 64如果 alpha 没同步提实际学习强度反而被稀释了。我的经验配比是这样场景rankalpha说明小数据量、任务单一如固定格式抽取8–1616–32参数少、不易过拟合中等数据、风格迁移3232–64比较通用的默认值数据量大、任务复杂64–128128需要更多显存也更容易过拟合对第三次作业这种几百条样本的规模rank 16 到 32 完全够用。有人一上来就设 128结果训完发现模型把训练集背下来了换个问法就崩这就是典型的容量过剩。4.2 等效批量与学习率的联动配置文件里通常写的是单卡 batch size 和梯度累积步数真正影响优化效果的是两者的乘积——等效批量。等效批量变了学习率也应该跟着变粗略有这么个经验关系等效批量翻倍学习率可以上浮 1.5 倍左右不是线性翻倍。下面是我常用的几组组合单卡 24G 跑 7Bbatch_size1, grad_accum16等效批量 16学习率 2e-4多卡场景batch_size2, grad_accum8四卡时等效批量 64学习率 3e-4 到 4e-4数据量极小200 条以内batch_size1, grad_accum8学习率 1e-4宁可慢一点也别冲飞学习率设大了的典型症状是 loss 在前几十步剧烈震荡然后卡在某个值不动甚至出现 nan设小了的症状是 loss 缓慢下降但三个 epoch 结束还在 2.0 以上模型几乎没变化。4.3 序列打包能提吞吐但要确认 mask 是对的序列打包sequence packing会把多条短样本拼进一条固定长度的序列里短样本越多收益越大吞吐能提升一到两倍。但它有一个前提注意力掩码必须正确区分不同样本否则不同样本之间会互相“看见”学出来的东西是串味的。工具链里通常有开关开启时它会自动用变长注意力的实现。我的做法是先用关闭状态跑通一次确认效果基线再打开跑一次对比 loss 曲线和最终评测分。如果打开后分数反而下降说明当前版本的掩码实现和你的数据形态不匹配直接关掉别在这上面耗。5. 训练跑起来之后三类异常的完整排查链路真正让人焦虑的不是配置是训练跑起来之后不知道它在发生什么。我把自己遇到过的三类高频问题整理成排查链路遇到时可以照着走一遍比盲目改参数有效得多。5.1 loss 不下降从数据往上倒查症状训练几百步后 loss 稳定在 2.3 附近小幅波动几乎不下降。排查顺序我建议这么走先看数据本身。抽 5 条样本把经过 map_fn 之后的 input_ids 解码回文本肉眼确认问题和答案都在、格式正确、没有被截断。这一步能过滤掉一半的问题。确认标签位置。对话微调时通常只对 output 部分计算 lossinput 部分被 mask 成 -100。如果 mask 逻辑写反了或者整条样本都被 mask 了loss 就学不到东西。打印一次 labels看看非 -100 的位置是不是集中在回答部分。再怀疑学习率。把学习率临时调到 1e-3 跑 50 步如果 loss 明显动了说明原学习率偏小如果直接炸成 nan说明原值没问题问题在别处。最后才怀疑 rank 和 alpha。rank 太小确实会导致容量不足但一般表现为 loss 能降但降得浅而不是完全不降。这条链路的逻辑是从“数据是否正确”到“梯度是否有效”再到“容量是否足够”因为越靠前的环节出错概率越高修复成本越低。5.2 显存 OOM不要在报错那一刻才找原因OOM 的报错信息通常只告诉你“试图分配多少、还差多少”不告诉你为什么。我的经验是 OOM 有七成发生在第一个 batch两成发生在保存 checkpoint 时剩下一成发生在评测阶段。第一个 batch 就 OOM先把 batch size 降到 1还不行就开梯度检查点再不行就降 max_length。这三个动作按“对效果影响从小到大”排序降 batch size 影响最小可以用梯度累积补回来降 max_length 影响最大会截断样本。保存时 OOM这是权重在 GPU 上做拼接或类型转换导致的。解决办法是把保存逻辑放到 CPU 侧或者分片保存别一次性把整个权重搬到显存里。评测时 OOM评测往往要生成较长文本KV cache 占用很大。把评测的 batch size 单独调小和训练的配置分开写。5.3 输出乱码或复读多半不是模型坏了我在第一次跑完微调后遇到过模型开始输出“的的的的的”这种复读当时以为训练崩了重训了两遍才发现是推理参数问题。生成阶段的重复惩罚、温度、top_p 都会显著影响这个现象。排查顺序是先把温度降到 0.1、top_p 设成 0.7 试一次如果复读消失那就是采样参数问题跟模型无关如果仍然复读再去看是不是训练数据里存在大量重复文本比如同一条问答复制了很多遍这种数据会让模型学到“重复是正常的”最后才怀疑是不是训练轮数太多导致过拟合。需要提醒的是合并 LoRA 权重这一步如果精度处理不当也会导致输出质量下降。合并时务必用 fp32 做加法再转回 bf16直接在 bf16 下相加会引入累积误差模型变得“有点傻”但又说不出哪里不对。6. 从权重文件到能对话的服务训练完拿到的是一个 adapter 目录里面只有几十兆到几百兆的增量权重不能直接当模型用。这一步要做两件事合并然后量化部署。6.1 合并的两个阶段别搞混合并通常分两步。第一步是把训练框架自己的 checkpoint 格式转成 HuggingFace 格式的 adapter第二步是把 adapter 合并进基座模型得到一份完整权重。两步的命令行工具在不同版本里名字略有差异跑之前先看一眼帮助信息确认子命令名# 第一步训练产物转 HuggingFace adapter xtuner convert pth_to_hf ./config.py ./iter_1000.pth ./hf_adapter # 第二步adapter 合并进基座 xtuner convert merge ./base_model ./hf_adapter ./merged_model \ --max-shard-size 2GB--max-shard-size这个参数别省它把大权重切成多个文件能避免单文件过大导致加载时内存峰值过高。合并完之后建议立刻做一次冒烟测试加载合并模型喂一句简单问题确认能正常输出。别等到部署阶段才发现权重是坏的。6.2 量化的取舍4bit 换来的是什么4bit 量化大约能把显存占用压到原来的四分之一代价是精度损失。对第三次作业这种场景W4A16权重 4bit、激活 16bit是最常见的折中方案实测在通用问答上的损失通常在一个百分点左右但推理速度能快一大截。量化流程一般是先做校准、再转换# 校准用一小批数据统计权重分布 lmdeploy lite calibrate ./merged_model \ --calib_dataset ./calib_data \ --work-dir ./awq_params # 转换按 4bit、分组大小 128 量化 lmdeploy convert ./merged_model ./workspace_awq \ --model-format awq --group-size 128 --w-bit 4分组大小 128 是个平衡点取 64 精度更好但显存压不下去取 256 显存更省但精度掉得明显。校准数据一定要用和你任务同分布的样本用通用语料校准出来的参数在你自己的领域数据上表现会差一截。6.3 部署后的三个必查项服务起来之后别急着交作业先过这三关对话模板是否和训练一致。前面提过这里必须在实际部署环境里再验一次因为部署框架可能自带一份模板配置会覆盖你手写的。多轮对话的上下文是否被正确保留。有些部署配置默认不保留历史导致用户问第二句时模型失忆。并发情况下的显存是否稳定。单请求跑得好好的两个请求同时进来就 OOM说明 KV cache 的显存配额设得太满留 20% 余量比较稳妥。7. 评测怎么证明模型真的变好了评测是第三次作业里最容易被写成流水账的部分。很多人跑一遍通用榜单贴个总分就完事但那说明不了你的微调有没有用。7.1 通用榜单和自建测试集的分工通用榜单比如学科知识类的选择题评测看的是模型有没有被训坏它是一个保底指标微调后分数如果暴跌说明训练过程有问题可能是学习率太大把基座能力冲掉了。而自建测试集看的是你的任务有没有变好这才是这次微调的目的。自建测试集的做法不复杂从你的数据里留出 50 条不参与训练改成问答形式人工写下参考答案的要点然后按“是否正确命中要点”做人工或规则判分。50 条足够看出趋势了不必追求统计学意义上的严谨。7.2 对比表怎么设计才有信息量我在作业里用的对比维度是三个比单一总分有用得多评测项基座模型微调后变化通用知识选择题准确率基准略降 0.5–1 个点可接受通常是灾难性遗忘的轻微表现自建任务集要点命中率基准明显提升微调的主要收益输出格式合规率基准大幅提升格式类任务收益最直观第三项经常被人忽略但对固定格式抽取这类任务格式合规率往往比语义准确率更能反映微调效果因为基座模型最常见的失败就是“答得对但格式不对”。7.3 一个反直觉的观察我做过两组对比一组用 300 条精挑的数据一组用 1200 条从网上粗略抓取的数据同样配置训同样的轮数。结果精挑组的自建测试集命中率高出十多个百分点而通用能力下降幅度反而更小。原因不难理解——脏数据里那些格式混乱、答案残缺的样本模型同样会认真学学到的就是坏习惯。所以如果这次作业时间有限我建议把时间花在数据清洗上而不是花在调参上。参数调优能带来几个点的提升数据质量的差异是十几个点的量级。最后分享一个我在做这类作业时养成的小习惯每完成一个环节就存一份可复现的快照包括配置文件、数据文件、命令记录、输出日志用一个带日期的目录装起来。第三次作业涉及训练、合并、量化、评测四个阶段中间任何一步出问题都需要回退重来没有快照的话你会在“到底是哪一版配置训出来的这个模型”这个问题上浪费掉大量时间。这个习惯跟技术无关但它决定了你在这类多阶段任务里能不能保持节奏。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询