
简介面向 DeepSeek 工程化落地的 346 页 PDF 专题资料以持续预训练优化、Prompt-Augmentation 微调、蒸馏压缩与加速协同为主线综合呈现从数据、训练到推理压缩的完整技术闭环适合算法工程师、LLM 应用开发者与模型优化实施者作为项目参考。全文共 75 个大章节支持目录跳转及阅读器书签大纲展示内容深入预训练数据筛选、语料清洗、任务设计、模型架构选型、学习率与批量大小、梯度累积、混合精度、正则化、分布式训练、checkpoint 管理、损失函数、监控体系、硬件适配以及 Prompt Augmentation 提示词设计与微调方法等关键细节便于按需查阅。资源包为 1 个 PDF 文件大小约 14.09MB文字、图表与目录显示正常。已有 88 人学习下载。获取后可得到一套 DeepSeek 深度落地的系统化知识骨架既能用于团队内部技术复盘也可作为个人梳理预训练与微调关键细节的参考资料。1. 这套 DeepSeek 落地全流程先替你省下两个月的试错成本本地部署 DeepSeek 模型早就不稀奇了真正让团队卡壳的是部署之后的那一步模型能跑但回答总差一口气业务词不认、格式不稳定、推理慢到没法上线。于是有人开始重新训练有人疯狂调 prompt有人直接换小模型结果多半是越调越乱。这套「持续预训练优化 → Prompt-Augmentation 微调 → 蒸馏模型压缩-加速协同」的落地流程解决的就是这个断层先用领域数据把模型底子掰正再用增强过的指令样本把输出风格锁死最后用蒸馏把体积和延迟压下来。适合手里已有业务数据、想把 DeepSeek 真正用进生产环境的算法工程师和架构师也适合刚从 API 调用转向私有化部署的团队。下面按我实际做过的方式把每一步怎么选、怎么做、参数怎么定讲清楚。2. 持续预训练优化不是所有场景都需要但要做就得做对数据配比2.1 先判断你的场景值不值得做持续预训练持续预训练Continued Pretraining不是默认选项。它解决的是「领域知识缺失」问题不是「指令遵循能力差」问题。如果你的业务文本里充满了医疗术语、法律条文、代码仓库内部命名模型在通用语料上没见过这些词的上下文关联那 prompt 怎么调都补不回来因为参数里根本没有这个知识。判断信号很简单喂一段你行业的典型文本让模型续写或概括如果它频繁把专有名词写错、改写时丢掉关键实体这就是知识层缺失如果它词都用对了但格式乱、不按指令走那是后面微调的事别用持续预训练解决。一旦确认要做就要接受一个事实持续预训练比微调贵得多。DeepSeek 系列模型从 7B 到几十 B 不等7B 用单卡 A100 还能勉强跑更大的模型就得考虑多卡或量化后训练。我一般会先用 LoRA 做一次小规模试探确认数据确实带来效果提升再决定要不要做全参持续预训练。这个试探最多花半天能筛掉一半以上的无效项目。2.2 数据清洗与配比决定成败的隐藏因素数据质量比训练参数更重要这已经是做持续预训练的共识了。常见做法是准备 100 万到 500 万条领域文本但数量不是关键关键有三点去重、去脏、控比例。去重用 MinHash 或 SimHash 按 n-gram 近似去重能做到 90% 以上的重复文本消除去脏要过滤掉表格乱码、HTML 标签残留、纯符号行控比例是指领域数据与通用数据的配比通常是 7:3 到 9:1通用数据用来缓解灾难性遗忘。下面这个脚本是我经常用的数据配比工具输入是各个来源的原始 JSON 文件输出是训练用的混合数据集# domain_mix.py import json import random from collections import Counter def sample_with_cap(records, cap): if len(records) cap: return records return random.sample(records, cap) def build_mix(domain_files, general_files, domain_ratio0.8, cap200_000): 按 domain_ratio 混合领域数据与通用数据并做随机打乱。 domain_ratio0.8 表示最终数据集中 80% 是领域文本。 domain_records [] for f in domain_files: with open(f) as fp: domain_records.extend(json.load(fp)) general_records [] for f in general_files: with open(f) as fp: general_records.extend(json.load(fp)) domain_records sample_with_cap(domain_records, int(cap * domain_ratio)) general_records sample_with_cap(general_records, int(cap * (1 - domain_ratio))) mixed domain_records general_records random.shuffle(mixed) # 统计配比确认领域数据没有因为清洗过程被过度压缩 total len(mixed) domain_count len(domain_records) print(f混合完成: 总样本 {total}, 领域占比 {domain_count / total:.2%}) with open(pretrain_mix.jsonl, w) as fp: for rec in mixed: fp.write(json.dumps(rec, ensure_asciiFalse) \n) return pretrain_mix.jsonl if __name__ __main__: # 按实际文件路径传入领域文件在前通用文件在后 build_mix( [domain_medical.json, domain_law.json], [general_corpus.json], domain_ratio0.8 )这段代码的核心逻辑是「先按比例采样再打乱」而不是「先合并再截断」。先采样保证领域数据在 cap 限制下不会被通用数据挤掉这一步很容易被忽略很多团队直接把两个文件 concat 后截断结果通用数据因为文件顺序排前面领域数据根本没进训练集。跑完脚本看一眼打印的占比如果和设定值偏差超过 2%回去检查是不是某个源文件里有很多空记录。2.3 学习率与步数的参数基线附经验值持续预训练的学习率要比微调低一个数量级这是最容易被新手忽视的点。全参训练时主流配置是 1e-5 到 2e-5LoRA 可以放宽到 2e-4 到 5e-4。我试过用 1e-4 跑全参到第 200 步 loss 直接冲高然后模型开始输出乱码只能回滚。步数方面没有固定值参考信号是领域数据 loss 和通用数据 loss 的差值如果领域 loss 降了但通用 loss 明显上升说明在遗忘需要增加通用数据回放比例或提前停止。参数全参持续预训练LoRA 持续预训练说明学习率1e-5 ~ 2e-52e-4 ~ 5e-4全参用低 lrLoRA 可略高训练轮数1 ~ 2 epoch2 ~ 3 epoch持续预训练不建议多轮warmup 步数前 3% ~ 5%前 5% ~ 10%让损失平稳下降通用数据回放20% ~ 30%20% ~ 30%低于 10% 会出现严重遗忘批大小按单卡显存最大化按单卡显存最大化建议梯度累积等效 batch 128 以上「通用数据回放」是我反复强调的一个点。持续预训练翻车案例里十有八九是为了省训练时间把回放数据砍到 5% 以下结果模型对通用指令的理解能力断崖式下降连「写一封邮件」这种基础指令都执行不好。稳妥的做法是回放比例固定在 25% 左右每 500 步在保留的验证集上测一次通用能力一旦通用任务准确率下降超过 3 个点立即停止训练或调大回放比例。2.4 训练过程的监控指标与停止条件训练中除了看 loss 曲线还要盯两个额外指标。第一个是「梯度范数」如果连续几十步都超过正常范围一倍以上说明数据里有异常长文本或 label 错误第二个是「领域词命中率」——定期拿 50 条领域文本把专有名词挖掉让模型做填空算一下准确率。loss 降得漂亮但领域词命中率不动说明模型在背句式而不是学知识这是数据配比里通用数据过多导致的现象。停止条件我没有用固定 epoch而是「领域词命中率连续 3 次评估不再上升 通用任务指标不掉点」就停。这比看 loss 更直接因为持续预训练后期 loss 下降非常缓慢曲线几乎是平的但领域能力的提升也几乎没有了继续跑纯属浪费算力。3. Prompt-Augmentation 微调把指令样本做成「高保真数据集」3.1 为什么是 Prompt-Augmentation而不是普通 SFTPrompt-Augmentation 微调简称 PA 微调和普通 SFT 的区别在于SFT 假设你的指令样本已经足够多样模型只要模仿就能泛化PA 微调则主动对指令进行系统性的扩充和变形把每一个业务场景拆成多种问法、多种格式约束、多种边界情况让模型在有限的样本量下看到更大的指令空间。这么做的好处很实际DeepSeek 基座模型在通用指令上已经很强你的微调目标不是让它学会对话而是让它学会「你业务里那几种特定输出格式」PA 通过构造足够多的指令变体把这种格式约束刻进参数里。什么时候必须用 PA 微调当你的业务输出是强结构化的比如必须返回 JSON 字段、必须带特定签名、必须按固定顺序输出多个段落。普通 SFT 样本量不够时模型会「偶尔遵守格式偶尔不遵守」PA 的核心价值就是把这个「偶尔」变成「稳定」。另一个信号是你的输入有多个来源形态比如用户可能粘贴一整段日志、也可能只给一个错误码PA 能把这些输入形态的差异都覆盖到。3.2 构造增强样本集模板变体与负样本策略PA 的落地方式是先写一批种子样本再用代码扩增。种子样本 500 到 1000 条足够扩增目标 5000 到 10000 条。扩增手段有三类同义改写把「请总结」改成「帮我提炼核心信息」、格式变体要求 JSON、要求 Markdown、要求纯文本、输入扰动输入加噪声、截断、换行符异常。负样本也要做故意构造格式错误的输出作为反例让模型知道不该怎么答。有的框架里把这类负样本叫「rejected sample」但在 PA 流程里直接混进训练集不一定效果好更常见的是用 DPO 或 ORPO 来消费负样本如果你只做 SFT负样本的作用主要是控制生成倾向。下面是我经常用的一组扩增代码基于种子样本生成格式变体和问法变体# pa_augment.py import json import random TEMPLATES [ 请把下面的内容总结成 {n} 条要点每条不超过 {m} 字\n{content}, 以下是一段原始信息请提取关键字段并输出 JSON\n{content}, 帮我分析这段内容输出包含【问题】【原因】【建议】三个部分\n{content}, ] FORMAT_HINTS [ 只输出 JSON不要附加任何解释。, 输出 Markdown 格式使用二级标题分节。, 用简洁的条目列表禁止使用表格。, ] def augment_seed(seed_records, n_variants3): 把每条种子样本扩成 n_variants 条变体。 变体数量不宜过大同一条样本超过 5 个变体容易过拟合。 augmented [] for rec in seed_records: content rec[content] target rec[target] for i in range(n_variants): tpl random.choice(TEMPLATES) hint random.choice(FORMAT_HINTS) new_input tpl.format( nrandom.randint(3, 6), mrandom.randint(20, 50), contentcontent ) \n hint augmented.append({ instruction: new_input, output: target }) random.shuffle(augmented) return augmented if __name__ __main__: seeds json.load(open(seed_samples.json)) train_data augment_seed(seeds, n_variants3) with open(pa_train.jsonl, w) as fp: for item in train_data: fp.write(json.dumps(item, ensure_asciiFalse) \n) print(f扩增完成: 种子 {len(seeds)} 条 - 训练样本 {len(train_data)} 条)扩增的核心参数是 n_variants我建议控制在 3 到 4超过 5 会让模型见过太多同一答案的不同问法产生对特定句式的过拟合。另一个关键是 FORMAT_HINTS 要覆盖你业务真实需要的输出格式不要贪多每加一种格式约束其他格式的稳定性就会下降一点。你只需要把生产环境真正会用的格式放进提示词其余格式一律不加。3.3 PA LoRA 的推荐配置PA 微调用 LoRA 就够了不必全参。DeepSeek 7B 全参微调需要至少 4 张 80G 显卡LoRA 单张 80G 就跑得动。我一般的配置是 LoRA rank 64alpha 128dropout 0.05学习率 2e-4训练 3 epoch。相比较而言rank 32 在 5000 条样本上就能看到不低于 rank 64 的效果但 rank 64 的稳定性更好尤其是面对输入格式多变时不容易崩。训练时要把 instruction 和 output 分别做模板包裹常见做法是用 ChatML 格式# chatml_format.py def format_chatml(instruction, output): return ( |im_start|system\n你是一个严格按照要求的助手。|im_end|\n f|im_start|user\n{instruction}|im_end|\n f|im_start|assistant\n{output}|im_end|\n )这个模板里的 system 提示会影响学习效果。如果你在 system 里写「你是一个严格输出 JSON 的助手」那么训练时模型会把 system 和 JSON 输出绑定线上调用时也必须带同样的 system 才能复现效果。所以训练时的 system 内容要和上线时的 system 保持一致这是 PA 微调最容易踩的坑。我只在生产环境用固定的 system 文本训练和推理完全一致实测格式稳定率能提升 10 到 20 个百分点。训练损失要盯着验证集上的「格式正确率」而不是只看 loss。我一般在验证集里放 200 条只改问法不改内容的样本每跑完一个 epoch 就解码一次统计 JSON 可解析率、字段完整率、禁止词出现率。模型 loss 降到 1.0 以下但格式正确率只有 60%说明模型在硬背答案而不是学格式这时候要减少训练轮数或降低学习率。4. 蒸馏模型压缩-加速协同把大模型的知识装进小模型里4.1 蒸馏为什么能压缩又为什么不能完全替代量化模型蒸馏的核心思路是让一个小模型去模仿大模型的输出分布而不是只模仿 argmax 结果。大模型在预测每个 token 时除了正确答案还会给其他候选 token 分配概率这些概率分布里包含了「哪些词是近义候选」「什么风格更自然」这类软知识。小模型从这些软标签里学比自己从硬标签里学更快也更容易达到接近大模型的效果。压缩的比例通常是 3 到 10 倍参数量差距比如拿 7B 蒸馏到 1.5B 或 3B再小就不是蒸馏能补回来的了。这里的误区是「蒸馏可以替代量化」。蒸馏解决的是参数量级问题量化解决的是相同参数量下的内存和计算精度问题。常见落地路径是先蒸馏到小模型再做 INT8 或 INT4 量化两者叠加的加速比才最可观。如果直接对大模型做 INT4 量化虽然也能跑但推理延迟还是受参数量制约硬件门槛降不下来。反过来只蒸馏不量化小模型仍然以 FP16 跑显存占用并不比大模型 INT8 低多少。4.2 教师模型与学生模型的选择边界教师模型直接用上一章微调好的 DeepSeek 模型不需要额外训练。学生模型的选择有讲究同系列的小尺寸模型优先因为 tokenizer 和词表一致蒸馏时 logits 可以直接对齐不需要额外的映射层跨系列蒸馏不是不能做但需要把教师和学生的 vocab 做映射工程成本翻倍。数据方面用「无标签的领域文本 教师生成的答案」组合数量 2 万到 5 万条即可教师批量生成一次缓存成文件后面训练直接复用。生成学生训练数据时教师模型的 temperature 设置很关键。我一般用 0.7 到 1.0temperature 过低会让分布过于尖锐接近硬标签学生学不到软知识过高则产生噪声样本学生学到的内容会偏散。top_p 保持 0.9 左右控制采样范围。每一条教师输出都保留完整 logits 或者 top 50 个 token 的概率值训练时直接用这些概率比用重新采样的文本更稳定。4.3 蒸馏训练循环温度退火与 loss 组合蒸馏的 loss 通常是硬标签交叉熵和软标签 KL 散度的加权和。温度 T 控制软标签的平滑程度T 越高分布越平滑学生能学到更多词间的相似关系。常见做法是训练初期用 T4.0 的大温度让软标签携带足够信息后期把 T 退到 1.0 附近让学生更贴近真实分布。这个温度退火过程不是必须的但做过对比实验的团队都会告诉你固定高温从头训到尾的模型生成质量偏保守经常输出「安全但平庸」的句子。下面是一个简化版的蒸馏训练循环适合在单卡或双卡上跑# distill_loop.py import torch import torch.nn.functional as F def distill_step(student_logits, teacher_logits, labels, T, alpha): 学生模型蒸馏单步更新。 student_logits / teacher_logits 是未归一化的 logits。 alpha0.5 表示软标签和硬标签各占一半权重。 # 软标签 KL 散度除以 T^2 是为了抵消温度放大后的梯度尺度 soft_loss F.kl_div( F.log_softmax(student_logits / T, dim-1), F.softmax(teacher_logits / T, dim-1), reductionbatchmean, ) * (T ** 2) # 硬标签交叉熵 hard_loss F.cross_entropy(student_logits, labels) return alpha * soft_loss (1 - alpha) * hard_loss # 训练时按 step 更新温度 # 总步数 total_steps 由 epoch 和 batch 数算出这里只展示温度退火公式 def get_temperature(step, total_steps, T_max4.0, T_min1.0): 线性退火前 10% 保持高温后 90% 线性降到 1.0。 warmup int(total_steps * 0.1) if step warmup: return T_max ratio (step - warmup) / max(1, total_steps - warmup) return T_max - (T_max - T_min) * ratio参数 alpha 控制软硬标签的权重0.5 是一个稳妥起点。软标签权重太低学生就退化成普通 SFT太高则模型过于在意概率分布的每一个细节但硬标签那一点点「绝对正确」的约束丢失后生成的准确率会掉。温度退火的 warmup 比例 10% 是我试过几次之后固定下来的太短会让模型一上来就接触过度平滑的分布容易不收敛太长则浪费训练时间。单卡 A100 上 3B 学生模型跑 3 万条样本大约 8 到 12 小时能训完你完全有时间在一天内迭代一个版本。4.4 蒸馏与量化的协同顺序先蒸后量量化后必须校准蒸馏结束得到 1.5B 或 3B 学生模型这时候再做 INT4 或 INT8 量化压缩收益最大化。顺序不能反先量化再蒸馏会让学生模型的学习目标被量化误差污染损失很难降下去。量化的关键步骤是校准calibration用一小部分蒸馏训练数据统计激活值的范围确定量化参数。校准数据一般 512 到 1024 条不需要多但必须和业务数据同分布。量化后的效果必须重新评估不能只看显存占用。我见过不止一次量化后生成质量肉眼可见地下降——术语被截断、格式标签丢失、JSON 偶尔少一个括号。这些都是量化误差集中在某些敏感层导致的现象。排查方法是逐层对比量化前后模型的输出 logits找出误差最大的前几个层把这些层保留 FP16 不量化这是 GPTQ 和 AWQ 都支持的混合精度配置。这一步能把量化损失收窄一半以上。4.5 加速收益怎么量化吞吐、时延和首 token 延迟压缩加速的效果不要只看「多少秒生成完」要拆成三个指标吞吐每秒处理多少请求、首 token 延迟用户发出请求到看到第一个字的间隔、端到端延迟完整生成一段回答的总时长。蒸馏主要改善的是第一个和第三个量化主要改善的是第二个和第三个。部署到 vLLM 后用 100 条线上真实请求做压测取 P50 和 P95比单看平均值有价值得多因为平均值会被长文本拖高掩盖短请求性能不足的问题。离线评估时我会额外对比「学生模型 INT8」和「教师模型 FP16」在相同硬件上的显存占用和吞吐。一个典型的收益数据是7B FP16 占 14GB 显存3B INT8 占 3GB 左右吞吐提升 2 到 3 倍端到端延迟降到原来的三分之一。如果硬件是 Jetson Orin 这类边缘设备蒸馏加量化几乎是唯一能跑通 DeepSeek 系模型的路径。5. DeepSeek 全流程落地的 5 个高频坑现象、原因与解决5.1 持续预训练 loss 不降反升还伴随输出乱码现象训练跑了几百步loss 不但没降到初始值以下还出现明显上升生成结果里开始出现重复乱码字符。原因是学习率设得过高加上数据里混入了没清洗干净的超长文本或二进制残留。全参训练用 1e-4 级别的学习率对 7B 以上模型来说几乎必炸。解决把学习率降到 1e-5 或 2e-5warmup 步数扩大到总步数的 5%同时检查数据里是否有超过 2048 token 的异常样本把头部和尾部截掉。5.2 PA 微调后训练集格式全对线上换一种问法就崩现象验证集准确率 95% 以上一上线用户换个词提问模型输出格式立刻走样要么丢了字段要么纯文本不认 JSON。原因是扩增只做了问法模板的排列组合没有覆盖输入内容的「结构变化」——比如训练时内容都是完整段落线上用户粘贴的是截断日志格式完全不同。解决PA 数据里必须混入至少 20% 的「脏输入」样本包括截断文本、存在换行符异常、夹杂代码片段的内容。扩增脚本里加一个 random_truncate 函数把一部分样本随机截到原长度的 60% 到 80%模型才能学会在残缺输入下仍然保持输出格式。5.3 蒸馏温度设得太高学生模型只会说「正确的废话」现象学生模型生成的内容语法完全正确、格式也能对上但信息密度极低句子空泛业务专有名词频繁丢失。原因是蒸馏全程用 T4.0 高温训练软标签过度平滑学生模型把「概率相近的近义词」全部学成了「都可以选」最终选词倾向平均化。解决使用温度退火训练末期温度降到 1.0 到 1.5同时适当提高 alpha 里硬标签的权重到 0.6让学生至少在最关键的事实性输出上严格对齐教师模型的 argmax 结果。另外检查教师生成数据时是不是 temperature 也设得过高建议教师生成时用 0.7 左右不要超过 1.0。5.4 回放数据比例低于 10%通用能力掉点严重现象持续预训练之后领域问答能力提升明显但模型原来会做的通用任务——翻译、代码生成、日常对话——质量明显退化有时连基础的指令遵循都出问题。原因是通用数据回放太少模型在领域数据上训练步数越多对通用分布的覆盖越弱。解决回放比例直接拉到 25% 到 30%不要心疼这点算力成本。如果训练资源紧张导致回放数据只能给 10%那就要把总训练步数砍半用「更少步数 更高数据质量」换平衡。训练过程中每 500 步跑一次通用任务 benchmark掉点超过 3% 立刻停止。5.5 vLLM 部署时显存估算失误导致 OOM 或启动失败现象vLLM 启动时直接报 CUDA out of memory或者启动成功后第一条请求就报「request extension preparation failed」这类运行时错误。原因是显存估算只算了权重本身没算 KV cache 和中间激活。7B FP16 权重约 14GB但 KV cache 按 4096 上下文算还要额外 4 到 8GB 显存加上 CUDA context 和激活单卡 24GB 其实非常紧张。解决用 vLLM 的--gpu-memory-utilization参数把 GPU 内存利用率限定到 0.85 到 0.9留给运行时必要的缓冲同时调低--max-model-len到 4096 或 2048避免默认值占用过多 KV cache 空间。请求报错时优先查输入 token 是否超过 max-model-len这是生产环境最常见的问题。6. 蒸馏产物上线前的最小验证流一个 40 分钟跑完的验收清单拿到蒸馏加量化后的模型不要直接部署就完事先用一个 40 分钟的验证流过滤掉大部分雷。第一步用 vLLM 把模型拉起来暴露一个 OpenAI 兼容接口这和你调用 DeepSeek 官方 API 的方式完全一致方便后续把模型平滑接入现有的 codex、VS Code 或企业微信机器人工作流。启动命令我一般这么写vllm serve ./deepseek-3b-distilled-int8 \ --served-model-name deepseek-3b \ --max-model-len 4096 \ --gpu-memory-utilization 0.88 \ --quantization awq启动后先跑 10 条「训练集里没有见过的输入变体」重点看 JSON 可解析率和关键字段完整率。第二步跑 10 条长文本输入长度分别取 500、1000、2000、4000 token确认模型在长上下文下不会丢格式。第三步对比教师模型和学生模型对同一批输入的语义相似度可以用 embedding 余弦相似度也可以用人工抽检。三个步骤全过再考虑接流量。如果 JSON 字段偶尔丢失不要急着重新训练先在 system prompt 里把 required 字段显式列一遍成本最低。最后说一个我自己的教训第一次做蒸馏时我为了压显存把量化位宽从 INT8 降到 INT4结果专有名词错误率直接翻倍。后来才想明白小模型本身容量就有限再压精度就是把仅剩的容量也牺牲掉。现在我的默认策略是「3B 以下不压 INT4只压 INT83B 以上才考虑 INT4」。如果你也在这个流程里摸索建议先把每一步的验证集固定下来每次改动都跑同一套口径不然你根本分不清效果变化到底来自数据、参数还是玄学。希望帮到你。本文还有配套的精品资源点击获取