« ECMAScript 2015とPython間におけるJSONPを用いたやりとり | トップページ | Promiseよ、今まで君のことを理解できていなかったよ »

2020.12.30

Apache Solrを使って形態素解析とBigramを併用してテキスト検索してみる

以前、ローカルに保存したWebページをApache Solrを使って検索して見る記事を書きました。
ScrapBook及びSave Page WEで保存したWebページをApache Solrで検索できるようにしてみた(その1)
ScrapBook及びSave Page WEで保存したWebページをApache Solrで検索できるようにしてみた(その2)

もちろん、デフォルト設定でも検索できるのですが、使っているうちに検索漏れやノイズなど調整できないかと思いまして、今回、スキーマをカスタマイズして、形態素、Bigramを組み合わせて、あるいは切り替えて検索できるようにしてみましたので、その過程をメモ。

今回は、検索インデックスとして形態素とBigramを併用するにあたり、この記事を参考にしました。
形態素解析とNgramを併用したハイブリッド検索をSolrで実現する方法:ZOZO TECH BLOG
ありがとうございます。

一般に形態素解析する方法はノイズが小さい一方新語について検索漏れが生じる問題があり、他方でn-gramを用いる方法は新語を検索できるがノイズが大きくなる特徴があります。下に例示するスキーマでは、text_jaを形態素、text_ja_ngramをBigram (n-gramにおけるn=2)インデックスのフィールドとして定義していきます。さらに、日本語、中国語、韓国語の文字についてBigramとしてそれらの言語以外の文字についてBigramにしない設定もあり、下ではtext_cjkフィールドとして定義されています。

Core作成
コア名をscrapbookとする場合、solrディレクトリ内で次のコマンドを実行することでscrapbookというコアを作成します。


$ ./bin/solr start
$ ./bin/solr create -c scrapbook

スキーマ設定
次いで、[path to solr]/server/solr/[core name]/confにあるmanaged-schemaを編集します。以下、関係ある箇所を抜粋しています。各項目が既に存在する場合は変更し、存在しない場合には追記します。

Field Type定義


<fieldType name="text_ja" class="solr.TextField" autoGeneratePhraseQueries="false" positionIncrementGap="100">
<analyzer>
<tokenizer class="solr.JapaneseTokenizerFactory" mode="search" userDictionary="lang/userdict_ja.txt"/>
<filter class="solr.SynonymFilterFactory" expand="true" ignoreCase="true" synonyms="synonyms.txt"/>
<filter class="solr.JapaneseBaseFormFilterFactory"/>
<filter class="solr.JapanesePartOfSpeechStopFilterFactory" tags="lang/stoptags_ja.txt"/>
<filter class="solr.CJKWidthFilterFactory"/>
<filter class="solr.StopFilterFactory" words="lang/stopwords_ja.txt" ignoreCase="true"/>
<filter class="solr.JapaneseKatakanaStemFilterFactory" minimumLength="4"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>

<fieldType name="text_ja_ngram" class="solr.TextField" autoGeneratePhraseQueries="true" positionIncrementGap="100">
<analyzer>
<tokenizer class="solr.NGramTokenizerFactory" maxGramSize="2" minGramSize="2"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>

<fieldType name="text_cjk" class="solr.TextField" positionIncrementGap="100">
<analyzer>
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.CJKWidthFilterFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.CJKBigramFilterFactory"/>
</analyzer>
</fieldType>


text_cjkはデフォルトの設定のまま維持しています。

Field定義
次のとおりfieldを定義します。


<field name="search" type="text_ja" multiValued="true" indexed="true" required="false" stored="true"/>
<field name="search_ngram" type="text_ja_ngram" multiValued="true" indexed="true" required="false" stored="true"/>
<field name="search_cjk" type="text_cjk" multiValued="true" indexed="true" required="false" stored="true"/>

Fieldをcopy
最後に各項目のデータを各Fieldにコピーします。


<copyField source="abstract" dest="search"/>
<copyField source="abstract" dest="search_cjk"/>
<copyField source="abstract" dest="search_ngram"/>
<copyField source="description" dest="search"/>
<copyField source="description" dest="search_cjk"/>
<copyField source="description" dest="search_ngram"/>
<copyField source="keyword" dest="search"/>
<copyField source="keyword" dest="search_cjk"/>
<copyField source="keyword" dest="search_ngram"/>
<copyField source="keywords" dest="search"/>
<copyField source="keywords" dest="search_cjk"/>
<copyField source="keywords" dest="search_ngram"/>
<copyField source="title" dest="search"/>
<copyField source="title" dest="search_cjk"/>
<copyField source="title" dest="search_ngram"/>
....

スキーマ設定を有効化
下のとおり、solrを再起動するか、スキーマ定義ファイルをリロードすることによりスキーマ設定を有効化します。

再起動する方法はこちら。


$ ./bin/solr restart

Web APIを利用する方法はこちら。


$ curl "http://localhost:8983/solr/admin/cores?action=RELOAD&core=scrapbook"

インデックス作成
蓄積したHTMLファイルのインデックスを作成・登録する方法はScrapBook及びSave Page WEで保存したWebページをApache Solrで検索できるようにしてみた(その1)にあるとおりです。
もし、すでにインデックスを作成していた場合、新たなコアを作成するか、コア名を維持してインデックスを再作成する際には例えば下のようなコマンドにより一度インデックスを削除します。


$ curl -H 'Content-Type:application/json' 'http://localhost:8983/solr/[コア名]/update?commit=true' -d '{delete:{query:"*:*"}}'

続けてインデックスを作成します。

検索時のオプション設定
形態素及びBigramを組み合わせて検索する際には、次のオプションを設定します。


defType=edismax
qf=search^x search_ngram^y search_cjk^z

x, y, zは各フィールドの重みであり、実際には整数を指定します。そして、この重みの最適な組み合わせ(形態素、n-gram)をチューイングしていきます。

検索ワードを「新型コロナ」として検索した例です。スライドバーを調整することで形態素とBigramの重みを調整して表示順を変えることができます。
2020123001

text_cjkフィールドのみを指定して検索した例です。
2020123002

形態素とBigramを混合して検索ワードを「Transformer」として検索した例です。
2020123003

text_cjkフィールドのみを指定して検索した例です。
2020123004

Apache SolrのGUI(Webブラウザを介したマンマシンインターフェイス)の一つであるAnalysis機能を使ってインデックスと検索クエリの分割状態をみてみます。
検索ワードが「新型コロナ」、text_jaフィールド(形態素)
2020123005

検索ワードが「新型コロナ」、text_ja_ngramフィールド(Bigram)
2020123006

検索ワードが「新型コロナ」、text_ja_cjkフィールド(日本語のみBigram)
2020123007

検索ワードが「Transformer」、text_jaフィールド(形態素)
2020123008

検索ワードが「Transformer」、text_ja_ngramフィールド(Bigram)
2020123009

検索ワードが「Transformer」、text_cjkフィールド(日本語のみBigram)
2020123010

これから普段使いの中で、各パラメータと適合率や再現率の相関を確認しようと思います。

|

« ECMAScript 2015とPython間におけるJSONPを用いたやりとり | トップページ | Promiseよ、今まで君のことを理解できていなかったよ »

パソコン・インターネット」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




« ECMAScript 2015とPython間におけるJSONPを用いたやりとり | トップページ | Promiseよ、今まで君のことを理解できていなかったよ »