↗️

テーブルデータをベクトル化!FT-Transformerによる推薦システム構築

に公開

はじめに:なぜ「テーブルデータ」のベクトル化が重要なのか?

近年の推薦システムは、以下の事例のように Deep Learning モデルによって Embedding (ベクトル表現)を生成し、レコメンドアイテムをベクトル検索させることが一般的になりました。

https://zenn.dev/team_zenn/articles/zenn-recommend-system

https://techblog.yahoo.co.jp/entry/2022122230379913/

https://techblog.zozo.com/entry/introduction-and-practice-of-vertex-ai-vector-search

LLMの登場によりテキストのベクトル化は容易になりましたが、ビジネスの現場で最も頻繁に扱われるデータ形式は、商品マスタ、物件情報、顧客データといったテーブルデータです。これらのデータは、既存システムの多くでリレーショナルデータベース(RDB)に格納されており、まさに宝の山です。

ところが、テーブルデータの予測や分類タスクで高い性能を出すことが知られている勾配ブースティング決定木モデルの一つLightGBMは、ベクトル検索で使われるような「意味を捉えた密なベクトル(Embedding)」を直接生成するようには設計されていません。

この記事では、テーブルデータのためのTransformerモデルであるFeature Tokenizer Transformer(FT-Transformer)を、Two-Towerモデルの一種であるSiamese Network上で訓練します。この訓練には、似たもの同士を近づける距離学習の考え方、特に対照学習のアプローチを用いることで、ベクトル検索レコメンドの核となる表現学習を実現します。その具体的な手法を、実装コードと評価を交えて解説します。

Siamese Network上での訓練からベクトル検索レコメンドまでの概観
Siamese Network上での訓練からベクトル検索レコメンドまでの概観

https://github.com/xxkuboxx/ft-transformer-recommender


プロジェクトの全体像:4ステップで実現する推薦システム

今回のプロジェクトは、大きく分けて以下の4つのステップで構成されており、それぞれがJupyter Notebookに対応しています。

  1. 特徴量エンジニアリング (01_feature_engineering.ipynb): 生の購買データから、各商品の「プロフィール」となる特徴量テーブルを作成します。
  2. 前処理 (02_preprocess.ipynb): モデルが学習に使うための「類似ペア」データセットを生成します。
  3. モデル訓練 (03_model_training.ipynb): 商品プロフィールを元に、「同時に買われた商品ペア」を学習させ、商品をベクトル化するモデルを構築します。
  4. 評価と推論 (04_evaluation_and_inference.ipynb): 学習済みモデルで全商品をベクトル化して推論エンジンを構築し、多様なシナリオで推薦性能を評価します。

Step 1: 商品の「プロフィール」を作る (特徴量エンジニアリング)

今回は、購買履歴から特徴量や類似ペアが作りやすいInstacartの購買履歴データセットを利用しました。まず、各商品の静的・動的な特徴量を抽出し、一つのテーブルにまとめます。

  • 静的特徴量: aisle_id, department_id など、商品自体が持つ不変の情報。
  • 動的特徴量: ユーザーの購買行動から計算される特徴量。これらが商品の「文脈」を豊かにします。
    • reorder_rate: 再注文率。商品の定番度を示します。
    • avg_add_to_cart_order: カートへの平均追加順。購買プロセスの早い段階で選ばれるかを示します。
    • total_orders: 総注文件数。商品の全体的な人気度です。
    • unique_users: 購入したユニークユーザー数。人気度の広さを示します。
    • avg_days_since_prior_order: 平均注文間隔。購買頻度に関わります。

これらの数値特徴量は、StandardScaler を用いて標準化し、モデルが学習しやすいようにスケールを揃えます。


Step 2: 学習データの準備 (前処理)

Siamese Networkでモデルを訓練するには、「類似ペア」が必要です。今回は「同じ注文(バスケット)で同時に購入された商品は、何らかの文脈で関連している」という仮説に基づき、ポジティブペアを生成しました。例えば、下図の場合「朝ごはん」という文脈で関連しているペアが作成できます。

同じ注文(バスケット)で同時に購入された商品をポジティブペアにする概念図
同じ注文(バスケット)で同時に購入された商品をポジティブペアにする概念図

order_products__prior.csvorder_idでグループ化し、各バスケットからitertools.combinationsを用いて商品ペアを抽出。最終的に500万ペアをランダムサンプリングし、学習データとしました。巨大なバスケットからペアが過剰に生成されないよう、1バスケットあたりのペア数に上限を設ける工夫も加えています。


Step 3: モデルを訓練する

