推薦するコンテンツを最適化する仕組み

渡邉直樹氏(以下、渡邉):そしてLearning workerにログが渡ってきて学習されるんですが、ここでどのように学習されているかを見ていきましょう。

全体最適の部分では2つの軸で学習の粒度を分けています。

1つ目はユーザサイド、2つ目はコンテンツサイドです。ユーザサイドでは、マシンラーニングで推定された年齢と性別を使っています。コンテンツサイドでは見ている時間であったりコンテンツのIDそのものであったり、コンテンツの種類を使っています。

たぶんこれを見ただけでは意味がよくわからないと思うので、具体的な例を見ていきましょう。ニュースのArmの切り方の例です。

まず、表の上段部分がユーザのフィーチャーになっています。20から24といった数値が書いてありますが、これは年齢です。あくまで推定なので、5歳刻みのレンジになっています。さらにその下には、性別でも分けています。

一方、左の列はコンテンツのフィーチャーになります。LINE NEWSのアイコンの横にある数値とアルファベットの文字列が一つひとつの記事のIDになっています。それぞれの記事に対して各年代の性別ごとにユーザの反応を学習した結果が0〜1の数値で表示されています。緑色に色を付けている記事が、スコアが高くその人に推薦できるコンテンツになります。

一方、別の例を見てみましょう。

これは天気のArmの切り方の例です。ユーザのフィーチャーはニュースのときと同じなんですが、こちらのコンテンツのフィーチャーが違います。天気では天気の種類、晴れ・曇り・雨・雪によって学習を分けています。一般的には、天気が崩れるとスコアが上がる傾向にあります。とくにこの辺りからスコアがグッと上がっています。

これは天気が崩れると傘を持って行かないといけなかったり、雪で交通に影響が出るなど、実生活に影響が出るので関心が高くなる傾向にあるからだと考えられます。一番スコアが高い傾向にあるのがこの青枠の部分。晴れのち雪のように天気が途中で変化するケースです。自分もそうなんですが、何時から天気が崩れるのかが気になるので、比較的にクリックしやすくなるんですね。なのでスコア的に高くなることが多いです。

ちなみにこの表では表現しきれなかったんですが、他には0時〜23時までの時間も加味しています。ちなみにここに挙げている数値はあくまでイメージなので、実際はこんなに大きな差は付かなくて、もっと小さい値で0.01ぐらいの差になるのですが、天気によって変わる傾向にあるというのは本当のことです。

今見てきたように、コンテンツごとのテーブルがたくさん集まっています。

この表では1つのコンテンツにつき1行で簡単に書いてしまっていますが、ニュースや天気の例のようにもっと細かく学習されたものがそれぞれの行に入っていると思ってください。こうして計算されたパラメータを使って、リアルタイムにランキングを処理をして表示するコンテンツを選んでいます。

膨大なトラフィックをいかにして処理するか

ラーニングの実装でも工夫していることが3つほどあります。

1つはLINEアプリから膨大なトラフィックが来るので安定的に処理するための工夫です。2つ目は同じく、膨大なトラフィックに対応するために処理をできるだけ分散していることです。3つ目は、実は2つ目と相反することになるんですが、一部の処理は分散してしまうと学習の効率が悪くなるので分散の仕方をちょっとだけ工夫しています。これもフローを見ながら確認していきましょう。

LINEのアプリから直接リクエストを受けるCRS Engineには、膨大なトラフィックが来ます。

最初の2つでご紹介したとおり、ターゲットは25万リクエスト/秒です。このCRS EngineはオンラインでコンテンツをスコアリングするRankerという役割があります。なので、先ほどラーニングのプロセスで学習されてきたモデルのパラメータを参照する必要があります。これはRedisクラスタに格納されています。

一方、バナーがLINEアプリに表示されたあと、impやクリックのイベントが集計されてラーニングのプロセスまで流れてきます。先ほどご説明したとおり、impを基準に集計しているので、ここまでトラフィックの数は膨大です。学習について複数のノードで分散して処理するんですが、さすがにこの規模のトラフィックをそのままラーニングの処理に流すのはつらいので、ある程度スロットリングしています。それがここのLearning Dispatcherです。

