Apache Solr

 

kuromojiの辞書のメンテナンス

kuromojiの形態素辞書・同義語辞書をメンテナンスし、検索の精度を向上させます。基本的な使い方から運用で発生しがちな問題の解決まで行います。

新サイト、tree-mapsを公開しました!!

tree-maps: 地図のWEB TOOLの事ならtree-mapsにお任せ!

地図に関するWEB TOOL専門サイトです!!

大画面で大量の緯度経度を一気にプロット、ジオコーディング、DMS<->DEGの相互変換等ができます!

◯ 広告

形態素・同義語の辞書のメンテナンスをする前に、形態素解析による検索について軽く解説します。

文字列1「私はjavascriptというプログラミング言語が得意です!」

文字列2「私はjavaというプログラミング言語が大嫌いです・・・」

検索ワード「java」をRDBで検索すると、以下のようになります。

select * from hoge where freeword like '%java%'; 

文字列1・・・ヒットする

文字列2・・・ヒットする

likeは中間一致検索であるため、文字列1・2共にヒットしてしまいます。

文章に対して完全一致検索をしてもほぼ検索にヒットしないので、通常中間一致検索をします。

しかしこの場合、javaを検索したいのであって、javascriptをヒットさせたくありません。

こういう状態を「検索の精度が低い」と言います。

検索ワード「java」を形態素解析で検索すると、以下のようになります。

solrの形態素解析による検索

文字列1・・・ヒットする

文字列2・・・ヒットしない

solrの形態素解析の検索は中間一致検索ではありません。完全一致検索なのです。

solrの形態素解析の検索は、文章(検索ワードとデータの両方)を単語に分割し、単語を完全一致でand検索しています。

javaで検索してjavascriptでヒットしないのは、完全一致検索だからです。

この単語の分割の仕方は、形態素解析の辞書を元に行われます。

辞書のメンテナンスは何故必要なのでしょうか。

理由は以下を見ると解ります。

solrの形態素解析辞書に問題があるケース

「東京タワー」で検索したのに、「東京」「タワー」という単語に分割されてしまい、文章2がヒットしてしまったのです

形態素解析器は「東京」「タワー」という単語は知っていたが、「東京タワー」という単語を知らなかったので、分割されたのです。

完全一致検索とはいえ、単語を「含んで」いれば検索にヒットするのです。単語が隣接していなくてもヒットします。

「東京タワー」と「東京にあるタワーマンション」は全く関連の無い文章なので、文章2は検索にヒットさせたくありません。

形態素解析器は人間ではないので、どこまでが単語なのかは辞書を引かないと解らないし、自動で学習もしません。

では形態素解析器の辞書に「東京タワー」という単語を追加し、学習させてみましょう。

まず、現在のkuromojiの形態素辞書を確認します。

kuromojiの初期状態

朝青龍など、ヘンテコなサンプル単語が最初から登録されてありますが、無視して最終行に単語を追加してみます。

kuromojiの辞書に単語を追加

追加後、tomcatを再起動して、再度solr adminで検索してみます。

(実はtomcatの再起動無しでも辞書の反映が可能ですが、それは別の記事で説明します)

kuromojiの辞書に単語を追加した後の検索結果

kuromojiが学習し、東京タワーという単語を覚えたことで、文章2がヒットしなくなりました。

これで文句ありませんね!!・・・・本当にそうでしょうか・・・

以下を見て下さい。

kuromojiの辞書に単語を追加した事で検索にヒットしなくなった例

今度は「タワー」という検索ワードで「東京タワー」がヒットしなくなってしまいました。

ヒットしない事が良いことか悪いことかは要件によりますね。今回はヒットさせることを目標とします。

ではどうやって検索にヒットさせるか。ここで同義語の出番です。

前述のように、形態素解析の辞書に単語を登録した場合、別の単語が検索にヒットしなくなることがあります。

その場合、同義語を使って解決できる場合があります。

初期設定では同義語フィルタがtext_jaに組み込まれていないので、schema.xmlのtext_jaに組み込みます。

<fieldType name="text_ja" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="false">
  <analyzer>
    <tokenizer class="solr.JapaneseTokenizerFactory" mode="search" userDictionary="lang/userdict_ja.txt"/>
    <!-- ↓↓↓ この1行を追加 -->
    <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
    <filter class="solr.JapaneseBaseFormFilterFactory"/>
    <filter class="solr.JapanesePartOfSpeechStopFilterFactory" tags="lang/stoptags_ja.txt" enablePositionIncrements="true"/>
    <filter class="solr.CJKWidthFilterFactory"/>
    <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_ja.txt" enablePositionIncrements="true" />
    <filter class="solr.JapaneseKatakanaStemFilterFactory" minimumLength="4"/>
    <filter class="solr.LowerCaseFilterFactory"/>
  </analyzer>
</fieldType>

synonyms.txtという名前で同義語の辞書を設定します。

同義語辞書の初期状態

今回は「タワー」で検索した場合に「東京タワー」も含めたいので、以下のように辞書登録します。

同義語辞書に単語を登録

同義語辞書に単語を登録した後、tomcatを再起動して再度solr adminで検索します。

同義語辞書に単語を登録した後の検索結果

文章1がヒットするようになりました。これで今回の目標は達成です。

前述のように、「東京タワー」を形態素辞書に等速したために「タワー」の検索にヒットしなくなる等、以外と難しいのです。

他にも、同義語辞書は aaa,bbb => ccc という記述をすると、aaaとbbbをcccとみなすという機能がありますが、 インデックスの量(インデックス生成時間)を増やさないようにすると => で寄せる方がお得ですが、 デプロイ直後はaaa・bbbで検索した場合、クエリがcccに置き換えられるため、検索にヒットしなくなります。

=>を使う場合、インデックス生成をして初めて検索にヒットします。

インデックス生成完了を待てる場合は=>で単語を寄せた方が圧倒的に効率がいいです。

インデックス生成完了を待てない場合は、単語を寄せずに aaa,bbb,ccc という3つの単語を全てインデクシングする必要があります。

この単語を寄せるかどうかも判断する必要があるのです。

運用上タイムラグが許されるか、今回登録する単語によって別の単語がヒットしなくならないか。

辞書登録は以外と難しいのです。

◯ 広告