文章标题:DeepCoder:一个完全开源的、达到O3-mini级别的14B编码器
作者/机构:Michael Luo, Sijun Tan, Roy Huang, Ameen Patel, Alpay Ariyak, Qingyang Wu, Xiaoxiang Shi, Rachel Xin, Colin Cai, Maurice Weber, Ce Zhang, Li Erran Li, Raluca Ada Popa, Ion Stoica (Agentica 团队与 Together AI 联合)

A1 主要贡献

本文通过Agentica团队与Together AI的合作,发布了DeepCoder-14B-Preview,这是一个通过分布式强化学习(RL)从Deepseek-R1-Distilled-Qwen-14B微调而来的代码推理模型。

核心问题与研究目标
近年来,通过强化学习在数学推理模型领域取得了显著进展,但在编码领域的进展相对滞后。这主要是由于构建具有可靠、可验证奖励的高质量数据集面临挑战。现有的一些编码数据集要么过于简单,要么包含带有缺陷或缺失测试用例的不可验证问题,这会产生无效或误导性的奖励信号,从而破坏RL训练的稳定性。因此,本文的目标是 democratize(普及)使用强化学习将小型模型训练成与o3-mini相媲美的强大竞争性编码器的秘诀。

创新与贡献点
1. 发布高性能模型:发布了DeepCoder-14B-Preview,这是一个仅有14B参数的模型,在LiveCodeBench上实现了60.6%的Pass@1准确率,性能与o3-mini(低)和o1-2024-12-17相匹配。
2. 构建并开源高质量数据集:策划并开源了一个包含24,000个可验证编码问题的高质量数据集,为社区进行RL训练提供了坚实基础。
3. 开源训练系统与优化:开源了verl-pipe,这是对verl后训练系统的扩展,包含多项系统优化,可将端到端训练速度提升2倍。
4. 完全开放训练方案:公开了完整的数据集、代码、训练日志和系统优化,使社区能够复现工作,并推动RL在扩展和加速智能方面的应用。

图1:DeepCoder在训练过程中的LiveCodeBench (LCB)得分。在第180步,上下文长度扩展到32K。最佳的32K检查点被用于推理时扩展到64K,实现了60.6%的LCB得分——与o3-mini的性能相匹配。
图1:DeepCoder在训练过程中的LiveCodeBench (LCB)得分。在第180步,上下文长度扩展到32K。最佳的32K检查点被用于推理时扩展到64K,实现了60.6%的LCB得分——与o3-mini的性能相匹配。

A3 背景知识/关键Observation/设计原则

数据集策划

编码领域高质量可验证数据的稀缺性。先前在数学领域的工作表明,使用可验证奖励的强化学习可以显著增强模型的推理能力。然而,与数学领域不同——互联网上随时可以获得大量高质量、可验证的数据——编码领域相对缺乏此类数据。

现有编码数据集的局限性。在早期的实验中,我们评估了几个流行的编码数据集,包括APPS、TACO、CodeContests、KodCode和LeetCode。我们发现其中一些对于我们的模型来说太简单(例如KodCode、LeetCode),而另一些则存在噪声或包含有缺陷或缺失测试用例的不可验证问题。这些问题常常产生无效或误导性的奖励信号,最终破坏了RL训练的稳定性。

高质量训练集的构建。为了克服这些限制,我们策划了一个高质量的训练集,由以下部分组成:
* TACO已验证问题。
* 来自PrimeIntellect的SYNTHETIC-1数据集中的已验证问题。
* 2023年5月1日至2024年7月31日期间提交的LiveCodeBench问题。

严格的数据过滤流程。为了确保数据质量以进行有效的RL训练,我们实施了一个严格的过滤流程:
1. 程序化验证:每个问题都使用外部的官方解决方案进行自动验证。我们过滤数据集,只包含那些官方解决方案能通过所有单元测试的问题。这个过程在tests/rewards/test_code_batch.py中是自动化的。
2. 测试用例过滤:每个问题必须包含至少5个单元测试。我们发现,测试用例较少的问题倾向于鼓励奖励hacking(奖励投机),即模型学会通过识别常见的测试用例来简单地打印出记忆的答案。
3. 去重:我们移除了跨数据集的重复问题以避免污染。我们对三个训练数据集(Taco Verified、PrimeIntellect SYNTHETIC-1和LCB (05/01/23-07/31/24))执行了此操作。然后,我们验证了测试数据集——LCB (08/01/24-02/01/25)和来自Codeforces的57个竞赛——中没有污染。

