検索
連載

ElasticsearchとKuromojiを使った形態素解析とN-Gramによる検索の適合率と再現率の向上Elasticsearch+Hadoopベースの大規模検索基盤大解剖(2)(2/3 ページ)

リクルートの事例を基に、大規模BtoCサービスに求められる検索基盤はどう構築されるものなのか、どんな技術が採用されているのか、運用はどうなっているのかなどについて解説する連載。今回は、テンプレートを利用したインデックス生成など、検索結果の品質を向上させるためのさまざまな取り組みを紹介する。

PC用表示 関連情報
Share
Tweet
LINE
Hatena

ElasticsearchとKuromojiを使った形態素解析とN-Gramによるハイブリッドなindex

 ここでは非常にシンプルな例で、「形態素解析とN-Gramのindexによるハイブリッドな検索をOSS(オープンソースソフトウエア)の「Elasticsearch」でどのように実現するか」を見てみます。なお本稿では、Elasticsearchの詳細な設定などは省略し、とても簡略化した説明になることをお許しください。Elasticsearchとは何かについては、少し古いですが、下記が参考になると思います。


Elasticsearchの「インデックス構成(論理)」(ElasticSearch入門 @johtani(PDF)より引用)

 Elasticsearchで形態素解析を行う場合、最も手軽な方法の一つはOSSの形態素解析エンジン「Kuromoji」を利用することです。

Kuromojiを利用したjson形式のテンプレート

 例えば、形態素解析と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": { "_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. ドキュメント「1」「2」共に「目黒」を含むため、両方ヒットすること(10行目)
  2. 「中目黒」を含むドキュメント「1」に比べ、「目黒」を持つドキュメント「2」が高いスコア(「_score」の値)を持ち、上位に来ていること

 特に注目すべき点は、「中目黒」を含むドキュメント「1」はスコアが「0.002352859」である(22行目)のに対し、「目黒」を含むドキュメント「2」はスコアが「0.16793148」と桁違いに大きな値となっている点です(16行目)。これはクエリの5行目で指定した次の2点の意図を汲んだ結果となっています。

  1. 取りこぼしのないように「text-ja-ma」と「text-ja-2gram」の両方を検索対象とすること(「OR」で結合している部分)
  2. 「text-ja-ma」の重要度を「100」とし(「^100」と指定している部分)、「text-ja-2gram」の重要度を「10」とすること(「^10」と指定している部分)


indexサイズが肥大化する

 以上により、形態素解析とN-Gramによるハイブリッドな検索を実現し、『「目黒」のクエリに対して、「中目黒」を含めるか否か?』という問題を、バランスを取る問題に変えることができました。

 しかし残念ながら、メリットばかりでなく、「indexサイズが肥大化する」というデメリットもあります。

 indexのサイズが形態素解析とN-Gramの2つ分用意する必要があるため、とても大きくなってしまいます。「メリット・デメリットのどちらを取るか?」に関しても、残念ながら一般的な回答を与えることは難しい問題です。

適合率・再現率を上げていく施策

 さて、これまでの議論で、適合率と再現率のバランスの良い検索結果を、形態素解析とN-Gramのハイブリッドなindexで実現できました。ここまで実現できれば、後は次のような施策を行うことにより、適合率・再現率を上げていくことが容易になります。

  • 適合率の向上
    • 例:形態素解析でのカスタマー辞書の拡充(形態素解析の結果が思わしくない場合の対策)
    • 例:クエリ展開(例えば、多くの製品を扱っており、ある製品名「42」が洗濯機であるならば、自動的に「42 OR (42 AND 洗濯機)」などと展開)
  • 再現率の向上
    • 例)ノーマライズの拡充
    • 例)表記ゆれへの対応
    • 例)同義語への対応(例えば、住宅やリフォームなどが検索対象ならば、「バス」を「バス OR 浴室 OR 風呂」と展開)


これまで見逃していた2つの問題

 しかし、何か重要なことを見逃していないでしょうか。これまでに、以下の二つの問題点を十分に考慮せずにいたのです。

  • そもそも「カスタマーの本来求めていたドキュメント」とは何か?
  • 検索順位(検索ランキング)は問題にならないのか?

 「カスタマーの本来求めていたドキュメント」の説明の際、それはあくまで理想の検索結果と説明しました。一般的に、全ての「カスタマーの本来求めていたドキュメント」を収集して、検索結果と突き合わせ、適合率・再現率を求めることは非現実的です。

 現実的な「カスタマーの本来求めていたドキュメント」は、用意可能な量のサンプルになり、「機械学習」の枠組みで考えれば、あらかじめ用意された正解データであり、教師あり学習における訓練データ、教師データです。

 多くの場合、次の2つが「カスタマーの本来求めていたドキュメント」のソースとなるでしょう。

  • 人為的に作成した正解データ:主観的に作成した「クエリ」と「カスタマーの本来求めていたドキュメント」の対
  • 検索ログ:検索システムを利用したカスタマーが検索を行った際の「クエリ」と「アクションのログ」の対

 それでは、これらのデータを用いて検索の精度を測るにはどのようにしたらよいでしょうか? そして、もう一つ考慮せずにいた検索ランキングの問題は解決できるでしょうか? ここで、大きく問題の転換を図ります。

「カスタマーの本来求めていたドキュメント」を直接追い求めるのではなく、「カスタマーが求めている順番で検索結果を並べ替える」ランキングを追い求める。


 このように、問題を転換して考えることにより、用意可能な「カスタマーの本来求めていたドキュメント」を理想の順序に並べ、検索システムの返却する結果の順序が、その理想の順序に近づくように改善することが主眼となります。

 これより先では、検索の精度は、検索ランキングの精度の問題となります。

Copyright © ITmedia, Inc. All Rights Reserved.

ページトップに戻る