Browse this library

Chinese source article

02 · DeepSeek-Coder 技术报告详解

DeepSeek-Coder 用仓库级数据组织, 50% PSM 形式的 FIM 和 16K 长上下文训练, 把代码生成, 中间补全与跨文件补全放进同一组 Dense 模型.

View original Markdown ↗

论文 DeepSeek-Coder: When the Large Language Model Meets Programming - The Rise of Code Intelligence 于 2024 年 1 月公开, 代码与模型见 deepseek-ai/DeepSeek-Coder. 报告给出 1.3B, 6.7B, 33B 三档 Dense 模型的 Base 与 Instruct 版本. 训练语料共 2T token, 覆盖 87 种编程语言, 上下文可靠使用范围为 16K. 这组模型同时面向从左到右生成, Fill-in-the-Middle(FIM)和跨文件补全, 因而数据的组织单位比单文件代码模型更接近真实仓库.

1. 仓库级语料改变了模型看到代码的方式

DeepSeek-Coder 训练的基本单位不只是单个文件, 还包括仓库依赖和跨文件上下文.

1.1. 87/10/3 配比先确定能力边界

预训练数据按 87% 源码, 10% 代码相关英文自然语言, 3% 非代码中文划分. 代码相关自然语言包括 GitHub Markdown 与 StackExchange 内容. 这部分数据承担需求说明, API 解释和错误讨论等语义, 让模型不只学习语法序列. 3% 中文维持中文输入的基本可用性, 但占比不足以把模型变成均衡的通用双语基座. 这也解释了后续 v1.5 为什么要从通用语言模型继续训练.

源码取自 2023 年 2 月以前的公开 GitHub 仓库. 清洗规则会删除平均行长超过 100, 最大行长超过 1000, 字母占比低于 25% 的文件. HTML, JSON, YAML 另有可见文本和长度条件, 用来排除压缩产物, 自动生成数据和大量结构化记录. 过滤后语料体积约为原始集合的 32.8%, 最终约 797.92 GB, 约 6.03 亿个文件. 数量减少并不能单独证明质量提升, 但 Table 3, Table 4 与跨文件消融共同给出了训练结果侧的支持.

去污染使用测试集文本的连续 10-gram. 与 HumanEval, MBPP, GSM8K, MATH 测试样本共享 10-gram 的训练内容会被剔除, 更短且不少于 3-gram 的测试文本采用精确匹配. 这种规则降低直接重复的风险, 无法排除题目改写, 同源解答或公开题库的间接重合. 报告在 LeetCode Contest 的月份分布上观察到异常高分, 因此没有把近重复过滤描述成绝对无污染.

1.2. 依赖排序让同仓文件形成可学习的上下文

单文件预训练把每个源文件视为独立样本, 但真实代码中的类型, 函数和配置常定义在别处. DeepSeek-Coder 用正则表达式抽取 Python import, C# using, C/C++ include 等关系, 把仓库表示成文件依赖图. 若文件 uu 被文件 vv 引用, 排序时尽量让 uu 先于 vv 出现. 每个文件前还加入路径注释, 使同名文件和目录层级在拼接后仍可区分.

标准拓扑排序只选择入度为 0 的节点, 遇到循环依赖会停住. 报告的 Algorithm 1 改为每轮选择当前入度最小的节点, 即使依赖图含环也能继续生成顺序. 不连通子图分别排序后再拼接. 这个顺序不保证等于构建系统的加载顺序, 也不表示模型执行了静态分析; 它只提高定义出现在使用位置之前的概率. 仓库级近重复去重也在这个阶段进行, 避免文件级去重从一个仓库中任意抽走若干文件, 破坏剩余依赖上下文.

跨文件价值由 CrossCodeEval 的消融给出. 评测将总输入限制为 2048 token, 输出上限为 50 token, 检索到的跨文件上下文最多占 512 token, 检索结果来自官方 BM25. 只用文件级语料训练后, Java, TypeScript 和 C# 的 exact match 与 edit similarity 出现下降. Python 的变化没有被报告概括为同样稳定的下降, 因而合理结论是仓库级构造对多种跨文件任务有效, 不能推广成所有语言和所有仓库布局都等量受益.

1.3. 仓库序列仍受上下文窗口和检索器约束

把仓库排成序列并不等于把整个仓库都放进一次推理. 预训练样本仍要切成固定长度, 超过窗口的文件会分散到不同样本. CrossCodeEval 还依赖外部 BM25 先挑出 512 token 的相关上下文. 模型处理的是检索器交付的片段, 因此最终准确率同时受检索召回, 文件排序与生成能力影响.

仓库级语料真正改变的是条件分布: 模型更经常在函数调用前见到定义, 在当前文件前见到依赖文件, 并通过路径注释区分模块. 这种训练分布更适合跨文件补全, 但不能代替编译器, 符号索引或动态执行. 对大型单体仓库, 16K 窗口仍只覆盖局部区域; 对依赖关系无法由正则识别的语言, 排序质量也会下降.

2. FIM 与自回归训练如何共用一个模型

2.1. PSM 把双侧条件改写成单向预测