最终数据集构成。经过过滤后,我们得到了24,000个高质量的编码问题用于我们的RL训练,其中7,500个问题来自TACO Verified,16,000个问题来自PrimeIntellect的SYNTHETIC-1,以及600个来自LiveCodeBench。

代码沙箱环境

代码沙箱在RL训练中的必要性。为了计算代码RL训练的奖励,我们必须在代码沙箱中对模型生成的代码运行单元测试。在每个RL迭代中,我们的训练批次会在1024个问题上进行评估——每个问题都有多个单元测试(≥ 5个)。这个要求苛刻的工作负载需要扩展100多个代码沙箱以并行运行,确保LLM生成的代码在合理的时间内得到准确验证。目前,我们分别使用了两种沙箱:Together代码解释器和本地代码沙箱。

Together代码解释器

Together代码解释器。这是一个快速、高效的环境,与我们的RL训练直接兼容,每个问题仅需花费3美分。我们一直致力于将Together代码解释器可靠地扩展到100多个并发沙箱和每分钟1000多次沙箱执行。这些沙箱暴露了stdout、stdin和最后一行代码输出的评估,同时安全地限制执行并将代码与主机系统隔离。Together代码解释器目前处于测试阶段;详细信息可在Together代码解释器文档中找到,集成示例代码可在我们的代码仓库中找到。

本地代码沙箱

本地代码沙箱。它会启动一个本地沙箱作为独立的、有防护的Python子进程,通过stdin接收测试用例输入,并将答案打印到stdout。我们的本地沙箱遵循官方LiveCodeBench仓库中相同的评估代码,确保我们的结果与现有排行榜保持一致。

A2 方法细节

奖励函数

稀疏结果奖励模型 (ORM)。我们的奖励函数采用稀疏的结果奖励模型(ORM)。我们避免分配部分奖励,例如思维链(Chain-of-Thought)惩罚或在N个测试中通过K个时分配K/N的奖励,因为这可能导致奖励投机(reward hacking),即LLM学会直接打印出公开测试的答案或错误地收敛于通过简单的边缘案例。

奖励的具体规则。奖励规则如下:
1. 奖励为1:生成的代码必须通过所有抽样的单元测试。由于某些问题包含数百个测试——使得完全验证不切实际——我们为每个问题抽样15个最具挑战性的测试,这些测试根据其输入字符串的长度来确定。
2. 奖励为0:如果LLM的代码在至少一个测试用例上失败,或者答案格式不正确(即缺少python [CODE]),我们分配无奖励。每个测试用例的超时时间设置为6-12秒。

GRPO+:一个稳定版的GRPO



图2:在我们的16K运行中,GRPO+和GRPO的平均训练奖励对比。GRPO的奖励曲线最终崩溃。由于Clip High机制,GRPO+的曲线是稳定的。
图3:由于过长过滤机制,GRPO+的响应长度随时间稳定增长。


图4:Clip High和无熵损失确保GRPO+的token级熵不会崩溃,并鼓励充分的探索。

GRPO+:对GRPO的稳定增强。我们增强了原始的GRPO算法,整合了来自DAPO的见解,以实现更稳定的训练。具体改进如下:
* 无熵损失 (No Entropy Loss):我们观察到,包含熵损失项通常会导致不稳定性,熵会呈指数级增长并最终导致训练崩溃。为了缓解这个问题,我们完全取消了熵损失。
* 无KL损失 (No KL Loss, from DAPO):取消KL损失可以防止LLM被限制在原始SFT模型的信任区域内。这一移除也避免了为参考策略计算对数概率的需要,从而加速了训练。
* 过长过滤 (Overlong Filtering, from DAPO):为了保留长上下文推理能力,我们对被截断的序列的损失进行掩码。这项技术使得DeepCoder即使在32K上下文长度下训练,也能泛化到64K上下文的推理。如图3所示,这种过滤方法允许响应长度自然增长,而不会因截断而受到惩罚。
* 高位裁剪 (Clip High, from DAPO):通过提高GRPO/PPO代理损失中的上限,我们鼓励更多的探索并稳定熵。图4表明,这种调整不仅带来了更稳定的训练,还提高了模型性能。

迭代式上下文长度扩展:开箱即用的泛化能力

迭代式上下文长度扩展技术回顾。在我们最初的DeepScaleR博文中,我们介绍了一种名为“迭代式上下文长度扩展”的训练技术,该技术使语言模型能够首先学会在较短的上下文长度下有效思考,然后泛化到更长的上下文。这种方法帮助我们的1.5B模型在我们将上下文窗口从8K扩展到16K再到24K时,下游性能稳步提升,在AIME上的准确率从33%提高到38%再到43%,最终达到了O1-preview的性能水平。

