Kerasでアニメキャラの顔認識

  • 17
    いいね
  • 3
    コメント

はじめに

こちらの記事を参考にしました
続・深層学習でアニメ顔を分類する with Keras
学習モデルを作ってその正解率は確かめることはできたけど、僕がやりたかったことはネットで拾ってきたアニメキャラの画像を入力としてそれを判別することだったのでこの記事ではその辺も含めて書いています。

データセット

学習に使うデータセットはanimeface-character-datasetから入手しました。
圧縮ファイルのanimeface-character-datasetを解凍して、そのファルダの中はthumbだけにしておいてください。

データセット前処理

画像はopencvを使って32×32にリサイズし、すべての画像(14490枚)の行列をnumpyのsave()を使って別のファイルに保存していきます。
これで毎回画像を扱わずに済み実行速度も少し速くなります。
画像の正解ラベルも別のファイルに格納しておきましょう。

dataset_predisporsal.py
import os
import numpy as np
import cv2 as cv

data_dir_path = "./animeface-character-dataset/thumb/"
tmp = os.listdir(data_dir_path)
tmp=sorted([x for x in tmp if os.path.isdir(data_dir_path+x)])
dir_list = tmp

X_target=[]
for dir_name in dir_list:
    file_list = os.listdir(data_dir_path+dir_name)
    for file_name in file_list:
        if file_name.endswith('.png'):
            image_path=str(data_dir_path)+str(dir_name)+'/'+str(file_name)
            image = cv.imread(image_path)
            image = cv.resize(image, (32, 32))
            image = image.transpose(2,0,1)
            image = image/255.
            X_target.append(image)

anime_class=[]
count=0
for dir_name in dir_list:
    file_list = os.listdir(data_dir_path+dir_name)
    for file_name in file_list:
        if file_name.endswith('.png'):
            anime_class.append(count)
    count+=1

anime_arr2=np.array(anime_class)
np.save('anime_face_target.npy',anime_arr2)
anime_arr=np.array(X_target)
np.save('anime_face_data.npy',anime_arr)

anime_face_data.npyには画像の行列(14490,3,28,28)
anime_face_target.npyには正解ラベル(14490,)
が保存されます。

Kerasによるモデル構築と学習

前処理で作成した2つのnpyファイルを読み込んで学習させます。

anime_face.py
import numpy as np
np.random.seed(20160715) # シード値を固定
from keras.layers.convolutional import Convolution2D
from keras.layers.core import Activation
from keras.layers.core import Dense
from keras.models import Sequential
from keras.callbacks import EarlyStopping
from keras.callbacks import LearningRateScheduler
from keras.optimizers import Adam
from keras.optimizers import SGD
import sklearn.cross_validation

X_test=np.load('anime_face_data.npy')
Y_target=np.load('anime_face_target.npy')

a_train, a_test, b_train, b_test = sklearn.cross_validation.train_test_split(X_test,Y_target)

model = Sequential()

model.add(Convolution2D(96, 3, 3, border_mode='same', input_shape=(3, 32, 32)))
model.add(Activation('relu'))

model.add(Convolution2D(128, 3, 3))
model.add(Activation('relu'))
model.add(Dropout(0.5))

model.add(Flatten())
model.add(Dense(1024))
model.add(Activation('relu'))
model.add(Dropout(0.5))

model.add(Dense(203))
model.add(Activation('softmax'))

init_learning_rate = 1e-2
opt = SGD(lr=init_learning_rate, decay=0.0, momentum=0.9, nesterov=False)
model.compile(loss='sparse_categorical_crossentropy', optimizer=opt, metrics=["acc"])
early_stopping = EarlyStopping(monitor='val_loss', patience=3, verbose=0, mode='auto')
lrs = LearningRateScheduler(0.01)

hist = model.fit(a_train,b_train, 
                batch_size=128, 
                nb_epoch=50, 
                validation_split=0.1, 
                verbose=1)

model_json_str = model.to_json()
open('anime_face_model.json', 'w').write(model_json_str)
model.save_weights('anime_face_model.h5')

score=model.evaluate(a_test, b_test, verbose=0)
print(score[1])

結果は約55%の正解率でした。
正解率を上げる方法については今回は触れません。

model_json_str = model.to_json()
open('anime_face_model.json', 'w').write(model_json_str)
model.save_weights('anime_face_model.h5')

この部分が重要で学習モデルと学習結果をanime_face_model.jsonとnime_face_model.h5にそれぞれ保存します。
これで学習モデルと学習結果を使い回すことができるようになりました。
実行する時も一瞬です。
さて次が本題です。

学習結果を使ってオリジナル画像を判別する

今回使用するオリジナル画像は魔法少女リリカルなのはの「八神はやて」(yagami.png)です

yagami.png

load_anime_face.py
import numpy as np
from keras.models import model_from_json
from keras.utils import np_utils
from keras.optimizers import SGD
import sklearn.cross_validation
import cv2 as cv
np.random.seed(20160717)

X_test=np.load('anime_face_data.npy')
Y_target=np.load('anime_face_target.npy')

model = model_from_json(open('anime_face_model.json').read())
model.load_weights('anime_face_model.h5')
init_learning_rate = 1e-2
opt = SGD(lr=init_learning_rate, decay=0.0, momentum=0.9, nesterov=False)
model.compile(loss='sparse_categorical_crossentropy', optimizer=opt, metrics=["acc"])

image = cv.imread('yagami.png')
image = cv.resize(image, (32, 32))
image = image.transpose(2,0,1)
image = image/255.

image=image.reshape(1,3,32,32)

for i in range(202):
    sample_target=np.array([i])
    score = model.evaluate(image, sample_target, verbose=0)
    if score[1]==1.0:
        break
print(i)

39と出力されました。
この数字はthumbの中にあるファイル名の先頭の数字になります。

スクリーンショット 2016-10-18 13.51.55.png

したがって39番の八神はやてを正解することができたということになります。
しかし正解率は55%なので、不正解であることもかなり多いです。

おわりに

特に解説もせず淡々とコードと処理内容を書いてきましたが、これでオリジナル画像を入力としてキャラを判別することができるようになりました。
正解率だけを見てもあまり実感が湧かないので、このように1枚の画像を入力として結果が返ってくると僕みたいな機械学習の初心者からするとやったぜという気持ちになります。
何か疑問や突っ込みがあればコメントしてもらえると嬉しいです。

簡単ですが以上になります。