32B IOI奧賽擊敗DeepSeek-R1!Open R1開源復刻第三彈,下一步R1-Zero
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輸入問題及解答,能獲得更好的推理軌跡。
但出人意料的是,結果并非如此。訓練時,直接從問題描述采樣,反倒讓性能有了一定的持續提升。
用<think>預填充以持續生成長CoT
在測試模型時,發現個奇怪現象:拿領域內問題(比如競技編程)提問,模型會在<think>和</think>標簽里,輸出長長的CoT,然后給出答案。
可要是問領域外的問題,像「瑞士首都是哪」,模型就變回原來指令模型的回答方式了。
舉個例子,簡單問題「1+1等于幾」,模型正常回答是這樣:
用戶:1+1等于多少?
模型回答:2。這是最基礎的數學加法題,1+1就是 2。這題常出現在面試或者在線測試里,用來考察答題人能不能快速解決問題。雖說答案大家都知道,但雇主能通過這題,看看答題人有沒有批判性思維和解決問題的能力。而且,這也是數學里復雜運算的基礎。下次再有人問,你就放心答2就行!
這段代碼里,定義了add_numbers函數,輸入兩個數返回它們的和。接著用1和1調用函數,把結果存進result,再打印出來。
但要是預先在模型回答里加上<think>,情況就不一樣了:
用戶:1+1等于多少?
模型回答:<think>好的,我要回答「1+1等于多少」這個問題。1+1等于2。不過,會不會有別的考法?或者不同進制?十進制里是2,二進制就是10了。但這問題問得直接,用戶估計就想要個簡單答案,那就是2。直接說出來?</think>對,1+1等于2,你還有別的想了解的嗎?
這說明,將推理軌跡融入現有預訓練模型時,得在最終模型的對話模板里,默認強制加上預填充。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,樣本重用的次數用μ表示。
現在,可以多次重用生成的樣本,從而顯著加快處理速度。
from trl import GRPOConfig
training_args = GRPOConfig(..., num_iteratinotallow=...)
不過需要注意的是,如果μ過大,可能會對學習產生負面影響。根據他們的經驗,2到4之間的值是一個較好的平衡點。
獎勵加權
在訓練模型時,并非所有獎勵都同等重要。例如,可能希望模型優先關注正確性而非格式,而不是平等對待兩者。
為了解決這個問題,現在可以為不同的獎勵分配不同的權重,從而更精細地控制優化過程。通過調整這些權重,我們可以引導模型更加關注特定任務中最重要的方面。
下面代碼配置了一個GRPO訓練器,通過GRPOConfig設置訓練參數,并為兩個獎勵函數(very_important_reward 和 less_important_reward)分配了不同的權重(0.9和0.1),以控制優化過程中不同獎勵的重要性。
from trl import GRPOConfig, GRPOTrainer
def very_important_reward(completions, **kwargs):
...
def less_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 能夠在多節點上擴展,從而支持訓練更大的模型。
Open R1 Math-Dataset更新
研究團隊還進一步豐富了之前發布的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過濾的數據集始終表現出較低的訓練損失。
研究團隊推測math_verify能有效識別特定任務子集(主要是數值型任務),而基于Llama的驗證或未過濾的數據集則保持了更廣泛的數據多樣性。
不僅如此,還有個意外發現,未過濾數據集并未嚴重退化。盡管包含錯誤樣本,no_restrictions數據集在長時間訓練中仍保持了競爭力。
下一步是什么?
通過這次更新,團隊現已具備完成復刻DeepSeek-R1計劃第1步和第2步的主要組件:
在接下來的幾周,他們計劃重點關注:
- 完善蒸餾數據集的組合,以訓練通用推理模型。
- 將GRPO擴展到更大的模型,如Qwen/Qwen2.5-Coder-32B-Instruct,從而推導出R1-Zero變體。
- 結合來自數學和代碼等多領域的獎勵信號,并引入獎勵模型來評分非推理數據。