DeepSeek-Coder 技术报告精译

From LLM Guide

本文内容为中文原文;Article content is the original Chinese source, and this page does not provide a translation.

DeepSeek-Coder 技术报告精译

🔙 返回 14.1-DeepSeek 家族总览

原文标题: DeepSeek-Coder: When the Large Language Model Meets Programming - The Rise of Code Intelligence 原文链接: https://arxiv.org/abs/2401.14196 发布日期: 2024-01-25 发布机构: DeepSeek-AI, 北京大学


摘要

大语言模型的快速发展彻底改变了软件开发领域的代码智能。然而,闭源模型的主导地位限制了广泛的研究与开发。为此,我们推出了 DeepSeek-Coder 系列,一系列从 1.3B 到 33B 参数规模的开源代码模型,在 2 万亿 token 上从零训练。这些模型在高质量的项目级代码语料库上预训练,并采用 16K 窗口的填空任务来增强代码生成和填充能力。我们的广泛评估表明,DeepSeek-Coder 不仅在多个基准上达到了开源代码模型的 state-of-the-art 性能,还超越了 Codex 和 GPT-3.5 等现有闭源模型。此外,DeepSeek-Coder 模型采用宽松许可证,允许用于研究和无限制的商业用途。

谱系与影响节点: DeepSeek-Coder 是 DeepSeek 家族的第一个重要开源模型(2023 年 11 月发布,2024 年 1 月发布技术报告)。它奠定了 DeepSeek 在代码智能领域的声誉,也是后续 DeepSeek-Math(v1.5 基座)、DeepSeek-Coder-V2 和 DeepSeek-V2 的数据工程基础。值得注意的是,论文中提到的「项目级代码语料库」和「FIM 训练」成为了后续代码模型的标准配置。DeepSeek-Coder 的成功也验证了「从零训练专用代码模型」路线的可行性——这与后来 DeepSeek-Coder-V2 选择「继续预训练通用基座」的路线形成了有趣的对比。


1. 引言

软件开发领域已被大语言模型的快速发展显著改变,它们开启了代码智能的新时代。这些模型有潜力自动化和简化编码的多个方面,从 bug 检测到代码生成,从而提高生产力并降低人为错误的可能性。然而,该领域的一个主要挑战是开源模型与闭源模型之间的性能差距。强大的闭源模型虽然性能出色,但由于其专有性质,许多研究人员和开发者无法使用。

为应对这一挑战,我们推出了 DeepSeek-Coder 系列。该系列包含一系列从 1.3B 到 33B 参数规模的开源代码模型,每个尺寸都包含基座版本和指令版本。系列中的每个模型都在来自 87 种编程语言的 2 万亿 token 上从零训练。此外,我们尝试在仓库级别组织预训练数据,以增强预训练模型在仓库内跨文件上下文中的理解能力。除了预训练期间采用下一 token 预测损失外,我们还引入了 Fill-In-Middle(FIM,中间填充)方法,旨在进一步增强模型的代码补全能力。为满足处理更长代码输入的需求,我们将上下文长度扩展到 16K,使我们的模型能够处理更复杂和广泛的编码任务,从而增加其在各种编码场景中的通用性和适用性。

我们使用多种公共代码相关基准进行了全面实验。结果表明,在开源模型中,DeepSeek-Coder-Base 33B 在所有基准上 consistently 表现出色。此外,DeepSeek-Coder-Instruct 33B 在大多数评估基准上超越了 OpenAI GPT-3.5 Turbo,显著缩小了 OpenAI GPT-4 与开源模型之间的性能差距。值得注意的是,即使是我们较小的模型 DeepSeek-Coder-Base 7B,也展现出与五倍规模模型(如 CodeLlama-33B)相当的竞争力。

设计动机节点: DeepSeek 选择代码作为第一个开源垂类模型,不是偶然的。代码是语言模型最容易量化和验证的领域——是否正确,测试用例一跑就知道。这种「可验证性」使得代码模型能够快速迭代和调优。另一个关键洞察是「项目级上下文」:传统代码模型在文件级别预训练,忽略了同一仓库中文件之间的依赖关系。但在实际开发中,跨文件引用(如 Python 的 import、C 的 include)是常态。DeepSeek-Coder 通过拓扑排序解析文件依赖并按依赖顺序排列文件,让模型在预训练阶段就接触到真实的跨文件上下文,这是其跨文件代码补全能力显著优于竞品的核心原因。

1.1 主要贡献

  • 我们推出了 DeepSeek-Coder-Base 和 DeepSeek-Coder-Instruct,这是我们先进的代码聚焦大语言模型。通过在庞大的代码语料库上进行广泛训练,这些模型展现出对 87 种编程语言的熟练理解。此外,它们提供多种模型规模以满足广泛的计算和应用需求。
  • 我们首次尝试在模型的预训练阶段引入仓库级数据构建。我们发现,尽管这种方法可能略微损害短代码生成的性能,但它能显著提升跨文件代码生成的能力。
  • 我们的分析严格审视了 FIM 训练策略对代码模型预训练阶段的影响。这些综合研究的结果揭示了 FIM 配置的有趣方面,提供了有价值的见解,对代码预训练模型的增强和发展做出了重要贡献。
  • 我们对代码大语言模型进行了广泛的评估,涵盖了众多代码相关任务的广泛基准。结果表明,DeepSeek-Coder-Base 在这些基准上超越了所有现有开源代码大语言模型。此外,通过使用指令数据精心微调,DeepSeek-Coder-Instruct 在代码相关任务中取得了比 OpenAI GPT-3.5 Turbo 更好的性能。

