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

MINHASH_LSH

大規模な機械学習データセット、特に大規模言語モデル(LLM)のトレーニングコーパスをクリーニングするようなタスクでは、効率的な重複排除と類似性検索が重要です。数百万または数十億のドキュメントを扱う場合、従来の完全一致検索は遅すぎてコストが高くなります。

Zilliz Cloud の MINHASH_LSH インデックスは、2 つの強力な手法を組み合わせることで、高速でスケーラブルかつ正確な近似重複排除を可能にします。

  • MinHash: ドキュメントの類似性を推定するためのコンパクトなシグネチャ(「フィンガープリント」)をすばやく生成します。

  • Locality-Sensitive Hashing(LSH): MinHash シグネチャに基づいて、類似するドキュメントのグループをすばやく見つけます。

このガイドでは、Zilliz Cloud で MINHASH_LSH を使用するための概念、事前準備、セットアップ、ベストプラクティスについて説明します。

概要​

展開して仕組みを確認する

Jaccard 類似度​

Jaccard 類似度は、2 つの集合 A と B の重なりを測定するもので、次のように定義されます。

J(A,B)=∣A∩B∣∣A∪B∣J(A, B) = \frac{|A \cap B|}{|A \cup B|}

値の範囲は 0(完全に重複がない状態)から 1(同一)です。

ただし、大規模なデータセット内のすべてのドキュメントペア間で Jaccard 類似度を正確に計算するには、計算コストが高くなります。n が大きい場合、時間とメモリの両方で O(n²) となります。そのため、LLM トレーニングコーパスのクリーニングや Web 規模のドキュメント分析などのユースケースには適していません。

MinHash シグネチャ: Jaccard 類似度の近似​

MinHash は、Jaccard 類似度を効率的に推定するための確率的な手法です。各集合をコンパクトな シグネチャベクトル に変換し、集合の類似性を効率的に近似するのに十分な情報を保持します。

基本的な考え方:

2 つの集合が類似しているほど、それらの MinHash シグネチャが同じ位置で一致する可能性が高くなります。この性質により、MinHash は集合間の Jaccard 類似度を近似できます。

この性質により、MinHash は集合全体を直接比較することなく、集合間の Jaccard 類似度を近似 できます。

MinHash のプロセスは次のとおりです。

  1. シングリング: ドキュメントを、重複するトークンシーケンス(シングル)の集合に変換します。

  2. ハッシュ: 各シングルに複数の独立したハッシュ関数を適用します。

  3. 最小値の選択: 各ハッシュ関数について、すべてのシングルにわたる 最小 ハッシュ値を記録します。

プロセス全体を以下に示します。

CCzEwT7uchMqI6bsxRJcK1qenEh

Notes

使用するハッシュ関数の数によって、MinHash シグネチャの次元数が決まります。次元数が大きいほど近似精度は向上しますが、ストレージと計算のコストが増加します。

MinHash 用の LSH​

MinHash シグネチャは、ドキュメント間で正確な Jaccard 類似度を計算するコストを大幅に削減しますが、すべてのシグネチャベクトルのペアを網羅的に比較すると、大規模な環境では依然として非効率です。

この問題を解決するために、LSH を使用します。LSH は、類似するアイテムが高い確率で同じ「バケット」にハッシュされるようにすることで、すべてのペアを直接比較する必要をなくし、高速な近似類似性検索を可能にします。

プロセスは次のとおりです。

  1. シグネチャの分割:

    n 次元の MinHash シグネチャを b 個のバンドに分割します。各バンドには r 個の連続するハッシュ値が含まれるため、シグネチャの全長は n = b × r を満たします。

    たとえば、128 次元の MinHash シグネチャ(n = 128)を 32 個のバンド(b = 32)に分割すると、各バンドには 4 個のハッシュ値(r = 4)が含まれます。

  2. バンド単位のハッシュ:

    分割後、各バンドは標準のハッシュ関数を使用して個別に処理され、バケットに割り当てられます。2 つのシグネチャが同じバンド内で同じハッシュ値を生成する場合、つまり同じバケットに分類される場合、それらは一致候補と見なされます。

  3. 候補の選択:

    少なくとも 1 つのバンドで衝突するペアが、類似性の候補として選択されます。

Notes

なぜ機能するのか

数学的には、2 つのシグネチャの Jaccard 類似度が ss である場合、次のようになります。

  • 1 つの行(ハッシュ位置)で一致する確率は ss です。

  • 1 つのバンドの r 個の行すべてで一致する確率は srs^r です。

  • 少なくとも 1 つのバンド で一致する確率は $1 - (1 - s^r)^b$ です。

