Subscribed unsubscribe Subscribe Subscribe

TensorFlowでのDeep Learningによるアイドルの顔識別 のためのデータ作成

Python Ruby

続・TensorFlowでのDeep Learningによるアイドルの顔識別 - すぎゃーんメモ の続き、というかなんというか。
前回までは「ももいろクローバーZのメンバー5人の顔を識別する」というお題でやっていたけど、対象をより広範囲に拡大してみる。

様々なアイドル、応援アプリによる自撮り投稿

あまり知られていないかもしれないけど、世の中にはものすごい数のアイドルが存在しており、毎日どこかで誰かがステージに立ち 歌って踊って頑張っている。まだまだ知名度は低くても、年間何百という頻度でライブを中心に活動している、所謂「ライブアイドル」。俗に「地下アイドル」と言ったりする。

そういったアイドルさんたち 活動方針も様々だけど、大抵の子たちはブログやTwitterを中心としてWebメディアも活用して積極的に情報や近況を発信していたりする。

そんな中、近年登場した「自撮り投稿サービス」。
概要としては、各アイドルさんが自撮りなどの写真を投稿し、ファンのユーザーが「応援」としてポイントを送ることでその数で順位がつく、というもの。ある期間で上位にランクインすると街頭広告や雑誌インタビューに掲載されるといったイベントも開催されるため、アイドルさんは可愛い自撮りをたくさん投稿するしファンはポイント貯めたり買ったりして頑張って応援する。有名なところで以下の2つのサービスが登場している。

それぞれのサービスに登録されているアイドルさんを 雑にスクレイピング して取得したところ、軽く1000件を超えた。それだけたくさんアイドルがいるわけで、最終目的としては入力した顔画像をそれら1000人以上のアイドルに分類すること、となる。

…のだけど、つまりそれを学習させるためにそれらのアイドルさんたちの「顔画像」と「(それが誰であるかを示す)ラベル」のセットが大量に必要となるわけで。

自撮り投稿から顔識別用データセットを作るには

多くのアイドルさんが利用しているこの自撮り投稿サービスから、投稿者と画像をセットで引っ張ってくれば「自撮り」なんだから自動でラベル付け済みの顔画像データセットを作れるんじゃないか、と考えたのだけど、まぁそんなに簡単な話でもなく。
まず「主に自撮りが投稿される」のであって必ず投稿した本人だけの顔が写っているとは限らない。別にそういうルールがあるわけではないので、共演した別のアイドルさんとの2ショットだったり 同じグループのメンバーと一緒の仲良しショットがあったりすることも珍しくない。そういった写真は顔検出は自動で出来ても、複数検出されたもののうちどの顔が投稿者本人なのかは分からない。
あと、そもそも上記の自撮り投稿サービスたちはデータ取得用のAPIが公開されていない。CHEERZの方はWeb版があるのでスクレイピングすれば可能そうではあるけど、DMM.yellはアプリ専用なのでそれなりにハックが必要になる。

Twitterから収集、管理するWebアプリ

…やはりラベル付けは人力でやるしかない、と覚悟を決めて、とにかくまずは収集してみることにした。
アプリには取得APIが無いが、投稿時にTwitterと連動する機能がついているようで、大抵のアイドルさんは自分のTwitterアカウントと紐付けてそちらにも画像付きTweetで流している。ので、連動投稿に付与されている「#CHEERZ」「#dmmyell」といったハッシュタグTwitter検索をかければ大抵の画像は取得できる。それらの投稿から各アイドルさんの個人アカウントも把握できるので、自撮りアプリ連携ではない普段の画像Tweetも収集対象になるように登録しておく。

そうやって定期ジョブでTwitterからひたすら画像付きTweetを取得し、 自作の顔検出器 にかけて顔部分を抽出して保存していく。このあたりは 前回までももクロちゃんデータセットを作成したときと同様で、管理用のWebアプリを自作している。

以前はこれをHerokuで運用していたのだけど、今回はあっという間に顔画像が10000件を超えて 無料枠で利用できる範囲をオーバーしてしまったので、以前から持っていたのにほぼ使っていなかったさくらVPSのサーバに移行した。

ひたすら目視でラベル付け

こんな感じで、様々なアイドルさんの顔画像が取得できる。(かわいい)

が、これらをそれぞれ「誰であるか」のラベル付けをする必要があり。僕だって一応ドルヲタとして年間200〜300の現場に足を運び何百組というアイドルを見てきたし そのへんの人よりはアイドルに詳しいつもりだから 100人とか200人くらいなら顔を識別できる自信はあるけれど、1000人くらいの顔がごちゃ混ぜになっていると流石に無理がある。

とりあえずは知ってる顔はどんどん片付けていって、あとは知らない子でも普通に自撮りっぽい画像で1人で写っていればそれはまぁ本人に違いないだろう、と判断できるのでラベルを付けていく。本当に見たことのない知らない子たちの集合写真とかはまったく判別できないので後回し。

というのを考えて、顔が1つだけ検出されている画像を優先でランダムで選択しつつラベル付けしていったり。いちおう入力しやすいよう補完つけたりインタフェースを工夫したりはしている。

気の遠くなるような作業ではあるけれど、まぁ好きなので意外と飽きない。数千件は自力でラベル付けできた。

とりあえず学習させてみる

