arXiv'26 | Cassandra:不训练、不加显存,把 draft model 从 target 模型的 bit 里抠出来
ISCA26 | Cassandra:不训练、不加显存,把 draft model 从 target 模型的 bit 里抠出来
原文:Cassandra: Enabling Reasoning LLMs at Edge via Self-Speculative Decoding
1. 前言
先交代下背景:我自己最近正好在 vLLM 里折腾 self-speculative decoding,对这个方向的痛点算是结结实实体会过——训练一个好用的 draft model 很贵(EAGLE 系列要单独训练),不训练的方法(layer skipping、KV 压缩)在低 batch 下加速又很有限。所以看到这篇 Cassandra 时眼睛一亮:它给出的答案是,draft model 根本不用另外造,直接从 target 模型的权重里按 bit 粒度"抠"一个出来。
场景设定很明确:消费级设备上的 reasoning LLM。Reasoning 模型的特点是 decode 阶段特别长(一道题思考几千 token),decode 又是 memory-bound 的,权重加载是大头;同时边缘设备 batch size 就是 1 或 2,量化这类有损方法在长推理链上的精度损失会被放大。无损加速里最顺手的武器就是 speculative decoding:

draft 模型先猜 N 个 token,target 模型并行验证,猜对了就白赚——输出分布和原模型严格一致,无损。问题在于 draft model 从哪来。
2. 核心矛盾:draft model 的"出身"问题
现有方案大致三类,各有各的坑:
- 训练式(EAGLE-3 等):效果好,但要训练,而且 draft 权重要额外占显存——边缘设备本来显存就抠抠搜搜
- Layer skipping(Draft&Verify、Swift):训练-free,但跳层对模型质量伤害大,acceptance rate 上不去
- KV 压缩式(MagicDec、QuantSpec):优化的是 attention/KV 路径,可低 batch + 中等长度序列时,decode 的瓶颈根本不在 attention,在 FFN 权重加载——药不对症
Cassandra 的 insight 是:投机解码里 draft model 不需要"自己能写出正确答案",只要"接下来几个 token 大概率猜对"。这个要求比有损压缩的"保持任务精度"低得多,所以可以把多种有损压缩狠狠地叠加起来用——精度崩了没关系,反正有 target 模型兜底验证。
3. 方案
3.1 先补个基础:一个 bf16 权重里有什么
后面所有操作都在 bit 层面进行,所以先把 BFloat16 的位组成摆清楚。一个 bf16 数占 16 bit,分三段:
[1 bit 符号位 S] [8 bit 指数位 E] [7 bit 尾数位 M]
数值 = (−1)^S × 2^(E−127) × 1.M
- 符号位:正负,1 bit 动不得
- 指数位:决定数量级(这个数是 0.001 量级还是 100 量级),8 bit。改动 1 bit 数值直接翻倍/减半,最敏感
- 尾数位:决定精度(小数点后的细节),7 bit。砍掉低位尾数≈四舍五入,最不敏感
一个例子:0.15625 的 bf16 表示是 0 01111100 0100000(S=0,E=124 即 2^-3,M=0.25,即 1.25×2^-3=0.15625)。把尾数砍到只剩 3 bit 010,还原时低位补零,得到的还是 0.15625;即使原来尾数是 0100111,砍完还原成 0100000,数值只差 0.5% 左右——数量级不变,只丢精度尾巴。这就是后面"截尾数"敢下手的原因。
3.2 三步变换:把模型切成"骨架"和"细节补丁"
有了这个基础,看 Cassandra 怎么把一个模型切成两份。它的存储格式设计遵循一个不变式:speculation data(draft 用的骨架)+ verification data(补丁)= 原始模型的每一个 bit,两份拼起来严格无损。

