メインコンテンツまでスキップ

BM25 関数

BM25 関数は、生テキストをスパースベクトルに変換し、語彙的関連性に基づいてドキュメントをスコアリングすることで、全文検索を可能にします。用語ベースのマッチングと頻度を考慮した重み付けを適用し、クエリ語に密接に一致するテキストドキュメントを効率的に取得できるようにします。

ローカルテキスト関数として、BM25 関数は Zilliz Cloud 内で実行され、モデル推論や外部連携を必要としません。テキストベースの検索シナリオに対して、決定論的で透過的な取得メカニズムを提供します。

BM25 の仕組み​

BM25 アルゴリズムは、全文検索で広く使用されている用語ベースの関連性スコアリングアルゴリズムです。Zilliz Cloud では、BM25 はテキストを用語重み表現に変換し、分散スパースインデックスを使用して上位 K 件のドキュメントを取得するスパース検索パイプラインとして実装されています。

全体のワークフローは、ドキュメント取り込みとクエリテキスト処理という 2つの対称的な経路で構成され、どちらも同じテキスト解析ロジックを共有します。

ドキュメント取り込み: テキストからスパース表現へ​

ドキュメントが挿入されると、その生テキストはまず アナライザー によって処理され、個々の用語にトークン化されます。

たとえば、次のドキュメント:

plaintext
"We are loving Milvus!"

は、次の用語に解析されます:

plaintext
["we", "love", "milvus"]

その後、各ドキュメントは、そのドキュメント内で各用語が何回出現するかを記録する用語頻度(TF)表現として表されます。たとえば:

plaintext
{
"we": 1,
"love": 1,
"milvus": 1
}

同時に、Zilliz Cloud は次のものを含むコーパスレベルの統計情報を更新します:

  • 各用語のドキュメント頻度(DF)

  • ドキュメントの平均長

  • 各用語を、その用語を含むドキュメントに対応付けるポスティングリスト

ドキュメントの TF 表現はスパース埋め込みに挿入され、用語のポスティングはスケーラブルな検索のためにノード間で分割されます。

クエリテキストの処理: IDF の重み付けを適用する​

テキストベースのクエリが発行されると、ドキュメント取り込み時に使用された同じアナライザーによって処理され、一貫した用語分割が保証されます。

たとえば、次のクエリ:

plaintext
"who loves Milvus?"

は、次のように解析されます:

plaintext
["who", "love", "milvus"]

各クエリ用語について、Zilliz Cloud はコーパス統計からその逆文書頻度(IDF)を参照します。IDF は、データセット全体を通じてその用語がどれだけ情報量を持つかを反映します。希少な用語ほど高い重みを受け、一般的な用語ほど低い重みを受けます。

概念的には、これにより次のような IDF で重み付けされたクエリ用語のセットが生成されます:

plaintext
{
"who": 0.1,
"love": 0.5,
"milvus": 1.2
}

BM25 スコアリングと上位 K 件の取得​

BM25 は、一致したクエリ用語に基づいて関連性スコアを計算することにより、ドキュメントを順位付けします。スコアリングは用語レベルで実行され、ドキュメントレベルで集約されます。

用語レベルのスコアリング

ドキュメントに出現する各クエリ用語について、BM25 は用語レベルのスコアを計算します:

plaintext
term_score =
IDF(term) ×
TF_boost(term, document, k1) ×
length_normalization(document, b)

ここで:

  • IDF(term) は、その用語がコレクション内でどれだけ希少であるかを反映します

  • TF_boost(…, k1) は用語頻度とともに増加しますが、頻度が高くなるにつれて飽和します

  • length_normalization(…, b) はドキュメント長に基づいてスコアを調整します

ドキュメントレベルのスコアリングと上位 K 件の取得

最終的なドキュメントスコアは、一致したすべてのクエリ用語に対する用語レベルスコアの合計です:

plaintext
document_score =
sum of term_score over all matched query terms

ドキュメントは最終スコアによって順位付けされ、スコアの高い上位 K 件のドキュメントが返されます。

事前準備​

BM25 関数を使用する前に、語彙ベースの全文検索をサポートできるようにコレクションスキーマを計画してください:

  • 生コンテンツ用のテキストフィールド

    コレクションには、生テキストを格納するための VARCHAR フィールドを含める必要があります。このフィールドは、全文検索のために処理されるテキストのソースです。

  • テキストフィールド用のアナライザー

    テキストフィールドではアナライザーを有効にする必要があります。アナライザーは、BM25 関数によって語彙的関連性が計算される前に、テキストがどのようにトークン化および正規化されるかを定義します。

    デフォルトでは、Zilliz Cloud は空白と句読点に基づいてテキストをトークン化する組み込みのアナライザーを提供します。アプリケーションでカスタムのトークン化や正規化の動作が必要な場合は、カスタムアナライザーを定義できます。詳細については、ユースケースに適したアナライザーを選ぶを参照してください。

  • BM25 出力用のスパースベクトル

    コレクションには、BM25 関数によって生成されるスパース表現を格納するための SPARSE_FLOAT_VECTOR フィールドを含める必要があります。このフィールドは、全文検索時のインデックス作成と取得に使用されます。

これらのスキーマレベルの検討事項を整理したら、コレクションの作成に進み、BM25 関数を使用します。

ステップ 1: BM25 関数を使用してコレクションを作成する​

BM25 関数を使用するには、コレクションの作成時にそれを定義する必要があります。関数はコレクションスキーマの一部となり、データの挿入時と検索時に自動的に適用されます。