この学習プロセスを効率化するため、自然言語処理ライブラリsentence-transformersを応用しました。FT-TransformerをラップするTabularFTTransformerクラスを定義し、同ライブラリが提供するSentenceTransformerモジュールとMultipleNegativesRankingLoss損失関数を活用します。

# FT-TransformerをSentenceTransformerモジュールとしてラップ
class TabularFTTransformer(nn.Module):
    # (実装はノートブック参照)
    def tokenize(self, item_feature_dicts):
        # ...辞書のリストを数値・カテゴリテンソルに変換...
        return {'x_num': x_num, 'x_cat': x_cat}
    
    def forward(self, features):
        embeddings = self.ft_transformer(features['x_num'], features['x_cat'])
        return {'sentence_embedding': embeddings}

# sentence-transformersの枠組みでモデルを定義
model = SentenceTransformer(modules=[ftt_wrapper])
# Siamese Networkの学習に適した損失関数
train_loss = losses.MultipleNegativesRankingLoss(model=model)

Multiple Negatives Ranking Loss は、sentence-transformersでは以下のような実装になっています。

https://github.com/UKPLab/sentence-transformers/blob/master/sentence_transformers/losses/MultipleNegativesRankingLoss.py#L14-L176

直感的には、「(アンカー1, ポジティブ1)という正解ペア」が、「(アンカー1, ポジティブ2)などの、同じバッチ内にある他のポジティブとの不正解ペア」を抑えて1位になるようにモデルを調整する関数と説明できます。数式は以下のように、ソフトマックス関数を適用した後にクロスエントロピー損失を計算していることと同じです。

L = -\log \frac{e^{\text{sim}(a, p) / \tau}}{\sum_{i=1}^{N} e^{\text{sim}(a, x_i) / \tau}}

具体的には、アンカー a1 とポジティブ p1 の類似度 sim(a1, p1) を、バッチ内の全ポジティブとの類似度 sim(a1, p1〜N) の合計で割ることで、「正解である確率」を算出しています。そして、その確率を最大化する(-logを最小化する)ことで、結果的に正解ペアの類似度スコアが最も高くなり、ランキング1位を達成するという仕組みです。

これにより、学習データがポジティブペアのみでの対照学習が簡単に行えました。もちろん、良質なネガティブペアも用意できれば、さらに高い精度を目指せると考えられます。

良質なネガティブペアとは

ICLR 2021 (International Conference on Learning Representations 2021) に採択された論文 Contrastive Learning with Hard Negative Samples では、対照学習における有益なネガティブサンプルについて、以下のように述べられています。

The most useful negative samples are ones that the embedding currently believes to be similar to the anchor.
最も有用なネガティブサンプルとは、現在の埋め込み(モデル)がアンカー(基準点)と類似していると信じているものである。

しかし、実際にはそのような理想的なハードネガティブ(モデルを騙すほど似ているが、クラスが異なるサンプル)を、低コストで自動的に抽出するのは困難です。そこで、この論文で提案されている重要度サンプリング(Importance Sampling) の考え方を応用し、損失関数を計算する際に、バッチ内でアンカーとの類似度が高いネガティブサンプルを重み付けする手法を使うことでも、精度を改善できると考えられます。


Step 4: 評価と推論

学習済みのモデルを用いて、実際に推薦エンジンを構築し、その性能を評価します。

ベクトル検索エンジンの構築 (FAISSによるインデックス化)

まず、学習済みモデルを使い、データセットに存在する全49,688商品を64次元のベクトルに変換します。そして、このベクトル群から類似ベクトルを検索するために、Facebook AIが開発したライブラリFAISSを利用します。

  • 内積 (IndexFlatIP) を用いて類似度を計算するインデックスを構築。
  • faiss.normalize_L2で全ベクトルを正規化し、検索を高速化。

これにより、数万件のアイテムの中から、類似アイテムをベクトル検索できるようになります。

学習結果の可視化:ベクトル空間は意味を捉えられたか?

モデルが本当に商品の「意味」を捉えられたのかを確認するため、UMAPを用いて64次元のベクトルを2次元に圧縮し、可視化しました。各点を商品のカテゴリ(department)で色分けしたところ、以下のような結果が得られました。

UMAPによるベクトルの可視化

図を見ると、「pantry」「snacks」「beverages」といった大きなカテゴリが、明確なクラスタを形成していることが分かります。これは、モデルが単なる数値の羅列ではなく、商品の本質的な違いを学習し、意味のあるベクトル空間を構築できたことを示唆しています。

LLMを評価者として、推薦性能を徹底分析

最後に、この推薦エンジンの真価を問うため、30個の多様な買い物シナリオでテストを行いました。推薦ロジックは、「カート内商品のベクトルを平均し、その平均ベクトルに最も近い商品を推薦する」というものです。

