infoMode

パソコンに疎い人のための情報セキュリティ発信

バンドリ・Poppin'Partyメンバーを機械学習で識別してみた

表題の通りこんな感じでPoppin'Partyメンバーを識別してみました。

f:id:rk12liv:20180418190422j:plain

※本記事のバンドリ画像の著作権は全て©Craft Egg Inc. ©bushiroad様に帰属します。

環境

主にpython (jupyter notebook)を利用。Windows 10です。主要ライブラリのバージョンは以下の通り:

  • TensorFlow 1.4.0
  • OpenCV 3.4.0

参考にしたブログ等

以下に、ステップ毎に参考にしたブログ等をリストアップします:

では、各ステップ毎の説明およびもういくつかの予測結果(失敗含)を紹介したいと思います。

画像の取得

前述の参考先に詳しいのでコードは省きます。Google Custom Search APIを設定し、検索エンジンからPoppin'Partyの各メンバー(戸山香澄、花園たえ、牛込りみ、山吹沙綾、市ヶ谷有咲。私はさーやが好き)の画像を集めまくります。

参考先にもある通り1回あたり100枚づつの画像しか取得できないため、もっと必要な方は工夫して何回かに分けても良いと思います。

ラベル付けに関してはそれぞれの画像を集める際のコードでファイルの名前にラベリングすることで後々とっても楽になります。

 

コードを回すと、こんな感じでコードが回り、画像を集めてくれます。なお、結構な割合でfailed to downloadするので何十人も判別したいなどといった場合はもう少し汗をかいて画像を集めます。今回はポピパの5人だけなので特に気にしていません。

f:id:rk12liv:20180418190436j:plain

なお参考先を回ると、色々と見たことのないライブラリをimportしています。それらはpip install ライブラリ名か、それでダメならライブラリ名 python インストールなどでググれば大抵インストールできます。先人たちに感謝。

 一通り画像を集め終わったところで、本当に欲しい画像かどうかをチェックしましょう。関係ない画像や学習を乱しそうな画像は目視で削除していきます。ラベル(ファイル名)も要注意です。

 

いざやってみると分かると思いますが、結構画像を集めるのに苦労します。ねんどろいどが混ざったり、コスプレが混ざったり、さーやと書いてあるのにおたえだったり。なので集まった500枚以上の画像を目視チェックして、本当に欲しい画像かどうかはきっちり確認する必要があります。Poppin'Partyに関しては特に苦でもありませんが。

このフィルタリングが終わったら、アニメ顔検出です。次のステップでフィルタリングを行ってもよいのですが、画像が小さくなった後のため少しやりづらいと思います。

画像のリサイズ・アニメ顔検出

ここでOpenCVが登場します。優秀な画像処理ライブラリで、できること・できないことについては以下のSlideがとても参考になります。

画像処理ライブラリ OpenCV で 出来ること・出来ないこと

基本的にはData Augmentation参考先のコードの通りで動くのですが、アニメ顔の検出ということで特徴ファイルを別のものにする必要があります。

具体的にはカスケード分類器を使う際のこの部分です。

 

image_gs = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cascade = cv2.CascadeClassifier(r"C:\xxxxx\Anaconda3\envs\tensorflow\Lib\site-packages\cv2\data\lbpcascade_animeface.xml")

 

これでアニメ顔を抽出できるようになります。ダウンロード元はこちらから:

GitHub - nagadomi/lbpcascade_animeface: A Face detector for anime/manga using OpenCV.

ここから同名ファイルをダウンロードし、上記コードから参照します。私はAnacondaのインストール先の都合上、保存先が通常と少し違うかもしれません。

 

正しく動けば、以下の通りポピパメンバーの顔がずらっと並びます。意外と公式じゃない画像とかも学習していたようです、気づきませんでしたが...

横顔や口を大きく開いた画像などはOpenCVで検出されなかったりするので、あまり極端な顔の角度の画像の判別には向いていません。横顔も検知したい!という場合は地道にそういった画像を集めていく他ないかもしれません。

f:id:rk12liv:20180418190452j:plain

また、Data Augmentation参考先のコードなどを使って、これらの画像を水増しします。画像の水増しにはいくつか方法があり、コントラストを変更したり、左右反転させたり、クロッピングしたり、画像を回転させるなどが挙げられます。

重要なのはテストデータでより「ありそうな」感じの処理を施すことのようです。同じ顔を複数の違う背景に重ね合わせて合成するなど背景で学習データ数を稼ぐことも可能なようです。

 

