はてだBlog(仮称)

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

サーチのUXチューニングとElasticsearchのmy覚書(まずはチューニングできる箇所の頭の整理)

はじめに(言い訳)

毎度、タイトルはでかく出たものの、その割になにか分からない感じですが、簡単に言うと、Elasticsearchで検索サイトを構築する時に、一発でなかなかうまくいかないこともあろうでしょうから、その時にどこをチューニングするかという話です。

ただ、現実的には、チューニングできる箇所をチューニングするしかないというところもあるので、じゃあ、どこをチューニングするのかというところについて、自分なりにまとめてみた次第。

構想どおりにはいかなくて、まずElasticsearchで検索のUX向上のためにチューニングする際、どことどこに切り口があるのかと、それに直接関係するElasticsearchの主な 仕掛けのキーワードを並べただけになっています。

想定したバージョンは、Elasticsearchの6.3ですが、概念的な話なので、最近のバージョンでは大体ざっくり当てはまるような気がします。

チューニング

世の検索エンジンで、実用ケースにおいての、検索の当たり方のチューニングというと次の4点で捉えるのが良さそう。

  1. ANALYZER
  2. 辞書(類義語)
  3. スコアリング
  4. 検索時のクエリパラメータ(Elasticsearchで言うと、Aggregationsなどの活用も含む)

4点目以外は、インデックスの更新と検索の両面で調整することになる。

f:id:azotar:20181113225450p:plain

アナライザー(ANALYZER)

f:id:azotar:20181113225011p:plain

中の仕組みはどうでもいいんだよぅ、おぅおぅ、とまでは言えないが、立場上即効性を重視してものごとを進めねばならん時もあるでしょう。

使う立場の人間としては、アナライザーは、少し雑に言うと、同じ意味の言葉の表記揺れの検索に対応するための仕組み。

こういう検索ユースケース・検索語でこういう結果をヒットさせたい...という議論の場においては、次項の類義語の辞書との境目は難しいところであるし、アカデミックな意味の曖昧検索とはさておき、TYPOなどような文字のうち何語か間違っている場合も検索時に救ってやる(レーベンシュタイン距離で判定)ための検索はElasticsearchだと、Fuzzy Queryということで、ここで言うクエリパラメータでの範疇だったりする。

ここでのオレオレ分類では、辞書(類義語)を切り出したが、Elasticsearchの場合、シノニムの辞書はANALYZERで指定。 ただ、シノニムの辞書はファイルベースで別管理してメンテナンスの契機が別になることも少なくないので、別枠かなーと考えました。

というところもあるものの、実務上は、表記揺れに対応するための仕組みと、そのための観点でチューニングしていくことになる。

  1. Elasticsearchのアナライザーは、前処理、トークン分割、後処理の構成。それぞれ、Character filters、Tokenizer、Token filters。
  2. 前処理は、htmlの除去をしたり、言語の世界とは少し異なるような変換や整形。専門用語は、トークン分割か後処理な気がするが、ギョーカイ用語的なものを良しなに扱いたければここで変換しても良いのかもしれないぐらいの理解。
  3. Mapping Character Filter、Pattern Replace Character Filter。
  4. トークン分割は、形態素解析とかの範疇。Elasticsearchでは、構造化テキスト(郵便番号や電話番号)もこの範囲で切り出しする。検索エンジンは転地インデックスがベーシックなんだと再認識する部分。
  5. 後処理は、ステミング、ノーマライズストップワードの除去、といった処理。正確でない用語も入っているかもしれないが、原型化、全角半角、長音除去、小文字化、踊り字の正規化、読み仮名、漢数字、送り字、字体違い(異字体)、外来語、ドメイン固有の単語などがこのあたりのキートピックなのでしょう。

実のところ、最近だと、ここらあたりは最初のサービスでのローンチ時点では、検索エンジンにお任せして、 あえていうなら「前処理」の範疇で少しだけ何かがんばっておくかどうか、あとはローンチ後にA/Bテストしたり、検索ログを分析したりしてどうしてもということがあればという範囲だと思う。

辞書(類義語)

ドメイン固有の言語や、一般的な用語の用法では意味は近いものの別の用語であるが、当サイト・当分野では、同じような語と見なして検索させたいあるいは別のケースでの多少正確性を犠牲にしても良いので関連語・連想語的なものとして扱いたい... といったことは、形態素の辞書などの世界とは別もので考えることになる。

Elasticsearchだと、確かにANALYZERのToken filtersの一つではあるが、どこで何の検索をチューニングするかというところで言うと別枠だと考えることにしている。

f:id:azotar:20181113225016p:plain

  1. → アナライザーのToken filtersの「Synonym Token Filter」で登録できる・・・ ファイルベースとインデックスベースに対応。ただしAWSのESではインデックスベースのみ対応。
  2. 加えて、辞書追加時には実質インデックス再作成が必要になる。(※詳細は割愛するが、インデックスのオープン・クローズに関する概念を理解しておくこと)
  3. →今後、有力なプラグインが開発される期待はないわけではない。ただし、使い勝手などを考えると次項の「力技」の方がコントロールしやすいかもしれないのでそちらに舵を切る手もある。
  4. → 別解(ただし、UXを考えると正解たりうる案)として、類義語の設定自体を個別のインデックスとして、メインのドキュメントのインデックスを検索して0件の場合は、類義語インデックスを検索して、オススメ代替検索キーワードをサジェストする。あるいは集合知を用いた「他の人はXXXで検索」のような協調的フィルタリング型のサジェストを提示する。
  5. → 力技:INPUTのドキュメントに対して、アナライザー走行前に「派生」させてやる。辞書を追加時に追加の単語のみ狙い撃ちで更新することでインデックスの部分更新が可能。処理時間に効いてくるのと件数には注意。あと、そもそも派生するとワードが無駄に増えたり、派生の際のキーワードの入れ場所によってはスコアリングの邪魔をするかもしれない。
  6. → 範囲は限定的だが、形態素解析のモードで mode: searchとすると、”関西国際空港”について、”関西国際空港”そのものと”空港”がインデックスに格納される。

