Open R1:更新#3

社群文章 釋出於 2025年3月11日

image/png

在過去的幾周裡,我們致力於復現 DeepSeek-R1 配方中競爭性程式設計(程式碼推理)方面的內容。

在此文章中,我們很高興能分享

  • CodeForces-CoTs 的構建:一個包含近 10 萬個高質量樣本的資料集,這些樣本從 R1 中提煉而來,用於生成 C++ 和 Python 解決方案。
  • IOI 基準:一個包含 2024 年國際資訊學奧林匹克競賽 (IOI) 挑戰性問題的新基準。
  • OlympicCoder:兩個經過微調的 7B 和 32B 程式碼模型,它們在 IOI 問題上的表現優於 Claude 3.7 Sonnet 等閉源前沿模型。

以下是 OlympicCoder 模型與各種指令微調和推理模型相比的概述。我們發現,在 CodeForces-CoTs 上訓練的模型表現出色,OlympicCoder-32B 的效能優於我們測試過的所有開源模型,其中一些模型的規模甚至超過 100 倍 🤯。

image/png

請繼續閱讀,瞭解我們如何構建資料集、基準和模型!

關鍵連結

CodeForces

國際資訊學奧林匹克競賽 (IOI)

OlympicCoder

CodeForces-CoTs 資料集

CodeForces 是競爭性程式設計者中最受歡迎的網站之一,定期舉辦競賽,參與者必須解決具有挑戰性的演算法最佳化問題。這些問題的挑戰性使其成為一個有趣的資料集,可以改進和測試模型的程式碼推理能力。

儘管之前的一些工作(如 DeepMind 的 CodeContests 資料集)已經編譯了大量的 CodeForces 問題,但今天我們釋出了我們自己的 open-r1/codeforces 資料集,其中包含 1 萬多個問題,涵蓋了從最早的競賽到 2025 年的所有問題,其中約 3 千個問題未包含在 DeepMind 的資料集中。此外,對於約 60% 的問題,我們包含了題解,這是競賽組織者撰寫的解釋正確解決方案的說明。您還會找到從官方網站提取的每個問題的 3 個正確解決方案。

此外,我們正在釋出 open-r1/codeforces-cots,其中包含 DeepSeek-R1 在這些問題上生成的思維鏈,我們要求模型用 C++(競爭性程式設計中使用的主要語言)和 Python 生成解決方案,總計近 10 萬個樣本。

我們使用該資料集對 Qwen2.5 Coder Instruct 7B 和 32B 進行了微調,從而得到了我們的 OlympicCoder-7B 和 OlympicCoder-32B 模型。您將在本部落格文章的後續內容中找到有關這些模型的更多詳細資訊。

程式碼可驗證性危機

儘管 DeepMind 的競賽資料集及其他包含競爭性程式設計問題的資料集包含測試用例並聲稱可驗證,但這些測試用例通常只是競賽網站上使用的完整測試套件的一小部分。特別是 CodeForces,其顯示的測試用例限制在約 500 個字元以內,這意味著這些資料集僅包含符合此限制的較短、較簡單的測試用例。

舉個例子,我們選取了 7 個 R1 生成的解決方案通過了所有公開測試用例的問題,並嘗試將它們提交到 CodeForces 平臺。

image/png

儘管它們通過了較短的測試,但這些解決方案中的每一個都在完整的測試集上失敗了。這凸顯了對新的完全可驗證的競爭性程式設計資料集的需求。雖然我們計劃嘗試基於模型的解決方案來生成和驗證可能在未來新增到 CodeForces 資料集中的其他具有挑戰性的測試,但目前我們正在其他地方尋找完全可用的問題資料。

國際資訊學奧林匹克競賽 (IOI)

國際資訊學奧林匹克競賽 (IOI) 是五項國際科學奧林匹克競賽之一(如果您熟悉 AIME,IOI 相當於 IMO 的程式設計競賽,最優秀的 AIME 學生會被邀請參加),它測試一小部分高中生(每個國家 4 名)解決複雜演算法問題的能力。

