不写出来的思考:把 Transformer 的层循环起来,是第三条 scaling 轴还是省错了地方?

想让模型"多想一会儿",标准做法是让它多生成文字------这就是思维链。 但这条路有个绕不开的收费口:每想一步,都要先把想法压成一个离散的、人能读的词, 而且一次只能输出一个。

还有另一个做法:楼不加高,让数据在同一段楼梯上多绕几圈。 绕三圈就相当于走了三倍的层数,参数一个没多加。2018 年就有这个想法了。

2025 到 2026 年,这条路线上突然出现了一批真东西:3.5B 参数训出来的模型 可以在推理时自由加圈换准确率;清华和字节 Seed 的论文在同时锁死算力、 参数、KV Cache 三本账 的条件下,仍然测出 6.8%--18.0% 的训练 FLOPs 节省

但它有一个没人愿意付的代价,而且这个代价和收益是同一件事的两面


一、两个默认出口,各自在机制上必然收费

先说清楚这条路到底想要什么。

现在的语言模型想做多步串行计算,默认只有两个出口:

markdown 复制代码
出口一:把楼盖高(加层)
        串行深度 = 层数,这是写死在结构里的上界
        收费:每加一层都要一份新参数
              → 深度和参数量被焊死
              → 你多存了一批东西,哪怕这批东西不是拿来存知识的

出口二:让它多生成文字(思维链)
        深度不受层数限制,写出来的每步都能当下一轮输入
        收费:每一步都要先压成离散的、人能读的词
              → 窄(一次一个 token)
              → 慢(串行解码)
              → 有损(模型内部用连续向量表示概念,翻译成词必然丢信息)

第二条收费值得展开。模型内部"想"一个概念,是一长串连续的数,可以同时压着好几种可能性。而输出的词只能挑一种,而且一旦输出就改不了。翻译这道关口既慢又有损。

第三条路就是:楼梯不造新的,让数据在同一段楼梯上多绕几圈。核心就是这三行:

python 复制代码
block = TransformerBlock()   # 楼梯只造一份
for _ in range(num_loops):   # 循环几次,临时定
    h = block(h)             # 每一次用的是同一份参数

后面十篇论文的分歧,全在这三行之外:循环次数由谁定、每一遍之间存什么、循环时人能不能看见。


二、机制:Huginn 的 P/R/C 与两个容易被忽略的设计

以 ELLIS Institute Tübingen 的 Jonas Geiping 等人的 Huginn(arXiv:2502.05171)为例。它把网络拆成三段:

scss 复制代码
        ┌─────────┐    ┌──────────────────┐    ┌─────────┐
输入 ──▶│ Prelude │───▶│      Core        │───▶│  Coda   │──▶ 输出概率
        │  (P)    │    │       (R)        │    │   (C)   │
        └─────────┘    └──────────────────┘    └─────────┘
          2 层             4 层,循环 r 次          2 层
                              ▲      │
                              └──────┘  同一份权重
                          每轮重新注入输入嵌入 e

具体配置:2 层 Prelude + 4 层共享 Core + 2 层 Coda = 8 层参数。Core 循环 32 次时:

ini 复制代码
执行深度 = 2 + 4×32 + 2 = 132 层
参数深度 = 8 层

执行了 132 层,只存了 8 层。 这就是"物理深度(physical depth)"和"有效深度(effective depth)"的分离。

两个反直觉的设计

第一,循环核心的隐藏状态不是从输入嵌入开始,而是从零(或高斯噪声)初始化。

第二,每一轮循环都重新注入原始输入嵌入 e

python 复制代码
e = model.encode(ids)          # [B, T, d],只算一次
h = torch.zeros_like(e)        # 隐藏状态初始化一次

for _ in range(loops):
    h = model.update_state(h, e)   # 每轮都把 e 重新拼进来

logits = model.lm_head(model.coda(h))

为什么不直接把 e 当初始状态一直循环?因为那样模型会过度依赖第一次进入循环时的状态,循环次数一多,微小偏差被不断放大,隐藏状态可能逐渐丢失原始输入信息。

每轮重新注入 e,等于给迭代过程加了一个固定锚点------无论内部状态怎么漂,模型始终知道自己正在处理什么问题。

