Kuromojiユーザ辞書に定義済みの単語で構成された複合語の形態素解析について

Elasticsearch Version: 5.6.14

Kuromojiを使って形態素解析を行っているのですが、ユーザー辞書に登録済みの単語で構成された複合語を定義した際に想定通りに文章の単語分割が行われませんでした。

実施したいこと

  • 文章にホタテが含まれる場合は、ホタテを1つの単語として取り扱いたい
  • 文章にホタテのりが含まれる場合は、ホタテのりを1つの単語として取り扱いたい

設定例

curl -X PUT "localhost:9200/sample_index?pretty" -H 'Content-Type: application/json' -d'
{
  "settings": {
    "index": {
      "analysis": {
        "tokenizer": {
          "kuromoji_user_dict": {
            "type": "kuromoji_tokenizer",
            "mode": "search",
            "user_dictionary": "sample.dict"
          }
        },
        "analyzer": {
          "my_kuromoji_analyzer": {
            "type": "custom",
            "tokenizer": "kuromoji_user_dict"
          }
        }
      }
    }
  }
}
'

sample.dict

ホタテ,ホタテ,ホタテ,名詞
のり,のり,ノリ,名詞
ホタテのり,ホタテのり,ホタテノリ,名詞
唐辛子のり,唐辛子のり,トウガラシノリ,名詞

複合語の構成要素が全てユーザ辞書に登録されている場合のAnalyze結果

curl -XGET 'localhost:9200/sample_index/_analyze?pretty&analyzer=my_kuromoji_analyzer' -d 'ホタテのり'

実行結果

{
  "tokens" : [
    {
      "token" : "ホタテ",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "のり",
      "start_offset" : 3,
      "end_offset" : 5,
      "type" : "word",
      "position" : 1
    }
  ]
}

複合語の構成要素の一部がユーザ辞書に登録されている場合のAnalyze結果

curl -XGET 'localhost:9200/sample_index/_analyze?pretty&analyzer=my_kuromoji_analyzer' -d '唐辛子のり'

実行結果

{
  "tokens" : [
    {
      "token" : "唐辛子のり",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "word",
      "position" : 0
    }
  ]
}

書き方の誤りや、他に良い方法がありましたら、ご教示いただきたいです。
よろしくお願いいたします。

お試しいただいているとおり、ユーザ辞書に登録した文字列は、tokenとして取得することを期待する定義になります。

ホタテ,ホタテ,ホタテ,名詞
のり,のり,ノリ,名詞

この2つの定義が存在するため、"ホタテ","のり" の単位で形態素解析結果が得られていると思われます。

具体的には、形態素解析機である kuromoji において、ユーザ辞書登録された文字列はtokenとして得やすくなるように、コスト計算処理の考慮(=辞書登録された文字列のコストを下げ、分割されやすくする)が行われます。
このため、"ホタテのり"という1tokenよりも、"ホタテ","のり"という文字列部分のコストがより低くなり、tokenが2つ得られる結果になります。
当然に、別の文字列 "AAA","BB" と "AAABB" であっても同様の挙動になりえます。

実施したいこと
文章にホタテが含まれる場合は、ホタテを1つの単語として取り扱いたい
文章にホタテのりが含まれる場合は、ホタテのりを1つの単語として取り扱いたい

単に形態素解析結果を上記のとおりにするのであれば、"ホタテ","のり" いずれか(もしくは両方)のユーザ辞書登録を除くのが早いです。
また、記載いただいた文字列はサンプルであり実態と異なるかもしれませんが、kuromojiデフォルトで1tokenになる文字列(一般的な表現)をユーザ辞書登録すると、kuromojiデフォルトの単語コストが変わることでデメリットも出る可能性があります。

例)「のりのりなお笑い芸人」といったフレーズにおいて、助動詞(体言接続)の「な」となっていた部分が「なお」に変わってしまう。
「のり」を登録しない場合→「のりのり|な|お笑い|芸人」
「のり」を登録した 場合→「のり|のり|なお|笑い|芸人」

"ホタテ","のり"の部分がそもそもユーザ辞書登録を必要とするような文字列の場合は、形態素解析結果を期待どおりのふるまいにするにはユーザ辞書登録だけでは難しいかと思います(kuromoji_analyzerそのものが使っている辞書に手を入れるなどしないと..)。
検索用途で検索対象のドキュメントにHitさせる目的であれば、以下Elasticのブログ記事のように形態素解析以外のインデックスも併用した検索を行うなど、やりようがあるように思います。

Elasticsearchで日本語の全文検索の機能を実装する

以上ご参考まで。

1 Like

ありがとうございます!
非常にわかりやすく教えていただき、とても理解しました。
ユーザ辞書の単語追加はkuromojiデフォルトの形態素解析結果を踏まえて実施するのはもちろんのこと、辞書だけで解決しようとせず、N-Gramを併用するなど他の手段も考慮に入れて実装しようと思います。