arXiv'26 | 无损压缩逼近 Shannon 极限:你的 LLM 权重里一半以上的 bit 是白存的

arXiv’26 | 无损压缩逼近 Shannon 极限:你的 LLM 权重里一半以上的 bit 是白存的

原文:Approaching Shannon Bound with Lossless LLM Weight Compression


1. 前言

你有没有想过,当你把一个 bf16 的模型权重加载进显存时,这 16 个 bit 里到底有多少是真正”有信息量”的?

这篇来自 NUS、华中科技大学和 ETH Zurich 的工作给出了一个让人有点意外的答案:LLM 权重的有效熵比存储位宽低 2-10 倍。换句话说,一个 bf16 权重名义上占 16 bit,但从信息论角度看,它实际只携带 10-12 bit 的信息;int4 更夸张,4 bit 的存储里真实熵只有 0.6-1.0 bit。剩下的全是统计冗余——理论上可以一个数值都不改地压掉。

注意关键词:无损(lossless)。不是量化,不是剪枝,解压出来的权重和原始权重逐 bit 相同,推理结果完全一致。这对 reasoning 模型特别重要——有损压缩带来的精度损失在长推理链上会累积放大,而无损压缩没有这个顾虑。

先看大背景,模型规模和单卡显存的剪刀差越拉越大:

模型规模增长远快于显存增长

Llama 4 已经摸到 2T 参数,而单卡 HBM 才刚爬到 288 GB。显存不够,要么加卡(贵),要么量化(掉精度),要么 offload(慢)。这篇论文给了第四条路:把权重里的统计冗余榨干。

2. 权重里到底有多少水分

2.1 熵分析:逐层算直方图

方法很朴素:把每层权重矩阵当成离散符号序列(fp8 code、int4 值等),做经验直方图,算 Shannon 熵,再按参数量加权平均得到整个模型的有效 bit 数。作者扫了从 Qwen-1.5B 到 Llama-405B 共 6 个模型、7 种数值格式(bf16 / fp8 / int8 / sq8 / fp4 / int4 / awq4):

各模型各数值格式的熵差距

几个值得记住的数字:

  • bf16 有 4-5 bit 的冗余,对应约 1.5 倍压缩空间
  • int4 冗余率最高,理论压缩比可达 6.3-10.4 倍(Mistral-7B 最狠)——因为量化后的符号分布高度尖峰化,大量 code 几乎不出现
  • SmoothQuant 的 sq8 冗余最少(1.1 倍),说明它的分布已经比较”满”

细思极恐的是:即便是 AWQ、int4 这些已经压得很狠的量化格式,里面仍然趴着大量统计冗余。量化和无损压缩是正交的,可以叠着用。

2.2 为什么大家不用无损压缩?

无损压缩在存储系统、多媒体领域用得飞起,在 LLM 系统里却几乎缺席,原因不在压缩比,在解压路径。现有方案(比如 ZipNN)是层级粒度的:先把一整层解压到 global memory,再启动 GEMM。

现有粗粒度流水线存在层级同步屏障

解压在关键路径上,每层一个同步屏障,计算单元干等,还要来回搬运 global memory。这就是核心矛盾:压缩比再高,解压跟不上 GPU 的消费速度,一切白搭

作者据此提炼了三条设计原则:

  1. 逼近 Shannon 极限 + 硬件级吞吐:解码速度必须跟得上 GPU 显存带宽,否则解压自己成瓶颈
  2. tile 粒度随机访问:GEMM 是一个 tile 一个 tile 消费权重的,codec 必须支持任意 tile 独立解码,不能”想解第 100 个 tile 得先解完前 99 个”
  3. 解压深度融合进 GEMM dataflow:解码结果直接写 shared memory,不落 global memory

对照这三条筛一遍经典压缩算法,答案几乎唯一:rANS(Asymmetric Numeral Systems)——熵效率 >99% 逼近 Shannon 极限,且天然支持字节级流式解码。gzip、Zstd 这些要么熵效率不够,要么访问粒度太粗。

3. 方案:tile 级 on-the-fly ANS 解压

3.1 整体执行模型

核心思路一句话:把压缩 bitstream 按 GEMM 的 tile 切分,每个 tile 独立编码,运行时在 shared memory 里”边解边算”

tile 对齐的 on-the-fly 解压执行模型

离线阶段,每层权重按下游 GEMM 的 tiling 几何(如 128×32、256×64)切成 tile,同层所有 tile 共享一个紧凑的 ANS codebook(分摊元数据开销),每个 tile 的起始偏移记进一张轻量索引表。这样任意 tile 都是自包含的子流,GEMM kernel 要哪块就解哪块。

运行时的流水线看下图,第 i 层的计算不用等整层解压完,第一个 tile 解好就能开算,后面的 tile 并发解码:

GPU kernel 全景:离线压缩 + 在线解压与 swizzled GEMM