まずはこのロードバランシングの仕組みを説明します。

バンディットの学習のためのログデータはアームごとに蓄積していきます。複数のアームにデータが積まれるイメージです。アームによってデータの溜まり方というのはマチマチで、例えば3番のアームは頻繁に露出するためデータがたくさん溜まっています。なので負荷分散のためにできるだけ分散して複数のノードで学習したいところです。

一方、2番のアームは露出が少ないのか、データの溜まり方が遅いです。なのでこれを分散して処理をしてしまうと学習がなかなか進みません。できるだけ単一の、同じノードで学習を続けたいです。こういった二律背反な要求を満たすための準備として、まずは定数K個ごとにデータを分割していきます。この分割された緑色の箱を我々はパーティションと呼んでいます。

アームの番号とパーティションの番号の組み合わせが同じものは、必ず同じノードで処理されるようにしています。例えばここであればアームが3でパーティションが3、こちらはアームが2でパーティションが1です。このアームとパーティションの番号のペアを使ってConsistent hashを使ってノードを固定していきます。Consistent hashにはNGINXを使っています。

この部分がNGINXです。このまま後続のLearning workerにConsistent hashして渡されていきます。

ここがTrainerになっていて、バンディットの学習がされています。しかし、このworkerで学習された内容はそのまま使うのではなく、もう1ステップ加工します。また、NGINXを経由してパラメータサーバで処理をしてからModel Parameter Redisに書き込んでいきます。

リアルタイムと非同期を組み合わせた「Hetero-Architecture」

ここはやや複雑なので簡単にお話しておきます。

こちらの前半部分はリアルタイムに処理がされています。一方ラーニング処理は効率化のため非同期で、ある程度まとめて処理されています。これにはリアルタイムで処理をするCRS EngineでのRankerとしての処理の役割を、できるだけ軽くしたいというニーズがあります。

そのため複雑で重い演算は非同期的にLearning worker側で行っています。この学習がリアルタイムな処理と非同期の処理に分かれているので、ここを同期させるための工夫がパラメータサーバでされています。実はここのパラメータサーバでの処理の話をもう少し詳しく紹介するセッションが別にありますので、それはスライドの最後でご紹介します。

また、ここは実装されている言語も異なっています。

リアルタイムの処理の部分は我々がサーバサイドの実装で馴染みのあるJavaで書かれており、こちらのLearning workerの部分は演算処理にアドバンテージのあるPythonで書かれています。

全部Javaで書くというアイディアもあったんですが、行列演算のライブラリがどうしてもCとかC++の実装が入ってきて、サービスレベルで安定的に処理できる自信がなかったため、複数の言語で実装してAPIで連携するというかたちに落ち着きました。我々はこの構成をHetero-Architectureと呼んでいます。Heteroというのは異なるという意味の接頭辞です。これには実装言語が異なるというだけではなくて、リアルタイムと非同期であるという違いも含まれています。

一部ユーザーに対してだけ露出を可能にする「Libra」

これでSmart Channelの一通りのコンポーネントについてご紹介ができました。この最適化の流れをなんとなくイメージしていただけたのではないかなと思います。さて、もうちょっとだけお話をさせてください。このシステムを運用・改善してきた苦労や、工夫についてです。

まず、我々はこのシステムを運用する上で一番困った話から。それはテストです。レコメンデーションのテストをしたいんですが、テスト用のベータ環境にはあまりログがありません。そのため、テスト環境ではレコメンドのテストをすることができません。

さすがにノーテストでリリースは怖いので、プロダクションの環境でテストをするしかありません。また、これはレコメンドの宿命なんですけど、レコメンドには正解というものがなくて何をもってテストがパスしたかというのは難しいです。そのため、実際にユーザに露出してみて性能を評価するしかありません。

これを解決するために我々はGradual Rolloutという機能を提供しています。これはプロダクションの環境でいきなり全ユーザにドンっと表示をするのではなくてnパーセントだけ、例えば1パーセントとか5パーセントのユーザにだけ出してみて性能を評価したり、IDを指定することで特定のユーザにしか出ないといった制御ができます。こちらはQ&Aのために使っています。

