QBit データ型は、より高速な近似検索のためにベクトルの格納方法を再構成します。各ベクトルの要素をまとめて格納するのではなく、すべてのベクトルにまたがって同じビット位置ごとにまとめて格納します。
これにより、ベクトルは元の精度のまま保持しつつ、検索時にきめ細かな量子化レベルを選択できます。ビットの読み取り量を減らせば I/O が減って計算が高速になり、より高い精度が必要な場合は多くのビットを読み取れます。量子化によるデータ転送量と計算量の削減による高速化のメリットを得ながら、必要に応じて元のデータをそのまま利用できます。
QBit 型のカラムを宣言するには、次の構文を使用します。
element_type– 各ベクトル要素の型。指定できる型はInt8、BFloat16、Float32、Float64ですdimension– 各ベクトルの次元数stride– 任意。1 つのストリームグループにまとめて格納される次元数です。省略した場合のデフォルトはdimension(単一グループ) です。指定する場合、dimensionはstrideの倍数である必要があり、またstrideがdimensionより小さい場合は、strideは 8 の倍数でなければなりません。dimension個の次元は、連続するdimension / stride個のグループに分割され、各グループのビットプレーンはそれぞれ別のストリームに格納されます。これにより、先頭D次元 (Dはstrideの倍数) を対象とする検索では、それらの次元を含むグループのストリームだけを読み取ればよくなり、Matryoshka embeddings で有用です。
QBit の作成
QBit 型を使用する場合:
配列をQBitに変換する
QBitの次元と一致していれば、配列はQBitに変換されます。配列の要素型は、QBitの要素型と一致している必要はありません。数値型の要素であれば、どの型でも自動的に変換されます。これにより、既存の埋め込みカラムをそのままQBitカラムへ移行できます。
CAST を使って明示的に行うこともできます。たとえば、CAST(embedding AS QBit(Float32, 8)) のように指定します。
QBit を Array に変換する
QBit を Array に CAST すると格納されている値が返されます。これは Array を QBit に変換する の逆です。
QBit の要素型が使用され、その後、各要素は指定された配列の要素型に変換されます。したがって、QBit(Float32, N) から Array(Float64) のように要素型も変更するキャストも可能です。
Array -> QBit -> Array のラウンドトリップは、Int8、Float32、Float64 では情報損失なしで行えます。BFloat16 の場合は、BFloat16 への直接変換と同じ結果になり、失われる精度は BFloat16 自体に起因するものだけです。
dimension が 8 の倍数でない場合、内部表現に含まれる末尾のパディング要素は取り除かれるため、結果には常にちょうど dimension 個の要素が含まれます。
QBit 型間の変換
QBit は、dimension (ベクトル要素数) が同じである限り、別の QBit にキャストできます。element_type と stride はどちらも変更できますが、dimension が異なる QBit にキャストするとベクトル自体が変わってしまうため、例外が発生します。
element_type を変更するとベクトルが再構築され、対応する Array の変換とまったく同じように、各要素が新しい型に変換されます。拡張変換 (たとえば QBit(Float32, N) から QBit(Float64, N)) は正確ですが、縮小変換では、Array の縮小キャストと同様に精度が失われます。
stride のみを変更し (element_type は同じまま) 、値には一切手を加えずに保存済みの ビットプレーン を再グループ化するだけなので、これは常に非破壊です:
QBit サブカラム
QBit はサブカラムアクセスパターンを実装しており、保存されたベクトルの各ビットプレーンに個別にアクセスできます。各ビット位置には .N 構文でアクセスでき、N はビット位置を表します。
Int8: stride group ごとに 8 個のサブカラム (1-8)BFloat16: stride group ごとに 16 個のサブカラム (1-16)Float32: stride group ごとに 32 個のサブカラム (1-32)Float64: stride group ごとに 64 個のサブカラム (1-64)
vec.N は stride group (N-1) / element_size のビットプレーン (N-1) % element_size を読み取ります。たとえば、QBit(BFloat16, 4096, 1024) では 4096 次元が 1024 ごとの 4 つのグループに分割されるため、サブカラムは 64 個あります。vec.1 … vec.16 は最初の stride group (次元 1–1024) のビットプレーン、vec.17 … vec.32 は 2 番目のグループ (次元 1025–2048) に対応し、以下同様です。
Strides
QBit は各ビットプレーンを、すべての dimension 次元にまたがる単一ストリームとして格納するため、検索では常にベクトル全体のビットプレーンを読み取ることになります。省略可能な stride パラメータを使うと、dimension 次元が dimension / stride 個の連続したグループに分割され、各グループのビットプレーンは別々のストリームに格納されます。これにより、先頭から D 次元だけを対象に検索する場合 (D は stride の倍数) 、その次元を含むグループのストリームだけを読み取ればよくなります。これは、先頭の次元だけでも実用的な低次元の埋め込みとして使える Matryoshka embeddings で特に有用です。
BFloat16 (16 個のビットプレーン) の場合、vec.1 … vec.16 は最初の stride group (次元 1–1024) の 16 個のビットプレーン、vec.17 … vec.32 は 2 番目の stride group (次元 1025–2048) に属し、以降も同様です。一般に、vec.N は stride group (N-1) / element_size のビットプレーン (N-1) % element_size を読み取ります。
reduced-dimension search を実行するには、転置された距離関数の第 4 引数として、読み取る次元数を渡します (下記参照) 。参照ベクトルには、少なくともその数の要素が含まれている必要があり (末尾に余分な要素があっても無視されます) 、この値は stride の倍数でなければなりません。
ベクトル検索関数
QBit データ型を使用する距離関数です。
ストライド化された QBit の場合、これらの関数はオプションの第4引数 used_dims (読み取る先頭の次元数) を受け付け、指定した次元をカバーする stride group のみを読み取ります。参照ベクトルは少なくとも used_dims 個の要素を持っている必要があり (余分な末尾要素は無視されるため、フルサイズのクエリベクトルを事前にスライスしなくても reduced-dimension search に再利用できます)、used_dims は stride の倍数でなければなりません。