コンテンツにスキップ

pgvectorの二値量子化、中心化の効果はモデルで変わる

pgvectorの二値量子化で、ベクトルから平均を引く「中心化」を試しました。8モデルを比較した結果、e5ではRecall@10が0.496から0.943へ改善し、snowflakeでは0.959から0.907へ低下しました。同じ前処理でも、モデルによって効果が逆になりました。

Vector Search Checkupでは、ベクトルの方向の偏りや、次元の使われ方を確認できます。今回は、その特徴を検索の設定に生かすため、PostgreSQL上で中心化の有無を比較した結果を紹介します。

二値化する前に、平均を引く

二値量子化(BQ)は、ベクトルの各成分を0か1で表す圧縮方法です。pgvectorの公式ドキュメントでは、二値化したベクトルで候補を集め、元のベクトルで順位を計算し直す検索方法が紹介されています。

候補選びに使うハミング距離は、二つのビット列で値が異なる位置の数です。この距離で多めに候補を取り、最後に元のベクトルのコサイン距離で上位を選びます。

今回比較したのは、そのまま二値化する方法と、平均ベクトルを引いてから二値化する方法です。中心化する側では、文書と検索クエリの両方から同じ平均を引きました。pgvector上で中心化した式索引を作成し、検索に使われることも確認できました。

8モデル、各10万件で比較する

pgvector 0.8.7を使い、8モデルそれぞれで10万件を検索対象にしました。クエリには、検索対象に含めない1,000件のベクトルを使っています。中心化に使う平均は、2,000件の標本から求めました。

HNSWの bit_hamming_ops で上位200件を取り、元のベクトルで並べ直して10件を返します。hnsw.ef_search は200に揃え、ベクトルの回転は加えていません。

評価指標のRecall@10は、元のベクトルを全件検索した上位10件を、どれだけ取得できたかの割合です。ここでは、圧縮と候補の絞り込みによる取りこぼしを測ります。

e5は改善し、snowflakeとnomicは低下した

モデル(実験内の識別名) 中心化なしのRecall@10 中心化ありのRecall@10
e5 0.496 0.943
snowflake 0.959 0.907
bge 0.961 0.971
mxbai 0.988 0.991
qwen3 0.976 0.976
nomic 0.951 0.937
minilm 0.937 0.945
arcface 0.572 0.560

e5では、中心化によって全件検索の上位10件を約94%取得できるようになりました。一方、snowflakeやnomicでは取得率が下がり、残る5モデルの変化はおおむね±0.01程度でした。

この結果から、中心化を一律に適用する方針を見直しました。ベクトルの方向集中度(cos_mean)だけで効果を判定せず、中心化あり・なしの検索結果を比較して選びます。

二値化と近似探索の影響を分けて測る

中心化の効果を調べるには、まず同じ文書とクエリから、中心化あり・なしのビット列を作ります。索引を使わずにハミング距離を全件計算し、同じ件数の候補を元のベクトルで並べ直せば、二値化した候補選択の差を測れます。

次に、その結果をHNSWで候補を選んだ結果と比較します。今回の実験では、索引を使わない厳密なハミング距離の比較でも、e5は改善し、snowflakeは悪化しました。モデルによる効果の違いは、HNSWを外しても残りました。

元のベクトルで並べ直しても、候補から漏れたデータは戻りません。中心化の有無に加え、最初に取る候補数を変えながら、Recallと処理時間を比較することが設定選びにつながります。

PostgreSQLで使える診断へ

PostgreSQLでは、ベクトルの特徴を判定するSQL文や、本格的な拡張にする前の機能づくりを進めています。今回の比較結果を踏まえ、診断では同じ標本で中心化の有無を比べ、その結果を示す方針です。SQLの具体例や処理負荷、式索引の扱いは、別の記事で紹介する予定です。

ベクトル検索 ANNプロジェクトでは、LanceDBの索引や再スコアリングの検証も進めています。実装上の課題や改善提案も、事例ごとに報告していきます。

この一連の研究のきっかけは、1月に取り組んだLSHの実験でした。ベクトルをビット列に変換して検索候補を絞る試みが、今回のPostgreSQLでの量子化の検証につながっていることは、感慨深いです。