fine tuningで顔識別(福士蒼汰vs中川大志)をやってみた
はじめに
前回、fine tuningで味を占めたので、ちょっと同じようなパターンで変わり種の識別をいくつかやっていきたいと思います.
shirakonotempura.hatenablog.com
今回やること:『福士蒼汰』と『中川大志』の2クラス分類
早速ですが、今回は顔認識に挑戦したいと思います.対象は俳優福士蒼汰氏と中川大志氏.よく見たらもちろん違う顔なのですが、パッと見、見間違いかねないこの2人の顔識別を前回と同じくfine tuningを用いてやってみたいと思います.
顔データの収集にあたっては、以下記事を参考にして顔部分の切り出しを行いました.ありがとうございます.
私は、毎回google.colab.files.upload()
を使って、ファイルのアップロードをしているのですが、Google Driveをマウント(使えるように認識させるくらいの認識でOKです)しておくとGoogle Drive上のファイルを参照できるようになります.最初にいくつか設定が必要ですが、この方法の方が手間と時間を短縮できるようです.
用意した画像
集めた画像を吟味して、ちゃんと本人の顔だけを選択しました.結局手元に残ったのは以下の枚数.これを水増しして学習用データとしたいと思います.(もちろんテストデータは最後の確認用なので水増ししません)
福士さん(fukushi):訓練用133枚、検証用20枚、テスト用30枚
中川さん(nakagawa):訓練用107枚、検証用20枚、テスト用30枚
こんな感じのデータをたくさん作成します.(念のため、モザイクかけておきます.モザイク越しでもイケメンぶりが伝わってきますね・・)
上記のような画像が準備できましたら、以降はほぼ前回と同じことの繰り返しです.ただし、今回は2クラス分類である点が前回(4クラス分類)と異なりますので、該当する部分は若干変更する必要があります.
fine tuningによる画像識別
- フォルダ構成について
* 検討の方針:
* VGG16の学習済み結果をベースに、一部重みを変更するFine Tuningを行う.
* 次に、fit_generator()
を利用して、水増ししながら学習を行う
不必要なデータの削除は手作業となります.ちゃんとチェックしておかないと,たまに誰?みたいなおばあさんが保存されたりします.これは顔のトリミングはあくまで顔検出で行っているので,最初にダウンロードした写真が例えば舞台挨拶の写真だったり、テレビのキャプチャだったりした場合、下図のように写っている顔全てを切り出してしまいます.あっ、千眼さん・・.
フォーゼは顔として認識せず
VGG16モデルの構築およびコンパイル
主な手順としては、
* VGG16モデルと学習済みの重みをロード
* 今回の分類用の全結合層を構築
* VGG16とFC層を接続
* 学習させない層を定義
* モデルのコンパイル
今回は2クラス分類なので、model.compile()
で定義するロス関数はloss = binary_crossentropy
として2値識別用の評価関数を定義しています(多クラスの場合はcategorical_crossentropy).
また、最終層の活性化関数は2値分類で使用するシグモイド関数とし、Dense(1, activation = "sigmoid")
としています.(最初の引数は1です.カテゴリー数の2とは違いますので注意).
# 必要なライブラリのインポート from keras.models import Model from keras.layers import Dense, GlobalAveragePooling2D,Input from keras.applications.vgg16 import VGG16 from keras.preprocessing.image import ImageDataGenerator from keras.optimizers import SGD from keras.callbacks import CSVLogger # 今回は2カテゴリに分類する n_categories=2 batch_size=32 img_size = 200 # 画像データを保存しているディレクトリの定義 train_dir="images/train" validation_dir="images/valid" test_dir="images/test" # display_dirの中身はテストデータと同じもの.ただし、クラスごとのフォルダ分けはしていない display_dir='images/display' file_name='face_finetuning_vgg16' # VGG16の既存の全結合層は、1000クラス分類用なので使えない→Falseで削除 vgg16_model=VGG16(weights='imagenet',include_top=False, input_tensor=Input(shape=(img_size,img_size,3))) # 全結合層(FC層)を構築 x=vgg16_model.output x=GlobalAveragePooling2D()(x) x=Dense(1024,activation='relu')(x) prediction=Dense(1, activation='sigmoid')(x) # vgg16と全結合層をつなぐ model=Model(inputs=vgg16_model.input, outputs=prediction) # 最後のCONV層の直前までの層を更新しない(freeze) for layer in vgg16_model.layers[:15]: layer.trainable=False # fine-tuningにおいては、optimizerはSGDがいいらしい. model.compile(optimizer=SGD(lr=0.0001,momentum=0.9), loss='binary_crossentropy', # 2クラスの場合はbinary_crossentropy metrics=['accuracy']) model.summary() #save model json_string=model.to_json() open(file_name+'.json','w').write(json_string)
必要パラメータの定義
epochs
はエポックの数
num_trainingおよびnum_validation
:generatorで繰り返し作成する画像の枚数.
epochs = 50 num_training = 1600 num_validation = 400 num_of_test = 60 label = ["fukushi","nakagawa"]
fit_genratorを使って学習
ImageDataGenerator
で学習データの水増しを行います.train_generatorおよびvalidation_generatorで定義しているclass_modeをclass_mode = "binary"
としています.(多クラスの場合は"categorical"
train_datagen=ImageDataGenerator( rescale=1.0/255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True) validation_datagen=ImageDataGenerator( rescale=1.0/255 ) train_generator=train_datagen.flow_from_directory( train_dir, target_size=(img_size,img_size), color_mode = "rgb", batch_size=batch_size, class_mode='binary', # categoricalからbinaryに変更 shuffle=True ) validation_generator=validation_datagen.flow_from_directory( validation_dir, target_size=(img_size,img_size), color_mode = "rgb", batch_size=batch_size, class_mode='binary', shuffle=True ) hist=model.fit_generator(train_generator, steps_per_epoch = num_training//batch_size, epochs=epochs, verbose=1, validation_data=validation_generator, validation_steps = num_validation//batch_size, callbacks=[CSVLogger(file_name+'.csv')]) #save weights model.save(file_name+'.h5')
テストデータによる結果
学習が終われば、テストディレクトリに入っているデータ(合計60枚)を使って精度の確認を行います.
# テストデータによる評価 from keras.models import model_from_json import matplotlib.pyplot as plt import numpy as np import os,random from keras.preprocessing.image import img_to_array, load_img #load model and weights json_string=open(file_name+'.json').read() model=model_from_json(json_string) model.load_weights(file_name+'.h5') model.compile(optimizer=SGD(lr=0.0001,momentum=0.9), loss='binary_crossentropy', metrics=['accuracy']) #data generate test_datagen=ImageDataGenerator(rescale=1.0/255) test_generator=test_datagen.flow_from_directory( test_dir, target_size=(img_size,img_size), batch_size= num_of_test, class_mode='binary', shuffle=True ) #evaluate model score=model.evaluate_generator(test_generator, steps = 1) print('\n test loss:',score[0]) print('\n test_acc:',score[1]) # 以下は図で表示するためのスクリプト #predict model and display images nb_of_disp = 16 nb_of_row = np.sqrt(nb_of_disp) nb_of_col = np.sqrt(nb_of_disp) files=os.listdir(display_dir) img=random.sample(files,nb_of_disp) plt.figure(figsize=(10,10)) for i in range(nb_of_disp): temp_img=load_img(os.path.join(display_dir,img[i]),target_size=(img_size,img_size)) plt.subplot(nb_of_row, nb_of_col,i+1) plt.imshow(temp_img) #Images normalization temp_img_array=img_to_array(temp_img) temp_img_array=temp_img_array.astype('float32')/255.0 temp_img_array=temp_img_array.reshape((1,img_size,img_size,3)) #predict image img_pred=model.predict(temp_img_array) #img_pred が0.5以下ならfukushi, 0.5以上ならnakagawa if img_pred <= 0.5: plt.title(label[0]) else: plt.title(label[1]) #eliminate xticks,yticks plt.xticks([]),plt.yticks([]) plt.show()
気になる精度ですが、テストデータに対して識別精度87%となりました.2値識別なので決して高い制度ではないですが,得られたデータが少ないことを考えるとぼちぼちといったところでしょうか.おそらく30代・40代男性の程度の識別率には達したはずです.そもそも2人を知らないなんて人は新入社員と会話できるくらいには覚えておきましょう.
Found 60 images belonging to 2 classes. test loss: 0.34022757411003113 test_acc: 0.8666666746139526
まとめ
fine tuningを使って、福士蒼汰氏と中川大志氏の顔認識を行いました.
2クラス分類としては精度87%といまいちでしたが、個人的には満足です.おそらく私より精度いいですし.
参考とさせていただいたこちらの記事でやられている、ぼかし処理や閾値処理などをすればもう少し上がるかもしれませんが、とりあえずここまで.
Colaboratoryのノートはコチラにおいておきます.