应用于14B模型的挑战。然而,当我们将这项技术应用于14B模型时,遇到了新的挑战:
1. 14B模型已经具备比1.5B模型强得多的推理能力,这意味着进一步的提升需要解决更难的问题。
2. 这些更难的问题自然需要比用于较小模型的8K起始点更长的上下文窗口。

初始策略的负面影响。从短上下文开始,并因模型超出该窗口而惩罚它,产生了负面影响——导致初始性能下降、响应变短,以及模型在长上下文上推理能力的退化。

结合过长过滤的解决方案。为了在实现高效训练的同时保留长上下文推理能力,我们引入了DAPO的“过长过滤”技术。该技术在训练期间掩盖掉被截断的序列,这样模型就不会因为生成超出当前上下文限制的、深思熟虑但冗长的输出而受到惩罚。因此,模型在较短的上下文上训练时仍然可以进行“长思考”。

DeepCoder-14B-Preview的上下文扩展成果。我们将迭代式上下文长度扩展应用于DeepCoder-14B-Preview,将上下文窗口从16K扩展到32K。在LiveCodeBench上,该模型分别在16K和32K上下文下达到了54%和58%的准确率,并在64K上下文下评估时达到了60.6%,展示了超越其训练上下文的强大泛化能力。

与基础蒸馏模型的对比。这种泛化能力与像DeepSeek-R1-DistillQwen-14B这样的基础蒸馏模型形成对比,后者在超出其训练的上下文长度后性能会停滞不前。


尽管由于平均响应长度较长——导致截断和分数惩罚——DeepCoder的原始16K性能较低,但由于其跨更长上下文的推理能力,它最终在64K下优于其他模型。

迭代式上下文扩展与过长过滤的结合效果。DeepCoder的成功是迭代式上下文长度扩展与过长过滤相结合的直接结果。如图5所示,在训练过程中,模型的平均响应长度从8K稳步增长到17.5K,而平均奖励从0.6提高到0.7——这清楚地表明模型正在学习更具可扩展性和连贯性的思维模式。


图5:DeepCoder在训练过程中的平均响应长度和训练奖励。平均响应长度从8K上下文增长到17.5K上下文。

宝贝,没有足够高的山峰。
没有足够长的上下文。——灵感来自 Marvin Gaye & Tammi Terrell

后训练的系统优化

长上下文RL训练的耗时性。使用长上下文RL训练LLM是时间密集型的,需要重复采样和在长上下文上进行训练。如果没有系统级优化,完整的训练运行可能需要数周甚至数月——我们的14B编码运行每步需要1200-2500秒,导致总训练时间为2.5周!

verl-pipeline:verl的优化扩展。我们引入并开源了verl-pipeline,它是开源RLHF库verl的一个优化扩展,应用了多项系统级改进来加速端到端RL训练。verl-pipeline相比基线verl实现,速度提升高达2.5倍。我们应用这些新的系统优化来训练DeepCoder-1.5B-Preview,它在LCB上达到了25%的准确率,比Deepseek-R1-DistillQwen-1.5B提高了8%。

社区合作邀请。我们邀请社区,包括verl团队和其他新兴项目,采纳并基于这些优化进行构建。

采样器是瓶颈

采样时间是后训练系统的瓶颈。后训练系统通常受限于采样时间——即使用像vLLM和SGLang这样的推理引擎生成长序列(最多32K tokens)的延迟。图7显示了Verl的PPO/GRPO流水线,其中响应长度的异质性导致一些采样器成为“掉队者”(stragglers)。这些掉队者会延迟训练,而已完成的采样器则处于空闲状态,导致GPU利用率低下。


图7:Verl的PPO/GRPO训练流水线。每个RL迭代都循环经历采样、奖励函数计算和训练。采样是瓶颈;训练速度受限于生成长序列的掉队采样器。

小批量流水线 (Minibatch Pipelining)

通过流水线化采样和训练减少空闲时间。为了减少后训练中的空闲时间,我们将采样和训练进行流水线化——允许训练器在采样器继续生成下一个小批量的同时,开始对早期的小批量进行更新。这种重叠有助于掩盖采样延迟。


图8:小批量流水线。采样器和训练器在不同的工作组中运行。当采样器完成并释放小批量(用于PPO/GRPO)时,训练器工作者会异步处理它们。在一次迭代结束时,训练器将其权重广播给采样器。