詳細は、Locality-sensitive hashing を参照してください。

128 次元の MinHash シグネチャを持つ 3 つのドキュメントを考えます。

E1dewMnqshua0ib7aHmcL10lnIe

まず、LSH は 128 次元のシグネチャを、それぞれ 4 個の連続する値からなる 32 個のバンドに分割します。

PhSMwS74rh25oybv9Docmfionze

次に、各バンドはハッシュ関数を使用して異なるバケットにハッシュされます。同じバケットを共有するドキュメントペアが類似性の候補として選択されます。次の例では、ドキュメント A とドキュメント B のハッシュ結果が Band 0 で衝突するため、両者が類似性の候補として選択されます。

RfmMwNkIvhlUFSb11alcP8fqnmf

Notes

バンドの数は mh_lsh_band パラメーターで制御されます。詳細は、インデックス構築パラメーター を参照してください。

MHJACCARD: MinHash シグネチャの比較​

MinHash シグネチャは、固定長のバイナリベクトルを使用して集合間の Jaccard 類似度を近似します。ただし、これらのシグネチャは元の集合を保持しないため、JACCARD、L2、COSINE などの標準的なメトリクスを直接適用して比較することはできません。

この問題に対応するため、Zilliz Cloud では、MinHash シグネチャの比較専用に設計された MHJACCARD という特殊なメトリクスタイプを導入しています。

Zilliz Cloud で MinHash を使用する場合は、次の点に注意してください。

  • ベクトルフィールドの型は BINARY_VECTOR である必要があります。

  • index_type は MINHASH_LSH(または BIN_FLAT)である必要があります。

  • metric_type は MHJACCARD に設定する必要があります。

他のメトリクスを使用すると、無効になるか、誤った結果が生成されます。

このメトリクスタイプの詳細は、MHJACCARD を参照してください。

重複排除のワークフロー​

MinHash LSH を活用した重複排除プロセスにより、Zilliz Cloud はコレクションに挿入する前に、ほぼ重複するテキストや構造化レコードを効率的に特定して除外できます。

It9wwbCFwhfT0RbwosAcGltZneb

  1. チャンク分割と前処理: 受信したテキストデータまたは構造化データ(レコード、フィールドなど)をチャンクに分割し、テキストを正規化(小文字化、句読点の削除)して、必要に応じてストップワードを削除します。

  2. 特徴量の構築: MinHash に使用するトークンセットを構築します(テキストからはシングル、構造化データからは連結されたフィールドトークンなど)。

  3. MinHash シグネチャの生成: 各チャンクまたはレコードの MinHash シグネチャを計算します。

  4. バイナリベクトルへの変換: シグネチャを Milvus と互換性のあるバイナリベクトルに変換します。

  5. 挿入前の検索: MinHash LSH インデックスを使用して、対象のコレクション内で受信アイテムのほぼ重複を検索します。

  6. 挿入と保存: 一意のアイテムのみをコレクションに挿入します。これらのアイテムは、今後の重複チェックで検索可能になります。

事前準備​

Zilliz Cloud で MinHash LSH を使用する前に、まず MinHash シグネチャ を生成する必要があります。このコンパクトなバイナリシグネチャは集合間の Jaccard 類似度を近似するものであり、Zilliz Cloud で MHJACCARD ベースの検索を行うために必要です。

Notes

MINHASH_LSH インデックス用の MinHash シグネチャは、次の 2 つの方法で準備できます。

  • 外部ツールを使用してシグネチャを生成し、BINARY_VECTOR フィールドに挿入する、または

  • 組み込みの MinHash 関数を使用して、テキストから互換性のあるバイナリベクトルを自動的に生成します。MinHash 関数のエンドツーエンドのワークフローと構成オプションについては、MinHash Function を参照してください。

MinHash シグネチャの生成方法を選択する​

ワークロードに応じて、次の方法から選択できます。

  • シンプルにするために Python の datasketch を使用します(プロトタイピングに推奨)。

  • 大規模なデータセットには分散ツール(Spark、Ray など)を使用します。

  • パフォーマンスのチューニングが重要な場合は、カスタムロジック(NumPy、C++ など)を実装します。

このガイドでは、シンプルさと Zilliz Cloud の入力形式との互換性のため、datasketch を使用します。

必要なライブラリをインストールする​

この例に必要なパッケージをインストールします。

bash
pip install pymilvus datasketch numpy

MinHash シグネチャを生成する​

256 次元の MinHash シグネチャを生成します。各ハッシュ値は 64 ビット整数で表されます。これは、MINHASH_LSH で想定されるベクトル形式と一致します。

