Hacker News 热门(buzzing.cc 中文翻译)
精选
72AI 编辑部评分,满分 100

DeepSeek-R1 的开源实现

2026-06-12 01:07· 66天前· yogthos
AI 导读

DeepSeek-R1 的开源复现项目已在 GitHub 发布,在 Hacker News 上获得 101 个积分。该项目旨在以开源方式复现 DeepSeek-R1 模型。

推荐理由

Hugging Face 把 DeepSeek-R1 的完整训练管线拆解得清清楚楚,从数据蒸馏到 GRPO 强化学习都开源了,还放出了复现的模型和数据集。想理解或自己训练推理模型的开发者,这大概是目前最实用的路线图。

正文 · AI 翻译

完全开源的 DeepSeek-R1 复现项目。本仓库仍在开发中,让我们一起构建它!

目录

  1. 概述
  2. 实施方案
  3. 安装
  4. Training models
    • 监督微调
    • GRPO
  5. 模型评估
  6. 复现 DeepSeek 的评估结果
  7. Data generation
    • 从精简蒸馏 R1 模型生成数据
    • 从 DeepSeek-R1 生成数据
  8. 贡献指南

概述

本仓库的目标是构建 R1 流程中缺失的环节,使所有人都能复现并在此基础上进行开发。该项目设计简洁,主要包含以下内容:

  • src/open_r1: contains the scripts to train models as well as generate synthetic data:
    • grpo.py:在给定数据集上使用 GRPO 训练模型。
    • sft.py:在数据集上对模型执行简单的监督微调。
    • generate.py:使用 Distilabel 从模型生成合成数据。
  • Makefile:包含利用上述脚本执行 R1 流程各步骤的易用命令。

实施方案

我们将以 DeepSeek-R1 技术报告为指南,该流程大致可分为三个主要步骤:

  • 步骤 1:通过从 DeepSeek-R1 蒸馏高质量语料库,复现 R1-Distill 模型。
  • 步骤 2:复现 DeepSeek 用于创建 R1-Zero 的纯强化学习流程。这可能涉及为数学、推理和代码领域策划新的大规模数据集。
  • 步骤 3:展示如何通过多阶段训练,从基础模型过渡到强化学习调优模型。

