類義語を同じようにスコアリングしたい

類義語のスコアリングが低いように思えるので、同じようにスコアリングしたいと思っています。

"settings": {
"analysis":
{"analyzer":
{"my_analyzer":
{"type": "custom", "tokenizer": "kuromoji_tokenizer",
"char_filter":
["icu_normalizer",],
"filter":
["synonym", "kuromoji_baseform",
"kuromoji_number", "kuromoji_stemmer"
],
}
},
"filter": {
"synonym": {
"type": "synonym",
"ignore_case": "true",
"expand": "true",
"synonyms": [
"canon, キヤノン, キャノン, CANON",
"ミニデスク, デスク"]
}}}}

のようなセッティングがあった時、
canonと検索した時と、キャノンと検索した時で同じ件数が欲しいのですが、
canonで出てくる件数と、キャノンで出てくる件数が違います。
ミニデスクと検索した時もデスクと件に乖離があります。
他にも類義語を登録していますが、ノイズと言えるようなものはないため、それらが影響しているとは考えにくいです。
ご知恵があれば、よろしくお願い致します。

「思えるので」とありますが、実際に使われている、クエリやマッピングを見せてもらうことは可能でしょうか?
また、バージョンや実際にクエリを投げた時の件数なども提示してもらえますでしょうか?

早速のご返信ありがとうございます!

バージョンは5.3

クエリは
curl -XGET 'https://xxxxxxxxxxxxxxxx.amazonaws.com/hogehoge/_search?pretty' -d ' { "query":{"match":{"name":"canon"}}

canon "total" : 200,
キャノン "total" : 28,

デスク "total" : 5,
ミニデスク "total" : 3,

マッピングは、
"mappings":
{"hogehoge":
{"properties":
{"name":
{"type": "text",
"analyzer": "my_analyzer"
}
}}}

ただ、程度こそあれ類義語が反映されていることは確かだと思えます。
なぜなら、piano,ピアノの時はほぼ同じように件数が出ました。
これは、類義語を反映する前に見られなかったことですし、
pianoと検索した時に、pianoというnameがないのに、
しっかりと、ピアノに反応してくれているようです。

不足があれば、ご指摘いただければと存じます。

お答えいただけると幸いでございます。
よろしくお願い致します。

my_analyzerも教えてください。

ご返信ありがとうございます。

