资讯动态

从零训练小语言模型实战:预训练、SFT、PEFT、蒸馏与DPO

发布时间:2026/9/26 8:09:44 来源:尧图企业网站定制
2. 从零训练一个小语言模型Xihe 预训练、CPT、SFT、PEFT、蒸馏与 DPO 实战2.1 这个标题背后是什么适合谁看先说结论这篇文章要聊的是如何从零开始训练一个真正属于自己的小语言模型不依赖任何现成的开源大模型而是把数据、分词器、模型结构、预训练、监督微调、参数高效微调、蒸馏和偏好优化这条完整的链路全部走一遍。模型代号就叫 Xihe。很多人一讲到“自己训练语言模型”第一反应是“这得多少显卡、多少数据、多少预算”。实际上如果你把目标从“复刻 GPT-4”调整为“训练一个 1.5 亿到 4 亿参数、能跑在消费级显卡上的领域小模型”这条路是完全走得通的。我实操下来整个流程涉及的核心技术点包括预训练阶段的 从头预训练From Scratch 和 继续预训练CPTContinual Pre-Training中间监督阶段用的 SFTSupervised Fine-Tuning训练加速和显存优化用的 PEFTParameter-Efficient Fine-Tuning主要是 LoRA/QLoRA推理加速用的 知识蒸馏Knowledge Distillation以及让模型“更懂人类偏好”的 DPODirect Preference Optimization。这篇文章不是纯理论科普而是偏实战记录。我会讲清楚每一步在做什么、为什么要这么做、有哪些关键参数、踩过哪些坑。适合的人包括想入坑 LLM 训练但不想直接套开源模型的学生、需要为垂直领域训练专用模型的工程师、以及单纯想搞懂“从预训练到 DPO 全链路”的技术爱好者。如果你已经会用 Hugging Face Transformers 跑推理这篇文章的上手成本会低很多如果你还没跑通过一个训练脚本也别急文中的关键代码和步骤我会尽量拆细。2.2 为什么选择“小模型”路线不直接微调开源大模型在正式拆流程之前我想先说清楚“为什么要费劲从零预训练一个小模型”。市面上太多教程都在讲“加载 Llama 或 Qwen 然后 LoRA 微调”那当然是一条捷径但它有一个隐藏问题你对模型本身的掌控力非常有限。基座模型的词表、知识范围、行为习惯都是别人定的你真想做一个特定领域比如医疗、法律、古文、代码补全的小助手会遇到三个很现实的问题第一个问题是知识覆盖不准。通用大模型的训练语料追求“广而全”落到你的垂直领域可能只有千分之几的占比针对性明显不够。第二个问题是词表和分词器不适配。很多领域有大量专有词汇、组合词、特殊符号通用分词器经常把一个完整领域名词切成碎片导致模型根本没有“这个词”的概念。第三个问题是成本逻辑。你只想要一个能稳定输出领域答案的小模型却每次都通过 API 调用几百亿参数的通用模型响应延迟、单位成本、离线部署都是麻烦。从零训练小模型的核心价值就是“按需定制”。Xihe 模型的定位就是一个实验性但完整的小模型参数量控制在 2 亿以内训练数据控制在几十 GB 以内单卡 A100 或双卡 4090 就能覆盖整个流程。你自己训练的分词器、预训练模型、SFT 模型、DPO 模型都放在本地仓库里每一步都清清楚楚。这种感觉和直接调 API 完全不一样你会真正理解一个语言模型是怎么“从文本里长大”的。2.3 从零训练小语言模型的整体蓝图预训练、CPT、SFT、PEFT、蒸馏、DPO 一条线讲透先看我实操时采用的完整流程链路后面所有章节都是围绕这张链路展开的原始文本语料 → 清洗与去重 → 训练 BPE/分词器 → 构建预训练语料 → 阶段一预训练From Scratch学习通用语言能力 → 阶段二CPT 继续预训练领域语料续训注入领域知识 → 阶段三SFT 监督微调指令遵循 问答能力 → 阶段四PEFT 高效微调LoRA/QLoRA节省显存与迭代成本 → 阶段五知识蒸馏大模型知识迁移到小模型加速推理 → 阶段六DPO 偏好优化对齐人类偏好提升回答质量 → 评估与部署导出这条链路每一步之间的衔接很关键。很多人以为“预训练完直接 SFT 就行”实际上中间还有个 CPT 的步骤。预训练阶段用的是大规模通用语料模型学到的是通用语言的概率分布而 CPT 则是在预训练好的基座上用你的领域语料继续训练让模型熟悉特定领域的词汇和表达。我打一个生活化比方预训练相当于一个人从小到大的通识教育CPT 则是毕业后进入某个行业进行“岗前专业培训”。没有 CPT直接拿通用基座做微调领域知识的注入效率会差很多因为模型底层压根没有这些领域词汇的“神经元表示”。SFT 的任务是把“会说”变成“会回答问题”。基座模型只会做文本续写你给它“中国的首都是”它能接出“北京”但你给它“中国的首都是哪里请回答”它不一定会规规矩矩地给出你想要的结构。SFT 就是用大量“指令 正确回答”的样本把模型的行为从“自由文本生成”校正为“指令响应”。PEFT 在这里有两种角色。第一种是 SFT 阶段直接用 LoRA 来做高效微调避免全参数微调时的显存爆炸。第二种是当你对全参数微调后的模型仍不满意想继续做快速迭代时PEFT 能让你把微调成本降到极低。我实际操作中SFT 用了全参数微调因为模型本身只有 2 亿参数全参也能跑但在 DPO 阶段用了 LoRA两者对比很有意思后面细说。蒸馏放在 SFT 之后、DPO 之前。蒸馏目的是把教师模型的知识迁移到学生模型上典型场景是“大模型的推理能力太强但部署成本太高小模型速度快但能力不够”。我用了“能力蒸馏 行为蒸馏”的方式让 Xihe 小模型去逼近一个大模型的输出分布。DPO 放在最后是因为它需要模型已经具备稳定的指令回答能力否则偏好优化无从谈起。2.4 数据准备预训练、SFT、偏好数据的构建方法Xihe 的整个训练过程我最想强调的就是数据。现在市面上的开源模型能力已经很强普通用户真正比拼的核心其实是数据质量。我的数据分为三类每一类的构建方式完全不同。2.4.1 预训练语料清洗与去重是第一优先级从零预训练的小模型并不追求数据量上的无限堆积更看重数据的干净度和覆盖度。我用的是约 15GB 的中文语料来源包括公开的通用中文语料、百科类文本、新闻类文本、以及部分开源的中文书籍语料。15GB 对于 2 亿参数的模型来说已经足够训练一个像样的基座如果你的目标只是某个垂直领域5GB 优质语料就足够了。清洗逻辑按以下顺序执行先去 HTML 标签和格式噪音然后用规则过滤掉乱码行、重复行、超短行、纯数字行再做一遍语言检测把混合了英文但没有实际信息量的行剔除最后做基于 MinHash 的相似性去重。这里的 MinHash 去重非常关键因为训练语料里经常出现大量“重复片段”模型反复见到同一段文本会导致严重的记忆偏差。分词器我单独训练了一个 16000 词表的中文 BPE 分词器。为什么选 16000 而不选更大的 32000因为小模型的参数量有限词表太大会把大量参数消耗在 embedding 上剩余可用的 Transformer 层参数就不够了。16000 对于中文领域基本够用而且可以控制 embedding 矩阵在 16000 × 512 820 万参数以内占比合理。分词器训练完之后我强烈建议手动检查几个典型领域词汇的分词结果比如“深度学习”“大语言模型”“心脏病学”这些词是否被完整切开。如果发现被切碎说明语料里这个词的出现频率不够需要补充语料重新训练分词器。2.4.2 SFT 指令数据质量比数量重要得多SFT 数据的质量直接决定模型的“人类友好度”。我的建议是哪怕只收集 1 万条高质量数据也不要硬上 10 万条噪声数据。我整理 SFT 数据集时用了一个“三来源混合”策略公开的开源指令微调数据做了大量的模板归一化和答案清洗占 40%从预训练语料里自动构造的“背景知识问答”占 30%这部分是让模型学会把知识转换成回答问题人工撰写的领域问答和少量思维链数据占 30%这部分质量最高是用来拉高模型上限的。SFT 样本的结构统一采用 chat 模板即多轮对话结构每轮都包含“用户指令”和“助手回答”。所有样本都要做长度过滤超过模型最大序列长度我设的是 1024的答案直接截断或丢弃。这里有一个关键经验并不是“指令越长越好”实际训练中短指令 高质量回答的效果往往更好。2.4.3 偏好数据DPO 的基础是“好/坏回答对”到了 DPO 阶段数据的组织方式和前面完全不一样。DPO 需要的是“同一问题下的两个回答一个被人类偏好一个不被偏好”。这种偏好对数据我建议优先人工构造数量 3000 到 5000 对足够一个 2 亿参数的小模型做 DPO。构建偏好对数据时比较省力的一个实用方法是用已经完成 SFT 的 Xihe 模型对同一问题生成多个候选回答。然后把明显不好的回答作为 rejected把人工修正后的高质量回答作为 chosen。这本质上是让“过去的自己”教“未来的自己”什么是对的。注意不要为了凑数而强行构造模糊偏好对如果两个回答质量差不多宁可不要否则会把模型训迷糊。2.5 预训练与 CPT 实操从损失下降到领域注入准备完成后就可以进入真正训练阶段。这里的训练框架我选的是 Hugging Face Transformers PyTorch配合 Accelerate 做分布式控制。为什么不用 DeepSpeed因为小模型用不到 ZeRO 的三级优化Accelerate 的配置更简单踩坑少。具体训练参数我放到表格里方便你直接抄作业参数预训练阶段值备注模型结构六层 Transformer隐藏维度 5128 个头参数量约 2 亿以下最大序列长度1024控制显存占用的关键Batch Size64梯度累积后等效显存不够就调小单卡 batch学习率5e-4预热 2000 步余弦衰减到 5e-5预训练学习率偏大收敛快优化器AdamWbeta(0.9, 0.95)小模型可加权重衰减 0.01训练步数约 30000 步单卡 A100 约需一至两天预训练阶段最容易犯的错误是“训练 loss 一直下降但生成质量完全不行”。首先要明确一点语言模型的 loss 下降到一定程度之后后续的下降并不代表“知识变多了”而更多是“对训练集的模式拟合更充分了”。判断预训练效果最直接的方法是拿出一批没有出现在训练语料中的“领域句子”让模型续写看续写内容是否通顺且具备领域语义。我在训练到 8000 步左右时模型已经能把“人工智能正在重塑医疗行业”续写成“通过大数据分析和机器学习算法辅助医生进行疾病诊断和治疗方案制定”说明基本语言能力已经建立。CPT 阶段在预训练好的基座上做继续训练数据和训练逻辑有一些明显差异参数CPT 阶段值备注学习率1e-4 到 2e-4比预训练低防止破坏原有权重训练数据领域语料为主可以混入 10% 通用语料防遗忘训练步数3000 到 5000 步不用贪多领域知识注入很快冻结策略不冻结任何层小模型全参继续训练显存可控稳定性检查每 500 步在通用数据集上测 loss防止领域数据导致通用能力崩溃CPT 里最典型的坑就是灾难性遗忘。我第一版 CPT 直接把全部 5GB 医疗领域数据塞进去训练 5000 步结果发现模型在通用知识上的续写质量明显下降。后来调整了语料比例领域数据和通用数据按 7:3 混合同时在训练过程中定期用固定的通用测试集计算 loss一旦发现通用 loss 明显反弹就提前早停。这样做了之后领域能力和通用能力的平衡好很多。2.6 SFT 监督微调把语言模型变成“会听话的助手”预训练和 CPT 做完后你手里的 Xihe 本质上还只是一个“文本接龙模型”。你要输入完整的提示词它才会接着往下写。要让模型真正变成一个能互动的助手必须做 SFT。SFT 的核心实现挺直接在输入序列上做标准语言建模但 loss 只在“助手回答部分”计算指令部分的 loss 要屏蔽掉。如果不屏蔽指令部分的 loss模型会把“如何回答”和“指令模板”混杂在一起导致回答时模板痕迹很重甚至出现“用户问 A模型先复述指令再答 B”的怪现象。我使用 XLNet 风格的 attention mask 不现实所以直接构造 train 数据时把指令部分用-100做 label 屏蔽。每一条 SFT 数据在进入模型前都做这样的预处理“用户指令部分 label 全部为 -100助手回答部分 label 为真实 token”。这一步在代码里是每条样本都要做的不能偷懒。SFT 训练参数我把学习率降到 1e-5 到 2e-5batch size 32训练 3 个 epoch。数据集是 2 万条高质量指令数据单卡 A100 约 3 小时搞定。训练完成后先做一个基础评估拿几个典型的测试指令让模型回答比如“什么是注意力机制”“帮我写一封请假邮件”。如果回答质量仍然乱来优先检查数据质量而不是模型结构。超过 80% 的 SFT 效果差都是因为数据里的答案本身写得就不行。2.7 PEFT 高效微调实操LoRA 与 QLoRA 的选型和实战到这里你已经拥有一个完成 SFT 的 Xihe 模型。如果后续只想做快速领域适配或者显存不够跑全参数微调PEFT 就派上用场了。我在这部分重点实测了 LoRA 和 QLoRA 两种方式。PEFT 的核心思路是冻结绝大部分模型参数只训练少量额外的低秩矩阵。换算成大白话就像一家公司里绝大部分员工保持不变只新招两个“低职级但高杠杆”的助理来调整大方向成本极低但效果突出。以 Xihe 这样 2 亿参数的小模型为例LoRA 需要训练的参数量通常只有总参数的 2% 到 5%。如果你用 QLoRA还会把底座模型量化为 4-bit 存储进一步把显存需求压缩到原来的三分之一以下。我在 Xihe 上走的具体流程是加载 SFT 后的完整权重冻结全部参数在模型的 attention 层的 q、k、v、o 四个投影层上挂 LoRA 适配器LoRA 的 rank 设为 16alpha 设为 32dropout 设为 0.05。然后继续用领域指令微调数据做小规模训练。表格对比一下两种方式的实测效果项目全参数微调LoRAQLoRA可训练参数占比100%约 3.5%约 3.5%训练显存占用2亿模型约 8GB约 6GB约 3GB训练耗时1万条数据60分钟30分钟40分钟效果保持度基准良好良好实测下来的结论对于 Xihe 这种参数量不高的小模型全参数微调当然效果上限最高但 LoRA 把显存和训练耗时打下来的同时效果下降非常有限。如果你的模型本身只有几亿参数而且显卡还算宽裕用全参数 SFT 问题不大但如果你要在 16GB 显存的笔记本显卡上跑整个流程QLoRA 是唯一合理的选择。另外我特别提醒一个细节QLoRA 的 4-bit 量化会带来一定精度损失训练完做生成测试时尽量用合并回 16-bit 之后的模型别直接用量化版做推理否则生成质量会有肉眼可见的下降。2.8 知识蒸馏实战大模型怎么把能力教给小模型做完全部监督训练后模型可能已经不错了但推理速度还是让人不满足。2 亿参数模型虽然不大但在 CPU 上跑还是慢。这时候知识蒸馏就登场了。蒸馏的基本流程是准备一个“教师模型”和一个“学生模型”。教师模型是一个能力更强的大模型我用的是一名开源大模型约 7B 参数或训练效果更好的同结构模型负责产生软标签soft label和中间层表示学生模型就是 Xihe 本身。训练时学生模型同时学习两个目标第一个是拟合真实文本标签也就是传统交叉熵第二个是拟合教师模型输出的概率分布这时计算的是 KL 散度。第二项的核心价值在于“暗知识迁移”——教师模型的概率分布里不仅仅包含正确的词还包含“哪些候选词距离正确词更近、哪些更远”的排序信息这正是小模型自己从纯文本里学不到的。训练时我给 KL 散度loss 的权重设到 0.5用 5 万条蒸馏数据训练 2 个 epoch。实验数据显示采用蒸馏后的 2 亿参数 Xihe在领域问答准确率上比“同结构从头训练但未蒸馏”版本高出约 6 个百分点推理速度与模型大小完全不变。这就是“免费的能力提升”。这里有一个实际的坑蒸馏时的教师模型输出概率分布是动态变化的如果本地显存不够同时加载教师和学生模型可以先把教师模型的输出概率提前离线存储下来训练时直接加载 logits不加载教师模型参数。这样 CPU 都能完成蒸馏。这是我最推荐的一种高效蒸馏执行方案。2.9 DPO 直接偏好优化让模型学会“说人话”最后一块拼图是 DPO。如果是做 RLHF你需要训练一个奖励模型再做强化学习采样流程复杂、调参难、耗时多。DPO 的核心简化是直接把“偏好数据”转成“语言模型的训练目标”不需要训练奖励模型。对于小模型对齐场景DPO 是投入产出比最高的方案。DPO 的数学公式看起来吓人实际理解起来很直观它比较模型对 chosen偏好回答和 rejected非偏好回答的概率差异目标就是“提高 chosen 的概率、降低 rejected 的概率”同时通过 KL 散度惩罚来防止模型跑偏到极端位置。本质上是一次“有方向感的对比学习”。我使用的具体训练配置参数DPO 阶段值备注基础模型Xihe SFT 版本必须是有监督微调过的偏好数据量4000 对质量优先模糊对剔除LoRA 注入是rank8DPO 用 LoRA 更稳学习率5e-6 到 1e-5DPO 对学习率极为敏感beta 温度0.1控制约束强度batch size16太大会让训练不稳定训练轮数2 轮第一轮效果提升最明显实测下来 DPO 的“魔力”非常明显同一批测试问题上SFT 模型回答经常“正确但不讨喜”比如有人问“怎么快速学会编程”SFT 模型可能会说“首先安装 Python其次准备编辑器…”DPO 后的模型则会给出“建议从兴趣出发先确定一个小项目目标再围绕项目学语法和调试……”这种更有“人味”的回复。DPO 最大的坑在于 beta 和学习率的配合。beta 值设太大模型会过度优化偏好对导致单句话反复重复beta 设太小则偏好优化毫无效果。我的经验是从 beta0.1 起步观察训练集上的 DPO loss如果训练过程中 chosen 和 rejected 的 log-probability 之差上升过快说明 beta 偏大应该回调。同时 DPO 阶段的学习率绝不能和 SFT 一样大5e-6 是一个安全起点。2.10 常见问题与排查技巧实录整个“从零训练 Xihe”的流程走下来我积累了下面这些高频问题基本覆盖了大多数人会遇到的坑训练 loss 不下降或者振荡明显。排查顺序先看数据是否被正确加载很多次是数据混入了大量重复文本或空行导致训练信号混乱再看学习率预训练超过 1e-3 基本会炸SFT 超过 5e-5 也会不稳定最后看梯度是否爆掉必要时加梯度裁剪我全程设置了 max_grad_norm1.0。预训练后生成的内容通顺但全是车轱辘话。这是小模型的常见病根源是模型容量不足或者序列长度太短。解决思路把最大序列长度从 512 提到 1024增加训练数据中长文本比例或者微调温度参数生成时 temperature 设到 0.8 到 0.9避免 model 走极端。SFT 做了很久但模型回答还是一本正经地胡说八道。优先检查训练数据的“指令-答案”配对质量。我整理数据时发现很多开源指令数据的“答案”本身就是从原文中复制的根本没有形成合理的回答结构。数据清洗环节增加一步“指令相关性过滤”凡是回答和问题关键词重合度低于 0.3 的直接删除。DPO 训练后模型变敷衍了总用“好的我来帮你”开头。这是 DPO 数据本身出了问题大概率是偏好对里的 chosen 回答模板化严重导致模型把“说套话”当成了最优策略。重新检查你的偏好对数据如果 chosen 回答明显比 rejected 长很多且极度模板化就要重写。蒸馏阶段学生模型 loss 降得很低但生成质量远不如教师。这通常是软标签温度设置太低。蒸馏时教师模型的软标签需要加高温度比如 2.0 或 4.0来放大分布差异否则软标签和硬标签几乎没区别知识蒸馏退化成普通学习。我之前用 1.0 温度蒸馏效果几乎为零调到 3.0 后明显上了一个台阶。2.11 个人体会与扩展建议整个 Xihe 项目做下来我最大的体会是“小模型的上限比你想象的高但下限也比想象的低”。它不会像大模型那样轻轻松松覆盖所有知识但如果你愿意花大量精力打磨数据、精通每一步技术点的细节它完全可以在一个垂直领域做到很可用的水平。如果后续你想把这个流程继续扩展我建议按这三个方向走一是继续扩大领域语料并延长预训练时间看模型能力的增长速度二是在 PEFT 阶段尝试更多适配器组合和 rank 调节找到最省显存但效果最好的配置三是把蒸馏和 DPO 结合起来做“两轮增强”先蒸馏提升基础能力再 DPO 调整表达风格。我自己试过来这两者的叠加效果远比单独使用任何一个更好。最后再分享一个真实心得训练过程中别盯着训练 loss 做判断多拿真实问题做生成测试。我踩过不少“loss 在下降但生成质量一塌糊涂”的坑。把训练指标和人的主观感受结合起来才是语言模型训练最靠谱的验收标准。希望这篇文章能帮你省掉一些弯路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