SDK を使用する場合​

スキーマフィールドを定義する​

コレクションスキーマには、少なくとも次の 3つの必須フィールドを含める必要があります:

  • プライマリフィールド: コレクション内の各エンティティを一意に識別します。

  • テキストフィールド(VARCHAR): 生のテキストドキュメントを格納します。Zilliz Cloud が BM25 の関連性ランキングのためにテキストを処理できるよう、enable_analyzer=True を設定する必要があります。デフォルトでは、Zilliz Cloud はテキスト解析に standard アナライザー を使用します。別のアナライザーを設定するには、アナライザーの概要を参照してください。

  • スパースベクトルフィールド(SPARSE_FLOAT_VECTOR): BM25 関数によって自動生成されるスパース埋め込みを格納します。

python
from pymilvus import MilvusClient, DataType, Function, FunctionType

client = MilvusClient(
uri="YOUR_CLUSTER_ENDPOINT",
token="YOUR_CLUSTER_TOKEN"
)

schema = client.create_schema()

schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True, auto_id=True) # Primary field
schema.add_field(field_name="text", datatype=DataType.VARCHAR, max_length=1000, enable_analyzer=True) # Text field
schema.add_field(field_name="sparse", datatype=DataType.SPARSE_FLOAT_VECTOR) # Sparse vector field; no dim required for sparse vectors
plaintext
#include "milvus/MilvusClientV2.h"

auto client = milvus::MilvusClientV2::Create();

milvus::ConnectParam connect_param{"YOUR_CLUSTER_ENDPOINT", "YOUR_CLUSTER_TOKEN"};
auto status = client->Connect(connect_param);
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}

milvus::CollectionSchemaPtr schema = std::make_shared<milvus::CollectionSchema>();
schema->AddField({"id", milvus::DataType::INT64, "", true, true});
schema->AddField(milvus::FieldSchema("text", milvus::DataType::VARCHAR).WithMaxLength(1000).EnableAnalyzer(true));
schema->AddField(milvus::FieldSchema("sparse", milvus::DataType::SPARSE_FLOAT_VECTOR));
shell
# Zilliz CLI

BM25 関数を定義する​

BM25 関数は、トークン化されたテキストを、BM25 スコアリングをサポートするスパースベクトルに変換します。

関数を定義し、スキーマに追加します:

python
bm25_function = Function(
name="text_bm25_emb", # Function name
input_field_names=["text"], # Name of the VARCHAR field containing raw text data
output_field_names=["sparse"], # Name of the SPARSE_FLOAT_VECTOR field reserved to store generated embeddings
function_type=FunctionType.BM25, # Set to `BM25`
)

schema.add_function(bm25_function)

インデックスを設定する​

必要なフィールドと組み込み関数を含むスキーマを定義したら、コレクションのインデックスを設定します。このプロセスを簡素化するには、index_type として AUTOINDEX を使用します。これは、データの構造に基づいて Zilliz Cloud が最適なインデックスタイプを選択して設定できるようにするオプションです。

python
index_params = client.prepare_index_params()

index_params.add_index(
field_name="sparse",
index_type="AUTOINDEX",
metric_type="BM25"
)
plaintext
auto index_params = milvus::IndexDesc("sparse", "", milvus::IndexType::SPARSE_INVERTED_INDEX, milvus::MetricType::BM25);
index_params.AddExtraParam("inverted_index_algo", "DAAT_MAXSCORE");
index_params.AddExtraParam("bm25_k1", "1.2");
index_params.AddExtraParam("bm25_b", "0.75");
shell
# Zilliz CLI

コレクションを作成する​

次に、定義したスキーマとインデックスパラメータを使用してコレクションを作成します:

python
client.create_collection(
collection_name='my_collection',
schema=schema,
index_params=index_params
)
plaintext
auto status = client->CreateCollection(milvus::CreateCollectionRequest()
.WithCollectionName("my_collection")
.WithCollectionSchema(schema)
.AddIndex(std::move(index_params)));
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
shell
# Zilliz CLI

Web コンソールを使用する場合​

または、Zilliz Cloud コンソールで BM25 関数付きのコレクションを作成することもできます。

BM25 関数付きのコレクションを作成したら、テキストを挿入し、テキストクエリに基づく語彙検索を実行できます。

ステップ 2: コレクションにテキストデータを挿入する​

コレクションとインデックスを設定したら、テキストデータを挿入する準備が整いました。このプロセスでは、生テキストを指定するだけで済みます。先ほど定義した BM25 関数が、各テキストエントリのスパースベクトルを自動的に生成します。

python
client.insert('my_collection', [
{'text': 'information retrieval is a field of study.'},
{'text': 'information retrieval focuses on finding relevant information in large datasets.'},
{'text': 'data mining and information retrieval overlap in research.'},
])

ステップ 3: テキストクエリで検索する​

コレクションにデータを挿入したら、生のテキストクエリを使用して全文検索を実行できます。Zilliz Cloud はクエリを自動的にスパースベクトルに変換し、BM25 アルゴリズムを使用して一致した検索結果を順位付けしたうえで、上位 topK(limit)件の結果を返します。

python
search_params = {
'params': {'level': 10},
}

res = client.search(
collection_name='my_collection',
data=['whats the focus of information retrieval?'],
anns_field='sparse',
output_fields=['text'], # Fields to return in search results; sparse field cannot be output
limit=3,
search_params=search_params
)

print(res)