1.2 评测与指标概述

  • 代码生成: DeepSeek-Coder-Base 33B 在 HumanEval 上取得 56.1%(Python)和 50.3%(多语言平均),在 MBPP 上取得 66.0%,均为开源模型最佳。DeepSeek-Coder-Instruct 33B 在 HumanEval 上达到 79.3%,超越了 GPT-3.5 Turbo(76.2%)。
  • FIM 代码补全: DeepSeek-Coder-Base 33B 在 Single-Line Infilling 基准上平均精确匹配率达到 81.2%,优于 StarCoder(69.7%)和 CodeLlama-13B(75.5%)。
  • 跨文件代码补全: 在 CrossCodeEval 基准上,DeepSeek-Coder-Base 6.7B 在 Python、Java、TypeScript 和 C# 四种语言上均优于同规模的开源模型,验证了仓库级预训练的有效性。
  • 程序辅助数学推理: DeepSeek-Coder-Base 33B 在 GSM8K 上达到 60.7%,在 MATH 上达到 29.1%,展示了代码模型在数学推理上的潜力。
  • 从通用 LLM 继续预训练: DeepSeek-Coder-v1.5 7B 在 DeepSeek-LLM-7B 基础上用代码数据继续预训练 2T token,在保持代码能力的同时显著增强了数学推理和自然语言理解能力。

2. 数据收集

DeepSeek-Coder 的训练数据集由 87% 的源代码、10% 的英文代码相关自然语言语料库和 3% 的与代码无关的中文自然语言语料库组成。英文语料库包括 GitHub 的 Markdown 和 StackExchange 材料,用于增强模型对代码相关概念的理解以及处理库使用和 bug 修复等任务的能力。中文语料库由高质量文章组成,旨在提高模型对中文语言的理解能力。

在本节中,我们将概述代码训练数据的构建过程。该过程涉及数据爬取、基于规则的过滤、依赖解析、仓库级去重和质量筛选,如图 1 所示。

图 1: 数据集创建流程。(见 images/data_clean_pipeline.pdf)

2.1 GitHub 数据爬取与过滤

我们从 GitHub 收集了 2023 年 2 月之前创建的公开仓库,并仅保留 87 种编程语言。为减少待处理的数据量,我们应用与 StarCoder 项目类似的过滤规则,初步过滤掉低质量代码。通过应用这些过滤规则,我们将总数据量减少到原始大小的 32.8%。

具体过滤规则如下:

  • 过滤掉平均行长度超过 100 个字符或最大行长度超过 1000 个字符的文件。
  • 移除字母字符比例低于 25% 的文件。
  • 除 XSLT 编程语言外,进一步过滤掉前 100 个字符中出现字符串 <?xml version= 的文件。
  • 对于 HTML 文件,考虑可见文本与 HTML 代码的比例。保留可见文本占代码至少 20% 且不少于 100 个字符的文件。
  • 对于 JSON 和 YAML 文件,只保留字符数在 50 到 5000 之间的文件,有效去除了大多数数据密集型文件。

2.2 依赖解析

在先前的工作中,代码大语言模型主要在文件级源代码上预训练,忽略了项目中不同文件之间的依赖关系。然而,在实际应用中,这类模型难以有效扩展到处理整个项目级代码场景。因此,我们在这一步考虑如何利用同一仓库内文件之间的依赖关系。

具体而言,我们首先解析文件之间的依赖关系,然后按一种顺序排列这些文件,确保每个文件所依赖的上下文都位于该文件在输入序列中的位置之前。通过按照依赖关系对齐文件,我们的数据集更准确地反映了真实的编码实践和结构。

值得注意的是,我们只考虑文件之间的调用关系,并使用正则表达式提取它们,例如 Python 中的 import、C# 中的 using 和 C 中的 include。

算法 1 描述了用于项目中文件列表的依赖分析拓扑排序。该算法初始化两个数据结构:一个名为 graphs 的空邻接表,用于表示文件之间的依赖关系;一个名为 inDegree 的空字典,用于存储每个文件的入度。然后算法迭代每对文件以识别依赖关系,相应地更新 graphs 和 inDegree。接下来,它识别整体依赖图中的任何不连通子图。对于每个子图,算法采用改进的拓扑排序。与标准方法选择入度为零的节点不同,该算法选择入度最小的节点,这使其能够处理图中的环。选中的节点被添加到 results 列表中,其连接节点的入度递减。该过程持续进行,直到为每个子图生成拓扑排序序列。最后,算法返回这些排序序列的列表,每个序列的文件被连接形成单个训练样本。为了纳入文件路径信息,在每个文件开头添加指示文件路径的注释。