社内にはこの機能をサポートしてくれるLibraと言われているツールがあります。画面のイメージがLibraです。

ここの下の「Rollout」というところで、露出のパーセンテージを設定することができます。これは変更もノーデプロイでできるので、非常に便利です。

レコメンドにおけるKPIと、改善のための仕組み

次の話に移ります。レコメンドのパフォーマンスの話です。我々の目的はユーザに喜んでもらうことなので、何らかの指標として、ユーザが喜んでくれているかどうかを指標として常にウォッチしておきたいと考えています。何を見るのが良いのか我々はいろいろ悩んだんですが、シンプルにポジティブな反応であるクリックを増やしてネガティブな反応であるミュートを減らすのが良いと考えました。

そこで「クリック数÷ミュート数」の結果をスコアと定義をして、重要なKPIの指標としてウォッチしています。このスコアを上げていくことが我々の日々の目標になっています。そのためには、このスコアの改善のために日頃からABテストをしています。さっきGradual Rolloutのところでも紹介した、ABテストツールのLibraがここでも活躍します。

改善の内容はUIの改善、レコメンドロジックの改善、バンディットのArrmの切り方の変更など多岐に渡ります。ABテストの設定は非常に簡単にできるようになっていて、このあたりでABテストの設定がされています。こちらもオンラインでいつでも変更できるので、非常に便利です。Smart ChannelではABテストは期間を一週間と限定して毎週何らかのテストを回しています。

このABテストの結果はデータサイエンティストが分析のレポートを出してくれます。

この例はすごくシンプルな例なんですが、実際には数値が何パーセント変化したとか有意差があるかどうかなどを判定してくれます。A or Bみたいにきっちり結果が出ないこともけっこう多くて、仮説を立てながらデータを分析したり、追加でABテストをしたり、さまざまなケースがあるのでとても心強いです。

バンディットのデメリットと、“真のパーソナライズ”への道

さて、改善の最後にご紹介するのは先ほどまで話していたバンディットについてです。実は先ほどまでのロジックには弱点がいくつかあります。

まず1つ目は学習が始まるまで10分間掛かってしまう点です。最初の学習までは最適化が効かず、探索をしないといけません。この図で言うと、A・B・Cが最初のうち同じ割合ぐらいで出ているところがありますが、これがそれにあたります。

2つ目がより重要で、バンディットのフィーチャーで、ユーザのフィーチャーがAge × Genderで切られている点です。これはArmを一人ひとりに分けてしまうと学習が収束しないという理由からなんですが、果たしてこれは真の意味でのパーソナライズと言えるのだろうか、という疑問が出てきます。できればここもユーザ一人ひとりに最適化していきたいと思います。

そこで、バンディットのアームとは別にユーザがそれぞれのコンテンツに対して過去にどんな反応をしてきたか。クリックのCTRとxのCTR、これはミュートのCTRです。これらを最適化の補足的なパラメータとして加味しています。

イメージにするとこんな感じです。

ユーザごとに各コンテンツに対する過去の反応を集計しています。もちろん、使っていないコンテンツの情報は出ないようになっているので、パラメータが存在しないところもあります。こういったパラメータを補足的に使うことで早い段階から確率を絞ることができます。

イメージにするとこんな感じで、各ユーザとも早い段階から最適なコンテンツが選ばれる確率が上がることが期待されています。この辺りは他にもいろいろアプローチがありますので、まだまだ改善の余地がありそうです。

Smart Channel以外にもレコメンドの提供を計画中

さて、最後に今後の計画をいくつか簡単にご紹介しようと思います。1つ目はライフインフォメーションのサポートです。どんな情報かというと、みなさんの生活に関わる大事な情報、例えば地震の情報とか、地域の安全情報とか、あとは交通情報などを指しています。

他にも花粉の情報とかエアコンディションとか、ゲリラ豪雨がもうすぐ来るぞという情報も考えています。

