news 2026/11/2 15:25:48

商汤SenseNova Loop-9B拆解:32层权重跑56层计算,循环Transformer加mHC双流残差的工程账

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
商汤SenseNova Loop-9B拆解:32层权重跑56层计算,循环Transformer加mHC双流残差的工程账

一、10 月 30 日商汤开源了什么

10 月 30 日,商汤在 Hugging Face 与 GitHub 同步放出 SenseNova-Loop-9B-Preview,权重以 Apache 2.0 协议开放。

官方定位很明确:面向长上下文与 Agent 工作流的多模态模型,文本和图像输入,文本输出。

核心数字只有三个:总参数约 9B、推理等效约 14.6B、每次前向传播执行 56 次解码器层计算。

56 次层计算从 32 组独立权重里跑出来,靠的就是 Looped Transformer:中间 24 层共享权重,依次执行两遍。

商汤的官方口径是在 9B 参数规模下媲美 27B 参数模型,并在 WorkBuddy Bench、RaccoonPPT Bench、AutomationBench 三个榜单上超过 Qwen3.5-27B。

训练侧的信息也一并公开:文本、图像、代码和工具调用轨迹的混合监督,最长训练序列262,144 个 token。

模型已经接入商汤小浣熊客户端做真实任务验证,说明这不是停留在论文里的架构玩具。

开源模型这两年的主航道是参数规模和上下文长度,Loop-9B 把第三条路计算深度第一次做到了可下载、可部署、可商用的尺寸。

官方新闻稿反复强调一句话:从参数规模到计算深度。这句话值一篇完整拆解,因为它直接改写端侧和私有化部署的成本结构。

时点信号也值得注意:权重以 Apache 2.0 协议直接开放,商用零门槛,社区的框架适配和第三方复现评测会很快跟上。

需要提醒的是评测口径:WorkBuddy Bench、RaccoonPPT Bench、AutomationBench 这三个榜单偏办公和 Agent 执行场景,数据来自官方自测,独立第三方的复现结论还没有出现。

这不妨碍拆解架构本身,因为 32 层权重 56 次执行、双流 mHC、混合注意力这三件事都写在公开的模型卡和配置文件里,是可以独立验证的硬事实。

这篇文章不重复新闻稿,只拆三个工程问题:56 次执行怎么排布、mHC 双流残差在防什么、显存账到底省在哪。

二、循环解码器:B(B(h)) 的 56 次层计算

Loop-9B 的解码器一共 32 个独立层,输入先经过第 0 到 3 层,随后第 4 到 27 层这个连续块整体执行两遍,最后由第 28 到 31 层和输出头收尾。

用 B 表示循环块、h 表示输入,循环计算就是 B(B(h)):第二遍以第一遍的输出为输入,复用同一组权重。

层数账很直白:4 加 24 乘 2 再加 4,等于56。

参数账值得细算。词表大小248,320,隐藏维度4096,仅词嵌入一项就占约 1.02B。

每层按隐藏维度 4096、前馈维度 12288 估算,SwiGLU 三个矩阵加注意力投影合计约 2.3 亿参数。

32 层权重合计约 7.5B,加上嵌入层后落在官方给出的约 9B 口径之内。

换成执行口径再算一遍:首尾 8 层各算一次约 1.9B,中间 24 层算两遍约 11.3B,再加嵌入,正好逼近官方给出的14.6B 推理等效参数。

这组数字说明循环计算买到的不是免费午餐:存储按 9B 付钱,计算量按 14.6B 付,换的是不加显存负担的额外深度。

延迟侧也要诚实:56 次层执行意味着同样权重下推理耗时比 32 层稠密模型高约 75%,这是用时间换空间的明确交易。

FLOPs 账再往细算一层:忽略注意力项,单 token 前向的主力开销约等于每层参数量乘两次运算,56 次层执行合计约 258 亿 FLOPs 每 token,而 32 层稠密版本只要约 147 亿。

但拿它和 27B 稠密模型对比时方向会反过来:27B 每 token 约 540 亿 FLOPs,Loop-9B 的计算量仍然只有对方一半左右,存储和计算两头都占便宜。