架构细节节点: 拓扑排序在代码数据预处理中的应用是一个精妙的工程洞察。传统的文件级预训练将代码文件视为独立的文本片段,而真实的软件开发中,文件之间存在复杂的依赖图(import/include 关系)。通过拓扑排序,模型在预训练时看到的序列顺序是「被依赖的文件在前,依赖它们的文件在后」——这与程序员阅读代码时的认知顺序一致。算法中选择「最小入度」而非「零入度」节点的策略尤为重要,因为真实代码库中存在循环依赖(如 A 依赖 B,B 又依赖 A),标准拓扑排序无法处理这种情况。最小入度策略 gracefully 处理了这种环,确保即使存在循环依赖,文件也能被合理地排列。

2.3 仓库级去重

最近的研究表明,对大语言模型训练数据集进行去重可以带来显著的性能提升。语言模型训练语料库通常包含大量近重复内容,通过去除长重复子串可以提升大语言模型的性能。Stack 数据集应用了近去重方法,取得了显著的改进,并强调近去重是在代码基准任务上取得竞争性性能的关键预处理步骤。

在我们的数据集中,我们也采用了近去重。然而,我们的方法与之前的工作有一个区别:我们在代码的仓库级别进行去重,而不是文件级别,因为后者可能会过滤掉仓库中的某些文件,从而破坏仓库的结构。具体而言,我们将仓库级别的连接代码视为单个样本,并应用相同的近去重算法,以确保仓库结构的完整性。

数据与实验节点: 仓库级去重 vs 文件级去重是一个容易被忽视但影响深远的工程决策。文件级去重(如 StarCoder 的做法)可能导致一个仓库中部分文件被保留、部分被删除,从而破坏文件之间的依赖关系——模型可能会看到 import 了一个不存在的模块的代码。仓库级去重将整個仓库作为原子单元,要么全部保留,要么全部删除,保持了仓库内部结构的完整性。论文虽然没有提供定量的消融实验来对比两种去重策略,但「保持结构完整性」的论证在工程逻辑上是成立的。

2.4 质量筛选与去污染

除了应用第 2.1 节提到的过滤规则外,我们还采用编译器和质量模型,结合启发式规则,进一步过滤掉低质量数据。这包括含有语法错误、可读性差和模块化程度低的代码。

源代码的统计摘要见表 1,包含 87 种语言,总计 798GB、6.03 亿个文件。

语言 大小(GB) 文件数(k) 占比(%) 语言 大小(GB) 文件数(k) 占比(%)
Java 148.66 134,367 18.63 TypeScript 60.62 62,432 7.60
C++ 90.87 36,006 11.39 C# 58.56 53,739 7.34
Python 120.68 75,188 15.12 PHP 58.92 40,627 7.38
JavaScript 53.84 71,895 6.75 HTML 30.05 14,998 3.77
C 28.64 27,111 3.59 其他 77 种 约 150 约 100,000 约 20
总计 797.92 603,173 100.00

表 1: 清洗后训练数据的统计摘要。(仅列出主要语言,完整列表见原文 Appendix)

为确保代码训练数据不被可能存在于 GitHub 上的测试集信息污染,我们实施了 n-gram 过滤流程。具体而言,如果一段代码包含与测试数据中任何 10-gram 字符串相同的内容,则将其从训练数据中排除。对于长度短于 10-gram 但不少于 3-gram 的测试数据,我们采用精确匹配进行过滤。

译者注: 10-gram 过滤是代码模型去污染的标准做法,因为代码的重复性比自然语言高得多——一个测试题的函数签名或 docstring 可能只有几十字符,但足以被模型记忆。与自然语言模型常用的 13-gram 去污染相比,代码模型使用更短的 n-gram 是合理的,因为代码的平均 token 长度更短,且结构更 rigid。


3. 训练策略

3.1 训练目标

下一 Token 预测 我们的第一个训练目标是下一 token 预测。在此过程中,将各种文件连接形成固定长度的条目,然后用这些条目训练模型,使其能够基于提供的上下文预测后续 token。

Fill-in-the-Middle(FIM) 代码预训练场景中,经常需要根据给定上下文和后续文本生成相应的插入内容。由于编程语言中的特定依赖性,仅依靠下一 token 预测不足以学习这种填空能力。因此,FIM 方法将文本随机分成三部分,然后打乱这些部分的顺序并用特殊字符连接。

在 FIM 方法中,采用两种不同模式:PSM(Prefix-Suffix-Middle)和 SPM(Suffix-Prefix-Middle)。在 PSM 模式中,训练语料按 Prefix、Suffix、Middle 的顺序组织;SPM 模式则将片段排列为 Suffix、Prefix、Middle。

为确定 FIM 方法中各种超参数的有效性,我们进行了一系列消融实验。使用 DeepSeek-Coder-Base 1.3B 作为模型架构,专注于训练数据集中的 Python 子集。主要目标是评估 FIM 技术的有效性,使用 HumanEval-FIM 基准进行测试。

实验结果如图 2 所示。虽然 100% FIM 率在 HumanEval-FIM 上达到峰值性能,但该配置也导致代码生成能力最弱。这表明 FIM 与代码生成能力之间存在权衡。此外,我们观察到 50% PSM 率优于 MSP 策略。为了在 FIM 效率和代码生成能力之间取得平衡,我们最终选择 50% PSM 率作为首选训练策略。

图 2: 使用 FIM 目标的效果。(见 images/fim.pdf)