常规 next-token prediction 只根据左侧序列预测下一个 token. 编辑器补全经常同时拥有光标前后的代码, 目标是生成中间缺口. FIM 将文档随机切成前缀 fpref_{pre}, 中间 fmiddlef_{middle} 与后缀 fsuff_{suf}, 再按 Prefix-Suffix-Middle(PSM)重排:

⟨∣fim_start∣⟩fpre⟨∣fim_hole∣⟩fsuf⟨∣fim_end∣⟩fmiddle⟨∣eos∣⟩\langle|\mathrm{fim\_start}|\rangle f_{pre}\langle|\mathrm{fim\_hole}|\rangle f_{suf}\langle|\mathrm{fim\_end}|\rangle f_{middle}\langle|\mathrm{eos}|\rangle

哨兵 token 划分三个片段, decoder-only Transformer 仍执行相同的 next-token prediction. 当模型开始生成 fmiddlef_{middle} 时, 前缀和后缀已经位于注意力可见范围内. PSM 因而没有新增双向编码器或另一套损失, 代价是改变了一部分训练序列的自然顺序. FIM 在文档级, packing 之前施加, 避免一个缺口跨越两份无关文档.

模型每次只见一种排列. 50% FIM 率表示约一半文档被改写为 PSM, 其余文档保持自然的从左到右顺序. FIM 训练得到的是根据双侧文本生成缺口的能力, 并不保证类型正确, 测试通过或跨文件符号可达. 这些属性仍取决于数据, 上下文和解码结果.

2.2. 50% 来自补全能力之间的消融

报告用 1.3B 架构和 Python 子集比较 0%, 50%, 100% FIM, 以及 50% Masked Span Prediction(MSP). HumanEval-FIM 随机遮住参考解答的一行, 用 exact match 衡量恢复结果. 100% FIM 在该填空任务上最好, 却得到最弱的常规代码补全表现. 训练分布完全改为重排序列后, 自然顺序生成得到的样本减少, 这项结果显示两类目标存在实际竞争.

50% PSM 比 50% MSP 更均衡, 所以最终配置采用 50% PSM. MSP 会遮蔽多个跨度并重建, 与单个光标缺口的产品输入不同. 消融只使用 1.3B 模型与 Python 子集, 能支持训练策略选择, 不能证明 50% 对所有尺寸, 语言与编辑场景都是全局最优. Table 6 的多语言单行 FIM 结果进一步表明该配置可以迁移到 Java 和 JavaScript, 但仍限于单行 exact match.

2.3. 模型尺寸与注意力实现服务于不同部署点

三档模型都使用 decoder-only Transformer 与 RoPE. 33B 模型采用 GQA, group size 为 8, 并使用 FlashAttention v2. GQA 让多个 query head 共享较少的 key/value head, 降低 Decode 阶段 KV cache 体积; FlashAttention v2 改变注意力计算的内存访问方式, 不改变注意力函数本身. 1.3B 与 6.7B 面向更低成本补全, 33B 提供更高生成能力.

Table 2 给出的最大学习率随规模下降: 1.3B 为 5.3×10−45.3\times10^{-4}, 6.7B 为 4.2×10−44.2\times10^{-4}, 33B 为 3.5×10−43.5\times10^{-4}. 优化器为 AdamW, β1=0.9\beta_1=0.9, β2=0.95\beta_2=0.95. 学习率分三阶段衰减, 先 warm-up 2000 步, 每阶段末降到上一阶段的 1/10\sqrt{1/10}, 最终约为初始值的 10%. 报告给出 HAI-LLM 框架及 A100, H800 集群, 但没有给出可复算的总 GPU 时或能耗, 因此无法从文中比较三档模型的完整训练成本.

3. 长上下文与指令微调各自补哪一块

64K 是位置扩展的理论范围, 报告明确验证的可靠输出范围是 16K.

3.1. RoPE 线性缩放的可靠范围是 16K

仓库代码需要更长输入, DeepSeek-Coder 将 RoPE scaling factor 从 1 调到 4, base frequency 从 10000 调到 100000, 再以序列长度 16K, batch size 512 训练 1000 步. 这项改动重新分配位置旋转频率, 让模型接触扩展后的相对位置范围. 理论长度可到 64K, 报告明确把可靠输出范围限定在 16K.

理论位置上限与实际可用上下文不是同一个指标. 模型在某个长度能够接收输入, 不代表能稳定检索任意位置的信息, 也不代表能理解完整仓库依赖. 报告没有给出覆盖 64K 的检索曲线或长代码修复结果. 因此部署时应把 16K 当作已训练和观察过的工作区间, 不应把 64K 当作等质量承诺.

3.2. Instruct 阶段把代码基座变成问答接口

Instruct 模型从对应 Base checkpoint 出发, 使用 Alpaca 格式的高质量指令数据, 多轮对话之间用 <|EOT|> 分隔. 微调采用 cosine 学习率调度, warm-up 100 步, 初始学习率 1×10−51\times10^{-5}, batch 约 4M token, 总训练量约 2B token. 指令微调使模型学习按问题描述生成完整答案, 解释代码并维持多轮上下文.

