ローマ字入力のゆれと読み(JapaneseCompletionAnalyzerその2)

Posted by johtani on Wednesday, August 10, 2022

目次

前回は日本語用オートコンプリートのためのAnalyzerとして、どうやって使うのかを簡単に紹介しました。

今回はもう少し、いろんなパターンを試してみたいと思います。

ローマ字入力のゆれ

前回のサンプルでも「吾輩…」のデータをサジェストするためのサンプルとして、「wagah」という「わがはい」をローマ字にしたものを利用しました。

ちなみにローマ字というとどんなものを思い起こしますか? 普通は学校で習ったヘボン式あたりだと思います。 Kuromojiの読みでローマ字出力するには通常、kuromoji_readingformでuse_romajiをtrueにします。 以下、「吾輩」のサンプルです。

GET _analyze
{
  "text": ["吾輩"],
  "tokenizer": "kuromoji_tokenizer",
  "filter": [
    {
      "type": "kuromoji_readingform",
      "use_romaji": true
    }
  ]
}

レスポンス

{
  "tokens": [
    {
      "token": "wagahai",
      "start_offset": 0,
      "end_offset": 2,
      "type": "word",
      "position": 0
    }
  ]
}

「吾輩」の場合は特に問題ないのですが、ローマ字による日本語入力の場合、次のような単語を入力するときに、ゆれが生じます。

  • シャボン=「shabon」
  • 新橋=「shimbashi」

括弧内はkuromoji_readingformで出力したもの(=ヘボン式のローマ字)です。 ですが、ローマ字入力の場合は「syabon」や「shabon」と入力したり、「sinbasi」や「sinnbasi」と入力すると思います。 JapaneseCompletionAnalyzerを利用したCompletion Suggesterの場合、これらのゆれも考慮してサジェストしてくれるようになっています。 前回のサンプルデータを用いると次のような感じです。

GET aozora_index/_search
{
  "explain": true, 
  "suggest": {
    "title-suggest": {
      "prefix": "sha",
      "completion": {
        "field": "suggest_ja"
      }
    }
  }
}


GET aozora_index/_search
{
  "explain": true, 
  "suggest": {
    "title-suggest": {
      "prefix": "sya",
      "completion": {
        "field": "suggest_ja"
      }
    }
  }
}

これらのリクエストは、「しゃ」について2パターンになっていますが、結果はどちらも次のものが返ってきます(このレスはsyaのレスになります)。

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 0,
      "relation": "eq"
    },
    "max_score": null,
    "hits": []
  },
  "suggest": {
    "title-suggest": [
      {
        "text": "sya",
        "offset": 0,
        "length": 3,
        "options": [
          {
            "text": "シャドウ・ワーク",
            "_index": "aozora_index",
            "_id": "000739",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "シャドウ・ワーク",
                "朝倉 克彦"
              ]
            }
          },
          {
            "text": "シャボン玉",
            "_index": "aozora_index",
            "_id": "045054",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "シャボン玉",
                "豊島 与志雄"
              ]
            }
          },
          {
            "text": "上海",
            "_index": "aozora_index",
            "_id": "050899",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "上海",
                "横光 利一"
              ]
            }
          },
          {
            "text": "斜坑",
            "_index": "aozora_index",
            "_id": "002095",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "斜坑",
                "夢野 久作"
              ]
            }
          },
          {
            "text": "社会事情と科学的精神",
            "_index": "aozora_index",
            "_id": "053864",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "社会事情と科学的精神",
                "石原 純"
              ]
            }
          }
        ]
      }
    ]
  }
}

「sinnba」や「shinba」も試してみました。

GET aozora_index/_search
{
  "explain": true, 
  "suggest": {
    "title-suggest": {
      "prefix": "sinnba",
      "completion": {
        "field": "suggest_ja"
      }
    }
  },
  "_source": ["suggest_ja"]
}