小批量流水线的局限性。然而,这种方法有三个关键的局限性:
1. 首先,小批量的平均序列长度会随着时间推移而增长,增加了后续小批量的训练时间。结果是,最后几个小批量通常在采样完成后才会处理完,限制了流水线化的好处。
2. 其次,流水线化需要在采样器和训练器之间分割GPU,减少了可用的采样器数量。与Verl在同一GPU池中动态切换采样器和训练器不同,这种静态分割由于采样器数量减少,可能会减慢端到端的采样时间。
3. 最后,奖励函数的计算可能需要很长时间,特别是对于编码相关任务,每个RL迭代需要运行数千个单元测试。默认情况下,Verl在采样完成后在头节点上计算奖励。

实现与改进。尽管存在这些限制,我们在代码库的ray_trainer_pipeline.py中实现了小批量流水线,并指出流水线化可以通过微批处理(microbatching)进一步改进。

一次性流水线 (One-Off Pipelining)

一次性流水线:完全并行化。为了完全将训练、奖励计算和采样流水线化,我们引入了一次性流水线。这个想法很简单:牺牲第一个RL迭代只进行采样,然后在下一次迭代中使用该批次数据进行训练。这使得采样和训练能够并行进行,消除了采样后的训练器空闲时间。


图9:一次性流水线。采样器提前一个迭代生成批次,而训练器使用前一个迭代的数据更新梯度。其次,奖励函数计算与采样交错进行。这种方法不会向GRPO/PPO的在线策略算法引入异步的离策略样本。

交错进行奖励计算。其次,奖励计算与采样交错进行。一旦一个请求完成,它的奖励会立即被计算——这减少了奖励评估的开销,特别是对于像编码任务中测试用例执行这样的计算密集型任务。

实现。我们在我们的verl分支的ray_trainer_async.py中实现了一次性流水线。

端到端性能

端到端性能评估。在图10中,我们评估了verl、微批处理流水线和一次性流水线在两个工作负载(数学和编码)上的性能。为了公平起见,所有基线都通过Python线程池并行计算奖励;verl官方是串行计算每个样本的奖励,这对于编码任务来说时间长得无法接受。


图10:一次性流水线完全掩盖了训练器和奖励计算时间,将数学任务的训练时间减少了1.4倍,编码任务减少了2倍。

实验平台。我们在8个A100上评估了Deepcoder-1.5B-Preview,并调整了采样器与训练器的比例,以更好地平衡训练器和采样器的时间。

数学任务性能。对于数学任务,一次性流水线将每次RL迭代的时间减少了1.4倍。我们注意到数学的奖励计算时间几乎为零,因为它只包含基本的sympy检查。特别地,一次性流水线完全掩盖了训练器的时间,不像小批量流水线那样,最后一个小批量会溢出。

编码任务性能。对于编码任务,计算奖励需要在每次RL迭代中运行数千个测试,这是一个耗时的过程。一次性流水线掩盖了训练器和奖励计算的时间,将端到端训练时间减少了2倍。

一次性流水线的有效性与可扩展性。最重要的是,一次性流水线是有效的,并且可以扩展到困难的编码任务。我们使用ray_trainer_async.py训练了DeepCoder-1.5B-Preview,其LCB分数相比基础蒸馏模型提高了8%。

A4 实验环境

  • 数据集
    • 训练集:一个包含24,000个高质量编码问题的策划数据集,来源包括:TACO Verified (7.5K), PrimeIntellect’s SYNTHETIC-1 (16K), 以及LiveCodeBench (600)。
    • 评估集:LiveCodeBench (LCB), Codeforces (使用Qwen CodeElo基准,包含57个竞赛的408个问题), HumanEval+, 以及用于数学推理泛化评估的AIME2024。
  • 模型架构
    • DeepCoder-14B-Preview:一个14B参数的模型,通过RL从Deepseek-R1-Distilled-Qwen-14B微调而来。
    • DeepCoder-1.5B-Preview:一个1.5B参数的模型,用于系统优化实验,从Deepseek-R1-DistillQwen-1.5B微调而来。
  • 硬件配置
    • 14B模型训练:32块 H100 GPU。
    • 系统优化实验:8块 A100 GPU。
  • 软件配置
    • 代码实现:基于开源RLHF库verl的优化扩展verl-pipeline
    • 依赖库:使用Ray进行分布式计算,vLLM和SGLang作为推理引擎进行采样,sympy用于数学奖励检查。

A4 实验结果

我们在多个编码基准上评估Deepcoder-14B-Preview,包括LiveCodeBench (LCB)、Codeforces和HumanEval+,以及AIME2024。

总体性能:我们的14B参数模型在所有编码基准上都表现出强大的性能,并且其从编码任务中获得的推理能力很好地泛化到了数学领域。

