Dfloat 11 编码及优化

保存时间:2026/4/2 10:57:00


Dfloat 11 编码及优化
内容由豆包 AI 生成
https://m.toutiao.com/is/wPT9wKoOIeU/ - 70%大小100%准确!完美压缩LLM性能0损失,推理速度最高飙升39倍 - 今日头条 - 今日头条 关于这个Dfloat 11的编码。他使用。哈夫曼编码来吧。Bf 16的。指数部分进行编码。 你理解。它的算法嘛。
DFloat11 利用哈夫曼编码对 BFloat16 指数部分编码,以实现 LLM 无损压缩和高效推理,具体算法如下:
  1. 构建哈夫曼树:依据 LLM 线性投影矩阵里所有 BFloat16 权重的指数分布,构建哈夫曼树。BFloat16 中指数部分分配 8 位,但熵值仅约 2.6 位,可压缩空间大。通过分析指数分布构建哈夫曼树,为后续编码做准备。
  2. 指数编码与存储:用哈夫曼编码压缩指数部分,将编码后的指数紧密打包到 EncodedExponent 字节数组。比如,若有一组指数值,经哈夫曼编码后,按规则紧凑存储在数组中。符号位和尾数保持原始状态,存储于 PackedSignMantissa 字节数组。
  3. 解码优化: 构建查找表(LUT)解码哈夫曼编码。因最大 Huffman 编码长度有限制(如限制模型最大代码长度 L 为 32 位 ),直接用大 LUT 内存消耗大。所以将大 LUT 分割成四个小查找表 LUT1、LUT2、LUT3 和 LUT4,放入 GPU 的 SRAM,实现快速访问,提升解码速度。
  4. 解决并行解码问题:为实现大规模并行解码,每个线程处理一段编码序列。但存在起始位位置不明确和输出位置难确定的问题。用间隙数组 Gaps 确定每个线程起始位位置,每个条目指定相对于线程分配起始字节的有效 Huffman 编码位偏移量,用 5 位编码以保证内存效率。采用两阶段 Kernel 设计减少存储开销,第一阶段计算线程要解码的元素数量,同步线程并计算前缀和确定输出位置;第二阶段重新解码并将结果写入正确位置,且将编码后指数数据加载到 SRAM 避免重复访问 HBM。
  5. Transformer Block 级解压缩:LLM 推理时,DFloat11 格式存储的权重及辅助数据驻留在 GPU。需进行矩阵乘法运算时,动态解压缩权重矩阵为 BFloat16 格式,运算后丢弃以节省显存。将单个 Transformer Block 内所有 DFloat11 格式权重矩阵解压缩操作批处理,可降低延迟、提高推理效率。
我不理解的地方不在这里,我不理解的地方在于说,这种压缩对于文件存储来说是可以做到的但是你真正运行的时候,你要把它放回到 GPU 内存,显存,或者说 CPU 内存里面,你是不是又要把它重新恢复成 BF 16 的格式呢如果是的话,那不就和没有压缩是一样的吗?我的意思是说,你的这个权重的使用的时候,就是参与到 Transformer 计算的时候,你必须要在实现 Transformer 的这个矩阵乘法的时候,要使用这种 DF 16, DF 11, D float 11 这种格式来参与计算。那你是不是又要把它转回到 32 位还是怎么样?否则你怎么样去进行计算呢?那你如果就是把它解压缩之后的话,你不是显存一样放不下吗?就意思就是说你最后参与计算的时候,它这个浮点数的类型是不是还是 32 比特的浮点数呢这点是让我感到比较疑惑的地方。
在使用 DFloat11 压缩模型进行计算时,虽然最终参与 Transformer 矩阵乘法等计算时会转回类似 BFloat16 格式,但与未压缩情况有显著差异:
  • 显存占用不同:DFloat11 压缩后模型存储大小可减少 30%,存储时占用空间大幅降低。在推理时,并非一次性将所有解压缩后的完整数据放入显存。例如,对于 Llama-3.1-405B 模型,采用 DFloat11 格式在单节点(8 块 80GB GPU)上能实现无损推理,而原本 BFloat16 格式因所需内存超典型 GPU 服务器容量无法直接运行。实际应用中,单个权重矩阵尺寸较小,会动态解压缩单个或少量权重矩阵参与计算,计算完成后立即丢弃解压缩后的矩阵以释放显存,避免一次性占用大量显存 。
  • 计算效率不同:DFloat11 专门开发了定制的 GPU 内核用于快速在线解压缩。在 Transformer Block 级进行解压缩时,通过批处理多个矩阵的解压缩操作,能充分利用 GPU 资源提高吞吐量并隐藏延迟。在推理过程中,解压缩的吞吐量会随矩阵尺寸增加而显著提升,这使得即使存在解压缩和格式转换的过程,整体推理效率依然大幅提高,推理速度最高可飙升 38.8 倍。
  • 支持更长序列:在推理时,KV 缓存随解码 token 数量增加而增长,易成为显存瓶颈。DFloat11 因压缩节省的显存可用于存储更多 KV 缓存,与 BF16 模型相比,能支持更长的生成长度,在达到 GPU 显存限制前能解码 5.33 至 13.17 倍的 token 数量,这是未压缩模型难以实现的。