這些問題極具挑戰性,並且完整的測試集在允許的 (CC-BY) 許可下可用併發布。這意味著 IOI 是測試模型程式碼推理能力的完美資料集。

在 IOI 中,每個問題都有幾個子任務,每個子任務都有不同的輸入限制。要解決一個子任務,提交的程式需要在(嚴格的)時間限制內透過其所有測試用例。雖然最終子任務通常是“完整問題”,但一些子任務實際上描述了一個更容易(更受限制)的問題,參賽者通常會針對特定的子任務以獲得部分分數,而不是僅僅嘗試解決完整問題(獲得滿分的相對較少)。

遵循 OpenAI 最近的一篇論文,其中 o1 即時參加了 IOI’2024(最新一屆),我們同樣處理了 IOI’2024(以及 2020 年之前的 IOI)的所有問題,並將它們拆分為子任務,以便每個提示都要求模型解決一個特定的子任務。我們釋出了處理後的問題描述,以及執行它們和測試用例所需的評分/檢查檔案,這些檔案可在 open-r1/ioiopen-r1/ioi-test-cases 中找到。

我們建立了自定義程式碼來執行解決方案(許多問題設定複雜,需要一個“管理器”程序與執行使用者提交的幾個程序和特殊檢查器通訊以驗證解決方案),並根據 IOI 規則進行評分,這些程式碼可在 https://github.com/huggingface/ioi 找到,並且評估了 40 多個領先的推理模型在 IOI’2024 上的表現

在 IOI 中,參賽者每個問題有 50 次提交限制。我們為每個子任務生成了 50 次提交,然後應用了與 OpenAI 對 o1-ioi 使用的類似的選擇策略,以獲得每個問題在比賽條件下的分數。結果可以在下面找到,其中水平線代表真實比賽資料中銅牌、銀牌和金牌模型的閾值。雖然 o1 非常接近銅牌,但最終沒有模型能達到獎牌閾值(參賽者中位數的 50%)。

image/png

我們的 OlympicCoder 模型(紅色)與其他前沿模型相比表現相當不錯,甚至超越了一些閉源模型(金色),例如 claude-3.7-sonnet-thinking,而 OlympicCoder-32B 在 50 次提交限制設定下甚至優於 o1-mini 和我們從中提取的 DeepSeek-R1 模型。

提交策略

需要注意的是,我們的提交策略可能會懲罰非推理模型,例如 Qwen-2.5-Coder-32B-Instruct,OlympicCoder-32B 的基礎模型。為了模擬真實比賽條件,即提交的分數只有在實際提交後才可知曉,我們採用了類似於 OpenAI 對 o1-ioi 使用的輪詢提交策略:我們首先提交一個針對問題最後一個子任務的解決方案,然後是倒數第二個,依此類推,只在選中提交時才評估解決方案。我們跳過針對已由先前選擇的提交解決的子任務的提交,並且在每個目標子任務中,我們更喜歡來自更長生成的提交——這是一個對推理模型有意義的標準,但對其他模型則不然。

如果我們取消 50 次提交限制(這將使我們脫離比賽條件),並評估我們生成的所有提交(每個子任務 50 次),我們將獲得以下結果

image/png

從 R1 軌跡訓練程式碼模型中吸取的教訓

在建立 OlympicCoder 模型時,我們進行了大量 SFT 實驗,以瞭解 CodeForces 資料集上各種過濾器的作用。我們發現 open-r1/codeforces-cots 的以下子集提供了最佳的整體效能:

  • solutions:根據問題描述生成的 R1 解決方案。
  • solutions_w_editorials:根據問題描述和解釋正確解決方案的說明生成的 R1 解決方案。

請注意,我們只關注了 C++ 解決方案,但如果融合 Python 解決方案,效能可能會進一步提升。

我們使用 LiveCodeBench 作為我們模型的測試平臺,然後將表現最佳的檢查點透過更難的 IOI 基準測試。我們測試了各種超引數配置來訓練我們的模型,並確定了以下配置:

  • 模型:Qwen2.5 Coder Instruct 7B 和 32B
  • 訓練輪次:10
  • 有效批大小:128
  • 學習率:4e-5
  • 排程器:餘弦排程,衰減到峰值學習率的 10%
  • 上下文大小:7B 模型為 32,768 個 token,32B 模型為 22,528 個 token

