ベクトル検索
ベクトル検索では、意味的類似性を使用して、クエリにすべてのキーワードが明示的に含まれていない場合でも、最も関連性の高いレコードを見つけやすくします。
注記:
ベクトル検索の完全な例については、 ベクトル検索の例参照してください。
基本的な使い方
このセクションでは、アプリケーションでベクトル検索を使用する方法を数ステップで説明します。始める前に、 データベースに接続する .
ステップ1. ベクトルフィールドを持つテーブルを作成する
client.create_table()使用してテーブルを作成し、 VectorField使用してベクトル フィールドを定義できます。
次の例では、4 つの列を持つdocumentsテーブルを作成します。
id: テーブルの主キー。text: ドキュメントのテキストコンテンツ。text_vec: テキスト コンテンツのベクトル埋め込み。meta: ドキュメントのメタデータ (JSON オブジェクト)。
from pytidb.schema import TableModel, Field, VectorField
from pytidb.datatype import TEXT, JSON
class Document(TableModel):
__tablename__ = "documents"
id: int = Field(primary_key=True)
text: str = Field(sa_type=TEXT)
text_vec: list[float] = VectorField(dimensions=3)
meta: dict = Field(sa_type=JSON, default_factory=dict)
table = client.create_table(schema=Document, if_exists="overwrite")
VectorFieldクラスは次のパラメータを受け入れます。
dimensions: ベクトルの次元。指定すると、このフィールドには指定した次元のベクトルのみが格納されます。index: ベクトルフィールドにベクトルインデックスを作成するかどうか。デフォルトはTrueです。distance_metric: ベクトルインデックスに使用する距離メトリック。サポートされる値:DistanceMetric.COSINE(デフォルト): コサイン距離メトリック。テキストの類似性を測定するのに適しています。DistanceMetric.L2: L2距離メトリック、全体的な差異を捉えるのに適しています
CREATE TABLEステートメントを使用してテーブルを作成し、 VECTOR型を使用してベクトル列を定義します。
CREATE TABLE documents (
id INT PRIMARY KEY,
text TEXT,
text_vec VECTOR(3),
VECTOR INDEX `vec_idx_text_vec`((VEC_COSINE_DISTANCE(`text_vec`)))
);
この例では、
text_vec列目はVECTOR(3)として定義されているため、この列に格納されるベクトルは 3 次元である必要があります。- ベクトル検索のパフォーマンスを最適化するために、
VEC_COSINE_DISTANCE関数を使用してベクトル インデックスが作成されます。
TiDB はベクトル インデックスに対して 2 つの距離関数をサポートしています。
VEC_COSINE_DISTANCE: 2つのベクトル間のコサイン距離を計算するVEC_L2_DISTANCE: 2つのベクトル間のL2距離(ユークリッド距離)を計算する
ステップ2. テーブルにベクターデータを挿入する
デモンストレーションとして、いくつかのテキストとそれに対応する埋め込みをテーブルに挿入します。
次の例では、それぞれ単純な 3 次元ベクトル埋め込みを持つ 3 つのドキュメントを挿入します。
dogベクトル埋め込み[1, 2, 1]fishベクトル埋め込み[1, 2, 4]treeベクトル埋め込み[1, 0, 0]
table.bulk_insert([
Document(text="dog", text_vec=[1,2,1], meta={"category": "animal"}),
Document(text="fish", text_vec=[1,2,4], meta={"category": "animal"}),
Document(text="tree", text_vec=[1,0,0], meta={"category": "plant"}),
])
INSERT INTO documents (id, text, text_vec, meta)
VALUES
(1, 'dog', '[1,2,1]', '{"category": "animal"}'),
(2, 'fish', '[1,2,4]', '{"category": "animal"}'),
(3, 'tree', '[1,0,0]', '{"category": "plant"}');
注記:
実際のアプリケーションでは、埋め込みは通常埋め込みモデルによって生成されます。
利便性のため、pytidb は、挿入、更新、または検索時にテキスト フィールドのベクトル埋め込みを自動的に生成できる自動埋め込み機能を提供します。手動処理は必要ありません。
詳細は自動埋め込みガイドをご覧ください。
ステップ3. ベクトル検索を実行する
ベクトル検索では、ベクトル距離指標を用いてベクトル間の類似性と関連性を測定します。距離が近いほど、レコードの関連性が高くなります。テーブル内で最も関連性の高いドキュメントを見つけるには、クエリベクトルを指定する必要があります。
次の例では、クエリがA swimming animalで、そのベクトル埋め込みが[1, 2, 3]あると想定しています。
ベクトル検索を実行するにはtable.search()メソッドを使用します。デフォルトではsearch_mode="vector"が使用されます。
table.search([1, 2, 3]).limit(3).to_list()
[
{"id": 2, "text": "fish", "text_vec": [1,2,4], "_distance": 0.00853986601633272},
{"id": 1, "text": "dog", "text_vec": [1,2,1], "_distance": 0.12712843905603044},
{"id": 3, "text": "tree", "text_vec": [1,0,0], "_distance": 0.7327387580875756},
]
結果によると、最も関連性の高いドキュメントは距離が0.00853986601633272のfishです。
クエリ ベクトルのn最も近い近傍を取得するには、 SELECTステートメントのORDER BY <distance_function>(<column_name>, <query_vector>) LIMIT <n>句を使用します。
次の例では、 vec_cosine_distance関数を使用して、 text_vec列に格納されているベクトルと指定されたクエリ ベクトル[1, 2, 3]間のコサイン距離を計算します。
SELECT id, text, vec_cosine_distance(text_vec, '[1,2,3]') AS distance
FROM documents
ORDER BY distance
LIMIT 3;
+----+----------+---------------------+
| id | text | distance |
+----+----------+---------------------+
| 2 | fish | 0.00853986601633272 |
| 1 | dog | 0.12712843905603044 |
| 3 | tree | 0.7327387580875756 |
+----+----------+---------------------+
3 rows in set (0.15 sec)
結果によると、最も関連性の高いドキュメントは距離が0.00853986601633272のfishです。
距離測定
距離メトリクスは、ベクトルのペア間の類似度を測る尺度です。現在、TiDBは以下の距離メトリクスをサポートしています。
table.search() API は次の距離メトリックをサポートしています。
| メトリック名 | 説明 | 最適な用途 |
|---|---|---|
DistanceMetric.COSINE | 2つのベクトル間のコサイン距離を計算します(デフォルト)。ベクトル間の角度を測定します。 | テキスト埋め込み、セマンティック検索 |
DistanceMetric.L2 | 2つのベクトル間のL2距離(ユークリッド距離)を計算します。直線距離を測定します。 | 画像の特徴 |
ベクトル検索に使用する距離メトリックを変更するには、 .distance_metric()メソッドを使用します。
例: L2距離メトリックを使用する
from pytidb.schema import DistanceMetric
results = (
table.search([1, 2, 3])
.distance_metric(DistanceMetric.L2)
.limit(10)
.to_list()
)
SQL では、次の組み込み関数を使用して、クエリ内でベクトル距離を直接計算できます。
| 関数名 | 説明 |
|---|---|
VEC_L2_DISTANCE | 2つのベクトル間のL2距離(ユークリッド距離)を計算します |
VEC_COSINE_DISTANCE | 2つのベクトル間のコサイン距離を計算します |
VEC_NEGATIVE_INNER_PRODUCT | 2つのベクトルの内積の負数を計算します |
VEC_L1_DISTANCE | 2つのベクトル間のL1距離(マンハッタン距離)を計算します |
距離閾値
table.search() APIでは、距離の閾値を設定して、返される結果の類似度を制御できます。この閾値を指定することで、類似度の低いベクトルを除外し、関連性基準を満たすベクトルのみを返すことができます。
.distance_threshold()メソッドを使用して、検索結果の最大距離を設定します。しきい値未満の距離を持つレコードのみが返されます。
例: 距離が 0.5 未満のドキュメントのみを返す
results = table.search([1, 2, 3]).distance_threshold(0.5).limit(10).to_list()
SQL では、距離関数を含むHAVING句を使用して、距離で結果をフィルタリングします。
例: 距離が 0.1 未満のドキュメントのみを返す
SELECT id, text, vec_cosine_distance(text_vec, '[1,2,3]') AS distance
FROM documents
HAVING distance < 0.1
ORDER BY distance
LIMIT 10;
距離範囲
table.search() API では、結果をさらに絞り込むために距離範囲を指定することもサポートされています。
最小距離と最大距離の両方を設定するには、 .distance_range()メソッドを使用します。この範囲内の距離を持つレコードのみが返されます。
例: 距離が 0.01 から 0.05 までのドキュメントのみを返す
results = table.search([1, 2, 3]).distance_range(0.01, 0.05).limit(10).to_list()
SQL で距離の範囲を指定するには、 HAVING句でBETWEENまたはその他の比較演算子を使用します。
例: 距離が 0.01 から 0.05 までのドキュメントのみを返す
SELECT id, text, vec_l2_distance(text_vec, '[1,2,3]') AS distance
FROM documents
HAVING distance BETWEEN 0.01 AND 0.05
ORDER BY distance
LIMIT 10;
メタデータフィルタリング
リレーショナル データベースである TiDB は、豊富なセットSQL演算子をサポートし、フィルタリング条件の柔軟な組み合わせを可能にします。
TiDB でのベクトル検索では、スカラー フィールド (整数や文字列など) または JSON フィールドにメタデータ フィルタリングを適用できます。
通常、メタデータ フィルタリングと組み合わせたベクター検索には、次の 2 つのモードがあります。
- 後フィルタリング:TiDBはまずベクトル検索を実行し、ベクトル空間全体から上位k個の候補を取得し、その候補セットにフィルターを適用します。ベクトル検索段階では、効率性を高めるため、通常、ベクトルインデックスが使用されます。
- 事前フィルタリング:TiDBはベクター検索の前にフィルタを適用します。フィルタの選択性が高く、フィルタリング対象フィールドにスカラーインデックスがある場合、このモードにより検索空間が縮小され、パフォーマンスが向上します。
後フィルタリング
フィルター辞書を持つ.filter()メソッドを使用して、ベクトル検索にフィルターを適用します。
デフォルトでは、 table.search() API はポストフィルタリング モードを使用して、ベクター インデックスによる検索パフォーマンスを最大化します。
例: ポストフィルタリングによるベクトル検索
results = (
table.search([1, 2, 3])
# The `meta` is a JSON field, and its value is a JSON object
# like {"category": "animal"}
.filter({"meta.category": "animal"})
.num_candidate(50)
.limit(10)
.to_list()
)
注記:
ベクトルインデックスを使用する場合、最後の
limitが非常に小さいと結果の精度が低下する可能性があります。3.num_candidate()の方法を使用すると、ベクトル検索フェーズでベクトルインデックスから取得する候補の数を、limit番目のパラメータを変更せずに制御できます。
num_candidate値を大きくすると、一般的に再現率は向上しますが、クエリのパフォーマンスが低下する可能性があります。データセットと精度要件に応じてこの値を調整してください。
現在、ベクトル インデックスは、次のような厳密な ANN (近似最近傍) クエリでのみ有効です。
SELECT * FROM <table> ORDER BY <distance_func>(<column>) LIMIT <n>
つまり、同じクエリ内でWHERE句とベクトル インデックスを一緒に使用することはできません。
ベクトル検索と追加のフィルタリング条件を組み合わせる必要がある場合は、ポストフィルタリングパターンを使用できます。このアプローチでは、ANNクエリは2つの部分に分割されます。
- 内部クエリは、ベクトル インデックスを使用してベクトル検索を実行します。
- 外側のクエリは
WHERE条件を適用して結果をフィルタリングします。
SELECT *
FROM (
SELECT id, text, meta, vec_cosine_distance(text_vec, '[1,2,3]') AS distance
FROM documents
ORDER BY distance
LIMIT 50
) candidates
WHERE meta->>'$.category' = 'animal'
ORDER BY distance
LIMIT 10;
注記:
後続フィルタリングパターンにより、結果が空になる場合があります。例えば、内部クエリで最も類似度の高い上位50件のレコードが取得されるものの、条件
WHEREに一致するレコードが1件も存在しない場合などです。これを軽減するには、内部クエリの
LIMIT値 (例: 50) を増やして、より多くの候補を取得し、フィルタリング後に十分な有効な結果が返される可能性を高めることができます。
サポートされている SQL 演算子については、 TiDB Cloudドキュメントのオペレーター参照してください。
プレフィルタリング
事前フィルタリングを有効にするには、 .filter()方法でprefilter=True設定します。
例: 事前フィルタリングによるベクトル検索
results = (
table.search([1, 2, 3])
.filter({"meta.category": "animal"}, prefilter=True)
.limit(10)
.to_list()
)
サポートされているフィルター演算子については、 フィルタリング参照してください。
SQL では、 ->>演算子またはJSON_EXTRACTを使用して、 WHERE句の JSON フィールドにアクセスします。
SELECT id, text, meta, vec_cosine_distance(text_vec, '[1,2,3]') AS distance
FROM documents
WHERE meta->>'$.category' = 'animal'
ORDER BY distance
LIMIT 10;
サポートされている SQL 演算子については、 TiDB Cloudドキュメントのオペレーター参照してください。
複数のベクトル場
TiDB は、単一のテーブルに複数のベクトル列を定義することをサポートしており、さまざまな種類のベクトル埋め込みを保存および検索できます。
たとえば、テキスト埋め込みと画像埋め込みの両方を同じテーブルに保存できるため、マルチモーダル データの管理に便利です。
スキーマ内に複数のベクトル フィールドを定義し、 .vector_column()メソッドを使用して指定されたベクトル フィールドに対してベクトル検索を実行できます。
例: 検索するベクトル場を指定する
# Create a table with multiple vector fields
class RichTextDocument(TableModel):
__tablename__ = "rich_text_documents"
id: int = Field(primary_key=True)
text: str = Field(sa_type=TEXT)
text_vec: list[float] = VectorField(dimensions=3)
image_url: str
image_vec: list[float] = VectorField(dimensions=3)
table = client.create_table(schema=RichTextDocument, if_exists="overwrite")
# Insert sample data ...
# Search using image vector field
results = (
table.search([1, 2, 3])
.vector_column("image_vec")
.distance_metric(DistanceMetric.COSINE)
.limit(10)
.to_list()
)
テーブル内に複数のベクトル列を作成し、適切な距離関数を使用してそれらを検索することができます。
-- Create a table with multiple vector fields
CREATE TABLE rich_text_documents (
id BIGINT PRIMARY KEY,
text TEXT,
text_vec VECTOR(3),
image_url VARCHAR(255),
image_vec VECTOR(3)
);
-- Insert sample data ...
-- Search using text vector
SELECT id, image_url, vec_l2_distance(image_vec, '[4,5,6]') AS image_distance
FROM rich_text_documents
ORDER BY image_distance
LIMIT 10;
検索結果を出力する
table.search() API を使用すると、検索結果をいくつかの一般的なデータ処理形式に変換できます。
SQLAlchemyの結果行として
生の SQLAlchemy 結果行を操作するには、次を使用します。
table.search([1, 2, 3]).limit(10).to_rows()
Python辞書のリストとして
Python での操作を容易にするために、結果を辞書のリストに変換します。
table.search([1, 2, 3]).limit(10).to_list()
pandas DataFrameとして
結果をユーザーフレンドリーな表で表示するには (特に Jupyter ノートブックで便利です)、結果を pandas DataFrame に変換します。
table.search([1, 2, 3]).limit(10).to_pandas()
Pydanticモデルインスタンスのリストとして
TableModelクラスは、データエンティティを表す Pydantic モデルとしても使用できます。結果を Pydantic モデルインスタンスとして操作するには、以下を使用します。
table.search([1, 2, 3]).limit(10).to_pydantic()