上图(a)是离线变换流程,从左到右走读:
第一步:非结构化 value pruning(选哪些权重进骨架)。权重用 Wanda 的准则打分——不是单看权重绝对值,而是"权重 × 对应激活的 L2 范数"(激活在某些 channel 上天然幅值大,这些位置的权重再小也重要),分数高的整值保留进 speculation data;被剪掉的权重不丢弃,归入 verification data,同时用一张 bitmap(图中蓝色块)记录"谁被留下了"。KV Cache 则用 per-token 幅值剪枝(Key cache 有明显的 channel 级 outlier,Mustafar 的结论)。之所以选非结构化剪枝而不是结构化,是因为它保输出能力更强——同样压缩比下 acceptance rate 更高,而这两种剪枝都几乎不需要校准,贴合 training-free 的定位。
第二步:mantissa truncation(骨架内部再瘦身)。留下来的权重,7 bit 尾数只保留高位若干 bit 进 speculation data,砍掉的低位尾数原封不动存进 verification data。这里有个容易错过的对比:为什么用"截断"而不是更常见的量化(如 INT4)?因为量化是换一套数值表示(原始 bf16 的 bit 在量化值里不复存在,还原要做反量化计算),而截断保留的是原始 bit 的子集——还原时把两段尾数拼回去就行,零计算开销。"draft 是 target 的严格子集"这个关键性质就是这么来的。
第三步:exponent 压缩(下一节展开)。剪枝砍掉了一部分权重、截断砍掉了尾数,这时看剩下的 speculation data 的构成:1 bit 符号 + 8 bit 指数 + 3~4 bit 尾数——8 bit 的指数占了一半以上,成了新的大头。指数又动不得(前面说了,错 1 bit 数值翻倍)。这就是第三步存在的原因。
再看图(b)(c)的两条推理路径:
- Draft 推理(图 b):只从外存加载 speculation data。被剪掉的权重位置按 bitmap 补零(zero padding),被截掉的尾数低位也补零,恢复成标准 bf16 张量后正常跑 forward。注意这里没有省计算——张量尺寸和原模型一样,FLOPs 一点没少——省的是从内存搬进来的字节数。decode 阶段是 memory-bound(算力等内存),搬得少就是跑得快
- Target 验证(图 c):speculation + verification 两份都加载,剪掉的权重拼回原位、截掉的尾数低位接回去、指数解压——逐 bit 还原出原始模型做并行验证。所以最终输出的分布和原始模型完全一致,无损
对比 EAGLE 这类方案的账本:EAGLE 要在显存里额外塞一个 draft 网络;Cassandra 的 draft 和 target 共享同一份存储(draft 读其中一部分,target 读全部),额外参数存储为零。
3.3 指数位怎么压:不能改,但可以换个编码
指数不能改值,但"不能改值"和"不能压缩"是两回事。突破口在于指数值的分布:LLM 权重高度集中在 0 附近(比如绝大多数权重在 0.001~0.1 这个区间),对应的指数值就扎堆在 119、120、121 这么几个数上,126、127 这种大指数几乎不出现。分布越集中,Shannon 熵越低——实测:

bf16 权重 exponent 的 Shannon 熵只有约 2.58-2.64 bit,KV Cache 也就 2.7-2.8 bit。8 bit 的存储格子里装着不到 3 bit 的信息,六成是水分。水分可以用熵编码无损榨掉:高频指数用短码,低频用长码,每个值都能精确还原——值一个没改,占的 bit 少了。
选哪种熵编码?论文对比了三条路(对应原文 Fig.5):
- Huffman 编码:按频率建最优二叉树,压缩率最好(贴近 2.6 bit 的熵界)。但解码要么查一张 2^N 项的 LUT(N 是最长码长,LLM 里可到 32,表大到不现实),要么沿树逐 bit 走(纯串行)。压缩率赢了,硬件输了
- Unary coding(一元码,Cassandra-1 选的):按出现频率排序,最高频的指数编成
1,次高频01,第三001……即"N 个 0 跟一个 1"。妙处在于每个码字都以 1 结尾,从压缩流里辨认码字边界只需要"数零、遇一停"——这是纯组合逻辑,可以大规模并行。代价是压缩率略逊:实测平均 2.85 bit,比 Huffman 差零点几 bit,但换来了硬件解码的全并行。作者的取舍很明确:解码开销省下来的,比压缩率上让出去的多 - MX 格式(Cassandra-2 选的,微损):不做逐值编码,而是让相邻一组值(如 32 个)共享一个最大指数,组内每个值的尾数按自己与共享指数的差右移对齐。压得更狠、解码更快,但右移会丢掉小值的低位精度——轻微有损
于是形成两个版本:Cassandra-1 = unary 无损路线(精度零风险),Cassandra-2 = MX 微损路线(更快,精度小让步)。用户按需选。
3.4 剪枝和截断的配比:为什么要两个都用
压缩总预算固定时,是全砍权重(激进剪枝)还是全砍精度(激进截尾数)?论文扫了整条 trade-off 曲线:

