# ハイブリッド検索

注意

このガイドは、DBバージョン1.5.0以降にのみ適用されます。

このガイドでは、MyScaleでの全文検索とベクトル検索のハイブリッド検索の方法について説明します。

全文検索ベクトル検索は、それぞれ独自の強みと弱点があります。全文検索は基本的なキーワードの検索とテキストのマッチングに適しており、ベクトル検索はドキュメント間の意味的なマッチングや深い意味の理解に優れていますが、短いテキストクエリでは効率が低下する場合があります。両方のアプローチの利点を活用するために、ハイブリッド検索が開発されました。全文検索のインデックスとベクトル検索の特性を組み合わせることで、さまざまなテキスト検索の要件に効果的に対応し、テキストの検索精度と速度を向上させ、正確な結果を効率的に提供することができます。

# チュートリアルの概要

このドキュメントでは、次の3つの検索実験をガイドします:

MyScaleでのベクトル検索

すべてのドキュメントとクエリテキストは、all-MiniLM-L6-v2 (opens new window)モデルを使用してベクトルを生成し、類似度の測定にはコサイン距離を使用します。

MyScaleでのテキスト検索

MyScaleは、全文検索(FTS)のためのBM25インデックスを有効にするためにTantivy (opens new window)ライブラリを統合しています。テーブル内のすべてのテキストデータをインデックス化することにより、MyScaleのFTS機能はBM25スコアに基づいて検索結果をランク付けし、より関連性の高い正確な検索体験を提供します。

RRF戦略を使用してベクトル検索とテキスト検索の結果を結合する

ベクトル検索とテキスト検索の結果を結合するために、広く使用されているPythonライブラリranx (opens new window)を使用します。この結合により、さまざまなシナリオでのテキスト検索の適応性が向上し、検索精度が最終的に向上します。

MyScaleにおけるハイブリッド検索のイラスト

開始する前に、MyScaleクラスターが設定されていることを確認してください。セットアップ手順については、クイックスタートガイド (opens new window)を参照してください。

# データセット

この実験では、RediSearchが提供するWikipediaの要約データセット (opens new window)を使用しました。このデータセットには5,622,309のドキュメントエントリが含まれています。テキストはall-MiniLM-L6-v2 (opens new window)モデルを使用して生成され、body_vector列に格納された384次元のベクトルとして保存されます。ベクトル間の類似度はコサイン距離を使用して計算されます。

TIP

all-MiniLM-L6-v2の使用方法の詳細については、HuggingFaceのドキュメント (opens new window)を参照してください。

データセットwiki_abstract_with_vector.parquet (opens new window)のサイズは8.2GBです。後続の実験では、このデータセットを直接S3経由でMyScaleにインポートするため、ローカルにダウンロードする必要はありません。

id body title url body_vector
... ... ... ... ...
77 Jake Rodkin is an American .... and Puzzle Agent. Jake Rodkin https://en.wikipedia.org/wiki/Jake_Rodkin (opens new window) [-0.081793934,....,-0.01105572]
78 Friedlandpreis der Heimkehrer is ... of Germany. Friedlandpreis der Heimkehrer https://en.wikipedia.org/wiki/Friedlandpreis_der_Heimkehrer (opens new window) [0.018285718,...,0.03049711]
... ... ... ... ...

# テーブルの作成とデータのインポート

MyScaleのSQLワークスペースでwiki_abstract_5mというテーブルを作成するには、次のSQLステートメントを実行します。

CREATE TABLE default.wiki_abstract_5m(
    `id` UInt64,
    `body` String,
    `title` String,
    `url` String,
    `body_vector` Array(Float32),
    CONSTRAINT check_length CHECK length(body_vector) = 384
)
ENGINE = MergeTree
ORDER BY id
SETTINGS index_granularity = 128;

S3からデータをテーブルにインポートします。データのインポートプロセス中は、お待ちください。

INSERT INTO default.wiki_abstract_5m
SELECT * FROM s3('https://myscale-datasets.s3.ap-southeast-1.amazonaws.com/wiki_abstract_with_vector.parquet', 'Parquet');

注意

データのインポートには約10分かかる見込みです。

次のクエリを実行して、テーブルに5,622,309行のデータがあるかどうかを確認します。

SELECT count(*) FROM default.wiki_abstract_5m;

出力:

count()
5622309

ベクトル検索のパフォーマンスを向上させるために、テーブルのデータパーツを1つのパーツにマージするためにテーブルを最適化する(オプション)には、次のSQLコマンドをSQLワークスペースで実行します。

OPTIMIZE TABLE default.wiki_abstract_5m FINAL;

この最適化ステップには時間がかかる場合があります。

次のクエリを実行して、このテーブルのデータパーツが1つのパーツに圧縮されたかどうかを確認します。

SELECT COUNT(*) FROM system.parts WHERE table='wiki_abstract_5m' AND active=1;

成功した場合、次のように表示されます。

count()
1

# インデックスの作成

# FTSインデックスの作成

TIP

FTSインデックスの作成方法については、FTSインデックスのドキュメントを参照してください。