报告没有公开指令数据的完整组成与质量筛选细节. 因此 Instruct 成绩的来源不能全部归因于预训练语料或模型尺寸. Base 与 Instruct 在 Table 3 中承担不同口径: Base 更适合衡量预训练后的代码分布建模, Instruct 更适合自然语言任务描述下的生成. 两者横向比较时必须保持 prompt 与解码设置一致.

3.3. v1.5 验证了通用基座的迁移价值

DeepSeek-Coder-v1.5 7B 不从零训练, 而是从 DeepSeek-LLM-7B Base 继续训练 2T token. 新配比为 70% 源码, 10% Markdown 与 StackExchange, 7% 代码相关自然语言, 7% 数学, 6% 中英双语. 它只使用 next-token prediction, 上下文为 4K, 没有沿用 FIM.

Table 10 中, HumanEval 从原 6.7B Base 的 44.7% 变为 43.2%, 代码略有回落; GSM8K 从 43.2% 升到 62.4%, MMLU 从 36.6% 升到 49.1%. 结果说明通用语言和数学基座能改善代码指令所依赖的题意理解, 同时更均衡的数据会分走纯代码容量. v1.5 没有 FIM, 也只有 4K 上下文, 因而不能取代原系列在编辑器中间补全和长仓库输入上的定位.

4. 评测数字回答了哪些问题

4.1. 生成成绩显示数据效率, 不能单独证明仓库理解

Table 3 使用 greedy 解码. DeepSeek-Coder-Base 33B 的多语言 HumanEval 平均为 50.3%, MBPP 为 66.0%; CodeLlama-Base 34B 对应为 41.0% 与 55.2%. 6.7B Base 的多语言平均为 44.7%, MBPP 为 60.6%, 在这组设置下超过 CodeLlama-Base 34B. 这说明数据构造和训练方案让较小模型获得较高的函数级生成分数, 但 HumanEval 与 MBPP 主要测试短函数, 不直接覆盖仓库依赖.

Instruct 33B 在 Python HumanEval 为 79.3%, 多语言平均 69.2%, MBPP 为 70.0%. 报告复现的 GPT-3.5-Turbo 对应为 76.2%, 64.9%, 70.8%, 因此 DeepSeek-Coder 在前两项领先, MBPP 略低. GPT-4 仍更高. DS-1000 测试数据科学库调用, Base 33B 总平均 40.2%, 高于 CodeLlama-Base 34B 的 34.3%, 补充了标准算法题之外的库调用证据.

4.2. LeetCode 与数学结果需要保留评测边界

LeetCode Contest 收集 2023 年 7 月至 2024 年 1 月的 180 道题, 每题约 100 个测试用例. Instruct 6.7B 与 33B 的 Pass@1 分别为 19.4% 和 27.8%, 33B 加 CoT 后为 28.9%. GPT-3.5-Turbo 为 23.3%, GPT-4-Turbo 加 CoT 为 41.8%. 33B 超过该表中的 GPT-3.5-Turbo, 与 GPT-4-Turbo 仍有明显差距.

DeepSeek-Coder 与 GPT-4-Turbo 在 7 月、8 月场次的得分偏高. 题目发布时间晚于主要数据截止时间, 直接收录的概率较低, 但提前讨论、镜像题库和相似题仍可能造成数据重合. 因此 LeetCode 数字可以衡量竞赛代码生成能力, 无法单独证明评测数据完全无污染.

PAL 评测要求交替写自然语言步骤和可执行代码. Base 33B 在七项数学任务上的平均为 65.8%, GSM8K 为 60.7%, MATH 为 29.1%. 这些结果衡量借助程序执行的数学求解, 与纯自然语言推理口径不同. 运行代码承担精确计算后, 得分同时反映题意转写和程序生成能力.

4.3. 补全结果把模型定位落到具体工作流

Table 6 的单行 FIM 中, Base 33B 均值为 81.2%, 6.7B 为 80.7%. 两者相差很小, 所以报告建议代码补全优先选择 6.7B. 该建议建立在单行 exact match 上: 对低延迟 IDE 补全很有参考价值, 对多文件修复, 长函数生成和交互式解释则不能直接外推.

CrossCodeEval 把仓库级训练, 外部检索与短补全放在同一条链路中. 仓库级消融在 Java, TypeScript, C# 上下降, 支持依赖排序的作用; 但输入只给 512 token 跨文件片段, 结果仍受 BM25 召回限制. DeepSeek-Coder 留下的工程结论很明确: 训练时保留仓库结构能改善跨文件条件建模, 部署时仍需要检索器和可控上下文, FIM 比例则要在左到右生成与双侧补全之间通过消融选择.

仓库排序的收益来自训练条件分布. 路径注释保留模块身份, 依赖图让定义更可能出现在调用之前, packing 再把排序后的内容切成定长样本. 模型没有接收显式依赖边监督, 学到的仍是 token 相关性. 反射, 宏展开, 运行时导入和生成代码无法由简单正则完整识别. 对这些项目, 仓库序列只是近似视图, 不能取代语言服务器或构建系统.

