HNSWインデックスがfloat32ではなく3ビット埋め込みを保存していたら? [R]

Reddit r/MachineLearning / 2026/4/11

💬 オピニオンSignals & Early TrendsIdeas & Deep AnalysisTools & Practical Usage

要点

  • この記事では、HNSWグラフのノード埋め込みを3ビットの量子化ベクトルとして保存する(ランダム直交回転の後、Lloyd-Maxのスカラー量子化を行う)ことで、float32保存に比べてメモリを大幅に削減することを提案している。
  • 各次元でのfloat乗算を、事前計算したセントロイド同士の内積ルックアップテーブルに置き換えることで、ANN探索時に復元(デコンプレッション)を避けながら近似距離計算を高速化できると主張している。
  • この手法は2段階パイプラインを用いる。すなわち、厳密なコサイン類似度によるリランキングのために、上位k候補だけを復元して精度を回復する。
  • ランダムな埋め込みに対する初期実験(dim=128、2Kベクトル、M=16)では、リランキング込みでrecall@10 > 0.85を報告し、ノードあたりのメモリは約4倍削減されたが、ビルドが遅くなることや、貪欲なトラバーサルが量子化ノイズに敏感であることに言及している。
  • 追加の知見として、圧縮埋め込みのキャッシングは、Zipfのようなアクセスパターン下でRAMの利用効率やキャッシュヒット率を大きく改善し得ること、さらに回転+量子化の際のメモリ往復を減らすために統合(fused)CUDAカーネルを使えることが挙げられている。

HNSWグラフのノードに、float32ベクトル(約4,096バイト)ではなく量子化埋め込み(次元=1024で各約388バイト)を格納する、というベクトル・インデックス作成のアプローチを試しています。

重要な洞察は、乱交差直交(ランダム直交)回転の後にLloyd-Maxスカラー量子化で埋め込みを量子化すること(ZandiehらによるPolarQuantアプローチ、ICLR 2026)です。これにより、セントロイド同士の内積テーブルを事前計算できます(3ビットなら8x8=64個のfloat)。グラフ巡回中の距離計算は、1024回のfloat乗算+加算ではなく、1024回のテーブル参照になります。検索中に復号(デコンプレッション)は不要です。

次に、上位-k候補を復号して、最終結果のために厳密なコサイン類似度で再ランキングします。

ランダム埋め込みでの初期結果(次元=128、2Kベクトル、M=16): - rerankingありで recall@10 > 0.85(総当たり探索と比較) - ノードあたりのメモリ使用量:float32 HNSWより約4倍少ない - 構築は遅い(最適化なしのPythonプロトタイプ)

まだうまくいっていない点: - 距離における量子化ノイズによって、貪欲探索が最適でない経路をたどることがあり、その補償のためにより大きいef値が必要になる - ごく小さい次元(dim=64)では、近傍リストとキャッシュされたインデックスのオーバーヘッドが支配的なので、実際にはメモリ節約にならない - 構築時間が遅い -- PythonのHNSW実装は、構築速度の点でFAISSに対して競争力がない

面白い副発見: 圧縮された埋め込みキャッシュも構築する(ベクトルをパックされたワイヤ形式で保存する)と、同じRAM予算で約10倍の埋め込みを収められます。Zipf分布に従うアクセスパターンでは、キャッシュヒット率が100MBで約60%から約95%へ跳ね上がります。

回転+量子化のための融合CUDAカーネル(フロート32の中間生成物を書き出さずにインラインで量子化する、タイルドGEMM)もまたうまくいきました。これにより、N*dimのフロート32のラウンドトリップをグローバルメモリに対して1回分まるごと削除できます。

復号なしで、量子化された表現のままANNインデックスを直接操作することを、他の方は試されていますか? 標準的なPQ(プロダクト量子化)以外のアプローチについても知りたいです。PQはサブベクトルに対して動作し、スカラー量子化された“完全なベクトル”ではないからです。

コードは https://github.com/ahb-sjsu/turboquant-pro にあります。見てみたい方はどうぞ。。

submitted by /u/ahbond
[link] [comments]