首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >从 TensorSharp 视角解读 Ternary Bonsai 2 27B:当 1.72 比特的权重遇上 Hadamard 变换

从 TensorSharp 视角解读 Ternary Bonsai 2 27B:当 1.72 比特的权重遇上 Hadamard 变换

作者头像
张善友
发布于 2026-09-27 08:23:48
发布于 2026-09-27 08:23:48
140
举报
文章被收录于专栏:张善友的专栏张善友的专栏

本文基于 PrismML 发布资料、CloudNavi 部署指南(2026-09-19)与 TensorSharp 官方模型文档(docs/models/bonsai2.md,集成验证日期 2026-09-22)整理,聚焦 TensorSharp 作为推理引擎是怎么"接住"这个模型的,不谈 Ollama / llama.cpp 路线。

01-公众号头图-三元盆栽27B
01-公众号头图-三元盆栽27B

1. 引言:一个"普通运行时跑不了"的模型

2026 年 9 月 17 日,PrismML(出自 Caltech 的研究团队,获 Khosla Ventures、Cerberus、Google 支持)发布了 Ternary Bonsai 2 27B:一个把 27.36B 参数压进 5.9GB 文件的三元量化多模态模型,在 20 项基准上保留了基座模型 98.2% 的分数。

数字很诱人,但它有一个苛刻的前提:原版 llama.cpp 与现行 Ollama 都无法运行它。权重不在普通的数值基底上,运行时必须执行 activation 侧的 Walsh–Hadamard 变换,而这个变换至今没有合并进上游 llama.cpp。

这就给推理引擎出了一道工程题:不魔改底层库、不重新量化,怎么把一个"带旋转基底"的低比特模型跑对、跑得可验证?TensorSharp 的集成方案是一个值得拆开看的样本,本文就沿着它展开。


2. 模型速览:压缩了什么,保留了什么

2.1 基本规格

项目

内容

参数量

273.6 亿(语言 243.5 亿 · 视觉塔 4.7 亿 · 嵌入与 LM head 25.4 亿)

架构

混合注意力(线性约 75% · 全注意力约 25%),SwiGLU、RoPE、RMSNorm

权重格式

三元 g128 + FP16 分组缩放,分块 Hadamard 旋转

有效位宽

真三元 1.72 bit/权重;发布的 PTQ1_0 为 1.76 bit

最大上下文

262,144 token

输入 / 输出

文本 + 图像输入 / 文本输出

许可

Apache 2.0

模型本质上是 PrismML 对 Qwen 系列稠密基座的三元量化版:权重只用 −1、0、+1 三个值存放,配合 FP16 分组缩放,FP16 下 53.8GB 的权重被压到约九分之一。

2.2 基准保留率(官方公布值)

20 项基准在思考模式下平均 83.9 分,相对基座 27B 的 85.4 分保留 98.2%。更值得看的是分项:

能力

Bonsai 2 27B

基座 27B

保留率

知识与推理

83.95

86.66

96.9%

数学

96.57

97.06

99.5%

编码

81.58

82.17

99.3%

代理与工具调用

77.57

79.74

97.3%

指令遵循

82.66

81.25

102.0%

视觉

78.59

81.64

96.3%

综合

83.9

85.4

98.2%

数学与编码几乎无损,指令遵循甚至反超基座;知识与推理、视觉各下降约 3%。误差会在长链路代理任务里累积,这个区间需要留意。

2.3 官方发布的三种文件

格式

体积

特点

GGUF PTQ1_0

5.93GB

体积最小,每权重 1.76 比特

GGUF PQ2_0

7.25GB

解包更轻,提示词处理更快(demo 默认)

MLX 2bit

8.49GB

面向 Apple Silicon,含全精度视觉塔,可用于原版 MLX


3. 核心难点:权重不只是三元数

这是理解整个集成工作的关键,TensorSharp 文档开篇就说得很直白:

Merely interpreting its weights as ordinary ternary numbers produces the wrong network: projection inputs and embedding outputs must also receive the declared transforms. (仅仅把权重当作普通三元数去解释,得到的是一个错误的网络:投影的输入和嵌入的输出必须施加声明的变换。)

换句话说,单做"把权重换成 −1/0/+1"这一步并不够:权重被存放在一个旋转基底上,运行时必须配合执行逆变换,网络才是原网络。具体来说:

  • 嵌入查表后,要经过逆变换;
  • 每个低比特投影在矩阵乘之前,要对输入激活做变换;
  • 输出 head 之前还要再做一次正变换。