文件级去重和仓库级去重的风险不同. 文件级方法能去掉模板与复制代码, 也可能从一个项目中只删定义文件而留下调用文件. 仓库级方法保住项目内部关系, 却可能保留不同仓库之间的共享组件. 报告没有公开相似度阈值, 各语言保留率与哈希实现, 因而公开材料只能复述设计原则, 无法精确重建 2T token 训练集.

FIM 控制符也是部署协议的一部分. <|fim_start|>, <|fim_hole|> 与 <|fim_end|> 必须由训练时同一分词器识别, PSM 次序也必须一致. 若产品侧遗漏结束标记, 交换前后缀或截断后缀, 输入就偏离训练分布. 模型生成中间片段后还要在结束 token 或长度上限处停止, 否则可能重复后缀. Table 6 的单行 exact match 没有覆盖这些集成错误.

100% FIM 在填空任务最高而左到右补全最低, 说明 FIM 并非免费的附加能力. 重排序列占比越高, 模型看到自然代码顺序的机会越少. 0% FIM 缺乏双侧条件, 50% PSM 则让两种输入都保留足够样本. 该比例在 1.3B Python 子集上选择, 后续多语言结果证明可用, 但没有证明对所有模型规模和编辑器分布均为最优.

33B 的 GQA 与 FlashAttention v2处理不同瓶颈. GQA 让多个 query head 共享 key/value head, 减少 Decode 时随历史长度增长的 KV cache. FlashAttention v2 改善 Prefill 注意力计算的内存访问, 不改变注意力函数. 报告没有给端到端吞吐或显存曲线, 所以结构上可以确认状态和访存更省, 不能补写具体加速倍数.

RoPE 线性缩放同样需要区分可表示长度与可靠长度. scaling factor 和 base frequency 改变位置旋转频率, 追加 1000 步让参数适应新位置分布. 训练序列只有 16K, 报告也把可靠输出限定在 16K. 64K 是位置参数允许的理论范围, 没有同等的训练覆盖和检索曲线. 仓库输入超过 16K 时, 先检索相关文件通常比无选择地拼接更符合报告的证据.

学习率与规模共同控制稳定性. 1.3B, 6.7B, 33B 的峰值学习率依次降低, 三阶段调度在 2000 步 warm-up 后逐段衰减. 这些数字必须与 batch, token 总量和架构一起使用. 报告没有提供随机种子, 多次训练方差与总 GPU 时, 因而表内差距不能换算为完整训练成本, 小百分点差距也没有置信区间.

Alpaca 指令格式和 <|EOT|> 主要改变交互接口. 约 2B token 的监督微调相对 2T 预训练很小, 但标签集中在回答顺序, 终止位置和指令遵循, 所以行为变化明显. 指令数据组成没有完整公开, Instruct 提升不能全归因于预训练语料. Base 与 Instruct 的成绩也应在相同 prompt 和解码设置下比较.

v1.5 提供另一条能力分配曲线. 它继承通用 7B 基座, 增加数学和中英数据, GSM8K 与 MMLU 提升, HumanEval 略降. 同时它没有 FIM, 上下文只有 4K. 因而代码模型不存在一个覆盖所有工作的单一排名: 函数生成, 数学, 通用语言, 双侧补全与长上下文分别由不同数据和目标控制.

四组主要代码评测也不能互相替代. HumanEval 和 MBPP 侧重短函数, DS-1000 增加真实库调用, 单行 FIM 测双侧精确补全, CrossCodeEval 加入跨文件检索. 报告没有覆盖从 issue 定位到多文件 patch 和测试验证的完整流程. 案例图展示模型能完成某些交互, 不能替代总体成功率.

LeetCode Contest 使用较新题目和每题约 100 个测试, 仍观察到 7 月和 8 月分数偏高. 时间切分能减少直接收录, 不能排除镜像, 提前讨论和同构题. CoT 把 33B 从 27.8% 提到 28.9%, 提升幅度有限, 且仍低于 GPT-4-Turbo 加 CoT 的 41.8%. 该表支持竞赛生成能力, 不支持完全无污染的断言.

许可允许研究和商业使用, 模型权重仍受 DeepSeek Model License 约束. 公开代码训练也不保证输出没有许可证或安全风险. 近重复去重服务于数据质量和污染控制, 不是输出归属检测. 实际部署仍要执行测试, 静态扫描, 依赖审查和许可证检查, 这些步骤不在论文准确率指标内.

从模型选择看, 6.7B 与 33B 的差距随任务变化. 单行 FIM 均值只差 0.5 个百分点, 短函数与竞赛题差距则更明显. 这说明补全产品的约束不是只追求最高离线准确率, 还要考虑 Decode 延迟, KV cache, 并发和候选刷新频率. 报告据此推荐 6.7B 用于补全, 该结论比笼统选择最大模型更贴合 Table 6.

CrossCodeEval 的 BM25 结果也揭示检索与生成的职责边界. 检索器负责从仓库挑选可能相关的文件片段, 模型负责结合当前文件生成最多 50 token. 如果相关定义没有进入 512 token 跨文件预算, 模型没有信息恢复它; 如果检索命中而模型仍生成错误, 才更接近条件建模问题. 评测只报告端到端分数, 没有拆分检索上限与生成上限.