スコアリング

いい感じのドキュメントを検索にヒットさせるようにするという、全文検索ならではで、RDBの検索には無い世界観。

スコアリングの考え方、およびそれがある程度後付けで設定で制御できるような)製品に入ったことで、使わせてもらう側としては格段に便利になった&用途が広がった。

店名のフィールドに完全一致した場合と、紹介文にたまたま隣の店ということでその店名が入っていた場合に、前者を優先するというような設定ができる。

あと、半ば定石となりつつあるようだけど、形態素解析N-Gramをハイブリッドで用いる。前者を優先して、ヒトの感覚に近い条件でヒットした場合はそれを優先して検索結果のリストに出すが、残念ながらヒットしない場合は、N-Gramで最小マッチで拾い上げて取り漏れを

昔は、「理想の検索結果」という幻影と戦う必要があったが、

※以下、私のポエムが辛くなってきた方は、↓のリンク先をご参照ください。

www.atmarkit.co.jp

その前に

リードでは、複数の条件のうち、どれを優先するかという話を書いたが、そもそも、 あるワードで検索した際に、ワードや検索条件に全文検索としてどれぐらい良い塩梅なのか話があり、その意味でのスコアリングが下地として存在する。

これを便宜上、基礎スコアリング*1ということにする。

基礎スコアリングにも、よーく研究された定石があるようだ。

個人的には、こちらの方がまとめていただいているのを参考にしている。

qiita.com

その名も「TF-IDF」である。

Elasticsearchでの検索では、

より短い文章の中に*2より多くの検索語が出現するほど良い。検索語がレアなワードであればそれを多く含む文章が際立つ、結果として

評価が高くなるということだと理解した。

※下線部は正確にはTF-IDFではなく、lucene系の検索エンジンでの味付け。

数値で体感というとElasticsearchやSolrを実際に検索してみるのが良いが実際のところインデックス作りなどがめんどくさい。

そんな時には↓のEXCELシートを使わせていただくのが良いだろう。

ダウンロード - 株式会社ロンウイット

RONDHUIT REPORT Vol.8 「Excelで学ぶ!Luceneのスコア計算」(全3ページ)+別紙Excelファイル

また、TF-IDFを改良したと言える「BM25」というのがあるらしい。

「BM25」はSolrでは標準がこちらになったという話もあるが、Elasticsearchではどうだろう。

www.elastic.co

Elasticsearchでスコアリングを調整する

長くなってきたので、息切れしてきた。

ひとまず、スコアリングの調整をどこのプロパティでやるんじゃい、というところを、期末テスト用のカンペ風にまとめたのがこちら。

f:id:azotar:20181118235656p:plain

  1. function_scoreというプロパティがある。こいつに通常のqueryプロパティをぶら下げる。
  2. functionsという配列(おそらく1つだけなら配列にしなくてもOK)に、Script score、Decay functions、Field Value factorの3大評価方法でうまくいきそうなものをスコアリングを個別設定したいフィールドを指定して、必要数分指定。
  3. Script score はスクリプトによる計算、Decay functions 減衰関数を使って目的の値との距離で評価(GEO検索などで、現在地にあるものを100点として、 それから離れるほどスコアが減少する...というような中心点が存在するものの評価方法に適する)、Field Value factor  インデックスにあらかじめ設定されている値(レストランのレビュー平均点など)に指定の数式で演算して評価
  4. script_score、exp、gauss、linear、field_value_factorというプロパティで公式をググります。
  5. functionsで並び立てたスコアリングをさらに外付けで設定するには、”weight”を指定。
  6. そもそもquery自体がTF-IDFなどのスコア値を持つので、これらと、function_scoreのfunctionsで計算したスコア値をどう掛け合わせて総合値にするかを決めるのがboost_mode(デフォルト値はmultiply、つまり掛け算)
  7. 1STEP戻って、functionsの中の値をどう料理するかもイコライザー調整ができて、それを司るのがscore_mode。
  8. もう1STEP戻って、query自体をboostで加点することができる。
  9. このドキュメントでは流れ上ふれていないが、queryの配下の各検索条件自体は、function_scoreの指定によらず、条件ごとに、boostでスコアを調整できる。
  10. 前項で基礎スコアリングと便宜上の用語で述べてたものは、画像中では、関連度としておきました。どこかで、relevantうんたらという記述を見たような気がするので。

www.elastic.co

Elasticsearch スコアリング参考文献リンク

qiita.com

notta55.hatenablog.com

*1:本ドキュメント内ローカルの説明用の造語です。

*2:長〜い文章よりも、短い文章内にその単語が詰まっている方がその単語に関して特化して説明した文章だとみなすのだと思われる