如果运行时"看起来能加载、能出 token",却跳过了这些变换,它输出的其实是一个从未训练过的网络的结果。这正是 CloudNavi 文章警告的陷阱:旧格式文件在某些原版构建上会静默加载并输出乱码;低比特格式没有被原生支持时,有的运行时会静默按高精度反量化运行,输出对了,但速度与内存优势全部消失。


4. TensorSharp 的接入方案:转码而非魔改

4.1 设计哲学:不 patch ggml

TensorSharp 对这两种自定义格式的处理方式很克制:

  • GGUF 读取器把 PQ2_0 识别为张量类型 142、PTQ1_0 识别为 143;
  • TensorSharp 自有的 native 代码把它们的块打包无损展开为上游 GGML 的 Q2_0 块;
  • 保留权重表示值,不做再量化;
  • 从不改写原始 GGUF 文件,也不给 ggml 打补丁去引入发行方私有的张量类型。

代价是内存占用不再等于文件体积:无损展开会增大量化负载,PQ2_0 约多 6%,PTQ1_0 约多 29%。这是用内存换兼容性的明确取舍。

4.2 变换契约:prism.hadamard.*

模型文件里带有一组 prism.hadamard.* 元数据,声明了:变换版本、归一化的 Sylvester-Walsh-Hadamard 变换、输入轴、块大小(本组模型为 1,024 元素/块)、显式 ±1 符号表,以及哪些权重名要做正变换或逆变换。

数学上,对一个投影输入 x,每个块计算:

代码语言:javascript
复制
H(D·x) / sqrt(block_size)

其中 D 是存储的 ±1 符号对角矩阵,H 是未归一化 Hadamard 矩阵。嵌入行则用逆序:

代码语言:javascript
复制
D·(H·x) / sqrt(block_size)

此外,分组 GDN 元数据还会把 SSM 输出输入按发行方的 head 顺序重排后再变换;融合的 QKV 和 gate/up 权重保留其原投影各自的变换。

TensorSharp 在加载权重之前校验完整的契约,遇到未知的变换布局直接拒绝:宁可跑不起来,也不静默跑错。

4.3 代码入口

整个集成散布在五个文件里,职责划分清晰:

文件

职责

BonsaiHadamardMetadata.cs

校验元数据,实现托管侧的逆嵌入变换

ModelBase.Bonsai.cs

模型加载期的转码与变换注册

QuantizedWeight.Bonsai.cs

权重缓存键的变换注册,释放时注销

bonsai_quant.cpp

精确的块解码/转码,不修改上游 GGML

ggml_ops_bonsai.cpp

把符号变换接入 TensorSharp 的 native 计算图路径

4.4 网络执行流程

代码语言:javascript
复制
token ID -> 低比特嵌入行 -> 逆符号 Hadamard 变换
  -> 64 个混合 Qwen 3.5 层:
       RMSNorm -> attention 或 GatedDeltaNet -> 残差
       RMSNorm -> 稠密门控 FFN -> 残差
       (声明的低比特投影会旋转其输入激活)
  -> RMSNorm -> 符号 Hadamard 变换 -> 低比特输出 head -> logits

底层架构上,两个 GGUF 文件声明 general.architecture=qwen35:64 层、hidden width 5,120、FFN width 17,408、词表 248,320、262,144 token 上下文。其中 48 层是递归的 GatedDeltaNet,16 层是全注意力;全注意力用 24 个 query 头 + 4 个 KV 头(宽度 256),GatedDeltaNet 用 16 个 key 组 + 48 个 value 头(宽度 128)。TensorSharp 复用了现成的 Qwen 3.5 实现(整模型 prefill/decode、逐序列的 KV 与递归状态、continuous batching),PRISM 变换被插入到这些既有路径里,直接继承其融合与状态管理。


5. 怎么跑:命令与限制

5.1 首版集成的硬性限制

初始集成只接受单设备 GGML backend。纯托管 CPU、直连 CUDA、MLX、张量并行配置都会被直接拒绝,而不是静默省略旋转变换。宁可拒绝加载,也不给出错误结果,这是整条集成线一致的原则。

5.2 运行示例

显式设置上下文上限,而不是按宣传的 262k 全量分配:

代码语言:javascript
复制
# CLI 推理
MAX_CONTEXT=4096 KV_CACHE_DTYPE=f16 \
dotnet run --project TensorSharp.Cli -c Release -- \
  --model /path/to/Ternary-Bonsai-2-27B-PQ2_0.gguf \
  --backend ggml_metal --input prompt.txt --max-tokens 128 \
  --temperature 0

