技術 約8分で読めます

BERTはキーワード検索から文脈検索へ何を変えたか

いけさん目次

Google検索がBERTを入れたときに変わったのは、「単語が含まれるページを探す」だけでは拾えなかった短いクエリの読み方だった。
DEV CommunityのBERT解説記事は、jaguar speedcan you get medicine for someone pharmacy のような例から入っている。
ここで大事なのは、BERTが検索エンジン全体をLLMチャットにしたわけではなく、クエリ中の語と語の関係をランキング前に読めるようにした点だ。

Googleの2019年の記事でも、BERTは米国英語クエリの10件に1件に使われ始めたと説明されていた。
例として出ていたのは、for someone が「誰かの代わりに薬を受け取る」という意味を持つケースや、no curbno を落とすと答えが逆になるケースだ。
どちらも長文理解ではない。
短い検索語の中で、前置詞や否定語が結果をひっくり返す。

BERTは穴埋めで左右の文脈を読む

BERTの中核はmasked language modelだ。
文章中の一部トークンを隠し、左右の文脈から隠れた語を当てる。
この訓練だと、モデルは左から右へ次の語を予測するだけでは済まない。

たとえば bank は、river が近くにあれば川岸側に寄り、deposit が近くにあれば金融機関側に寄る。
同じ文字列でも、周辺語によって内部表現が変わる。

この性質は、以前書いたエンコーダーモデル+ローカルLLMでOCR誤字を自動検出・修正するの実験とそのままつながる。
OCR校正では、BERT系モデルを「次に何を書くか」ではなく「この位置の文字が文脈上おかしいか」を見る部品として使った。
投入優 のような誤認識を、左右の語から 投入後 へ寄せられるかを見る。
生成LLMに全文を直させると、原文まで勝手に整えることがある。
BERT側は穴の周辺だけを見るので、校正の発火点として扱いやすい。

15%マスクと80/10/10ルールで訓練する

masked language modelの「マスク」は単純な穴埋めではない。
入力トークンの15%を訓練対象に選び、その中で扱いをさらに分ける。
80%は[MASK]に差し替え、10%はランダムな別トークン、残り10%は元のトークンのまま残す。

推論時に[MASK]トークンは入力に現れない。
すべての訓練対象を[MASK]で置き換えると、モデルは「[MASK]を見たときだけ働く」表現を学んでしまう。
ランダムや原形を混ぜることで、どのトークンも常に左右の文脈から再解釈し直す癖がつく。

flowchart LR
  A["原文<br/>The cat sat on the mat"] --> B["15%を訓練対象に選ぶ<br/>例: cat, mat"]
  B --> C["80% [MASK]置換<br/>10% ランダム置換<br/>10% 原形のまま"]
  C --> D["双方向Self-Attention<br/>12層 or 24層"]
  D --> E["選んだ位置の<br/>語彙分布"]
  E --> F["正解トークンと<br/>クロスエントロピー"]

オリジナルBERTでは、もう一つの事前学習タスクとしてNext Sentence Predictionも乗っていた。
2つの文をペアで入力し、[SEP]で区切って「後半は前半の続きか」を当てる。
ただし後のRoBERTaがNSPを外しても下流タスクのスコアが下がらないことを示し、現在の派生モデルでは省かれていることが多い。
事前学習データはオリジナル版でBooksCorpus + 英語Wikipediaの3.3B語、RoBERTa以降は160GB級まで増えている。

WordPieceで語彙外の語も分割する

BERTの英語版語彙は30,522トークンで、WordPieceというサブワード分割を使う。
未知語をそのまま[UNK]に落とすのではなく、覚えている断片に分けて表現する。

入力分割結果
playingplay, ##ing
unhappinessun, ##happiness
tokenizationtoken, ##ization
Qwenq, ##wen

##は「直前のトークンに続く断片」を表す印で、語頭のplayと語中の##ingは別IDになる。
固有名詞や新語が語彙になければ、2〜3個の断片に分かれて入力される。

入力先頭には常に[CLS]、文の区切りには[SEP]、パディングには[PAD]が入る。
分類タスクでは[CLS]位置の出力ベクトルを取り出してヘッドに渡す形が多い。
fill-maskで穴埋め候補を見たい場合は、[MASK]を狙ったサブワード境界に置く必要がある。
語の途中にマスクを置くと、モデルは##で始まる断片を返してくるので、復元側で結合処理が要る。

検索では候補生成と意味判定が別になる

キーワード検索は、転置インデックスで候補文書を引くところが強い。
このブログの検索を速くするデータ構造では、転置インデックス、Trie、n-gram、BK木を整理した。
それらは「どの文書に語があるか」「どの文字列が近いか」を高速に返す。

