みてね×gihyo.jpスペシャル

みてねのステッカープランにおけるステッカー自動提案の仕組み

株式会社MIXIで家族アルバム みてね⁠以下みてね)のData Engineeringグループ所属の吉野と申します。

みてねでは機能の1つとして「ステッカープラン」を提供しています。本記事ではステッカープランの概要からステッカー自動提案が届くまでの処理の流れとそれを支える基盤アーキテクチャについて紹介します。

ステッカープランとは

みてねにアップロードされた画像から毎月8枚のステッカーを自動生成し配信するサービスです。配信されたステッカーはみてねアプリ内のコメント欄で使用でき、より気軽にコミュニケーションを取ることができます。また、ステッカーは毎月新しいデザインで作成されますので、配信されたステッカーを見るだけでも楽しんでいただけるプランとなっています。

図1 ステッカープラン紹介
図1 ステッカープラン紹介

ステッカー自動提案の処理の流れ

「ステッカー自動提案」とは、ユーザが家族のアルバムにアップロードした画像の中からシステムが自動で画像に写る顔を抽出し、それをステッカーテンプレートと合成して生成されたステッカーをユーザに提案することを指します。

図2 ステッカー生成に必要な要素
図2 ステッカー生成に必要な要素

では、ステッカー自動提案をどのように実現しているのかを解説します。主にステッカー自動提案の処理は次の4ステップで構成されています。以降で各ステップの中身を詳細にみていきます。

  1. 自動提案を配信する家族を抽出
  2. 合成対象の顔を抽出
  3. 合成対象の顔とテンプレートを合成してステッカーを生成
  4. ステッカーを各家族に配信
図3 ステッカー自動提案アーキテクチャ図
図3 ステッカー自動提案アーキテクチャ図

①自動提案を配信する家族を抽出

まず、ステッカー自動提案を配信する家族を抽出します。これは毎月任意のタイミングで定期バッチ(Batch Server)が起動し自動実行され、特定の配信条件を満たす家族のみが抽出されます。

この配信対象の家族抽出処理は、Batch ServerからBigQueryへクエリを投げ取得しています。なぜなら、配信対象となる家族数は非常に多く、各家族のメディア数や直近のログイン履歴などから配信条件を満たすか否かの判定を行う必要があるため、アプリケーションデータベース(MySQL)で同クエリを実行するとみてねシステム全体に高負荷がかかります。これを起因として、その他機能にも影響を及ぼす恐れがあるため負荷をBigQueryにオフロードしています。

Batch Serverは配信対象家族を取得すると、並列で後続処理を実行するため各家族ごとにJobを作成し非同期ワーカー(Sidekiq Worker)が処理対象とするQueueにJobを追加します。

図4 Batch Serverの処理の流れ
図4 Batch Serverの処理の流れ

しかし、Batch ServerはBigQueryから非常に多くの家族を取得するため途中で処理が停止した場合に、処理を最初から再実行するとJobを重複して積むので時間やコスト、DB負荷を余計にかけてしまいます。そのためBatch Serverには処理を途中から再開できる仕組み(冪等性)を持たせています。

  1. BigQueryから対象家族をユニークかつソート可能なキー(仮称:family_key)を参照し、特定の順序で取得する
  2. Batch Serverはfamily_keyを参考に特定の順序でJobをQueueに追加する
  3. 「2」で積んだJobに紐づくfamily_keyをBatch Serverから参照可能な外部キャッシュストアに保存する(最後に積んだJobに紐づくfamily_keyを更新し続ける)
  4. 「2,3」「1」によって取得した対象家族のJobをすべて積むまで繰り返す
図5 Batch Serverの冪等性を考慮した設計
図5 Batch Serverの冪等性を考慮した設計

つまり、再実行でBigQueryへ対象家族抽出クエリを投げるときは、次のように条件を1つ付与することで未処理の対象家族のみを取得でき、処理を途中から再開できます。

BigQueryへの発行クエリイメージ
# 通常の対象家族抽出クエリ
SELECT family_number FROM [table_name];

# 再実行時の対象家族抽出クエリ
SELECT family_number FROM [table_name] WHERE family_key > [外部キャッシュストアに保存されているfamily_key]

②合成対象の顔を抽出(顔抽出Worker)