と、こんな作業を続けているうちに 何人かはある程度の枚数の顔画像が揃うので、集まっている限りのデータを使って 前回のもの と同じ、96x96 size, 3 channelの画像を入力とする4層の畳み込みと3層の全結合によるネットワークを使って学習させてみる。
1分類につき10枚程度だと心許ないが、30枚くらい学習させたらある程度の特徴を掴んでそれっぽいものは判別してくれるようになるのでは…?という目論見。

学習用と評価用とかデータセットを分ける余裕はないので、とりあえずはラベル付けしたものは全部学習用に使う。前回と同じTFRecordのファイルを入力に使うため、 ダウンロードできるように顔画像JPEGバイナリとラベル番号のセットを含むTFRecordバイナリを吐くエンドポイントも 実装 した。

ラベル付けして30枚以上集まっている顔画像セットに、それ以外のものとして 数枚しか集まっていないものやそもそも顔画像じゃないものも「分類対象外」のラベルとして3割ほど混ぜる。数千枚を50クラスくらいに分類するもの、となる。これは前回と同じモデル(むしろ畳み込み層のパラメータ数は減らしてる)で 1000〜2000 stepくらいでも十分にcross entropyが減少して 教師データに対してはほぼミスなく ちゃんと分類してくれるようになる。

推論結果を確認、修正

で、ある程度学習が済んだモデルが出来上がったら、未分類の顔画像に対してその分類器にかけて推論してもらう。

学習済みの顔に近いものは高いスコアで識別されるはずなので、これを確認することで、未分類の顔画像に対して「誰であるか」を考えるのではなく「○○と推測されているが、合っているか」だけを考えることになるので、人間の負担が軽減し作業が捗る。

そして当然ながらまだまだ学習データ数が少ないので、この推論はすごくよく間違う(上記の画像のは全部あってます)。
傾向としては、一人金髪の子を学習すると 髪の明るい人物は大抵その子と認識するようになる とか、画質がボヤけ気味のが多い子を学習しているとボヤけてる画像はだいたいその子と認識する、とか。髪の短い子や 頬にほくろのある子 とか 一応なんらかの特徴を掴んでいるようで 似たようなものはそれっぽく分類することは多いけど、やっぱりヒトが見たら全然ちがうだろって思うような間違いをしていたりはする。

それはそれで確認しながら正しくラベル付けしてやることでまた学習に使えるデータが増えるので、ある程度の答え合わせが済んだら増えたデータセットを元に再び学習してやる。そうすることで以前間違えたようなものはもう同じ間違いはしないし、さらに精度の高い推論をするよう進化する。

似たような入力に対し何度も学習プロセスを繰り返すのに 毎回まっさらな状態から始めるのは非効率だと思ったので、学習し直すときは前回の学習済みモデルのパラメータをロードしてそこから始めることにした。分類対象数が増えた場合でも、それは最後の全結合の 隠れ層→出力層 の部分だけしか構造は変わらないので、それ以外のパラメータはそのまま使っても 初期化状態から始めるよりは早い、はず。

def restore_or_initialize(sess):
    if os.path.exists(FLAGS.checkpoint_path):
        for v in tf.all_variables():
            print 'restore variables "%s"' % v.name
            try:
                restorer = tf.train.Saver([v])
                restorer.restore(sess, FLAGS.checkpoint_path)
            except Exception:
                print 'could not restore, initialize!'
                sess.run(tf.initialize_variables([v]))
    else:
        print 'initialize all variables'
        sess.run(tf.initialize_all_variables())

https://github.com/sugyan/tf-classifier/blob/master/models/v2/train.py#L30-L46

結論

ということで、学習用のデータを用意するのは大変だけど、

  • ある程度集まったらとりあえず学習させる
  • 学習させたモデルを使って推論させてみる
  • 推論結果を検証することで学習データを増やし、再び学習させることで精度が上がる

というサイクルを続けることで、なんだかんだで自力で13000点ほどの分類済みのアイドル顔データを作ることができている。



現時点での最新の学習済み分類器での結果はたとえば

というかんじで、100クラス以上の分類数である中で Luce Twinkle Wink☆ のメンバー5人中4人くらいは一応判別できるようになっている。

課題

30枚くらい集まれば学習対象になって 推論結果にも出るからさらにデータを増やすのに使えるけれど、そもそもその30枚くらいまで集めるのが大変なわけで。
似たような特徴を持つ顔をクラスタリングしてまとめてラベル付けできたら良いのかな…?と思って、学習済みモデルの隠れ層の出力パターンが似たものとかで分類できないかと調べてみたのだけど ちょっと有意な傾向は把めなそうだった…。

あとは推論結果に対する検証なんかはアイドルに詳しいドルヲタの方々に手伝ってもらう形で集合知を利用して実現できれば良いのだけど、なんとも良いインタフェースが思い浮かばない。

余談

分類作業しているうちに、知らなかった子の顔もけっこう覚えるようになる。人間のラーニング能力もすごいな、って。

今後の展望

だんだん「アイドルの顔画像」が集まってきたので、今度はこれらを利用して"生成する"というのにもチャレンジしてみたいと思っている。

他にもなにか良いアイディアがあれば。現時点で作ったデータセットも何かに利用したい、という方がおりましたら提供の相談させていただきますのでお気軽に連絡ください。