コネヒト開発者ブログ

コネヒト開発者ブログ

検索システムで再現率向上に取り組んだ話

こんにちは。エンジニアの永井(shnagai)です。

今回は、現在進めている検索システム内製化プロジェクトの中で、検索エンジニアとしてはほぼ未経験に近い自分が半年ほど試行錯誤した内容の一部を書き記していこうと思います。

※筆者の経験としては、Elasticsearch✕kibanaのログ基盤は複数構築経験はありで、Elasticsearch周りの設定への知識は0ではないレベル

この記事を見て、検索に詳しい方や自分もやってみたいという方がいたら是非お声がけいただけるとうれしいです。

内容は、ざっくり下記の構成になっています。

  • 作りながら身にしみた検索システムの奥深さ
  • 初回ABテストでは既存エンジンに惜敗。再現率向上を目指すためのチューニング
  • これから

作りながら身にしみた検索システムの奥深さ

プロジェクトの開始前に、そもそも検索システムを自社で作り運用していけるのかの当たりをつけるために、ママリのデータを使った検索システムのモック作りを開始しました。この時の自分は、暗に「1つの検索クエリに対していかに正確な検索結果を返すか」を解くべき課題と設定し、下記にあるように検索ワードに対して間違いの少ない検索システムを目指してモック作りを進めました。

社内向けのモックを作る際の最初の方針

  • 出来るだけ検索ノイズが少なくなるように
    • OpenSearchの match_phrase クエリを使って検索ワードの順序を厳格に判定
    • kuromojiはnormalモードを利用 ※searchで意図せぬトークン分割されるのを防ぐため
    • 家族ノートという別プロダクトを開発するときに作ったユーザ辞書をカスタム辞書として利用

※検索エンジンとしては、OpenSearchを採用しているのでクエリ等はOpenSearch(Elasticsearch7.10.2相当)のもの

その後、モックが出来上がり社内定性チェックを行い、そのFBを読み解く中で、下記のようなインサイトを得て少しずつ意識が変わってきました。

「検索クエリは検索者の検索意図を必ずしも表したものではない」

「検索クエリぴったりのものでなくても検索意図に合う結果であればよいパターンもある」

「0件ヒットの体験は検索システムとしてはかなり悪い」 ※コンテンツがそもそもなければもちろん許容

つまり、検索クエリは検索者の目的を必ずしも正しく反映したものではなく(自分的には目からウロコでした)、検索クエリからいかに意図を汲み取り次のアクションにつながる結果を返せるかが検索システムの役目ということをだんだんと理解し始めました。

そんな中、「検索システム」という良書をチームメンバが見つけ、検索システム内製化チームで輪読会をはじめました。この本は、めちゃくちゃ良書で今自分たちがぶつかっている課題等が見事に言語化されており毎度チームメンバとうなりつつ検索システムに対する理解を深めている最中です。

特に、「ルックアップベースの検索モデル」には課題があり、「クエリや検索行動」自体を理解する QueryUnderstandingが検索システムを作る上では欠かせないと解説されていて、めちゃくちゃうまく言語化されており自分が抱えていた課題感が腑に落ちたのを覚えています。 何回も読み直しており、めちゃくちゃオススメの良書です。

検索システム ― 実務者のための開発改善ガイドブック(電子書籍のみ) – 技術書出版と販売のラムダノート

初回ABテストでは既存エンジンに惜敗。再現率向上を目指すためのチューニング

「ルックアップベースの検索モデル」は、自分の中では出来るだけ間違いの少ない検索システムと捉えており、その方針で作った検索システムで最初のABテストに望みました。

結果、KPIに設定していた検索CTRの有意差判定で既存検索システムに負けてしまいました。

当初の検索システムは間違いを少なくするために適合率高めに設定していたのですが、検索ログから出した数値やユーザからのFBから再現率が低すぎる傾向が読み取れました。

適合率と再現率については、下記のelastic社のブログが詳しいのでこちらをご参照ください。

How to implement Japanese full-text search in Elasticsearch

www.elastic.co

さて、再現率向上のためのアプローチですが、ここではわかりやすい2つのチューニングについて説明していきます。

※一般的というよりはコネヒトの環境独自のチューニングの観点が大きいのでその点はご了承ください。

カスタム辞書のチューニング

ユーザからのお問い合わせベースで調査していると、地名を含めた検索で意図した検索結果になっていないことが見えてきました。

  • 地域名を入れた検索クエリで、検索結果が返ってこないケースがたまにある
    • 例)「厚木」では「厚木市」のドキュメントが当たらない

これは、別プロダクトで作ったカスタム辞書で市区町村が細かく定義されており、それをそのままKuromojiのカスタム辞書として利用していたことが原因でした。

わかりやすく、具体例で説明すると

厚木市の保育園 という文章をKuromojiで形態素解析すると

  • 【本来の形】 厚木/市/の/保育園 と分割され、厚木市 厚木 どちらでも検索にヒットする
  • 【ABテスト時点】 厚木市 というカスタム辞書が優先されるので、 厚木市/の/保育園 と分割されてしまう。結果、 厚木 という検索ワードでは厚木市の保育園 というドキュメントがヒットしない状況になっていました。

元のカスタム辞書は、BigQueryに独自の転置インデックスを登録するために作った辞書で、細かい単語を定義することに価値があったのですが、OpenSearchのKuromojiの辞書としてそのまま転用すると思わぬ落とし穴があったなという所感です。

地名系をカスタム辞書から一通り除外して、例のような検索クエリにドキュメントが返ることを確認しました。

Kuromojiのmodeをnormalからsearchに変える

日本語形態素解析エンジンとして、Kuromojiを使っているのですがモードを当初は normal にしていました。

詳細は、下記が詳しいのですが、複数の単語が組み合わさった単語をいい感じに分割してトークナイズしてくれるモードとのことで、再現率向上の観点から search モードを採用しました。

kuromoji_tokenizer | Elasticsearch Plugins and Integrations [7.10] | Elastic

www.elastic.co

上記2点のチューニングを行い、改めてABテストを実施したところ、再現率が向上とKPIにしているCTRで既存エンジンと有意差なしまで持っていくことが出来ました。

これから

今後、オートコンプリートや関連検索の提案等の複数検索機能も含めた検索システムを通して、よりママリの検索がユーザの悩みや課題解決につながる機能となっていくよう開発をしていこうと思います。

難易度が高くなるランキング周りの話やカスタム辞書のさらなるチューニング、シノニム(同義語)の整備、 QueryUnderstanding をチームで進めるためのログやモニタリングの整備等、検索めちゃくちゃやること多いなわくわくするなという思いをチームメンバはもっており、今後も走りながら進めていき、少しずつ事例も紹介していければと思っています。

最後にコネヒトでは検索システムを一緒に開発してくれるエンジニアを募集しています。

下記の募集以外にもポジションありますので、少しでも興味もたれた方は、是非気軽にオンラインでカジュアルにお話出来るとうれしいです。

https://hrmos.co/pages/connehito/jobs/00n

hrmos.co