下面我們分享一些從 R1 推理軌跡上微調 Qwen2.5 Coder 模型中學到的經驗。

教訓 1:樣本打包會損害推理效能

樣本打包是一種廣泛使用的方法,可以有效地處理可變長度序列並加速訓練。如下圖所示,其工作原理是將訓練樣本(彩色)連線成大小相等的塊,從而消除了在批次之間使用填充標記(灰色)的需要。

image/png

透過打包,樣本可以跨每個塊的邊界重疊,但實際上,如果大多數樣本遠小於塊大小,這並不太重要。

然而,對於我們從 R1 中提取的推理軌跡,我們想知道打包是否會損害效能,因為許多軌跡很長,並且剪下答案的比例可能很高。這意味著訓練模型關注長上下文資訊可能很困難,特別是當問題和答案被打包到不同的塊中時。

如下面的驚人圖所示,我們發現打包極大地損害了模型的效能:使用打包時,模型幾乎無法解決 LiveCodebench 中的問題,但沒有打包時,效能在幾個 epoch 後才得到改善並趨於穩定。

image/png

我們懷疑這種顯著差異是因為我們的訓練集僅包含 C++ 解決方案,而 LiveCodeBench 僅評估 Python 效能。儘管如此,我們發現,在我們消融的所有資料集中,打包始終表現更差。

教訓 2:使用較大的學習率以獲得最佳效能

對於我們使用 Qwen 模型進行的大多數 SFT 實驗,2e-5 的學習率通常足以獲得強大的效能。然而,當將 SFT 與推理資料應用於現有指令模型時,我們發現透過使用更大的學習率 4e-5 可以獲得顯著的效能提升。如下圖所示,學習率每次加倍,LiveCodeBench 上的效能幾乎提升了 10 個百分點!

image/png

教訓 3:包含題解並不能提升效能

當我們建立 open-r1/codeforces-cotssolutions_w_editorials 子集時,我們的直覺是,透過向 R1 提供問題和解決方案,我們將獲得更好的推理軌跡。令人驚訝的是,這種直覺在評估過程中並未得到證實,從問題描述中進行樸素取樣在訓練過程中帶來了適度但持續的提升。

image/png

教訓 4:使用 <think> 進行預填充以持續啟用長 CoT

在對我們的一些模型進行感覺測試時,我們遇到了一種特殊現象:如果以領域內查詢(如競爭性編碼)進行提示,模型會發出預期的長 CoT,其中包含 <think></think> 標記,然後是最終答案。然而,對於領域外查詢(如“瑞士的首都是什麼”),模型會恢復到原始指令模型的行為!

這是一個簡單查詢未能返回長 CoT 的示例:

使用者:1+1 是多少?

助手:2。這是最簡單的數學加法問題之一。1 + 1 等於 2。這個問題經常被用作謎語或謎題,尤其是在工作面試或線上測試中,以確定候選人快速解決問題的能力。雖然對大多數人來說這可能很明顯,但對於僱主來說,它可能是一個有用的問題,用於評估批判性思維和解決問題的能力。此外,它是數學中的一個重要概念,構成了更復雜算術運算的基礎。所以下次有人問你 1+1 是什麼時,你可以自信地說它是 2!

# Define a function to calculate sum of two numbers
def add_numbers(a, b):
    return a + b

# Example usage of the function
result = add_numbers(1, 1)
print("The sum of 1 + 1 is:", result)

在這個解決方案中,我們定義了一個函式 add_numbers,它接受兩個引數 ab 並返回它們的和。然後我們用 1 和 1 呼叫這個函式,將結果儲存在變數 result 中,然後打印出來。這演示了一種計算和顯示兩個整數和的直接方法。這個例子簡單而有效地說明了 Python 中的基本算術運算。

然而,如果我們用 <think> 預填充助手的回覆,就會出現所需行為:

使用者:1+1 是多少?