日々、どんな情報があったらみなさんに便利と思っていただけるか、Smart Channel開発メンバーでいつも考えているので、もし何か良いアイディアとかほしいものがあったら僕らに教えてください。

次はバンディットAPIです。今紹介してきたバンディットのシステムは作るのがけっこう大変なので、Smart Channelのバンディットの機能の部分だけAPIにして提供してしまったら楽なんじゃないか、という意欲的なプロジェクトです。どうやって動作するかというと、まず連携サービスからユーザのIDと候補となるコンテンツのIDを配列でもらって、バンディットAPIでランキングにして返します。

サービスからはフィードバックとしてimpやクリックの情報を送ってもらい、これを学習していきます。バンディットの最適化はデータが何であるかを知る必要はなくて、あくまでIDに対するリアクションだけを学習する。いわゆるMeta optimizerとして動作するので、こういったことが可能になります。

また、レコメンド自体をSmart Channel以外にも提供しようという計画もあります。今はトークタブ、つまりSmart Channelにしか提供していないんですが、コアシステムであるCRSはとくに場所を限定しないのでLINEの中であればウォレットタブとかホームタブにSmart Channelとは違ったコンテンツセット、例えばウォレットタブであればクーポンとかに限定したコンテンツを提供することが考えられます。

また、コンテンツを提供してもらっている各サービスに、学習して最適化されたコンテンツを返すとか、先ほどご説明したバンディットAPIを提供するとか、学習されたパラメータ自体を提供するというフィードバックもできるように考えています。

最後にご紹介するのがロケーションやビーコンを使った機能の拡張です。これはまだプランニング段階なのであまり具体的なものは出てこないんですが、わかりやすい例でいうと今の天気予報はユーザの推定居住地域か、LINE NEWSに天気予報のコーナーがあるんですが、そこで設定されている都市を出しています。しかし、例えばみなさん旅行に行ったら旅行先の天気を見たいですよね。なので、今現在いる場所の天気を表示するような改善を入れたいと思っています。

あとはこれはイベント的なものなんですが、特定の場所だけで見れるコンテンツなどを考えています。LINEにはBeaconのプラットフォームもあるので、Beaconを使った便利情報とかその場所に行かないと見れないコンテンツとか、楽しいコンテンツを提供していきたいと思っています。

どれも共通して言えることは、ユーザのみなさんが便利と思っていただけるコンテンツを増やしていきたいと考えていることです。ぜひ楽しみにしていてください。

Smart Channelにまつわるプレゼンテーションは他にも

私の発表は以上なんですが、最後にお知らせです。今回、Smart Channelに関するプレゼンテーションが私の発表以外にも3つあります。

1つ目は今日の夕方のセッションで、Smart Channelのマシンラーニングの部分をより詳しくマシンラーニングのエンジニアが解説しています。このセッションで、マシンラーニングの部分をもっと聞きたかったという方は、ぜひ聞きに行ってみてください。

2つ目は明日のセッションになるんですが、Smart Channelのプロダクトマネジメントの話です。非常にLINEらしいやり方で開発されているSmart Channelを例に、LINEでのモノづくりがどうやって行われているかを話しています。3つ目はポスターセッションなので2日ともあるんですが、Smart ChannelではどのようなABテストが、どのように行われて、実際にどんな成果があったのかを解説しています。

これらのすべてのセッションは内容ができるだけ重複しないように工夫していますので、Smart Channelに興味を持っていただけた方は全部見ていただけると、システムの理解だけではなくLINEでの開発の仕方が見えてくると思います。

もちろん、他にもたくさん楽しいセッションがあるので楽しんでいってください。私は、このあとAsk the Speakerとして会場を出たところにおりますので、質問や感想など、「こういうコンテンツがほしいよ!」みたいなものがあったら、ぜひお気軽に声を掛けてください。

また、ブース展示のほうで「WORK AS LINER」というLINEのエンジニアの働き方を紹介するブースがあるので、LINEのエンジニアに興味があるとかLINEで働いてみたいという方は、もしおられたらぜひ遊びに来てください。

以上です。長い時間ありがとうございました。