Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

2. BM25アルゴリズムの仕組み

BM25とは何か

BM25(Best Matching 25)は、1990年代に情報検索の分野で開発された、伝統的かつ強力なキーワード検索スコアリングアルゴリズムです。 一般的には「Okapi BM25」とも呼ばれ、オープンソースの全文検索エンジンである Elasticsearch や OpenSearch、Apache Lucene でも長年標準スコアとして採用されています。

BM25 は、古典的な検索手法である「TF-IDF(Term Frequency - Inverse Document Frequency)」を大幅に改良したものです。 質問に含まれる単語が、ある文書の中にどれくらい頻繁に、そしてどれくらい特徴的に現れるかを確率統計モデルに基づいて計算します。

TF-IDFからの進化とBM25の計算式

TF-IDF では、単語の出現回数が増えれば増えるほどスコアが比例して直線的に上昇します。 しかし、現実には単語が10回出てくる文書と100回出てくる文書の関連度は、10倍も違わないはずです。 BM25 はこの課題を解決するために、次の数式で関連度スコアを算出します。

Score(D, Q) = Σ IDF(q_i) * [ (f(q_i, D) * (k1 + 1)) / (f(q_i, D) + k1 * (1 - b + b * (|D| / avgdl))) ]

各記号の意味は次の通りです。

  • Q: ユーザーが入力した質問文(単語 q_1, q_2, ... の集まり)

  • D: 評価対象の文書

  • f(q_i, D): 文書 D の中に単語 q_i が出現した回数(TF)

  • |D|: 文書 D の単語数(文書長)

  • avgdl: データベース内の全文書の平均単語数(Average Document Length)

  • k1, b: スコアの挙動を調整するためのパラメータ

BM25を支える3つの重要な要素

BM25 が高精度に機能する理由は、次の3つの仕組みをバランスよく組み合わせているためです。

▸ 珍しい単語を高く評価する(IDF: 逆文書頻度)

「これ」「です」「あります」のような、どの文書にもよく出てくる一般的な単語は、検索の役に立ちません。 一方で、「量子力学」や「LangChain」のように限られた文書にしか出てこない珍しい単語は、文書の主題を強く表しています。 IDF は、全文書中に出現する割合が低い希少な単語ほど、高い重みを与える仕組みです。

▸ 単語頻度の頭打ち効果(TFの飽和特性とパラメータ k1)

BM25 では、文書内に単語が何回も現れた場合、最初はスコアが大きく上がりますが、回数が増えるにつれてスコアの上昇が緩やかになり、やがて上限値に近づきます。 これを「飽和(Saturation)」と呼びます。 パラメータ k1 は、その飽和の速さを制御する値です。 通常は 1.2 から 2.0 程度に設定され、k1 を小さくすると早い段階でスコアが頭打ちになります。

▸ 長い文書に対するペナルティ(文書長正規化とパラメータ b)

長い文書は文字数が多いため、短い文書に比べて偶然多くの単語が含まれやすくなります。 BM25 は、データベース全体の平均文書長(avgdl)と対象文書の長さ(|D|)を比較し、平均より長い文書にはスコアを割り引くペナルティを課します。 パラメータ b はこのペナルティの強さを調整する値で、0.0 から 1.0 の範囲で設定します。 通常は 0.75 前後が最適とされています。

日本語におけるBM25の注意点

英語は単語同士が半角スペースで区切られているため、文字列をそのまま分割して BM25 を適用できます。 しかし、日本語は単語の間にスペースが入らない「わかち書き」ではない言語です。

そのため、日本語テキストで BM25 を使用する場合は、事前に「形態素解析」を行い、文章を品詞単位の単語(トークン)に正しく分割する必要があります。 形態素解析ツールには、MeCab、Janome、Sudachi などがあります。 適切なトークナイズを行わずに文字単位などで分割してしまうと、BM25 本来の単語頻度や珍しさが正しく計算できず、検索精度が著しく低下します。 Python + LangChain で実装する際も、このトークナイザーの設定が極めて重要な鍵となります。