最近有个模型挺火啊现在都排进了HF排行榜的第四了
模型叫做microsoft/bitnet-b1.58-2B-4T
其实非常小的一个模型,只有2B,那这东西有多大意义呢?
它主要探索一个打法
也就是这篇论文
The era of 1-bit llms: All large language models are in 1.58 bits
先说这论文解决啥问题
现在LLM越来越大,虽然也有各种量化策略,但是,你基础模型越来越大这是不争的事实,从2B上来到7B,最后14,100,现在动不动加MOE就六七百B,甚至有上trillion的模型,都不考虑激活,光这模型权重,一般卡就根本装不下,举个例子比如你40B得模型,以BF16的形式存储,就要占80G的显存,直接给卡的显存就撑爆了,只能跨TP或者PP存着。
因为它存一个参数就要消耗16个bit
那能不能把这节省一点呢,答案是可以的,比如量化到int4啥的,一下子就变4bit了,但是因为量化反向化造成的损失也比较大,除非你做了特别好的方式,例如负载的bit wise 的quantized,context wise的quantized,才能有一些好的效果
还一个问题就是矩阵乘,这玩意耗算力,耗电
那有没有一种方法来解决方便两个问题的,就是这篇1.58b论文来解决的。
我门先看啥是1.58B
其参数采用三元值{-1, 0, 1}表示。在信息论中,三个可能的值需要log₂(3) ≈ 1.58496 bits来表示,约等于1.58 bits。这是一种极端的量化方式,旨在减少计算和内存需求,同时保持与全精度模型(如FP16或BF16)的性能相当
所以又把它叫三进制,2进制不是[0,1]么
讲到这会有学数学的同学骂我,说你懂不懂进制啊。三进制是计数系统,每一位有{0, 1, 2},用于表示数值。而{-1, 0, 1}是离散的权重值,不构成连续的计数位。例如,两个三进制位可以表示0到8(3²-1),但BitNet b1.58的两个参数只是独立取值,没有组合成数值的含义,然后巴拉巴拉,我就爱叫三进制,爱咋咋地
我们继续
我这里面举举一个具体的例子来说明BitNet b1.58如何通过三元值{-1, 0, 1}减少存储需求,相比于你熟悉的浮点数(如BP16啥的)。
例子:假如有5个参数,我们来算一下存储对比
有这么一个包含5个参数的小模型,参数值为:1, -1, 0, 1, 0。
- BF16存储
-
每个参数用16位存储,总共5 × 16 = 80位。
-
- BitNet b1.58存储
-
这些参数被量化为{-1, 0, 1},可以映射为三元数字0、1、2(例如,-1→0,0→1,1→2),得到序列:2, 0, 1, 2, 1。(不量化肯定不行,要不对不上2进制,你放不进去现有的计算机系统里面去)
-
然后,这5个三元参数(称为trit)可以高效打包到8位中。例如,将它们视为基数3的数,计算为2×81 + 0×27 + 1×9 + 2×3 + 1×1 = 178,转换为二进制是10110010,占用8位。
=====================
这块要是有看不懂的,我讲一下,看懂了就直接略过
-
从高位到低位排列并分配不同指数(3⁴, 3³, 3², 3¹, 3⁰,即81, 27, 9, 3, 1)是三进制计数系统的标准数学表示法,类似十进制的位权(10ⁿ)。
-
这种方法确保5个trit的243种组合(3⁵)能唯一映射到8位二进制的256种状态(2⁸),实现接近理论最优的存储(1.6位/trit ≈ 1.58位/trit)。
=====================
-
-
这种打包方式(5个trit到8位)使每个参数实际占用约1.6位,接近理论值1.58位,显著降低了存储需求
然后
每个参数可以是{-1, 0, 1},这对应于三种状态。在存储时,这些值通常被映射为0、1、2(即三元数字或trit),以便于用二进制表示。例如,可以通过量化函数将权重映射到{-1, 0, 1},如论文所述,使用absmean量化函数
其实你理解根量化也差不多
为了进一步优化存储,BitNet b1.58采用了将多个三元参数打包到二进制位中的方式。研究表明,5个三元参数(trit)可以被高效打包到8位(byte)中:
-
-
理由:3^5 = 243(5个trit的可能组合),而2^8 = 256(8位的可能组合),因此8位足以存储5个trit,且信息密度接近理论最优(99.06%效率)。
-
具体方法:将5个trit视为基数3的数,转换为基数2。例如,序列2, 0, 1, 2, 1可以计算为2×81 + 0×27 + 1×9 + 2×3 + 1×1 = 178,转换为二进制10110010,占用8位。
-
现在你2进制得数字有了占8位
对比结果
-
BF16需要80位,BitNet b1.58只需8位,节省了约10倍(80/8=10)
所以是不是把你模型权重占的显存就一下子给拉低了?
可以一下子降10倍的内存占用
另外一个比较牛B得是它其实不太用算矩阵乘
也就是计算优化:三元值的前向传播用加减法替代乘法,显著降低计算成本。论文显示,3B模型的矩阵乘法能耗降低71.4倍,延迟从5.07ms降至1.87ms(2.71倍)
这个啥意思呢?
在传统的神经网络中,矩阵乘那还用说吗,就靠这东西吃饭呢,当然我们叫GEMM,页不光乘,其实事涉及大量的乘法和加法操作。而在BitNet b1.58中,由于参数被限制为{-1, 0, 1},乘法操作可以被简化为简单的加法或减法:
-
-
乘以1:相当于直接加法(保持原值)。
-
乘以-1:相当于减法(取负值)。
-
乘以0:相当于忽略(结果为0)。
-
总之乘不乘法也没那么重要了。使得计算更加高效,因为乘法操作通常比加法或减法更耗时且更耗能。论文提到,BitNet b1.58的矩阵乘法能耗比16的模型低71.4倍。此外,3B规模的模型延迟从5.07ms降至1.87ms,吞吐量显著提高,因为小啊
顺带手的,三元值中的0还引入了稀疏性(sparsity),即许多参数为0,可以在计算过程中被直接跳过,这进一步减少了计算量。例如,论文指出,70B规模的BitNet b1.58吞吐量达到2977 tokens/s,而LLaMA LLM仅为333 tokens/s,效率提高8.9倍。
它和llama同等级模型的对比,当然其实事实上llama事最拉的,同等级它现在还时没qwen啥的好的,一会我粘一下结果
这就是1.58B得由来,那么最近发布的ms这个模型有啥更新呢
它搞了个新的.cpp的推理框架
在 CPU 上的快速和无损推理是通过 bitnet.cpp
这一专门设计的软件栈实现的。bitnet.cpp
的核心在于其 优化的内核 以及对 1.58 位模型特性的利用,从而在保证推理精度的前提下提升速度和降低能耗。下面给大家讲一下实现的关键方面:
定制化的内核 (Optimized Kernels):bitnet.cpp
提供了一套专门为 1.58 位模型设计的优化内核,包括 I2_S、TL1 和 TL2。这些内核针对 x86 和 ARM 架构进行了优化,旨在实现快速且无损的推理。
-
- I2_S 内核
该内核在离线状态下将全精度权重转换为 2 位表示,以节省内存和带宽。在进行计算时,这些 2 位权重会被 解包 (Unpack) 回原始值,然后执行标准的矩阵乘法 (GEMV) 操作。由于其直接的乘加方式,推荐在 多线程环境下使用,以便编译器生成高效的流水线指令序列。
- TL1 内核
该内核预处理每 两个全精度权重,将它们打包成一个 4 位索引,并预先计算它们对应的激活值到一个 查找表 (LUT) 中。推理时,通过查找表和 int16 的累加来执行 GEMV 操作。该方法借鉴了先前在低比特 LLM 推理方面的工作 [PPK+22, WCC+24, 19]。对于服务大型模型,推荐使用 有限数量的线程。
- TL2 内核
与 TL1 类似,但 TL2 将每 三个权重压缩成一个 5 位索引(1 位符号和 4 位索引)。相比 TL1,TL2 实现了更高的压缩率,模型尺寸减少了 1/6,从而降低了带宽需求。因此,推荐在 内存或带宽受限的环境中使用。
- I2_S 内核
无损推理 (Lossless Inference):bitnet.cpp
旨在实现对三元 BitNet b1.58 LLM 的无损推理。通过对比 bitnet.cpp
和 llama.cpp
在 WildChat 数据集上的输出与 FP32 内核的输出(其实这么比完全没必要,比BF16就够了),结果表明 bitnet.cpp
的 l2_S、TL1 和 TL2 内核实现了 100% 的推理准确率,即其输出与全精度基线完全匹配。
性能优化 (Performance Optimization):通过使用这些优化的内核,bitnet.cpp
在 CPU 上实现了显著的性能提升。
-
-
在 ARM CPU (Apple M2 Ultra) 上,相对于
llama.cpp
(BF16),bitnet.cpp
实现了 1.37x 到 5.07x 的加速,并且 降低了 55.4% 到 70.0% 的能量消耗。尤其值得注意的是,即使对于 100B 参数量的模型,bitnet.cpp
也能在单个 CPU 上达到 接近人类阅读的速度(5-7 tokens per second),100B得模型CPU能给你推出来7个token,这已经可以了 -
在 x86 CPU (Intel Core i7-13700H) 上,
bitnet.cpp
的加速效果更加显著,达到了 2.37x 到 6.17x,并且 降低了 71.9% 到 82.2% 的能量消耗。即使在线程受限的情况下(2 个线程),bitnet.cpp
仍然表现出强大的性能。但这玩意尤其是cpu不光x86还是arm玩得就是thread多,所以肯定是越多core越快
-
官方给的评测看着同等级的一些模型来看,它也还行,但是值得注意的是MMLU和humaneval都不咋滴,这个在实际使用起来,肯定还时比较别扭,所以目前是实验性质多一点
因为它在做三元到二元的时候其实走的是QAT的思想,所以这块如果能做到bit wise的quantized或者优化的时候理论能力还能有上升
总之吧,以后edge测的推理,真就有可能搞一台arm就OK了,页不用GPU了。(GPU本来擅长的是矩阵乘,你这么做把乘当加法了,tensor core页没什么用了,光用cuda core加速,有用,但是就没那么明显了)