FTSインデックスを設定する際に、ユーザーはトークナイザーをカスタマイズするオプションがあります。この例では、stemトークナイザーを使用し、stop wordsを適用します。stemトークナイザーは、テキスト内の単語の時制を無視してより正確な検索結果を得ることができます。stop wordsを使用することで、「a」、「an」、「of」、「in」などの一般的な単語をフィルタリングして検索の精度を向上させることができます。

ALTER TABLE default.wiki_abstract_5m
ADD INDEX body_idx (body)
TYPE fts('{"body":{"tokenizer":{"type":"stem", "stop_word_filters":["english"]}}}');

インデックスのマテリアライズを実行します。

ALTER TABLE default.wiki_abstract_5m MATERIALIZE INDEX body_idx;

# ベクトルインデックスの作成

TIP

MSTGベクトルインデックスについての詳細は、ベクトル検索のドキュメントを参照してください。

default.wiki_abstract_5mテーブルのbody_vec_idxにCosineを距離計算方法として使用してMSTGベクトルインデックスを構築するには、次のSQLステートメントを実行します。

ALTER TABLE default.wiki_abstract_5m
ADD VECTOR INDEX body_vec_idx body_vector
TYPE MSTG('metric_type=Cosine');

ベクトルインデックスの構築には時間がかかる場合があります。進行状況を監視するために、次のSQLクエリを実行します。ステータス列が「Built」になっている場合、インデックスが正常に作成されたことを意味します。進行中の場合は「InProgress」と表示されるはずです。

SELECT * FROM system.vector_indices;

# ハイブリッド検索の実行

# 事前準備

クエリテキストをベクトルに変換するために、all-MiniLM-L6-v2 (opens new window)モデルを使用します。

まず、次のコマンドを実行してsentence-transformersライブラリをインストールします。

pip install -U sentence-transformers

次に、次のコマンドを実行してranx (opens new window)ライブラリをインストールします。

pip install -U ranx

ハイブリッド検索の実践は、Pythonファイルで実装されます。この実践を進める前に、提供されたサンプルコードにMyScaleクラスターの情報を入力してください。

TIP

MyScaleクラスターの接続詳細は、Webコンソールで確認できます。

import clickhouse_connect
from numba import NumbaTypeSafetyWarning
from prettytable import PrettyTable
from ranx import Run, fuse
from ranx.normalization import rank_norm
from sentence_transformers import SentenceTransformer
import warnings
warnings.filterwarnings('ignore', category=NumbaTypeSafetyWarning)
# Use transfromer all-MiniLM-L6-v2
model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
# MyScale information
host = "your cluster end-point"
port = 443
username = "your user name"
password = "your password"
database = "default"
table = "wiki_abstract_5m"
# Init MyScale client
client = clickhouse_connect.get_client(host=host, port=port,
                                       username=username, password=password)
# Use a table to output your content
def print_results(result_rows, field_names):
    x = PrettyTable()
    x.field_names = field_names
    for row in result_rows:
        x.add_row(row)
    print(x)

# ベクトル検索の実行

TIP

British Graham Land expedition(BGLE)は、イギリスの探検隊です。詳細については、Wikipedia (opens new window)を参照してください。

このデータセットで、BGLE探検隊が訪れ、マッピングした場所を特定することを目指します。そのために、ベクトル検索クエリを実行する際に「"Charted by BGLE"」を検索文字列として使用します。

TIP

前の準備作業中に作成したPythonファイルに、このメッセージで提供されたサンプルコードを含めて、プログラムのスムーズな実行を確保してください。また、他のステップも同様の方法で処理するようにしてください。

terms = "Charted by BGLE"
terms_embedding = model.encode([terms])[0]
vector_search = f"SELECT id, title, body, distance('alpha=3')" \
                f"(body_vector,{list(terms_embedding)}) AS distance FROM {database}.{table} " \
                f"ORDER BY distance ASC LIMIT 100"
vector_search_res = client.query(query=vector_search)
print("\nVectorSearch results:")
print_results(vector_search_res.result_rows[:10], ["ID", "Title", "Body", "Distance"])

ベクトル検索によって取得されたドキュメントは、トップ10の結果に「Charted by BGLE」と関連する情報は含まれていません。ベクトル検索は、短いテキストクエリに対しては性能が低下する傾向があるためです。

ID Title Body Distance
1177089 B.G. discography This is the discography of B.G. 0.44516414403915405
2710950 Ruy Blas and the Blasé Roué Ruy Blas and the Blasé Roué is a burlesque written by A. C. 0.4736078381538391
4571962 BLL BLL may refer to: 0.48070353269577026
3797998 Bles Bles may refer to: 0.4849556088447571
4558187 The Complete Blam Blam Blam The Complete Blam Blam Bam is a compilation LP by Blam Blam Blam. It was released on November, 1992. 0.4863016605377197
5556733 BLG BLG may refer to: 0.49544525146484375
4591273 Blagg Blagg may refer to: 0.5007722973823547
3087541 Paul Bley discography This is the discography for Canadian jazz musician Paul Bley. 0.5030192136764526
3389086 Blot (album) Blot is the fourth full-length album by the Norwegian black/Viking metal band Einherjer. It was released on 21 November 2003 by Tabu Recordings. 0.5040180087089539
1876940 Blåljus! Blåljus! is a six-volume light pocket series of novels by Margit Sandemo and published by the Swedish subsidiade Boknöje AB of the Norwegian publication Bladkompaniet in year 2004. 0.5080643892288208