把三笔账并排放:权重存储约为同能力稠密模型的三分之一,每 token 计算量约为二分之一,代价仅仅是比同参数量的 9B 稠密模型慢约 75%。

有效深度的价值集中体现在多步推理链上:循环块的第二遍相当于让模型对同一份中间表示再做一轮抽象加工,这是官方在复杂任务榜单上追平 27B 的主要结构来源。

循环块选在第 4 到 27 层也不是随意决定:靠近输入的前 4 层和靠近输出头的后 4 层保持独立权重,词表嵌入到语义空间的映射和解码输出前的表示精修都不被循环复用。

这种首尾独立、中间共享的切法同时照顾了两头:底层的特征提取需要专用权重,高层的输出校准也需要专用权重,中间的概念加工才是可以反复复用的部分。

从训练成本角度看,共享权重还有一个隐性收益:循环块只存储一份参数,优化器状态和梯度副本也同步减少,微调时的显存峰值比同执行深度的稠密模型低得多。

三、混合注意力:循环边界为什么必须对齐四层组

Loop-9B 的注意力不是单一形态,而是每四层为一个循环单元:三层 Gated-DeltaNet 线性注意力加一层全注意力。

全注意力层采用16 个查询头配 4 个 KV 头的 GQA 结构,带 QK 归一化和注意力输出门。

线性注意力层维护的是常数大小的循环状态,计算量随序列长度线性增长,不需要存历史 KV。

位置编码使用交错式 M-RoPE,旋转比例因子 0.25,只有四分之一的维度参与旋转,其余维度保持原样。

关键设计在边界:循环块第 4 到 27 层恰好是6 个完整的四层组,两遍执行看到完全相同的注意力排列。

如果边界切在某个四层组中间,第二遍的层序就会和第一遍错位,共享权重在两遍里承担不同角色,训练好的表示直接被拆散。

按 56 次执行摊开算:全注意力一共出现14 次,首尾各 2 层加循环两遍各 6 层;线性注意力出现42 次。

这个 3 比 1 的比例决定了长上下文的成本结构,也是下一节显存账的前提条件。

Gated-DeltaNet 值得单独展开:它是线性注意力的一个变体,用门控加 delta 规则维护并更新一个常数大小的循环状态,状态维度与序列长度完全无关。

这正是 42 层线性注意力不需要存历史 KV 的原因:无论上下文是 4K 还是 256K,这些层的显存占用都是同一个常数。

M-RoPE 的 0.25 也有明确含义:每个注意力头 256 维里只有 64 维参与旋转并携带位置信息,其余 192 维专注于内容匹配,长上下文外推时位置干扰更小。

三比一的分工可以这样理解:全注意力负责精确检索长距离依赖,线性注意力负责以常数成本压缩背景信息,比例本身就是召回能力和推理成本之间的折中点。

GQA 的 4 比 1 头数配比同样服务于推理成本:4 个 KV 头相对 16 个查询头把 KV 写入量直接压到四分之一,和线性注意力层的常数状态形成同一套降本逻辑。

这些选择叠加起来的风格非常一致:凡是随序列长度增长的量全部压缩,凡是常数项才允许保留,长上下文场景的显存曲线因此变得可线性外推。

反过来讲,这个设计的软肋也清晰:线性注意力对需要逐字精确回溯的任务天然吃亏,所以 14 次全注意力层的召回质量决定了整个模型在长上下文检索任务上的上限。

四、显存账:权重省了,KV cache 怎么算

权重侧省得很直接:9B 参数按 BF16 存储约18GB,同档 27B 稠密模型要 54GB,单卡部署门槛差了整整一档。

KV cache 一侧容易被误读。GGUF 移植方的说明写得很清楚:循环块的两次执行各自保留自己的注意力 KV 和 GDN 循环状态。

也就是说 KV cache 按56 次执行记账,不按 32 层记账,权重共享在这里不带来任何折扣。