助手:<think> 好的,我需要回答“1+1 是什麼?”這個問題。讓我想想。哦對了,1 加 1 等於 2。但是等等,也許還有更多。比如,如果有人用不同的方式問呢?或者考慮其他進位制?比如在十進位制中是 2,但在二進位制中是 10。嗯,但問題很簡單。使用者可能期望得到簡單的答案。所以這裡的正確答案是 2。我應該直接說出來嗎?</think> 是的,1 + 1 等於 2。您還想了解這個計算的任何其他資訊嗎?

這表明,在將推理軌跡提煉到現有預訓練模型中時,應該在最終模型的聊天模板中預設強制進行預填充。巧合的是,DeepSeek 在其提煉模型中也這樣做了,而上述行為可能就是原因。(猜測在流行的聊天 UI 上點選“思考”按鈕是否只是預填充了助手回覆也很有趣。)

結合所有這些經驗,我們得到了 OlympicCoder-7B,其效能與 DeepSeek 的提煉模型相當,並顯著優於基礎 Qwen2.5-Coder 模型。

image/png

教訓 5:使用 8 位最佳化器來擴充套件長上下文大型模型

在 OlympicCoder-7B 的整個訓練過程中,我們發現 DeepSpeed ZeRO-3 足以在單個 8 個 H100 節點上訓練每個模型,上下文長度為 32k。然而,當我們嘗試將我們的配方擴充套件到 32B 時,我們遇到了許多記憶體問題。特別是,我們的執行在上下文擴充套件到 20k token 後就會 OOM,即使在 16 個節點上也是如此 😢。這並不理想,因為 CodeForces-CoTs 軌跡中 20% 的長度超過 20k token,這意味著它們將在訓練期間被截斷。

問題的根本原因是 transformerstrl 尚不支援上下文並行,儘管可以檢視此 問題 來跟蹤進度。

與此同時,我們探索了各種節省記憶體的技術,發現將 FSDP 與 paged_adamw_8bit 最佳化器結合使用,使我們能夠將上下文擴充套件到 22,528 個 token:雖然仍不理想,但只有 9% 的資料被截斷。

更新

GRPO 更新

TRL 中的 GRPO 實現取得了最新進展,帶來了進一步提升效率、可擴充套件性和資源利用率的增強功能。以下是自上次更新以來最重要的更改摘要:

生成複用

GRPO 的主要瓶頸之一與任何線上方法相同:生成需要時間。提高 GRPO 樣本效率的關鍵方法是在最佳化過程中多次複用生成的樣本,而不是在使用一次後就將其丟棄。這項技術實際上在很久以前就隨著 PPO 被引入。

對於 GRPO,樣本被複用的次數表示為 μ

image/png

現在可以多次複用生成的樣本,顯著加快了流程。

from trl import GRPOConfig

training_args = GRPOConfig(..., num_iterations=...)

image/png

但是,請注意——如果 μ 過大,可能會對學習產生負面影響。根據我們的經驗,一個好的平衡點是介於 2 到 4 之間。

獎勵加權

在訓練模型時,並非所有獎勵都同等重要。例如,我們可能希望模型優先考慮正確性而不是格式,而不是同等對待這兩個方面。

為了解決這個問題,現在可以為不同的獎勵分配不同的權重,從而對最佳化過程進行更精細的控制。透過調整這些權重,我們可以引導模型更多地關注給定任務中最重要的方面。

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 (Parameter-Efficient Fine-Tuning) 和 vLLM 結合使用,將高效微調與最佳化推理相結合,以實現更好的可擴充套件性。
  • 梯度檢查點 – 此功能已新增,透過重新計算某些啟用而不是儲存它們來減少訓練期間的記憶體消耗,從而可以訓練更大的模型。
  • 最佳化選擇性對數 softmax 計算 – 引入了一種新的對數 softmax 計算方法,減少了訓練期間的記憶體峰值。

下一步和進行中的工作

目前的重點是兩個關鍵領域:

  1. 提高生成速度 – 正在探索進一步的最佳化(如靜態快取),以使生成過程更快。
  2. 將 GRPO 擴充套件到多節點設定 – 正在努力使 GRPO 能夠跨多個節點進行擴充套件,從而可以訓練更大的模型。

