仔细瞅瞅抱抱脸针对R1的开源复现代码。
DeepSeek R1训练分为2个阶段。通过课程学习持续优化,第二阶段的数据部分来源于第一阶段。
第一阶段纯RL, 第二阶段 SFT + RL
R1训练完成之后,通过蒸馏到小模型,可以让小模型获得非常好的推理性能,同时是优于使用小模型直接进行强化学习的。
蒸馏复刻,使用 R1 构造推理思维链数据,使用小模型SFT
数据集来源包括中国高中数学练习、美国和国际数学奥林匹克竞赛问题。数据主要来自在线考试试卷 PDF 和数学讨论论坛。处理步骤包括(a)从原始 PDF 中进行 OCR,(b)分割成问题-解决方案对,(c)翻译成英语,(d)重新排列以产生 CoT 推理格式,以及(e)最终答案格式化。
构造思维链数据
SFT训练
https://github.com/huggingface/transformers/blob/main/src/transformers/trainer\_pt\_utils.py#L554
transformers 默认针对生成式模型用到的是标签平滑的损失。
标签平滑, 将正确标签的设为1-epsilon,而其他标签平均分配epsilon的部分。
->
->
GRPO
Reward:
- 准确率
def accuracy_reward(completions, solution, **kwargs):
"""Reward function that checks if the completion is the same as the ground truth."""
- 大格式
def format_reward(completions, **kwargs):
"""Reward function that checks if the completion has a specific format."""
pattern = r"^<think>.*?</think>\s*<answer>.*?</answer>$"
- 逐步格式
def reasoning_steps_reward(completions, **kwargs):
r"""Reward function that checks for clear step-by-step reasoning.
Regex pattern:
Step \d+: - matches "Step 1:", "Step 2:", etc.
^\d+\. - matches numbered lists like "1.", "2.", etc. at start of line
\n- - matches bullet points with hyphens
\n\* - matches bullet points with asterisks
First,|Second,|Next,|Finally, - matches transition words
"""
pattern = r"(Step \d+:|^\d+\.|\n-|\n\*|First,|Second,|Next,|Finally,)"
- 长度
def cosine_scaled_reward(completions, solution, **kwargs):
"""Reward function that scales based on completion length using a cosine schedule.
Shorter correct solutions are rewarded more than longer ones.
Longer incorrect solutions are penalized less than shorter ones.
- 重复的ngram惩罚
def get_repetition_penalty_reward(ngram_size: int, max_penalty: float):
"""
Computes N-gram repetition penalty as described in Appendix C.2 of https://arxiv.org/abs/2502.03373.
Reference implementation from: https://github.com/eddycmu/demystify-long-cot/blob/release/openrlhf/openrlhf/reward/repetition.py
https://github.com/huggingface/trl/blob/main/trl/trainer/grpo\_trainer.py#L459
- 左边是:ratio * advantage(优势)
- 右边是:KL散度
advantage 表示当前completion相对于组内平均水平的好坏,如果当前样本奖励大于组内(batch内)平均值,advantage > 0,否则小于0。
- 正值:比平均水平好
- 负值:比平均水平差
ratio * advantage(优势)
- 如果advantage > 0:增加该行为的概率
- 如果advantage < 0:减少该行为的概率
初始 :模型A(优化模型) 模型B(Ref_model)
数据 D
模型A, 针对数据D, 得到一批生成结果 C, 这个可以用VLLM加速,因为生成是一个token一个token生成的,比较慢。
结果C可以用规则奖励函数算出Rewards, Rewards可以进一步算出advantages。
https://github.com/huggingface/trl/blob/main/trl/trainer/grpo\_trainer.py#L579
算损失
- KL散度,通过 模型A 和 参考模型B 在C上的概率分布的差异,在保持模型稳定性的同时,逐步改进生成策略。
https://github.com/huggingface/trl/blob/main/trl/trainer/grpo\_trainer.py#L643
- 根据Advantage,优化策略模型。
正常做梯度传导,比如AC模型,格式长这样:
loss = (log_probs * advantages).mean() # 梯度:advantages * (1/log_probs)
trl的实现是:
per_token_loss = torch.exp(per_token_logps - per_token_logps.detach()) * advantages.unsqueeze(1) = (1 * advantages).mean()
per_token_logps - per_token_logps.detach() 数值是0,但是会将数值Advantage带来的优势传导到策略模型上,可以避免log_prob的数值不稳定?
大体上就这些,step3是sft+grpo,就是2步串联起来。