「ホットペッパービューティー」美容クリニックでのElasticsearchのユーザー辞書登録による検索改善

はじめに

『ホットペッパービューティー』美容クリニックのカウンセリング予約サービス(以降、美容クリニック)のバックエンドを担当している安達です。

本記事では、美容クリニックでの全文検索エンジンの利用と形態素解析器でのユーザー辞書登録の取り組みについて紹介します。 美容クリニックでの検索機能の技術スタックは以下です。

役割 技術 備考
全文検索エンジン Elasticsearch Elastic Cloudで運用しています
形態素解析器 Sudachi Sudachiの辞書はUniDicとNEologdをベースに作られており専門家により継続的に更新されています(参考)

美容クリニックにおけるElasticsearchの利用例

美容クリニックでの全文検索の例を紹介します。 美容クリニックではクリニックを検索し、カウンセリング予約できます。 検索キーワード(画像青枠部分)を入力し検索すると、それに適したクリニック(画像黄枠部分)とキーワードに対応したクリニックの施術メニューがピックアップ(画像緑枠部分)され表示されます。 クリニック検索と施術メニューピックアップは内部的に独立した検索になっており、両方ともElasticsearchにより実装されています。

検索結果画面

クリニック検索ではNgramと形態素解析を併用し、施術メニューピックアップでは形態素解析のみで検索しており、要件により使い分けています。

Ngramと形態素解析の検索ノイズと検索漏れの特徴は以下です。

検索ノイズ 検索漏れ
Ngram 多い 少ない
形態素解析 少ない 多い

クリニック検索では、Ngramと形態素解析を併用することにより検索結果のヒット数を担保しつつ検索ノイズの増加による悪影響をスコアソートにより軽微なものに抑えています。 施術メニューピックアップのための全文検索では、検索ノイズが少ない形態素解析のみを利用しています。

美容クリニックでの検索における問題

形態素解析では、辞書に含まれている単語の集合に基づいて形態素が認識されます。 Sudachiの辞書において美容医療の専門用語が網羅されておらず、問題が発生します。

形態素解析で専門用語がカバーされていない問題

上記の表の通り、形態素解析では検索漏れが多いです。 例えば、「ダーマペン」は美容医療では人気な単語ですが、Sudachiに搭載されているデフォルトの辞書ではカバーされていないので、形態素として抽出されません。

形態素解析で期待通り認識・分割されない問題

「二重」が「フタエ」でなく「ニジュウ」と変換される問題も発生しました。 美容医療分野では二重(フタエ)という語句が頻出します。 しかし多くの場合、二重は「ニジュウ」と解釈されてしまいます。 他にも、「当医院人気No1二重埋没法」のように数字の後に「二重」とついてしまう場合があり「12」として解析されていました。

解決策

どちらの問題もユーザー辞書を利用することで解決できます。 Sudachiを含む形態素解析器では開発者が自分で追加したい単語を辞書に登録でき、これをユーザー辞書と言います。 デフォルトの辞書に含まれていない単語かつ、美容医療分野でよく使われる単語をユーザー辞書に登録することで、検索漏れを少なくできます。

また、ユーザー辞書では、単語と同時にその単語のコストも登録できます。 形態素解析の仕組み上、文章をどのように単語に分割するか決める際にラティスを用います。 ラティスは文章における考えられる全ての分割方法を表したデータ構造です。 その中で単語の連結の仕方やその単語自体のコストにより累積コストを最小にするパスを選択し、それに対応する分割方法が解になります。 そこで、小さいコストで「二重(フタエ)」をユーザー辞書に登録することで、「ニジュウ」より「フタエ」が表出しやすいようにしました。 形態素解析に関してはこちらの記事が参考になりました。 日本語形態素解析の裏側を覗く!MeCab はどのように形態素解析しているか - クックパッド開発者ブログ

しかし、ユーザー辞書を拡充しようとしても、明確な運用フローはありませんでした。 そのため、単語を効率的に発見し登録するためにフローを整備しました。 類義語についても同様のフローを利用できます。

ユーザー辞書を拡充するための運用フロー設計

1.登録候補となる単語の発見

元々は思いつきや偶然、総当たりによって登録候補となる単語を発見するしかありませんでした。 そこで効率的にどの単語を登録すべきか探すためにデータを用いました。 よく検索される単語で正しく形態素解析されないものがあると悪影響が大きい、離脱率が高い単語は正しい検索結果を表出していないと考え、検索回数が多い単語と離脱率が高い単語のデータを収集し、Redashを利用して可視化しました。

redash

検索回数上位の単語については、正しく形態素解析できていないもの(sudachi_readingformを通したら空文字になるもの)をスクリプトで抽出しました。 離脱率が高い単語については、実際の検索結果を見てヒット数が少なすぎないか、検索結果上位にノイズが多すぎないか、正しく形態素解析されているか確認しました。

2.登録候補となる単語の検証

実際にElastic Cloudにユーザー辞書を登録して検証すると、検証サイクルに時間がかかりすぎます。 そこで、手元で簡単に検証できるようにElasticsearch + Sudachi + Dockerでローカル環境を作成し検証しました。

こちらの記事を参考に、美容クリニックのElasticsearch環境用にアレンジして作成しました。 Elasticsearch + Sudachi + Docker でユーザー辞書を作ってみるハンズオン