各基准测试结果
* LiveCodeBench (LCB):取得了60.6%的Pass@1准确率,与o3-mini (low)和o1的性能相当。如图6所示,DeepCoder仅用14B参数就达到了前沿推理模型的水平。
* Codeforces:获得了1936的Elo评分,同样与o3-mini (low)和o1相当。
* HumanEval+:获得了87.8%的Pass@1准确率。
* AIME2024 (数学泛化):尽管模型没有专门针对数学任务进行训练,但它在AIME2024上取得了73.8%的分数,比基础模型提高了4.1%,这表明其推理能力有很好的泛化性。

下表汇总了详细的性能对比:

由于Deepseek和OpenAI在内部评估Codeforces,我们参考附录A了解Codeforces评估的更多细节。
*非推理模型。


图6:LiveCodeBench Pass@1准确率与模型大小对比。DeepCoder仅用14B参数就与前沿推理模型o1和o3-mini (low)相当。

系统优化实验结果
* 我们引入的verl-pipeline系统,采用一次性流水线(one-off pipelining)策略,显著提升了训练效率。对于数学任务,训练时间减少了1.4倍;对于计算密集的编码任务,端到端训练时间减少了2倍(如图10所示)。
* 使用这套优化系统,我们成功训练了DeepCoder-1.5B-Preview模型,其LCB得分达到了25%,相比其基础蒸馏模型提升了8%。

A5 结论

在本文中,我们介绍了Deepcoder-14B-Preview,这是一个14B参数的模型,在LiveCodeBench上达到了60.6%的Pass@1准确率,性能媲美o3-mini。为了实现这一目标,我们策划了高质量、可验证的编码数据,并为有效的RL训练引入了算法和系统上的优化。

我们的目标是普及用于LLM的RL训练。Deepcoder-14B-Preview代表了朝这个方向迈出的第二个重要里程碑,它建立在我们第一个专注于数学推理的模型DeepScaleR-1.5B-Preview所奠定的基础之上。通过完全分享我们的数据集、代码和训练秘诀,我们赋能社区复现我们的工作,并使RL训练对所有人开放。

我们相信,推动RL扩展是一个集体的、社区驱动的努力,我们欢迎开源贡献和赞助。让我们共同努力,推动RL在LLM推理及更广阔领域的前沿发展!

A6 附录

A — Codeforces

Codeforces评估基准。我们的Codeforces评估使用了Qwen CodeElo基准,该基准包含来自57场比赛的408个问题,难度从Div. 4到Div. 1不等。由于之前缺乏一个统一的Codeforces评估基准,OpenAI和Deepseek使用了不同的方法。这是一项旨在标准化这个被广泛使用的基准的倡议。

得分

得分计算方法。遵循Codeforces官方的得分计算方法,每个问题开始时都有一个最大可获得的分数 ??。每次提交错误,分数 ?? 减少50,直到因足够多的错误提交而降至0。

模型评估流程。在我们的评估中,我们让每个模型为每个问题生成8个回答,并收集每次提交的成功/失败情况,以计算模型在该问题上获得的分数。对于每场比赛,将分数相加,得到模型在该场比赛的总分。

Elo评分计算

Elo评分计算方法论。Elo评分的计算方法与Codeforces官方采用的方法非常相似。主要区别在于,Codeforces是跨多场比赛持续更新参与者的评分,而我们的方法将每场比赛独立对待。这简化了计算,并提高了对独立性能评估的准确性。

期望评分计算公式。具体来说,我们使用Elo & Sloan(1978)的以下公式来估计每个模型的期望评分:


在这个场景中,一场比赛有 ?? 个已知评分为 ?? (?? = 1, 2, ..., ??) 的人类参与者。模型在这些参与者中排名为 ??,利用这些信息,我们可以计算出模型 ?? 在特定比赛中的期望评分。

真实世界数据的使用。我们通过Codeforces API获取特定比赛的人类参与者得分,以反映我们模型非常真实的Elo评分。

总体Elo评分。模型在所有57场比赛中的总体估计Elo评分是通过对各场比赛的评分取平均值得到的。

等效性证明

等效性证明。关于等效性的详细证明,请参考CodeElo论文的附录C,其中他们证明了这种Elo评分计算方法与官方Codeforces方法在数学上的等效性。

百分位计算

百分位计算。所有人类参与者的评分百分位是基于Codeforces平台上公开的用户评分。数据来自2024年,包含89352名有评分的用户。我们选择2024年是为了保持一致性,因为CodeElo基准中使用的比赛范围都在2024年内。非常感谢Codeforces用户123gjweq2提供此数据。