Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Amebaにおけるレコメンデーションシステムの紹介

9,393 views

Published on

2016年7月25日開催
「夏真っ盛り!Spark + Python + Data Science祭り」

Published in: Engineering
  • Be the first to comment

Amebaにおけるレコメンデーションシステムの紹介

  1. 1. Amebaにおける レコメンデーションシステムの紹介 2016 July 25 CyberAgent, Inc. All Rights Reserved
  2. 2. 内藤 遥(ないとう よう) ●2012年サイバーエージェント入社 秋葉原ラボ所属 ●やっていること 推薦基盤や検索基盤の開発、運用 ●趣味 カラオケ、炭水化物ダイエット
  3. 3. タイトル TITLE事業内容 インターネット広告事業 メディア事業 ゲーム事業 広告代理事業 自社広告商品 (アドテク) など など など
  4. 4. 本日のアジェンダ ①レコメンデーションシステムの概要について ②Item-to-Item Collaborative FilteringのSpark実装について
  5. 5. 本日のアジェンダ ①レコメンデーションシステムの概要について ②Item-to-Item Collaborative FilteringのSpark実装について
  6. 6. 秋葉原ラボが提供しているレコメンデーションシステム ● A.J.A. Recommend Engine ○ 外部メディア向けのテキスト類似度を基にしたレコメンド ● Phoenix(プロジェクト名) ○ 協調フィルタリングによるレコメンドがメイン ○ Spark を使って推薦結果を作成 ● バトルレコメンド ○ 各種ゲームのギルドのバトルなどのマッチングをするシステム 以降、レコメンデーションシステム = Phoenix のレコメンデーションシステムとして話を進めていきます 今回のお話
  7. 7. レコメンデーションシステムの利用
  8. 8. Seasparrow (データ解析基盤) レコメンデーションシステム概要 HDFS YARN Spark HBase ZooKeeper Services アクティビティログ ・閲覧 ・購入 ・いいね ・お気に入りなど ● アクティビティログの転送 ○ 購入履歴や閲覧履歴といったユーザの行動のログを データ解析用のHadoopクラスタに転送する
  9. 9. Seasparrow (データ解析基盤) レコメンデーションシステム概要 HDFS YARN Spark HBase ZooKeeper Services Job Scheduler Recommendation Program ● サービスごとに必要なデータソースや推薦アルゴリズムを選択 ● 推薦結果の作成 submit 推薦アルゴリズム ・Matrix Factorization(MLLib) ・Item-to-Item Collaborative Filtering(実装) ・PLSA(実装) など 外部データソース
  10. 10. Seasparrow (データ解析基盤) レコメンデーションシステム概要 HDFS YARN Spark HBase ZooKeeper Services Recommendation Program ● 推薦結果をHBaseに格納 rowkey: <ソルト(<> ハッシュ値の先頭1byte)> _<推薦ID(service + algorithm)> _ <user or itemID>_<version(timestamp)> value: <> に対する推薦結果(アイテムID, スコアなどのリスト)をシリアライズ ● ZooKeeper上の推薦IDに紐づくversion情報を更新 API notification 推薦ID: version
  11. 11. Seasparrow (データ解析基盤) レコメンデーションシステム概要 HDFS YARN Spark HBase ZooKeeper Services ● 推薦リクエスト ● imp, clickログの転送 Services Zero (リアルタイムカウンタ) API ・時間 ・推薦ID (service + algorithm) ・枠ID (どの面で推薦結果を表示したか) ・推薦対象 (user or itemID) ・推薦結果 ・imp/click など LB request response ・user or itemID ・推薦ID ・version
  12. 12. Seasparrow (データ解析基盤) レコメンデーションシステム概要 HDFS YARN Spark HBase ZooKeeper Services ● CTRのレポーティング、CTRを基にした推薦結果のリランキング Services Zero (リアルタイムカウンタ) Counting Program reporting API LB 推薦結果のimp/click バンディットアルゴリズム CTRを基にリランキング counting
  13. 13. 本日のアジェンダ ①レコメンデーションシステムの概要について ②Item-to-Item Collaborative FilteringのSpark実装について
  14. 14. Item-to-Item Collaborative Filtering ● ユーザベースの協調フィルタリング ○ ユーザ間の類似度をアイテムの評価を要素にしたベクトルで求め、自分とよく似 ているユーザの評価が高いアイテムを推薦する ○ 問題点 ■ アイテムの評価が少ないと精度が悪い ■ 計算量が多く、オンラインでの推薦が困難 ● アイテムベースの協調フィルタリング ○ アイテム間の類似度をユーザの評価を要素にしたベクトルで求め、対象のアイテ ムと同じように評価されているアイテムを推薦する ○ 購入履歴などの評価情報をリクエストに含めることで、オンラインでの推薦が可 能 ○ アイテムの評価が少なくても精度の良い推薦ができる
  15. 15. Item-to-Item Collaborative Filtering user item1 item2 item3 item4 A ◯ ◯ ◯ B ◯ ◯ ◯ C ◯ D ◯ ◯
  16. 16. Item-to-Item Collaborative Filtering user item1 item2 item3 item4 A ◯ ◯ ◯ B ◯ ◯ ◯ C ◯ D ◯ ◯ アイテム間の共起数 (ユーザの重複数)、 アイテムを評価したユーザ数 がわかればよい
  17. 17. Sparkでの実装(Java) ● 事前準備として、各itemを int のIDに変換する(データ量によってはuserも) Sparkではzip関数でユニークなIDを付与することもできるが、long型であまり メモリ効率がよくないため、int型のIDに変換する内製のIDMakerを利用している (user ID, item ID) の評価データの集合のRDDを生成する JavaPairRDD<Integer, Integer> userItemPairData = ...
  18. 18. Sparkでの実装(Java) アイテムを評価したユーザ数の取得 JavaPairRDD<Integer, Integer> itemCountData = userItemPairData.mapToPair(tuple -> { return new Tuple2<>(tuple._2, tuple._1); }).groupByKey().mapToPair(tuple -> { return new Tuple2<>(tuple._1, Iterators.size(tuple._2.iterator())); }); Int2IntMap itemCountMap = new Int2IntOpenHashMap(itemCountData.collectAsMap()); Broadcast<Int2IntMap> broadcastItemCountMap = context.broadcast(itemCountMap);
  19. 19. Sparkでの実装(Java) アイテムを評価したユーザ数の取得 JavaPairRDD<Integer, Integer> itemCountData = userItemPairData.mapToPair(tuple -> { return new Tuple2<>(tuple._2, tuple._1); }).groupByKey().mapToPair(tuple -> { return new Tuple2<>(tuple._1, Iterators.size(tuple._2.iterator())); }); Int2IntMap itemCountMap = new Int2IntOpenHashMap(itemCountData.collectAsMap()); Broadcast<Int2IntMap> broadcastItemCountMap = context.broadcast(itemCountMap); user, itemの並び替え item ごとに user を連結 Guavaライブラリ
  20. 20. Sparkでの実装(Java) アイテムを評価したユーザ数の取得 JavaPairRDD<Integer, Integer> itemCountData = userItemPairData.mapToPair(tuple -> { return new Tuple2<>(tuple._2, tuple._1); }).groupByKey().mapToPair(tuple -> { return new Tuple2<>(tuple._1, Iterators.size(tuple._2.iterator())); }); Int2IntMap itemCountMap = new Int2IntOpenHashMap(itemCountData.collectAsMap()); Broadcast<Int2IntMap> broadcastItemCountMap = context.broadcast(itemCountMap); fastutil ライブラリを使って省メモリ・高速化 メモリ量はそんなに大きくならないため、ブロードキャスト変数を利用して各ワーカーに転送 複雑なJoinの操作がなくなり、処理がシンプルに書ける
  21. 21. Sparkでの実装(Java) アイテム間の共起数の取得、コサイン類似度の計算 // アイテムと共起するアイテムリストのRDDを生成 JavaPairRDD<Integer, int[]> itemCoItemsPairData = userItemPairData.groupByKey().values() .flatMapToPair(items -> { int[] itemArr = StreamSupport.stream(items.spliterator(), false) .mapToInt(i -> i).toArray(); return Arrays.stream(itemArr).mapToObj(i -> new Tuple2<>(i, itemArr)) .collect(Collectors.toList()); });
  22. 22. Sparkでの実装(Java) アイテム間の共起数の取得、コサイン類似度の計算 // アイテムと共起するアイテムリストのRDDを生成 JavaPairRDD<Integer, int[]> itemCoItemsPairData = userItemPairData.groupByKey().values() .flatMapToPair(items -> { int[] itemArr = StreamSupport.stream(items.spliterator(), false) .mapToInt(i -> i).toArray(); return Arrays.stream(itemArr).mapToObj(i -> new Tuple2<>(i, itemArr)) .collect(Collectors.toList()); }); ユーザごとのアイテムリストにフォーカス primitive の配列を使うとメモリ効率が良い アイテムごとに共起アイテムの配列を紐付け
  23. 23. Sparkでの実装(Java) アイテム間の共起数の取得、コサイン類似度の計算 itemCoItemsPairData.groupByKey().mapToPair(tuple -> { Int2IntOpenHashMap itemCoMap = new Int2IntOpenHashMap(); int item = tuple._1; Int2IntMap itemCountMap = broadcastItemCountMap.value(); for (int[] coItems : tuple._2) { for (int coItem : coItems) { if (item == coItem) continue; // 自身のitemは含めない itemCoMap.addTo(coItem, 1); } } for (Int2IntMap.Entry entry : itemCoMap.int2IntEntrySet()) { int coItem = entry.getIntKey(); int coCount = entry.getIntValue(); double similarity = coCount / (Math.sqrt(itemCountMap.get(item)) * Math.sqrt(itemCountMap.get(coItem))); ... // sort
  24. 24. Sparkでの実装(Java) アイテム間の共起数の取得、コサイン類似度の計算 itemCoItemsPairData.groupByKey().mapToPair(tuple -> { Int2IntOpenHashMap itemCoMap = new Int2IntOpenHashMap(); int item = tuple._1; Int2IntMap itemCountMap = broadcastItemCountMap.value(); for (int[] coItems : tuple._2) { for (int coItem : coItems) { if (item == coItem) continue; // 自身のitemは含めない itemCoMap.addTo(coItem, 1); } } for (Int2IntMap.Entry entry : itemCoMap.int2IntEntrySet()) { int coItem = entry.getIntKey(); int coCount = entry.getIntValue(); double similarity = coCount / (Math.sqrt(itemCountMap.get(item)) * Math.sqrt(itemCountMap.get(coItem))); ... // sort アイテムごとの共起アイテムの配列を集める 共起数をカウント コサイン類似度の計算
  25. 25. Tips ● 推薦結果のアイテムを鮮度の新しいものだけにしたい ○ マスターデータから条件に合致する推薦候補のアイテムリストを作成して、ブロー ドキャスト変数として転送 ○ 推薦結果作成時にバリデーションをかける IntSet validItemSet = IntOpenHashSet(); ... // 有効なアイテムの追加 Broadcast<IntSet> broadcastValidItemSet = context.broadcast(validItemSet);
  26. 26. Tips ● Spark上のバッチアプリケーションをJMXでモニタリングしたいが、portの管理が面倒く さい ○ 事前にレコメンデーションが使うport のレンジを決めておき、zookeeper上に最新 バッチで使用しているport番号をセットし、 バッチ実行の度にインクリメントしていく ○ curatorライブラリの DistributedAtomicInteger を使うと便利
  27. 27. メンバーを募集しています! ●詳しくはコーポレートサイトまで!     https://www.cyberagent.co.jp/recruit/career/jobs/ ●もしくはお気軽に内藤までお声かけ ください!

×