
AI研究日志2:从单文件到项目级理解DeepSeek-Coder如何解决复杂代码生成难题?
不仅学单个文件,还学整个项目的结构,就像学做菜时知道所有步骤的顺序。去掉重复和低质数据,只保留精华。
本人对AI发展有着浓厚的兴趣, 也参加过一些大模型相关的大赛,在考研之余会研究一些前沿AI的paper作为兴趣,想着把我自己的研究和思考发发csdn分享一下,内容是由我自己的研究理解、paper原文和与AI深度交流过后的一些精选内容组成,本人只是一个ai爱好者,内容请大家理性甄别,欢迎沟通讨论(不喜勿喷)
一、论文要解决什么问题?
问题背景:
假设你是一个程序员,想用AI写代码,但发现现有的工具(比如GitHub Copilot)虽然强大,但闭源(无法自由修改和研究)。而开源的模型(比如CodeLlama)虽然免费,但效果不够好,尤其在处理复杂项目或跨文件的代码时容易出错。
论文的目标:
开发一个开源的代码生成模型(DeepSeek Coder),性能接近闭源模型(如GPT 3.5),甚至超过它们。让模型能理解整个项目的结构(而不仅仅是单个文件),并能处理长代码和复杂任务。
二、核心创新点
1. 项目级数据构建
传统方法的问题:
假设你学做菜,但只学了“切洋葱”和“煎牛排”两个步骤,却不知道如何把这些步骤组合成一道完整的菜。这就是传统模型的问题——它们只学单个文件的代码,但不知道文件之间的依赖关系(比如一个文件用到了另一个文件的函数)。
DeepSeek Coder的解决方案:
像整理整个厨房一样整理代码,模型在训练时会把整个项目的文件按逻辑顺序拼接成一个样本。例如:
文件A导入了文件B的函数,训练时会先放文件B的代码,再放文件A的代码。这样模型就能理解“为什么A要用到B的函数”,就像知道“煎牛排前必须先切牛排”一样。
假设项目中有两个文件: `math_utils.py`(工具函数,比如加法函数) `main.py`(调用加法函数) 训练时,模型会把这两个文件按顺序拼接,确保`main.py`知道`math_utils.py`的存在,避免写代码时找不到函数。
2.Fill in the Middle(FIM)训练
我们模型的第二个训练目标称为 fil-in-the-middle。在代码预训练场景中,通常需要根据给定的上下文和后续文本生成相应的插入内容。由于编程语言中的特定依赖关系,仅依靠 next token:预测不足以学习这种填充中间人功能。因此,几种方法(Bavarian et al.,2022;Li et al..2023)提出了 Fill-in-the-Midlle(FIM)的预训练方法。这种方法包括将文本随机分为三个部分,然后打乱这些部分的顺序并用特殊字符将它们连接起来。该方法旨在在训练过程中加入填空预训练任务。在 FIM 方法中,采用两种不同的模式:PSM(Prefix-Suffix-Middle)和 SPM(Sufix-Prefix-Middle)。在 PSM 模式下,训练语料库按 Pre fix、Su ffix、Middle 的顺序进行组织,以中间段两侧带有前缀和后缀的方式对齐文本。相反SPM 模式将段排列为 Su ffix、Pre fix、Middle,提出了不同的结构挑战。这些模式有助于增强模型处理代码中各种结构安排的能力,为高级代码预测任务提供强大的训练框架。(机翻原文)
传统方法的问题:
模型像“填空游戏”,但只能从左到右补全。比如,如果句子是“我今天去__公园,看到了__”,模型只能补全“公园”后面的内容,但无法补全中间的空格。
FIM的改进:
像玩拼图一样训练模型:将代码随机分成三段,打乱顺序后让模型还原。例如: 原始代码:`def add(a,b): return a + b` 。挖空后:`def add(a,b): <fim_hole> return`(中间挖空)。模型需要根据前后文补全`return a + b`。
这样做模型能更好地处理 中间代码缺失 的情况,比如补全一个函数的中间逻辑,而不仅仅是续写。
3.长上下文支持(16K上下文窗口)
为了增强 DeepSeek-Coder 处理扩展上下文的能力,特别是对于存储库级代码处理等场景,我们重新配置了 RoPE (Su et a., 2023) 参数以扩展默认上下文窗口。遵循以前的做法(Chen et al..2023;kaiokendev, 2023),我们采用了线性缩放策略,将缩放因子从1增加到 4,并将基本频率从10000 更改为 100000。该模型还进行了额外的 1000 个训练步骤,使用批量大小 512 和序列长度 16K学习率与最后的预训练阶段一样保持不变。从理论上讲,这些修改使我们的模型能够在上下文中处理多达64K个令牌。然而,实证观察表明,该模型在 16K 标记范围内提供了最可靠的输出。未来的研究将继续改进和评估长上下文适应方法,旨在进一步提高 DeepSeek-Coder 在处理扩展上下文方面的效率和用户友好性。 (机翻原文)
问题:
想写一个超长的代码文件(比如1万行),但模型只能记住前面几百行,后面就“健忘”了。
解决方案:
像记忆一本长篇小说一样记住代码,通过调整模型的参数(比如位置编码),让模型能处理长达16,384行的代码,理解更复杂的逻辑。
例子:
写一个大型游戏程序时,模型能同时记住角色设定、关卡逻辑和输入控制,而不会混淆或遗漏关键步骤。
4.指令调优(让模型听懂自然语言)
通过使用高质量数据进行基于指令的微调来增强DeepSeek-Coder-Base 来开发 DeepSeek-Coder- Instruct。这些数据包括有用且公正的人工指令,由 Alpaca Instruction 格式构建(Taori et al., 2023)。为了划分每个对话回合,我们采用了一个独特的分隔符标记 <|EOT|> 来表示每个片段的结论。对于训练,我们使用具有 100 个热身步骤和初始学习率 1e-5 的余弦时间表。我们还总共使用 4M 令牌和 2B 令牌的批量大小。(机翻原文)
问题:
你告诉模型“写一个贪吃蛇游戏”,但它可能完全不知道怎么开始,或者生成的代码有错误。
下图描述了使用 DeepSeek-Coder-instruct 348 的示例。此示例是用于构建贪吃蛇游戏的多回合对话场景。最初,我们要求模型使用 pygame 编写一条游戏蛇。该模型成功地创建了一个基本的贪吃蛇游戏,它可以无错误地运行。为了改进游戏,我们进一步要求在左上角添加一个计分系统。然后,该模型引入了“score” 变量和一个“display score” 函数,并解释了如何集成这些功能。此示例说明了DeepSeek-Coderinstruct 在多轮次对话设置中提供完整的解决方案的能力。(机翻原文)
解决方案:
像教小学生写作业一样训练模型,用大量人类写的“指令+代码”示例微调模型。例如:
人类指令:“用Pygame写一个贪吃蛇游戏,并添加计分系统。”
模型学习后,能根据指令一步步生成代码,甚至先规划步骤再写代码(比如先画界面,再写移动逻辑)。
例子:
用户输入:“帮我写一个计算器程序,支持加减乘除。”
模型会先生成代码框架,再逐个实现功能,而不是乱写一气。
三、训练过程:如何让模型变聪明?
1. 数据收集与清洗
数据来源 :
从GitHub上爬取87种语言的代码(比如Python、Java、C++),再加上自然语言文档(比如英文的StackExchange问答、中文的技术文章)。
筛选规则 :
去掉垃圾代码(比如超长行、乱码),保留高质量代码,就像整理图书馆时只保留好书。
2. 依赖解析与拓扑排序
比喻:
想做一道菜,但食材和步骤散落在不同地方。模型需要先整理所有步骤的顺序,确保先切菜再炒菜。
具体操作:
分析代码文件间的依赖关系(比如文件A用到了文件B的函数)。
用算法(拓扑排序)确定文件拼接的顺序,确保依赖的文件先出现。
3. FIM训练策略
分三步训练:
1. 把代码分成三段:`前半段`、`中间段`、`后半段`。
2. 打乱顺序,比如变成`前半段 <空> 后半段`,模型需要补全中间段。
3. 同时练习“逐行写代码”和“填空”,平衡两种能力。
例子:
原始代码:`for i in range(10): print(i)`
FIM训练时可能变成:`for i in range(10): <fim_hole>`,模型要补全`print(i)`。
四、模型效果:为什么它比其他模型好?
1. 代码生成能力(HumanEval基准)
HumanEval :测试模型能否写出通过测试用例的代码。
结果 :
DeepSeek Coder 33B在Python任务中通过率56.1%,比CodeLlama 34B(48.2%)更好。 小模型(6.7B)甚至比大模型(CodeLlama 34B)表现更好,说明数据质量很重要。
2. 跨文件代码补全
问题 :
写一个Java程序,但需要用到另一个文件的类,模型能否正确引用?
结果 :
DeepSeek Coder在跨文件任务中的准确率比CodeLlama高5 8%,说明它更懂“项目整体结构”。
3. 数学题解决能力(LeetCode)
例子 :
让模型解一道数学题:“找出最强的球队(没有比它更强的对手)”。
模型表现 :
DeepSeek Coder Instruct 33B的通过率27.8%,接近GPT 3.5(23.3%),远超其他开源模型。
五、通俗总结:DeepSeek Coder是怎么做到的?
1. 数据更聪明
不仅学单个文件,还学整个项目的结构,就像学做菜时知道所有步骤的顺序。
去掉重复和低质数据,只保留精华。
2. 训练方法创新
FIM训练 :像玩填空游戏,让模型学会补全中间代码,而不是只会续写。
长上下文 :能记住超长代码,处理复杂项目。
3. 理解自然语言
你可以说“写个登录页面”,模型能生成带验证的完整代码,而不需要复杂的指令。
4. 开源且强大
免费使用,性能接近闭源模型(如GPT 3.5),填补了开源代码模型的空白。
六、关键指标解释(小白也能懂!)
1. HumanEval Pass@1(通过率)
简单解释 :
给模型一个编程任务(比如写一个函数),它一次写出正确代码的概率。
比如HumanEval的Python任务,DeepSeek Coder 33B通过率56.1%,意味着它有56%的概率一次写出正确的代码。
2. LeetCode Pass@1
简单解释 :
在LeetCode的难题(比如算法题)中,模型一次写出能通过所有测试用例的代码的概率。
DeepSeek Coder 33B的通过率27.8%,说明它能解决较复杂的问题。
3. FIM任务(填空能力)
简单解释 :
像玩填空游戏,模型补全中间代码的准确率。
DeepSeek Coder在Python的填空任务中准确率81.2%,比其他模型高。
七、为什么这些创新重要?
项目级数据是为了让模型理解代码的“全局视角”,比如一个函数可能依赖另一个文件,而不会“只见树木,不见森林”。FIM训练是为了类似人类写文章时需要补全中间段落,模型能处理更复杂的逻辑漏洞。而长上下文可以写一个包含多个函数的程序时,模型不会“健忘”,记得所有定义和逻辑。指令调优,你不用学复杂的代码提示,直接说“写个XXX程序”就行,模型能听懂并执行。
DeepSeek Coder的优势我觉得是:
像程序员一样思考,能理解项目结构,补全中间代码,处理长程序。
像助手一样听话,你用自然语言描述需求,它能生成可运行的代码。
开源免费,比闭源模型(如GitHub Copilot)更自由,适合开发者和研究者。
更多推荐
所有评论(0)