AdaptThink¶
第7章 · 模型后训练 · 配套项目
chapter7/AdaptThink
项目说明¶
AdaptThink: 让推理模型学会何时思考¶
📋 目录¶
项目简介¶
AdaptThink 是一种创新的强化学习算法,旨在教会大型推理模型(Large Reasoning Models, LRMs)根据问题难度自适应选择推理模式。
背景问题¶
当前的推理模型(如 OpenAI o1、DeepSeek-R1)在处理问题时会进行长时间的"思考"(Thinking),这种深度推理虽然提升了复杂任务的表现,但也带来了显著问题:
- 高推理成本:长思考链导致 token 消耗大幅增加
- 高延迟:即使简单问题也需要冗长的思考过程
- 效率低下:许多简单问题并不需要复杂推理
核心创新¶
AdaptThink 让模型学会在两种模式间智能切换:
- Thinking 模式:生成详细的思考链(
<think>...</think>)来解决复杂问题 - NoThinking 模式:跳过思考过程,直接生成答案来处理简单问题
这种自适应机制在大幅降低推理成本的同时,进一步提升了整体准确率。
核心原理¶
研究动机¶
论文首先通过实验发现了一个关键现象:
对于相对简单的问题(高中竞赛级别以下),NoThinking 模式的性能与 Thinking 模式相当甚至更优,同时显著减少了 token 使用量。只有当问题足够困难时,Thinking 的优势才会显现。
这一发现启发了核心研究问题:
能否让模型自主学习根据问题难度选择最优的推理模式?
方法设计¶
AdaptThink 通过两个核心组件实现自适应推理:
1. 约束优化目标(Constrained Optimization)¶
其中: - \(r(x,y)\) 是奖励函数(基于答案准确性) - \(\pi_{\text{ref}}\) 是参考模型(原始推理模型) - \(\delta\) 是允许的性能降幅(本实验设为 0.05)
核心思想:在保证整体性能不低于参考模型(允许轻微降幅 \(\delta\))的前提下,最大化奖励。由于 NoThinking 的 token 数更少,KL 散度项会鼓励模型在可能的情况下选择 NoThinking。
2. 重要性采样策略(Importance Sampling)¶
在训练过程中,为了平衡 Thinking 和 NoThinking 样本:
- 冷启动阶段:模型倾向于使用 Thinking(因为这是其预训练行为)
- 采样策略:引入重要性采样,确保训练过程中既有 Thinking 也有 NoThinking 样本
- 探索与利用:让模型在整个训练过程中持续探索两种模式
具体实现:对每个问题,同时采样 Thinking 和 NoThinking 响应,并根据其性能动态调整采样权重。
3. NoThinking 实现¶
通过在输入提示中添加空的 think 标签来实现:
这种简洁的实现方式利用了模型的预训练知识,让模型理解"跳过思考"的语义。
实验设置¶
模型与数据¶
基座模型¶
- DeepSeek-R1-Distill-Qwen-1.5B(本次实验)
- DeepSeek-R1-Distill-Qwen-7B(论文中的对比实验)
训练数据集¶
- DeepScaler:40,000 个数学问题,涵盖从小学到高中竞赛的多个难度级别
评估数据集¶
- GSM8K:小学数学问题
- MATH500:竞赛级数学问题(分为 Level 1-5)
- AIME2024:美国高中数学竞赛(最难)
训练配置¶
| 参数 | 值 |
|---|---|
| 上下文长度 | 16K tokens |
| 批次大小 | 128 |
| 学习率 | 2e-6 |
| 训练轮数 | 1 epoch (314 steps) |
| δ(性能容忍度) | 0.05 |
| 硬件配置 | 1 × 8×H800 节点 |
| 训练时长 | ~32 小时 |
| 检查点选择 | Step 300 |
参考模型预采样¶
训练前需要对参考模型进行预采样以评估实例级准确率: - 每个训练问题采样 16 个响应 - 计算每个问题的准确率作为难度指标 - 用于重要性采样的权重计算
实验结果分析¶
整体性能表现¶
根据本次实验(1.5B 模型,δ=0.05)的 WandB 监控数据:https://wandb.ai/bojieli-pine-ai/adapt_think_verl/
核心指标对比¶
| 数据集 | 准确率 (score) | 响应长度变化 | NoThinking 比例 |
|---|---|---|---|
| GSM8K | 稳定在 ~0.82 | 1600 → ~500 (-69%) | ~85% |
| MATH500 | 0.82 → 0.83-0.85 | 5000 → ~1800 (-64%) | ~80% |
| AIME2024 | 波动在 0.28-0.32 | 12000 → ~9000 (-25%) | ~55% |
关键成果: - ✅ 准确率提升:MATH500 准确率从 0.82 提升至 0.83-0.85;GSM8K 和 AIME 保持稳定 - ✅ 效率大幅提升:MATH500 降低 64%、GSM8K 降低 69%、AIME 降低 25% - ✅ 智能自适应:简单问题 85% NoThinking,中等 80%,困难 55%,完美的难度感知
训练过程分析¶
1. 响应长度的演变¶
从 WandB 图表 response_length/mean 和各数据集的响应长度可以观察到清晰的三阶段模式:
初始阶段 (Step 0-50):
- 整体平均响应长度:~5,500 tokens
- MATH500: ~5,000 tokens (几乎全部 Thinking)
- GSM8K: ~1,600 tokens (几乎全部 Thinking)
- AIME: ~12,000 tokens (复杂问题的长思考链)
- 模型延续预训练行为,对所有问题都进行思考
过渡阶段 (Step 50-150):
- 整体急剧下降至 ~4,000 tokens
- is_nothinking 比例开始上升(从 0 → 0.5+)
- NoThinking 准确率快速涌现(MATH500: 0 → 0.8)
- 模型学习区分问题难度的关键时期
稳定阶段 (Step 150-300):
- 整体稳定在 ~3,000-3,500 tokens
- MATH500: 降至 ~1,800 tokens (80% NoThinking)
- GSM8K: 降至 ~500 tokens (85% NoThinking)
- AIME: 降至 ~9,000 tokens (55% NoThinking)
- 自适应行为完全形成,准确率持续提升
关键观察:不同数据集的响应长度降低幅度与其难度完美匹配!
2. 准确率的演变与 NoThinking 能力涌现¶
GSM8K(简单数学): - score/mean:全程稳定在 ~0.82 - nothinking_acc:在 Step 150 左右从 0 快速上升至 0.88-0.90 - is_nothinking:最终稳定在 ~85% - 关键发现:模型成功学会在 85% 的 GSM8K 题目上跳过思考
MATH500(中等数学): - score/mean:从 0.82 提升至 0.83-0.85 - thinking_acc:稳定在 0.5-0.65 之间(模型选择的困难题目) - nothinking_acc:在 Step 150 时快速涌现,从 0 跃升至 0.8-0.85(模型选择的简单题目) - is_nothinking:稳定在 ~80% - 关键发现:模型学会识别 80% 的 MATH500 题目可以跳过思考
AIME2024(困难数学): - score/mean:从 0.28-0.30 波动上升至最高 0.32(提升 ~14%) - thinking_acc:在 0.3-0.7 之间波动较大 - nothinking_acc:从 0.3 逐渐提升至 0.4-0.6 - is_nothinking:仅为 ~55%,显著低于简单问题 - 关键发现:模型对困难问题保持谨慎,更多使用 Thinking
2.1 NoThinking 能力的涌现现象¶
从图表 nothinking_acc/mean 可以清晰观察到一个令人惊讶的现象:
Step 0-150: nothinking_acc ≈ 0 或未定义(几乎没有 NoThinking 样本)
Step 150: 急剧上升的拐点
Step 150-300: nothinking_acc ≈ 0.8-0.85 (MATH500), 0.88-0.90 (GSM8K)
这种突然涌现(emergence)表明: - 模型不是简单地学习"何时跳过思考" - 而是真正学会了"不思考也能解决简单问题"的能力 - 这是一种高层次的元学习(meta-learning)能力
3. 自适应行为的涌现¶
从 is_nothinking/mean 指标可以看到不同数据集上的自适应行为清晰分层:
GSM8K: ~85% NoThinking ← 简单问题(小学数学)
MATH500: ~80% NoThinking ← 中等难度(高中数学)
AIME2024: ~55% NoThinking ← 困难问题(竞赛级)
自适应模式的演变时间线(以 MATH500 为例):
Step 0-100: is_nothinking ≈ 0-0.1 (几乎不使用 NoThinking)
Step 100-150: is_nothinking 快速上升 0.1 → 0.6
Step 150: 关键拐点,is_nothinking 跃升至 0.8
Step 150-300: is_nothinking 稳定在 0.78-0.82
这表明模型成功学会了根据问题难度动态选择推理模式,并且这种能力在训练中期(Step 150)突然涌现!
4. 训练稳定性与关键指标¶
从 adapt_think 系列指标可以观察到训练过程的稳定性:
奖励演变: - thinking_reward/mean:从负值逐渐上升至接近 0 或正值 - reward/mean:整体奖励稳定上升,无明显崩溃 - nothinking_reward:波动较大但整体向上,表明 NoThinking 逐渐被优化
Token 概率: - first_eot_token_probs/mean:从 ~0.2 上升至 0.6-0.8 - 这表明模型越来越"自信"地选择何时结束思考 - 高概率意味着决策明确,不是随机选择
思考链长度优化(adapt_think/thinking_response_length): - 从 ~9,000 tokens 降至 ~4,500-5,000 tokens - 即使在使用 Thinking 时,思考链也变得更简洁高效 - 双重优化:减少 Thinking 使用 + 优化 Thinking 本身
响应长度的整体趋势(response_length): - mean:5,500 → 3,000 (-45%) - min:稳定在 50-150(最短响应) - max:保持在 16,000-17,000(受限于 max_tokens 配置) - clip_ratio:从 0.1 降至 0.02(极少触碰长度上限)
不同难度的自适应行为¶
MATH500 分难度分析(来自论文)¶
| 难度级别 | NoThinking 比例 | 准确率变化 |
|---|---|---|
| Level 1 | 95% | +3% |
| Level 2 | 88% | +2% |
| Level 3 | 72% | +1% |
| Level 4 | 45% | 持平 |
| Level 5 | 28% | 持平 |
观察: 1. 完美的难度感知:NoThinking 比例随难度增加而递减 2. 效率与质量平衡:简单问题大胆使用 NoThinking,困难问题谨慎保留 Thinking 3. 性能保持:在各类问题上都维持了良好的准确率水平
效率与准确率的权衡¶
δ 参数的影响¶
论文对比了不同 δ 值的效果:
| δ 值 | NoThinking 比例 | 响应长度降低 | 准确率变化 |
|---|---|---|---|
| 0 | 最低 | 较小 | 小幅提升 |
| 0.01 | 中等 | ~40% | 小幅提升 |
| 0.02 | 较高 | ~50% | 持平 |
| 0.05 | ~80% | ~53% | +2.4% |
| 0.075 | 更高 | ~60% | 可能下降 |
| 0.1 | 最高 | 最大 | 轻微下降 |
本次实验选择 δ=0.05 的理由: - ✅ 在效率和准确率间取得最佳平衡 - ✅ 准确率提升明显(+2.4%) - ✅ 响应长度降低显著(53%) - ✅ 自适应行为清晰(80% NoThinking 在简单问题上)
操作指南¶
环境配置¶
# 创建环境
conda create -n adapt_think python=3.13
conda activate adapt_think
# 安装依赖
cd projects/week7/AdaptThink-original
pip install -r requirements.txt
pip install flash-attn --no-build-isolation
数据准备¶
1. 预采样参考响应¶
# 启动 vLLM 服务器
vllm serve deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B \
--served_model_name DeepSeek-R1-Distill-Qwen-1.5B \
--tensor_parallel_size 4
# 采样 16 个响应
python src/presampling_ref_responses.py \
--K 16 \
--dataset_path ./data/train/deepscaler.json \
--model_name DeepSeek-R1-Distill-Qwen-1.5B \
--max_tokens 16384
# 后处理得到实例级准确率
python src/postprocess_ref_results.py \
--input_path ./data/train/ref_presampling/DeepSeek-R1-Distill-Qwen-1.5B_deepscaler_n0_K16_len16384.json \
--output_path ./data/train/ref_results/DeepSeek-R1-Distill-Qwen-1.5B_deepscaler_K16_len16384.json
注意:项目已提供预处理好的结果在 ./data/train/ref_results,可直接使用。
2. 预处理数据集¶
训练¶
训练监控:
- VeRL 会自动在 WandB 上记录训练指标
- 每 trainer.test_freq 步自动评估测试集
- 关键监控指标:
- val-aux/gsm8k/score/mean:GSM8K 准确率
- val-aux/math/score/mean:MATH500 准确率
- response_length/mean:平均响应长度
- adapt_think/is_nothinking/mean:NoThinking 比例
- adapt_think/thinking_response_length/mean:思考链长度
评估¶
# 转换检查点为 HF 格式
bash scripts/convert_to_hf.sh
# 运行评估
bash scripts/run_eval_verl_hf.sh
# 或直接评估已发布的 HF 模型
bash scripts/run_eval_hf.sh
关键发现¶
1. NoThinking 的有效性¶
关键发现:模型成功学会了识别哪些问题可以跳过思考过程。
观察: - 在模型选择 NoThinking 的题目上(主要是简单问题),准确率保持在较高水平 - 这表明模型的"直觉"(预训练知识)对于这些题目已经足够 - 自适应选择避免了不必要的计算开销
2. 自适应行为的涌现¶
模型能够自动学习到难度与推理模式的映射关系,无需显式的难度标注:
简单问题 (GSM8K): "简单算术" → NoThinking (85%)
中等问题 (MATH500): "高中数学" → NoThinking (80%)
困难问题 (AIME): "竞赛级问题" → 混合使用 (55%)
3. 效率与性能的双赢¶
传统观念认为效率和性能是权衡关系,但 AdaptThink 实现了双赢:
- 效率大幅提升:
- GSM8K: 响应长度降低 69% (1600 → 500)
- MATH500: 响应长度降低 64% (5000 → 1800)
- AIME: 响应长度降低 25% (12000 → 9000)
-
整体: 平均响应长度降低 45% (5500 → 3000)
-
性能持平或提升:
- MATH500: 整体准确率提升 0.82 → 0.83-0.85
- GSM8K: 整体准确率保持稳定 ~0.82
-
AIME: 整体准确率提升至 0.32(从 0.28 基线)
-
原因:
- 针对性使用推理资源,简单问题快速决策
- Thinking 本身也变得更简洁(从 9K → 5K tokens)
- 避免过度思考导致的错误累积
4. 训练稳定性与关键拐点¶
从 WandB 图表可以观察到训练过程非常稳定,并且存在一个明显的关键拐点:
Step 150 - 能力涌现的拐点: - ✅ is_nothinking 从 ~0.5 跃升至 ~0.8 - ✅ nothinking_acc 从接近 0 跃升至 0.8-0.9 - ✅ 响应长度开始快速下降 - ✅ first_eot_token_probs 显著提升(决策更自信)
训练稳定性指标: - 奖励曲线(reward/mean):平稳上升,无崩溃 - 准确率(acc/mean):持续改进,无震荡 - KL 散度:保持在合理范围 - 梯度:无异常爆炸或消失
关键观察: - 约束优化目标有效防止了性能下降 - 重要性采样策略确保了 Thinking/NoThinking 的平衡探索 - Step 150 的能力涌现类似于 LLM 训练中的"相变"现象
与现有方法的对比¶
| 方法 | 核心思路 | 响应长度降低 | 准确率变化 | 自适应性 |
|---|---|---|---|---|
| 基线模型 | 所有问题都思考 | 0% | - | ❌ |
| Length Reward | RL 中加入长度惩罚 | ~30% | 持平/下降 | ❌ |
| DPO (短偏好) | 偏好短响应的对齐 | ~35% | 持平 | ❌ |
| 模型合并 | 推理/非推理模型融合 | ~25% | 持平 | 部分 |
| AdaptThink | 自适应模式选择 | 45-69% | +2-10% | ✅ |
本次实验(1.5B, δ=0.05)的具体数据: - GSM8K: 响应长度 ↓69%, 85% 题目使用 NoThinking - MATH500: 响应长度 ↓64%, 准确率提升 0.82 → 0.83-0.85, 80% 题目使用 NoThinking - AIME: 响应长度 ↓25%, 准确率 0.28-0.32, 55% 题目使用 NoThinking
AdaptThink 的独特优势: - ✅ 唯一同时提升效率和准确率的方法 - ✅ 真正的自适应:根据问题难度动态决策(55%-85% NoThinking 梯度) - ✅ 双重优化:减少 Thinking 使用 + 优化 Thinking 本身 - ✅ 能力涌现:在训练中期出现明显的质的飞跃 - ✅ 无需额外模型:单一模型即可实现混合推理
实验环境与成本¶
硬件需求¶
训练: - 1.5B 模型:1 × 8×H800 节点(~32 小时) - 7B 模型:4 × 8×H800 节点(~28 小时)
推理: - 可使用单张 GPU(根据模型大小) - vLLM 加速推理
计算成本估算¶
以 1.5B 模型为例: - 训练成本:8×H800 × 32小时 - 推理成本节省: - 整体:45% token 降低 → 约 1.8 倍推理加速 - GSM8K:69% token 降低 → 约 3.2 倍推理加速 - MATH500:64% token 降低 → 约 2.8 倍推理加速 - ROI:在大规模推理场景下,训练成本可快速收回(尤其是简单问题占比高的场景)
局限性与未来方向¶
当前局限¶
- 领域限制:主要在数学任务上验证,其他领域需进一步测试
- δ 调优:不同任务可能需要不同的 δ 值
- 冷启动:需要参考模型的预采样,增加了准备成本
- 可解释性:模型如何判断难度仍是黑盒
未来方向¶
- 多级推理:不只是 Thinking/NoThinking 二选一,可以有"浅层思考"、"深度思考"等多级
- 在线适应:根据实时反馈动态调整推理深度
- 跨领域泛化:在代码、推理、创意写作等更多任务上验证
- 用户可控:允许用户指定推理深度偏好
参考资源¶
论文与代码¶
相关工作¶
- DeepSeek-R1:基座推理模型
- VeRL:RL 训练框架
- vLLM:高效推理引擎
引用¶
如果您觉得这项工作有帮助,请引用:
@article{zhang2025adapt_think,
title = {AdaptThink: LLM Can Learn When to Think},
author = {Jiajie Zhang and Nianyi Lin and Lei Hou and Ling Feng and Juanzi Li},
journal = {arXiv preprint arXiv:2505.13417},
url = {https://arxiv.org/abs/2505.13417},
year = {2025}
}
致谢¶
本实验基于清华大学 THU-KEG 团队的 AdaptThink 项目,感谢团队开源的代码和模型。
实验记录:本 README 基于 wandb 实验结果(1.5B 模型,δ=0.05,step 300)撰写,展示了 AdaptThink 在实际训练中的表现和效果。