随机初始化则是在逼模型学"路径无关性(path independence)" :从不同起点出发,仍然能把状态推向相近的有效表示。它要学的不是一条只能从固定起点执行的计算链,而是一种能反复修正、逐渐趋于稳定的更新规则

训练:随机采深度 + 截断 BPTT

python 复制代码
loops = random.choice((2, 4, 8, 16))     # 每个样本随机采一个深度
logits = model(inputs, loops=loops)
loss = F.cross_entropy(logits.view(-1, V), targets.view(-1))

但反向传播很贵。Core 权重虽然共享,每调用一次都会在计算图里产生新的中间激活。完整保留 R 轮的图,反向要穿过整条状态链。折中办法:

python 复制代码
with torch.no_grad():                    # 前 R-k 轮:只前向
    for _ in range(R - k):
        h = model.update_state(h, e)
for _ in range(k):                       # 最后 k 轮:保留梯度
    h = model.update_state(h, e)

Huginn 的做法是按对数正态-泊松分布采样循环次数(平均 32),反向传播只对最后 8 步做。


三、先说代价:省下的算力和丢掉的可读性,是同一件事的两面

这一节必须放在收益之前讲。

循环之所以省 token,正是因为它不输出 token。那几遍多出来的计算既然不经过"生成一个词"这道关口,就没有任何地方会把它变成人能读的东西。

Coconut(Meta FAIR, 2024.12)是最早尝试"连续思维"的工作之一,而它自己的消融实验给出了一个很不体面的结果:

变体 ProsQA 成绩
完整版(6 步连续思维) 97.0% → 99.8%
把潜向量换成一串无意义的停顿符占位 96.6% → 100.0%
只保留分阶段训练法,拆掉潜向量 95.5% → 99.9%

三组数字落在同一个区间里。 功劳大概率在那套分阶段训练课程上,而不在"用连续向量思考"这个卖点上。

更直接的证据来自 Huginn 本身:外部探针在它的中间状态里找不到可读的推理步骤。

这条线在 2026 年 9 月引发了不小的争吵,值得记一笔:

  • The Information(9/1)报道 OpenAI 的 Astra 用了"受限形式"的循环深度,落点是"思考被藏起来了"
  • Jakub Pachocki(OpenAI 首席科学家)罕见直接回应,说包括 Astra 在内的前沿模型,计算图深度"在 GPT-4 的两倍以内";同时承认 CoT 监控"是脆弱的,而且不幸正在朝坏的方向走"
  • Sebastian Raschka 泼冷水:复用层本身并不会压制可见的思维链,它只是在输出下一个 token 之前多算几轮。更短的 trace 更可能反映"模型犯错更少、回溯更少"

我不打算判断 Astra 用了什么------它只有匿名信源,OpenAI 的发布材料里从未出现过"循环深度"字样。但这个争论点出了一个真实的结构性张力:

如果监控工具把"trace 长度"当作"思考深度"的代理指标,那么任何让模型少写字的技术都会打破这个代理------不管它是不是循环架构。


四、收益侧的证据:三条独立的支持

4.1 推理靠深度,记忆靠参数(可以分开买)

Google Research 的 Reasoning with Latent Thoughts(2025.02)用等参数、等深度的对照实验把缩放律拆成了两半:

结果
推理侧 1 层循环 12 遍,把多位数加法从 0.1% 拉到 99.9%,参数一个没加
4 层循环 6 遍在四项推理原语上拿 56.9,超过参数多 6 倍的 24 层基线(47.5)
记忆侧 参数只有基线 1/6,困惑度 8.79 vs 7.40 ;闭卷问答 6.7 vs 11.2

代价很干净:你在买推理深度的同时,是在卖记忆容量。 这正好解释了为什么后来的工作越来越喜欢把循环和 MoE 放在一起------前者增加处理次数,后者补充参数容量。

4.2 Huginn:推理时加圈,准确率真的会涨

同一个 checkpoint、同样的 800B token 训练,只在测试时改循环次数 r