数据与实验节点: FIM 比率的选择揭示了代码预训练中一个核心的「多任务学习」困境。FIM 任务(双向上下文)和下一 token 预测任务(单向上下文)在注意力机制层面存在竞争:FIM 要求模型在编码 prefix 时「屏蔽」对 suffix 的注意力(否则信息泄漏),而下一 token 预测则要求模型充分利用所有前文信息。100% FIM 虽然最大化补全能力,但削弱了生成能力;0% FIM 则相反。50% 的折中比率是工程上的务实选择——它让模型同时掌握两种技能,虽然每种都不是极致,但在实际产品场景中(IDE 既需要补全也需要生成)更为实用。

在我们的实现中,我们引入了三个专门的任务哨兵 token。对于每个代码文件,首先将其内容分为 fpref_{pre}、fmiddlef_{middle} 和 fsuff_{suf} 三段。使用 PSM 模式,训练样本构造如下:

<|fim_start|>fpre<|fim_hole|>fsuf<|fim_end|>fmiddle<|eos_token|>\texttt{<|fim\_start|>} f_{pre} \texttt{<|fim\_hole|>} f_{suf} \texttt{<|fim\_end|>} f_{middle} \texttt{<|eos\_token|>}

3.2 分词器

对于分词过程,我们使用 HuggingFace Tokenizer 库在训练语料库的子集上训练 Byte Pair Encoding(BPE)分词器。最终,我们使用词汇量为 32,000 的分词器。

3.3 模型架构

我们开发了不同参数规模的模型以满足 diverse 应用,包括 1.3B、6.7B 和 33B 参数的模型。这些模型基于 DeepSeek 大语言模型框架构建。每个模型都是Encoder-Only的 Transformer,采用 Rotary Position Embedding(RoPE)。值得注意的是,33B 模型集成了 Grouped-Query-Attention(GQA,分组查询注意力),组大小为 8,以提高训练和推理效率。此外,我们采用 FlashAttention v2 来加速注意力机制的计算。

超参数 1.3B 6.7B 33B
隐藏层激活函数 SwiGLU SwiGLU SwiGLU
隐藏层维度 2048 4096 7168
中间层维度 5504 11008 19200
隐藏层层数 24 32 62
注意力头数 16 32 56
注意力类型 Multi-head Multi-head GQA(8)
批量大小 1024 2304 3840
最大学习率 5.3e-4 4.2e-4 3.5e-4

表 2: DeepSeek-Coder 的超参数。

3.4 优化

遵循 DeepSeek LLM 的做法,我们使用 AdamW 作为优化器,β1\beta_1 和 β2\beta_2 值分别为 0.9 和 0.95。我们根据 DeepSeek LLM 建议的 scaling law 调整批量大小和学习率。对于学习率调度,我们实现三阶段策略:包括 2000 步 warmup,最终学习率设为初始值的 10%。值得注意的是,每个阶段的学习率按前一阶段的 110\sqrt{\frac{1}{10}} 缩放。

3.5 训练环境

我们的实验使用 HAI-LLM 框架进行,该框架以高效和轻量级的方式训练大语言模型。该框架结合了多种并行策略以优化计算效率,包括张量并行、ZeRO 数据并行和 PipeDream 流水线并行。

实验集群配备 NVIDIA A100 和 H800 GPU。A100 集群中每个节点配置 8 块 GPU,通过 NVLink 桥接成对互连。H800 集群同样每个节点 8 块 GPU,使用 NVLink 和 NVSwitch 技术组合互连。节点间通信采用 InfiniBand。

3.6 长上下文

为增强 DeepSeek-Coder 处理扩展上下文的能力(特别是仓库级代码处理场景),我们重新配置了 RoPE 参数以扩展默认上下文窗口。遵循先前做法,我们采用线性缩放策略,将缩放因子从 1 增加到 4,并将基频从 10000 改为 100000。模型使用 512 的批量大小和 16K 的序列长度额外训练了 1000 步。理论上,这些修改使模型能够处理最多 64K token 的上下文。然而,经验观察表明,模型在 16K token 范围内输出最可靠。

设计动机节点: 线性缩放 RoPE 是一种经典的上下文外推技术,最早由 Chen 等人(2023)在「Extending Context Window of Large Language Models via Position Interpolation」中提出。其核心思想是:如果模型在位置 0-L 上训练,要外推到 0-kL,可以通过将所有位置索引除以 k 来实现。DeepSeek-Coder 的缩放因子为 4,意味着理论外推长度为 16K * 4 = 64K。但经验上 16K 内最可靠,这是因为线性缩放虽然保证了位置编码的内积关系不被破坏,但注意力权重的分布仍然需要模型「适应」——而 1000 步的继续训练只够让模型适应 16K 左右的长度。

3.7 指令微调

我们通过使用高质量数据进行基于指令的微调来增强 DeepSeek-Coder-Base。这些数据包含有用且公正的人类指令,按 Alpaca 指令格式组织。为标记每个对话轮次,我们使用独特的分隔符 token <|EOT|> 表示每段的结束。训练使用余弦调度,100 步 warmup,初始学习率 1e-5。批量大小为 4M token,总共 2B token。


4. 实验结果

4.1 代码生成

