- 大規模LLMはGPUメモリの限界のためデプロイが難しいが、DFloat11はBFloat16重みを約70%のサイズまで縮小しつつ、元のモデルとビット単位で同一の出力を維持する
- 中核となる着想は、BFloat16の8ビット exponentが実際には約2.6ビット分の情報しか持たない点にあり、signとmantissaは保持し、exponentだけをHuffman codingで圧縮する
- 動的長エンコーディングはGPUではボトルネックになりやすいため、DFloat11は階層型LUT、2段階カーネル、transformer block単位の圧縮解除を用い、並列推論に適した設計にしている
- Llama 3.3、Qwen 3、Mistral 3、DeepSeek R1 Distilled、FLUX.1、Stable Diffusion 3.5などで、約30%のモデルサイズ削減と元の出力の保持を確認した
- CPU offloadという代替手法と比べて、token generationのスループットが2.3〜46.2倍高く、8×80GB GPUの単一ノードで810GB規模のLlama 3.1 405Bの無損失推論を可能にする
DFloat11が狙うメモリボトルネック
- LLMやDiffusion Modelのような基盤モデルは急速に大規模化しており、メモリ制約のあるハードウェア上で効率的にデプロイするのが難しい
- Llama 3.1 405Bは4050億個のパラメータをBFloat16形式で保存しており、推論全体に約810GBのメモリが必要となる
- これは8×80GB GPUを備えた一般的な高性能GPUサーバーの容量を超える
- 複数ノードが必要になると、デプロイコストとアクセス性の負担が大きくなる
- DFloat11はBFloat16モデルを元サイズの約70%まで圧縮しながら、どのタスクでも精度を100%保持することを目標としている
損失量子化とは異なるアプローチ
- 量子化は重みの精度を下げてメモリを削減する損失圧縮方式である
- メモリ使用量を大幅に減らし、推論速度を高められる一方で、近似誤差が生じる
- 精度低下はベースモデル、量子化方式、評価ベンチマーク、目標bit-widthによって異なるため予測しにくい
- たとえば8-bit SmoothQuantをDeepSeek-R1-Distill-Qwen-1.5Bに適用すると、reasoning taskの平均精度が9.09%低下する
- 全体の精度指標が似ていても、量子化モデルは元モデルと異なる回答挙動を示すことがある
- Dutta et al. は、正解が誤答に、または誤答が正解に変わるflips現象を観測した
- W8A16 GPTQで量子化したQwen2-1.5Bは、GSM8K 8-shot精度低下が0.3%にとどまる一方、回答の6.37%で正誤状態が入れ替わる
- 金融・医療のような分野では、量子化モデルの出力変化は規制と信頼性の要件を満たしにくくする可能性がある
- 既存の無損失モデル圧縮は主にcheckpoint保存効率、モデルハブのダウンロード時間短縮、FPGAのような特殊ハードウェアを対象としており、一般的なGPU推論では利点が小さい
BFloat16 exponentに残された圧縮余地
- BFloat16は16ビットをsign 1ビット、exponent 8ビット、mantissa 7ビットに分ける
- DFloat11の出発点は、LLM重みのBFloat16各構成要素ごとのShannon entropy分析である
- signとmantissaのentropyはそれぞれのbit-widthに近く、圧縮余地は限られる
- exponentには8ビット割り当てられているが、entropyは約2.6ビットしかない
- exponent値の分布は非常に不均衡である
- 可能な256個の8ビット値のうち、実際に使われるのは約40個だけである
- 残りの値は出現しない
- 出現頻度の順位も急速に低下する
- 低いentropyのおかげで、exponentは無損失圧縮の対象となり、約5.4ビット分のexponent情報を圧縮できる
DFloat11形式
- DFloat11またはDF11は、BFloat16重みのexponentだけをentropy codingで圧縮する動的長の浮動小数点形式である
- Huffman treeはモデル重みのexponent分布に基づいて構築される
- 頻出するexponent値には短いコードが割り当てられる
- まれな値には長いコードが付く
- signとmantissaは元のまま保持される
- exponentは
EncodedExponent byte arrayにbit-packed形式で保存される
- signとmantissaは
PackedSignMantissa byte arrayに別途保存される
- その結果、BFloat16重みは平均して約11ビット程度まで縮小され、精度損失なく元のBFloat16値へ復元できる
GPU推論向けの圧縮解除
- entropy-coded重みは動的長エンコーディングを用いるため、matrix multiplicationにそのまま投入することはできない
- 必要なweight matrixをその場で元のBFloat16に圧縮解除する必要がある
- matrix multiplicationが終わると、BFloat16 matrixはGPUメモリ節約のため直ちに破棄される
- 一般的なHuffman decodingはビット単位でtreeを逐次探索するため、GPUの並列構造と相性が悪い
- 単一threadが圧縮解除を担当するとGPU利用率が低くなり、latencyも大きくなる
階層型LUTベースのデコーディング
- DFloat11はHuffman tree探索の代わりに、lookup tableベースのデコーディングを使う
- 最大Huffman code長をLとすると、単一LUTには
2^Lのサイズが必要になる
- LLMではLは通常24〜32の範囲である
2^32エントリ規模のLUTをGPU SRAMに載せるのは難しい
- これを避けるため、Huffman treeを高さ8の重ならないsubtreeに分割し、各subtreeを256-entryのcompact LUTにする
- 階層型LUTでは、一部のエントリが下位LUTを指す参照として機能する必要がある
- LLM exponentで未使用の値が多いことを活用する
- 240〜255の未使用exponent値を内部ポインタとして再利用する
- これらの値は±2^113〜±2^128という非常に大きな値を表し、LLM重みには現れない
- 実験では、BFloat16 exponent Huffman treeのcompact LUT数kは4〜8個の範囲である
CodeLengthsと合わせて最大(8 + 1) × 256 bytesのメモリを使う
- この程度のサイズならSRAMに収まり、繰り返しlookupを高速に実行できる
2段階GPUカーネルと補助メタデータ
- 各GPU threadは、encoded exponentの連続したn byte区間を担当する
- 実験ではn = 8を使用した
- threadは自分の区間内で始まるHuffman codeをデコードする
- 動的長コードのため、2つの問題が生じる
- 各threadの正確な開始bit位置が明確でない
- 最初のthreadを除くと、decoded elementの出力indexを知るのが難しい
- 最初の問題はGaps配列で解決する
- Gapsはthreadごとに1つのエントリを持つ
- 各エントリはthread開始byteを基準に、最初の有効なHuffman codeのbit offsetを表す
- 最大code長は32ビットなので、offsetは
[0, 31]の範囲で、5ビットで保存される
- 出力位置の問題は、thread block単位の位置だけを保存することでメモリオーバーヘッドを抑える
- threadごとに32ビットの出力位置を保存すると、weight matrixごとに数万thread規模でオーバーヘッドが大きい
- DFloat11は各thread blockの最初のelementの出力位置だけを保存する
- カーネルは2段階で動作する
- 第1段階では、各threadが自分の区間をデコードしてelement数だけを数え、HBMには書き込まない
- block内threadはBlelloch algorithmでprefix sumを行い、threadごとの出力位置を計算する
- 第2段階で同じ区間を再度デコードし、計算した位置に従ってSRAM write bufferへdecoded値を書き込む
- encoded exponentは最初のpass前にSRAMへ読み込み、global memoryへの重複アクセスを避ける
- すべてのdecoded exponentがSRAMに書かれた後、HBMへcoalesced writeを1回だけ実行する
Transformer block単位の圧縮解除
- 単一のweight matrixの圧縮解除はサイズが小さく、GPU資源を十分に活用できない場合がある
- matrixサイズが大きくなるほど、DFloat11の圧縮解除スループットは向上する
- 複数のmatrixをまとめて圧縮解除することで、スループットを高め、latencyを隠蔽する
- transformer block内のすべてのDFloat11 weight matrixを1つのbatchとして圧縮解除する
- このbatched decompressionは、該当transformer blockのforward pass直前に行われる
- LLMのtoken embeddingとlanguage modeling headも圧縮対象である
- これらのmatrixはGPU資源を飽和させるのに十分大きいため、別途batchingは不要である
評価結果と実際の効果
- DFloat11はLlama 3、Qwen 3、Mistral 3、DeepSeek R1 Distilled、FLUX.1、Stable Diffusion 3.5を含むLLMおよびdiffusion transformerで評価された
- 結果として、約30%のモデルサイズ削減と元の出力の完全な保持が示された
- 出力は元モデルとbit-for-bitで同一である
- 損失圧縮ではないため、元のBFloat16重み精度を維持する
- 非圧縮モデルの一部をCPUへoffloadしてメモリ制約に合わせる代替手法と比べると、DFloat11はtoken generationで2.3〜46.2倍高いスループットを達成する
- 固定GPUメモリ予算では、非圧縮モデルより5.7〜14.9倍長いgeneration lengthを可能にする
- Llama 3.1 405Bは810GB規模のモデルだが、DFloat11により8×80GB A100 GPUの単一ノードで無損失推論が可能になる
- この結果は、Llama-3.1-405Bの実行に必要なハードウェア要件を半分にしつつ、accuracy lossなしで推論できることを示している
1件のコメント
Hacker Newsのコメント
これは、bfloat16の動的範囲が非常に広いものの、実際にはその範囲を使い切っていないという事実の結果にすぎない。
人々はハイパーパラメータが10^10ではなく0.01のように見えることを好むが、各指数ごとに同じ相対精度を使える。ネットワークのハイパーパラメータ、初期化された重み、学習データなどをすべて10^6倍しても、上限領域をほとんど使わないため、たいていは似たように動作する。ただし、一部の特殊関数は例外かもしれない。
重みと活性値に見られるbfloat16値の典型的なエントロピーは10〜12ビット程度で、実際には値域の約65〜75%しか使われない。符号ビットと仮数ビットは、圧縮しにくいノイズに近い。
こうした性質は、古典的なハイパフォーマンスコンピューティングとAIの両方で、すでに何度も活用されてきた。Martin Burtscherの研究室によるロスレス圧縮の研究(https://userweb.cs.txstate.edu/~burtscher/)、LLNLのfpzip(https://computing.llnl.gov/projects/fpzip)、2021年に作った私のライブラリdietgpu(https://github.com/facebookresearch/dietgpu)がその例だ。私たちは大規模GPUクラスタで、転送前のすべてのデータ、たとえば勾配やバックアップ由来の重みをロスレス圧縮し、受信時に展開することで、全体の学習ウォールクロック時間を約10%短縮した。ロスレスなので計算結果は以前と同じだ。
また、rANSはHuffman符号化よりもSIMD系の命令セットで効率が高く、実装もしやすい。DFloat11でも算術演算の前に展開する必要があるため、レイテンシとスループットの損失を減らせる。
量子化はローカルLLM実行者にとって重要なツールで、たいていRAMがボトルネックになる。BF16重みに対して、より良いロスレス圧縮があるのかも気になる。
DFloat11は既存の量子化ワークフローに比較的簡単に差し込めそうに見えるが、論文にはかなり懐疑的なようなので、自分が見落としている点を理解したい。
ニューラルネットワークの層では、入力に重みを掛けて足し合わせ、その出力が次の層の入力になる。この過程が100回以上繰り返されることもある。最終出力層に到達する頃には、その10^6という係数が何度も適用され、10^600レベルまで雪だるま式に大きくなるはずだ。
DFloat11で毎秒トークン数が高かったケースは、一部の層をCPUへオフロードして推論した場合との比較だけだ。
典型的なコンピュータサイエンスの空間と速度のトレードオフであり、ただ飯はない。
それでも、切り落として0で埋めるコストは小さい。
最も目を引くのは実用上の含意だ。405Bパラメータモデルを8×80GB GPUの単一ノードでロスレス推論できるようにするというのは、かなり驚きだ。
巨大なインフラ費用なしでフロンティアモデルを動かしたい研究室やスタートアップの双方にとって、大きな解放要因になり得る。
こんなに興味深い時代に生きていられてありがたい。HNを開くたびに、機械学習とTransformerモデル関連の新しいニュースが次々に見える。
もっと深く読んでみる必要があるが、llama.cppがcuBLASと一緒にある種のカスタムカーネルを使っているのか、それともcuBLASカーネルをうまく活用しているだけなのか気になる。
2週間? 2か月? 2日? 2分?
場合によっては全部正しい。本当に興味深い時代だ。
この重み形式戦争が落ち着けば、ハードウェアがそれをサポートするように作られる可能性がある。合理的に最適な重み形式が何に決まるにせよ、それに合わせて最適化された行列乗算ハードウェアが必要になるだろう。
実際のエージェント活用事例では、品質、コスト、性能のバランスを取るのが難しい場合が多い。この手法は、エージェントのコストを最適化しようとする際に、予測しにくい結果も含め、量子化手法が生み出すトレードオフを避ける助けになるかもしれない。
DFloat11によってより安価なGPUに収められるなら、場合によってはコスト削減がかなり大きくなり得る。私はxmad.aiで働いている。
メモリ制約のため、圧縮していないモデルの一部をCPUへオフロードする代替案と比べると、DFloat11はトークン生成のスループットが1.9〜38.8倍高いとのこと。固定されたGPUメモリ予算では、圧縮していないモデルより5.3〜13.17倍長いコンテキスト長を可能にするという
コンテキスト長だけ見ても、モデルがメモリに収まるとしても使えそうに見えるが、LLMはメモリ帯域幅に縛られることが多いという初歩的な理解からすると、すべてGPU上にある場合でも秒間トークン数が改善するのか気になる
論文ではA100で200GB/s未満だと主張しており、ベンチマーク上はGPUとモデルによって、バッチサイズ1で1.5〜4倍遅いように見える。もちろんバッチサイズが十分大きくなれば、このオーバーヘッドはほとんど消える
他の可逆コーデックは同じハードウェアで600GB/sに到達できるので、改善の余地はありそうだ。ただしA100の生のメモリ帯域幅は1.6TB/sである
これは、量子化していないLLMのメモリ要件を一般的に30%削減できるという意味なのか? もし本当ならかなり大きい
既存の量子化に足せるなら確かに興味深いが、K量子化もすでに全体的なパープレキシティへの影響に応じて層ごとに異なる精度を使っている。例えばQ6は4ビットと8ビットを混ぜて使うが、これはここで使われているエントロピー指標に似ている。補正済みのimatrixまで考慮すると、概念的にはFFTに似た方式でより強く圧縮している
これはZipNNとは違うのか? https://arxiv.org/pdf/2411.05239
言及は見えるが、それがベースなのか、それとも別物またはより良いものなのか分からない
しかしLZ系の圧縮器はニューラルネットワークの重みにはあまり意味がなさそうだ。反復の多い大半のテキストデータほど冗長性が高くなく、データが非常に疎でない限り、辞書オーバーヘッドを避けられるほど活用できる反復も多くないかもしれない
LZ系の圧縮器を追加して推論のクリティカルパスに入れると、展開ははるかに遅くなるはずだ。展開を計算カーネルと融合するのが最も良い。例えば各タイルを算術演算の前に展開するGEMMのように作れるし、展開ルーチンが単純であるほどそれは容易になる
これらすべてがどれほど速く動いているかを見るのはかなりクールだ。毎週新しい効率化手法やハードウェアのアップグレードが出てくる感じがする
こうした改善にはつい気を取られやすい
これを新しいモデルにも実行できるのか? 自分が誤解していなければ、コードは推論用にしか見えない