そして、その評価はGemini 2.5 Proを"評価者"としてLLM-as-a-Judgeで行いました。具体的には、以下の4つの指標で、各シナリオの推薦結果を5段階評価させました。

  • Similarity (類似性): カートや意図との関連性
  • Diversity (多様性): 推薦リスト内のアイテムの多様さ
  • Serendipity (意外な発見): 予期せぬ嬉しい驚き
  • Novelty (新規性): 推薦アイテムの珍しさ
指標選定の根拠

Similarity は類似ペアを学習しているモデルの直接的な評価となります。それ以外の指標のレコメンドにおける重要性は、学術論文誌 "ACM Transactions on Interactive Intelligent Systems" に掲載された論文 Diversity, Serendipity, Novelty, and Coverage: A Survey and Empirical Analysis of Beyond-Accuracy Objectives in Recommender Systems で以下のように説明されています。

  • Diversity

    The results of the study showed that light diversification (changing up to four items in a list of 10 recommendations) positively influences user satisfaction with the item-based CF recommender.
    この研究結果は、軽い多様化(10件のレコメンドリストのうち最大4件を変更すること)が、アイテムベースCFレコメンダーに対するユーザー満足度に肯定的な影響を与えることを示しました。

  • Serendipity

    Interestingly, despite providing less enjoyable recommendations, the serendipity-enhancing system version was preferred over the baseline system as the users were willing to sacrifice recommendation accuracy for the sake of discovering new interesting artists.
    興味深いことに、楽しさが劣るレコメンドを提供したにもかかわらず、ユーザーは新しい興味深いアーティストを発見するためにレコメンドの精度を犠牲にすることを厭わなかったため、セレンディピティ強化バージョンがベースラインシステムよりも好まれました。

  • Novelty

    The results of the study showed the perceived usefulness of a recommender to be influenced by the perceived accuracy and novelty, and to a lesser extent by the perceived diversity.
    この研究の結果、レコメンダーの有用性は、知覚された正確性(accuracy)と新規性(novelty)によって影響を受け、多様性(diversity)の影響はそれより小さいことが示されました 。

評価結果:見えてきた「強み」と「致命的な弱点」

LLMによる評価の平均スコアは以下の通りでした。

  • mean_similarity_score: 3.20
  • mean_diversity_score: 2.53
  • mean_serendipity_score: 1.63
  • mean_novelty_score: 1.43

LLMが生成した総合レポートを要約すると、モデルの性能は以下のようにまとめられます。

顕著な強み 💪
  • 特定カテゴリにおける深掘り: 「ビール好きの飲み比べセット」のシナリオでは、IPAやスタウトに対し、さらに多様なスタイルのビールを的確に推薦。ユーザーの好みを深く理解していることを示しました。
  • 高い類似性: 「サンドイッチ作り」や「調味料セット」など、文脈が明確なシナリオでは、ユーザーの意図に沿った類似商品を高い精度で推薦できました。
明確な弱点と課題 🚧
  • 多様性の著しい欠如: 多くのシナリオで、推薦が単一カテゴリの代替品に終始しました。「アボカドトースト」のシナリオでパンとアボカドの類似品しか推薦されず、期待される卵やスパイスが提案されないのが典型例です。
  • 文脈および暗黙的な制約の完全な無視: これが最も致命的な弱点です。「ヴィーガン」のシナリオで魚介類を、「愛猫のため」のシナリオで犬用おやつを推薦するなど、ユーザーの意図を根本から覆す間違いをしました。

結論と今後の展望

今回の実験を通じて、FT-TransformerとSiamese Networkを組み合わせることで、テーブルデータから意味のあるベクトルを生成し、推薦システムを構築できるという、パイプラインを確立できました。

一方で、LLMによる評価から、現在の特徴量だけでは「ヴィーガン」のようなセマンティックな(意味的な)制約を学習できないという限界も明らかになりました。

今後の改善アプローチとしては、以下が考えられます。

  1. モデル訓練方法の見直し: 類似ペアの定義方法やサンプリング方法の工夫、ハードネガティブの利用など。
  2. テキスト情報の活用: 商品名や商品説明をBERT等でベクトル化し、FT-Transformerのベクトルと組み合わせる。
  3. メタデータの活用: 「レシピ」や「用途」といった情報を活用し、アイテム間の補完関係(クロスセル)を学習させる。
  4. 多様性を向上させる仕組み: Maximal Marginal Relevance (MMR) のようなリランキング処理を導入し、推薦リストの多様性を担保する。

Discussion