arXiv:2401.14196v2[cs.SE]2024年1月26日 DeepSeek-Coder:当大型语言模型遇到编程时-代码智能的兴起 郭大雅*1,朱启浩∗1,2、杨德建1,谢振达1、启东1,张文涛1 陈官厅1、小碧1,Y.Wu1,Y.K.Li1、罗富力1,熊英飞2,梁文峰1 1DeepSeek-AI 2HCST(PKU)教育部重点实验室;北京大学SCS [zhuqh,guodaya]@deepseek.comhttps://github .com/deepseek-ai/DeepSeek-Coder Abstract 大型语言模型的快速发展已彻底革新了软件开发中的代码智能。然而,主要依赖闭源模型限制了广泛的研究与开发。为解决这一问题,我们推出了DeepSeek-Coder系列开源代码模型,其规模从13亿到330亿不等,并从2万亿个标记中从头开始训练。这些模型预先在高质量的项目级代码语料库上进行训练,并采用16K窗口的填空任务来增强代码生成和填补。我们的广泛评估表明,DeepSeek-Coder不仅在多个基准测试中实现了开源代码模型的最佳性能,还超越了现有的闭源模型如Codex和GPT-3.5。此外,DeepSeek-Coder模型处于宽松的许可之下,允许同时进行研究和无限制的商业使用。 图1|DeepSeek-Coder的性能 *核心贡献者,按名称字母顺序排序。 1.Introduction 软件开发领域因大型语言模型的迅速进步(OpenAI,2023;Touvronetal.,2023)而得到了显著转型,这些模型带来了代码智能的新时代。这些模型有可能自动化和简化许多编码方面的工作,从错误检测到代码生成,从而提高生产力并减少人为错误的可能性。然而,该领域的重大挑战之一是在开源模型(Lietal.,2023;Nijkampetal.,2022;Rozieretal.,2023;Wangetal.,2021)与闭源模型(GeminiTeam,2023;OpenAI,2023)之间的性能差距。尽管巨型闭源模型非常强大,但由于其专有性质,许多研究人员和开发者仍然难以访问这些模型。 为了应对这一挑战,我们推出了DeepSeek-Coder系列。该系列包括一系列开源代码模型,规模从13亿到33亿不等,涵盖每个规模的基版本和指令版本。每个系列中的模型均从87种编程语言中提取的2万亿个标记重新训练,确保对编程语言和语法有全面的理解。此外,我们尝试在仓库级别组织预训练数据,以增强模型在仓库内部跨文件上下文中的理解能力。除了在预训练过程中使用下一个标记预测损失外,我们还引入了Fill-In-Middle(FIM)方法(Bavarianetal.,2022;Lietal.,2023)。这种方法旨在进一步提升模型的代码完成能力。为满足处理更长代码输入的要求,我们将上下文长度扩展到16K。这一调整使我们的模型能够处理更加复杂和广泛的编码任务,从而增强了其在各种编码场景中的适用性和灵活性。 我们使用多种公开的代码相关基准进行了全面的实验。研究发现,在开源模型中,DeepSeek-Coder-Base33B在所有基准测试中均表现出优越的性能。此外, OpenAIGPT-3.5Turbo Coder-Instruct33B超越在大多数评估基准中, OpenAIGPT-4 显著缩小两者之间的性能差距并且采用了开源模型。令人惊讶的是,尽管参数量较少,DeepSeek-Coder-Base7B在与参数量大五倍的模型(如CodeLlama-33B)相比时仍能展现出竞争力的表现(Roziereetal.,2023)。总结来说,我们的主要贡献是: •我们引入了DeepSeek-Coder-Base和DeepSeek-Coder-Instruct,这两种先进的代码集中型大型语言模型(LLMs)。通过广泛训练于庞大的代码语料库,这些模型展示了对87种编程语言的理解能力。此外,它们还提供了多种模型规模以满足不同计算和应用需求。我们首次尝试在模型构建过程中纳入仓库级别的数据。 •我们的模型预训练阶段。我们发现这可以显著提升跨文件代码生成的能力。•我们的研究严格分析了FIM训练策略对代码模型预训练阶段的影响。这些全面的研究结果揭示了FIM配置的一些有趣方面,提供了宝贵的见解,极大地促进了代码预训练模型的改进和发展。•我们对我们的代码LLM进行了广泛的评估,涵盖了多种代码相关任务的广泛基准。研究发现表明,DeepSeek-Coder-Base在这些基准上超越了所有现有的开源代码LLM。此外, 通过使用教学数据进行细致的微调,DeepSeek-Coder-Instruct实现了 OpenAIGPT-3.5Turbo 更好的性能相比与代码相关的任务中的模型。 2.数据收集 DeepSeek-Coder的训练数据集由87%的源代码、10%的英语代码相关自然语言语料库和3%的代码无关的中文自然语言语料库组成。英语语料库来自GitHub的Markdown和StackExchange材料。1,这些数据用于增强模型对代码相关概念的理解,并提高其处理库使用和错误修复等任务的能力。与此同时,中文语料库由高质量的文章组成,旨在提高模型理解中文语言的能力。在本节中,我们将概述我们构建代码训练数据的过程。这一过程包括数据爬取、基于规则的过滤、依赖解析、仓库级别去重以及质量筛选,如图2所示。接下来,我们将逐步描述数据创建流程。 Data爬行 回购级 重复数据删除 质量筛选 Rule过滤 依赖关系解析 图2|创建数据集的过程 2.1.GitHub数据抓取和过滤 我们在GitHub上收集了截至2023年2月之前创建的公共仓库,并仅保留了87种编程语言(如表1所示)。为了减少处理的数据量,我们应用了与StarCoder项目(Li等,2023)中使用的规则类似的过滤规则,初步筛选掉质量较低的代码。通过应用这些过滤规则,我们将数据总量减少了至原始大小的32.8%。为了使论文更加自包含,我们简要描述了StarCoderData项目中使用的过滤规则: 首先,我们过滤掉平均行长度超过100字符或最大行长度超过1000字符的文件。此外,我们去除包含少于25%字母字符的文件。除了XSLT编程语言外,我们进一步过滤掉其他类型的文件 。 "<?xml版本=" 其中字符串出现在前100个字符中。对于HTML文件,我们考虑可见文本与HTML代码的比例 。我们保留那些可见文本至少占代码20%且不少于100个字符的文件。对于通常包含更多数据的JSON和YAML文件,我们仅保留字符计数在50到5000之间的文件。这有效地移除了大多数数据密集型文件。 2.2.DependencyParsing 在之前的研究中(Chenetal.,2021;Lietal.,2023;Nijkampetal.,2022;Rozieretal.,2023),大型语言模型主要在文件级别源代码上进行预训练,忽略了项目中不同文件之间的依赖关系。然而,在实际应用中,这类模型难以有效地扩展以处理整个项目级别的代码场景。因此 ,我们 1https://stackexchange.com 算法1依赖分析的拓扑排序 1:procedureTOPOLOGICALSORT(�𝑖𝑙𝑒𝑠) 2:𝑔𝑟𝑎𝑝ℎ�←[]⊲初始化空邻接列表 3:𝑖𝑛𝐷𝑒𝑔𝑟𝑒�←[]⊲初始化in-degrees的空字典 4:对于每个�𝑖𝑙�in�𝑖𝑙𝑒�do 5:𝑔𝑟𝑎𝑝ℎ𝑠[�𝑖𝑙𝑒]←[] 6:𝑖𝑛𝐷𝑒𝑔𝑟𝑒𝑒[�𝑖𝑙𝑒]←0 7:结束 8: 9:对于每个�𝑖𝑙𝑒�in �𝑖𝑙𝑒�do 10: 对于每个�𝑖𝑙𝑒�in�𝑖𝑙𝑒�do 11: ifHASDEPENDENCY (�𝑖𝑙𝑒𝐴,�𝑖𝑙𝑒𝐵)then ⊲如果fileA依赖于fileB 12:𝑔𝑟𝑎𝑝ℎ𝑠[�𝑖𝑙𝑒𝐵].append(�𝑖𝑙𝑒𝐴)⊲将边从B添加到A 13:𝑖𝑛𝐷𝑒𝑔𝑟𝑒𝑒[�𝑖𝑙𝑒𝐴]←𝑖𝑛𝐷𝑒𝑔𝑟𝑒𝑒[�𝑖𝑙𝑒𝐴]+1⊲A程度的增量 14: 15: 16: 17: 如果结束结束 结束 18:𝑠𝑢𝑏𝑔𝑟𝑎𝑝ℎ�getDisconnectedSubgraps( 19:𝑎𝑙𝑙𝑅𝑒𝑠𝑢𝑙𝑡�←[] 𝑔𝑟𝑎𝑝ℎ𝑠)⊲识别断开的子图 20:对于每个𝑠𝑢𝑏𝑔𝑟𝑎𝑝ℎin𝑠𝑢𝑏𝑔𝑟𝑎𝑝ℎ�do 21:𝑟𝑒𝑠𝑢𝑙𝑡�长←度[]()※NumberOfNodes(do 22: while 𝑟𝑒𝑠𝑢𝑙𝑡�𝑠𝑢𝑏𝑔𝑟𝑎𝑝ℎ) 23:�𝑖𝑙�-argmin([ 𝑖𝑛𝐷𝑒𝑔𝑟𝑒𝑒[�𝑖𝑙𝑒]|�𝑖𝑙�∈𝑠𝑢𝑏𝑔𝑟𝑎𝑝ℎand�𝑖𝑙�∉𝑟𝑒𝑠𝑢𝑙𝑡𝑠]) 24: 对于每个𝑛𝑜𝑑�in𝑔𝑟𝑎𝑝ℎ𝑠[�𝑖𝑙𝑒]do 25:𝑖𝑛𝐷𝑒𝑔𝑟𝑒𝑒[𝑛𝑜𝑑𝑒]←𝑖𝑛𝐷𝑒𝑔𝑟𝑒𝑒[𝑛𝑜𝑑𝑒]−1 26:结束 27:𝑟𝑒𝑠𝑢𝑙𝑡𝑠.append(�𝑖𝑙𝑒) 28: 结束时 29:𝑎𝑙𝑙𝑅𝑒𝑠𝑢𝑙𝑡𝑠.append𝑟(𝑒𝑠𝑢𝑙𝑡𝑠) 30: 31: 32: 33: 结束 返回结束程序 𝑎𝑙𝑙𝑅𝑒𝑠𝑢𝑙𝑡� 我们将考虑如何在同一仓库内利用文件之间的依赖关系。具体来说,我们首先解析文件间的依赖关系,然后按照确保每个文件所依赖的上下文出现在该文件之前的顺序排列这些文件。通过根据依赖关系对文件进行对齐,我们的数据集更准确地反映了实际的编码实践和结构。这种增强的对齐不仅使数据集更具相关性,还可能提高模型在处理项目级代码场景时的实用性和适用性。值得注意的是,我们仅考虑文件之间的调用关系,并使用正则表达式提取这些关系,例如 "导入"在Python中,"使用"在C#中,和"包括"inC. 该算法1描述了在一个项目内的文件列表中进行依赖分析的拓扑排序。初始步骤是设置两个数据结构:一个名为空邻接表。"图形" 表示文件和空字典之间的依赖关系"学位"用于存储每个文件的in-degrees。然后,算法迭 代每个文件对,以识别 驻留,更新"图形"and"学位"相应地,接下来识别整体依赖图中的任何孤立子图。对于每个子图,算法采用修改后的拓扑排序。与标准方法选择入度为零的节点不同,该算法选择入度最小的节点,这使其能够处理图中的循环。选定的节点被添加到"结果"列表中的节点及其连接节点的入度减少。这一过程将持续进行,直到为每个子图生成一个拓扑排序序列。算法最终返 回这些排序序列的列表,并将每个序列中的文件连接起来形成单个训练样本。为了包含文件路径信息,在每个文件开头添加一条注释以指示文件的路径。这种方法确保了路径信息保留在训练数据中。 2.3.Repo级重复数据删除 最近的研究表明,通过去重训练数据集可以显著提高大型语言模型(LLMs)的性能。李等人 (2022)发现语言模型训练语料库中往往包含大量的近似重复内容,并且通过移除长篇重复子字符串可以提升LLM的性能。科切托夫等人(2022)应用了近似去重方法到训练数据上,取得了显著的改进,并强调近似去重是实现竞争性性能的关键预处理步骤。在我们的数据集中 ,我们也采用了近似去重的方法。然而,与以往的工作相比,我们采取了不同的方法。我们是在代码仓库级别进行去重,而不是在文件级别进行,因为后者可能会过滤掉仓库内的某些文件 ,从而可能破坏仓库结构。具体而言,我们将仓库级别的所有代码拼接成一个样本,并应用相同的近似去重算法以确保仓库结构的完整性。