基准 r=1 r=8 r=16 r=32
ARC-Easy 34.89 65.11 69.49 69.91
ARC-Challenge 24.06 35.15 37.71 38.23
HellaSwag 29.34 58.54 64.67 65.21
MMLU 23.60 25.29 31.25 31.38
OpenBookQA 26.80 35.40 37.60 38.80
PiQA 55.33 73.45 75.79 76.22
SciQ 47.10 92.10 93.90 93.50 ← 掉了
GSM8K (CoT) 0.00 / 0.15 --- --- 34.80 / 42.08

三个读法:

  1. 从 r=1 到 r=8 是断崖式提升 ,之后快速饱和。ARC-C 上 0-shot 大约在 8--12 次饱和,1-shot 到 20 次,25--50 few-shot 时才需要 32 次。问题越依赖上下文,越吃得住更多潜空间算力。
  2. SciQ 在 r=32 反而降了(93.90 → 93.50)。加圈不是单调的。
  3. GSM8K 从 0.00/0.15 到 34.80/42.08 ------不带 CoT 时几乎做不了,带上 CoT 才起来。潜空间循环不替代 CoT,两者是叠加关系。

最后这点很关键,后面还要用。

4.3 一个被遗忘的对照实验

Universal Transformer(2018)早就做过一件现在很多人忘了的事:让每个位置自己决定跑几遍

结果是:自适应版本平均跑 8.2 遍 ,困惑度 142 ;而把遍数固定成 8 遍是 202,固定 9 遍是 239

收益来自"每个位置自己决定跑几遍",不来自单纯多跑几遍。

这个前提后来被大多数工作丢掉了。七年后有人回头捡起来(KAIST 和 Google 的 token 级自适应版本),撞上了另一堵墙。


五、公平比较:SMELT 的三本账

到这里为止,所有比较都有一个控制问题:循环模型多执行了几遍,它拿到的预算本来就更多。 分数高不代表架构好。

SMELT(arXiv:2609.01343,2026-09-01,清华 + 字节 Seed)就是把这本账算清楚的工作。它同时锁死三个预算:

预算 对应什么 怎么匹配
每 token FLOPs 训练/推理成本 缩窄 hidden width
非嵌入总参数量 知识容量 增加 MoE 专家数补回来
KV Cache 可服务的最大上下文 调整 GQA / head dim

MoE 是关键------它把"总参数量"和"每 token 计算量"解耦了,才让三本账同时匹配成为可能。

5.1 配方很克制:只让中间一半跑第二遍

在 200M active、12 物理层、约 85% 稀疏下的 ablation:

循环跨度 有效层数 Val. loss DCLM Core
0%(基线) 12 1.9445 24.92 ± 0.13
17% 14 1.9384 26.20 ± 0.14
33% 16 1.9275 25.13 ± 0.16
50% 18 1.9257 27.57 ± 0.13
67% 20 1.9374 25.84 ± 0.10
83% 22 1.9413 24.45 ± 0.17
100%(全循环) 24 1.9322 25.31 ± 0.17

50% 是最优,全循环反而更差。 作者的解释:首部靠近输入编码、尾部靠近输出预测,这两段承担专门化职责,全部共享会损失专门化能力。这正好支持 P--R--C 那种"前奏--循环中段--尾声"的布局。

5.2 两次最优,三次四次退化

配置 有效深度 Val. loss DCLM Core
基线(1×) 12 1.9445 24.92 ± 0.13
循环 2× 18 1.9257 27.57 ± 0.13
循环 3× 24 1.9385 27.15 ± 0.20
循环 4× 30 1.9360 27.30 ± 0.19

原因不是"循环多了不好",而是为了维持 FLOPs 匹配,循环越多模型就得越窄。窄到一定程度,多出来的执行深度抵不过宽度损失。

这个结论和三个月前 LoopCoder-v2(7B dense)独立收敛到同一个数------两篇论文、两个半月、不同架构族、不同规模,都指向"两次"------其中一篇现在还给了解释。这比任何单独一篇都强。

5.3 规模化结果与机制

四个激活规模(100M / 200M / 600M / 1.6B)× 四个稀疏度,最大到 54B 非嵌入参数 ,共 16 对匹配配置。给每个架构单独拟合 Chinchilla 式 scaling law:L = E + A/(F^a · S^b) + K/D^c

指数 Baseline SMELT
算力指数 a 0.3703 0.3892
数据指数 c 0.6594 0.7011