HumanEval 和 MBPP 基准 HumanEval 和 MBPP 基准广泛用于评估代码大语言模型。HumanEval 包含 164 个手写 Python 问题,使用测试用例验证代码大语言模型在零样本设置下生成的代码。MBPP 基准包含 500 个问题,采用少样本设置。为评估模型的多语言能力,我们将 HumanEval 的 Python 问题扩展为 7 种额外常用编程语言:C++、Java、PHP、TypeScript、C#、Bash 和 JavaScript。

模型 规模 Python C++ Java PHP TS C# Bash JS 平均 MBPP
开源基座模型
CodeGeeX2 6B 36.0% 29.2% 25.9% 23.6% 20.8% 29.7% 6.3% 24.8% 24.5% 36.2%
StarCoderBase 16B 31.7% 31.1% 28.5% 25.4% 34.0% 34.8% 8.9% 29.8% 28.0% 42.8%
CodeLlama 7B 31.7% 29.8% 34.2% 23.6% 36.5% 36.7% 12.0% 29.2% 29.2% 38.6%
CodeLlama 13B 36.0% 37.9% 38.0% 34.2% 45.2% 43.0% 16.5% 32.3% 35.4% 48.4%
CodeLlama 34B 48.2% 44.7% 44.9% 41.0% 42.1% 48.7% 15.8% 42.2% 41.0% 55.2%
DS-Coder-Base 1.3B 34.8% 31.1% 32.3% 24.2% 28.9% 36.7% 10.1% 28.6% 28.3% 46.2%
DS-Coder-Base 6.7B 49.4% 50.3% 43.0% 38.5% 49.7% 50.0% 28.5% 48.4% 44.7% 60.6%
DS-Coder-Base 33B 56.1% 58.4% 51.9% 44.1% 52.8% 51.3% 32.3% 55.3% 50.3% 66.0%
指令微调模型
GPT-3.5-Turbo - 76.2% 63.4% 69.2% 60.9% 69.1% 70.8% 42.4% 67.1% 64.9% 70.8%
GPT-4 - 84.1% 76.4% 81.6% 77.2% 77.4% 79.1% 58.2% 78.0% 76.5% 80.0%
DS-Coder-Instruct 1.3B 65.2% 45.3% 51.9% 45.3% 59.7% 55.1% 12.7% 52.2% 48.4% 49.4%
DS-Coder-Instruct 6.7B 78.6% 63.4% 68.4% 68.9% 67.2% 72.8% 36.7% 72.7% 66.1% 65.4%
DS-Coder-Instruct 33B 79.3% 68.9% 73.4% 72.7% 67.9% 74.1% 43.0% 73.9% 69.2% 70.0%

表 3: 各模型在多语言 HumanEval 和 MBPP 基准上的性能。

如表 3 所示,DeepSeek-Coder-Base 在 HumanEval 上达到平均 50.3% 的准确率,在 MBPP 上达到 66.0%,均为开源模型最佳。与同规模开源模型 CodeLlama-Base 34B 相比,我们的模型在准确率上分别提升了 9% 和 11%。值得注意的是,即使是我们的较小模型 DeepSeek-Coder-Base 6.7B,也超越了 CodeLlama-Base 34B 的性能。经过指令微调后,我们的模型在 HumanEval 基准上超越了闭源 GPT-3.5-Turbo,显著缩小了 OpenAI GPT-4 与开源模型之间的性能差距。

数据与实验节点: 几个引人注目的数字。第一,DeepSeek-Coder-Base 6.7B(44.7% 平均) > CodeLlama-Base 34B(41.0% 平均),参数量只有后者的 20%,性能却更高。这说明数据质量(项目级语料 + 精细过滤)和训练规模(2T vs 500B)的增益可以超过纯参数规模的增益。第二,DeepSeek-Coder-Instruct 33B 在 HumanEval 上 79.3% 超过了 GPT-3.5-Turbo 的 76.2%,这是开源代码模型首次在 HumanEval 上超越 GPT-3.5——一个具有里程碑意义的结果。

DS-1000 基准 HumanEval 和 MBPP 的一个显著缺点是它们严重依赖简单的编程任务,可能无法准确代表大多数程序员通常编写的代码类型。相比之下,DS-1000 基准提供了 1000 个跨越 7 个不同库的实用且 realistic 的数据科学工作流。

模型 规模 Matplotlib Numpy Pandas Pytorch Scipy Scikit-Learn Tensorflow 平均
CodeGeeX2 6B 38.7% 26.8% 14.4% 11.8% 19.8% 27.0% 17.8% 22.9%
StarCoder-Base 16B 43.2% 29.1% 11.0% 20.6% 23.6% 32.2% 15.6% 24.6%
CodeLlama-Base 7B 41.9% 24.6% 14.8% 16.2% 18.9% 17.4% 17.8% 22.1%
CodeLlama-Base 13B 46.5% 28.6% 18.2% 19.1% 18.9% 27.8% 33.3% 26.8%
CodeLlama-Base 34B 50.3% 42.7% 23.0% 25.0% 28.3% 33.9% 40.0% 34.3%
DS-Coder-Base 1.3B 32.3% 21.4% 9.3% 8.8% 8.5% 16.5% 8.9% 16.2%
DS-Coder-Base 6.7B 48.4% 35.5% 20.6% 19.1% 22.6% 38.3% 24.4% 30.5%
DS-Coder-Base 33B 56.1% 49.6% 25.8% 36.8% 36.8% 40.0% 46.7% 40.2%

