DeepSeek R1 Zero中文复现教程来了!

DeepSeek R1 Zero中文复现教程来了!

DeepSeek R1 Zero中文复现教程来了! DeepSeek R1 Zero中文复现教程来了! Modified November 26, 2025 其实从上图我们也可以看到 GRPO 已经意识到重复问题带来的奖励值下降,它在 200 步左右开始逐步限制模型输出长度,而这时模型的计算正确率也保持在 0.3 到 0.4 左右。 我们还发现,在训练初期,你会看到比较明显的方程奖励提升,而输出长度不断减小。模型似乎有一种趋向于缩短思考长度的趋势,所以我们引入思考长度奖励函数来对抗这种趋势,我们把它解释为模型计算能力提升之后,就像学霸一眼秒杀题目一样,模型不想输出更多“废话”来解释解题过程。 在训练开始 1 分钟左右,我们就观察到下面的输出,还以为我们重现了 Aha Moment。后来证明其实不是,Qwen 2.5 很喜欢反复试错、验算,反复试错很容易导致上文提及的重复输出问题。 我们发现了另一种语言混用现象,哈哈哈。current n. 电流; adj. 当前的。 所以结论就是,我们没有复现 Aha Moment。其实在观察大量 Qwen 2.5 的输出之后,一种直觉告诉我,可能 Aha Moment 跟模型本身的输出风格相关,网友都说 DeepSeek 文风很锐利、很活泼,但是 Qwen 2.5 给人的感觉总是冷静、平和。简单做了一个不严谨的测试,可能能够佐证这个想法,我要求两个模型用 Aha Moment 的语气跟我说话,再随便回复了一个字,观察两个模型本身对 Aha Moment 的映射是会输出什么。我们另外测试了 Llama 和 MiniCPM,它们的输出风格都跟 Qwen 很接近,试图像说教一样给你做比喻,所以我大胆判断,可能写武侠小说的大模型更容易观察到 Aha Moment。 我们会一同公布模型输出的采样文本文件,大家也可以在里面找到一些我们还没有发现的新奇玩意,欢迎向 Unlock DeepSeek 团队报告你的发现。 展望 在本文写作前一天,我们发现 另一组团队也公开了他们的 Qwen 2.5 7B 的 R1 Zero 复现结果 (https://zhuanlan.zhihu.com/p/21290410831) ,他们也观察到了很多有趣的结果,虽然他们的曲线非常震荡,但是也稍微能看出一点佐证我们观点的证据:似乎拉长模型输出,能带一定的计算正确率的提升。他们的工作非常棒!我们就不用去验证 7B 模型的性能了,非常环保,节能减排。大家也可以追踪观察社区其他小组的复现报告,相信开源社区的力量! 最后嘱咐一些要点, • Math 模型不太好用,它有固有的数学输出会影响格式奖励,可能需要更长的步长才能纠正,不环保,训了一会我就停了。 • 小于 3B 的模型真不好用,没什么必要再试验了,DeepSeek 官方蒸馏的 1.5B 的推理也很烂,小模型承受了太多它不该承受的东西。我们甚至还在 0.5B 的模型看到了俄语,但是找不到图了。 其实从上图我们也可以看到 GRPO 已经意识到重复问题带来的奖励值下降,它在 200 步左右开始逐步限制模型输出长度,而这时模型的计算正确率也保持在 0.3 到 0.4 左右。 我们还发现,在训练初期,你会看到比较明显的方程奖励提升,而输出长度不断减小。模型似乎有一种趋向于缩短思考长度的趋势,所以我们引入思考长度奖励函数来对抗这种趋势,我们把它解释为模型计算能力提升之后,就像学霸一眼秒杀题目一样,模型不想输出更多“废话”来解释解题过程。 在训练开始 1 分钟左右,我们就观察到下面的输出,还以为我们重现了 Aha Moment。后来证明其实不是,Qwen 2.5 很喜欢反复试错、验算,反复试错很容易导致上文提及的重复输出问题。 我们发现了另一种语言混用现象,哈哈哈。current n. 电流; adj. 当前的。 所以结论就是,我们没有复现 Aha Moment。其实在观察大量 Qwen 2.5 的输出之后,一种直觉告诉我,可能 Aha Moment 跟模型本身的输出风格相关,网友都说 DeepSeek 文风很锐利、很活泼,但是 Qwen 2.5 给人的感觉总是冷静、平和。简单做了一个不严谨的测试,可能能够佐证这个想法,我要求两个模型用 Aha Moment 的语气跟我说话,再随便回复了一个字,观察两个模型本身对 Aha Moment 的映射是会输出什么。我们另外测试了 Llama 和 MiniCPM,它们的输出风格都跟 Qwen 很接近,试图像说教一样给你做比喻,所以我大胆判断,可能写武侠小说的大模型更容易观察到 Aha Moment。 我们会一同公布模型输出的采样文本文件,大家也可以在里面找到一些我们还没有发现的新奇玩意,欢迎向 Unlock DeepSeek 团队报告你的发现。 展望 在本文写作前一天,我们发现 另一组团队也公开了他们的 Qwen 2.5 7B 的 R1 Zero 复现结果 (https://zhuanlan.zhihu.com/p/21290410831) ,他们也观察到了很多有趣的结果,虽然他们的曲线非常震荡,但是也稍微能看出一点佐证我们观点的证据:似乎拉长模型输出,能带一定的计算正确率的提升。他们的工作非常棒!我们就不用去验证 7B 模型的性能了,非常环保,节能减排。大家也可以追踪观察社区其他小组的复现报告,相信开源社区的力量! 最后嘱咐一些要点, • Math 模型不太好用,它有固有的数学输出会影响格式奖励,可能需要更长的步长才能纠正,不环保,训了一会我就停了。 • 小于 3B 的模型真不好用,没什么必要再试验了,DeepSeek 官方蒸馏的 1.5B 的推理也很烂,小模型承受了太多它不该承受的东西。我们甚至还在 0.5B 的模型看到了俄语,但是找不到图了。 • 这种训练方式用来规范模型输出格式特别好用。 • Jian Hu 报告 GRPO 有严重震荡问题 (https://zhuanlan.zhihu.com/p/14888098807) ,或许大家可以试试其他算法。 • 如果你的资源充足,可以试试更大的模型,希望在开源社区能够见到大家的新发现。 • TRL 目前的 LoRA 模块有严重 Bug,请不要使用。 • 最后一点,要复现,请用 TinyZero,省钱! 完整文件获取 Unlock DeepSeek 团队后续会陆续发布更多关于 DeepSeek 相关工作解读的文章(马上就会发布 GRPO 解读文章),敬请关注,我们下次再见! Unlock DeepSeek 项目主页:https://datawhalechina.github.io/unlock deepseek/ Github 仓库:https://github.com/datawhalechina/unlock deepseek Gitee 国内仓库:https://gitee.com/anine09/unlock deepseek Swanlab 实验数据:https://swanlab.cn/@anine09/datawhale r1/overview 模型会在晚些时候上传 HuggingFace 和 ModelScope,并在项目中公布,虽然模型本身没什么用。 复现文件在 Datawhale R1 文件夹。 Unlock DeepSeek 项目目前并不完善,并且正在快速迭代,请持续关注。 一起“ 点 赞 ” 三连 ↓ 🔗 原文链接: https://mp.weixin.qq.com/s/Z7P61IV3... https://mp.weixin.qq.com/s/Z7P61IV3... 原创 骆师傅 Datawhale 2025年02月06日 22:01 浙江 Datawhale干货 作者 : 骆秀韬,Datawhale成员 项目代码可见:unlock deepseek/Datawhale R1(https://github.com/datawhalechina/unlock deepseek),欢迎关注和 star! 其余所有开源内容见文末。本文实现细节有bug,已在文末网址更正。 各位同学好,我是来自 Unlock DeepSeek 开源项目团队的骆师傅。先说结论, 我们(Datawhale X 似然实验室)使用 3 张 A800(80G) 计算卡,花了 20 小时训练时间,做出了可能是国内首批 DeepSeek R1 Zero 的中文复现版本,我们把它叫做 Datawhale R1,用于 R1 Zero 复现教学。 按照 5.5 元 7.0 元每小时的价格计算,3 张 A800 花费最低为 3 5.5 20 = 330 元,预计花费接近 420 元,而 TinyZero (https://github.com/Jiayi Pan/TinyZero) 项目用了 4 张 A800 训练了 8 小时,预计花费为:224 元,这中间的差异可能是由于硬件性能瓶颈和框架差异带来的(我们用的是 Huggingface TRL,TinyZero 使用的是 veRL)。所以建议 大家如果真的要复现,请使用 TinyZero 项目 ,我们出于教育目的使用 TRL 为大家报告这个结果。 另外,不是所有人都能随时随地调用 3 张 A800 的,我们正在努力减小硬件资源要求,让复现工作尽可能平民化(比如在 4090 上跑)。在这里特别感谢:似然实验室,提供本次复现的计算资源,并与 Datawhale 团队合作贡献了本教程。 回到正题,首先回答一个关键问题:为什么这个方案更贵,而我们却选择了它?答案就是:它更符合教育目的,截止本文发布,大部分同学没有足够的资源来亲手体验复现流程,但是我们希望大家能更清楚的看到,复现 R1 Zero 的过程中都发生了什么,真正对复现原理有个大致把握,就算做“云玩家”也要学到知识,看完骆师傅做一遍就好像自己也做了一遍。 本方案在 mini r1 (https://www.philschmid.de/mini deepseek r1) 的基础上改进而来。 环境搭建 配置基础工具 首先我们要搭建环境,作为手把手教程以及骆师傅的看家本领,我们会在这部分说得细致些。结合国内的实际情况,我们需要的环境信息如下: 暂时无法支持非 Linux 系统(Windows、MacOS) • CUDA 12.0 (我们使用的是 CUDA 12.4) • Python 建议版本为 3.12(我们使用 Miniforge 管理虚拟环境) • Pytorch 版本为 2.5.1 (GPU版本,请使用 torch.cuda.is available() 检查能否正常识别 GPU 设备) 建议使用 Miniforge / Conda 来安装 Pytorch,我们在南方科技大学的开源镜像源测试,下载速度会比官网 pip 安装快不少,请在下面的网址找到适合你硬件的 2.5.1 版本:https://pytorch.org/get started/previous versions/,推荐使用 mamba 安装(安装 Miniforge 后直接将 conda 替换为 mamba) 编译安装 flash attn 接着重头戏就来了,我们需要编译安装 Flash Attention 包,这步非常消耗 CPU 资源,非常不建议CPU核心少的玩家执行。如果你没有办法在“有生之年”编译完 Flash Attention,可以在 https://github.com/Dao AILab/flash attention/releases/ 找到与你环境对应的编译好的包。(没对应上的话,改环境反而更快,相信我,编译很慢) 这个步骤倒是很简单,执行下面的命令: pip install packaging pip install ninja 用于加速编译 编译安装 Flash Attention 包 pip install flash attn no build isolation 注意!如果你的设备CPU核心多,但是运行内存小于 96 GB,请适当设置 MAX JOBS 的数量,并替换为下面的命令,参考:https://github.com/Dao AILab/flash attention installation and features MAX JOBS=4 pip install flash attn no build isolation 按下回车后,可以泡杯咖啡,打开 htop 看 CPU 疯狂运作,再重新品读一遍 《DeepSeek R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning》 (https://arxiv.org/abs/2501.12948) 等待 flash attn 安装完毕后,我们就可以安装其他涉及到的库了,我们提供了一份 requirements.txt 在 Unlock DeepSeek (https://github.com/datawhalechina/unlock deepseek) 项目,核心列表如下: setuptools<71.0.0 transformers==4.48.1 datasets==3.1.0 accelerate==1.3.0 hf transfer==0.1.9 deepspeed==0.15.4 trl==0.14.0 vllm==0.7.0 modelscope==1.22.3 swanlab==0.4.6 huggingface hub==0.28.1 大家也可以在这个地址找到我们所有涉及的 Python 包列表:https://swanlab.cn/@anine09/datawhale r1/runs/4tp31j1zxbm1fshjsi53b/environment/requirements 下载模型和数据集 接下来我们需要下载数据集和模型,在本次实验中,我们使用的数据集为: Jiayi Pan/Countdown Tasks 3to4 (https://huggingface.co/datasets/Jiayi Pan/Countdown Tasks 3to4) ,模型为: Qwen/Qwen2.5 3B Instruct (https://huggingface.co/Qwen/Qwen2.5 3B Instruct) ,我们目前不建议用小于 3B 的模型(其他社区多次报告,小于 3B 的模型无法学会推理,经过我们的测试,确实!) 数据集下载方式: export HF ENDPOINT=https://hf mirror.com 更换为国内镜像源,这个只用执行一次,每次重新打开终端就要重新执行,或者写入 .bashrc 下载数据集,替换整个 <xxx 为你自己的内容 huggingface cli download repo type dataset resume download Jiayi Pan/Countdown Tasks 3to4 local dir <你想要存放的路径,比如:dataset 模型下载方式,哪个速度快用哪个: • 方案一,Huggingface 镜像源 下载模型,替换整个 <xxx 为你自己的内容 huggingface cli download resume download Qwen/Qwen2.5 3B Instruct local dir <你想要存放的路径,比如:models • 方案二,ModelScope 下载 新建 model download.py 文件,填入以下内容,替换整个 <xxx 为你自己的内容,保存后使用python model download.py 执行下载。 from modelscope import snapshot download model dir = snapshot download('Qwen/Qwen2.5 3B Instruct', cache dir='<你想要存放的路径,比如:models ', revision='master') 编写配置文件和训练代码 接下来我们需要准备 3 个文件,我们会在 Unlock DeepSeek (https://github.com/datawhalechina/unlock deepseek) 项目中提供完整的复现文件,方便同学们直接使用。 • 第一个是 Accelerate 配置文件,用于分布式训练(三张卡)。新建 deepspeed zero3.yaml 填入以下内容并保存(不是 DeepSeek,别看错!)。 compute environment: LOCAL MACHINE debug: false deepspeed config: deepspeed multinode launcher: standard offload optimizer device: none offload param device: none zero3 init flag: true zero3 save 16bit model: true zero stage: 3 distributed type: DEEPSPEED downcast bf16: 'no' machine rank: 0 main training function: main mixed precision: bf16 num machines: 1 num processes: 8 我们在这里保持常规默认的 8 卡机器,会在后面的启动命令中覆盖新值 rdzv backend: static same network: true tpu env: [] tpu use cluster: false tpu use sudo: false use cpu: false 一般来说,这个文件内容不需要修改,如果有定制需求,请不要使用这个文件,运行 accelerate config 自行设定。 在介绍下一个文件之前,我们强烈建议大家使用 Swanlab (https://swanlab.cn/) 来可视化追踪实验过程,打开:https://swanlab.cn/login ,登录之后点击图中所示的 Quick Start,或者打开:https://swanlab.cn/space/ /settings ,复制 API Key。 在终端输入 swanlab login ,直接粘贴(你是看不见东西被粘贴上去的),回车,出现类似如下提示就是登录成功。 • 第二个是 TRL 配置文件,在这里我们会设定训练的超参数。新建 Datawhale R1.yaml 填入以下内容,并根据实际情况修改 (阅读注释) ,并保存。 模型参数 model name or path: <你的模型存放的路径,比如:models/Qwen/Qwen2.5 3B Instruct model revision: main torch dtype: bfloat16 attn implementation: flash attention 2 bf16: true tf32: true output dir: <你想要模型输出的路径,比如 output/Datawhale R1 数据集参数 dataset id or path: <你的数据集存放的路径,比如:dataset Swanlab 训练流程记录参数 swanlab: true 是否开启 Swanlab workspace: <用户名 project: <项目名,整个复现项目的名称,例如:Datawhale R1 by xxx experiment name: <实验名,某次超参数运行的自定义名称,例如:qwen2.5 3B lr:5e 7 beta:0.001 训练参数 max steps: 450 最大训练步长 per device train batch size: 1 gradient accumulation steps: 8 gradient checkpointing: true gradient checkpointing kwargs: use reentrant: false learning rate: 5.0e 7 学习率,调整过,参见下文介绍 lr scheduler type: cosine 学习率衰减方案 warmup ratio: 0.03 学习率预热比率(对于整个步长),好用! seed: 2025 随机种子,方便实验复现 GRPO 算法参数 beta: 0.001 KL 惩罚因子,调整过,参见下文介绍 max prompt length: 256 输入 prompt 最大长度,本实验基本不会有太大变化 max completion length: 4096 输出回答长度,包含推理思维链,设为 4K 比较合适 num generations: 8 use vllm: true 启用 vllm 来加速推理 vllm device: <计算卡编号,例如:cuda:2 留出一张卡来启用 vllm 推理,参见下文介绍 vllm gpu memory utilization: 0.5 Logging arguments logging strategy: steps logging steps: 1 save strategy: "steps" save steps: 50 每隔多少步保存一次 我们并没有介绍全部参数,如果需要调整,请查阅 Huggingface 相关文档。当然,直接询问 DeepSeek 可能是更快的方式。 这份配置文件中有一些值得大家注意的地方: • learning rate 和 beta 在 GRPO 的原始论文 《DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models》 (https://arxiv.org/abs/2402.03300) 里分别为 1e 6 和 0.04 。在这里我们根据 《Unraveling RLHF and Its Variants: Progress and Practical Engineering Insights》 (https://hijkzzz.notion.site/unraveling rlhf and its variants engineering insights) 将其调整为 5e 7 和 0.001 。 • vllm device 本实验需要留出一张卡作为 vllm 的推理卡,假设我们手上有 3 张卡(编号cuda: 0, cuda: 1, cuda: 2),我们需要指定其中一张卡为 vllm 推理卡,例如我们指定最后一张 cuda:2 。另外,如果你使用了 CUDA VISIBLE DEVICES 情况会有些不一样,比如我们有 8 张卡(编号 cuda:0 7),指定编号为 1、2、3 的卡可见( CUDA VISIBLE DEVICES=1,2,3 ),这时我们想指定最后一张卡为 vllm 推理卡,则是需要设置为 cuda:2 ,因为设置完可见性后,cuda:1 cuda:0,cuda:2 cuda:1,cuda:3 cuda:2,所以原先的 3 号卡变为了新编号的 2 号卡。 • save steps 在 mini r1 (https://www.philschmid.de/mini deepseek r1) 中是被设为 25 ,但是跑完整个训练后,保存的文件大小达到了 700+ GB!因为不仅包含了模型,还包含了其他卡的优化器状态和其他检查点信息,我们在这里改为 50 ,但仍然要提醒同学们设置成合适自己的大小(训练代码中已经包含结束后保存模型的代码)。 • 最后,就是创建训练代码文件 train Datawhale R1.py 并保存,我们几乎给每个关键步骤都添加了注释(建议大家从后往前读),在后文我们会再梳理一遍核心步骤。 import logging import os import random import re from dataclasses import dataclass from datetime import datetime from typing import List from datasets import load dataset from swanlab.integration.transformers import SwanLabCallback from transformers import AutoTokenizer from transformers.trainer utils import get last checkpoint from trl import GRPOConfig, GRPOTrainer, ModelConfig, TrlParser @dataclass class DatasetArguments: """数据集参数的数据类""" 数据集 ID 或路径 dataset id or path: str = "Jiayi Pan/Countdown Tasks 3to4" 数据集拆分 dataset splits: str = "train" 分词器名称或路径 tokenizer name or path: str = None @dataclass class SwanlabArguments: """SwanLab参数的数据类""" 是否使用 SwanLab swanlab: bool SwanLab 用户名 workspace: str SwanLab 的项目名 project: str SwanLab 的实验名 experiment name: str 配置日志记录器 logging.basicConfig(level=logging.INFO) logger = logging.getLogger( name ) logger.setLevel(logging.INFO) handler = logging.StreamHandler() handler.setFormatter( logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s") ) 设置日志格式 logger.addHandler(handler) def format reward func(completions, kwargs): """ 格式奖励函数,检查模型输出格式是否匹配: <think ...</think <answer ...</answer 参数: completions (list[str]): 生成的输出 返回: list[float]: 奖励分数 """ 初始化奖励列表 rewards = [] 遍历生成的输出 for completion in completions: try: 在生成的输出前添加<think 标签,便于后续正则表达式匹配 completion = "<think " + completion if random.random() < 0.1: 1% 的概率将生成输出写入文件 创建生成输出目录(如果不存在) os.makedirs("completion samples", exist ok=True) log file = os.path.join("completion samples", "completion samples.txt") with open(log file, "a") as f: f.write(f"\n\n==============\n") f.write(completion) 写入生成的输出 定义正则表达式模式,用于匹配 <think 和 <answer 标签 regex = r"^<think ([^<] (?:<(?!/?think )[^<] ) )<\/think \n<answer ([\s\S] ?)<\/answer $" match = re.search(regex, completion, re.DOTALL) 使用正则表达式进行匹配 if match is None or len(match.groups()) != 2: rewards.append(0.0) 如果格式不正确,奖励为 0 else: rewards.append(1.0) 如果格式正确,奖励为 1 except Exception: rewards.append(0.0) 如果发生

在 小宇宙note 阅读完整内容