DS-1000 覆盖 NumPy, Pandas, SciPy 等数据科学库, 与 HumanEval 的自包含函数不同. Base 33B 总平均 40.2% 高于 CodeLlama-Base 34B 的 34.3%, 说明训练数据中的代码相关文本和真实 API 使用模式提供了额外收益. 该分数仍低于可以直接查文档并执行调试的完整 Agent, 因为评测模型只按给定 prompt 生成答案.

程序辅助数学把自然语言步骤与代码执行交替组织. Base 33B 的七项平均 65.8% 表示模型能把部分算术和符号操作转写为程序, 并借助执行避免手算误差. 该设置不等于模型内部独立完成全部推理, 也不能与无工具数学成绩直接比较. v1.5 的数学提升进一步说明代码能力与数学数据能互相支持, 但评测协议必须保留.

报告中的训练路线依次处理四类约束: 按仓库保存文件结构, 混合 FIM 与自然顺序样本, 修改位置参数并续训长上下文, 再用指令数据调整交互方式. 各机制作用范围不同: 仓库排序保留文件关系, RoPE 扩展增加位置容量, FIM 学习中间补全, SFT 改变响应格式. 代码正确性和编译验证仍需另外的执行反馈, 离线指标也应对应到各自覆盖的环节.

对于复现工作, 最可核对的材料是 Table 1 的语言集合与过滤条件, Algorithm 1 的依赖排序, Figure 3 的 FIM 消融, Table 2 的架构和学习率, 以及 Table 3 至 Table 10 的任务口径. 训练语料本身, 质量模型, 编译过滤器和指令集没有完整发布, 所以能够复现的是公开算法与评测流程, 不是逐 token 相同的模型. 这一边界也解释了为什么仅复制 87/10/3 比例不足以重现成绩.

5. 从 GitHub 文件到训练序列

数据漏斗改变的远不只是规模: 官方仓库把数据构造概括为四步: 收集并沿用 StarCoder 过滤规则, 解析仓库内文件依赖, 拼接依赖文件并做仓库级 MinHash 去重, 再过滤语法错误和可读性差的代码. 这条顺序把原始 GitHub 快照逐渐变成语言模型样本. 每一步都在修改经验分布: 文件过滤决定哪些语言形态可见, 依赖排序决定定义与调用的相对位置, 去重决定模板代码的权重, packing 决定哪些 token 能在同一注意力窗口中相遇.

假设原始仓库集合为 R\mathcal R, 仓库 rr 内有文件 FrF_r. 过滤器 q(f)∈{0,1}q(f)\in{0,1} 决定文件是否保留, 依赖排序 πr\pi_r 给出文件排列, tokenizer τ\tau 把字符串映射成 token. 一个仓库样本可抽象为

Xr=τ ⁣(concat⁡f∈πr(Fr),q(f)=1[path⁡(f),text⁡(f)]).X_r=\tau\!\left(\operatorname{concat}_{f\in\pi_r(F_r),q(f)=1} [\operatorname{path}(f),\operatorname{text}(f)]\right).

训练器再把 XrX_r 切成不超过窗口的片段. 这个表达式揭示三个损失点: 被过滤文件的信息完全消失; 依赖图只能近似真实构建关系; 超长仓库会在切片边界断开. 「项目级语料」表示项目结构参与样本构造, 不表示每次输入包含完整项目.

平均行长、最大行长和字母比例适合剔除压缩文件、机器生成 blob 与二进制误判, 也可能误伤 minified JavaScript、数值表、汇编和符号密集的形式化证明. HTML、JSON、YAML 的单独规则说明统一阈值并不适合所有语言. 若某种语言天然含较多符号或超长生成行, 过滤后的 87 种语言列表只说明 tokenizer 和数据流水支持它, 没有说明每种语言数据量充足.

数据从原始集合缩到 32.8%, 不能把被删除的 67.2% 全叫作垃圾. 其中可能包括近重复、生成文件、许可证文本、格式异常和真实但不符合阈值的代码. 质量变化应由固定算力对照训练验证. 论文的下游成绩说明最终语料有效, 没有逐项消融每条过滤规则. 如果另一个复现只照抄阈值, 源仓库时间、语言构成和去重实现不同, 结果仍会显著偏移.

5.1. 依赖图、环与排序误差

令有向图 Gr=(V,E)G_r=(V,E) 中每个顶点是一份文件. 若文件 vv 引用文件 uu, 记边 u→vu\to v, 目标是让 uu 尽量排在 vv 前. 无环图可用 Kahn 算法反复取入度为零的顶点. 实际仓库常有循环引用, DeepSeek-Coder 的算法每轮选当前入度最小的顶点, 删除后继续. 这等价于用局部启发式打破环, 保证每个文件最终都有位置.

设排列 π\pi 的违背边数为

V(π)=∑(u,v)∈E1[π(u)>π(v)].V(\pi)=\sum_{(u,v)\in E}\mathbf 1[\pi(u)>\pi(v)].

