はじめに
こんにちは。Gunosy TechLab Ads MLチームのしょうえいです。 この記事は Gunosy Advent Calendar 2022 の22日目の記事です。 Ads ML側はユーザに興味がありそうな広告を洗い出し、レコメンドすることを担当しています。
今回は、導入する予定の新広告レコメンドモデルのオフライン実験で実践したIncremental Trainingについて話します。
背景
Gunosyではグノシー・ニュースパス・LUCRA・auサービスTodayという4つのプロダクトを展開しています。各プロダクトにおいては、ユーザに最適なニュースと記事を配信して情報を届けています。
同時に、サービスを継続的に運営するために、ユーザに興味がありそうな広告をレコメンドして収益を確保しています。広告主から毎日たくさんの広告を入稿し、どれがユーザが興味を持っているかをMLモデルで予測しています。
広告は文字だけではなく、画像と動画などにも深い意味を持っています。そのために、自然言語処理(NLP)に基づいたコンテンツベースのレコメンドだけでは不十分であります。その代わりに、ユーザが過去クリックし、コンバージョンした履歴に基づいてレコメンドしています。いわゆる協調フィルタリングベースの手法です。
課題
しかし、協調フィルタリングベースの手法では、新規ユーザと新しい広告の履歴が少ないため、うまくレコメンドできない場合があります。 これはCold-start問題と呼ばれています。この問題を解決するために、まず新しく入稿した広告の重みを調整し、優先的にユーザに表示する仕組みが作られました。その上、新広告のデータがたまったら、一刻も早く収集したログをモデルに学習させたいです。
早くCTR予測モデルを再トレーニングするために、データ収集と学習自動化のパイプラインを作りました。そのパイプラインも毎日数回の高頻度で回しています。イメージは以下のようになっています。
しかし、このように高頻度に学習すると、トレーニングコストが大きくなる上長い期間のログを毎回学習することが非現実的になります。そのため、既存モデルは毎回短い期間のログだけ学習しています。学習データの期間が短くすることでコストとモデルのトレーニング時間を抑えています。その反面、短い期間のログだけ利用できるようになり、長期的な情報が失われてモデル精度が落ちています。
また、今はニューラルネットワークに基づいた新モデルを導入する途中です。新モデルではどのように長い期間のログを学習しながら、高頻度に新規ユーザと広告の情報を学習していくのかは、問題になりました。
解決策:Incremental Trainingの導入
今回の課題に対して、モデルの変更に伴い、以下の論文の提案手法Incremental Trainingを採用しました。
A Practical Incremental Method to Train Deep CTR Models
Incremental Trainingは長い期間で学習したモデル(batch model)に基づいて、増えた部分のログだけ学習する技術です。
上の図のように、従来のトレーニング手法(batch training)で使うログデータは前回の学習と大半が重複しているために、学習コストが高くなっています。
Incremental Trainingでは前回トレーニングしたモデルをfine-tuningし、新しく増えた分のデータを学習させます。とてもシンプルなアイディアです。
しかし、ニューラルネットワークモデルに増えた分だけ学習することも簡単にはいきません。 特に、新規広告IDとユーザIDなどの情報は旧モデルに存在しないために、正しく対応する必要があります。
この問題を解決するために、論文では以下のようなアーキテクチャが提案されています。
1, 特徴モジュール
新規ユーザIDと新しい広告IDなど、カテゴリ特徴の新IDの頻度をカウントして閾値を設けています。閾値以上になったら、特徴の学習すべき新IDとして、ランダムのEmbedding値を与えます。トレーニング途中で他の特徴と同じ扱いになります。
Embedddingを初期化するときのコードのイメージは以下です。
if len(trained_embedding) == len(vocab): return trained_embedding embedding_matrix = np.random.normal( loc=0, scale=1.0e-4, size=(len(self.vocab), embedding_dim) ) for i in range(len(trained_embedding)): embedding_matrix[i] = trained_embedding[i] if "__PAD__" in self.vocab: self.vocab["__PAD__"] = self.pad_token embedding_matrix[self.pad_token, :] = 0 # set as zero vector for PAD return embedding_matrix
2, モデルの継承
前回トレーニングしたモデルの重みをそのまま継承し、増えた分の学習データでFine-tuningする形で学習します。 元の論文は蒸留の手法を加えて、前回の知識を引き継いでいます。
下のアルゴリズムは蒸留(KD)とFine-tuning(FT)を結合し、学習しています。
実際にオフライン実験を回した結果、Fine-tuningだけの場合が一番高いAUCが得られました。
実験結果
Gunosyデータでのオフライン実験でAUCに基づいてIncremental手法を評価しました。
実装はOpen sourceのPyTorch CTRモデルライブラリ FuxiCTR を使っています。
Incrementalで長い期間のデータを学習できるようになりました。Incrementalのモードで学習時間を大幅に短縮でき、AUCにも改善が見れました。
例えばBatch Trainingの時、二ヶ月のログデータ(サンプリングされた)を学習するために、2〜3時間が必要です。Incrementalの方は1日だけなので、3分以内で学習が終わります。
本番実装時、Batch Trainingは週一回で、そして数時間ごとの頻度でIncremental Trainingを実施する予定です。
また、Batchモデル一回だけ回して、その後Incrementalだけ学習する場合も実験しました。 二週間でも学習効果の劣化とAUCの低下はありませんでした。
まとめ
- CTRモデルの学習でIncremental Trainingを実装した
- baselineモデルより長い期間のログデータを学習できた
- Gunosyのデータで元論文と一致した結果が得られた
- 学習の時間とコストを大幅に短縮できた
- Incremental Trainingにおいて明らかの劣化はオフライン実験ではなかった
将来はオフライン実験の結果を本番で新モデル導入とともに実装します。ユーザ、メディアと広告主に対して「三方よし」の精神でサービスを磨いていきます。