两个指数都更大 = loss 随算力和数据下降得更快。在计算最优前沿上,节省 6.8%--18.0% 训练 FLOPs,且节省比例随预算增大而增大

下游:DCLM 96 个配置中 83 个胜出,MMLU 30 个 above-chance 配置中 29 个胜出 ,而且收益超过 val loss 预测的部分(同等 loss 下 SMELT 得分更高,gap 随规模变大)。

领域分布:Code > Finance, Math/STEM > Web 。长上下文样本的收益显著大于短上下文------而这个模式在单纯加专家或加参数时观察不到,说明它是循环独有的。

机制上,作者给了两个可观测的东西:

  1. 第二次访问会削弱 attention sink (注意力质量堆在少数无信息量的首/分隔 token 上的现象),把质量重定向到内容相关 token。这解释了为什么收益随上下文变长而变大------要区分的东西越多,sink 越伤。
  2. 专家路由 :第二次经过同一个物理 MoE 层时,router 没有照搬第一次的选择。约 97% 稀疏度下,top-8 专家通常只重合 2--3 个

5.4 Loopie 的警告:优势可能来得很晚

另一篇按实际训练时间(而非理论 FLOPs)匹配的工作是 Loopie(2026.07)。它的结果里有一条对所有团队都重要:

Loopie-20B-A2B 与同等硬件、同等 token 预算的 Qwen3 风格 30B-A3B MoE 相比,训练早期落后,约到 600B token 才反超

如果实验停在 300B token,结论可能正好相反。

对没有这个训练预算的团队,这条路线未必合算。


六、五个工程死结

6.1 循环不减少串行计算,p99 可能更难看

Prime Intellect 的 Elie Bakouch 的质疑最直接:vanilla recurrent depth 不会自动缩短训练或推理时间。 信息仍要走完全部有效深度,而且循环带有串行依赖,单请求 p99 甚至可能更难看。

这不是说循环没用,而是说收益的来源不是"算得更少",而是"存得更少" 。权重和 optimizer state 变少后,显存和通信开销下降,省出的预算可以拿去放大 microbatch、加专家或加宽度。这是系统层的收益,不是算法层的。

6.2 KV Cache 不会因为你"没多生成 token"就不涨

一个很容易踩的坑。循环的 KV 缓存需要区分维度:

css 复制代码
cache[层号][循环轮次][token 位置]

循环没有增加序列长度 T,但增加了"执行层数"。所以"不生成更多 token"推不出"KV Cache 不增长"。标准循环 Transformer 在长上下文下,KV 膨胀是直接 OOM 的级别。

(这也是为什么 SMELT 要把 KV Cache 单列为一本账------不算这本账的循环模型,在服务时就不是 drop-in 替代。)

6.3 Coconut 的消融至今没有被正面回应

重复一遍那个表:把潜向量换成无意义停顿符,成绩不变。

这个结果说明"在潜空间里思考"这件事,在某些任务上的贡献可能主要是训练课程带来的,而不是连续表示带来的。SMELT 的 attention-sink 机制分析是目前最好的正面回应(它给出了一个可测量的、循环独有的效应),但它解释的是"第二次访问改变了注意力分配",不是"潜向量承载了比文字更丰富的推理"。

6.4 SMELT 是 arXiv v1,且架构搜索只在 200M 完成

必须说清楚:

  • 没有独立复现
  • 关键架构搜索(循环跨度、深度宽度比、循环次数)只在约 200M active 参数完成,然后外推到 1.6B
  • 匹配的是算术预算,不是真实 wall-clock
  • 残差不匹配虽然小但不为零:FLOPs 最大 3.9%、参数 1.0%、KV Cache 3.6%(作者用实测 FLOPs 进入拟合,方法上比"贴个 matched 标签就当相等"更严谨,但残差仍在)
  • span 的选择基于 val loss,而下游指标排序不一致:约 95% 稀疏下,DCLM Core 偏好 67% 跨度,val loss 偏好 50%

不能直接把 6.8%--18.0% 外推到商业模型。

6.5 循环不替代 CoT

Huginn 的 GSM8K 数字已经说明了:不带 CoT 时 0.00/0.15,带上才到 34.80/42.08。