my_analyzerは、

        {"analysis":
            {"analyzer":
                 {"my_analyzer":
                      {"type": "custom", "tokenizer": "kuromoji_tokenizer",
                       "char_filter":
                           ["icu_normalizer", "kuromoji_iteration_mark"],
                       "filter":
                           ["synonym", "kuromoji_baseform",
                            "kuromoji_part_of_speech", "ja_stop",
                            "kuromoji_number", "kuromoji_stemmer"
                            ]
                       }

                  },
             "filter": {
                 "synonym": {
                     "type": "synonym",
                     "synonyms": [
                         "canon, キヤノン, キャノン、CANON",
                          "ミニデスク, デスク"]

です。

よろしくお願い致します。

今回挙げていただいた類義語に関しては、「キャノン」の後のカンマ(",")が読点("、")になっているので、「キャノン」で検索した場合に、はSynonymが展開されていないです。
ですので、件数が違うのだと思います。

きちんとSynonymがうまく動いているかをチェックするのに、_analyze APIが便利です。
こちらを利用して試してみてはどうでしょうか?

ご返信ありがとうございます。

ミニコンポも類義語に追加し、
ミニコンポ、コンポで分析したところ、

{
"analyzer" : "my_analyzer",
"text" : "コンポ"}'
{"tokens":[{"token":"コンポ","start_offset":0,"end_offset":3,"type":"word","position":0},{"token":"ミニコンポ","start_offset":0,"end_offset":3,"type":"SYNONYM","position":0}]}

{
"analyzer" : "my_analyzer",
"text" : "ミニコンポ"}'
{"tokens":[{"token":"ミニコン","start_offset":0,"end_offset":4,"type":"word","position":0},{"token":"ポ","start_offset":4,"end_offset":5,"type":"word","position":1}]}

これは"ミニコン"がkuromojiの形態素解析で優先的に分割されて、その後類義語が適応されたため、
同一視できなかったということでしょうか。
となると、類義語ではなく、形態素解析の辞書の問題といことになるでしょうか。

立て続けの返信申し訳ございません。
お答えいただけると、幸いでございます。
よろしくお願いいたします。

はい。ご想像の通りです。

Synonym Filterのプロパティでtokenizerというのがあります。
ここに、kuromoji_tokenizerを指定すれば検索にヒットしたり、類義語展開されるようにはなると思います。
このプロパティはsynonymの設定を読み込む時に、tokenizerを利用するかどうかというものになります。
synonymはtokenizerの後に動くので、tokenizerで区切られたものをもとに類義語かどうかをチェックできるようにするべきとなります。

ただし、char_filterの部分の処理はsynonymのtokenizerはうまく処理してくれないという欠点があるので注意です。
(これは6では指定しなくてもきちんと動くようになる予定です。)

ご返信ありがとうございます!

つまり、tokenizerで区切られたものをもとに空欄を設けてやるべきだとなりますでしょうか?

"synonyms": [
"ミニコン ポ, コンポ"
]

の設定(ミニコンの後に空欄を設けた)で、ちゃんと、ミニコンポと検索をかけてもコンポが引っかかるようになり、類義語とみなしてくれたように見えます。(実際に引っかかった)
そこで、やはり、上記が正しいとすると、等価類義語ということで、ミニコンポとコンポというキーワードは同等扱いになってしますと思います。ミニコンポと検索して、ミニコンポを優先したいときは、esの機能では難しく、形態素解析の辞書にミニコンではなく、ミニで分割するように登録するか、ユーザの行動データを機械学習させて、検索結果に反映させるのが解決策なのでしょうか。

また、"synonym" : {
"tokenizer" : "keyword",
"filter" : ["synonym"]
}
それや、
"synonym" : {
"tokenizer" : "false",
"filter" : ["synonym"]
}
でtokenizeせず完全一致で類義語の登録を図ろうとしましたが、
私のやり方が間違っているのか、コンポでミニコンポの検索、あるいはその逆ができませんでした。
synonymだけtokenizerさせない方法はあるのでしょうか?

お忙しい中、さらにご質問を重ねてしまい、申し訳ございません。
何卒、よろしくお願い致します。

つまり、tokenizerで区切られたものをもとに空欄を設けてやるべきだとなりますでしょうか?

はい、それでもOKです。
char_filterの処理もされているので、その処理も行った上の文字列を作る必要があります。

やはり、上記が正しいとすると、等価類義語ということで、ミニコンポとコンポというキーワードは同等扱いになってしますと思います。ミニコンポと検索して、ミニコンポを優先したいときは、esの機能では難しく、形態素解析の辞書にミニコンではなく、ミニで分割するように登録するか、ユーザの行動データを機械学習させて、検索結果に反映させるのが解決策なのでしょうか。

ここはちょっと意図がわからないのですが。
最初の質問では、類義語を設定したが、異なる件数が帰ってくるという話でした。
別の問題点もしくは、解決したいものがあるということでしょうか?
例えば、類義語展開する前のデータに検索してヒットした場合はそちらのスコアを上げたいなどでしょうか?

でtokenizeせず完全一致で類義語の登録を図ろうとしましたが、

Analyzerはchar_filter/tokenizer/filterの組み合わせで構成されており、それぞれの出力をもとにデータを処理します。
synonymだけtokenizerをkeywordに変えても、synonym filterの入力としてそもそも分割された単語が来た場合には対処できないです。
どのように入力文字列が変化していき、転置インデックスの単語として使われるかを見るためにはanalyze APIのexplainをtrueにするとより詳細が分かるようになります。

https://www.elastic.co/guide/en/elasticsearch/reference/current/_explain_analyze.html

ご返信ありがとうございます!

別の問題点もしくは、解決したいものがあるということでしょうか?
例えば、類義語展開する前のデータに検索してヒットした場合はそちらのスコアを上げたいなどでしょうか?

おっしゃる通りでございます。
ECサイトなのですが、ユーザーが入力したキーワードに完全に一致したもののスコアリングを類義語より若干上げた方が
よりユーザーのニーズを満たすだろうという考えに基づいております。
ミニコンポと検索して、ミニコンポが上位、コンポがその下にくることができたならば、今回の質問のゴールでございます。

また、

synonymだけtokenizerをkeywordに変えても、synonym filterの入力としてそもそも分割された単語が来た場合には対処できないです。

単語を分割する前にsynonymのセッテッィングをすることは可能でしょうか?
例えば、類義語に"ミニコンポ, コンポ"とあれば、ミニコンポ、あるいはコンポという一連の文字列が来た場合、形態素解析されず、キーワードとして独立するというイメージです。それが可能ですと、ミニコンポと検索しても、ミニコンポとコンポを検索に引っ掛けることが可能だと思っています。

ご返信、本当にありがたいです。

何卒、よろしくお願い致します。

そういう場合は、synonymを利用するフィールドと、使用しないフィールドを用意して、
両方に検索しつつ、使用しないフィールド側のスコアをあげるという方法があるかと思います。

単語を分割する前にsynonymを設定することはできないです。
そのようなことをやりたい場合は、検索のクエリを投げる前にやる方法が一般的です。

引き続き、ご返信ありがとうございます!

そういう場合は、synonymを利用するフィールドと、使用しないフィールドを用意して、
両方に検索しつつ、使用しないフィールド側のスコアをあげるという方法があるかと思います。

なるほど、そういうやり方があるのですね。
この方法は非常に参考になりました。

そのようなことをやりたい場合は、検索のクエリを投げる前にやる方法が一般的です。

上記の解釈ですが、

クエリーを完全一致のkyewordの"ミニコンポ"で検索するように投げればよいが、それだと、形態素解析された"ミニコ/ン" のインデックスには引っかからないから意味がない。

あるいは、検索するクエリーに対してもsynonymを設定し、トークナイズすれば、検索にミニコンポでコンポを引っ掛けることができる。(これだと、esはデフォルトでインデックスのアナライズと同様なものをクエリーのキーワードに対してもしてないことになる?)

どちらになりますでしょうか。
また、上記の解釈にあまり自信がないため、
どちらの解釈も合ってない場合、もう少し、詳細を教えていただけると、とてもありがたいと思っております。

すみませんが、何卒、よろしくお願い致します。

「検索のクエリを投げる前にやる方法」は、ElasticsearchのSynonymは利用せず、
検索条件をアプリ側で組み立てる時点で、アプリ側で類義語の表を管理しつつ、独自に実装するという意味になります。
ElasticsearchやLuceneでは、Tokenizerが単語に分割した後に、Synonym Filterが動作する仕組みになっているためです。

ご返信ありがとうございます!

「検索のクエリを投げる前にやる方法」は、ElasticsearchのSynonymは利用せず、
検索条件をアプリ側で組み立てる時点で、アプリ側で類義語の表を管理しつつ、独自に実装するという意味になります。
ElasticsearchやLuceneでは、Tokenizerが単語に分割した後に、Synonym Filterが動作する仕組みになっているためです。

承知致しました。

一連のわかりやすい回答ありがとうございました!

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.