これにより手元で高速にキーワードを検証できるようになりました。 登録候補になっている単語は本当に正しく形態素解析されていないのか、辞書登録されたあとは正しく形態素解析されるようになるのか等をKibana上でAnalyze APIを利用して確かめました。

3.登録すべき単語の登録

最後にElastic Cloudに実際にユーザー辞書を登録します。 Elastic Cloudでユーザー辞書を使う場合、Extensionsとしてユーザー辞書を登録する必要があり、登録するユーザー辞書は検証環境で利用したDockerfileをアレンジして作成しました(以下のコード)。

ユーザー辞書作成スクリプト

1
2
3
$ docker rm dict_builder_container || true
$ docker build -t dict_builder .
$ docker run -v $(PWD):/tmp/ --name dict_builder_container dict_builder # ローカルに辞書を持ってくる

Dockerfileの例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
FROM openjdk:8-alpine as dict_builder
WORKDIR /home
# Sudachiプラグインのjarファイルを持ってくる (バイナリ辞書の作成のため)
RUN wget https://github.com/WorksApplications/elasticsearch-sudachi/releases/download/v7.5.2-2.0.3/analysis-sudachi-7.5.2-2.0.3.zip && \
    unzip analysis-sudachi-7.5.2-2.0.3.zip && \
    # 用意されているシステム辞書を持ってくる
    wget https://object-storage.tyo2.conoha.io/v1/nc_2520839e1f9641b08211a5c85243124a/sudachi/sudachi-dictionary-20200722-core.zip && \
    unzip sudachi-dictionary-20200722-core.zip
## ユーザー辞書ソースを持ってくる
COPY custom_dict.csv /home
# バイナリ辞書の作成
RUN java -Dfile.encoding=UTF-8 -cp /home/sudachi-0.4.3.jar com.worksap.nlp.sudachi.dictionary.UserDictionaryBuilder -o /home/custom.dic -s /home/sudachi-dictionary-20200722/system_core.dic /home/custom_dict.csv
RUN mkdir hpbc_dic && \
    cp /home/custom.dic hpbc_dic && \
    cp /home/sudachi-dictionary-20200722/system_core.dic hpbc_dic && \
    apk add --no-cache zip
COPY entrypoint.sh /home/entrypoint.sh
ENTRYPOINT [ "sh","/home/entrypoint.sh" ]

Dockerfileで利用されるcustom_dict.csvの例

1
2
3
ダーマペン,4786,4786,5000,ダーマペン,名詞,固有名詞,一般,*,*,*,ダーマペン,ダーマペン,*,*,*,*,*
ダーマシャイン,4786,4786,5000,ダーマシャイン,名詞,固有名詞,一般,*,*,*,ダーマシャイン,ダーマシャイン,*,*,*,*,*
ダーマローラー,4786,4786,5000,ダーマローラー,名詞,固有名詞,一般,*,*,*,ダーマローラー,ダーマローラー,*,*,*,*,*

Dockerfileで利用されるentrypoint.sh

1
2
3
cd /home
zip -r hpbc_dic.zip hpbc_dic
cp hpbc_dic.zip /tmp

実際にインデックスを更新する際は、新しい辞書を利用したインデックスを作成し、古いものから新しいものへエイリアスを張り替えるという一般的な方法により無停止で本番Elasticsearchでのインデックスの向き先を変えています。

解決策を実施した結果

うまくいった点

今回の改善により、形態素解析による検索漏れを少なくできました。 クリニック検索では、専門用語で検索した場合にスコアソートがより効くようになり期待する検索結果が上位に表出されやすくなりました。 施術メニューピックアップでは、形態素解析の検索漏れを少なくすることで0件ヒットを少なくできました。

同様に類義語を登録したことによって、略語など同義語で検索された場合にも期待する検索結果が表示されるように改善しました。

うまくいかなかった点

今回、実はそれほど登録候補となる単語を発見できませんでした。 理由としては「離脱率が高い→検索結果が悪い」という仮定のもとで辞書登録すべき単語を発見できると思いましたが、「離脱率が高い→検索結果が悪い」というわけではなかったからです。 予想される原因は、検索キーワードによって特性が異なるからではないかと考えています。 (ここでの「検索結果が悪い」とは、検索ヒット数が少なすぎたり、検索結果上位にノイズが多すぎたりすること)

例えば、「脱毛」と「ダーマペン」ではどちらとも検索結果は悪くないにもかかわらず「脱毛」の方が大幅に離脱率が低いです。 脱毛はダーマペンと比較すると一般に普及しており不安がなく選べる一方、ダーマペンはダウンタイムがあり、普及率も脱毛より低いためより慎重に検討されているのではないかと考えています。

おわりに

本記事では、ユーザー辞書を登録することで全文検索を改善したこととそのプロセスについて説明しました。 今回、登録候補となる単語の発見のために検索データを収集・可視化したことや、単語を検証するための検証環境を作成したことで、検索結果の改善だけではなく、今後の検索改善や検索にまつわる案件を推進しやすくなる土壌ができました。 例えば、ホットワード機能や検索キーワード機能など検索関連でポピュラーな機能の検討がしやすくなったり、検証環境があることで機能をイメージしやすくなり、企画職との要件すり合わせの際にも認識合わせがスムーズになったりしています。

情報検索の分野は技術的にもプロダクト的にも面白さがあり、個人的に興味を持っています。 今後も今回得られたデータや作成した検証環境を利用することにより、検索を改善していきます。