Nous Research 的 Shannon Sands 说得更直白:循环模型的计算路径接近把普通模型加深 2--3 倍,recurrent depth 不会取代 CoT。

它们的分工应该是:循环提供"每步更深的加工",CoT 提供"更多步 + 可审计的中间态"。 想用循环把 CoT 全省掉,既做不到,也不该做。


七、决策树与可执行判断

markdown 复制代码
你的瓶颈到底是哪一个?
        │
        ├── 显存 / 参数通信 / optimizer state
        │      → 循环有真实收益(Loopie 的路径)
        │      → 但确认你有 >600B token 的训练预算
        │
        ├── 算术 FLOPs
        │      → 循环不省算术量。省不了多少。
        │
        ├── 单请求延迟 / p99
        │      → 循环是负收益(串行依赖变长)
        │
        ├── 长上下文的 KV Cache
        │      → 循环会加重问题,除非你做跨循环 KV 共享
        │
        └── 可审计性
               → 循环明确是负的。潜空间不可读是机制上的必然。

如果要做,配方是这四条

  1. 只循环中间约 50% 的层,跑两遍。 三遍四遍会因为 FLOPs 匹配迫使模型变窄而退化。全循环也不如半循环。
  2. 首部和尾部保留独立参数。 它们承担输入编码和输出预测的专门化职责。
  3. 循环模型偏好更大的执行深度/宽度比。 额外执行深度不增加参数,共享参数同时收到浅位和深位的梯度信号,训练反而更稳。
  4. 每轮重新注入输入嵌入,隐藏状态从零/噪声初始化。 这是路径无关性的来源。

