资讯详情

A100单卡8小时训练1.5B循环思考小模型

📅 2026/10/8 11:47:30 | 华诺云谱 👁 阅读
A100单卡8小时训练1.5B循环思考小模型
先说结论我用一张 A100 80G在 8 小时里从基础底座模型出发训练出一个 15 亿参数、会在给出最终答案前反复“想一下、自查一下、再改一下”的小模型。注意这里的“从零”是指从空白的数据管线和训练流程开始而不是从随机权重预训练——后者在单卡 8 小时里做 1.5B 级模型基本不现实。这个项目的核心是把“循环思考”这种推理时计算能力完整训练进一个小模型让它面对数学题、逻辑题时不急着给答案而是先生成思考过程、再自我批评、再修正思路最后才输出结论。适合正在玩大模型微调、想复现推理时计算、或者手头只有一块 A100 的读者参考。如果你试过直接问 1.5B 模型复杂一点的数学题大概率会有种“差一点就对了”的感觉。这其实不是模型笨而是它只会“一次生成定生死”。我这篇文章解决的问题就是如何用数据构造、SFT、GRPO 三件套把“会循环思考”这个行为真正刻进模型里。1. 为什么要把“循环思考”训练进一个小模型1.1 直接回答的瓶颈一次生成定生死大模型推理时有个很尴尬的现象模型内部其实知道正确路径但生成器是自回归的一旦前几个 token 走偏后面整段都救不回来。小模型尤其明显因为它的容量有限每一步选择的概率分布更“糊”。拿一道稍微绕一点的题举例某商品按定价的 80% 出售仍能获得 20% 的利润问定价时期的期望利润百分数是多少如果让模型直接回答它经常把“利润售价-成本”和“利润率利润/成本”搞混。更糟的是它生成错误步骤时自己完全没察觉因为它没有“回头检查”的机会。人类做题会先列式再代入再反向验算最后才写答案。但标准指令微调出来的模型只学会了“问题到答案”的捷径。这就是推理时计算test-time compute想解决的问题不改变模型权重而是在推理时多花 token 多“想几步”。OpenAI 的 o1 系列、DeepSeek-R1 都是这个路线。可问题是这些思路大多用在超大模型上小模型直接照搬很容易变成“啰嗦但没用”。1.2 “循环思考”不是玄学而是可训练的推理策略循环思考的具体形式可以拆成三段第一轮思考、反思、修正后的思考。关键不在“多想”而在“有自我否定的机会”。比如模型先列出一种解法然后自己发现“等等这里利润率的分母错了”最后重新推导。这种“产生候选思路 验证 修正”的结构比单纯拉长思维链更能提升小模型的正确率。从训练角度看这其实是一种可学习的行为模式。模型需要学会的并不是某个题的正确答案而是“如何合理使用多个思考轮次”的通用策略。数据里必须有足够多的“错误—被发现—被修正”轨迹模型才能真正形成循环。如果只给标准答案它永远学不会自查。这也是我把目标从 7B 降到 1.5B 的原因。小模型学这类结构化行为比死磕容量更有效。1.5B 足够装下“多轮思考”的格式和习惯同时训练开销能控制在单卡 8 小时内。换成 70B光是 rollout 采样一轮都不够时间。1.3 项目的目标边界我需要先明确边界避免你对标题产生误解。这个项目不涉及从随机权重预训练一个全新的语言模型在超大语料上做继续预训练让模型真的拥有“自我意识”或“反思能力”它做的只有一件事把“先思考、再批评、再修正、后回答”这个完整的决策路径用数据蒸馏和强化学习训练进一个小模型并让它能在推理阶段稳定复现出来。如果你是想给业务里的垂直问答、数学解题、代码 Debug 场景加上“会想两遍再回答”的能力这个项目非常合适。如果你想复现一个能打满分的数学模型那 1.5B 的容量上限还是会卡住你但思路完全可以复用。2. 训练数据让模型先学会“提出一个想法再否定它”2.1 三阶段轨迹设计训练数据是最关键的一步比训练本身更影响最终效果。我最终用了 16 万条轨迹每一条都是“思考、反思、再思考、最终答案”的四段结构。具体格式我定义成下面这样|begin_of_question|题目内容|end_of_question| |begin_of_think|第一轮思考先尝试按一般思路列式|end_of_think| |begin_of_reflect|反思这个思路里有个假设可能不成立|end_of_reflect| |begin_of_think|第二轮思考修正假设后重新推导|end_of_think| |begin_of_reflect|反思这次单位、公式、边界条件都检查过了|end_of_reflect| |begin_of_answer|最终答案xx|end_of_answer|为什么不只保留思维链因为传统思维链是线性推进的模型一旦走了错误分支后续全部白费。循环思考的价值在于反思段强制要求模型“把前一轮的结论当作批评对象”。这样即使第一轮错了模型也有机会纠正。我在设计时给反思段加了一句隐性规则反思的内容必须是可执行的修正而不是模糊的“我觉得不对”。比如“这里不能直接把定价乘以 80%因为利润率的分母应该是成本价”就是合格反思而“这里有点问题再想想”就是无效数据。无效反思会教模型凑长度反而浪费训练预算。2.2 用 teacher 模型批量生成“思考-反思-修正”轨迹16 万条轨迹不可能手写我用的方法是大模型蒸馏。选 5 万道种子题以 GSM8K 训练集、MATH 子集和一部分自建逻辑题为主然后让一个更强的 teacher 模型跑“三轮生成”流程第一轮让 teacher 先生成完整的思考过程和答案不许跳过步骤。自动判分用最终答案比对器判断是否正确。如果错误进入第二步。第二轮把第一轮结果和判分结果塞回 prompt让 teacher 扮演“检查者”指出第一步哪里错了再重新思考并给出答案。重复到正确或者达到三轮上限。注意 teacher 生成时要加温度采样同一个题生成多个 candidate。我只保留“最终正确”的轨迹筛选后得到约 16 万条。有个反直觉的点刻意保留一部分“第一轮错误但第二轮正确”的轨迹这个比例不要太高维持在 30% 左右效果最好。全是正确轨迹会导致模型学不会纠错全部是错误轨迹又会让模型过度犹豫。2.3 数据质量控制与格式统一蒸馏数据最大的坑是“看起来对实际上错”。我做了三件事来控制质量最终答案必须通过规则验证数学题用数字表达式匹配选择题用选项字符匹配不搞模糊语义匹配。轨迹长度上限 2048 token超过的轨迹直接丢弃。太长会把大量训练预算消耗在车轱辘话上而且小模型根本学不动。剔除模板病如果某条轨迹里出现“好的让我一步步思考”这类与题目无关的开场白我直接截断。与任务无关的社交辞令会稀释格式信号。最后把 16 万条轨迹统一转成上面那个模板按 9:1 切训练集和验证集。格式统一这一点极度重要因为后续训练阶段我会用 special token 控制输出结构数据格式不一致会让 tokenizer 学到一堆坏习惯。3. 硬件、模型与 8 小时预算3.1 为什么选 1.5B 而不是 7B 或更大从效果看7B 的上限肯定比 1.5B 高但单卡 A100 跑 7B 全参微调加 GRPO8 小时内几乎不可能完成。7B 在 GRPO 阶段做一次 rollouts 采样光是存储和吞吐就很吃力还要腾出显存跑 vLLM。1.5B 是一个性价比拐点全参微调的显存占用在 30GB 以内A100 80G 有充足余量吞吐足够高可以在 8 小时里完成“SFT GRPO 评测”三个完整步骤1.5B 的容量足够学会“循环”这种结构化策略虽然学不会所有世界知识我最后选用的是 Qwen2.5-1.5B 作为底座模型它中文和数学的底座能力在小模型里比较稳不做二次预训练。如果你用别的 1.5B 级模型流程完全一样只有 tokenizer 细节要调。3.2 一张 A100 80G 的显存账本很多人一看到“全参微调”就以为要很大显存其实 1.5B 在 A100 面前非常轻松。我大概算过一笔账项目显存估算模型权重bf16约 3GB梯度fp32/bf16约 3GBAdamW 优化器状态约 12GB激活值带梯度检查点约 4-6GB中间缓存、数据集缓冲2GB 左右合计约 24-26GB如果你开 LoRA显存还能低到 10GB 左右但全参微调能让模型更彻底地记住循环格式。我实际开了gradient_checkpointingTrue再配合 batch size 8、gradient_accumulation_steps 4峰值显存稳定在 30GB 上下。GRPO 阶段因为要同时起 vLLM 做 rollout就需要给 vLLM 留出一部分显存这个我后面细说。3.3 8 小时的时间预算怎么算出来的我做这个实验前先估算过吞吐避免训练到一半发现时间不够。A100 80G 在 bf16 下跑 1.5B 全参 SFT保守吞吐在 9k-12k token/s。16 万条轨迹平均长度约 750 token也就是 1.2 亿 token 左右纯 SFT 耗时要 3 到 4 小时。GRPO 阶段则复杂一些。我需要让模型针对 5000 道训练题每道题采样 8 条候选完成序列平均每条 1000 token光生成就是 4000 万 token。再加上训练过程的 forward/backward整体 GRPO 预估 2 到 3 小时。剩下时间用来做评测、调 bug、看 loss 曲线正好卡在 8 小时。这里我给所有人的建议是时间预算一定要按最坏情况算不要按理想吞吐算。数据加载卡一下、vLLM 崩一次、显存 OOM 重启都可能吞掉 20 分钟。我第一次跑的时候纯 SFT 就花了 5 小时因为忘记开 gradient_checkpointing激活值把显存撑爆了。4. 阶段一SFT 微调把循环格式刻进模型4.1 特殊 token 与模板设计训练前第一件事是扩展 tokenizer。Qwen2.5 本身有 chat template但我的循环格式需要四个额外的段落边界所以加了一组 special tokenfrom transformers import AutoTokenizer tokenizer AutoTokenizer.from_pretrained(Qwen/Qwen2.5-1.5B, trust_remote_codeTrue) special_tokens { additional_special_tokens: [ |begin_of_think|, |end_of_think|, |begin_of_reflect|, |end_of_reflect|, |begin_of_answer|, |end_of_answer|, ] } tokenizer.add_special_tokens(special_tokens) model.resize_token_embeddings(len(tokenizer))这里有个容易被忽略的细节这 6 个 token 必须在 SFT 之前加好并且重新 resize embedding否则模型训练出来的格式边界不存在推理时没法稳定复现循环行为。模板整体采用这样的结构system 提示“你必须按照 Thinking-Reflection-Thinking-Answer 的顺序完成任务”后面接问题段再后面是助理的完整循环回答。SFT 阶段我不直接暴露|begin_of_question|给模型当作输入前缀而是用 chat template 封装成正常的 user/assistant 会话只在 assistant 侧输出这些特殊 token。4.2 用 SFTTrainerWrapper 做格式对齐数据准备好了训练入口我用的是 TRL 里较新的SFTTrainerWrapper。这个 wrapper 的优点是天然支持 padding、packing、completion-only loss能自动把我们的特殊 token 段当作完整输出序列来学习不需要手动写循环 mask。核心训练脚本长这样from trl import SFTTrainerWrapper, SFTConfig train_dataset load_loop_dataset(data/loop_sft.jsonl) trainer SFTTrainerWrapper( modelmodel, tokenizertokenizer, train_datasettrain_dataset, configSFTConfig( output_dirckpt/loop-sft, max_seq_length2048, per_device_train_batch_size8, gradient_accumulation_steps4, learning_rate2e-5, lr_scheduler_typecosine, warmup_ratio0.03, logging_steps10, save_steps500, gradient_checkpointingTrue, bf16True, ), ) trainer.train()SFT 阶段我只跑了一个 epoch。学习率没有用 LoRA 常用的 1e-4 级别而是保守的全参 2e-5。原因很简单循环格式是一个全新结构学习率太大会把底座模型的原有能力冲掉。如果你想跑得更快可以在这个阶段用 LoRA但训练时间会短到一小时以内效果上格式能对齐但反思质量会弱一些。训练完观察验证集 loss我最终停在 0.91 左右。从可视化上看模型已经能在输出|begin_of_think|后生成完整段落然后正确进入|begin_of_reflect|。但这时候它只是“格式会了”推理能力还没真正变强因为它在模仿 teacher 的平均行为而不是自己寻找更优策略。4.3 SFT 阶段我踩过的三个坑第一个坑是 LoRA 学习率过高导致训练发散。一开始我用 LoRA 1e-4 学习率前 200 步 loss 正常300 步之后突然飙到 3.0。换成全参 2e-5 之后才稳定。如果你坚持用 LoRA学习率建议降到 3e-5 以下。第二个坑是 tokenizer 把特殊 token 和前面的中文字符黏在一起。比如|begin_of_think|前面没有空格时模型可能把它当成一个普通词的子词导致输出时不能完整生成边界 token。解决方案是在每个特殊 token 前后都加空格让 tokenizer 把它们切成独立 token。第三个坑是数据里反思段过长。我发现 teacher 蒸馏出来的反思经常重复第一轮内容占了大量序列长度。后来我在清洗时加了一条规则反思段超过 350 token 直接截断。反思要“准”不要“长”这直接影响了后续 GRPO 阶段的学习效率。5. 阶段二GRPO 强化从“会格式”到“会推理”5.1 只做 SFT 不够这是为什么SFT 后的模型能输出标准的循环结构但它有两个问题。第一它不知道“什么情况下需要反思”。比如简单题它也强行绕两轮导致输出又长又空。第二它反思了也不一定改对因为 SFT 只是在模仿 teacher 的轨迹分布没有足够信号告诉它“改完之后答案正确”这个行为更高分。要解决这两个问题需要引入强化学习。我用的是 GRPOGroup Relative Policy Optimization它是近两年推理模型训练里非常常用的一种策略优化方法核心优势是不需要单独的 critic 模型而是用同组的多个采样结果做相对比较。TRL 里对应的入口是GRPOTrainerWrapper它会把 reward 函数、rollout 采样、policy update 串成一条完整链路。5.2 奖励函数不能只奖励答案对GRPO 的 reward 设计决定了模型学习方向。我定义了三个奖励信号最后相加def loop_reward_fn(prompts, completions, **kwargs): rewards [] for completion in completions: r 0.0 # 1) 最终答案是否正确 if extract_and_match_answer(completion, prompts): r 1.0 # 2) 格式是否完整 if is_well_formed_loop(completion): r 0.2 else: r - 0.5 # 3) 长度控制防止无限循环 length tokenizer_len(completion) if length 2048: r - (length - 2048) / 2048 * 0.3 rewards.append(r) return rewards答案正确给 1.0是主要信号格式完整给 0.2是给“按循环结构走完”的基本奖励超长惩罚则是防止模型过度生成。你可能发现我没有给“反思内容质量”打分因为那很难自动化。但实际训练里模型会在正确的答案信号引导下自己摸索出“哪些反思行为更可能得到最终正确”的模式。如果你有财力做更精细的奖励可以加一个进程奖励把思考、反思、答案分别拆开对“反思中是否出现否定词、修正词”给小奖励。我在实验里试过效果有提升但奖励函数变复杂之后训练稳定性下降新手不建议一上来就堆奖励。5.3 GRPOTrainerWrapper 接入 vLLM 的实操GRPO 最耗时间的部分不是更新参数而是生成候选完成序列。如果让模型用 transformers 的generate逐个跑5000 道题每题 8 条候选可能要跑 10 小时以上。所以必须用 vLLM 做 rollout让采样速度快一个数量级。实际操作时我在同一张 A100 上先起 vLLMvllm serve Qwen/Qwen2.5-1.5B-Instruct \ --max-model-len 4096 \ --gpu-memory-utilization 0.35注意gpu-memory-utilization只给 vLLM 约 35% 显存因为 GRPO 训练本身要占用 30GB 左右加这个参数能防止显存冲突。然后训练脚本里这样接from trl import GRPOTrainerWrapper, GRPOConfig trainer GRPOTrainerWrapper( modelmodel, tokenizertokenizer, reward_funcs[loop_reward_fn], train_datasetrl_dataset, configGRPOConfig( output_dirckpt/loop-grpo, max_prompt_length512, max_completion_length2048, num_generations8, beta0.04, learning_rate1e-6, vllm_server_host0.0.0.0, vllm_server_port8000, bf16True, gradient_checkpointingTrue, ), ) trainer.train()GRPO 的超参里beta是 KL 惩罚系数。它约束模型不要离 SFT 后的版本太远。我实测下来 0.04 比较平衡太大模型学不动太小模型容易奖励 hack。学习率方面 GRPO 用 1e-6 就够策略更新本来就很激进再大就会震荡。训练过程中有一个阶段特别有意思前 1000 步模型的平均输出长度从 600 token 涨到 900 token这是它在“多想想”以争取正确答案奖励训练到 2000 步之后平均长度又回落到 750 左右因为模型开始学会筛选掉没用的思路。如果你的模型输出长度一路疯涨不回落的优先检查超长惩罚系数是不是设得太小。6. 评估循环思考是真的变强还是只是变啰嗦6.1 对比基线直接回答、SFT 循环、GRPO 循环训练结束我在 500 道抽样题上做了三组对比基线没训练过的底座模型直接回答SFT 循环阶段一训练后的模型按循环格式推理GRPO 循环阶段二训练后的模型按循环格式推理评测集底座直接答SFT 循环GRPO 循环GSM8K 抽样 200 题51.0%55.5%61.0%MATH 子集抽样 200 题7.5%11.0%17.5%自建逻辑题 100 题64.0%68.0%74.0%看到差距主要来自 GRPO 而不是 SFT。这也符合预期SFT 只是让模型“长得像会循环思考”GRPO 才是真正让模型“在循环中找到正确路径”的关键。GSM8K 这种步骤比较规范的题1.5B 模型收货最大MATH 高阶题受容量限制提升有限但依然正向。6.2 一个值得分享的成功样例评测里让我印象最深的一道题是鸡兔同笼的变体模型直接回答时算错了一个方程系数但 GRPO 后的循环推理完整复现了“发现问题—修正—答对”的过程思考设鸡 x 只兔 y 只xy352x4y94。 反思第二个方程如果 xy35 成立则 2x2y70所以 2y24y12x23。 思考代入验证 23*212*4464894正确。 答案鸡 23 只兔 12 只。这个样例的价值在于第一轮的 2x4y94 本身没问题但模型通过反思找到了更快速的验证路径。这不再是单纯的格式输出而是真的有了一点“验算”的味道。当然失败案例也不少。比如一些三位数乘法模型循环两轮后仍然算错因为小模型的基础算术能力本身就有瓶颈。**循环思考能修正推理路径错误但很难修正单步计算错误。**这是容量问题不是策略问题别指望训练技巧能完全弥补。7. 复现、降配与后续扩展的方向7.1 如果你没有 A100只有一块 24GB 显卡不要把“A100、8 小时”当成硬门槛。24GB 显存完全够用但训练策略要降档模型换成 0.5B 或 1B 级全参微调没问题数据集从 16 万条缩到 3 万到 5 万条SFT 阶段开 LoRAr32, alpha64, lr3e-5一样能学会格式GRPO 阶段减少num_generations到 4并把 vLLM 的显存占用降到 25%这样大概 2 到 3 小时能跑完一轮效果虽然比 A100 版本弱些但循环行为依然能学到。关键不是卡有多好而是你愿不愿意先把数据管线建起来。7.2 三个值得继续做的优化方向第一个是推理时投票。训练完的模型可以在推理时生成多组独立的循环思考轨迹提取每个最终答案后投票。我在评测集上试过5 次投票能再涨 3 到 5 个点。这个方案不占训练时间只占推理时间性价比极高。第二个是长上下文的适配。当前模板最长为 2048 token如果想让模型做更复杂的题需要处理超过 4k 的轨迹。你可以参考 Longformer 这类长文本模型的滑动窗口注意力思路或者干脆用稀疏注意力的变体做继续预训练。不过 8 小时时间窗口下直接砍掉超长轨迹更现实。第三个是训练一个轻量验算器。与其让模型自己反思不如训练一个小 reward 模型打分“本轮答案对没对”再让主模型循环到验算器满意为止。这就把循环思考从生成策略变成了搜索策略效果会更稳但工程复杂度也上一个台阶。7.3 给后来者的最后提醒如果你决定复现这个项目我的建议是先做“小号完整版”用 5000 条数据、0.5B 模型、跑通 SFT GRPO vLLM 全流程再放大到 1.5B 和 16 万条。不要一上来就追求完整数据量因为 GRPO 和 vLLM 之间的工程问题远比训练本身更容易劝退人。我实际踩过最重的坑是 vLLM 与 GRPO trainer 之间的端口没接通日志一直显示等待 rollout白白浪费了 30 分钟。先小规模跑通再扩量能省下无数时间。这个项目做完后我最大的体会是小模型不是不能“想”而是缺一个让它“想了之后能检查”的训练目标。循环思考本质上给了小模型一个在推理阶段自我迭代的接口。把这个接口训练稳定后面接什么任务都有意思。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