GET aozora_index/_search
{
  "explain": true, 
  "suggest": {
    "title-suggest": {
      "prefix": "shinba",
      "completion": {
        "field": "suggest_ja"
      }
    }
  },
  "_source": ["suggest_ja"]
}

これらも同じ結果です。

{
  "took": 0,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 0,
      "relation": "eq"
    },
    "max_score": null,
    "hits": []
  },
  "suggest": {
    "title-suggest": [
      {
        "text": "shinba",
        "offset": 0,
        "length": 6,
        "options": [
          {
            "text": "しんばい",
            "_index": "aozora_index",
            "_id": "044942",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "しんばい",
                "村山 籌子"
              ]
            }
          },
          {
            "text": "新橋",
            "_index": "aozora_index",
            "_id": "002409",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "新橋",
                "北原 白秋"
              ]
            }
          }
        ]
      }
    ]
  }
}

入力されるパターンを想定した実装がされているので対応できている形です。 使い方が楽なだけでなく、より使いやすくなってるのは便利ですね。

うまくいかないパターン

万事OKかというと残念ながらそうでもありません。 うまくいかないパターンもあります(これが本当にうまくいかないかは難しい話な気がしますが)。 「日本」という漢字ですが、「にほん」「にっぽん」どちらとも読めますよね? これらを試してみると次のようになります(リクエストは省略します)。

まず「にほん」

{
  "took": 0,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 0,
      "relation": "eq"
    },
    "max_score": null,
    "hits": []
  },
  "suggest": {
    "title-suggest": [
      {
        "text": "にほん",
        "offset": 0,
        "length": 3,
        "options": [
          {
            "text": "日本橋",
            "_index": "aozora_index",
            "_id": "004565",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "日本橋",
                "泉 鏡花"
              ]
            }
          },
          {
            "text": "日本橋",
            "_index": "aozora_index",
            "_id": "045357",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "日本橋",
                "牧野 信一"
              ]
            }
          },
          {
            "text": "日本橋あたり",
            "_index": "aozora_index",
            "_id": "047648",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "日本橋あたり",
                "長谷川 時雨"
              ]
            }
          },
          {
            "text": "日本橋附近",
            "_index": "aozora_index",
            "_id": "055666",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "日本橋附近",
                "田山 花袋"
              ]
            }
          }
        ]
      }
    ]
  }
}

次に「にっぽん」

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 0,
      "relation": "eq"
    },
    "max_score": null,
    "hits": []
  },
  "suggest": {
    "title-suggest": [
      {
        "text": "にっぽん",
        "offset": 0,
        "length": 4,
        "options": [
          {
            "text": "日本アルプスの五仙境",
            "_index": "aozora_index",
            "_id": "056269",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "日本アルプスの五仙境",
                "木暮 理太郎"
              ]
            }
          },
          {
            "text": "日本出版協会論",
            "_index": "aozora_index",
            "_id": "049436",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "日本出版協会論",
                "嶋中 雄作"
              ]
            }
          },
          {
            "text": "日本文化と科学的思想",
            "_index": "aozora_index",
            "_id": "055750",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "日本文化と科学的思想",
                "石原 純"
              ]
            }
          },
          {
            "text": "日本文化のために",
            "_index": "aozora_index",
            "_id": "003183",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "日本文化のために",
                "宮本 百合子"
              ]
            }
          },
          {
            "text": "日本文化の特殊性",
            "_index": "aozora_index",
            "_id": "055290",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "日本文化の特殊性",
                "戸坂 潤"
              ]
            }
          }
        ]
      }
    ]
  }
}