左图横轴是压缩比、纵轴是 acceptance rate(draft 猜的 token 被 target 接受的比例):单用 value pruning(VP,蓝线)压过 60% 就跳水,单用 mantissa truncation(MT,黄线)55% 就崩,两者联用(VP+MT,红线)能扛到 80% 压缩比。右图换算成理想加速:VP+MT 在压缩比 75% 附近达到峰值约 2.5 倍。
直觉解释:剪枝伤的是"哪些神经元还在",截断伤的是"每个神经元有多精确",两种损伤模式作用在不同维度上,各砍一半比在单一维度上砍到底更温和——和"鸡蛋不放一个篮子"一个道理。
3.5 硬件侧:2% 面积换掉格式转换开销
最后一块拼图:这套自定义 bit 格式,xPU 的浮点单元不认识,算之前必须转回标准 bf16。转换涉及三类位级操作——按 bitmap 把剪掉的位置补零(de-sparsification)、unary 解码指数、拼接尾数——用通用 SIMD 核串行做,省下的带宽会被转换开销吐回去。
所以配了专用 encoder/decoder。核心部件是并行 zero counter:把 unary 码流切成 8-bit chunk,对每个 bit 位置并行输出"到此为止累计了几个零",配合前一 chunk 传来的边界状态(最后一位是啥、连零数多少),一个周期内并行辨认出多个码字边界,再查一张小 codebook 还原指数值。SystemVerilog 实现、28nm 综合,在 64 TFLOPS 的 NPU 上面积开销约 2%。
为什么 encoder 也不能省?权重可以离线压好,但 KV Cache 是推理时在线生成的——每生成一个 token 就产出一批新的 K/V,要现场剪枝、截断、编码后再存显存。这条在线路径没有硬件兜底就会变成新瓶颈。
4. 效果
三个 reasoning 模型(DeepSeek-R1-Distill-Llama-8B、Qwen3-8B-Thinking、Qwen3-4B-Thinking)、三种硬件(RTX 4090、Jetson AGX Orin、systolic NPU):

- 对 BFloat16 baseline 加速 1.78-2.41 倍(INT8 量化只有 1.25-1.42 倍,还掉精度)
- Acceptance rate 稳定在 0.74-0.91,DeepSeek 蒸馏模型最高
和其他投机解码方法对刚:

Cassandra-1 全面压过训练-free 的同行(Draft&Verify 约 1.1 倍、MagicDec 低 batch 下甚至负优化 0.9 倍、Lookahead 1.1-1.46 倍),和要训练的 EAGLE-3 打得有来有回(GPQA 上 1.93 vs 2.38,LiveCodeBench 上 2.10 vs 2.12,AIME 上 1.88 vs 1.99)——不训练能摸到训练式方法的天花板,这就是最大卖点。
还有个容易被忽略的赢点——显存:

由于 exponent 压缩是实打实作用在整个模型上的,Cassandra 的显存占用比 BF16 baseline 还低:同样 24 GB 的 RTX 4090,BF16 和 Eagle-3 在 64K 序列附近撞墙,Cassandra 能撑到 128K,相同显存下能生成 11.59 倍于 Llama3-based 投机解码、1.81 倍于 Eagle-3 的 token 数。加速的同时还省显存,这在"加速方法普遍要多吃显存"的投机解码赛道里是个异类。
5.讨论
作为最近在 vLLM 里亲手搓过 self-spec decoding 的牛马,说几句体感:
- "draft 不需要对,只需要像"这个观察是整篇文章的灵魂。有损压缩的评价体系(perplexity、任务精度)在投机解码语境下完全换了坐标系——换成 acceptance rate vs compression ratio 的 trade-off。想通这一点,各种"平时不敢用"的激进压缩手段全部解锁。我自己实验里用 layer-skip 路线在 Qwen3-8B 上只拿到 1.1-1.2 倍,看到 bit 级方案能到 2 倍上下,只能说粒度选对了确实不一样:跳层是把某些"完整的思考步骤"整个扔掉,bit 级修剪是全体权重"均匀降精度",后者对 next-token 预测的伤害显然更温和。
- Exponent 熵只有 2.6 bit 这个数字值得单独记住。这和前几天那篇逼近 Shannon 界的无损压缩论文(bf16 有效熵 10-12 bit)互相印证:浮点格式的 exponent 是整个 LLM 存储里水分最大的部位。以后看到任何压缩方案,先问一句"exponent 怎么处理的"。
- 泼冷水的部分:核心收益依赖自定义硬件解码器。RTX 4090 上的数字是模拟器给的,真实 GPU 没有那个并行 unary decoder,落地要等硬件厂商买账,或者看有没有人能用 tensor core + bit 操作 kernel 逼近这个效果——这其实是个挺有意思的开源坑位。
顺带扯一句题外话:Cassandra 本质上是在"剪枝 + 截断 + 熵编码"的组合空间里找 acceptance rate 和压缩比的最优 trade-off——这个"在离散设计空间里搜索最优配置"的问题形态,和 NAS/AutoML 是同源的。我们把相关积累整理成了《动手学 AutoML:从 NAS 到大语言模型优化实战》,书里第 8 章讲 LLM 压缩(剪枝/量化),实战篇还有 LLM 后训练剪枝的完整实现,和这篇论文的 value pruning 思路可以对照着看。


浙公网安备 33010602011771号