新闻 🗞️

  • 🧑‍🍳 [2025/05/26](步骤 1 已完成!)我们发布了 Mixture-of-Thoughts——一个包含 35 万条经过验证的推理轨迹的精选推理数据集,这些轨迹从 R1 蒸馏而来。该数据集涵盖数学、编程和科学领域的任务,旨在教会语言模型逐步推理。我们还提供了训练 OpenR1-Distill-7B 的配方,该模型复现了 deepseek-ai/DeepSeek-R1-Distill-Qwen-7B 的推理能力,标志着 Open R1 项目步骤 1 的完成。
  • ⚡️ [2025/03/11](更新 #3):我们发布了 CodeForces-CoTs 数据集,包含 1 万个竞赛编程问题和从 R1 蒸馏的 10 万个解题方案。我们还发布了 IOI24:一个包含国际奥赛极难问题的新基准。在 CodeForces-CoTs 上训练的 7B Qwen 模型在 IOI24 上可超越 Claude 3.7 Sonnet,而 32B 模型则可超越 R1 本身。
  • ∞ [2025/02/10](更新 #2):我们发布了 OpenR1-Math-220k 数据集,该数据集包含从 R1 在新版 NuminaMath 上蒸馏得到的 22 万条推理轨迹。基于该数据集训练的模型,其性能与 DeepSeek 蒸馏模型相当。
  • 🔥 [2025/02/02](更新 #1):我们实现了训练、推理和评估流程的第一部分。开工!

安装

[!CAUTION] 相关库依赖 CUDA 12.4。如果遇到与段错误相关的报错,请使用 nvcc --version 再次确认系统运行的版本。

要运行本项目中的代码,首先请使用 uv 等工具创建一个 Python 虚拟环境。安装 uv 请参考 UV 安装指南。

[!NOTE] 快捷方式:运行 make install 来安装开发库(具体步骤如下)。之后,如果一切配置正确,你就可以尝试使用 Open-R1 模型了。

uv venv openr1 --python 3.11 && source openr1/bin/activate && uv pip install --upgrade pip

[!TIP] 对于 Hugging Face 集群用户,请在 .bashrc 中添加 export UV_LINK_MODE=copy,以抑制 uv 的缓存警告。

接下来,安装 vLLM 和 FlashAttention:

uv pip install vllm==0.8.5.post1
uv pip install setuptools && uv pip install flash-attn --no-build-isolation

这也会安装 PyTorch v2.6.0,使用此版本非常重要,因为 vLLM 的二进制文件是针对它编译的。然后,你可以通过 pip install -e .[模式列表] 为你的特定用例安装其余依赖。对于大多数贡献者,我们推荐:

GIT_LFS_SKIP_SMUDGE=1 uv pip install -e ".[dev]"

接下来,按如下方式登录你的 Hugging Face 和 Weights and Biases 账户:

huggingface-cli login
wandb login

最后,检查你的系统是否已安装 Git LFS,以便你能从 Hugging Face Hub 加载和推送模型/数据集:

git-lfs --version

如果未安装,请运行:

sudo apt-get install git-lfs

训练模型

[!NOTE] 下面的训练命令是针对配备 8 块 H100(80GB)的节点配置的。对于不同的硬件和拓扑结构,你可能需要调整批次大小和梯度累积步数。

我们支持使用 DDP 或 DeepSpeed(ZeRO-2 和 ZeRO-3)训练模型。例如,要在包含推理轨迹(如 open-r1/Mixture-of-Thoughts)的 DeepSeek-R1 蒸馏数据集上执行 SFT,请运行:

# Train via command line
accelerate launch --config_file=recipes/accelerate_configs/zero3.yaml src/open_r1/sft.py \
    --model_name_or_path open-r1/Qwen2.5-Math-7B-RoPE-300k \
    --dataset_name open-r1/Mixture-of-Thoughts \
    --dataset_config all \
    --eos_token '<|im_end|>' \
    --learning_rate 4.0e-5 \
    --num_train_epochs 5 \
    --max_seq_length 32768 \
    --per_device_train_batch_size 2 \
    --gradient_checkpointing \
    --bf16 \
    --use_liger_kernel \
    --output_dir data/OpenR1-Distill-7B

# Train via YAML config
accelerate launch --config_file recipes/accelerate_configs/zero3.yaml src/open_r1/sft.py \
    --config recipes/OpenR1-Distill-7B/sft/config_distill.yaml

目前,支持以下任务:

  • 监督微调 sft
  • 分组相对策略优化 grpo

[!TIP] 如果你增加/减少 GPU 数量,我们建议同时调整每设备批次大小或梯度累积步数,以保持全局批次大小不变。

默认情况下,这些脚本会将每个模型推送到你的 Hugging Face Hub 用户名下,即 `{username}/{model_name}-{task}`。你可以通过在命令后追加参数来覆盖每个 YAML 配置文件中的参数,操作如下:

# Change the base model to a smaller variant
accelerate launch --config_file recipes/accelerate_configs/zero3.yaml src/open_r1/sft.py \
    --config recipes/OpenR1-Distill-7B/sft/config_distill.yaml \
    --model_name_or_path Qwen/Qwen3-0.6B-Base \
    --hub_model_id OpenR1-Distill-0.6B \
    --output_dir data/OpenR1-Distill-0.6B

如果你还想覆盖 Weights and Biases 的默认设置,可以按如下方式操作:

accelerate launch --config_file recipes/accelerate_configs/zero3.yaml src/open_r1/sft.py \
    --config recipes/OpenR1-Distill-7B/sft/config_distill.yaml
    --wandb_entity huggingface --wandb_project open-r1 --run_name Qwen2.5-1.5B-GRPO

🚨 警告 🚨

大多数基础模型(如 `meta-llama/Llama-3.2-1B`)没有聊天模板,因此我们在训练期间将 ChatML 设置为默认模板。然而,对于像 `Qwen/Qwen2.5-1.5B` 这样的 Qwen 基础模型,其分词器中已预定义了聊天模板,因此必须相应地设置 EOS token,例如:

# Align EOS token with chat template for Qwen base models
accelerate launch --config_file=recipes/accelerate_configs/zero3.yaml src/open_r1/sft.py \
    --model_name_or_path Qwen/Qwen2.5-1.5B \
+   --eos_token '<|im_end|>'
    --dataset_name open-r1/Mixture-of-Thoughts \
    --dataset_config all \
    --learning_rate 4.0e-5 \
    --num_train_epochs 1 \
    --max_seq_length 32768 \
    --per_device_train_batch_size 16 \
    --gradient_checkpointing \
    --bf16 \
    --use_liger_kernel \
    --output_dir data/Qwen2.5-1.5B-Open-R1-Distill

如果你想使用自定义聊天模板(例如 Llama 或 Gemma),则必须提供聊天模板及相关的 EOS token:

# Align EOS token with custom chat template
accelerate launch --config_file=recipes/accelerate_configs/zero3.yaml src/open_r1/sft.py \
    --model_name_or_path meta-llama/Llama-3.2-1B \
+   --chat_template "$(cat llama_chat_template.jinja)" \
+   --eos_token '<|eot_id|>' \
    --dataset_name open-r1/Mixture-of-Thoughts \
    --dataset_config all \
    --learning_rate 4.0e-5 \
    --num_train_epochs 1 \
    --max_seq_length 32768 \
    --per_device_train_batch_size 16 \
    --gradient_checkpointing \
    --bf16 \
    --use_liger_kernel \
    --output_dir data/Llama-3.2-1B-Open-R1-Distill

SFT 知识蒸馏

我们提供了一个配方,用于从相同的基础模型出发,复现 `deepseek-ai/DeepSeek-R1-Distill-Qwen-7B` 的推理能力。为此,请运行:

ACCELERATE_LOG_LEVEL=info accelerate launch --config_file recipes/accelerate_configs/zero3.yaml \
    src/open_r1/sft.py \
    --config recipes/OpenR1-Distill-7B/sft/config_distill.yaml

结果将得到一个类似 `open-r1/OpenR1-Distill-7B` 的模型,其下游性能如下:

模型 AIME 2024 MATH-500 GPQA Diamond LiveCodeBench v5
OpenR1-Distill-7B 52.7 89.0 52.8 39.4
DeepSeek-R1-Distill-Qwen-7B 51.3 93.5 52.4 37.4

你可以调整 YAML 配置文件,以在不同的基础模型或数据集上进行训练。

GRPO

我们使用 TRL 的 vLLM 后端,将训练扩展到跨多个节点的大型模型。对于在 8 个 GPU 上进行小型模型的单节点训练,请使用 `vllm_mode="colocate"`,让 vLLM 在与训练脚本相同的进程中运行:

ACCELERATE_LOG_LEVEL=info \
    accelerate launch --config_file recipes/accelerate_configs/zero3.yaml \
    src/open_r1/grpo.py --config recipes/DeepSeek-R1-Distill-Qwen-1.5B/grpo/config_demo.yaml \
    --vllm_mode colocate

[!警告] 经过蒸馏的 DeepSeek 模型所使用的聊天模板,会省略 `<think>` 和 `</think>` 标签内推理块的内容。它还会在助手回复中预填充 `<think>`,这会干扰格式奖励函数。为处理此问题,务必像 `recipes/DeepSeek-R1-Distill-Qwen-1.5B/grpo/config_demo.yaml` 中那样覆盖聊天模板。

对于 N+1 个节点的多节点训练(其中 1 个节点运行 vLLM 服务器,N 个节点运行训练),我们提供了一个示例 Slurm 脚本。例如,要在 1+1 个节点上通过数据并行运行上述示例,请运行:

sbatch --nodes=2 slurm/train.slurm --model Qwen2.5-1.5B-Instruct --task grpo --config demo --accelerator zero2 --dp 8 --tp 1

更多详情请参阅“在 Slurm 集群上启动作业”部分。

GRPO 数据集过滤

我们提供通过生成并计算可验证任务上的通过率来筛选数据集的支持,详情请参阅此 README。

👨‍💻 使用代码解释器进行训练

我们提供了一个代码奖励函数,用于在训练期间执行策略模型生成的代码。目前,该奖励函数针对 Codeforces 等编程竞赛,解决方案会针对一组测试用例执行,并将整体成功率作为最终奖励返回。为确保安全执行,我们支持多种沙箱提供商:

  1. E2B - 基于云的快速沙箱,专注于 Python 执行
  2. Morph - 基于云的沙箱,支持更广泛的语言——Python/JS/C++/Rust

要使用代码奖励函数,首先安装必要的依赖项:

uv pip install -e '.[code]'
E2B 提供商

要使用 E2B 沙箱,请创建一个 .env 文件并添加你的 E2B API token:

E2B_API_KEY="e2b_xxx"
Morph 提供商

要使用 Morph,首先安装 morphcloud 包:

pip install morphcloud

然后将你的 Morph API token 添加到 .env 文件中:

MORPH_API_KEY="YOUR_MORPH_API_KEY"

要指定使用哪个提供商,请在配置中添加 provider_type 参数:

# For E2B
provider_type: e2b

# For Morph
provider_type: morph
数据集要求

确保你的数据集包含一个 verification_info 列,其结构如下(采用自 PrimeIntellect 优秀的可验证问题数据集):

{
    "language": "python",  # Morph supports more languages including C++, Java, etc.
    "test_cases": [
        {
            "input": "4\n4\n0001\n1000\n0011\n0111\n3\n010\n101\n0\n2\n00000\n00001\n4\n01\n001\n0001\n00001\n",
            "output": "1\n3 \n-1\n0\n\n2\n1 2 \n",
            "type": "stdin_stdout",
        }
    ],
}

例如,要在 Python 问题上训练一个小模型,请启动 vLLM 服务器:

CUDA_VISIBLE_DEVICES=0 trl vllm-serve --model Qwen/Qwen2.5-1.5B-Instruct

然后运行训练:

CUDA_VISIBLE_DEVICES=1,2,3,4,5,6,7 ACCELERATE_LOG_LEVEL=info \
    accelerate launch --config_file recipes/accelerate_configs/zero2.yaml --num_processes=7 \
    src/open_r1/grpo.py --config recipes/Qwen2.5-1.5B-Instruct/grpo/config_demo_code.yaml
使用路由服务

当在沙箱服务上执行过多脚本时,可能会受到速率限制。针对这两个提供商,我们提供了可在 CPU 节点上启动的路由脚本:

对于 E2B:

sbatch slurm/e2b_router.slurm

对于 Morph:

sbatch slurm/morph_router.slurm

然后在你的训练 YAML 配置中添加路由 URL:

# For E2B
e2b_router_url: 1.2.3.4:8000

# For Morph
morph_router_url: 1.2.3.4:8000

端口应与启动路由时使用的端口一致。所有训练任务可以共享同一个路由 IP,这将确保并行执行得到妥善管理。

竞赛编程问题:IOI 与 CodeForces

我们提供了 ioi_code_reward 和 cf_code_reward 奖励函数,分别用于执行来自 IOI 和 CodeForces 的问题。你可以使用 piston 或 Morph(目前仅限 IOI)作为执行提供商。

Piston

要使用 Piston:

  1. 启动 piston 工作节点,请参阅 slurm/piston/README.md
  2. 将你的环境变量 PISTON_ENDPOINTS 设置为 slurm 或一个 piston 工作节点端点列表

对于 IOI:

  1. 在你的配置中,使用 ioi_provider: "piston"

对于 CodeForces:

  1. 下载生成的(困难)测试用例:
# change PATH_TO_SAVE_TESTCASES. Increase --max-workers according to your machine's capacity
huggingface-cli download open-r1/codeforces --repo-type=dataset --include='generated_tests/*.parquet' --max-workers=8 --local-dir PATH_TO_SAVE_TESTCASES 
  1. 在 .env 文件中保存路径:
CF_TESTS_FOLDER=PATH_TO_SAVE_TESTCASES
Morph

Morph 是一种基于云的解决方案,提供沙盒化环境来运行代码。使用方法如下:

  1. 安装 Morph 客户端:pip install morphcloud
  2. 将你的 Morph API 密钥添加到 .env 文件中:MORPH_API_KEY="your_key_here"
  3. 在你的配置中,使用 ioi_provider: "morph"
示例配方

对于 IOI:

查看示例配方,了解如何使用 IOI 奖励函数:

ACCELERATE_LOG_LEVEL=info accelerate launch --config_file recipes/accelerate_configs/zero2.yaml \
    --num_processes=7 src/open_r1/grpo.py \
    --config recipes/Qwen2.5-1.5B-Instruct/grpo/config_demo_code_ioi.yaml

对于 CodeForces:

sbatch --job-name=cf-grpo --nodes=2 slurm/train.slurm --model Qwen2.5-Coder-7B-Instruct --task grpo --config codeforces --accelerator zero3 --dp 8 --tp 1

在 Slurm 集群上启动作业

如果你有权访问 Slurm 集群,我们提供了一个 slurm/train.slurm 脚本,该脚本会自动为你排队训练作业。使用方法如下:

sbatch --job-name=open_r1 --nodes=1 slurm/train.slurm --model {model_name} --task {task} --config {config_suffix} --accelerator {accelerator}

这里的 {model_name} 和 {task} 如上所述,而 {config_suffix} 指的是具体的配置,{accelerator} 指的是 recipes/accelerate_configs 中 🤗 Accelerate 配置的选择。如果你想覆盖默认配置参数,可以通过附加一个空格分隔的字符串来提供,例如 '--arg1=value1 --arg2=value2'。以下是在 1 个节点 8 块 GPU 上运行 SFT 的具体示例:

sbatch --job-name=open_r1 --nodes=1 slurm/train.slurm --model OpenR1-Distill-7B --task sft --config distill --accelerator zero3

你可以通过增加 --nodes 标志来扩展节点数量。

对于 GRPO,我们使用 1 个节点作为 vLLM 服务器,N 个节点用于训练。例如,要在 1+1 个节点上使用混合数据和张量并行运行 GRPO,请执行:

sbatch --job-name=open_r1 --nodes=2 slurm/train.slurm --model Qwen2.5-1.5B-Instruct --task grpo --config demo --accelerator zero2 --dp 4 --tp 2

[!注意] slurm/train.slurm 中的配置是针对 Hugging Face 计算集群优化的,可能需要调整才能适配你自己的计算节点。

自定义数据集混合

要将多个数据集组合成一个训练混合集,你可以在 YAML 配置文件中指定 dataset_mixture 参数。以下是操作模板:

dataset_mixture:
  datasets:                     # List of datasets to include in the mixture
    - id: dataset_1             # Hub dataset ID
      config: config_name_1     # Name of the dataset config
      split: split_1            # Split to use from the dataset
      columns:                  # Columns to keep
        - column_1              
        - column_2    
      weight: 0.25              # Fraction of dataset to use
    - id: dataset_2
      config: config_name_2
      split: split_2
      columns:                  
        - column_1              
        - column_2   
      weight: 0.5
  seed: 42                      # Seed for shuffling the combined dataset
  test_split_size: 0.1          # Fraction of mixture to use for a test split

评估模型

我们使用 lighteval 来评估模型。对于适合单块 GPU 的模型,运行:

export VLLM_WORKER_MULTIPROC_METHOD=spawn # Required for vLLM
MODEL=deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
MODEL_ARGS="model_name=$MODEL,dtype=bfloat16,max_model_length=32768,gpu_memory_utilization=0.8,generation_parameters={max_new_tokens:32768,temperature:0.6,top_p:0.95}"
OUTPUT_DIR=data/evals/$MODEL

# AIME 2024
TASK=aime24
lighteval vllm $MODEL_ARGS "lighteval|$TASK|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR

# MATH-500
TASK=math_500
lighteval vllm $MODEL_ARGS "lighteval|$TASK|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR

# GPQA Diamond
TASK=gpqa:diamond
lighteval vllm $MODEL_ARGS "lighteval|$TASK|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR

# LiveCodeBench
lighteval vllm $MODEL_ARGS "extended|lcb:codegeneration|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR 

要跨多块 GPU 提高吞吐量,请按如下方式使用数据并行:

NUM_GPUS=8
MODEL=deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
MODEL_ARGS="model_name=$MODEL,dtype=bfloat16,data_parallel_size=$NUM_GPUS,max_model_length=32768,gpu_memory_utilization=0.8,generation_parameters={max_new_tokens:32768,temperature:0.6,top_p:0.95}"
TASK=aime24
OUTPUT_DIR=data/evals/$MODEL

lighteval vllm $MODEL_ARGS "lighteval|$TASK|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR

对于需要跨 GPU 分片的大型模型,请使用张量并行并运行:

NUM_GPUS=8
MODEL=deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
MODEL_ARGS="model_name=$MODEL,dtype=bfloat16,tensor_parallel_size=$NUM_GPUS,max_model_length=32768,gpu_memory_utilization=0.8,generation_parameters={max_new_tokens:32768,temperature:0.6,top_p:0.95}"
TASK=aime24
OUTPUT_DIR=data/evals/$MODEL

export VLLM_WORKER_MULTIPROC_METHOD=spawn
lighteval vllm $MODEL_ARGS "lighteval|$TASK|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR

你也可以使用 make evaluate 启动评估,指定模型、任务,以及可选的并行技术和 GPU 数量。

在单块 GPU 上评估:

make evaluate MODEL=deepseek-ai/DeepSeek-R1-Distill-Qwen-32B TASK=aime24

使用数据并行:

make evaluate MODEL=deepseek-ai/DeepSeek-R1-Distill-Qwen-32B TASK=aime24 PARALLEL=data NUM_GPUS=8

使用张量并行:

make evaluate MODEL=deepseek-ai/DeepSeek-R1-Distill-Qwen-32B TASK=aime24 PARALLEL=tensor NUM_GPUS=8

复现 DeepSeek 的评估结果

DeepSeek-R1 论文使用每次查询采样 4-64 个响应来估算 pass@1 准确率,但未说明每个基准测试的具体响应数量。在下表中,我们使用每次查询的以下响应数量来估算 pass@1 准确率:

基准测试 每次查询的响应数量
AIME 2024 64
MATH-500 4
GPQA Diamond 8
LiveCodeBench 16

请注意,对于像 AIME24 这样的基准测试,由于只有 30 道题,采样多个响应很重要,否则在重复运行中可能会引入高方差。每个提示词采样的响应数量选择,很可能解释了我们的评估结果与 DeepSeek 报告结果之间存在微小差异的原因。

AIME 2024

我们能够在约 1-3 个标准差范围内复现 DeepSeek 在 AIME 2024 基准测试上报告的结果:

模型 AIME 2024 (🤗 LightEval) AIME 2024 (DeepSeek 报告值)
DeepSeek-R1-Distill-Qwen-1.5B 30.7 28.9
DeepSeek-R1-Distill-Qwen-7B 50.8 55.5
DeepSeek-R1-Distill-Qwen-14B 65.9 69.7
DeepSeek-R1-Distill-Qwen-32B 69.7 72.6
DeepSeek-R1-Distill-Llama-8B 43.9 41.7
DeepSeek-R1-Distill-Llama-70B 63.0 70.0

要复现这些结果,请使用以下命令:

NUM_GPUS=1 # Set to 8 for 32B and 70B models
MODEL=deepseek-ai/{model_name}
MODEL_ARGS="model_name=$MODEL,dtype=bfloat16,max_model_length=32768,gpu_memory_utilization=0.8,data_parallel_size=$NUM_GPUS,generation_parameters={max_new_tokens:32768,temperature:0.6,top_p:0.95}"
OUTPUT_DIR=data/evals/$MODEL

lighteval vllm $MODEL_ARGS "lighteval|aime24|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR

或者,你也可以按如下方式启动 Slurm 作业:

python scripts/run_benchmarks.py --model-id {model_id}  --benchmarks aime24

MATH-500

我们能够在约 1-3 个标准差范围内复现 DeepSeek 在 MATH-500 基准测试上报告的结果:

模型 MATH-500 (🤗 LightEval) MATH-500 (DeepSeek 报告值)
DeepSeek-R1-Distill-Qwen-1.5B 83.1 83.9
DeepSeek-R1-Distill-Qwen-7B 94.5 92.8
DeepSeek-R1-Distill-Qwen-14B 94.1 93.9
DeepSeek-R1-Distill-Qwen-32B 95.6 94.3
DeepSeek-R1-Distill-Llama-8B 88.6 89.1
DeepSeek-R1-Distill-Llama-70B 95.1 94.5

要复现这些结果,请使用以下命令:

export VLLM_WORKER_MULTIPROC_METHOD=spawn
NUM_GPUS=1 # Set to 8 for 32B and 70B models
MODEL=deepseek-ai/{model_name}
MODEL_ARGS="model_name=$MODEL,dtype=bfloat16,max_model_length=32768,gpu_memory_utilization=0.8,data_parallel_size=$NUM_GPUS,generation_parameters={max_new_tokens:32768,temperature:0.6,top_p:0.95}"
OUTPUT_DIR=data/evals/$MODEL

lighteval vllm $MODEL_ARGS "lighteval|math_500|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR

或者,你也可以按如下方式启动 Slurm 作业:

python scripts/run_benchmarks.py --model-id {model_id}  --benchmarks math_500

GPQA Diamond

我们能够在约 1-3 个标准差范围内复现 DeepSeek 在 GPQA Diamond 基准测试上报告的结果:

模型 GPQA Diamond (🤗 LightEval) GPQA Diamond (DeepSeek 报告值)
DeepSeek-R1-Distill-Qwen-1.5B 35.8 33.8
DeepSeek-R1-Distill-Qwen-7B 50.5 49.1
DeepSeek-R1-Distill-Qwen-14B 61.5 59.1
DeepSeek-R1-Distill-Qwen-32B 63.1 62.1
DeepSeek-R1-Distill-Llama-8B 46.7 49.0
DeepSeek-R1-Distill-Llama-70B 67.4 65.2

要复现这些结果,请使用以下命令:

export VLLM_WORKER_MULTIPROC_METHOD=spawn
NUM_GPUS=1 # Set to 8 for 32B and 70B models
MODEL=deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
MODEL_ARGS="model_name=$MODEL,dtype=bfloat16,max_model_length=32768,gpu_memory_utilization=0.8,generation_parameters={max_new_tokens:32768,temperature:0.6,top_p:0.95}"
OUTPUT_DIR=data/evals/$MODEL

lighteval vllm $MODEL_ARGS "lighteval|gpqa:diamond|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR
python scripts/run_benchmarks.py --model-id {model_id}  --benchmarks gpqa

LiveCodeBench

我们能够在约 1-3 个标准差范围内复现 DeepSeek 在 LiveCodeBench 代码生成基准测试上报告的结果:

模型 LiveCodeBench(🤗 LightEval) LiveCodeBench(DeepSeek 报告值)
DeepSeek-R1-Distill-Qwen-1.5B 16.1 16.9
DeepSeek-R1-Distill-Qwen-7B 37.4 37.6
DeepSeek-R1-Distill-Qwen-14B 51.3 53.1
DeepSeek-R1-Distill-Qwen-32B 56.0 57.2
DeepSeek-R1-Distill-Llama-8B 37.4 39.6
DeepSeek-R1-Distill-Llama-70B 55.9 57.5

要复现这些结果,请使用以下命令:

NUM_GPUS=1 # Set to 8 for 32B and 70B models, or data_parallel_size=8 with the smaller models for speed
MODEL=deepseek-ai/{model_name}
MODEL_ARGS="model_name=$MODEL,dtype=bfloat16,max_model_length=32768,gpu_memory_utilization=0.8,data_parallel_size=$NUM_GPUS,generation_parameters={max_new_tokens:32768,temperature:0.6,top_p:0.95}"
OUTPUT_DIR=data/evals/$MODEL

lighteval vllm $MODEL_ARGS "extended|lcb:codegeneration|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR
python scripts/run_benchmarks.py --model-id {model_id}  --benchmarks lcb

数据生成

从一个小型蒸馏 R1 模型生成数据

以下示例可在 1 块 H100 上运行。首先安装以下依赖:

uv pip install "distilabel[vllm]>=1.5.2"

现在将以下代码片段保存到名为 pipeline.py 的文件中,并用 python pipeline.py 运行它。它将为 10 个示例中的每一个生成 4 个输出(将仓库的用户名改为你的组织/用户名):

from datasets import load_dataset
from distilabel.models import vLLM
from distilabel.pipeline import Pipeline
from distilabel.steps.tasks import TextGeneration


prompt_template = """\
You will be given a problem. Please reason step by step, and put your final answer within \boxed{}:
{{ instruction }}"""

dataset = load_dataset("AI-MO/NuminaMath-TIR", split="train").select(range(10))

model_id = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B"  # Exchange with another smol distilled r1

with Pipeline(
    name="distill-qwen-7b-r1",
    description="A pipeline to generate data from a distilled r1 model",
) as pipeline:

    llm = vLLM(
        model=model_id,
        tokenizer=model_id,
        extra_kwargs={
            "tensor_parallel_size": 1,
            "max_model_len": 8192,
        },
        generation_kwargs={
            "temperature": 0.6,
            "max_new_tokens": 8192,
        },
    )
    prompt_column = "problem"
    text_generation = TextGeneration(
        llm=llm, 
        template=prompt_template,
        num_generations=4,
        input_mappings={"instruction": prompt_column} if prompt_column is not None else {}
    )


if __name__ == "__main__":
    distiset = pipeline.run(dataset=dataset)
    distiset.push_to_hub(repo_id="username/numina-deepseek-r1-qwen-7b")

查看 HuggingFaceH4/numina-deepseek-r1-qwen-7b 处的示例数据集。

从 DeepSeek-R1 生成数据

要运行更大的 DeepSeek-R1,我们使用了 2 个节点,每个节点配备 8 块 H100 GPU,使用本仓库中 slurm/generate.slurm 处的 slurm 文件。首先,安装依赖:

(目前我们需要安装修复了 R1 CUDA 图捕获问题的 vllm 开发版 wheel 包)

pip install https://wheels.vllm.ai/221d388cc5a836fa189305785ed7e887cea8b510/vllm-1.0.0.dev-cp38-abi3-manylinux1_x86_64.whl --extra-index-url https://download.pytorch.org/whl/cu121

uv pip install "distilabel[vllm,ray,openai]>=1.5.2"

然后运行以下命令:

sbatch slurm/generate.slurm \
    --hf-dataset AI-MO/NuminaMath-TIR \
    --temperature 0.6 \
    --prompt-column problem \
    --model deepseek-ai/DeepSeek-R1 \
    --hf-output-dataset username/r1-dataset

[注意] 当任务运行时,你可以通过集群登录节点设置 SSH 隧道,从你的计算机运行 ssh -L 8265:ray_ip_head_node:8265 <login_node> 来访问 Ray 仪表板,然后浏览 http://localhost:8265

数据去污染

参照 s1:简单测试时缩放,可以使用 scripts/decontaminate.py 脚本对数据进行去污染,该脚本使用 8-gram 对数据集进行去污染并去重数据。示例运行:

python scripts/decontaminate.py \
    --dataset "open-r1/verifiable-coding-problems-python" \
    --problem_column problem \
    --cleanup

它将针对基准测试数据集进行去污染,然后移除被污染的样本。如果没有提供参数 --new_dataset_name,则会复用同一数据集,并添加 _decontaminated 后缀。它针对提示词运行,对于该数据集,提示词列是 problem,但也可以指定其他列。

脚本的参数:

usage: decontaminate.py [-h] --dataset DATASET [--split SPLIT] [--ngram_size NGRAM_SIZE] [--problem_column PROBLEM_COLUMN] [--cleanup] [--new_dataset_name NEW_DATASET_NAME]

options:
  -h, --help            show this help message and exit
  --dataset DATASET     Name of the dataset to check for contamination.
  --split SPLIT         Split to check for contamination, defaults to `train`.
  --ngram_size NGRAM_SIZE
                        Size of n-grams to build, defaults to 8.
  --problem_column PROBLEM_COLUMN
                        Name of the column containing the problem (prompt).
  --cleanup           Whether to remove the contaminated rows before pushing the dataset.
  --new_dataset_name NEW_DATASET_NAME
                        New name for the dataset. If not provided, will reuse the name and add a `_decontaminated` to the name.

贡献

欢迎贡献。请参考 https://github.com/huggingface/open-r1/issues/23。

致谢

本项目凝聚了开放 AI 社区众多团队与个人的共同努力。我们特别感谢 vLLM 和 SGLang 团队,他们开发了高性能工具,用于扩展 GRPO 的部署规模。同时,我们也感谢 OpenThoughts、Prime Intellect 和 General Reasoning 团队,他们创建并分享了高质量的推理数据集。

引用

如果您认为本项目对您的工作有所帮助,请考虑按以下方式引用:

@misc{openr1,
    title = {Open R1: A fully open reproduction of DeepSeek-R1},
    url = {https://github.com/huggingface/open-r1},
    author = {{Hugging Face}},
    month = {January},
    year = {2025}
}

来源:Hacker News 热门(buzzing.cc 中文翻译) · github.com

DeepSeek-R1 的开源实现

Hacker News 热门(buzzing.cc 中文翻译)·2026-06-12 01:07·66天前·yogthos
AI 导读

DeepSeek-R1 的开源复现项目已在 GitHub 发布,在 Hacker News 上获得 101 个积分。该项目旨在以开源方式复现 DeepSeek-R1 模型。

正文 · AI 翻译

完全开源的 DeepSeek-R1 复现项目。本仓库仍在开发中,让我们一起构建它!

目录

  1. 概述
  2. 实施方案
  3. 安装
  4. Training models
    • 监督微调
    • GRPO
  5. 模型评估
  6. 复现 DeepSeek 的评估结果
  7. Data generation
    • 从精简蒸馏 R1 模型生成数据
    • 从 DeepSeek-R1 生成数据
  8. 贡献指南

概述

本仓库的目标是构建 R1 流程中缺失的环节,使所有人都能复现并在此基础上进行开发。该项目设计简洁,主要包含以下内容:

  • src/open_r1: contains the scripts to train models as well as generate synthetic data:
    • grpo.py:在给定数据集上使用 GRPO 训练模型。
    • sft.py:在数据集上对模型执行简单的监督微调。
    • generate.py:使用 Distilabel 从模型生成合成数据。
  • Makefile:包含利用上述脚本执行 R1 流程各步骤的易用命令。

实施方案

我们将以 DeepSeek-R1 技术报告为指南,该流程大致可分为三个主要步骤:

  • 步骤 1:通过从 DeepSeek-R1 蒸馏高质量语料库,复现 R1-Distill 模型。
  • 步骤 2:复现 DeepSeek 用于创建 R1-Zero 的纯强化学习流程。这可能涉及为数学、推理和代码领域策划新的大规模数据集。
  • 步骤 3:展示如何通过多阶段训练,从基础模型过渡到强化学习调优模型。

新闻 🗞️

  • 🧑‍🍳 [2025/05/26](步骤 1 已完成!)我们发布了 Mixture-of-Thoughts——一个包含 35 万条经过验证的推理轨迹的精选推理数据集,这些轨迹从 R1 蒸馏而来。该数据集涵盖数学、编程和科学领域的任务,旨在教会语言模型逐步推理。我们还提供了训练 OpenR1-Distill-7B 的配方,该模型复现了 deepseek-ai/DeepSeek-R1-Distill-Qwen-7B 的推理能力,标志着 Open R1 项目步骤 1 的完成。
  • ⚡️ [2025/03/11](更新 #3):我们发布了 CodeForces-CoTs 数据集,包含 1 万个竞赛编程问题和从 R1 蒸馏的 10 万个解题方案。我们还发布了 IOI24:一个包含国际奥赛极难问题的新基准。在 CodeForces-CoTs 上训练的 7B Qwen 模型在 IOI24 上可超越 Claude 3.7 Sonnet,而 32B 模型则可超越 R1 本身。
  • ∞ [2025/02/10](更新 #2):我们发布了 OpenR1-Math-220k 数据集,该数据集包含从 R1 在新版 NuminaMath 上蒸馏得到的 22 万条推理轨迹。基于该数据集训练的模型,其性能与 DeepSeek 蒸馏模型相当。
  • 🔥 [2025/02/02](更新 #1):我们实现了训练、推理和评估流程的第一部分。开工!

安装

[!CAUTION] 相关库依赖 CUDA 12.4。如果遇到与段错误相关的报错,请使用 nvcc --version 再次确认系统运行的版本。

要运行本项目中的代码,首先请使用 uv 等工具创建一个 Python 虚拟环境。安装 uv 请参考 UV 安装指南。

[!NOTE] 快捷方式:运行 make install 来安装开发库(具体步骤如下)。之后,如果一切配置正确,你就可以尝试使用 Open-R1 模型了。

uv venv openr1 --python 3.11 && source openr1/bin/activate && uv pip install --upgrade pip

[!TIP] 对于 Hugging Face 集群用户,请在 .bashrc 中添加 export UV_LINK_MODE=copy,以抑制 uv 的缓存警告。

接下来,安装 vLLM 和 FlashAttention:

uv pip install vllm==0.8.5.post1
uv pip install setuptools && uv pip install flash-attn --no-build-isolation

这也会安装 PyTorch v2.6.0,使用此版本非常重要,因为 vLLM 的二进制文件是针对它编译的。然后,你可以通过 pip install -e .[模式列表] 为你的特定用例安装其余依赖。对于大多数贡献者,我们推荐:

GIT_LFS_SKIP_SMUDGE=1 uv pip install -e ".[dev]"

接下来,按如下方式登录你的 Hugging Face 和 Weights and Biases 账户:

huggingface-cli login
wandb login

最后,检查你的系统是否已安装 Git LFS,以便你能从 Hugging Face Hub 加载和推送模型/数据集:

git-lfs --version

如果未安装,请运行:

sudo apt-get install git-lfs

训练模型

[!NOTE] 下面的训练命令是针对配备 8 块 H100(80GB)的节点配置的。对于不同的硬件和拓扑结构,你可能需要调整批次大小和梯度累积步数。

我们支持使用 DDP 或 DeepSpeed(ZeRO-2 和 ZeRO-3)训练模型。例如,要在包含推理轨迹(如 open-r1/Mixture-of-Thoughts)的 DeepSeek-R1 蒸馏数据集上执行 SFT,请运行:

# Train via command line
accelerate launch --config_file=recipes/accelerate_configs/zero3.yaml src/open_r1/sft.py \
    --model_name_or_path open-r1/Qwen2.5-Math-7B-RoPE-300k \
    --dataset_name open-r1/Mixture-of-Thoughts \
    --dataset_config all \
    --eos_token '<|im_end|>' \
    --learning_rate 4.0e-5 \
    --num_train_epochs 5 \
    --max_seq_length 32768 \
    --per_device_train_batch_size 2 \
    --gradient_checkpointing \
    --bf16 \
    --use_liger_kernel \
    --output_dir data/OpenR1-Distill-7B

# Train via YAML config
accelerate launch --config_file recipes/accelerate_configs/zero3.yaml src/open_r1/sft.py \
    --config recipes/OpenR1-Distill-7B/sft/config_distill.yaml

目前,支持以下任务:

  • 监督微调 sft
  • 分组相对策略优化 grpo

[!TIP] 如果你增加/减少 GPU 数量,我们建议同时调整每设备批次大小或梯度累积步数,以保持全局批次大小不变。

默认情况下,这些脚本会将每个模型推送到你的 Hugging Face Hub 用户名下,即 `{username}/{model_name}-{task}`。你可以通过在命令后追加参数来覆盖每个 YAML 配置文件中的参数,操作如下:

# Change the base model to a smaller variant
accelerate launch --config_file recipes/accelerate_configs/zero3.yaml src/open_r1/sft.py \
    --config recipes/OpenR1-Distill-7B/sft/config_distill.yaml \
    --model_name_or_path Qwen/Qwen3-0.6B-Base \
    --hub_model_id OpenR1-Distill-0.6B \
    --output_dir data/OpenR1-Distill-0.6B

如果你还想覆盖 Weights and Biases 的默认设置,可以按如下方式操作:

accelerate launch --config_file recipes/accelerate_configs/zero3.yaml src/open_r1/sft.py \
    --config recipes/OpenR1-Distill-7B/sft/config_distill.yaml
    --wandb_entity huggingface --wandb_project open-r1 --run_name Qwen2.5-1.5B-GRPO

🚨 警告 🚨

大多数基础模型(如 `meta-llama/Llama-3.2-1B`)没有聊天模板,因此我们在训练期间将 ChatML 设置为默认模板。然而,对于像 `Qwen/Qwen2.5-1.5B` 这样的 Qwen 基础模型,其分词器中已预定义了聊天模板,因此必须相应地设置 EOS token,例如:

# Align EOS token with chat template for Qwen base models
accelerate launch --config_file=recipes/accelerate_configs/zero3.yaml src/open_r1/sft.py \
    --model_name_or_path Qwen/Qwen2.5-1.5B \
+   --eos_token '<|im_end|>'
    --dataset_name open-r1/Mixture-of-Thoughts \
    --dataset_config all \
    --learning_rate 4.0e-5 \
    --num_train_epochs 1 \
    --max_seq_length 32768 \
    --per_device_train_batch_size 16 \
    --gradient_checkpointing \
    --bf16 \
    --use_liger_kernel \
    --output_dir data/Qwen2.5-1.5B-Open-R1-Distill

如果你想使用自定义聊天模板(例如 Llama 或 Gemma),则必须提供聊天模板及相关的 EOS token:

# Align EOS token with custom chat template
accelerate launch --config_file=recipes/accelerate_configs/zero3.yaml src/open_r1/sft.py \
    --model_name_or_path meta-llama/Llama-3.2-1B \
+   --chat_template "$(cat llama_chat_template.jinja)" \
+   --eos_token '<|eot_id|>' \
    --dataset_name open-r1/Mixture-of-Thoughts \
    --dataset_config all \
    --learning_rate 4.0e-5 \
    --num_train_epochs 1 \
    --max_seq_length 32768 \
    --per_device_train_batch_size 16 \
    --gradient_checkpointing \
    --bf16 \
    --use_liger_kernel \
    --output_dir data/Llama-3.2-1B-Open-R1-Distill

SFT 知识蒸馏

我们提供了一个配方,用于从相同的基础模型出发,复现 `deepseek-ai/DeepSeek-R1-Distill-Qwen-7B` 的推理能力。为此,请运行:

ACCELERATE_LOG_LEVEL=info accelerate launch --config_file recipes/accelerate_configs/zero3.yaml \
    src/open_r1/sft.py \
    --config recipes/OpenR1-Distill-7B/sft/config_distill.yaml

结果将得到一个类似 `open-r1/OpenR1-Distill-7B` 的模型,其下游性能如下:

模型 AIME 2024 MATH-500 GPQA Diamond LiveCodeBench v5
OpenR1-Distill-7B 52.7 89.0 52.8 39.4
DeepSeek-R1-Distill-Qwen-7B 51.3 93.5 52.4 37.4

你可以调整 YAML 配置文件,以在不同的基础模型或数据集上进行训练。

GRPO

我们使用 TRL 的 vLLM 后端,将训练扩展到跨多个节点的大型模型。对于在 8 个 GPU 上进行小型模型的单节点训练,请使用 `vllm_mode="colocate"`,让 vLLM 在与训练脚本相同的进程中运行:

ACCELERATE_LOG_LEVEL=info \
    accelerate launch --config_file recipes/accelerate_configs/zero3.yaml \
    src/open_r1/grpo.py --config recipes/DeepSeek-R1-Distill-Qwen-1.5B/grpo/config_demo.yaml \
    --vllm_mode colocate

[!警告] 经过蒸馏的 DeepSeek 模型所使用的聊天模板,会省略 `<think>` 和 `</think>` 标签内推理块的内容。它还会在助手回复中预填充 `<think>`,这会干扰格式奖励函数。为处理此问题,务必像 `recipes/DeepSeek-R1-Distill-Qwen-1.5B/grpo/config_demo.yaml` 中那样覆盖聊天模板。

对于 N+1 个节点的多节点训练(其中 1 个节点运行 vLLM 服务器,N 个节点运行训练),我们提供了一个示例 Slurm 脚本。例如,要在 1+1 个节点上通过数据并行运行上述示例,请运行:

sbatch --nodes=2 slurm/train.slurm --model Qwen2.5-1.5B-Instruct --task grpo --config demo --accelerator zero2 --dp 8 --tp 1

更多详情请参阅“在 Slurm 集群上启动作业”部分。

GRPO 数据集过滤

我们提供通过生成并计算可验证任务上的通过率来筛选数据集的支持,详情请参阅此 README。

👨‍💻 使用代码解释器进行训练

我们提供了一个代码奖励函数,用于在训练期间执行策略模型生成的代码。目前,该奖励函数针对 Codeforces 等编程竞赛,解决方案会针对一组测试用例执行,并将整体成功率作为最终奖励返回。为确保安全执行,我们支持多种沙箱提供商:

  1. E2B - 基于云的快速沙箱,专注于 Python 执行
  2. Morph - 基于云的沙箱,支持更广泛的语言——Python/JS/C++/Rust

要使用代码奖励函数,首先安装必要的依赖项:

uv pip install -e '.[code]'
E2B 提供商

要使用 E2B 沙箱,请创建一个 .env 文件并添加你的 E2B API token:

E2B_API_KEY="e2b_xxx"
Morph 提供商

要使用 Morph,首先安装 morphcloud 包:

pip install morphcloud

然后将你的 Morph API token 添加到 .env 文件中:

MORPH_API_KEY="YOUR_MORPH_API_KEY"

要指定使用哪个提供商,请在配置中添加 provider_type 参数:

# For E2B
provider_type: e2b

# For Morph
provider_type: morph
数据集要求

确保你的数据集包含一个 verification_info 列,其结构如下(采用自 PrimeIntellect 优秀的可验证问题数据集):

{
    "language": "python",  # Morph supports more languages including C++, Java, etc.
    "test_cases": [
        {
            "input": "4\n4\n0001\n1000\n0011\n0111\n3\n010\n101\n0\n2\n00000\n00001\n4\n01\n001\n0001\n00001\n",
            "output": "1\n3 \n-1\n0\n\n2\n1 2 \n",
            "type": "stdin_stdout",
        }
    ],
}

例如,要在 Python 问题上训练一个小模型,请启动 vLLM 服务器:

CUDA_VISIBLE_DEVICES=0 trl vllm-serve --model Qwen/Qwen2.5-1.5B-Instruct

然后运行训练:

CUDA_VISIBLE_DEVICES=1,2,3,4,5,6,7 ACCELERATE_LOG_LEVEL=info \
    accelerate launch --config_file recipes/accelerate_configs/zero2.yaml --num_processes=7 \
    src/open_r1/grpo.py --config recipes/Qwen2.5-1.5B-Instruct/grpo/config_demo_code.yaml
使用路由服务

当在沙箱服务上执行过多脚本时,可能会受到速率限制。针对这两个提供商,我们提供了可在 CPU 节点上启动的路由脚本:

对于 E2B:

sbatch slurm/e2b_router.slurm

对于 Morph:

sbatch slurm/morph_router.slurm

然后在你的训练 YAML 配置中添加路由 URL:

# For E2B
e2b_router_url: 1.2.3.4:8000

# For Morph
morph_router_url: 1.2.3.4:8000

端口应与启动路由时使用的端口一致。所有训练任务可以共享同一个路由 IP,这将确保并行执行得到妥善管理。

竞赛编程问题:IOI 与 CodeForces

我们提供了 ioi_code_reward 和 cf_code_reward 奖励函数,分别用于执行来自 IOI 和 CodeForces 的问题。你可以使用 piston 或 Morph(目前仅限 IOI)作为执行提供商。

Piston

要使用 Piston:

  1. 启动 piston 工作节点,请参阅 slurm/piston/README.md
  2. 将你的环境变量 PISTON_ENDPOINTS 设置为 slurm 或一个 piston 工作节点端点列表

对于 IOI:

  1. 在你的配置中,使用 ioi_provider: "piston"

对于 CodeForces:

  1. 下载生成的(困难)测试用例:
# change PATH_TO_SAVE_TESTCASES. Increase --max-workers according to your machine's capacity
huggingface-cli download open-r1/codeforces --repo-type=dataset --include='generated_tests/*.parquet' --max-workers=8 --local-dir PATH_TO_SAVE_TESTCASES 
  1. 在 .env 文件中保存路径:
CF_TESTS_FOLDER=PATH_TO_SAVE_TESTCASES
Morph

Morph 是一种基于云的解决方案,提供沙盒化环境来运行代码。使用方法如下:

  1. 安装 Morph 客户端:pip install morphcloud
  2. 将你的 Morph API 密钥添加到 .env 文件中:MORPH_API_KEY="your_key_here"
  3. 在你的配置中,使用 ioi_provider: "morph"
示例配方

对于 IOI:

查看示例配方,了解如何使用 IOI 奖励函数:

ACCELERATE_LOG_LEVEL=info accelerate launch --config_file recipes/accelerate_configs/zero2.yaml \
    --num_processes=7 src/open_r1/grpo.py \
    --config recipes/Qwen2.5-1.5B-Instruct/grpo/config_demo_code_ioi.yaml

对于 CodeForces:

sbatch --job-name=cf-grpo --nodes=2 slurm/train.slurm --model Qwen2.5-Coder-7B-Instruct --task grpo --config codeforces --accelerator zero3 --dp 8 --tp 1

在 Slurm 集群上启动作业

如果你有权访问 Slurm 集群,我们提供了一个 slurm/train.slurm 脚本,该脚本会自动为你排队训练作业。使用方法如下:

sbatch --job-name=open_r1 --nodes=1 slurm/train.slurm --model {model_name} --task {task} --config {config_suffix} --accelerator {accelerator}

这里的 {model_name} 和 {task} 如上所述,而 {config_suffix} 指的是具体的配置,{accelerator} 指的是 recipes/accelerate_configs 中 🤗 Accelerate 配置的选择。如果你想覆盖默认配置参数,可以通过附加一个空格分隔的字符串来提供,例如 '--arg1=value1 --arg2=value2'。以下是在 1 个节点 8 块 GPU 上运行 SFT 的具体示例:

sbatch --job-name=open_r1 --nodes=1 slurm/train.slurm --model OpenR1-Distill-7B --task sft --config distill --accelerator zero3

你可以通过增加 --nodes 标志来扩展节点数量。

对于 GRPO,我们使用 1 个节点作为 vLLM 服务器,N 个节点用于训练。例如,要在 1+1 个节点上使用混合数据和张量并行运行 GRPO,请执行:

sbatch --job-name=open_r1 --nodes=2 slurm/train.slurm --model Qwen2.5-1.5B-Instruct --task grpo --config demo --accelerator zero2 --dp 4 --tp 2

[!注意] slurm/train.slurm 中的配置是针对 Hugging Face 计算集群优化的,可能需要调整才能适配你自己的计算节点。

自定义数据集混合

要将多个数据集组合成一个训练混合集,你可以在 YAML 配置文件中指定 dataset_mixture 参数。以下是操作模板:

dataset_mixture:
  datasets:                     # List of datasets to include in the mixture
    - id: dataset_1             # Hub dataset ID
      config: config_name_1     # Name of the dataset config
      split: split_1            # Split to use from the dataset
      columns:                  # Columns to keep
        - column_1              
        - column_2    
      weight: 0.25              # Fraction of dataset to use
    - id: dataset_2
      config: config_name_2
      split: split_2
      columns:                  
        - column_1              
        - column_2   
      weight: 0.5
  seed: 42                      # Seed for shuffling the combined dataset
  test_split_size: 0.1          # Fraction of mixture to use for a test split

评估模型

我们使用 lighteval 来评估模型。对于适合单块 GPU 的模型,运行:

export VLLM_WORKER_MULTIPROC_METHOD=spawn # Required for vLLM
MODEL=deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
MODEL_ARGS="model_name=$MODEL,dtype=bfloat16,max_model_length=32768,gpu_memory_utilization=0.8,generation_parameters={max_new_tokens:32768,temperature:0.6,top_p:0.95}"
OUTPUT_DIR=data/evals/$MODEL

# AIME 2024
TASK=aime24
lighteval vllm $MODEL_ARGS "lighteval|$TASK|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR

# MATH-500
TASK=math_500
lighteval vllm $MODEL_ARGS "lighteval|$TASK|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR

# GPQA Diamond
TASK=gpqa:diamond
lighteval vllm $MODEL_ARGS "lighteval|$TASK|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR

# LiveCodeBench
lighteval vllm $MODEL_ARGS "extended|lcb:codegeneration|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR 

要跨多块 GPU 提高吞吐量,请按如下方式使用数据并行:

NUM_GPUS=8
MODEL=deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
MODEL_ARGS="model_name=$MODEL,dtype=bfloat16,data_parallel_size=$NUM_GPUS,max_model_length=32768,gpu_memory_utilization=0.8,generation_parameters={max_new_tokens:32768,temperature:0.6,top_p:0.95}"
TASK=aime24
OUTPUT_DIR=data/evals/$MODEL

lighteval vllm $MODEL_ARGS "lighteval|$TASK|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR

对于需要跨 GPU 分片的大型模型,请使用张量并行并运行:

NUM_GPUS=8
MODEL=deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
MODEL_ARGS="model_name=$MODEL,dtype=bfloat16,tensor_parallel_size=$NUM_GPUS,max_model_length=32768,gpu_memory_utilization=0.8,generation_parameters={max_new_tokens:32768,temperature:0.6,top_p:0.95}"
TASK=aime24
OUTPUT_DIR=data/evals/$MODEL

export VLLM_WORKER_MULTIPROC_METHOD=spawn
lighteval vllm $MODEL_ARGS "lighteval|$TASK|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR

你也可以使用 make evaluate 启动评估,指定模型、任务,以及可选的并行技术和 GPU 数量。

在单块 GPU 上评估:

make evaluate MODEL=deepseek-ai/DeepSeek-R1-Distill-Qwen-32B TASK=aime24

使用数据并行:

make evaluate MODEL=deepseek-ai/DeepSeek-R1-Distill-Qwen-32B TASK=aime24 PARALLEL=data NUM_GPUS=8

使用张量并行:

make evaluate MODEL=deepseek-ai/DeepSeek-R1-Distill-Qwen-32B TASK=aime24 PARALLEL=tensor NUM_GPUS=8

复现 DeepSeek 的评估结果

DeepSeek-R1 论文使用每次查询采样 4-64 个响应来估算 pass@1 准确率,但未说明每个基准测试的具体响应数量。在下表中,我们使用每次查询的以下响应数量来估算 pass@1 准确率:

基准测试 每次查询的响应数量
AIME 2024 64
MATH-500 4
GPQA Diamond 8
LiveCodeBench 16

请注意,对于像 AIME24 这样的基准测试,由于只有 30 道题,采样多个响应很重要,否则在重复运行中可能会引入高方差。每个提示词采样的响应数量选择,很可能解释了我们的评估结果与 DeepSeek 报告结果之间存在微小差异的原因。

AIME 2024

我们能够在约 1-3 个标准差范围内复现 DeepSeek 在 AIME 2024 基准测试上报告的结果:

模型 AIME 2024 (🤗 LightEval) AIME 2024 (DeepSeek 报告值)
DeepSeek-R1-Distill-Qwen-1.5B 30.7 28.9
DeepSeek-R1-Distill-Qwen-7B 50.8 55.5
DeepSeek-R1-Distill-Qwen-14B 65.9 69.7
DeepSeek-R1-Distill-Qwen-32B 69.7 72.6
DeepSeek-R1-Distill-Llama-8B 43.9 41.7
DeepSeek-R1-Distill-Llama-70B 63.0 70.0

要复现这些结果,请使用以下命令:

NUM_GPUS=1 # Set to 8 for 32B and 70B models
MODEL=deepseek-ai/{model_name}
MODEL_ARGS="model_name=$MODEL,dtype=bfloat16,max_model_length=32768,gpu_memory_utilization=0.8,data_parallel_size=$NUM_GPUS,generation_parameters={max_new_tokens:32768,temperature:0.6,top_p:0.95}"
OUTPUT_DIR=data/evals/$MODEL

lighteval vllm $MODEL_ARGS "lighteval|aime24|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR

或者,你也可以按如下方式启动 Slurm 作业:

python scripts/run_benchmarks.py --model-id {model_id}  --benchmarks aime24

MATH-500

我们能够在约 1-3 个标准差范围内复现 DeepSeek 在 MATH-500 基准测试上报告的结果:

模型 MATH-500 (🤗 LightEval) MATH-500 (DeepSeek 报告值)
DeepSeek-R1-Distill-Qwen-1.5B 83.1 83.9
DeepSeek-R1-Distill-Qwen-7B 94.5 92.8
DeepSeek-R1-Distill-Qwen-14B 94.1 93.9
DeepSeek-R1-Distill-Qwen-32B 95.6 94.3
DeepSeek-R1-Distill-Llama-8B 88.6 89.1
DeepSeek-R1-Distill-Llama-70B 95.1 94.5

要复现这些结果,请使用以下命令:

export VLLM_WORKER_MULTIPROC_METHOD=spawn
NUM_GPUS=1 # Set to 8 for 32B and 70B models
MODEL=deepseek-ai/{model_name}
MODEL_ARGS="model_name=$MODEL,dtype=bfloat16,max_model_length=32768,gpu_memory_utilization=0.8,data_parallel_size=$NUM_GPUS,generation_parameters={max_new_tokens:32768,temperature:0.6,top_p:0.95}"
OUTPUT_DIR=data/evals/$MODEL

lighteval vllm $MODEL_ARGS "lighteval|math_500|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR

或者,你也可以按如下方式启动 Slurm 作业:

python scripts/run_benchmarks.py --model-id {model_id}  --benchmarks math_500

GPQA Diamond

我们能够在约 1-3 个标准差范围内复现 DeepSeek 在 GPQA Diamond 基准测试上报告的结果:

模型 GPQA Diamond (🤗 LightEval) GPQA Diamond (DeepSeek 报告值)
DeepSeek-R1-Distill-Qwen-1.5B 35.8 33.8
DeepSeek-R1-Distill-Qwen-7B 50.5 49.1
DeepSeek-R1-Distill-Qwen-14B 61.5 59.1
DeepSeek-R1-Distill-Qwen-32B 63.1 62.1
DeepSeek-R1-Distill-Llama-8B 46.7 49.0
DeepSeek-R1-Distill-Llama-70B 67.4 65.2

要复现这些结果,请使用以下命令:

export VLLM_WORKER_MULTIPROC_METHOD=spawn
NUM_GPUS=1 # Set to 8 for 32B and 70B models
MODEL=deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
MODEL_ARGS="model_name=$MODEL,dtype=bfloat16,max_model_length=32768,gpu_memory_utilization=0.8,generation_parameters={max_new_tokens:32768,temperature:0.6,top_p:0.95}"
OUTPUT_DIR=data/evals/$MODEL

lighteval vllm $MODEL_ARGS "lighteval|gpqa:diamond|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR
python scripts/run_benchmarks.py --model-id {model_id}  --benchmarks gpqa

LiveCodeBench

我们能够在约 1-3 个标准差范围内复现 DeepSeek 在 LiveCodeBench 代码生成基准测试上报告的结果:

模型 LiveCodeBench(🤗 LightEval) LiveCodeBench(DeepSeek 报告值)
DeepSeek-R1-Distill-Qwen-1.5B 16.1 16.9
DeepSeek-R1-Distill-Qwen-7B 37.4 37.6
DeepSeek-R1-Distill-Qwen-14B 51.3 53.1
DeepSeek-R1-Distill-Qwen-32B 56.0 57.2
DeepSeek-R1-Distill-Llama-8B 37.4 39.6
DeepSeek-R1-Distill-Llama-70B 55.9 57.5

要复现这些结果,请使用以下命令:

NUM_GPUS=1 # Set to 8 for 32B and 70B models, or data_parallel_size=8 with the smaller models for speed
MODEL=deepseek-ai/{model_name}
MODEL_ARGS="model_name=$MODEL,dtype=bfloat16,max_model_length=32768,gpu_memory_utilization=0.8,data_parallel_size=$NUM_GPUS,generation_parameters={max_new_tokens:32768,temperature:0.6,top_p:0.95}"
OUTPUT_DIR=data/evals/$MODEL

lighteval vllm $MODEL_ARGS "extended|lcb:codegeneration|0|0" \
    --use-chat-template \
    --output-dir $OUTPUT_DIR
python scripts/run_benchmarks.py --model-id {model_id}  --benchmarks lcb

数据生成

从一个小型蒸馏 R1 模型生成数据

以下示例可在 1 块 H100 上运行。首先安装以下依赖:

uv pip install "distilabel[vllm]>=1.5.2"

现在将以下代码片段保存到名为 pipeline.py 的文件中,并用 python pipeline.py 运行它。它将为 10 个示例中的每一个生成 4 个输出(将仓库的用户名改为你的组织/用户名):

from datasets import load_dataset
from distilabel.models import vLLM
from distilabel.pipeline import Pipeline
from distilabel.steps.tasks import TextGeneration


prompt_template = """\
You will be given a problem. Please reason step by step, and put your final answer within \boxed{}:
{{ instruction }}"""

dataset = load_dataset("AI-MO/NuminaMath-TIR", split="train").select(range(10))

model_id = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B"  # Exchange with another smol distilled r1

with Pipeline(
    name="distill-qwen-7b-r1",
    description="A pipeline to generate data from a distilled r1 model",
) as pipeline:

    llm = vLLM(
        model=model_id,
        tokenizer=model_id,
        extra_kwargs={
            "tensor_parallel_size": 1,
            "max_model_len": 8192,
        },
        generation_kwargs={
            "temperature": 0.6,
            "max_new_tokens": 8192,
        },
    )
    prompt_column = "problem"
    text_generation = TextGeneration(
        llm=llm, 
        template=prompt_template,
        num_generations=4,
        input_mappings={"instruction": prompt_column} if prompt_column is not None else {}
    )


if __name__ == "__main__":
    distiset = pipeline.run(dataset=dataset)
    distiset.push_to_hub(repo_id="username/numina-deepseek-r1-qwen-7b")

查看 HuggingFaceH4/numina-deepseek-r1-qwen-7b 处的示例数据集。

从 DeepSeek-R1 生成数据

要运行更大的 DeepSeek-R1,我们使用了 2 个节点,每个节点配备 8 块 H100 GPU,使用本仓库中 slurm/generate.slurm 处的 slurm 文件。首先,安装依赖:

(目前我们需要安装修复了 R1 CUDA 图捕获问题的 vllm 开发版 wheel 包)

pip install https://wheels.vllm.ai/221d388cc5a836fa189305785ed7e887cea8b510/vllm-1.0.0.dev-cp38-abi3-manylinux1_x86_64.whl --extra-index-url https://download.pytorch.org/whl/cu121

uv pip install "distilabel[vllm,ray,openai]>=1.5.2"

然后运行以下命令:

sbatch slurm/generate.slurm \
    --hf-dataset AI-MO/NuminaMath-TIR \
    --temperature 0.6 \
    --prompt-column problem \
    --model deepseek-ai/DeepSeek-R1 \
    --hf-output-dataset username/r1-dataset

[注意] 当任务运行时,你可以通过集群登录节点设置 SSH 隧道,从你的计算机运行 ssh -L 8265:ray_ip_head_node:8265 <login_node> 来访问 Ray 仪表板,然后浏览 http://localhost:8265

数据去污染

参照 s1:简单测试时缩放,可以使用 scripts/decontaminate.py 脚本对数据进行去污染,该脚本使用 8-gram 对数据集进行去污染并去重数据。示例运行:

python scripts/decontaminate.py \
    --dataset "open-r1/verifiable-coding-problems-python" \
    --problem_column problem \
    --cleanup

它将针对基准测试数据集进行去污染,然后移除被污染的样本。如果没有提供参数 --new_dataset_name,则会复用同一数据集,并添加 _decontaminated 后缀。它针对提示词运行,对于该数据集,提示词列是 problem,但也可以指定其他列。

脚本的参数:

usage: decontaminate.py [-h] --dataset DATASET [--split SPLIT] [--ngram_size NGRAM_SIZE] [--problem_column PROBLEM_COLUMN] [--cleanup] [--new_dataset_name NEW_DATASET_NAME]

options:
  -h, --help            show this help message and exit
  --dataset DATASET     Name of the dataset to check for contamination.
  --split SPLIT         Split to check for contamination, defaults to `train`.
  --ngram_size NGRAM_SIZE
                        Size of n-grams to build, defaults to 8.
  --problem_column PROBLEM_COLUMN
                        Name of the column containing the problem (prompt).
  --cleanup           Whether to remove the contaminated rows before pushing the dataset.
  --new_dataset_name NEW_DATASET_NAME
                        New name for the dataset. If not provided, will reuse the name and add a `_decontaminated` to the name.

贡献

欢迎贡献。请参考 https://github.com/huggingface/open-r1/issues/23。

致谢

本项目凝聚了开放 AI 社区众多团队与个人的共同努力。我们特别感谢 vLLM 和 SGLang 团队,他们开发了高性能工具,用于扩展 GRPO 的部署规模。同时,我们也感谢 OpenThoughts、Prime Intellect 和 General Reasoning 团队,他们创建并分享了高质量的推理数据集。

引用

如果您认为本项目对您的工作有所帮助,请考虑按以下方式引用:

@misc{openr1,
    title = {Open R1: A fully open reproduction of DeepSeek-R1},
    url = {https://github.com/huggingface/open-r1},
    author = {{Hugging Face}},
    month = {January},
    year = {2025}
}

来源:Hacker News 热门(buzzing.cc 中文翻译)· github.com