はてだBlog(仮称)

私的なブログど真ん中のつもりでしたが、気づけばWebサイト系のアプリケーション開発周りで感じたこと寄りの自分メモなどをつれづれ述べています。2020年6月現在、Elasticsearch、pandas、CMSなどに関する話題が多めです。...ですが、だんだんとより私的なプログラムのスニペット置き場になりつつあります。ブログで述べている内容は所属組織で販売している製品などに関するものではなく、また所属する組織の見解を代表するものではありません。

Elasticsearchの検索スコアリング(boost_mode、score_modeのsumとmultiplyの噛み合わせについてもう少し)

はじめに

この記事は次の記事のちょっとだけ補足(つづき)です。

itdepends.hateblo.jp

記事の概要

前の記事では、ひとことでいうとスコアリングは文字通り加点方針(条件に該当するとプラスが重なる方針)がオススメみたいなところを示唆しておりました。

スコアリングには、掛け算や平均をとったりということもできますが、加点の方が感覚的にわかりやすく・傾向も予測しやすいし、思ったより調子が悪い場合に、他の良いところを損なわずに調整するということも比較的やりやすいかなという思いからでした。

ただ、どこまで複雑にするかはともかく、やっぱり掛け算方式などにしたい場合もあるよね、ということで、私の場合、どんな時に掛け算方式にするかここに書きなぐってみます...というのがこの記事です。

boost_modeとscore_mode

この加算型か掛け算型かというのは、boost_modeとscore_modeです。*1

score_modeは、function句に並べ立てた「条件に該当した場合は、点数アップする」に該当したものどおしの掛け合わせの点数です。

score_modeがsumなら、条件に該当すればするほど、スコアがアップします。

multiply設定なら、条件に該当すればするほど、スコアが掛け算の倍々ゲームでスコアがアップします。ただし、掛け算なので、weightを1以下に設定したものの場合、実は減点相当として機能するということにも使えるかもしれません。

一方、boost_modeは、function句側のスコアリングの合計点ともともとの検索クエリのマッチングのスコアの合計方式になります。

前の記事のように、単純にsum設定なら合計ですし、ここをmultiplyにすると、検索クエリのマッチングのスコアとfunction句の「条件に該当した場合は、点数アップ」の掛け算が最終スコア値になります。

掛け算なので、特徴を際立たせる傾向がありそうですね。

なんども言いますが、multiply、sum以外にavg、first、maxなどいろいろありますので、もっとできるはずだという方は公式Rをあらためてご覧下さい.。

www.elastic.co

わがうんちく(boost_mode=multiply、score_mode=sum とするケースの例)

さて、ここから持論です。

スコアリングを掛け算方式とする例(ここではboost_modeの方のみmultiply)ですが、冒頭の先の記事で、スポットを検索するのに

①駅> ②ランドマーク> ③住所表記のエリア(市区郡町村レベル)

という3階級の序列があってという架空のシナリオを示しました。

が、それに加えて、

検索のマッチング度合い(例. 完全一致でヒットした、形態素解析でヒットした、N-gramでヒットした)やフィールドの序列も全て混在で、全体の(ほぼ)厳格な優先度を決めたい時

に使うと良いと感じています。

例えば、

形態素解析でヒットした①駅>

形態素解析でヒットした②ランドマーク>

形態素解析でヒットした③住所表記のエリア(市区郡町村レベル)> 

N-gramでヒットした①駅 > 

N-gramでヒットした②ランドマーク > 

N-gramdでヒットした③住所表記のエリア(市区郡町村レベル)

のような6階級の序列をもうけるといった具合です。

実のところ、私の2つの人格のうちより本音の人格の方は、3種間の序列を設けた時点である程度恣意的になるし、あとは検索のマッチング度で多少振れ幅があるぐらいの方が面白い結果になるのではという感覚です。

ただ、世の中にはなんでも「決定論」的に扱いたい世界もあるでしょうし、その方がバグも仕様として説明がつくというメリット(?)あるかもしれません。

なので、この組み合わせ6段階の例も、ユル要件でもまあある範囲かなと思います。

話を元に戻すと、形態素解析の境界のところにいる、

形態素解析でヒットした③住所表記のエリア(市区郡町村レベル) > N-gramでヒットした①駅

が成り立つように、形態素解析当てとN-gram当ての配点の比率を、①と③の配点の逆数(ちょっと数学的に正確ではないかもしれません)にしてやれば良いです。

この時に、2つの軸を掛け算で組み合わせる、multiplyが指定できることが生きてくるのかなと思います。

multi_matchの例でいうと、①駅の持ち点(フィールドCにプリセット済み。field_value_factorでコントロール)が1000、③住所表記エリアの持ち点が10という状況なら、 形態素解析フィールドをフィールドA、N-gramフィールドをフィールドNとすると次のような例になるでしょうか。

POST /myidx/_search
{
  "query": {
    "function_score": {
      "boost": "1",
      "boost_mode": "multiply",
      "score_mode": "sum",
      "functions": [
        {
          "field_value_factor": {
            "field": "C",
            "factor": 1,
            "missing": 1
          }
        }
      ],
      "query": {
        "multi_match": {
          "query": "中央",
          "fields": [
            "A^1000",
            "N^10"
          ]
        }
      }
    }
  }
}

これにより、Aにヒットした時点で、1000でそれと③住所表記エリアの持ち点10をかけて10000、AにはヒットせずNでヒットしたが10点で、それと駅の持ち点をかけるとこちらも10000となり、おおよそ同等のスコアになる...という理屈になります(注記の※1参照)。

※1 厳密には、Aの1000、Bの10はもともとのBM25/TF-IDFなどで得られた数値に対する重みづけなので、裸のスコア値の傾向をつかまえておいて、もう少し加減してやる必要がありますね。 また、数学的に常に成り立つ大小関係を保証するのは難しいかもしれません。が、ここぐらいまでやっておけば、序列として成り立っていると言える状況が作れるでしょう。

まとめ

Elasticsearchの検索スコアリングの加算方式か乗算方式かというところについて少し述べてみました。

数式を書いてみれば数学に強い人だと当たり前でより良い採点方式を導けるかもしれません。

また、ML的なアプローチなどもあるでしょう。

一方で、このあたりのUXはスジの良い人のヒューリスティックでグッといい感じに近づけておいて、あるいは実験・評価をしてみて微調整というのが実際はうまくいく進め方のように思います。

このブログでは、微調整がより楽チンな「加算型」をオススメするのですが、この記事では、「乗算型」もこれぐらいのバランス感で使うと良いのではないか(というか単に私のこのみですが)というところを述べみました。

以上、ではでは。

*1:他にも似た様なパラメータがあるかもしれませんが、そもそもこの2つだけでも、いつもどっちがどっちかわからなくなるので、一応クエリを作って見てスコア値を確認することにしています。