BERTが足したのは、その後段で「この候補はクエリの意味に沿っているか」を見る層だ。
parking on a hill with no curb なら、curb を含むページを拾うだけでは反対の答えに寄る。
nocurb の関係まで見て、候補の並びを変える。

今のRAGやサイト内検索でも、似た分担になる。
最初にBM25やベクトル検索で候補を絞り、後段でrerankerがクエリと候補をペアで読む。
Sentence Transformers v5.4でテキスト・画像・音声・動画の統合Embeddingが可能にで書いたEmbeddingとRerankerの分担も同じ系譜にある。
BERT単体で巨大な検索基盤を置き換えるのではなく、候補を読むモデルとして入る。

生成しないBERTは判定タスクで扱いやすい

原典記事の最後でも触れられているが、BERTはエンコーダーだ。
GPT系のように左から右へ文章を生成するモデルではない。
質問にそのまま答えさせたり、長い文章を書かせたりする用途には向かない。

一方で、分類、固有表現抽出、穴埋め、類似度計算、rerankのように、入力テキストを読んで判定する処理では今でも壊れ方が読みやすい。
出力空間が狭いので、生成LLMよりテストしやすい。
OCR校正ならFIX/KEEP/ESCALATE、検索なら候補ごとのスコア、分類ならラベルに落とせる。

この違いは、手元でモデルを使うときにも出る。
bert-base-uncased は110Mパラメータ、最大512トークンの古いモデルだが、分類やfill-maskならCPUでも試せる。
ドメインがずれると精度は落ちる。
日本語OCRには日本語BERTやLUKE、医療文書にはBioBERT系、法律文書にはLegalBERT系のように、事前学習データとの距離がそのまま誤判定に出る。

BERT以降のエンコーダー系譜は今も伸びている

BERTは2018年のモデルで、最大512トークン、Attentionも当時の素朴な実装だ。
派生・後継がいくつもあり、現在の判定タスクでは下のほうを選ぶことが多い。

モデル公開年主な変更点
RoBERTa2019NSP廃止、大バッチ、動的マスク、データ拡大
ALBERT2020層間でパラメータ共有、軽量化
DeBERTa2020位置情報をAttentionに分離注入
DeBERTa-v32021ELECTRA風の置換検出で再訓練、cross-encoderの定番
ModernBERT20248,192トークン、Flash Attention、現代的レシピで再訓練

rerank用途ではDeBERTa-v3ベースのcross-encoderが定番、長文埋め込みや長コンテキスト判定にはModernBERTを選ぶ場面が増えてきた。
パラメータ数で正面から並べると古典BERTは分が悪いが、CPUで動かす分類器や手元のfill-mask実験ならbert-base-uncasedでも今でも十分に動く。

日本語BERTは事前学習データとトークナイザで選ぶ

日本語は空白分割が効かない上に、ひらがな・カタカナ・漢字・英字・記号が混ざる。
事前学習データのドメインとトークナイザの方式でモデルを選ぶ。

モデル特徴使いどころ
cl-tohoku/bert-base-japanese-v3MeCab + WordPiece、Wikipedia + CC-100汎用、Web文の分類
cl-tohoku/bert-base-japanese-char-v3文字単位OCR校正、表記揺れの多いテキスト
LUKEエンティティ表現を別に学習固有表現抽出、関係抽出
nlp-waseda/roberta-base-japaneseRoBERTaレシピ文分類、類似度
ku-nlp/deberta-v2-base-japaneseDeBERTa系rerank、文ペア判定
line-corporation/line-distilbert-base-japaneseDistilBERT、軽量エッジ・CPU推論

OCR誤字検出のように「この位置の文字が文脈上おかしいか」を見るタスクでは、形態素ベースのモデルだと誤字でMeCabの分割自体が崩れることがある。
文字ベースモデルだとサブワード境界の影響を受けにくく、誤認識の周辺確率を直接見られる。

BERTのスコアはタスクごとに分布が変わる

BERT系の出力は、タスクごとに見方が変わる。
fill-maskの確率は「正解らしさ」ではなく、モデルの語彙と文脈でそのトークンが出る相対的な強さだ。
類似度検索のcosine similarityも、モデル、正規化、文長、ドメインで分布が変わる。

OCR校正で使ったときも、BERTのTop1候補をそのまま採用するとfalse positiveが出た。
そこでQwen判定を重ね、不一致は人間に戻すパイプラインにした。
検索でも同じで、BERTやrerankerのスコアだけを単独の真実として扱うより、既存の検索スコア、クリックログ、手元の評価セットと突き合わせたほうが壊れ方を見つけやすい。

Google検索のBERT導入は、検索が「単語一致から文脈理解へ進んだ」という派手な話として語られがちだ。
実際の使い道はもっと狭い。
短い入力の中で、否定語、前置詞、語順、周辺語が候補の順位を変える場面に入る。

参考