ふくふくHukuhuku Inc.
EP.06Algorithms 10分公開: 2026-05-11

MinHash + LSH:1 億文書から似たペアを高速発見する確率的アルゴリズム

「剽窃検査」「重複コンテンツ検出」「商品レコメンド」。1 億件の文書から似たペアを O(n) で見つける MinHash + LSH を Python で実装、Google・Spotify の中身。

#MinHash#LSH#Jaccard#類似文書
CO📔 Google Colab で開く(上から順にセルを実行)
シェア

「Wikipedia の 600 万記事から、ある記事に似たものを見つける」「ECサイトの 1 億商品から、ある商品に似たものを推薦する」「論文の剽窃検査で、過去 100 億件と照合する」。これらすべて 「類似ペアを大量データから高速に発見する」 問題。MinHash + LSH がこれを O(n) で解く。

1. これで何が動いているか

  • Google の重複コンテンツ検出 (Web ページのほぼコピーを除外)
  • Spotify の楽曲類似性 (Discover Weekly の一部)
  • Turnitin などの剽窃検査ツール
  • メルカリ・Yahoo!オークション の類似商品検出
  • Wikipedia の編集履歴差分検出
  • Apache Solr / Elasticsearch の moreLikeThis

2. 仕組みのざっくり

  1. 1Step 1: 文書を集合化 (例: 単語、3-gram のシングル)
  2. 2Step 2: MinHash signature 計算 — 集合の類似度を圧縮 (128 個の整数)
  3. 3Step 3: LSH バケット に振り分け — signature を b バンドに分割、各バンドのハッシュをキーに
  4. 4Step 4: 同じバケットに入った候補ペア だけを Jaccard 推定で精査
なぜ Jaccard を MinHash で推定できるか

集合 A の全要素を 1 つのハッシュ関数 h でハッシュし、その最小値 min(h(A)) を取る。A と B の最小値が等しい確率は Jaccard(A,B) と等しい、という美しい数学的性質がある (1997 年 Andrei Broder 発見)。これを多数のハッシュで並列実行することで類似度を推定。

3. Python 実装: datasketch (動作確認済)

MinHash + LSH で類似文書検出
Python
# pip install datasketchfrom datasketch import MinHash, MinHashLSH
def make_minhash(text, num_perm=128):    '''テキストを MinHash signature に変換'''    m = MinHash(num_perm=num_perm)    for word in text.lower().split():        m.update(word.encode('utf-8'))    return m
# サンプル文書documents = {    'doc1': 'the quick brown fox jumps over the lazy dog',    'doc2': 'the quick brown fox jumps over the lazy cat',  # 1 word 違い    'doc3': 'a completely different document about machine learning',    'doc4': 'the lazy dog sleeps under the warm sun',    'doc5': 'the quick brown fox jumps over the sleepy dog',  # 2 words 違い}
# MinHash 計算minhashes = {k: make_minhash(v) for k, v in documents.items()}
# LSH index に投入lsh = MinHashLSH(threshold=0.5, num_perm=128)for k, m in minhashes.items():    lsh.insert(k, m)
# クエリ: doc1 と類似するものresult = lsh.query(minhashes['doc1'])print(f'doc1 と類似 (threshold=0.5): {result}')
# Jaccard 推定値の確認print('\nJaccard 類似度 (推定):')for a in documents:    for b in documents:        if a < b:            j = minhashes[a].jaccard(minhashes[b])            print(f'  {a} vs {b}: {j:.3f}')
# 実機実行結果:# doc1 と類似 (threshold=0.5): ['doc5', 'doc1', 'doc2']# doc1 vs doc2: 0.758# doc1 vs doc5: 0.789# doc1 vs doc3: 0.000 (全く違う)

4. パラメータ調整

num_permメモリ/文書Jaccard 推定誤差
64256 B12.5%
128 (推奨)512 B8.8%
2561 KB6.2%
5122 KB4.4%

5. 大規模データでの実用パターン

1 億文書を分散処理
Python
# 各文書を MinHash に変換 (Spark / Dask で並列化)# 1 文書 ≈ 512B、1 億で 50 GB → 分散ストア
# datasketch の MinHashLSH は Redis backend にも対応from datasketch import MinHashLSH
# Redis をストレージとして使うlsh = MinHashLSH(    threshold=0.7,    num_perm=128,    storage_config={'type': 'redis', 'redis': {'host': 'localhost', 'port': 6379}})
# 100 万件投入後、クエリ# similar = lsh.query(query_minhash)

6. 計算量比較

手法前処理クエリ1 億件で
全ペア比較なしO(n²)100 京演算 (現実的でない)
MinHash + LSHO(n)O(1) per query実時間

7. メリットとデメリット

  • メリット: O(n) で類似ペア発見、メモリ効率良い、分散しやすい
  • メリット: Jaccard 類似度が信号源、文書だけでなく「ユーザの好み集合」「タグの集合」等にも応用可
  • デメリット: 「集合」表現に向く問題のみ (順序・頻度が重要なケースは苦手)
  • デメリット: 偽陽性・偽陰性が一定割合発生
  • デメリット: 短い文書 (10 単語以下) では精度が落ちる

8. 関連ツール

  • datasketch (Python): MinHash / LSH / Theta Sketch 統合
  • Apache DataSketches (Java): Yahoo 製、本番向け
  • spark-mllib: Spark の LSH 実装
  • simhash (Google 系): 似た目的の別アルゴリズム

9. 次の話

EP.07 では CRDT (Conflict-free Replicated Data Types) を扱います。Notion / Figma / Linear のリアルタイム同時編集 を可能にする分散データ構造の実装。

シェア

この記事の感想を教えてください

あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。

シリーズの外も探す:

まずは、現状を聞かせてください。

要件が固まっていなくて大丈夫です。現状診断と方針提案までを無料でお手伝いします。

無料相談フォームへ hello [at] hukuhuku [dot] co [dot] jp