はじめに
こんにちは、Data Management Office、データサイエンスセクションの横山です。データサイエンスセクションでは、AIや機械学習などの技術を用いて、サービスを改善する施策を推進しています。
Oisixに「あなたにおすすめ」機能を開発し、実際のパイプラインとしてリリースし、サービス全体の売上向上を確認できました。 本記事では、「あなたにおすすめ」機能の内容とそれを支える機械学習システムについて解説します。
「あなたにおすすめ」とは
「あなたにおすすめ」は、過去の購入履歴からユーザーの嗜好を学習し、各ユーザーに対して購入しそうな商品を提示するパーソナライズ推薦機能です。 ユーザーが過去に購入していない商品を中心に推薦するように設計され、新しい商品との出会いが増えることを目指しています。
従来、Oisixのパーソナライズ推薦機能は、リターゲティング(写真の「もう一度買う」が該当します)などのルールベースの手法に限定されていました。
一方で、「あなたにおすすめ」では、機械学習モデルを用いて購入履歴から各ユーザーに最適な商品ランキングを算出するモデルベースのアプローチを採用しています。ここでの「モデルベース」とは、過去のデータからユーザーの嗜好を数値化する手法を指し、より柔軟で精度の高い推薦が可能となります。以下ではより技術的な点に注目して詳細を解説します。

データセット
推薦システムを学習するためのデータとして以下を利用しました。
- レイティング
- ユーザーがOisixで過去に購入した商品のデータ
- 商品属性
- 商品名、区分、価格、温度帯(冷蔵・冷凍)など
ユーザーが星をつけるといった明示的な評価ではなく、ユーザーの購入履歴をもとにレイティングを定義しました。 Oisixではサブスクリプションに入られているお客様に商品を提案するため、いくつかの商品が週の初めのタイミングであらかじめカートに入っています。 カートにあらかじめ入っている商品は最終的に多く購入される傾向があり、ユーザーの真の嗜好を正確に反映していない可能性があるため、レイティングから除外しています。
評価値の設定方法について複数の方法が考えます。具体例を挙げます。
- (1) 一度でも購入している商品は1.0, 購入していない商品は0.0
- 最も単純な方法ですが、購入回数の違いやリピート性を反映しない
- (2) リピート購入 > 初回購入 > 未購入の大小関係で評価値をつける
- この方法は、ユーザーの嗜好の強さをより細かく評価できる点がメリット
- (3) 購入された商品について
(売価 / 定価) ** alphaで評価値を決定する- 売価が定価に対して小さい(割引率が大きい)ほどユーザーの嗜好性が低いとみなす。
alphaは評価値を調整するパラメータ - 割引商品の購入は、嗜好性よりもお得感やタイミングによる影響が大きいと考えられるのでこのような補正をする
- 売価が定価に対して小さい(割引率が大きい)ほどユーザーの嗜好性が低いとみなす。
方法(2)については、複数回購入を重視することで枠単体のランキング性能は向上を見込める一方で、「もう一度買う」枠とのカニバリゼーション(互いに影響しあってしまう現象)には注意が必要です。 最適な評価値の決定方法については、今後オフライン評価およびA/Bテストにより検証する予定です。
レイティングだけでは十分な精度が出なかったため、商品属性を特徴量として活用しました。次のように複数のデータ型の特徴量がありますが、モデルが扱いやすいようにカテゴリ型に変換しました。
- テキスト型
- 例:商品名
- 日本語のトークナイザーで単語分割しカテゴリ型に変換する
- 数値型
- 例:価格
- 数値を区間ごとに離散化してカテゴリ型に変換する
- カテゴリー型
- 例:区分、温度帯(冷蔵・冷凍)
機械学習モデル
先に説明したデータをグラフ構造として表現して、Graph neural network (GNN、参考文献) に入力して学習・推論します。
グラフのノードは、ユーザー、商品、商品属性の3種類に分類され、これらの関係性はエッジで表現されます。

グラフデータを入力として学習・推論するGNNのモデルアーキテクチャについて説明します。 それぞれのノードには、IDに基づく初期のembeddingが与えられ、学習の過程で更新されます。
この際、2つの点に留意しています。学習時に存在しなかった新規商品の場合、通常はembeddingが学習されていないため初期値としてゼロベクトルを使用します。 商品属性の情報が集約されることで新規商品であっても有用な表現を得て推薦が可能となります。
また、ユーザーの初期値はゼロベクトルに固定し、周辺の商品からユーザー表現が更新されるようにしています。ユーザー数が多いため、各ユーザーに個別のembeddingを割り当てると計算リソースが膨大になるので、それを避けることが目的です。