最後に「日本」

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 0,
      "relation": "eq"
    },
    "max_score": null,
    "hits": []
  },
  "suggest": {
    "title-suggest": [
      {
        "text": "日本",
        "offset": 0,
        "length": 2,
        "options": [
          {
            "text": "日本アルプスの五仙境",
            "_index": "aozora_index",
            "_id": "056269",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "日本アルプスの五仙境",
                "木暮 理太郎"
              ]
            }
          },
          {
            "text": "日本出版協会論",
            "_index": "aozora_index",
            "_id": "049436",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "日本出版協会論",
                "嶋中 雄作"
              ]
            }
          },
          {
            "text": "日本文化とは何ぞや(其二)",
            "_index": "aozora_index",
            "_id": "002940",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "日本文化とは何ぞや(其二)",
                "内藤 湖南"
              ]
            }
          },
          {
            "text": "日本文化と科学的思想",
            "_index": "aozora_index",
            "_id": "055750",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "日本文化と科学的思想",
                "石原 純"
              ]
            }
          },
          {
            "text": "日本文化のために",
            "_index": "aozora_index",
            "_id": "003183",
            "_score": 1,
            "_source": {
              "suggest_ja": [
                "日本文化のために",
                "宮本 百合子"
              ]
            }
          }
        ]
      }
    ]
  }
}

最初の「にほん」は「日本橋」がヒットしているようです。返ってきたoptionsの数は4件しかなく、どうやらこれがすべてのようです(取得件数はデフォルト5件)。 2番目の「にっぽん」は「日本」という単語から始まるものがヒットしていますが、「日本橋」はなさそうでした。 最後に「日本」という漢字を入力にした場合は2つの合計が帰ってきているようです(今回のレスポンス例にはありませんが、サイズを大きくすると「日本橋」が帰ってきていました)。

どうしてそうなるの?

JapaneseCompletionAnalyzerの基本的な動作としては、

  1. Kuromojiが単語に区切る
  2. 区切られた単語ごとに読みを持っている
  3. 元の単語と読みをローマ字にしたものが最終的に出てくる

というような動きです。 この時、単語には「1つ」の読みが対応しています。 読みから単語を探そうとすると、読みの違い(ゆれ)を吸収することはできないためです。 JapaneseCompletionAanlyzerは3点目のローマ字のゆれに対応していますが、入力となる読みのゆれまでは対応できないです。 対応しているといいかどうかというのも難しい判断になる気もします。。。

そのほかの例としては次のようなものもあります。 これは、今回のオートコンプリートの話とは少しずれてるかもしれませんが、1番目の処理の結果が変わってくる例です。 「南方熊楠」という名前の間にスペースがあるかないかで、出力される読みが変わってくる例です。

GET _analyze
{
  "text": ["南方 熊楠"],
  "tokenizer": "kuromoji_tokenizer",
  "filter": ["kuromoji_readingform"]
}

GET _analyze
{
  "text": ["南方熊楠"],
  "tokenizer": "kuromoji_tokenizer",
  "filter": ["kuromoji_readingform"]
}
# GET _analyze
{
  "tokens": [
    {
      "token": "ナンポウ",
      "start_offset": 0,
      "end_offset": 2,
      "type": "word",
      "position": 0
    },
    {
      "token": "クマグス",
      "start_offset": 3,
      "end_offset": 5,
      "type": "word",
      "position": 1
    }
  ]
}
# GET _analyze
{
  "tokens": [
    {
      "token": "ミナカタ",
      "start_offset": 0,
      "end_offset": 2,
      "type": "word",
      "position": 0
    },
    {
      "token": "クマグス",
      "start_offset": 2,
      "end_offset": 4,
      "type": "word",
      "position": 1
    }
  ]
}

人名や地名は1つの漢字に対して多くの読みが存在しているので大変です。。。 どうしてこれは出てこないんだろう?と不思議に思った場合は、_analyze APIを使ってどういう動きなのかを見てみるのがいいかと。

まとめ

ということで、少しだけですが、こんなことができるよ、できないよというのを書いてみました。 挙動をしっていれば、なんでこうなってるんだっけ?といったことを調べる役に立つかと思います。 入力された文字をもとにどれをサジェストするかなどについてはもう少し違う動きなので、中の仕組みをそろそろ書かないとなぁ。

参考


comments powered by Disqus