真正的节省藏在注意力结构里:56 次执行中只有 14 次是全注意力,其余 42 次线性注意力只维护常数状态,不随序列变长。

按演示口径估算,假设值可替换:每个 token 在单层全注意力的 KV 占用是 4 个 KV 头乘 256 维头维度,再乘 K 和 V 两份,乘 BF16 两个字节,约 4KB。

14 层全注意力合计约56KB 每 token,128K 上下文约 7.2GB,256K 满载约 14.4GB。

对照一个假设的 27B 稠密配置,64 层全注意力、8 个 KV 头、128 维头维度:每 token 每层同样约 4KB,64 层约 256KB,128K 上下文就要约 32.8GB。

口径Loop-9B-Preview假设的 27B 稠密模型
权重显存(BF16)约 18GB约 54GB
128K 上下文 KV cache约 7.2GB约 32.8GB
层执行次数56(32 组权重)64(64 组权重)
全注意力层数14(其余为线性注意力)64
存储参数 / 推理等效9B / 约 14.6B27B / 27B

结论很工程化:Loop-9B 用 1.75 倍的层执行换 3 倍的权重显存压缩,KV cache 再靠混合注意力砍掉四分之三以上。

对 24GB 到 48GB 显存的单卡场景,这个组合意味着 128K 长上下文第一次变得可以在本地认真考虑。

Agent 会话的 KV 增长曲线更能说明问题:多轮工具调用场景里 KV 随轮数线性累积,7.2GB 每 128K 的口径意味着 24GB 显卡在装下 18GB 权重之后,仍然有完整长会话的回旋空间。

预填充带宽账也连带受益:KV 总量小四分之三意味着写入带宽同比节省,长文档预填充阶段的显存峰值压力同步下降。

边界同样要说清楚:42 层线性注意力的循环状态本身也占显存,且随 batch 大小和头数增长,但它是常数项,不随上下文长度变化,256K 满载时总量完全可预测。

还有一个部署层面的推论:权重 18GB 加 128K 会话 KV 约 7.2GB,合计 25GB 出头,一张 32GB 级显卡就能同时装下权重和满配长上下文。

放到 Agent 工作流里看更具体:每一轮工具调用都会把工具返回追加进上下文,几十轮下来上下文轻松突破 64K,KV 曲线的斜率直接决定单会话的显存寿命。

对服务端批量部署同样有意义:单卡能容纳的并发会话数由权重加 KV 总量决定,KV 砍四分之三意味着同卡并发数接近翻倍,这部分收益和权重压缩相互独立。

五、mHC 双流残差:Sinkhorn 在防什么

循环计算的老问题是信息混叠:同一组权重跑两遍,如果第二遍的输入只经过一条残差通路,梯度和特征很容易在两遍之间互相污染。

Loop-9B 的解法是 mHC,流形约束超连接。循环块内部始终维护两条残差流,每一层通过可学习映射决定怎么从双流里读出、怎么把层输出写回双流。

约束体现在混合矩阵上:流间混合映射不做自由加权,而是经过Sinkhorn 归一化 20 次迭代,逼近一个双随机矩阵。

双随机矩阵的行和与列和都等于 1,意味着信息在两条流之间重新分配时总量守恒,不会出现某条流被无限放大或者逐渐饿死。

这本质上是把最优传输里的熵正则思想搬进了残差通路,比朴素的多路加权求和稳定得多。

数值精度上也能看出刻意保护:主干用 BF16 存储,mHC 路由参数全程用FP32,混合矩阵的计算不吃半精度舍入误差。

消融实验给了直接证据:引入 mHC 之后GPQA Diamond 提高 4.42 分,TeamBench 提高 13.34 分,后者正是多步骤 Agent 任务的榜单。

13.34 分的提升幅度说明,双流残差对长链路任务的帮助远大于对单轮推理题的帮助,这和官方主打的 Agent 工作流定位完全自洽。

谱系上看 mHC 并不是凭空出现:残差连接从 ResNet 时代的单流开始,Hyper-Connections 把残差拓宽成多条流并允许跨流自由混合,mHC 则是在循环场景下给混合矩阵加上了流形约束。