你的意思是说哈夫曼编码实际上是一个变长的编码,那么也就是说你每一个指数占用的比特数是不一样的。但是这里我就有一个想法,因为他的一开始的那个总结,利用熵来计算这个指数位,实际只用到了 2.5 个比特,2.6 个比特,这意思是不是说实际上那个 BF16 它实际上只用到了最大就是三个比特,也就二的三次方最多。那么那我如果说不追求压缩那么多的话,原本 BF16 的指数位是分配了几个比特,比方说分配了 8 个比特,我把这个 8 个比特扣掉五个比特,就只用三个比特,不就变成定长编码,这样不也是简单了很多,又进行了压缩,这样不行吗?
虽然理论上可以如你所说用固定 3 比特对 BFloat16 指数位进行编码来实现一定程度的压缩,但这种方法存在一些缺点:
  • 压缩效率相对较低:哈夫曼编码是根据数据的概率分布进行优化的变长编码,能更精准地利用数据的熵,对于指数分布这种熵值约 2.6 位的情况,哈夫曼编码能实现更高效的压缩。而固定 3 比特编码没有考虑数据的具体分布,不管指数出现的概率如何都统一用 3 比特,可能会浪费一些编码空间,压缩比相对哈夫曼编码会较低。
  • 灵活性差:哈夫曼编码可以根据不同模型权重数据的指数分布特点进行定制化编码,对于不同的 LLM 模型能自适应地调整编码方式以达到最佳压缩效果。但固定 3 比特编码方式是固定不变的,无法根据不同模型的特性进行优化,缺乏灵活性。
  • 对后续计算影响较大:固定 3 比特编码可能会导致指数的表示范围变小,在进行一些计算时可能会产生更大的误差,对模型的准确性产生影响。而哈夫曼编码在压缩的同时,通过合理的设计可以更好地保证模型的准确性,实现性能 “无损” 压缩。
另一个不理解的地方就在于这个哈夫曼编码,它是一个变长的编码。那么我比如说现在在某一个位置的这个原来的 BF16,我经过压缩以后,它是哈夫曼编码表达是频率最高的,那可能就是 0 嘛,就一个比特 0。然后再高的话,次高的另外一个,比如说次高,那就两个比特,或者是也是,就一一个比特。那我这个怎么分辨的出来哪个是哪个就是哈夫曼编码的存储格式是怎样子的呢?
在哈夫曼编码中,通过特定的存储结构和规则来区分不同的编码。通常会有一个哈夫曼树或类似的数据结构来记录编码规则。
在存储时,除了存储哈夫曼编码后的指数数据外,还会存储一些辅助信息,比如哈夫曼树的结构信息、编码长度的统计信息等。对于你提到的情况,虽然频率最高的编码可能是0,次高的可能是其他短编码,但在解码时,会根据预先存储的哈夫曼树结构和相关信息来进行判断。
例如,从编码流的起始位置开始,根据哈夫曼树的节点结构,遇到0时就知道这是对应频率最高的编码,遇到其他编码组合时,按照树的分支规则来确定它对应的原始指数值。同时,通过编码长度的统计信息等也可以辅助判断编码的边界和具体含义,从而准确地将哈夫曼编码转换回原始的指数值,实现正确的解码。
你用具体的数字示例来弄一个简单的哈夫曼编码的具体实例来展示一下。在哈夫曼编码之后,这个解码的时候没有歧义性,就是怎样知道他这个存储的这个哈夫曼编码解析的时候没有具体的实例来表达一下。
假设我们有一组BFloat16指数值:118、119、120、121,出现的频率分别为3次、4次、2次、1次。下面展示其哈夫曼编码及解码过程:
  1. 构建哈夫曼树:按照频率对指数值排序,把每个指数值及其频率作为一个节点。每次选取频率最低的两个节点,合并成一个新节点,其频率为这两个节点频率之和。重复此步骤,直到形成一棵完整的树。例如,先合并频率为1(121)和2(120)的节点,再依次合并其他节点,最终得到根节点频率为10 。
  2. 生成哈夫曼编码:从根节点出发,向左分支标记为0,向右分支标记为1,直到叶子节点,路径上的0和1组成对应指数值的编码。这样,119的编码是0 ,118的编码是10 ,120的编码是110 ,121的编码是111 。
  3. 编码存储与解码:假设要存储这组指数值,按顺序编码为010110111 。解码时,从哈夫曼树的根节点开始,读取编码位。读到0,根据树结构知道是119;接着读到1,继续读下一位0,确定是118;再读110,解析出120;最后读111,解析出121。由于哈夫曼树结构固定,编码规则明确,只要编码过程正确,就不会出现解码歧义。