# 带 OpenAI 兼容 API 的服务端,挂载视觉投影器
MAX_CONTEXT=4096 KV_CACHE_DTYPE=f16 \
dotnet run --project TensorSharp.Server.Host -c Release -- \
  --model /path/to/Ternary-Bonsai-2-27B-PTQ1_0.gguf \
  --mmproj /path/to/Ternary-Bonsai-2-27B-mmproj-Q8_0.gguf \
  --backend ggml_metal --host 127.0.0.1 --port 5000

配套的投影器文件有两个:mmproj-BF16(全精度)和 mmproj-Q8_0(更省内存),对比图像质量或内存占用时用 --mmproj 显式指定。


6. 可复现验证:数字摆到桌面上

TensorSharp 的验证流程覆盖:精确 raw-token 参照、单路/并行流式请求、算术、结构化 JSON、工具调用、两个投影器、解析器测试。基线是 PrismML 官方的 llama.cpp 分支(bdc23b56 提交)。文档特别强调:一个连 PQ2_0/PTQ1_0 都加载不了的上游 llama.cpp 构建,不构成有效的性能对比对象。

6.1 正确性(2026-09-22,Metal)

在未改动的上游 ggml 179b60f2 上:

  • 两种格式在 Metal 上逐 token 精确复现发行方基线的 4 条贪心提示(各 32 token);
  • 4 路并发序列的输出与串行完全一致,31 步融合 decode、零回退步;
  • 每种格式通过 21 项 HTTP 单路/并发对比,以及算术、JSON、工具调用、图像颜色检查(PQ2 + Q8_0 投影器、PTQ1 + BF16 投影器各测一组)。

注意这些是功能性冒烟测试,不是全面的质量评估;262k 上下文、CUDA、Vulkan、iOS、张量并行均未验证。

6.2 性能(Apple M5 Pro,48GB 统一内存)

格式

指标 (tokens/s)

TensorSharp

发行方 llama.cpp

PQ2_0

Prefill 512

364.05

384.44

PQ2_0

Decode 64

25.50

26.96

PQ2_0

HTTP 并发 4

28.40

32.57

PTQ1_0

Prefill 512

364.49

357.67

PTQ1_0

Decode 64

25.53

26.31

PTQ1_0

HTTP 并发 4

28.17

16.15

模型级速率为三次预热运行(零上下文深度)的均值;HTTP 速率为三次运行的均值(每请求 64 生成 token)。两侧引擎均用 F16 KV 和 512 token 物理 prefill 批。

文档对结果的定性很直白:性能目标尚未达成。模型级 decode 比基线慢 5.4%(PQ2)和 3.0%(PTQ1),PQ2 并发 4 的 HTTP 吞吐低 12.8%。旋转变换本身有开销,转码后的权重也比发行方自定义格式更占内存。PTQ1_0 的 prefill 和高并发 HTTP 反而更快,但样本量太小,文档明确声明"小幅差异不构成普遍性能优势的证据"。vLLM/SGLang 的吞吐未测量(其实现参考了融合投影、分块递归 prefill 等模式,但不构成对等基线)。


7. 对照:为什么 Ollama / 原版 llama.cpp 走不通

三条路线放进一张表对照:

维度

PrismML llama.cpp 分支

原版 llama.cpp / Ollama

TensorSharp

PQ2_0 / PTQ1_0 加载

原生支持

拒绝加载(旧 Q2_0 会静默乱码)

识别为类型 142/143,无损转码

Walsh–Hadamard 变换

已实现(未合并上游)

缺失

通过 ggml_ops_bonsai.cpp 接入计算图

ggml 是否被修改

是(发行方分支)

—

否(转码到上游格式)

元数据契约校验

内建

—

加载前全量校验,未知布局拒绝

部署方式

setup.sh 预编译二进制

一键(但跑不了)

dotnet run,.NET 生态

CloudNavi 的建议是走 PrismML 官方 demo 仓库(PrismML-Eng/Bonsai-demo,setup.sh 获取二进制),这确实是普通用户最省事的路径。而 TensorSharp 的价值在另一面:给 .NET 生态一个可审计、可嵌入的参考实现。它用托管代码加独立 native 层完成接入,不 fork 上游库,全部变换契约显式校验,验证数据和限制如实公开。


8. 内存账本:跑起来要多少资源

总内存 = 权重 + KV 缓存 +(用图像输入时的)视觉塔。27B 的 KV 缓存约 64 KiB/token,随上下文线性增长:

用途

所需内存

合适的硬件

短上下文(约 8K)

约 8.4GB

16GB 统一内存 Mac,或 12GB 显存显卡

实用线(32K)

约 9.9GB

16GB 统一内存或 12GB 显存以上

长上下文(64K)+ 图像

约 11.9GB

