ここでは非常にシンプルな例で、「形態素解析とN-Gramのindexによるハイブリッドな検索をOSS(オープンソースソフトウエア)の「Elasticsearch」でどのように実現するか」を見てみます。なお本稿では、Elasticsearchの詳細な設定などは省略し、とても簡略化した説明になることをお許しください。Elasticsearchとは何かについては、少し古いですが、下記が参考になると思います。
Elasticsearchで形態素解析を行う場合、最も手軽な方法の一つはOSSの形態素解析エンジン「Kuromoji」を利用することです。
例えば、形態素解析とN-Gramのハイブリッドな検索を実現するために、以下のようなjson形式のテンプレートを用います。
- {
- "template": "sample*",
- "settings": {
- "analysis" : {
- "analyzer" : {
- "ja-ma-analyzer" : {
- "type" : "custom",
- "tokenizer" : "ja-ma-tokenizer"
- },
- "ja-2gram-analyzer" : {
- "type" : "custom",
- "tokenizer" : "ja-2gram-tokenizer"
- }
- },
- "tokenizer": {
- "ja-ma-tokenizer": {
- "type": "kuromoji_tokenizer",
- "mode": "normal"
- },
- "ja-2gram-tokenizer": {
- "type" : "nGram",
- "min_gram" : "2",
- "max_gram" : "2"
- }
- }
- }
- },
- "mappings": {
- "_default_" : {
- "dynamic_templates" : [
- {
- "sample_text_for_ma" : {
- "match" : "text-ja-ma",
- "mapping" : {
- "type" : "string",
- "store" : "no",
- "analyzer" : "ja-ma-analyzer"
- }
- }
- },
- {
- "sample_text_for_2gram" : {
- "match" : "text-ja-2gram",
- "mapping" : {
- "type" : "string",
- "store" : "no",
- "analyzer" : "ja-2gram-analyzer"
- }
- }
- }
- ]
- }
- }
- }
- }
ポイントとなる点は、形態素解析用のアナライザー「ja-ma-analyzer」(中身は形態素解析用のトークナイザー「ja-ma-tokenizer」)と2-Gram用のアナライザー「ja-2gram-analyzer」(中身は2-Gram用のトークナイザー「ja-2gram-tokenizer」)を用意している点です。これらのアナライザーを適用するフィールドをそれぞれ「text-ja-ma」と「text-ja-2gram」としています。
それでは、このテンプレートが適用されるindexに次のようなドキュメントを登録してみましょう。
- { "index": { "_index": "sample", "_type": "at_it", "_id": "1"}}
- { "text-ja-ma": "中目黒の桜", "text-ja-2gram": "中目黒の桜"}
- { "index": { "_index": "sample", "_type": "at_it", "_id": "2"}}
- { "text-ja-ma": "目黒の桜", "text-ja-2gram": "目黒の桜"}
IDが「1」のドキュメントは「中目黒の桜」、IDが「2」のドキュメントは「目黒の桜」が登録されています。それぞれ、形態素解析用のフィールドと2-Gram用のフィールドに同じ文章を登録するように指示しています。
それでは、この二つのドキュメントが登録されたindexに「中目黒」と「目黒」で検索を行ってみましょう。初めに「中目黒」を検索します。クエリのフォーマットは次のようになります。
- $ curl -XGET localhost:9200/sample/at_it/_search?pretty -d '
- {
- "query": {
- "query_string": {
- "query": "text-ja-ma:\"中目黒\"^100 OR text-ja-2gram:\"中目黒\"^10"
- }
- }
- }
- '
ポイントとしては、形態素解析のフィールド「text-ja-ma」には、重み100を指定し、2-Gram用のフィールド「text-ja-ma」には重み10を指定している点です(5行目)。これは「2-Gram用のフィールドに対して、形態素解析のフィールドを10倍重要視しなさい」とElasticsearchに指定していることになります。
「中目黒」を検索した結果は次のようになるでしょう。
- {
- "took" : 3,
- "timed_out" : false,
- "_shards" : {
- "total" : 5,
- "successful" : 5,
- "failed" : 0
- },
- "hits" : {
- "total" : 1,
- "max_score" : 0.21062575,
- "hits" : [ {
- "_index" : "sample",
- "_type" : "at_it",
- "_id" : "1",
- "_score" : 0.21062575,
- "_source":{ "text-ja-ma": "中目黒の桜", "text-ja-2gram": "中目黒の桜"}
- } ]
- }
- }
特に違和感なく「中目黒」を持つドキュメント「1」が返却されます(15行目)。
次に、問題となっていた「目黒」を検索してみます。クエリは、先ほどと同様に次のようになります。
- $ curl -XGET localhost:9200/sample/at_it_sample/_search?pretty -d '
- {
- "query": {
- "query_string": {
- "query": "text-ja-ma:\"目黒\"^100 OR text-ja-2gram:\"目黒\"^10"
- }
- }
- }
- '
結果は次のようになるはずです:
- {
- "took" : 5,
- "timed_out" : false,
- "_shards" : {
- "total" : 5,
- "successful" : 5,
- "failed" : 0
- },
- "hits" : {
- "total" : 2,
- "max_score" : 0.16793148,
- "hits" : [ {
- "_index" : "sample",
- "_type" : "at_it_sample",
- "_id" : "2",
- "_score" : 0.16793148,
- "_source":{ "text-ja-ma": "目黒の桜", "text-ja-2gram": "目黒の桜"}
- }, {
- "_index" : "sample",
- "_type" : "at_it_sample",
- "_id" : "1",
- "_score" : 0.002352859,
- "_source":{ "text-ja-ma": "中目黒の桜", "text-ja-2gram": "中目黒の桜"}
- } ]
- }
- }
この結果でポイントとなるのは、次の2点です。
特に注目すべき点は、「中目黒」を含むドキュメント「1」はスコアが「0.002352859」である(22行目)のに対し、「目黒」を含むドキュメント「2」はスコアが「0.16793148」と桁違いに大きな値となっている点です(16行目)。これはクエリの5行目で指定した次の2点の意図を汲んだ結果となっています。
以上により、形態素解析とN-Gramによるハイブリッドな検索を実現し、『「目黒」のクエリに対して、「中目黒」を含めるか否か?』という問題を、バランスを取る問題に変えることができました。
しかし残念ながら、メリットばかりでなく、「indexサイズが肥大化する」というデメリットもあります。
indexのサイズが形態素解析とN-Gramの2つ分用意する必要があるため、とても大きくなってしまいます。「メリット・デメリットのどちらを取るか?」に関しても、残念ながら一般的な回答を与えることは難しい問題です。
さて、これまでの議論で、適合率と再現率のバランスの良い検索結果を、形態素解析とN-Gramのハイブリッドなindexで実現できました。ここまで実現できれば、後は次のような施策を行うことにより、適合率・再現率を上げていくことが容易になります。
しかし、何か重要なことを見逃していないでしょうか。これまでに、以下の二つの問題点を十分に考慮せずにいたのです。
「カスタマーの本来求めていたドキュメント」の説明の際、それはあくまで理想の検索結果と説明しました。一般的に、全ての「カスタマーの本来求めていたドキュメント」を収集して、検索結果と突き合わせ、適合率・再現率を求めることは非現実的です。
現実的な「カスタマーの本来求めていたドキュメント」は、用意可能な量のサンプルになり、「機械学習」の枠組みで考えれば、あらかじめ用意された正解データであり、教師あり学習における訓練データ、教師データです。
多くの場合、次の2つが「カスタマーの本来求めていたドキュメント」のソースとなるでしょう。
それでは、これらのデータを用いて検索の精度を測るにはどのようにしたらよいでしょうか? そして、もう一つ考慮せずにいた検索ランキングの問題は解決できるでしょうか? ここで、大きく問題の転換を図ります。
「カスタマーの本来求めていたドキュメント」を直接追い求めるのではなく、「カスタマーが求めている順番で検索結果を並べ替える」ランキングを追い求める。
このように、問題を転換して考えることにより、用意可能な「カスタマーの本来求めていたドキュメント」を理想の順序に並べ、検索システムの返却する結果の順序が、その理想の順序に近づくように改善することが主眼となります。
これより先では、検索の精度は、検索ランキングの精度の問題となります。
Copyright © ITmedia, Inc. All Rights Reserved.
Java Agile 鬮ォ�ェ陋滂ソス�ス�コ闕オ譁溷クキ�ケ譎「�ス�ウ驛「�ァ�ス�ュ驛「譎「�ス�ウ驛「�ァ�ス�ー