python
from datasketch import MinHash
import numpy as np

MINHASH_DIM = 256
HASH_BIT_WIDTH = 64

def generate_minhash_signature(text, num_perm=MINHASH_DIM) -> bytes:
m = MinHash(num_perm=num_perm)
for token in text.lower().split():
m.update(token.encode("utf8"))
return m.hashvalues.astype('>u8').tobytes() # Returns 2048 bytes

各シグネチャは 256 × 64 ビット = 2048 バイトです。このバイト文字列は、BINARY_VECTOR フィールドに直接挿入できます。Zilliz Cloud で使用されるバイナリベクトルの詳細は、Binary ベクトル を参照してください。

デフォルトでは、Zilliz Cloud は MinHash シグネチャと LSH インデックスのみを使用して近似近傍を検索します。これは高速ですが、誤検出が返されたり、近い一致が見逃されたりする可能性があります。

正確な Jaccard 類似度 が必要な場合、Zilliz Cloud は元のトークンセットを使用する詳細検索をサポートしています。これを有効にするには、次のようにします。

トークンセット抽出の例:

python
def extract_token_set(text: str) -> str:
tokens = set(text.lower().split())
return " ".join(tokens)

MinHash LSH を使用する​

MinHash ベクトルと元のトークンセットを準備したら、Zilliz Cloud で MINHASH_LSH を使用して保存、インデックス作成、検索を行うことができます。

クラスターに接続する​

python
from pymilvus import MilvusClient

client = MilvusClient(uri="YOUR_CLUSTER_ENDPOINT") # Update if your URI is different

コレクションスキーマを定義する​

次の項目を含むスキーマを定義します。

  • プライマリキー

  • MinHash シグネチャ用の BINARY_VECTOR フィールド

  • 元のトークンセット用の VARCHAR フィールド(詳細検索が有効な場合)

  • オプションで、元のテキスト用の document フィールド

python
from pymilvus import DataType

VECTOR_DIM = MINHASH_DIM * HASH_BIT_WIDTH # 256 × 64 = 16384 bits

schema = client.create_schema(auto_id=False, enable_dynamic_field=False)
schema.add_field("doc_id", DataType.INT64, is_primary=True)
schema.add_field("minhash_signature", DataType.BINARY_VECTOR, dim=VECTOR_DIM)
schema.add_field("token_set", DataType.VARCHAR, max_length=1000) # required for refinement
schema.add_field("document", DataType.VARCHAR, max_length=1000)

インデックスパラメーターを構築してコレクションを作成する​

Jaccard リファインメントを有効にして MINHASH_LSH インデックスを構築します。

python
index_params = client.prepare_index_params()
index_params.add_index(
field_name="minhash_signature",
index_type="MINHASH_LSH",
metric_type="MHJACCARD",
params={
"mh_element_bit_width": HASH_BIT_WIDTH, # Must match signature bit width
"mh_lsh_band": 16, # Band count (256/16 = 16 hashes per band)
"with_raw_data": True # Required for Jaccard refinement
}
)

client.create_collection("minhash_demo", schema=schema, index_params=index_params)

インデックス構築パラメーターの詳細は、インデックス構築パラメーター を参照してください。

データを挿入する​

各ドキュメントについて、次の項目を準備します。

  • バイナリの MinHash シグネチャ

  • シリアル化されたトークンセット文字列

  • (オプション)元のテキスト

python
documents = [
"machine learning algorithms process data automatically",
"deep learning uses neural networks to model patterns"
]

insert_data = []
for i, doc in enumerate(documents):
sig = generate_minhash_signature(doc)
token_str = extract_token_set(doc)
insert_data.append({
"doc_id": i,
"minhash_signature": sig,
"token_set": token_str,
"document": doc
})

client.insert("minhash_demo", insert_data)
client.flush("minhash_demo")

Zilliz Cloud は、MinHash LSH を使用した 2 つのモードの類似性検索をサポートしています。

  • 近似検索 — MinHash シグネチャと LSH のみを使用し、高速ですが確率的な結果を返します。

  • 詳細検索 — 元のトークンセットを使用して Jaccard 類似度を再計算し、精度を向上させます。

5.1 クエリを準備する​

類似性検索を実行するには、クエリドキュメントの MinHash シグネチャを生成します。このシグネチャは、データ挿入時に使用したものと同じ次元数とエンコード形式に一致する必要があります。

python
query_text = "deep learning uses neural networks to model patterns"
query_sig = generate_minhash_signature(query_text)

5.2 近似検索(LSH のみ)​