为什么恰好是两条流而不是四条:流数翻倍意味着混合矩阵和读写映射的参数量同步翻倍,Sinkhorn 迭代成本也上涨,两条流是 9B 规模下收益和开销之间的折中。

双随机约束还有一个等价解释:任何一条流的更新都是两条流的凸组合再叠加本层输出,信息流自带归一化性质,深层堆叠时激活尺度更可控。

训练稳定性的收益也直接相关:两遍执行共享权重时梯度会跨时间步累加,混合矩阵被约束在双随机流形上,梯度路径不会在两条流之间越走越偏。

Sinkhorn 迭代自身的成本几乎可以忽略:2 乘 2 的混合矩阵做 20 次行列归一化,相对每层千万级的矩阵乘法只是零头,约束的性价比非常高。

官方也提到后续会探索 DSA 稀疏注意力和自适应迭代次数,说明双流加循环只是一个可扩展框架的第一组配置,不是终点形态。

六、最小复现:PyTorch 验证 32 层权重 56 次执行

下面这段代码不加载任何权重,只用标准 PyTorch 复现 Loop-9B 的三个结构声明:32 组权重、56 次层执行、双流 mHC 加 Sinkhorn 混合。

代码直接可运行,末尾的断言会逐一核对这三个数字,并验证 Sinkhorn 迭代确实收敛到双随机矩阵。