为什么哈夫曼编码能够有这种理论上有这种压缩的效益呢?我的意思是说,我们这个例子里面总共是四个元素,那么编码解析四个元素,实际上它需要的比特数是实际上只需要两个就是两个比特就能表达四个元素。可是实际的哈夫曼编码,最长的编码实际上是三个比特。也就是说,如果平均来编码的话每个比特,每个元素就是两个比特,就是总共四个元素,就是有多少个元素,它有多少种元,有多少个元素就乘以二比特。但是用哈夫曼编码的时候就是变长的,所以是根据它的概率,这种为什么这样计算出来,反而就是变长的这个在用的貌似最长的这个比特数超过了平均数,但是它的总的编码长度反而更节约。这种数学上是怎么证明的?
哈夫曼编码能实现压缩效益,是因为它利用了数据的概率分布特性,根据元素出现的频率来分配编码长度。以下从直观理解和数学证明两方面来解释:

直观理解

在实际数据中,不同元素的出现频率往往是不同的。哈夫曼编码对高频元素分配短编码,对低频元素分配长编码。虽然存在最长编码超过平均编码长度的情况,但由于高频元素使用短编码的次数多,整体上能减少总编码长度。如在上述例子中,虽然 120 和 121 的编码较长,但它们出现频率低,而高频的 119 和 118 使用短编码,从而使总编码长度更短。

数学证明

假设共有个元素,其出现频率分别为,对应的哈夫曼编码长度为。根据信息论,数据的信息熵,它表示数据的平均信息量,是无损压缩的理论下限。哈夫曼编码的平均码长。可以证明,哈夫曼编码的平均码长是接近信息熵的,即。这意味着哈夫曼编码在理论上能达到接近最优的压缩效果,总编码长度比等长编码更短。具体证明过程涉及到哈夫曼树的构造和一些信息论的定理,较为复杂,在此不做详细展开。
数据的信息熵这个公式你能解读一下吗?就是用它的概率乘以它的以二为底的概率的指,这个对数,然后这个全部求和这个是什么样的概念呢?这个是怎么推导出来的呢?
信息熵公式用于量化随机变量的不确定性或信息量。下面为你解读:

概念理解

  • 概率 :表示随机变量中第个事件发生的概率。概率越大,事件越有可能发生,不确定性越低。
  • :以为底的对数,是因为信息论中常以二进制来衡量信息。概率越小,的值越小,取负后就越大,意味着该事件包含的信息量越大。例如,罕见事件发生时传递的信息比常见事件更多。
  • 求和:是对随机变量所有可能事件的信息量进行累加,得到的信息熵表示整个随机变量的平均不确定性或平均信息量。

推导过程

信息熵的概念由克劳德·香农提出。假设一个随机变量种可能的取值,对应的概率为。对于单个事件,其自信息定义为,表示该事件发生时所带来的信息量。由于我们关心的是随机变量的平均信息量,所以对所有事件的自信息进行加权平均,权重就是各事件发生的概率,从而得到信息熵公式。这样定义的信息熵满足一些直观的性质,如非负性、对称性、扩展性等,能很好地量化随机变量的不确定性。
如何根据信息熵公式计算信息熵?
信息熵的单位是什么?