几个关键工程点:

  • warp 分工:每个 thread block 里 warp 0 负责流式解码压缩 tile,解出的权重直接写进 shared memory(不经过 global memory),其余 warp 立刻拿去喂 tensor core
  • 双缓冲(ping-pong):解 tile k+1 和算 tile k 重叠,用 cuda::atomic_ref 做生产者-消费者同步,解压延迟几乎全被藏掉
  • warp 级并行 rANS:ANS 状态机本质是串行的,作者把每个 tile 的 bitstream 切成 R 条独立子流,每个 warp lane 维护自己的 rANS 状态,把串行解码摊到整个 warp 上
  • 确定性驱逐:shared memory 放不下时,按 tile-swizzle 遍历顺序静态决定驱逐谁,不需要运行时引用计数

整套东西做成了 CUTLASS 之上的 plugin 式 kernel,PyTorch 可以直接调,改动极小。

4. 效果

4.1 压缩率:离 Shannon 极限只差 0.01-0.1 bit

tile 级 ANS 压缩率与 Shannon 熵界几乎重合

绿色柱(Ours)和红色柱(Shannon bound)几乎贴在一起——低位宽格式(int8/sq8/fp4/int4)上基本编到了理论最优。唯一的例外是 bf16,16-bit 符号表太大,频率表的有限精度归一化引入约 0.1-0.2 bit 开销,但也在 ANS 理论预期之内。实际压缩比:bf16 约 1.4-1.5 倍,int4 最高 10.4 倍。

4.2 kernel 层面:比 SOTA 无损方案快最多 11 倍

和两个最新的无损压缩推理系统对比(都是层级解压路线):

对比 NeuZip:低 batch 下最高 11.1 倍加速

对 NeuZip 峰值加速 10.1-11.1 倍,对 DFloat11 也有 6.4-6.9 倍。差距的来源正是第 2.2 节说的那个矛盾:它们都要先把整层 materialize 到 global memory,而本文的 tile 级路径完全绕开了这一步。

消融实验把收益拆开看:

从 naive ANS 解码到融合 kernel 的逐步加速

从 naive 实现(DietGPU 解码 + CUTLASS GEMM 两段式)出发,tile 对齐流水线贡献 3.3-8.2 倍,双缓冲再往上叠,最终 4.4-10.1 倍(Llama-405B 的大矩阵上收益最明显,10.06 倍)。和原生 CUTLASS 比,A100 上开销基本打平(1.0-1.1 倍),H200 上由于片上内存更大,某些配置甚至反超 baseline——解压不再是税,偶尔还是补贴

4.3 端到端:省下的显存全喂给 KV Cache

集成进 SGLang 后,在固定显存预算下测端到端 serving(数据来自论文 Table II):

  • Qwen-14B(单卡 A100 80GB):权重从 27.5 GB 压到 18.1 GB,省下的 9.4 GB 全部让给 KV Cache,最大 batch 从 47 涨到 75(1.3 倍),吞吐提升至 1.2 倍
  • Mixtral-176B(4×A100):权重 261.9 GB → 163.7 GB,KV Cache 空间从 26.3 GB 暴涨到 124.6 GB,可行 batch size 从 20 涨到 95(4.8 倍),吞吐 241 → 391 tokens/s(1.6 倍)

这里有个很本质的观察:压缩把 LLM 推理的瓶颈从”显存容量”挪回了”计算吞吐”。权重瘦身 → KV Cache 变大 → batch 变大 → tensor core 吃得更饱。代价是 TPOT(每 token 延迟)略涨——这是给吞吐导向 serving 场景设计的,追求单请求低延迟的场景要自己掂量一下。

5. 一点个人 take

这篇工作我觉得最值得借鉴的不是 ANS 本身(熵编码是老技术了),而是“压缩格式必须迁就计算的访问模式”这个视角。以前大家做权重压缩,习惯把压缩当成存储问题——压缩比越高越好;这篇明确指出真正的门槛在解压路径和 GEMM dataflow 的融合。tile 粒度、shared memory 直写、双缓冲,每一步都是在把解压往 GEMM 的固有节奏里塞。

另外两个我比较在意的点:

  1. 和量化正交是这个方法最大的实用卖点。AWQ 完模型还能再无损压 1.4 倍,白捡的显存为什么不要?
  2. bf16 上 1.4-1.5 倍的收益说实话不算惊艳,但 MoE 场景(Mixtral-176B batch 4.8 倍)的收益是实打实的——MoE 的痛点恰恰是权重占用大而单 token 激活少,压权重换 KV Cache 空间这笔账在 MoE 上最划算。作为长期做 MoE 推理优化的牛马,看到这个数字还是挺兴奋的。

局限也说一句:目前收益集中在 memory-bound 和容量受限的场景,compute-bound 的大 batch prefill 阶段,压缩带来的是”能跑更大 batch”而非”单次 GEMM 更快”;另外每种 tiling 几何都要离线预处理一遍,模型切换的工程成本还在。

欢迎评论区交流,有 kernel 细节问题也可以一起讨论。


顺带扯一句题外话:这篇论文属于”不改数值的压缩”,而我们平时更常打交道的是”改数值的压缩”——剪枝、量化这些有损路线。我们把这个方向的积累整理成了一本书《动手学 AutoML:从 NAS 到大语言模型优化实战》,第 8 章和第 11 章专门讲 LLM 压缩(剪枝/量化/模型融合)和后训练剪枝实战。读完这篇再去看有损压缩,你会对”每个 bit 到底值多少信息”这件事有完全不同的体感。

动手学AutoML书籍封面

Flag Counter