系统层必做的三件事

  • 跨循环深度共享 KV Cache------所有循环轮次共享权重,这是天然的优化点
  • 投机解码 :低 r 起草、高 r 验证,草稿状态可复用(r=4 的 s0→s3 可以直接续到 r=12
  • per-token 早退:简单 token 的潜状态收敛得更快,不需要跑满

八、我的判断

第一,这条线真正的价值可能不在"省算力",而在"把深度和参数解耦"。

过去加深度必然加参数,是因为深度和参数被焊在同一个旋钮上。循环把这根焊点拆开了------于是你可以分别回答"这个任务需要多少连续计算步"和"这个任务需要存多少知识"。SMELT 最重要的结果不是 6.8%--18.0%,而是证明了这本账拆开之后仍然有收益

第二,"两次"这个数字比任何单项收益都更值得记住。

两篇论文、两个半月、一个 dense 一个 MoE、规模差一个数量级,独立收敛到同一个循环次数,而且后一篇还给了解释(第三次访问时,为匹配 FLOPs 而被迫变窄的宽度损失超过了深度收益)。这种交叉验证在架构研究里很罕见。

第三,最被低估的风险是可审计性,而它恰好和收益同源。

省下的算力和丢掉的可读性是同一件事的两面------这不是工程没做好,是机制上的必然。Coconut 的消融、Huginn 的探针失败、Astra 的 trace 缩短,指向的是同一件事。

如果你的系统里有任何一层依赖"读模型的推理过程"来做安全检查或合规审计,那么"让模型少写字"的每一项优化都在削弱这一层------不管它叫循环深度、潜空间推理还是别的什么。 这类系统应该把 trace 长度当作一个要主动监控的指标,而不是一个顺带变小的数字。

第四,对大多数团队,现在还不是上车的时候。

Loopie 的 600B token 才反超是个硬警告。SMELT 是 v1、无复现、架构搜索在 200M 完成。而收益的实现路径(更大的 microbatch、更少的通信)只对自己从头预训练大模型的团队有意义。

如果你是在做微调、做应用、做 agent------这条线目前对你没有任何可执行的动作。 值得做的是记住那个决策树,等开源生态出现可调 loop count 的成熟权重(字节的 Ouro 已经开了个头),再实测。

下一个值得看的信号 :有没有第二个团队在 SMELT 的三本账口径下复现 6.8%--18.0%,尤其是在真实 wall-clock 而不只是算术 FLOPs 上。如果复现成功且 wall-clock 也对得上,这条线会从"有意思的架构研究"变成"预训练的默认选项之一";如果对不上,那它就只是把成本从显存挪回了时间。


参考

  • Huginn / Recurrent Depth :Jonas Geiping et al.(ELLIS Institute Tübingen),Scaling up Test-Time Compute with Latent Reasoning: A Recurrent Depth Approach,arXiv:2502.05171。3.5B 参数、约 7950 亿 token、Frontier 超算 4096 张 MI250X。本文第二节机制与第四节实验表出自此文。
  • SMELT :Jian Li, Jiaheng Liu, Ge Zhang 等(清华 + 字节 Seed),SMELT: Scaling Laws for Compute-Matched MoE Looped Transformers,arXiv:2609.01343,2026-09-01 提交。三本账、配方 ablation、scaling law 指数、attention sink 与专家路由分析均出自此文。
  • Coconut:Shibo Hao et al.(Meta FAIR),2024-12。潜向量消融实验出自此文。
  • Reasoning with Latent Thoughts:Nikunj Saunshi et al.(Google Research),2025-02。深度/参数分离实验出自此文。
  • Universal Transformer:Mostafa Dehghani et al.(Google Brain),arXiv:1807.03819,2018。自适应 vs 固定遍数的对照出自此文。
  • LoopieLoop the Loopies!,2026-07。600B token 反超的观察出自此文。
  • Parcae:循环训练稳定性,谱范数约束注入参数。
  • 争议与评论 :Jakub Pachocki(OpenAI)关于计算图深度与 CoT 监控的公开回应;Sebastian Raschka,Ahead of AI,2026-09-09;Elie Bakouch(Prime Intellect)关于串行计算与 p99 的质疑;Shannon Sands(Nous Research)。ARC Prize Foundation 对某个病毒式传播结果的独立复现发现其"起效的原因与宣传的不同"。

数据来源与免责说明

  1. Huginn 的测试时 scaling 表(r=1/8/16/32)引自该论文公开表格;SciQ 在 r=32 的回落(93.90→93.50)为原表数据,非本文推断。
  2. SMELT 的 6.8%--18.0% 为计算最优前沿上、按各自拟合的 scaling law 曲面在相同算力与稀疏度坐标下比较 得出的结果;论文明确匹配的是算术预算而非真实 wall-clock,架构搜索在约 200M active 完成。该论文为 arXiv v1,无独立复现,不应直接外推至商业模型。
  3. Coconut 的消融数据(潜向量换成停顿符后成绩不变)为该论文自报,本文如实引用;该结果至今未见正面回应。
  4. 关于 OpenAI Astra 是否采用循环深度:仅有 The Information 的匿名信源报道,OpenAI 从未在发布材料中使用"循环深度"字样,Pachocki 的回应也未确认架构。本文不对此下判断,只记录三方说法。
  5. 文中"省下的算力和丢掉的可读性是同一件事的两面"是本文基于上述证据的归纳,非任何单一论文的原文表述。
  6. 本文未对任何方法做独立实验验证。
相关推荐
A.说学逗唱的Coke1 小时前
【大模型专题】别再用 HTTP 直连 Agent 了:用 Kafka 承载 A2A 协议,从 PoC 走到生产
人工智能·kafka·a2a
资讯综合1 小时前
2026企业AI平台选型指南:多维视角下的主流方案解析
人工智能
资讯综合1 小时前
向日葵 vs ToDesk 个人远控横评:PC/手机/弱网/远程开机实测(26年9月更新)
人工智能
布吉岛的石头1 小时前
Java 程序员第 48 阶段15:Transformer 架构总览与自注意力直觉,注意力权重可视化:用 Java 打印注意力矩阵理解模型在看什么
人工智能·深度学习·transformer
正经教主1 小时前
【FDE系列】阶段1Day 11:Prompt — 给模型立规矩
人工智能·fde
海上小飞龙2 小时前
LangChain 模型调用:invoke、stream、batch 到底该怎么选?
人工智能·语言模型·自然语言处理
美狐美颜sdk2 小时前
直播APP开发技术栈详解:视频美颜SDK、人脸识别与实时渲染
android·人工智能·音视频·美颜sdk·直播美颜sdk
jason_renyu2 小时前
Windows 环境下 Python 方式安装 Milvus 向量库与 Attu 避坑指南
人工智能·milvus·windows安装milvus·windows安转向量库