MIXI DEVELOPERS
🧠

minimoの検索システム用に機械学習モデルを作る

2024/12/25に公開

はじめに

サロンスタッフ予約サービス「minimo」でバックエンドエンジニアをしている肉です。

minimoはユーザーにぴったりな美容師やネイリスト、アイデザイナー(以下、掲載者様)などを検索・予約できるサービスです。
サロンに予約するのではなく掲載者様個人に直接予約することができ、来店前にメッセージでやり取りできるのが特徴です。
minimoを説明するページがありますので気になった方や使ってみたい方はぜひ御覧ください。

検索機能の問題点とその改善案

さて、minimoには検索機能があり、お客様が希望する場所や時間、施術内容などの条件に合う掲載者様の一覧を表示しています。
その中での表示順は「最良の美容体験」をお贈りすることを目的に、
お客様の支持や快適な予約体験、魅力的な掲載内容を考慮したアルゴリズムで表示しています。
また、表示順は独自のアルゴリズムに加え、お客様の年代や性別ごとの組み合わせで毎日用意し、運用しています。
年代や性別だけであればパターン数が少ないため問題ないのですが、ここに例えば

  • お客様が望む価格帯
  • お客様がよく予約するメニュー傾向
  • お客様が望む掲載者様の経験年数

などの要素が増えていくと、運用していく並び順が指数関数的に増えていき管理が大変です。
そこで、それらの要素を特徴量として用いた機械学習モデルを用意し、表示順を計算する方法を考えました。

この機械学習モデルを使うメリットとして

  • 各ユーザに合わせた掲載順を計算することができる
  • 追加するユーザ情報について従来より複雑化しにくい

ことが挙げられます。

使う手法

ランキング学習(ランク学習)を使用しました。
ライブラリでいうと、LightGBMにあるLambdaRankを使用しました。

ランキング学習は一言でいうと「対象を並び替える技術」であり、競馬や競輪の着想予想や検索エンジンの表示順で利用されています。
今回はお客様が入力したクエリに対して該当した掲載者様のリストを最適な順序に並び替える必要があるため、ランキング学習を使う判断をしました。

評価指標はNDCG(Normalized Documented Cumulative Gain)です。
これはランキング学習の性能を評価するための指標で、重要なアイテムが上位に来ているかどうかを評価する指標になっています。
今回使った重要なアイテムは、お客様が予約した掲載者様と定義しております。
お客様が過去に予約した掲載者様を上位に並び替えることができるかどうかを評価するためです。

モデルについて

以下の図を使って説明します。

主に4つのデータを入力して学習を行っています。

  • 検索条件
    • お客様が検索をしたときの条件
  • お客様の情報
    • 性別や年代、過去に予約した掲載者様の情報
  • 掲載者様の情報
    • 掲載やメニューの情報、お客様からの評価など
  • 過去の検索結果と予約データ
    • お客様が検索時、ヒットした掲載者様の並び順
    • ヒットした掲載者様の中でどの掲載者様が予約されたか(正解ラベル)

これらを訓練データと検証データ、テストデータに分割します。
この時データの下処理として、各データを検索毎に分けてから表示順の並び替えを行います。

train_df.sort_values(by=['search_id', 'display_number'], inplace=True)
train_group_size =  train_df.groupby('search_id').size()
train_groups =  train_group_size.tolist()
train_data = lgb.Dataset(train_df, label=label, categorical_feature=categorical_features, feature_name=feature_names)
train_data.set_group(train_groups)

search_idが検索毎のidを表し、display_numberが検索時の表示順位を表しています。
上記コードでは、一つ一つの検索について表示順で並び替えています。
これによりお客様の検索結果を再現し、相対評価ができるようになります。

ここまでで学習データが用意できたので、モデルの学習を行います。

gbm = lgb.train(params, train_data, valid_sets=[valid_data], callbacks=[lgb.early_stopping(stopping_rounds=50, verbose=True)]))

検証について

モデルの検証について、オフライン環境にて以下の3つの指標を見ていました。

  • NDCG
  • 検索順位
  • shap(SHapley Additive exPlanations)

検索順位については、お客様の過去に見た検索結果の中から予約した掲載者様をより上に並び替えることができるかを見ており、並び替えた結果の平均順位を見ています。
shapとは一つ一つの特徴量がモデルの予測値に対してどれだけ貢献しているかを測る手法になります。
各テストデータあるいはテストデータ全体に対してshapを求め、そのモデルが各特徴量に対してどのような評価をしているかを確認するために使用しました。

import shap
explainer = shap.TreeExplainer(model)
shap_values_1 = explainer(test_df)

shap.initjs()
shap.waterfall_plot(shap_values_1[0], max_display=30)


各特徴量がモデルのスコアに対してプラスに効いているか、マイナスに効いているかがshapによって可視化されています。
(具体的な特徴量名は白塗りで隠しております)

まとめ

minimoにおける検索システムの課題を解決するためにランキング学習を用いた機械学習モデルを開発しました。
また、開発したモデルについてそれぞれNDCG、検索順位、shapの3つを使いオフライン上で評価を行いました。
この記事には書けていない細かい仕様や実装などは今後記事にしていきたいと思っております。

最後に

まだまだ走り始めたばかりの機能で、基本的なことからやっているような状況です。
また、チーム作りも頑張っておりまして、minimoでは一緒に働く仲間を募集中です!
特に検索アルゴリズムを一緒に改善してくれる仲間を探しています!
フルリモート勤務も可能ですので、詳しくは下記採用ページをご確認ください。
https://mixigroup-recruit.mixi.co.jp/jobs/
https://mixigroup-recruit.mixi.co.jp/recruitment-category/career/12083/?minimo
https://mixigroup-recruit.mixi.co.jp/recruitment-category/career/11607/?minimo
https://mixigroup-recruit.mixi.co.jp/recruitment-category/career/12068/

MIXI DEVELOPERS
MIXI DEVELOPERS

Discussion