今回はそんな複雑なことはせず、反転、コントラスト変更及び回転程度で済ませました。具体的な方法はKaggleの画像認識コンペなどのKernelを見るとたくさん見つかりますので、自分のやりやすい方法を確立するのが良いと思います。

TensorFlowで畳み込みニューラルネットワーク

機械学習ステージです。非力なGeForce GTX1050しかなかったため、300 epoch程度の学習でも10分ぐらいかかってしまいました。しかもメモリも少ないので、バッチ数を小さくするなどして積極的にメモリを節約しています。

今回は前述のDeep MNISTのチュートリアルと同じCNNの構成で、サイズやチャンネル数などを変更して動かしました。

コードはチュートリアルのものとあまり変わりませんが、いくつか変更・苦労した点を。

画像データをTensorFlowに流し込む前に

私の場合はcv2.imreadで読み込んだ画像をこんな感じで処理していました。

img = cv2.resize(img, (IMG_SIZE, IMG_SIZE))

img = img.flatten().astype(np.float32)/255.0

IMG SIZEは画像サイズで、64x64に設定しました。が、強力なGPUがあればもっと大きくても良いと思います。

大きければ大きいほど、精度が上がるのかというと必ずしもそうではないようですが...

また、255で割って0から1の間に数字を収め、flatten()で1列にします。

 

画像サイズですが、畳み込みのstrideやwindowの大きさにも注意する必要があります。paddingは普通に’SAME’で良いと思います。

畳み込み層

# 1st Convolution Layer
W_conv1 = weight_variable([5,5,チャンネル数,32])
b_conv1 = bias_variable([32])
x_image = tf.reshape(x, [-1, IMG_SIZE, IMG_SIZE, チャンネル数])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1)+b_conv1)
h_pool1 = max_pool_2x2(h_conv1)

このあたりはチュートリアルから変えないともちろん動きません。

初期化の位置

sess.run(tf.global_variables_initializer())

これは全てのプレースホルダー・式を定義した後に持ってきました。

学習したモデルの保存・読み込み

毎回毎回ファンが最大速度で回るのもあれなので、学習が終わったら保存して予測の際は読み込むだけにします。

# モデルの保存
saver = tf.train.Saver()
saver.save(sess, './test_model')

尚、読み込みですがこれが結構曲者で、保存したモデルと同じのプレースホルダーや式をもう一度定義して、そのあとで以下のコードをrunすることで読み込めます。何もせずimport直後にrestoreしてもエラーが出ますので、ご注意を。

# モデルの読み込み
saver = tf.train.Saver()
saver.restore(sess, './test_model')

学習したモデルで、いざポピパメンバー識別

無事学習したモデルをrestoreできたら、予測に入ります。

流れとしては、画像を読み込み->アニメ顔検出->その周りを64x64にリサイズ・抽出->予測モデルへfeed->予測->openCVへreturn->クラス名を画像に書き込み、という具合です。

ここもData Augmentation参考先のブログがとても参考になります。但しカスケード分類器の特徴ファイルはlbpcascade_animeface.xmlにします。

またその他の注意として、openCVで処理する・されたデータはそのままTensorFlowへ流し込めないので、以下の一文を入れて形を整形してからfeedします。

img = np.expand_dims(img, axis=0)

ポピパメンバー識別結果

ということで、畳み込みニューラルネットワークを使ってポピパメンバーを予測してみました。

f:id:rk12liv:20180418190513j:plain

当然というか、教師データとたぶんほとんど同じ顔なので問題なく分類できています。

f:id:rk12liv:20180418190524j:plain

ゲーム画面でもバッチリですね。ポピパキって何だろう。

f:id:rk12liv:20180418190556j:plain

教師データに含まれていない画像ですが、正しく認識しています。

f:id:rk12liv:20180418190606j:plain

同じく教師データに含まれていません。さーやの鎖骨をおたえと認識しているのは・・・というかOpenCV先生のせいですね。

 

ちなみに失敗例としては以下があります。

とはいえ、この香澄は確かにちょっとりみりんっぽい

f:id:rk12liv:20180418190619j:plain

また、冒頭の画像も、教師データには含めず予測した結果です。

まとめ

学習データの少なさを考えるとそこそこ健闘しているのではないでしょうか。学習データにポピパメンバーではない画像なども与えていけば、より改善されていくと思います。

例えばpixivなどで別の絵師が書いた画像などでも正しくポピパのメンバーを分類できるのか、例えば全バンド含めた25人分類だと一人当たりどの程度の教師データが必要なのか等気になることは多数ありますが、今回はここまで。お疲れさまでした。