理想排序希望 V(π)V(\pi) 小, 在含环图中不可能为零. 最小入度启发式没有给出全局最优保证, 优点是便宜且适合海量仓库. 不连通分量分别排序再拼接, 其相对顺序没有依赖依据. 当仓库超过 16K, 哪些分量恰好落在同一片段会影响训练条件.

正则解析只能覆盖显式 import、include、using 等模式. Python 动态导入、C/C++ 宏条件、Java 反射、插件注册、生成代码和配置驱动依赖可能漏掉; 同名模块与别名又会制造假边. 依赖排序的作用因此是提高统计命中率, 不是恢复编译器语义. 一个可证伪实验应比较正则图、语言服务器图、随机文件顺序和目录顺序, 固定仓库、token 与模型, 再按真实跨文件引用类型分组报告 CrossCodeEval.

路径注释承担轻量命名空间. src/parser.py 和 tests/parser.py 内容相似, 路径能提示职责; 目录层级也让模型学到常见工程组织. 路径仍是普通 token, 没有独立图编码. 仓库重命名、monorepo 深目录和生成临时路径会带来噪声. 若评测 prompt 使用与训练完全不同的路径格式, 模型利用该信号的能力可能下降.

仓库级去重与泄漏: MinHash 用 shingle 集合的 Jaccard 相似度近似判断重复. 对仓库 ri,rjr_i,r_j, 可把规范化文件内容或拼接序列映射为集合 Si,SjS_i,S_j, 比较

J(ri,rj)=∣Si∩Sj∣∣Si∪Sj∣.J(r_i,r_j)=\frac{|S_i\cap S_j|}{|S_i\cup S_j|}.

仓库级去重能够识别 fork 和大面积复制项目, 又避免从单个仓库中零散删除定义文件. 若两个仓库共享大型第三方目录但业务代码不同, 仓库相似度可能过高; 若 fork 只改了大量生成文件, 也可能逃过阈值. 官方没有公开 shingle、阈值和 canonical repo 选择策略, 第三方无法逐仓复原.

文件级和仓库级去重可以组合, 目标却有冲突. 文件级去重降低许可证、模板、vendored library 的重复权重; 仓库级保留项目内部自洽性. 一个折中方案是在仓库内保留文件, 训练采样时降低跨仓重复组件权重. 论文公开流程没有描述这种加权, 不能假定采用. 2T token 是处理后训练量, 也不等于 2T 独特语义 token.

测试污染用连续 10-gram 与短文本精确匹配筛查. 对 token 或字符序列 bb, 若训练文档含测试样本任一连续窗口, 就剔除匹配内容. 长 nn 降低误报, 对变量改名、翻译、注释改写和算法同构不敏感. 代码题还可能以题解、测试、函数签名或博客形式出现, 只比较题面会漏检. 时间切分补了一层证据, 仍挡不住提前泄露和历史同题.

污染检查应同时覆盖 prompt、canonical solution、测试样例和语义近邻. 最强证据来自私有新题或发布后题目, 配合执行测试; 公开题库只做字符串去重只能降低风险. LeetCode 月份异常正是反例: 即使主要数据截止早于比赛, 某些月份仍显著偏高. 这可能来自难度变化、相似题、数据泄漏或采样噪声, 无法仅凭曲线确定原因.

/10/3 混合怎样分配梯度: 若总 token 数为 D=2×1012D=2\times10^{12}, 名义配比对应代码约 1.74T, 代码相关自然语言约 0.20T, 中文约 0.06T. 实际第一阶段是 1.8T token、第二阶段长上下文 0.2T token; 若两阶段都维持同一比例, 才能直接按总量相乘. 论文给出总体和训练流程, 未逐项公布长上下文阶段的分域计数, 所以上述数字是配比换算, 不代表日志精确值.

混合训练目标可写成

L(θ)=0.87 Ex∼Pcodeℓ(x)+0.10 Ex∼Pdocℓ(x)+0.03 Ex∼Pzhℓ(x).\mathcal L(\theta)=0.87\,\mathbb E_{x\sim P_{code}}\ell(x)+0.10\,\mathbb E_{x\sim P_{doc}}\ell(x)+0.03\,\mathbb E_{x\sim P_{zh}}\ell(x).

系数按 token 采样, 不按文档或字节. Markdown 的自然语言 token 密度、代码 tokenizer 压缩率和中文切分不同, 相同原始字节会贡献不同 token 数. 87% 代码使模型把大部分容量用于语法、API 与项目模式; 13% 语言让它理解描述和注释, 但不能期待与通用 LLM 相同的百科覆盖.

数据域还会通过共享参数互相迁移. API 文档能帮助库调用, StackExchange 问答能提供错误原因, 数学文本能帮助竞赛题意. 同时存在容量竞争: v1.5 增加通用与数学比例后 MMLU、GSM8K 上升, 原始 HumanEval 略降. 这是一条观察, 不能证明每增加一个数学 token 就挤走一个代码 token 的收益. v1.5 的初始化、FIM、上下文和数据配方都变了.

5.2. 自回归与 FIM 的统一概率模型

