論文読み:Monolith

元論文
https://arxiv.org/pdf/2209.07663.pdf

bytedance社のレコメンドエンジンに関する内容。
Deepなモデルをプロダクション環境で扱うためのモデルの学習・更新の方法やデータの管理方法の工夫などをまとめている。

全体のアーキテクチャ

特徴量のメモリ削減

モデルのパラメータはdenseとsparseの二つにカテゴライズされる。
dense:DNNの重みや変数
sparse:embedding tableなど

1. ハッシュ関数の工夫
新しいIDの増加に伴いパラメータの管理に対するコストが高くなる。
その問題を防ぐため、スパースなパラメータについてTensorflow Variableのような衝突がなく効率の高いハッシュテーブルを用意する。具体的にはカッコウハッシュを用いる。

2. ヒューリスティック
分析の結果、下記のことがわかっている。

  • 滅多に出現しないIDの情報はモデルの改善に寄与しない
  • 過去の情報はほとんどモデル改善に寄与しないし、そういったユーザーはもはや訪問しない。

こういった背景により、embedding tableに挿入する前に以下を実施

  • 出現頻度に応じたフィルター
  • 確率的なフィルター
  • IDのexpire timeの設定

オンライン上の学習

two stageで実現する。

  • batch training

ミニバッチ単位でパラメータ取得->更新->更新後パラメータ返却

  • online training

リアルタイムデータをon the flyで処理して更新。
train PSが serve PSに周期的にパラメータを同期する

  • ストリーミングエンジン

kafkaを利用。
ユーザーアクションとFeatureでパイプラインを分けておく。
Flinkを活用してユーザーアクションとfeatureをJoinするJobをかましておく。
最終的に学習データをkafkaキューに流し、オンライン学習の場合はそのデータをそのまま使い、バッチの場合はダンプしてHDFSに格納。

  • online joiner

ログが順不同で入ってくるので、それぞれのリクエストに対してユニークなキーを使うことでユーザーアクションとFeatureを正しくペアにすることができる。
ユーザーのアクションのラグ(提示したアイテムを購入するまでに数日を要するかもしれないなど)に対応するにはin-memory chacheだけでは対応できない。
この対応のためon-diskのKVSを設置してそちらに格納しておく。
最初にin-memory chacheを参照して、キャッシュに乗っていなかった場合にはKVSを参照するようにしている。
その後ネガティブサンプリングをして学習データとしてkafkaに流す。

  • パラメータ同期

リアルタイムログを使って更新したパラメータを同期するためのチャレンジ。

  • パラメータ更新を推論サーバーを止めることなく行うこと
  • 新しいモデルを推論に持っていく際のメモリサイズと帯域の問題

基本的には小さい範囲のタイムレンジが与えられた時に、小さな部分集合のみを対象にして学習とembeddingsの更新を行うようにする。
疎なembeddingsを頻繁に更新し、密なパラメータの更新頻度を下げる。

  • 耐障害性

一般的には、モデルのスナップショットを定期的に保存、失敗の際に最新のスナップショットから回復するのが考えられるが、トレードオフがある。
・モデルの品質
頻度が高いほど直近の情報を反映できる
・計算コスト
メモリ使用量、disk I/Oがかかる。
バランスとして、現状は全てのtraining PSでdailyでスナップショットを取る方針としている。

評価

  • HashTable

movie lensとプロダクション環境のデータセットを用意。
movie lensではuser,itemのIDそれぞれをMD5でハッシュ化し、より小さいスペースにマッピングしたものを用意する。
学習モデルはDeepFMを使い、評価はAUCで行う。
プロダクション環境では、baselineとしてembeddingテーブルサイズを半分程度に圧縮するように新しいテーブルを用意する。
実際に本番に入っているモデルを用いてAUCで評価する。
実際に比較すると衝突が発生していないデータの方がAUCが良い傾向になっている。
特にトレーンングエポック数が増えるとAUCの増分が増える傾向がある。
また、プロダクション環境において、モデルの経年劣化に対してロバストであることがわかる。

  • リアルタイムなオンライン学習で重要なものは何か

(1)モデル更新頻度
1週間のデータセットを5day-2dayに分割。
5dayのデータをバッチトレーニングとして利用し2dayをオンライン学習に利用する。
オンライン学習はそれぞれデータを10,50,100(実時間における更新頻度5hr,1hr,30minに対応)に分割し、それぞれでオンライン学習->評価を繰り返し性能を比較する。
比較の結果、sync頻度が高い方がAUCが良い結果になっている。
更新頻度にほぼ線形にAUCが改善しているように見える。

(2)本番テスト
オンライン学習をした場合とバッチ学習のみの比較。
ライブテストにおいてもAUC改善がおよそ15~18%の改善となっている。

実際に、100,000IDの1024次元のembeddingを分単位で更新しても400MB/min程度のデータにしかならない。また、密なパラメータは先述の通りdailyかつ最もトラフィックが小さい時間に更新するようにしている。

また、スナップショットについて、頻度が高い方がいいかと思っていたが、1日単位であってもほとんど性能に変化がなかった。