Open R1 數學資料集更新

我們進一步豐富了之前釋出的 OpenR1-Math-Raw 資料集,增加了新的元資料,以實現更明智的過濾和驗證決策。具體來說,我們添加了以下列:

  • reparsed_answers:我們觀察到 answer 列中的許多條目要麼格式不正確,要麼只包含部分答案。此外,由於有些問題是多項選擇題,因此正確答案本身和相應的字母都應被視為有效響應。為了解決這個問題,我們使用 Llama-3.3-70B-Instruct 模型從 solution 列重新提取了所有答案,以確保 reparsed_answers 包含正確答案,在多項選擇題的情況下還包含相應的字母。我們相信這項補充將對社群極具價值,提高 GRPO 期間的列評分準確性和驗證過程。
  • correctness:當依賴基於模型的驗證時,執行答案驗證可能會消耗大量資源。因此,我們使用 Llama-3.3-70B-Instruct 作為判斷器評估了所有解決方案,同時針對 answerreparsed_answers 列運行了 math_verify

實驗詳情

為了幫助社群理解基於驗證的過濾對 SFT 蒸餾數學資料集的影響,我們進行了幾項消融實驗。我們從隨機選擇的 20 萬個樣本池開始,根據以下過濾規則建立了六個不同的 SFT 資料集:

  1. no_restrictions (20 萬) - 未應用任何過濾。
  2. llama_verification (12.4 萬) - 經過 LLAMA-70B 驗證為正確的樣本。
  3. math_verify_answer (8.87 萬) - 經 math_verifyanswer 列上驗證為正確的樣本。
  4. math_verify_reparsed (10.1 萬) - 經 math_verifyreparsed_answers 列上驗證為正確的樣本。
  5. llama_verification_or_math_verify_reparsed (LorMV) (15.4 萬) - 資料集 2 和 4 的並集。
  6. llama_verification_and_math_verify_reparsed (LandMV) (7.12 萬) - 資料集 2 和 4 的交集。

訓練與評估

對於資料受限設定中的過濾,精度和召回率都是重要的考量因素。因此,我們沒有以相同的 token 預算執行每個實驗,而是對所有資料訓練了一個 epoch。對於模型,我們選擇了 Qwen7B-Instruct,並使用 RoPE 擴充套件將其微調到 32k 的上下文長度和餘弦排程。為了跟蹤效能進展,我們每隔 40 步使用 lighteval 在 AIME-24、AIME-25 和 MATH-500 上評估模型。結果總結如下:

image/png

主要觀察

  • 驗證顯著影響早期效能。在最初的 40 步中,過濾被證明尤其重要。在 MATH-500 資料集上,更嚴格的驗證方法在早期階段提供了顯著的效能提升(例如,no_restrictions 得分為 0.61,而 LandMV 為 0.72)。然而,隨著訓練的進行,這種效能差距減小,並且擁有更多樣本被證明是有益的——即使其中一些包含錯誤。
  • 訓練損失在不同資料集之間存在顯著差異。使用 math_verify 過濾的資料集表現出持續較低的訓練損失。我們假設 math_verify 有效地識別了特定的任務子集(主要是數值任務),而基於 Llama 的驗證或未過濾的資料集則保持了更廣泛的資料種類。
  • 令人驚訝的是,未過濾的資料集沒有遭受嚴重的效能下降。儘管包含不正確的樣本,但 no_restrictions 資料集在長時間訓練執行中仍保持了競爭性效能。

建議

根據我們的發現,最佳的過濾方法在很大程度上取決於訓練 token 預算。對於較短的執行,更嚴格的驗證方法提供了顯著優勢。作為一般建議,我們建議將 Llama 驗證與 math_verify 結合使用——如 LorMV 資料集所示。

推理課程

Hugging Face 學習團隊正在致力於提供關於強化學習、GRPO 以及使用 TRL 訓練推理模型的可訪問材料。它包括 Maxime Labonne、Unsloth 和 Marimo 的教程和演示。如果您正在這個快速發展的領域中尋找一個好的起點,請檢視推理課程

社群亮點