合成対象の顔は、可能な限りお子さんの顔を選び、その中でもより良い顔を抽出することを目指しています。では、どのような処理で合成対象の顔が抽出されるのかを解説します。これは主に「アップロード時の画像・動画解析処理」「ステッカー自動提案時の3つの処理(画像フィルタ、顔フィルタ、顔スコアリング⁠⁠」から成り立っています。

a. アップロード時の画像⁠動画の解析処理

みてねでは、ユーザが画像・動画をアップロードすると機械学習モデルを用いた画像・動画解析処理が実行され、顔の検出や顔の傾き推定、年齢推定などが解析されます。そして、解析結果はデータベースに保存し、各種自動提案(ステッカー、1秒動画など⁠⁠、人物ごとのアルバム機能に活用されます。

図6 メディアアップロード時のメディア解析処理
図6 メディアアップロード時のメディア解析処理

b. 画像フィルタ

顔を抽出する上で適していない画像を除外します。たとえば、事前の解析処理結果を参照し、顔が検出されていない画像や集合写真などの小さすぎる顔のみ検出される画像。また、⁠Perceptual Hash」という手法を用いて設定したハミング距離の閾値を下回れば、連写・類似画像も除外します。

情報Perceptual Hashとは、画像や動画などのメディアデータからハッシュ値を計算するアルゴリズムで、類似画像であればハミング距離は小さくなり(まったく同じ画像であればハッシュ値は同値になりハミング距離は0になります⁠⁠、まったく異なる画像では値は大きくなる性質があります。

c. 顔フィルタ

画像から抽出した顔の中で適していない顔を除外します。たとえば、画像から見切れている顔、画像に対する顔面積が小さすぎる顔、横向き顔・俯き顔など。この段階でステッカー生成することが難しい顔を除外することで、画像における主要な被写体の顔を絞り込むことができ、よりステッカーに適した顔を抽出することが可能になります。

d. 顔スコアリング

顔フィルタを通過した顔の中でもより良い顔を抽出します。正面顔や画像に対する顔面積が大きめの顔、お気に入り・コメント等が付いた画像に対してスコアを加算し、子供の顔の優先度を上げるため年齢推定結果を参照して子供年齢と近ければさらに加算をします。そして、スコア上位の顔のみを合成対象の顔として抽出します。

③顔画像とテンプレートを合成(合成Worker)

前段で抽出した顔とテンプレートを合成してステッカー画像を生成します。

前段の顔抽出Workerと合成Workerを分割している理由は、パフォーマンスのボトルネックが異なるためです。顔抽出Workerは対象候補となる画像情報が保存されているレコードとそれに紐づく解析結果をDBから取得するため、Server⇔DB間のネットワーク通信の入出力操作がボトルネック(=I/Oバウンド)となり無闇に並列数を上げるとDBに高負荷を与えます。一方、合成Workerは主に顔画像とテンプレートの画像合成処理を行うためCPUがボトルネック(=CPUバウンド)となります。

図7 顔抽出Workerと合成Worker
図7 顔抽出Workerと合成Worker

合成Workerは1家族につき複数枚のステッカー画像を生成するので処理時間も顔抽出Workerと比較すると長くなりますが、DBの負荷状態を考慮不要なため容易に並列数を上げることができます。また分割しておくことでMemory、CPUも各Workerに最適化した値に調整することができます。そして、遅延なく一連の処理を実行するため顔抽出Workerと合成Workerのスループットが同じになるよう並列数は自動スケーリングで調整しています。e.g. 顔抽出Worker1つあたり200/rpmで、合成Worker1つあたりの処理速度が10/rpmであれば並列数は1:20の割合で調整します。

④配信

生成されたステッカーは、iOS/AndroidアプリへとPUSH通知を送り、ユーザへお届けします。

図8 配信されたステッカー画面
図8 配信されたステッカー画面

最後に

この記事では、ステッカープランの概要とみてねData Engineeringグループにおける取り組みのひとつとしてステッカー自動提案の仕組みやそれを支えるインフラ構成、BigQuery活用などの負荷対策についてご紹介しました。今後も負荷対策のほか、より最適な提案を行えるよう顔抽出ロジックなどの改善にも引き続き取り組んでいきます。

おすすめ記事

記事・ニュース一覧