标准目标的监督密度: 对 token 序列 x1:Tx_{1:T}, decoder-only 模型最小化

LAR(x)=−∑t=1Tlog⁡pθ(xt∣x<t).\mathcal L_{AR}(x)=-\sum_{t=1}^{T}\log p_\theta(x_t\mid x_{<t}).

代码中的缩进、标识符、字符串与注释都进入同一类别预测. 训练器不知道某个 token 是否编译关键; 一个分号和一段许可证文本都贡献交叉熵. 数据过滤和采样由此格外重要, 它们决定算力花在哪些模式上. 从头训练表示模型参数不继承通用 checkpoint, tokenizer、结构和全部 2T token 共同建立代码分布.

自回归 loss 降低不保证程序通过. 设生成程序 yy 的语法、类型、测试正确性事件分别为 S,T,ES,T,E, 语言模型优化 p(y∣x)p(y|x), benchmark 关心 Pr⁡(S∩T∩E)\Pr(S\cap T\cap E). 高频但错误的实现仍可能有高概率; 多种语义等价实现又会被 exact match 判错. 执行测试把评价从字符串概率推进到行为, 训练阶段却没有公开的大规模执行反馈目标.

PSM 变换的概率分解: 原文档切为前缀 PP、中段 MM、后缀 SS, 自然顺序联合概率为

p(P,M,S)=p(P) p(M∣P) p(S∣P,M).p(P,M,S)=p(P)\,p(M\mid P)\,p(S\mid P,M).

PSM 重排后模型学习

p(P,S,M)=p(P) p(S∣P) p(M∣P,S),p(P,S,M)=p(P)\,p(S\mid P)\,p(M\mid P,S),

哨兵 token 告诉模型片段角色. 真正服务补洞的是末项 p(M∣P,S)p(M\mid P,S), 前两项仍产生训练 loss. 后缀在缺少中段时并非自然代码连续, 模型需要学习跨哨兵的协议. FIM 能复用因果 decoder, 代价是部分 token 用于学习重排格式.

若切点 i<ji<j, P=x1:i,M=xi+1:j,S=xj+1:TP=x_{1:i},M=x_{i+1:j},S=x_{j+1:T}. 切点采样决定缺口长度分布. 短缺口偏向单行补全, 长缺口要求函数级规划; 靠近边界的缺口几乎退化为续写. 论文说明文档级 PSM 与 FIM rate, 没完整公开所有切点分布细节. HumanEval-FIM 遮一行, 因而更直接评价短缺口.

FIM 在 packing 前施加十分关键. 若先把无关文档拼成一条再随机切, P,M,SP,M,S 可能跨越文档边界, 后缀不能为中段提供语义约束. 文档级变换保证三段来自同一逻辑样本. 仓库样本内部可能含多文件, 因此缺口仍可能位于一个文件, 前后条件却包含依赖文件与路径.

% 混合目标与梯度竞争: 令 FIM 采样概率为 qq, 总目标近似为

Lq=(1−q)ELAR(X)+qELAR(TPSM(X)).\mathcal L_q=(1-q)\mathbb E\mathcal L_{AR}(X)+q\mathbb E\mathcal L_{AR}(T_{PSM}(X)).

q=0.5q=0.5 让自然顺序与重排序列各占一半样本. 两项共享参数, 梯度可能同向也可能冲突. 标识符语义、语法和 API 知识在两种排列中共用; 顺序建模对哨兵协议的需求不同. 100% FIM 削弱自然生成, 说明冲突确实存在.

若只比较 FIM benchmark, q=1q=1 可能最好; 产品同时需要代码生成、右侧续写和中间编辑, 综合目标会选择内部点. 论文在 1.3B Python 子集上选 50%, 它是可用折中, 不是数学上普适常数. 不同 IDE 请求中 infill 占比、缺口长度和语言分布变化后, 最优 qq 可能移动.

MSP 与 PSM 的监督形态不同. MSP 可遮多个 span, 常需连续恢复多个缺口; PSM 把单个中段移到末尾, 与编辑器一个光标区域更匹配. 多文件重构可能有多个离散修改, PSM 也覆盖不了. 第一代 DeepSeek-Coder 的 FIM 能力应限定为单缺口生成协议.

FIM 推理协议与停止条件: 官方 README 的输入形如 <fim_begin>P<fim_hole>S<fim_end>, 模型从其后生成 MM. 三个 special token 必须由对应 tokenizer 映射为单独 ID. 普通文本拼出相似字符、使用别的 Code LLM token 或在截断时丢失后缀, 都会造成分布偏移. 训练时 PSM 顺序和产品侧协议必须完全一致.

输出终止可依赖 EOS、长度上限或模型生成的协议标记. 若停止太早, 缺口未闭合; 太晚则模型可能继续复制后缀. exact match 通常只比较目标行, 产品还需处理缩进、换行和后缀重叠. 语法正确却风格不同的候选会被 exact match 判错, edit similarity 更平滑, 执行测试更接近语义.