これは高速でスケーラブルですが、近い一致が見逃されたり、誤検出が含まれたりする可能性があります。

python
search_params={
"metric_type": "MHJACCARD",
"params": {}
}

approx_results = client.search(
collection_name="minhash_demo",
data=[query_sig],
anns_field="minhash_signature",
search_params=search_params,
limit=3,
output_fields=["doc_id", "document"],
consistency_level="Strong"
)

for i, hit in enumerate(approx_results[0]):
sim = hit['distance']
print(f"{i+1}. Similarity: {sim:.3f} | {hit['entity']['document']}")

これにより、Zilliz Cloud に保存された元のトークンセットを使用して、正確な Jaccard 比較が可能になります。やや低速ですが、品質が重要なタスクに推奨されます。

python
search_params = {
"metric_type": "MHJACCARD",
"params": {
"mh_search_with_jaccard": True, # Enable real Jaccard computation
"refine_k": 5 # Refine top 5 candidates
}
}

refined_results = client.search(
collection_name="minhash_demo",
data=[query_sig],
anns_field="minhash_signature",
search_params=search_params,
limit=3,
output_fields=["doc_id", "document"],
consistency_level="Strong"
)

for i, hit in enumerate(refined_results[0]):
sim = hit['distance']
print(f"{i+1}. Similarity: {sim:.3f} | {hit['entity']['document']}")

インデックスパラメーター​

このセクションでは、インデックスの構築とインデックスに対する検索に使用するパラメーターの概要を説明します。

インデックス構築パラメーター​

次の表は、インデックスを構築する ときに params で設定できるパラメーターを一覧にしたものです。

パラメーター説明値の範囲チューニングの推奨事項
mh_element_bit_widthMinHash シグネチャ内の各ハッシュ値のビット幅です。8 で割り切れる必要があります。8, 16, 32, 64パフォーマンスと精度のバランスを取るには 32 を使用します。大規模なデータセットで高い精度を得るには 64 を使用します。許容できる精度低下と引き換えにメモリを節約するには 16 を使用します。
mh_lsh_bandLSH のために MinHash シグネチャを分割するバンド数です。再現率とパフォーマンスのトレードオフを制御します。[1, signature_length]128 次元のシグネチャの場合は、32 個のバンド(4 個の値/band). から始めます。再現率を高めるには 64 に増やし、パフォーマンスを向上させるには 16 に減らします。シグネチャの長さを均等に分割できる必要があります。
mh_lsh_code_in_memLSH ハッシュコードを匿名メモリに保存する(true)か、メモリマッピングを使用する(false)かを指定します。true, false大規模なデータセット(100 万セット超)では、メモリ使用量を削減するために false を使用します。最大の検索速度が必要な小規模なデータセットでは true を使用します。
with_raw_dataリファインメントのために、LSH コードと一緒に元の MinHash シグネチャを保存するかどうかを指定します。true, false高い精度が必要で、ストレージコストを許容できる場合は true を使用します。わずかな精度低下と引き換えにストレージのオーバーヘッドを最小限に抑える場合は false を使用します。
mh_lsh_bloom_false_positive_probLSH バケットの最適化に使用されるブルームフィルターの偽陽性確率です。[0.001, 0.1]メモリ使用量と精度のバランスを取るには 0.01 を使用します。値を小さくする(0.001)と偽陽性は減少しますが、メモリ使用量が増加します。値を大きくする(0.05)とメモリを節約できますが、精度が低下する可能性があります。

インデックス固有の検索パラメーター​

次の表は、インデックスを検索する ときに search_params.params で設定できるパラメーターを一覧にしたものです。

パラメーター説明値の範囲チューニングの推奨事項
mh_search_with_jaccardリファインメントのために、候補結果に対して正確な Jaccard 類似度計算を実行するかどうかを指定します。true, false高い精度が必要なアプリケーション(重複排除など)には true を使用します。わずかな精度低下を許容できる場合に高速な近似検索を行うには false を使用します。
refine_kJaccard リファインメントの前に取得する候補の数です。mh_search_with_jaccard が true の場合にのみ有効です。[top_k, top_k * 10]再現率とパフォーマンスのバランスを取るには、希望する top_k の 2〜5 倍に設定します。値を大きくすると再現率は向上しますが、計算コストが増加します。
mh_lsh_batch_search同時に複数のクエリを実行する場合のバッチ最適化を有効にするかどうかを指定します。true, false複数のクエリを同時に検索してスループットを向上させる場合は true を使用します。メモリのオーバーヘッドを削減するため、単一クエリのシナリオでは false を使用します。