過去幾周,GRPO 在各種任務中得到了持續探索,同時還發布了幾個針對數學和程式碼之外領域的新推理資料集。以下是我們發現特別令人興奮的一些釋出:

GRPO 探索

  • Unsloth 的最佳化嚮導進一步降低了使用 LoRA 訓練 GRPO 模型所需的 VRAM,現在 1.5B 模型僅需 5GB 🧙
  • Kalomaze 寫了一篇非常棒的部落格文章,介紹瞭如何為 GRPO 選擇合適的超引數。他們還觀察到小於 7B 的模型收斂速度往往慢得多,這意味著您應該在這些規模下探索新的想法,然後再斷定它們不起作用。
  • Hrishbh Dalal 已經證明,您可以使用 GRPO 來教導大型語言模型解決數獨謎題!
  • Yutai Li 及其合著者發表了一篇非常好的論文,表明對於小型模型,最好從更強大的教師模型中提煉出長短 CoT 資料的混合體

推理資料集

  • KodKode 釋出了一個非常大的資料集,包含約 50 萬個程式設計樣本。這看起來是 CodeForces-CoTs 的絕佳補充,我們很高興能用它訓練一些新模型!
  • GeneralReasoning 團隊已開始釋出高質量且多樣化的資料集,例如 GeneralReasoning/GeneralThought-323K,這些資料集涵蓋了比公開可用資料更廣泛的領域和模型。他們還有一個不錯的網站,允許您探索資料,以及

接下來是什麼?

透過這次更新,我們現在已經具備了完成我們復現計劃的第 1 步和第 2 步的主要部分。

image/png

在接下來的幾周,我們計劃重點關注:

  • 完善蒸餾資料集的混合,以訓練通用推理器。
  • 將 GRPO 擴充套件到更大的模型,例如 Qwen/Qwen2.5-Coder-32B-Instruct,以便我們可以推匯出 R1-Zero 變體。
  • 結合來自數學和程式碼等多個領域的獎勵訊號,並整合獎勵模型以評估非推理資料。

社群

感謝您的文章。我很想看看訓練模型的程式碼。例如,我想用一種新的程式語言訓練一個模型,我該怎麼做?

·
文章作者

您好 @aaurelions 我們使用 open-r1 程式碼庫訓練了這些模型。您可以在此處找到並調整配方:https://github.com/huggingface/open-r1/tree/main/recipes#olympiccoder

KodKode -> KodCode

您的程式碼中如何實現打包?您是否嘗試過使用 4D 注意力掩碼來避免您提到的樣本重疊?

·
文章作者

由於我們使用了 FlashAttention2,我們不需要使用 4D 打包。相反,我們以 2D 方式打包序列,並讓 FA2 處理其餘部分。Axolotl 在這裡有一個很好的視覺化:https://axolotl-ai-cloud.github.io/axolotl/docs/multipack.html

僅使用 open-r1/codeforces 進行 SFT 就獲得了頂級效能?令人印象深刻!

接下來你會做編碼強化學習嗎?

·
文章作者

是的,這是接下來的任務——敬請期待!

嗨,感謝您的文章,它非常有用!!如果我沒弄錯,有一些小錯別字:)

  • "While o1 comes very close to bronze, ultimately no model would reach the medal threshold (50th percentile of contestants)." -> "While o3-mini comes very close to bronze, ultimately no model would reach the medal threshold (50th percentile of contestants)."
  • "Our OlympicCoder models (in red), do fairly well compared to other frontier models, even surpassing some closed source models (in gold)" -> "Our OlympicCoder models (in purple), do fairly well compared to other frontier models, even surpassing some closed source models (in gold)"

您最終使用了哪個檢查點來評估 IOI?是 10 個 epoch 之後的那個檢查點還是中間的某個檢查點?

很棒的博文!感謝這項出色的工作!我們成功地訓練了一個 SQL 文字到程式碼模型,使用 7B 模型實現了與超過 400B 引數的模型相當的效能!

請在此處檢視我們的 Think2SQL 論文:https://huggingface.co/papers/2504.15077

再次感謝您的出色工作!

註冊登入評論

© . This site is unofficial and not affiliated with Hugging Face, Inc.