AI / Technology
DeepSeek-Coder代码模型为什么要看项目,而不只看单个文件
这篇论文不只是“再训练一个更大的代码模型”。它把三个容易混在一起的改动拆开做:按项目依赖组织训练数据、让模型练习补代码中间的缺口,再把可靠上下文扩到 16K。真正值得理解的是,这三件事各自解决什么问题,又有哪些结果能单独支撑它们。
原论文: DeepSeek-Coder: When the Large Language Model Meets Programming — The Rise of Code Intelligence
作者: Daya Guo 等 · arXiv:2401.14196
只看当前文件,模型可能连函数该传几个参数都不知道
真实项目里,一个文件经常调用另一个文件里的函数。上图用一个最小例子说明这个缺口:如果模型只看到 checkout.py,它看不到 formatter.py 里的函数定义,就可能补出参数数量不匹配的调用。让依赖文件进入上下文后,模型至少拿到了完成这次调用所需的信息。
论文指出,之前的代码模型多以单个文件为训练单位,容易忽略项目内部的跨文件依赖。这里展示的是教学例子,不是论文实验中的原始代码;它说明的是“缺少依赖信息”这一类失败,并不保证模型看到依赖后就一定写对业务逻辑。【论文 §2.2,pp. 3–5;§4.3,p. 14】
项目级预训练先把依赖文件排到调用者前面
DeepSeek-Coder 先从 import、include、using 等调用关系里解析“谁依赖谁”,再把被依赖的文件排到调用者前面。最后,它给每个文件加上路径,并把排好序的一组文件拼成一条训练样本。这样做没有增加一个新的推理模块;它改变的是模型在预训练时反复看到代码的顺序。
这个顺序为什么可能有用?当 utils.py 总是在调用它的 app.py 之前出现,普通的下一个 token 预测也有机会利用前面已经出现的定义。论文的实现只用正则表达式抽取调用关系,并用修改过的拓扑排序处理循环;它不是完整的程序语义分析,所以仍可能漏掉动态调用或复杂依赖。【论文 §2.2,pp. 3–5】
FIM 改变的不是窗口大小,而是模型拿到上下文的顺序
普通续写只根据左边已经出现的代码预测下一段。Fill-in-the-Middle(FIM)则把一段代码随机切成前文、中间和后文,再把训练顺序改成“前文 → 后文 → 中间”。模型因此能用缺口两边的信息预测中间内容,这更接近编辑器里“在已有代码中间补一段”的任务。
DeepSeek-Coder 采用 PSM 顺序,并在文档级别、打包训练样本之前执行 FIM。16K 是另一项改动:论文另外调整 RoPE 并继续训练 1,000 步,把可靠上下文扩到约 16K。FIM 决定“中间缺口能否同时看两边”,长窗口决定“一次能放进多少上下文”,两者不能压成同一个“看得更多”。【论文 §3.1.2,pp. 7–8;§3.6,p. 9】
论文没有把 FIM 拉到 100%,因为填空和普通续写会互相拉扯
论文用 1.3B 模型和一部分 Python 训练数据做了消融。100% FIM 在 HumanEval-FIM 上最好,却让普通代码续写最弱;50% PSM 又比 50% MSP 的填空效果更好。作者因此没有追求单项最高,而是选择 50% PSM,让一半样本练中间填空,另一半保留普通续写。
图中的长条只表达论文报告的相对关系,不是从曲线里猜出的精确分数。这个消融能支持“训练比例存在取舍”,但它只覆盖小模型、Python 子集和单行填空任务,不能证明 50% 对所有语言、规模和真实编辑场景都最优。【论文 Figure 3;§3.1.2,pp. 7–8】
有因果支撑的是项目级预训练;整套性能仍不能拆开归因
CrossCodeEval 是这里最接近因果检验的一组结果:在同一个 6.7B 模型、同样使用 BM25 检索的设置下,保留项目级预训练后,Python、Java、TypeScript 和 C# 的精确匹配率分别提高 0.12、1.08、0.80 和 1.75 个百分点。提升不算巨大,也不是每种语言一样大,但方向一致,说明依赖排序训练确实贡献了一部分跨文件能力。【论文 Table 7;§4.3,p. 14】
整体成绩更强,但不能全归给这一项。33B 基础模型在多语言 HumanEval 平均为 50.3%,MBPP 为 66.0%;同表中的 CodeLlama-Base 34B 分别是 41.0% 和 55.2%。33B 指令模型的多语言 HumanEval 平均为 69.2%,高于表中的 GPT-3.5-Turbo 64.9%,但 MBPP 是 70.0%,略低于 GPT-3.5-Turbo 的 70.8%。这些结果来自数据清洗、项目级组织、模型规模、FIM、长上下文和指令微调共同作用,论文没有把总提升完整拆到每个组件上。【论文 Table 3;§4.1,p. 11】
研究附录
一页结论
- 论文发布了 1.3B、6.7B 和 33B 三种规模的 Base 与 Instruct 模型,从零训练 2 万亿 tokens,覆盖 87 种编程语言。
- 数据由 87% 源代码、10% 英文代码相关自然语言和 3% 中文自然语言组成。
- 论文最有辨识度的贡献不是“2 万亿”这个规模,而是把仓库内依赖关系带进预训练样本,并用 CrossCodeEval 做了组件对照。
- FIM 的证据显示填空与普通续写之间有取舍;论文选 50% PSM,而不是把所有样本都改成填空。
- 长上下文调整理论上可到 64K,但作者明确说可靠输出主要在 16K 内;正文没有给出一组隔离 16K 改动的完整任务消融。
关键实验数字
| 问题 | 对照 | 结果 | 能说明什么 |
|---|---|---|---|
| 同规模基础模型代码生成 | DeepSeek-Coder-Base 33B vs CodeLlama-Base 34B | 多语言 HumanEval 50.3% vs 41.0%;MBPP 66.0% vs 55.2% | 整套基础模型训练方案更强,不能只归因于项目级数据 |
| 指令模型与闭源基线 | DeepSeek-Coder-Instruct 33B vs GPT-3.5-Turbo | 多语言 HumanEval 69.2% vs 64.9%;MBPP 70.0% vs 70.8% | “超过 GPT-3.5”取决于具体任务,不能写成全面领先 |
| 中间填空 | DeepSeek-Coder-Base 6.7B / 33B | 三语言平均精确匹配率 80.7% / 81.2% | FIM 能力强;规模增加后的提升有限 |
| 项目级预训练消融 | 保留 vs 去掉项目级预训练 | 四种语言精确匹配率均提高 0.12–1.75 个百分点 | 直接支持项目级数据组织对跨文件补全有贡献 |
实验边界
CrossCodeEval 的比较把最大输入长度设为 2,048 tokens,跨文件检索上下文最多 512 tokens,输出最多 50 tokens。因此,它检验的是“检索到跨文件片段后,模型能否更好利用”,并不是把整个仓库一次塞进 16K 窗口。
论文自建的 LeetCode Contest 基准包含 2023 年 7 月到 2024 年 1 月的 180 道题,每题 100 个测试用例。作者仍提醒,GPT-4-Turbo 和 DeepSeek-Coder 在较早月份得分更高,不能完全排除数据污染。
来源
关于这篇论文的三个关键问题
DeepSeek-Coder:代码模型为什么要看项目,而不只看单个文件 解决了什么问题?
这篇论文不只是“再训练一个更大的代码模型”。它把三个容易混在一起的改动拆开做:按项目依赖组织训练数据、让模型练习补代码中间的缺口,再把可靠上下文扩到 16K。真正值得理解的是,这三件事各自解决什么问题,又有哪些结果能单独支撑它们。
DeepSeek-Coder:代码模型为什么要看项目,而不只看单个文件 的核心结论有哪些证据?
CrossCodeEval 是这里最接近因果检验的一组结果:在同一个 6.7B 模型、同样使用 BM25 检索的设置下,保留项目级预训练后,Python、Java、TypeScript 和 C# 的精确匹配率分别提高 0.12、1.08、0.80 和 1.75 个百分点。提升不算巨大,也不是每种语言一样大,但方向一致,说明依赖排序训练确实贡献了一部分跨文件能力。【论文 Table 7;§4.3,p. 14】
阅读 DeepSeek-Coder:代码模型为什么要看项目,而不只看单个文件 时最需要注意什么局限?
整体成绩更强,但不能全归给这一项。33B 基础模型在多语言 HumanEval 平均为 50.3%,MBPP 为 66.0%;同表中的 CodeLlama-Base 34B 分别是 41.0% 和 55.2%。33B 指令模型的多语言 HumanEval 平均为 69.2%,高于表中的 GPT-3.5-Turbo 64.9%,但 MBPP 是 70.0%,略低于 GPT-3.5-Turbo 的 70.8%。这些结果来自数据清洗、项目级组织、模型规模、FIM、长上下文和指令微调共同作用,论文没有把总提升完整拆到每个组件上。【论文 Table 3;§4.1,p. 11】