表 4: 各模型在 DS-1000 基准上的性能。

如表 4 所示,DeepSeek-Coder 模型在所有库中都取得了相对较高的准确率,证明我们的模型不仅能够生成良好的代码,还能在真实数据科学工作流中更准确地使用库。

LeetCode 竞赛基准 为了进一步验证模型在真实编程问题中的能力,我们构建了 LeetCode 竞赛基准。LeetCode 提供竞赛级问题,对模型的问题理解和代码生成技能提出重大挑战。我们收集了 LeetCode 竞赛的最新题目以防止预训练数据中出现这些问题或解答。共收集了 2023 年 7 月至 2024 年 1 月的 180 道问题,每道题收集 100 个测试用例以确保测试覆盖。

模型 规模 Easy(45) Medium(91) Hard(44) Overall(180)
WizardCoder-V1.0 15B 17.8% 1.1% 0.0% 5.0%
CodeLlama-Instruct 34B 24.4% 4.4% 4.5% 9.4%
Phind-CodeLlama-V2 34B 26.7% 8.8% 9.1% 13.3%
GPT-3.5-Turbo - 46.7% 15.4% 15.9% 23.3%
GPT-3.5-Turbo + CoT - 42.2% 15.4% 20.5% 23.3%
GPT-4-Turbo - 73.3% 31.9% 25.0% 40.6%
GPT-4-Turbo + CoT - 71.1% 35.2% 25.0% 41.8%
DS-Coder-Instruct 1.3B 22.2% 1.1% 4.5% 7.2%
DS-Coder-Instruct + CoT 1.3B 22.2% 2.2% 2.3% 7.2%
DS-Coder-Instruct 6.7B 44.4% 12.1% 9.1% 19.4%
DS-Coder-Instruct + CoT 6.7B 44.4% 17.6% 4.5% 21.1%
DS-Coder-Instruct 33B 57.8% 22.0% 9.1% 27.8%
DS-Coder-Instruct + CoT 33B 53.3% 25.3% 11.4% 28.9%

表 5: 各模型在 LeetCode 竞赛基准上的性能。

DeepSeek-Coder-Instruct 6.7B 和 33B 在此基准上分别取得 19.4% 和 27.8% 的 Pass@1 分数,显著超越 CodeLlama-33B 等现有开源模型。DeepSeek-Coder-Instruct 33B 是唯一在此任务上超越 OpenAI GPT-3.5-Turbo 的开源模型。然而,与更先进的 GPT-4-Turbo 相比仍存在显著性能差距。

我们的分析表明,Chain-of-Thought(CoT)提示显著增强了 DeepSeek-Coder-Instruct 模型的能力,这种改进在更具挑战性的任务子集中尤为明显。在初始提示后添加指令「You need first to write a step-by-step outline and then write the code.」,我们观察到性能提升。这说明首先编写详细的代码描述有助于模型更有效地理解和处理编码任务中逻辑和依赖关系的复杂性,特别是更高复杂度的任务。

译者注: CoT 对代码生成的增益主要体现在 Medium 难度题目上(+5.5% for 6.7B, +3.3% for 33B),但在 Hard 上反而有轻微下降。这可能是因为 Hard 题目本身就已经很难,额外的 CoT 步骤可能引入了更多出错的机会。论文坦诚地承认了数据污染的可能性——LeetCode 的题目和解答广泛存在于 GitHub 上,即使采取了时间过滤(2023 年 7 月后的题目),也无法完全排除模型在预训练时见过类似题目或解法。

4.2 Fill-in-the-Middle 代码补全

DeepSeek-Coder 模型在预训练阶段以 0.5 的 FIM 率进行训练。这种专门的训练策略使模型能够熟练地基于给定代码片段的前后上下文生成填空代码。

模型 规模 Python Java JavaScript 平均
SantaCoder 1.1B 44.0% 62.0% 74.0% 69.0%
StarCoder 16B 62.0% 73.0% 74.0% 69.7%
CodeLlama-Base 7B 67.6% 74.3% 80.2% 69.7%
CodeLlama-Base 13B 68.3% 77.6% 80.7% 75.5%
DS-Coder-Base 1.3B 57.4% 82.2% 71.7% 70.4%
DS-Coder-Base 6.7B 66.6% 88.1% 79.7% 80.7%
DS-Coder-Base 33B 65.4% 86.6% 82.5% 81.2%

表 6: 各模型在 FIM 任务上的性能。

如表 6 所示,即使是最小的 1.3B 参数模型,DeepSeek-Coder 在这些基准上也超越了更大的同类模型 StarCoder 和 CodeLlama。基于这些发现,我们推荐将 DeepSeek-Coder-Base 6.7B 模型部署于代码补全工具中。该模型在效率和准确率之间取得了良好的平衡,已被证明在代码补全场景中非常有效。

4.3 跨文件代码补全

本节评估现有开源模型在跨文件代码补全任务中的性能。与前面讨论的代码生成不同,跨文件代码补全要求模型访问和理解跨越多个文件且具有众多跨文件依赖关系的仓库。