GNNを構成する要素であるGraphConvは、隣接するノード表現を変換・集約してノード表現を更新するオペレーターです。 中央に位置するGraphConvを見ると、商品と商品属性からのノード表現を入力として商品のノード表現を更新しています。
最終的なユーザーと商品のノード表現から予測値が計算されます。予測値とレイティングの評価値から計算されるPointwise lossを最小化するように学習しました。 予測時は候補となる商品全体のノード表現を計算して、ユーザーごとに予測値が大きいTop-Kの商品を選択することで推薦リストを出力します。
推薦リストに対する後処理
機械学習モデルが計算する推薦リストをそのまま表示すると、ユーザー体験を損なう可能性があります。モデルが出力した推薦リストを自部署や事業部のメンバーと確認しながら、改善点を洗い出し必要となる後処理を実装しました。
1つめは、推薦リストの多様化です。モデルの出力する推薦リストには似たような商品(例えば、クラフトビールの銘柄違い)が連続して並ぶ傾向が見受けられました。より多様な商品をユーザーに見せることが重要だと考え、同じ商品区分(加工品、青果、飲料といった粒度)が同時に並ばないような仕組みを導入しました。
具体的には、Maximal Marginal Relevance(MMR, 参考文献)というアルゴリズムを適用し、精度と多様性を両立させています。MMRは、モデルが出力するランキングの予測値が高い結果を選びつつ、推薦リスト内に同じ区分の商品が偏らないように選択するアルゴリズムです。両者をバランスさせるパラメータがありますが、オフラインデータでのランキング性能や定性評価を確認しながら適切な値を決めました。
2つめは、「あなたにおすすめ」枠でインプレッションした商品の除外です。 推薦システムではOisixの購入サイクルに合わせて毎週推薦リストを計算して出力しています。 特に直近購入していないユーザーに対しては、モデルの入力である購入履歴が変化しないので、数週間前と同じような推薦リストを表示する恐れがあります。
新しい商品との出会いを重視するため、過去数週間に「あなたにおすすめ」枠でインプレッションがあったにも関わらず、クリックなどのポジティブな反応がなかった商品は推薦リストから除外する後処理を追加しました。この仕組みにより、ユーザーが既に目にして反応のなかった商品を繰り返し推薦するリスクを低減し、常に新鮮な商品リストを提供できるようになります。
システム構成・運用
「あなたにおすすめ」を提供する機械学習システムは学習・推論両方とも週次でバッチ処理する設計にしました。 一般的には、学習・推論のそれぞれにおいてバッチ処理・ストリーム処理・リアルタイム処理といくつかの実装方法が選択肢にあります。
Oisixの購入サイクルから最低限、週次で学習・推論できれば問題なく、最初のリリースでは運用負荷を低くするのが良いと考え、バッチ処理を選択しました。 将来的にはより高い精度を目指すために、より短い時間間隔のバッチ処理で学習を行なったり、推論をリアルタイムに処理することも検討できます。

機械学習システムのアーキテクチャ概要について説明します。 学習・推論に用いるデータは、社内で開発しているSnowflakeのデータ基盤に格納されています。 データの前処理・後処理は、Amazon SagemakerにProcessing jobを起動してSnowflakeのクエリを実行し、学習・推論に使う中間データをAmazon S3へ保存します。Snowflakeへクエリするときは、Sparkとほぼ同等のAPIを持っているSnowparkを利用することで、SQLだけでは実現できない複雑な処理をスケーラブルに実行できました。
モデルの学習・推論はPytorchで実装されSagemakerのProcessing jobで実行されます。 パイプラインの最終出力である推薦リストは、基幹システムが参照するデータベースに書き込むことでECサイトに表示できます。
Amazon Managed Workflows of Apache Airflow (MWAA)が複数のProcessing jobの依存関係に応じてタスクを実行します。
学習と推論のパイプラインをYAML定義によって動的に構成するようにしました。
A/Bテストの実験群・比較群ごとに別々のモデル・パラメータで学習したい場合に、下記YAML定義のgroupsに複数の設定を書きます。MWAAのPythonファイルがYAMLを読み込み、群ごとにパイプライン(AirflowのTaskGroup)を生成します。
name: fy25_abtest01 salt_key: some_key # A/Bテストでユーザーを分割する時のsalt key num_slots: 3 # A/Bテストで使うスロット数 groups: - id: C slot: [0] - id: T1 slot: [1] - id: T2 slot: [2]
以下がYAML定義によって構成されたDAGです。pipeline_C, pipeline_T1, pipeline_T2のTaskGroupはYAML定義によって生成されたものです。 この仕組みを導入することで、MWAAに不慣れな人であってもコードを修正せずYAMLだけで実験条件を変更できるため、実験サイクルを迅速に回すことが期待できます。

最終出力である推薦リストはSnowflakeのStageにも保存され、Streamlitで実装されたデモアプリケーションによって定性的なチェックができます。 デモアプリケーションを通じて、実際の推薦結果が過去の購買履歴とどのように関連しているかを視覚的に確認することで、システムの精度や納得感を確かめます。 SnowflakeではStreamlitがネイティブでサポートされているため、追加の環境構築を必要とせずアプリケーションを作成できました。

終わりに
「あなたにおすすめ」機能とそれを支える機械学習システムについて解説しました。A/Bテストを繰り返すために手間を抑える仕組みを整えたので、今後はいろんなアイデアを検証していきたいです。 我々は本記事で説明した機能以外でも推薦・需要予測・生成AI活用に関わる開発をしており、一緒に働いていただけるデータサイエンティストやMLエンジニアの方を募集しています。カジュアルに話すだけでも大丈夫なので、興味がある方はご連絡ください。