双侧上下文也可能互相矛盾. 前缀调用 foo(int), 后缀期待字符串结果, 仓库分支或未保存编辑会让约束不一致. 模型按概率折中, 无法保证类型安全. 语言服务器提供的符号与诊断可以减少歧义, 这属于部署系统补充, 不是 FIM 目标自动获得的能力.

5.3. Tokenizer、上下文与计算复杂度

代码 tokenizer 的边界: 代码中空格、换行、缩进和标点都携带结构. tokenizer 若把四空格、常见运算符与关键字编码得紧凑, 16K token 能容纳更多源码; 对罕见语言或长标识符切得细, 同一窗口覆盖更少文件. 论文给出多语言范围和 special token 协议, 没公开每种语言字符/token 比, 所以「16K 项目上下文」在 Python、C++、JSON 上对应不同字节量.

词表还要同时覆盖 10% 英文代码文本和 3% 中文. 代码与自然语言共享子词能迁移 API 命名和解释, 也会竞争词表槽位. 路径、哈希、UUID 和压缩字符串容易碎成许多 token. 过滤超长行能减少这类低压缩内容, 数据规则和 tokenizer 效率因此相互影响.

特殊 FIM token 的加入必须在训练前确定 embedding. 它们不仅是分隔符, 还改变注意力角色: fim_hole 后的内容属于右侧条件, fim_end 后进入待生成中段. 模型通过大量重排样本学会协议. 把普通占位符字符串临时用于未训练模型, 不会自动产生等价能力.

K 到 16K 的两阶段训练: 第一阶段在 4K 窗口训练 1.8T token, 第二阶段用 200B token 扩到 16K. 90% token 在较短窗口完成语言、代码与 API 模式学习, 10% 长阶段让模型适应更远依赖和位置编码. 这种安排比全程 16K 省算力, 因为 dense attention 的 score 计算随长度平方增长.

对单层、batch bb、长度 TT、宽度 dd, QK 与 AV 前向主项约

Cattn,fwd≈4bT2d,C_{attn,fwd}\approx4bT^2d,

线性投影与 FFN 主项约 O(bTd2)O(bTd^2). 在 token 总数固定时, batch 中序列数随 1/T1/T 降低, attention 每 token 仍约随 TT 线性增长. 从 4K 到 16K, attention 的每 token 理论计算约增四倍, KV/激活内存也增长. 长阶段只占 200B 是数据与计算的折中.

训练 token 总量相同不代表 FLOPs 相同. 200B 长序列 token 的 attention 成本高于 200B 短序列 token, 还可能降低 batch 并增加通信. 报告没有给逐阶段实际 FLOPs 与 GPU 时, 不能按 90/10 token 比例推断成本也为 90/10. 33B 使用 FlashAttention v2改善 IO, 仍需执行数学上必要的 dense attention 乘加.

RoPE 缩放能做什么: RoPE 对第 mm 个位置的二维通道旋转角为 mθim\theta_i, 常见频率

θi=base−2i/dh.\theta_i=\mathrm{base}^{-2i/d_h}.

增大 base 会让部分通道旋转更慢, 线性 scaling 又把位置映射到训练过的相位范围. factor 4 可把 4K 名义位置扩展到更大范围. 追加 16K 训练让参数看到新的距离分布, 避免纯外推完全脱离训练.

旋转可计算到 64K 不代表注意力能可靠利用 64K. 高频通道相位、远距检索、训练长度覆盖与中间干扰共同决定质量. 报告把可靠输出写在 16K, 没给 64K needle 或仓库任务. 因此 64K 适合作为位置实现上限, 16K 才是论文有训练支持的能力范围.

长上下文还受信息选择约束. 把十六千 token 无差别塞满无关文件, 相关定义可能被稀释; BM25 或符号检索先筛文件, 更符合 CrossCodeEval 的设置. 模型窗口与检索器互补: 窗口决定可消费上限, 检索决定上限内的信息密度.

GQA、KV cache 与部署档位: 自回归解码的 KV cache 近似为

Bytes⁡KV=2bTLhkvdhs,\operatorname{Bytes}_{KV}=2bTLh_{kv}d_hs,

hkvh_{kv} 是 KV 头数, ss 是元素字节数. GQA 让多个 query 头共享 KV, 将 hkvh_{kv} 降低, 对长上下文与并发尤其重要. group size 8 通常表示每组八个 query 头共享一组 KV, 具体头数还应以模型 config 为准.

GQA 节省 K/V 投影参数和缓存, Q/O 以及 attention score 的 query 头计算仍在. 它可能牺牲每个 query 头独立的键值表示. 33B 使用 GQA并取得高分证明该配置可用, 没有 MHA 等算力消融来证明无损. 1.3B、6.7B 与 33B 的部署差异还包含层宽深度、权重显存和生成质量, 不能把延迟差全归给 GQA.

单行 FIM 上 6.7B 与 33B 接近, 说明该任务可能较早饱和, 或 exact match 对额外能力不敏感. 更大模型在复杂生成和竞赛题上优势明显. IDE 每次按键触发补全, 延迟与并发比离线最高分更重要; 6.7B 的推荐来自这个任务—成本组合, 不是所有代码场景的统一最优.