新智元报道
编辑:犀牛 英智
【新智元导读】Hugging Face的Open R1重磅升级,7B击败Claude 3.7 Sonnet等一众前沿模型。凭借CodeForces-CoTs数据集的10万高质量样本、IOI难题的严苛测试,以及模拟真实竞赛的提交策略优化,这款模型展现了惊艳的性能。
Hugging Face的Open R1再度升级!
Hugging Face的Open R1是一个社区驱动的项目,目标是创建一个完全开源的DeepSeek-R1版本。目前,已有模型如OlympicCoder-32B和数据集如codeforces发布,显示了项目的进展。
最新发布的7B和32B OlympicCoder,在IOI挑战上超越了一众前沿模型,比Claude 3.7 Sonnet还猛。
OlympicCoder已经成了代码推理界的「肌肉猛男」,有些模型规模比它大100倍,结果还是被它按在地上摩擦……
模型在2024年国际信息学奥林匹克竞赛(IOI)50次提交中的表现
而这一切,得感谢Open R1的一系列骚操作:
CodeForces-CoTs数据集:近10万个高质量样本,提炼自DeepSeek-R1,专门训练C++和Python代码生成。
IOI基准测试:拿2024年国际信息学奥林匹克竞赛(IOI)的难题来虐AI,看看谁是真正的「代码战神」。
提交策略优化:模拟OpenAI的策略,让模型最大化得分,像真正的选手一样参加比赛。
我们来扒一扒它是怎么炼成的,以及Hugging Face团队踩过的那些坑。
(小心,可能会让你怀疑人生:AI连刷题都比你强了……)
CodeForces-CoTs数据集
CodeForces作为编程竞赛的热门平台,其中的算法优化问题极具挑战性。
这使其成为一个有趣的数据集,用于提升和测试模型的代码推理能力。
此次发布的open-r1/codeforces包含了超过1万个问题,时间跨度从最初的竞赛一直到2025年,其中约3000个问题是DeepMind和CodeContests中没有的。
对于约60%的问题,数据集提供了竞赛组织者撰写的解题思路,这对理解原理至关重要。
同时,每个问题都从官方网站提取了3个正确解决方案。
open-r1/codeforces-cots数据集更是一大亮点,其中包含了DeepSeek-R1针对这些问题生成的近10万个思维链(CoT)样本,用C++和Python两种语言呈现。
研究团队在这个数据集上对Qwen2.5 Coder Instruct 7B和32B进行微调,得到了OlympicCoder-7B和OlympicCoder-32B模型。
代码可验证性危机
虽然DeepMind和其他竞赛数据集都包含测试用例,并声称是可验证的,但这些通常只是竞赛网站上全套测试用例的一小部分。
特别是CodeForces,显示的测试用例上限约为500个字符,这意味着这些数据集只包含符合此限制的较短、较简单的测试用例。
例如,研究者选取了7个问题,R1生成的解决方案通过了全部公开测试用例,并将它们提交到CodeForces平台:
尽管这些方案通过了较短的测试,但均未通过完整测试集。这凸显了对新的可验证的编程竞赛数据集的需求。
国际信息学奥林匹克竞赛(IOI)
国际信息学奥林匹克竞赛(IOI)是全球顶尖的编程竞赛。
完整测试集遵循宽松的(CC-BY)许可发布,使其成为测试代码推理能力的理想数据集。
如果你熟悉美国数学邀请赛(AIME),IOI就相当于数学奥林匹克竞赛(IMO)的编程版,参加AIME的最优秀学生才有资格受邀参加IMO。
IOI的问题设计独特,每个问题细分为多个子任务,各子任务输入约束不同。
参赛者要解决子任务,提交的方案须在严格时限内通过所有测试用例。
最后子任务通常是完整复杂问题,而前面子任务相对简单、约束更多,参赛者常针对特定子任务拿部分分数,竞赛中得满分十分罕见。
团队整理了2020-2024年的IOI问题,将它们拆分为子任务,使每个提示都能解决一个特定的子任务,便于有针对性地训练和评估。
他们还在open-r1/ioi和open-r1/ioi-test-cases中发布了处理后的问题陈述、评分检查文件及测试用例,同时创建了自定义代码,用于运行解决方案并按IOI规则评分,代码可在https://github.com/huggingface/ioi上获取。
研究者对2024年IOI上40多个领先的推理模型进行了全面评估。
每个问题的提交次数限制为50次,采用与OpenAI类似的选择策略模拟得分。
评估结果显示,OlympicCoder模型表现出色。
OlympicCoder-32B在50次提交限制下,超越了o1-mini、DeepSeek-R1、Claude-3.7-Sonnet-thinking等模型。
模型在2024年国际信息学奥林匹克竞赛(IOI)50次提交中的表现
提交策略
这种提交策略可能不利于非推理模型,像OlympicCoder-32B-Instruct和Qwen-2.5-Coder-32B-Instruct。
为模拟真实竞赛,团队采用类似OpenAI用于o1-ioi的循环提交策略。
首先提交针对最后一个子任务的解决方案,然后是倒数第二个子任务的方案,以此类推,只有选定提交时才评估解决方案。
若子任务已被之前选定的提交解决,就跳过针对该子任务的提交。
在每个目标子任务里,倾向于选择更长的生成内容,这对推理模型合理,对其他模型不太适用。
如果取消50次提交限制,并评估生成的所有提交,会得到以下结果:
国际信息学奥林匹克竞赛(2024年)无提交限制时模型的表现
基于R1轨迹训练的经验教训
在创建OlympicCoder模型时,研究者进行了大量SFT实验,以了解用于CodeForces数据集的各种筛选条件的作用。
发现open-r1/codeforces-cots的以下子集表现最佳:
solutions:R1根据问题陈述生成的方案。
solutions_w_editorials:R1根据问题陈述和解释正确解决方案的说明生成的方案。
请注意,这里只关注了C++解决方案,融入Python解决方案可能进一步提高性能。
用LiveCodeBench作为模型的测试平台,然后将表现最佳的checkpoints用于更具挑战性的IOI基准测试。
研究者测试了各种超参数配置来训练模型,最终确定如下:
模型:Qwen2.5 Coder Instruct 7B和32B
轮数:10
有效批大小:128
学习率:4e-5
调度器:余弦衰减至峰值学习率的10%
上下文长度:7B为32,768个token,32B为22,528个token
样本打包是一种在训练中常用的加速方法,它将训练样本连接成大小相等的块,无需填充token。
打包后,样本可能会跨块边界重叠。不过,要是大部分样本比块小很多,这种重叠影响不大。
然而,对于从R1提取的推理轨迹,这可能会带来负面影响。
因为很多轨迹长,答案被截断的可能性高。这就导致训练时,它很难关注长上下文信息,尤其是问题和答案被分到不同块的时候。
如下图所示,打包会严重损害模型的性能。用打包时,模型几乎解不出LiveCodebench里的题;不用打包,性能在几个训练周期后趋于平稳。
这种差异可能是由于训练集仅包含C++解决方案,而LiveCodeBench仅评估Python的性能。
尽管如此,在所有分析过的数据集里,打包的效果都更差。
用较大的学习率获得最佳表现
在用Qwen进行的大多数SFT实验中,2e-5的学习率通常足以获得强大的性能。
但是,当将带有推理数据的SFT用于现有指令模型时,将学习率大幅提高到4e-5,性能会显著提升。
如下图所示,学习率每提高一倍,在LiveCodeBench上的得分就会提高近10分!
纳入解题思路无助于提升性能
在创建open-r1/codeforces-cots数据集中的solutions_w_editorials子集时,原以为给R1输入问题及解答,能获得更好的推理轨迹。
但出人意料的是,结果并非如此。训练时,直接从问题描述采样,反倒让性能有了一定的持续提升。
用
在测试模型时,发现个奇怪现象:拿领域内问题(比如竞技编程)提问,模型会在
可要是问领域外的问题,像「瑞士首都是哪」,模型就变回原来指令模型的回答方式了。
举个例子,简单问题「1+1等于几」,模型正常回答是这样:
用户:1+1等于多少?
模型回答:2。这是最基础的数学加法题,1+1就是 2。这题常出现在面试或者在线测试里,用来考察答题人能不能快速解决问题。虽说答案大家都知道,但雇主能通过这题,看看答题人有没有批判性思维和解决问题的能力。而且,这也是数学里复杂运算的基础。下次再有人问,你就放心答2就行!
这段代码里,定义了add_numbers函数,输入两个数返回它们的和。接着用1和1调用函数,把结果存进result,再打印出来。
但要是预先在模型回答里加上
用户:1+1等于多少?
模型回答:
这说明,将推理轨迹融入现有预训练模型时,得在最终模型的对话模板里,默认强制加上预填充。DeepSeek处理提炼模型用的也是这招,这可能就是原因所在。
把这些经验都用上,团队做出了OlympicCoder-7B,性能和DeepSeek的提炼模型差不多,比基础的Qwen2.5-Coder强多了。
巧用8位优化器,拓展长上下文大模型
训练OlympicCoder-7B时,研究者发现DeepSpeed ZeRO-3在配有8个H100的单节点上,训练32k上下文长度的模型没问题。
可一旦把训练方案用于32B模型,就遇到了一系列内存问题。尤其是上下文长度超过20k token时,哪怕用16个节点,也会因内存不足而崩溃。
这可不妙,CodeForces-CoTs里20%的轨迹长度都超过20k token,意味着它们会在训练期间被截断。
问题的根源在于transformers和trl尚不支持上下文并行。
团队尝试了各种节省内存的办法,发现将FSDP与paged_adamw_8bit优化器结合起来,可以将上下文扩展到 22,528个token,但仍有9%的数据被截断。
更新
最近,团队在改进TRL中GRPO的实现方面取得了进展,带来了一些提升,进一步提高了效率、可扩展性和资源利用率。
以下是这次更新最重要的变化概要:
生成重复使用
GRPO的主要瓶颈与其他在线方法相同:生成过程需要时间。
提高GRPO样本效率的一个关键方法是在优化过程中多次重用生成的样本,而不是在单次使用后丢弃。这一技术实际上早在PPO中就已经引入。
对于GRPO,样本重用的次数用μ表示。
现在,可以多次重用生成的样本,从而显著加快处理速度。
training_args = GRPOConfig(..., num_iterations=...)
不过需要注意的是,如果μ过大,可能会对学习产生负面影响。根据他们的经验,2到4之间的值是一个较好的平衡点。
奖励加权
在训练模型时,并非所有奖励都同等重要。例如,可能希望模型优先关注正确性而非格式,而不是平等对待两者。
为了解决这个问题,现在可以为不同的奖励分配不同的权重,从而更精细地控制优化过程。通过调整这些权重,我们可以引导模型更加关注特定任务中最重要的方面。
下面代码配置了一个GRPO训练器,通过GRPOConfig设置训练参数,并为两个奖励函数(very_important_reward 和 less_important_reward)分配了不同的权重(0.9和0.1),以控制优化过程中不同奖励的重要性。
defvery_important_reward(completions, **kwargs):...
defless_important_reward(completions, **kwargs):...
training_args= GRPOConfig(...,reward_weights= [0.9, 0.1],)trainer= GRPOTrainer(...,reward_funcs= [very_important_reward, less_important_reward],args= training_args,)
其他改进
GRPO还进行了一些较小但影响深远的改进:
PEFT+vLLM集成:现在可以将PEFT(参数高效微调)与vLLM结合使用,将高效微调与优化的推理相结合,提升可扩展性。
梯度检查点:通过重新计算某些激活值而非存储它们来减少训练过程中的内存消耗,从而支持训练更大的模型。
优化的选择性Log Softmax计算:引入了一种新的Log Softmax计算方法,降低了训练期间的内存峰值。
团队当前的重点集中在两个关键领域:
提升生成速度:正在探索进一步的优化(如静态缓存),以使生成过程更快。
将GRPO扩展到多节点设置:正在努力使 GRPO 能够在多节点上扩展,从而支持训练更大的模型。
研究团队还进一步丰富了之前发布的OpenR1-Math-Raw数据集,添加了新的元数据,以在过滤和验证过程中支持更明智的决策。具体来说,新增了以下列:
reparsed_answers:注意到答案列中的许多条目要么LaTeX格式不正确,要么仅包含部分答案。此外,由于一些问题是多项选择题,正确答案本身及其对应的字母都应视为有效响应。
为此,他们使用Llama-3.3-70B-Instruct模型从解决方案列中重新提取了所有答案,确保reparsed_answers包含正确答案,并在多项选择题中同时包含对应的字母。
correctness:基于模型的答案验证可能需要大量资源。因此,团队使用Llama-3.3-70B-Instruct作为评判模型,并结合math_verify对答案列和reparsed_answers列进行了评估。
在数据受限的过滤场景中,精确度和召回率都是重要的考虑因素。
因此,研究团队没有为每个实验设定相同的token预算,而是对所有数据进行了单个周期的训练。
他们选择了Qwen7B-Instruct模型,并通过RoPE扩展将其微调至32k的上下文长度,采用余弦调度。
为了跟踪性能进展,每隔40步在AIME-24、AIME-25和MATH-500数据集上使用lighteval评估模型。
下图展示了在AIME-24、AIME-25和MATH-500数据集上,不同方法(包括无限制、LLaMA验证、math_verify等)对模型准确率随训练步数变化的影响。
结果显示,验证显著影响了早期性能。过滤在前40步尤为重要。
在MATH-500数据集上,较严格的验证方法在早期阶段显著提升了性能(例如,no_restrictions得分为 0.61,而LandMV 为0.72)。然而,随着训练的进行,这种性能差距逐渐缩小,拥有更多样本(即使其中包含错误)变得更有利。
使用math_verify过滤的数据集始终表现出较低的训练损失。