24GB 统一内存,或 16GB 显存

最大上下文(262K)

约 23.9GB(4bit KV 约 12.5GB)

32GB 以上内存 + 4bit KV 缓存

纯 CPU

建议 32GB

无独显的迷你主机,速度会下降

两个紧凑配置开关(PrismML 侧):BONSAI_KV4=1 把 KV 缓存量化为 4bit(约缩至三分之一),BONSAI_MMPROJ_CPU=1 把视觉投影器移到系统内存(释放约 0.9GiB 显存)。

对 TensorSharp 用户还要叠加 4.1 节的转码开销:加载后的权重内存会比文件体积大 6%(PQ2_0)到 29%(PTQ1_0),规划内存时不能只看磁盘占用。

速度参考(官方公布):RTX 5090(CUDA)最高 143 token/s;M5 Max(MLX)46.8 token/s。


9. 现状与局限:文档没有回避的事

TensorSharp 文档在结尾把局限一条条列了出来:

  • 性能目标未达成:decode 慢 3–5.4%,PQ2 高并发吞吐低 12.8%;
  • 仅验证 Metal 单设备路径:CUDA、Vulkan、iOS、张量并行均未验证;
  • CPU 路径只做了最小检查(PQ2,两条提示各 8 token);
  • 262k 上下文未验证;
  • native CPU 测试套件中有一个继承自上游 HEAD 的 DeepSeek41 容差失败,未被计入通过项;
  • 所有保留率与速度均为 PrismML 公布值,独立复现需要时间。

10. 结语

Ternary Bonsai 2 27B 代表了低比特推理的一个真实拐点:压缩本身已经足够好(98.2% 保留率、1.72 有效位宽),瓶颈转移到了运行时基础设施:变换契约、格式转码、kernel 融合、验证方法学。

TensorSharp 的集成里有三个决策尤其值得参考:

  1. 转码而非 patch:把私有格式无损展开到上游格式,底层库零改动,兼容性风险被隔离在自己这一层;
  2. 校验而非假设:prism.hadamard.* 契约加载前全量验证,不满足条件(多设备、无变换能力的 backend)直接拒绝。拒绝加载永远好过静默跑错;
  3. 公开而非粉饰:性能差距、未验证路径、失败用例全部写进文档,对比基线严格限定为"能加载同款文件的引擎"。

对想在 .NET 技术栈里跑端侧大模型、或想搞清楚"带旋转基底的三元量化到底怎么在引擎层落地"的开发者来说,docs/models/bonsai2.md 及其引用的五个实现文件,是目前公开资料里最完整的一条路径。


参考

  • PrismML:Ternary Bonsai 2 27B 发布资料(2026-09-17)
  • CloudNavi:《【2026】Ternary Bonsai 2 27B 本地部署:内存需求、量化与推荐硬件》(2026-09-19)
  • TensorSharp:https://github.com/zhongkaifu/TensorSharp/docs/models/bonsai2.md(zhongkaifu/TensorSharp,集成验证 2026-09-22)
    • BonsaiHadamardMetadata.cs / ModelBase.Bonsai.cs / QuantizedWeight.Bonsai.cs / bonsai_quant.cpp / ggml_ops_bonsai.cpp
  • PrismML-Eng/llama.cpp 分支(prism-b10658 及更新;验证基线 bdc23b56)

注:文中基准分数、保留率与速度均为 PrismML 公布值;TensorSharp 性能数据来自其 2026-09-22 在 Apple M5 Pro(48GB)上的测量,样本量有限,不构成普遍性结论。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-09-26,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • 1. 引言:一个"普通运行时跑不了"的模型
  • 2. 模型速览:压缩了什么,保留了什么
    • 2.1 基本规格
    • 2.2 基准保留率(官方公布值)
    • 2.3 官方发布的三种文件
  • 3. 核心难点:权重不只是三元数
  • 4. TensorSharp 的接入方案:转码而非魔改
    • 4.1 设计哲学:不 patch ggml
    • 4.2 变换契约:prism.hadamard.*
    • 4.3 代码入口
    • 4.4 网络执行流程
  • 5. 怎么跑:命令与限制
    • 5.1 首版集成的硬性限制
    • 5.2 运行示例
  • 6. 可复现验证:数字摆到桌面上
    • 6.1 正确性(2026-09-22,Metal)
    • 6.2 性能(Apple M5 Pro,48GB 统一内存)
  • 7. 对照:为什么 Ollama / 原版 llama.cpp 走不通
  • 8. 内存账本:跑起来要多少资源
  • 9. 现状与局限:文档没有回避的事
  • 10. 结语
  • 参考
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档