# テキスト検索の実行

MyScaleのTextSearch()関数を使用して、関連する結果セットとBM25スコアを取得します。後で結果の結合を容易にするために、このドキュメントデータを一時的に保存する必要があります。

text_search = f"SELECT id, title, body, TextSearch(body, '{terms}') AS score " \
              f"FROM {database}.{table} " \
              f"ORDER BY score DESC LIMIT 100"
text_search_res = client.query(query=text_search)
# 検索結果を一時的に保存します。
stored_data = {}
for row in vector_search_res.result_rows:
    stored_data[str(row[0])] = {"title": row[1], "body": row[2]}
for row in text_search_res.result_rows:
    if str(row[0]) not in stored_data:
        stored_data[str(row[0])] = {"title": row[1], "body": row[2]}

# ベクトル検索とテキスト検索の結果を結合するためにRRF戦略を使用する

ranx (opens new window)ライブラリを使用して、ベクトル検索とテキスト検索の結果を結合し、検索精度を向上させるためにRRF(Reciprocal Rank Fusion)結合戦略を実装します。他の結合戦略については、ranx/fusion (opens new window)を参照してください。

# クエリ結果からidとスコアを抽出します。
bm25_dict = {"query-0": {str(row[0]): float(row[3]) for row in text_search_res.result_rows}}
# ranxライブラリでは、スコアが高いほど関連性が高いとされるため、CosineやL2などのベクトル距離計算方法の場合は前処理が必要です。
max_value = max(float(row[3]) for row in vector_search_res.result_rows)
vector_dict = {"query-0": {str(row[0]): max_value - float(row[3]) for row in vector_search_res.result_rows}}
# クエリ結果のスコアを正規化します。
vector_run = rank_norm(Run(vector_dict, name="vector"))
bm25_run = rank_norm(Run(bm25_dict, name="bm25"))
# RRFを使用してクエリ結果を結合します。
combined_run = fuse(
    runs=[vector_run, bm25_run],
    method="rrf",
    params={'k': 10}
)
print("\nFusion results:")
pretty_results = []
for id_, score in combined_run.get_doc_ids_and_scores()[0].items():
    if id_ in stored_data:
        pretty_results.append([id_, stored_data[id_]["title"], stored_data[id_]["body"], score])
print_results(pretty_results[:10], ["ID", "Title", "Body", "Score"])

結合クエリの結果は次のとおりです。ハイブリッド検索は、BGLE探検隊がチャートされた場所に関連する5つの記事を正確に一致させ、短いテキストクエリの処理におけるハイブリッド検索の利点を示しています。

ID Title Body Score
200245 Salmon Island Salmon Island () is the westernmost of the Fish Islands, lying off the west coast of Graham Land. Charted by the British Graham Land Expedition (BGLE) under Rymill, 1934-37. 0.09090909090909091
1177089 B.G. discography This is the discography of B.G. 0.09090909090909091
5024941 Woozle Hill Woozle Hill () is a hill near the center of Galindez Island, in the Argentine Islands in the Wilhelm Archipelago. First charted by the British Graham Land Expedition (BGLE) under Rymill, 1934-37. 0.08333333333333333
2710950 Ruy Blas and the Blasé Roué Ruy Blas and the Blasé Roué is a burlesque written by A. C. 0.08333333333333333
4571962 BLL BLL may refer to: 0.07692307692307693
4426976 Symington Islands Symington Islands () is a group of small islands lying west-northwest of Lahille Island, in the Biscoe Islands. Charted by the British Graham Land Expedition (BGLE) under Rymill, 1934-37. 0.07692307692307693
3797998 Bles Bles may refer to: 0.07142857142857142
197443 Tadpole Island Tadpole Island () is an island just north of Ferin Head, off the west coast of Graham Land. Charted by the British Graham Land Expedition (BGLE) under Rymill, 1934-37. 0.07142857142857142
202128 Sohm Glacier Sohm Glacier () is a glacier flowing into Bilgeri Glacier on Velingrad Peninsula, the west coast of Graham Land. Charted by the British Graham Land Expedition (BGLE) under Rymill, 1934-37. 0.06666666666666667
4558187 The Complete Blam Blam Blam The Complete Blam Blam Bam is a compilation LP by Blam Blam Blam. It was released on November, 1992. 0.06666666666666667

# 結論

このドキュメントでは、テキスト検索シナリオでのMyScaleハイブリッド検索の使用方法についての洞察を提供し、非構造化テキストデータの検索における方法と技術に焦点を当てました。実践的な演習では、Wikipediaの要約を使用して例を開発しました。MyScaleの高度な全文検索とベクトル検索の機能を使用することで、ハイブリッド検索はキーワードと意味情報の両方を組み合わせることで、より正確な結果を提供します。

Last Updated: Mon Apr 29 2024 08:19:07 GMT+0000