我们使用 CrossCodeEval 来评估当前可用的 7B 规模开源代码模型在跨文件补全任务中的能力。该数据集基于四种流行编程语言(Python、Java、TypeScript、C#)的各种真实世界开源仓库构建,专门设计为严格需要跨文件上下文才能准确补全。

模型 规模 Python EM Python ES Java EM Java ES TS EM TS ES C# EM C# ES
CodeGeex2 6B 8.11% 59.55% 7.34% 59.60% 6.14% 55.50% 1.70% 51.66%
+ Retrieval 10.73% 61.76% 10.10% 59.56% 7.72% 55.17% 4.64% 52.30%
StarCoder-Base 7B 6.68% 59.55% 8.65% 62.57% 5.01% 48.83% 4.75% 59.53%
+ Retrieval 13.06% 64.24% 15.61% 64.78% 7.54% 42.06% 14.20% 65.03%
CodeLlama-Base 7B 7.32% 59.66% 9.68% 62.64% 8.19% 58.50% 4.07% 59.19%
+ Retrieval 13.02% 64.30% 16.41% 64.64% 12.34% 60.64% 13.19% 63.04%
DS-Coder-Base 6.7B 9.53% 61.65% 10.80% 61.77% 9.59% 60.17% 5.26% 61.32%
+ Retrieval 16.14% 66.51% 17.72% 63.18% 14.03% 61.77% 16.23% 63.42%
+ Retrieval w/o Repo Pre-training 16.02% 66.65% 16.64% 61.88% 13.23% 60.92% 14.48% 62.38%

表 7: 各模型在跨文件代码补全上的性能。(EM=Exact Match, ES=Edit Similarity)

结果(表 7)表明,DeepSeek-Coder 在多种语言的跨文件补全任务中 consistently 超越其他模型,展示了其 superior 的实际应用能力。当仅使用文件级代码语料库(w/o Repo Pre-training)预训练 DeepSeek-Coder 时,我们观察到 Java、TypeScript 和 C# 语言中性能下降,表明了仓库级预训练的有效性。

数据与实验节点: 表 7 中的消融实验「w/o Repo Pre-training」是整篇论文中最有说服力的证据之一。它精确地隔离了「仓库级预训练」的因果效应:在控制模型架构、参数规模、训练数据量和其他条件不变的情况下,仅将预训练数据从「仓库级」替换为「文件级」,跨文件补全性能在 Java(-1.08% EM)、TypeScript(-0.80% EM)和 C#(-1.75% EM)上均有下降。这个下降幅度虽然不大,但在跨文件补全这种已经很难的任务上(基线 EM 只有 5-10%),相对提升是显著的。这也说明仓库级预训练的收益主要体现在「需要理解跨文件依赖」的任务上,对纯文件内补全的收益有限。

4.4 程序辅助数学推理

程序辅助数学推理评估模型通过编程理解和解决数学问题的能力。我们采用 Program-Aided Math Reasoning(PAL)方法,在 GSM8K、MATH、GSM-Hard、SVAMP、TabMWP、ASDiv 和 MAWPS 七个基准上进行评估。在每个基准中,模型被提示交替用自然语言描述解题步骤,然后用代码执行该步骤。

模型 规模 GSM8K MATH GSM-Hard SVAMP TabMWP ASDiv MAWPS 平均
CodeGeex-2 7B 22.2% 9.7% 23.6% 39.0% 44.6% 48.5% 66.0% 36.2%
StarCoder-Base 16B 23.4% 10.3% 23.0% 42.4% 45.0% 54.9% 81.1% 40.0%
CodeLlama-Base 7B 31.2% 12.1% 30.2% 54.2% 52.9% 59.6% 82.6% 46.1%
CodeLlama-Base 13B 43.1% 14.4% 40.2% 59.2% 60.3% 63.6% 85.3% 52.3%
CodeLlama-Base 34B 58.2% 21.2% 51.8% 70.3% 69.8% 70.7% 91.8% 62.0%
DS-Coder-Base 1.3B 14.6% 16.8% 14.5% 36.7% 30.0% 48.2% 62.3% 31.9%
DS-Coder-Base 6.7B 43.2% 19.2% 40.3% 58.4% 67.9% 67.2% 87.0% 54.7%
DS-Coder-Base 33B 60.7% 29.1% 54.1% 71.6% 75.3% 76.7% 93.3% 65.8%

表 8: 各模型在程序辅助数学推理任务上的性能。

如表 8 所示,DeepSeek-Coder 模型在所有基准上均表现出色,特别是 33B 版本,展示了使用此类模型在需要复杂数学计算和问题解决能力的应用中的潜力。


5. 从通用大语言模型继续预训练

为进一步增强 DeepSeek-Coder 模型的自然语言理解和数学推理能力,我们在通用语言模型 DeepSeek-LLM-7B Base 上进行了额外的 2 万亿 token 预训练,得到了 DeepSeek-Coder-v1.5 7B。预训练使用的数据源及占比如表 9 所示。与 DeepSeek-Coder 不同,v1.5 仅使用下一 token 预测目标,上下文长度为 4K。

数据源 占比
源代码 70%
Markdown 和 StackExchange 10%
代码相关自然语言 7%
数学相关自然语言 7%
中英双语自然语言 6%

表 9: DeepSeek-Coder-v1.5 7B 预训练数据源。

我们将 DeepSeek-Coder-v1.5 7B 与 DeepSeek-Coder 6.7B 进行比较,使用相同的评估流水线重新运行所有基准以确保公平比较。评估涵盖编程、数学推理和自然语言三类任务:

模型 规模 HumanEval MBPP GSM8K MATH MMLU BBH HellaSwag WinoG ARC-C
DS-Coder-Base 6.7B 44.7% 60.6% 43.2% 19.2% 36.6% 44.3% 53.8% 57.1% 32.5%
DS-Coder-Base-v1.5 6.9B 43.2% 60.4% 62.4% 24.7% 49.1% 55.2% 69.9% 63.8% 47.2%
DS-Coder-Instruct 6.7B 66.1% 65.4% 62.8% 28.6% 37.2% 46.9% 55.0% 57.6% 37.4%
DS-Coder-Instruct-v1.5 6.9B 64.1% 64.6% 72.6% 34.1% 49.5% 53.3% 72.2% 63.4% 48.1%

表 10: DeepSeek-Coder-Base 与 DeepSeek-Coder-v1.5 的对比。数学任务通过编程解决。

观察到,尽管 DeepSeek-Coder-Base-v1.5 在编码性能上略有下降,但在大多数任务上相比 DeepSeek-Coder-Base 表现出显著改进。特别是在数学推理和自然语言类别中,v1.5 在所有基准上都显著超越前身,这也证明了其在数学推理和自然语言处理能力上的显著提升。

谱系与影响节点: DeepSeek-Coder-v1.5 是后续 DeepSeek-Math 的直接基座——DeepSeek-Math 就是从 DeepSeek-Coder-Base-v1.5 7B 出发,在 120B 数学 token 上继续预训练得到的。v1.5 的数据配比(70% 代码 + 20% 自然语言/数学)相比原始 DeepSeek-Coder(87% 代码 + 13% 自然语言)更加均衡,这使得它在保持代码能力的同时大幅提升了通用推理和数学能力。这个实验为后来 DeepSeek-Coder-V2 的「60% 代码 + 10% 数学 + 30% 自然语言」配比提供了重要的实证依据。


6. 结论

本文中,我们介绍了 DeepSeek-Coder 系列,一系列从 1.3B 到 33B 参数的开源代码模型,在 2 万亿 token 上从零训练。这些模型在高质量的项目级代码语料库上预训练,并采用 16K 窗口的填空任务来增强代码生成和填充能力。我们的广泛评估表明,DeepSeek-Coder 不仅在多个基准上达到了开源代码模型的 state-of-the-art 性能,还超越了 Codex 和 GPT-3.5 等现有闭源模型。


附录 A: 术语表

英文术语 中文译名 首次出现位置 简要解释
FIM Fill-In-the-Middle 引言 中间填充,代码补全训练目标
GQA Grouped-Query-Attention 模型架构 分组查询注意力,减少 KV Cache
RoPE Rotary Position Embedding 模型架构 旋转位置编码
BPE Byte Pair Encoding 分词器 字节对编码分词算法
CoT Chain-of-Thought 实验结果 链式思维提示
PSM Prefix-Suffix-Middle 训练策略 FIM 的一种序列组织模式
MSP Masked Span Prediction 训练策略 掩码跨度预测
EM Exact Match 跨文件补全 精确匹配率
ES Edit Similarity 跨文件补全 编辑相似度
PAL Program-Aided Math Reasoning 数学推理 程序辅助数学推理

附录 B: 核心数据汇总

任务 基准 DS-Coder-Base 1.3B 6.7B 33B DS-Coder-Instruct 33B
代码生成 HumanEval(平均) 28.3% 44.7% 50.3% 69.2%
代码生成 MBPP 46.2% 60.6% 66.0% 70.0%
数据科学 DS-1000(平均) 16.2% 30.5% 40.2% -
竞赛编程 LeetCode - - - 28.9%(+CoT)
FIM 补全 FIM(平均) 70.4% 80.7% 81.2% -
跨文件补全 CrossCodeEval(EM) - 9.53% - -
数学推理 GSM8K(PAL) 14.6% 43.2% 60.7% -
数学推理 MATH(PAL) 16.8% 19.2% 29.1% -

附录 C: 模型谱系定位

  • 直接继承自: DeepSeek-LLM(通用语言模型,为 Coder 提供基础架构)
  • 核心创新:
    • 首次在预训练阶段引入仓库级数据构建(拓扑排序 + 仓库级去重)
    • 系统分析 FIM 训练策略对代码预训练的影响(50% PSM 最优)
    • 开源代码模型首次在 HumanEval 上超越 GPT-3.5-Turbo
    • 验证了代码预训练对数学推理的正向迁移
  • 被后续工作引用/影响:
    • DeepSeek-Coder-Base-v1.5 成为 DeepSeek-Math 的初始化基座
    • 数据工程方法(过滤规则、依赖解析)被 DeepSeek-Coder-V2 继承和扩展
    • FIM 训练策略成为后续代码模型的标准配置