import torch import torch.nn as nn CFG = dict( n_layers=32, # 独立解码器层数 loop_start=4, # 循环块起点(含) loop_end=28, # 循环块终点(不含) loop_passes=2, # 循环块执行遍数 d_model=256, n_heads=4, d_ff=768, vocab=1000, mhc_streams=2, sinkhorn_iters=20, ) def sinkhorn(logits, iters): # 行列交替归一化,逼近双随机矩阵 m = logits for _ in range(iters): m = m - torch.logsumexp(m, dim=-1, keepdim=True) m = m - torch.logsumexp(m, dim=-2, keepdim=True) return torch.softmax(m, dim=-1) class Block(nn.Module): def __init__(self, d, h, f): super().__init__() self.attn = nn.MultiheadAttention(d, h, batch_first=True) self.ffn = nn.Sequential(nn.Linear(d, f), nn.SiLU(), nn.Linear(f, d)) self.n1 = nn.LayerNorm(d) self.n2 = nn.LayerNorm(d) def forward(self, x): n = self.n1(x) a, _ = self.attn(n, n, n) x = x + a return x + self.ffn(self.n2(x)) class MHC(nn.Module): # 双流流形约束超连接:读双流、跑层、按双随机矩阵写回 def __init__(self, d, streams, iters): super().__init__() self.read = nn.Linear(d * streams, streams) self.mix_logits = nn.Parameter(torch.randn(streams, streams) * 0.5) self.write = nn.Linear(d, streams) self.iters = iters def forward(self, streams, layer_fn): cat = torch.cat(streams, dim=-1) rw = torch.softmax(self.read(cat), dim=-1) x = sum(rw[..., s:s + 1] * streams[s] for s in range(len(streams))) out = layer_fn(x) mix = sinkhorn(self.mix_logits, self.iters) ww = torch.softmax(self.write(out), dim=-1) return [sum(mix[s, t] * streams[t] for t in range(len(streams))) + ww[..., s:s + 1] * out for s in range(len(streams))] class LoopDecoder(nn.Module): def __init__(self, cfg): super().__init__() self.cfg = cfg self.emb = nn.Embedding(cfg["vocab"], cfg["d_model"]) self.layers = nn.ModuleList( Block(cfg["d_model"], cfg["n_heads"], cfg["d_ff"]) for _ in range(cfg["n_layers"])) self.mhc = MHC(cfg["d_model"], cfg["mhc_streams"], cfg["sinkhorn_iters"]) self.head = nn.Linear(cfg["d_model"], cfg["vocab"], bias=False) self.exec_count = 0 def forward(self, idx): c = self.cfg h = self.emb(idx) for i in range(c["loop_start"]): # 头部 0-3 层 h = self.layers[i](h); self.exec_count += 1 streams = [h, h.clone()] # 开两条残差流 for _ in range(c["loop_passes"]): # [4,28) 跑两遍 for i in range(c["loop_start"], c["loop_end"]): streams = self.mhc(streams, self.layers[i]) self.exec_count += 1 h = sum(streams) / len(streams) for i in range(c["loop_end"], c["n_layers"]): # 尾部 28-31 层 h = self.layers[i](h); self.exec_count += 1 return self.head(h) torch.manual_seed(0) model = LoopDecoder(CFG) logits = model(torch.randint(0, CFG["vocab"], (1, 16))) stored_layers = len(model.layers) assert stored_layers == 32, f"存储层数应为 32,实际 {stored_layers}" assert model.exec_count == 4 + 24 * 2 + 4 == 56 mix = sinkhorn(model.mhc.mix_logits, CFG["sinkhorn_iters"]) assert torch.allclose(mix.sum(-1), torch.ones(2), atol=1e-2) assert torch.allclose(mix.sum(-2), torch.ones(2), atol=1e-2) print(f"存储权重层数: {stored_layers}") print(f"实际层执行次数: {model.exec_count}") print(f"输出 logits 形状: {tuple(logits.shape)}") print(f"Sinkhorn 混合矩阵行和: {mix.sum(-1).tolist()}") print(f"Sinkhorn 混合矩阵列和: {mix.sum(-2).tolist()}")

跑完之后四个断言全部通过:存储层数 32、执行次数 56、混合矩阵行和与列和都收敛到 1 附近。

这个复现还暴露了一个实现细节:循环块的两次执行共享 ModuleList 里的同一批模块,权重从头到尾只有一份,翻倍的只是计算图。

把 d_model 换成 4096、d_ff 换成 12288、词表换成 248,320,参数统计就会落到 9B 量级,和官方规格表完全对上。

训练侧有一个注意点:共享权重的模块在反向传播时会收到两遍执行累加的梯度,优化器实际看到的是两个时间步梯度之和,学习率口径和稠密模型不能直接对齐。

推理内核侧的接口形状也要先对齐:进入循环块时把隐藏状态复制成两条残差流,离开循环块时再取平均合并,自定义算子必须完整复现这个分叉再汇合的结构。

如果要把这个复现改成生产实现,还需要补上三件事:混合注意力的层型交替、M-RoPE 的部分维度旋转、以及循环块两次执行各自独立的 KV 缓存槽位。

这段复现在纯 CPU 上几秒钟就能跑完,不依赖 GPU 和任何外部权重文件,读者可以直接改配置做自己的结构实验。

比如把 loop_passes 改成 3,执行次数会变成 76,等效参数进一步上涨,这就是官方说的自适应迭代次数机制的雏形。

七、部署现实:推理框架还没跟上

官方核对的推理环境是 Transformers 5.18.0 加 PyTorch 2.11.0,CUDA 13.0,加载时必须带 trust_remote_code=True,走仓库自带的自定义实现。

本地推理三件套的现状要泼一盆冷水:llama.cpp、LM Studio、Ollama 目前都还不认识 sensenova_loop 这个新架构。

社区能用的 GGUF 来自 AtomicChat,作者明确标注文件是用打过补丁的 llama.cpp 构建的,基于 ggml-org/llama.cpp 的 commit 5047748f11,上游支持 PR 仍在排队。

这批 GGUF 目前只覆盖文本部分,视觉塔没有包含在内,多模态能力暂时只能走官方 PyTorch 路径。

推理加速方面,MTP 多 token 预测已经可用,Dspark 和 Dflash2 两种加速方案还处在训练阶段。

训练侧商汤放出了自己的 torchtitan 分支,单机 8 卡可以对 Loop-9B 做带 mHC 的微调,脚本和配置都挂在 GitHub 仓库里。

对想尝鲜的团队,现实路径只有一条:官方 Transformers 路线单卡 BF16 起步,等 llama.cpp 上游合并之后再考虑端侧量化部署。

部署清单值得逐项核对:配置、分词器、对话模板、自定义模型代码、权重分片五件套缺一不可,缺任何一个 trust_remote_code 加载都会在中途报错。

量化展望也值得提前算:BF16 下 18GB 的权重意味着 INT8 后约 9GB、INT4 后约 4.5GB,56 次执行带来的延迟会被量化换来的显存和带宽收益部分对冲。

文本 GGUF 已经可以先跑起来做架构验证和长上下文压测,等视觉塔进入移植版本之后再评估端侧多模态,两条线不要互相等待。

另一个现实约束是多模态输入的训练上下文口径:262,144 token 是训练时的上限,实际推理时的有效长度还受显存和位置外推能力约束,需要按场景实测。

生态响应速度可以参考先例:Qwen 系新架构从发布到 llama.cpp 合并通常以周计,sensenova_loop 有官方权重和活跃社区,适配周期大概率不会拖太久。

在此期间做技术选型的团队可以先用官方环境跑通任务基线,把准确率和延迟数据记录下来,等端侧链路成熟后直接平移配置。

采购和评估环节还要把 Preview 标签写进预期管理:这意味着权重、配置甚至架构细节都可能在正式版中调整,基于它的微调投入要控制在小步验证的规模内。

八、这条赛道还看什么

Loop-9B 不是孤例。今年 Hyperloop Transformers 等研究已经证明,给循环中间块配上超连接,可以用约一半的参数追平深度对齐的标准模型。

商汤的贡献是把这条研究线拉到了可用的多模态尺寸,并且把 Qwen3.5 级骨干、Gated-DeltaNet、M-RoPE 这些现役组件一次性组装到位。

官方路线图上有三个方向值得盯:DSA 稀疏注意力、自适应迭代次数、以及更大规模下的循环 Scaling。

自适应迭代次数最有想象力:简单输入跑一遍循环,困难输入跑三遍,把计算深度变成按输入定价的可变资源。

Preview 标签也在提醒边界:这是一个架构探索版本,真实 Agent 负载下的稳定性还需要社区实测数据回填。

对显存受限的边缘和私有化场景,循环计算给出了一条和量化、蒸馏正交的新路:不压缩权重本身,而是让同一份权重多干一遍活。

接下来 30 天值得盯三个信号:llama.cpp 上游支持 PR 是否合并、第三方独立评测能否复现 27B 级结论、Dspark 与 Dflash2 两种加速方案是否按期落地。

对读者的行动建议很直接:显存充裕的团队走官方 PyTorch 路径做 Agent 负载实测,显存受限的团队等 GGUF 生态成熟,架构研究者把重点放在 mHC 的混合矩阵约束上。

量化掉的是数值精度,蒸馏丢的是能力上限,循环付出的只是延迟,三种手段叠加起来的端侧模型组合会是明年最值得跟踪的工程方向。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/2 15:25:16

小Why的循环置换【牛客tracker 每日一题】

小Why的循环置换 时间限制:1 秒 空间限制:256M 网页链接 牛客tracker 牛客tracker & 每日一题,完成每日打卡,即可获得牛币。获得相应数量的牛币,能在【牛币兑换中心】,换取相应奖品!助力每…

作者头像 李华
网站建设 2026/11/2 15:03:24

Dolphin-Mistral 24B威尼斯版:掌控你的无审查AI

Dolphin-Mistral 24B威尼斯版:掌控你的无审查AI 导语:Dolphin-Mistral 24B威尼斯版正式发布,这款由dphn.ai与Venice.ai联合打造的大语言模型以"无审查"和"用户控制权"为核心卖点,挑战现有AI服务模式&#xff…

作者头像 李华
网站建设 2026/11/2 14:55:48

C++仿C#实现事件处理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/11/2 14:51:52

自研轻量级服务监控雷达:主动探活、负载感知与告警实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/11/2 14:51:48

PLFM_RADAR:可编程线性调频雷达完整信号处理链路解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华