ディープラーニングで美人女優の顔検出に挑戦

美人女優で学習モデルを作成

こんにちは。

AI coordinatorの清水秀樹です。

以前紹介したcifar10を使った学習モデルのモノマネで、自分で準備した画像から学習モデルを作成することに挑戦したので、その内容を紹介します。

画像集めから顔を識別するところまでの内容を紹介しますので、自分で準備した画像で学習モデルを作成してみたい方はぜひ参考にしてみてください。

 

参考にした書籍

これからDeep Learningの勉強をするなら、こちらで紹介する書籍が参考になります。

 

テスト環境

macOS Sierra

Anaconda3-4.2.0-MacOSX-x86_64

python 3.5.2

opencv3 3.1.0

tensorflow-0.12.1

keras 1.2.2

 

まずは画像集めから

これが一番大変かもしれません。

今回は美人女優の顔を識別できる学習モデルの作成にチャレンジしたいので、どうやって画像を大量に集めるか考える必要があります。

 

手っ取り早い方法はネットから画像検索すれば良いのですが、いちいち一枚づつ保存するのは非常に面倒かつ非効率です。

こういう時にpythonでスクレイピングができる方は羨ましいですね。

筆者は頑張ってスクレピングに挑戦しましたが、上手くダウンロードできず諦めました。

今回は学習モデルの作成と顔検出が目的ですので、スクレイピングについてはいずれまた挑戦したいと思います。

 

画像集めについてはfirefoxのプラグイン「FlashGot Mass Downloader 」が便利です。

プラグインのインストール方法とプラグインを使った画像のダウンロード方法の説明はここでは割愛します。

ググって頂ければすぐにわかると思います。

 

今回はこのfirefoxのプラグインで画像を集めました。

集めたサイトはgoogleの画像検索からです。

今回集めた画像は、剛力彩芽さん、佐々木希さん、上戸彩さん、新垣結衣さん、渡辺麻友さんの計5名の画像です。

どうせ画像を集めるなら美人さんが良いと思いまして・・・・

ただ、こうやって画像を並べてみるとみんな似たような顔してますね。

そう思うのは筆者だけかもしれませんが。

 

画像が似ているのでマイクタイソンやボブサップの画像も混ぜ込もうと思いましたが、機械学習こそ似た画像をしっかり識別できるはず!と判断し、あえて明らかに顔の形が違う人は含めず、上記5名で学習モデルを作成することにしてみました。

 

顔部分のトリミング

画像を集めたら、顔部分のトリミングを行います。

大量画像からの顔部分のトリミングについてはこちらの記事を参考にしてみてください。

この記事ではOoenCVの顔検出でトリミングを行いますが、顔以外もトリミングされてきてしまいますので、不要な画像は後から人力で削除する必要があります。

 

これも結構大変。

頑張ってやりましょう。

 

トリミング後の顔画像は少なくとも100以上あると良いでしょう。

画像は多いに越したことはありませんから。

 

画像の増幅と数値データへの変換

画像の増幅といっても、ただ無駄に同じ画像を増幅するわけではありません。

画像の角度を変更したり、反転させたりしてデータの数を増やします。

そうすることでディープラーニングする際のインプット画像を増やし、学習モデルの精度を上げることができるようになります。

 

また、機械学習させるためには画像データを数値データへ変換しておく必要があります。

cifar10も画像データの集まりというよりは、画像データを数値データへ変換したデータセットになっています。

画像データのままでは機械学習に使えませんので、画像を数値データへ変換しましょう。

 

以下のソースコードは画像を増やしつつ、数値データへの変換もやってくれます。

ソースコードを実行する前に、保存したファイルのディレクトリ構造をソースにあった形にしておく必要があります。

人物ごとの画像ファイルを保存するディレクトリは以下の画像を参考にしてください。

photo_outフォルダの配下に人物ごとのフォルダを作り、その人物ごとのフォルダの中に大量の顔画像があるディレクトリになっています。

from PIL import Image
import os, glob
import numpy as np
import random, math

# 画像が保存されているディレクトリパスの指定
root_dir = "./photo_out/"
# 画像が保存されているフォルダ名を順番に指定してください。
categories = ["剛力彩芽", "佐々木希", "上戸彩", "新垣結衣","渡辺麻友"]
image_size = 50

# 画像データを読み込む
X = [] # 画像データ
Y = [] # ラベルデータ
def add_sample(cat, fname, is_train):
    img = Image.open(fname)
    img = img.convert("RGB") # カラーモードの変更
    img = img.resize((image_size, image_size)) # 画像サイズの変更
    data = np.asarray(img)
    X.append(data)
    Y.append(cat)
    if not is_train: return
    # 角度を変えたデータを追加
    # 少しずつ回転する
    for ang in range(-20, 20, 5):
        img2 = img.rotate(ang)
        data = np.asarray(img2)
        X.append(data)
        Y.append(cat)
        # 反転する
        img2 = img2.transpose(Image.FLIP_LEFT_RIGHT)
        data = np.asarray(img2)
        X.append(data)
        Y.append(cat)

def make_sample(files, is_train):
    global X, Y
    X = []; Y = []
    for cat, fname in files:
        add_sample(cat, fname, is_train)
    return np.array(X), np.array(Y)

# ディレクトリごとに分けられたファイルを収集する
allfiles = []
for idx, cat in enumerate(categories):
    image_dir = root_dir + "/" + cat
    files = glob.glob(image_dir + "/*.jpg")
    for f in files:
        allfiles.append((idx, f))

# シャッフルして学習データとテストデータに分ける
random.shuffle(allfiles)
th = math.floor(len(allfiles) * 0.6)
train = allfiles[0:th]
test  = allfiles[th:]
X_train, y_train = make_sample(train, True)
X_test, y_test = make_sample(test, False)
xy = (X_train, X_test, y_train, y_test)
np.save("./idol_makedata/idol.npy", xy)
print("ok,", len(y_train))

 

無事に正常終了すれば、画像データを数値データに変換したデータセットが出来上がります。

この場合は「idol.npy」というファイル名で保存されます。

 

学習モデルの作成

いよいよ学習モデルの作成です。

畳み込みニューラルネットワークを使った学習ですね。

学習モデルの定義や学習させる回数はお好みで変更してください。

from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.utils import np_utils
import numpy as np

# 分類対象のカテゴリ
root_dir = "./photo_out/"
categories = ["剛力彩芽", "佐々木希", "上戸彩", "新垣結衣","渡辺麻友"]
nb_classes = len(categories)
#image_size = 50

# データをロード --- (※1)
def main():
    X_train, X_test, y_train, y_test = np.load("./idol_makedata/idol.npy")
    # データを正規化する
    X_train = X_train.astype("float") / 256
    X_test  = X_test.astype("float")  / 256
    y_train = np_utils.to_categorical(y_train, nb_classes)
    y_test  = np_utils.to_categorical(y_test, nb_classes)
    # モデルを訓練し評価する    
    model = model_train(X_train, y_train)
    model_eval(model, X_test, y_test)

# モデルを構築 --- (※2)
def build_model(in_shape):
    model = Sequential()
    
    print(in_shape)
    #畳み込み層の作成
    #1層目の追加  1024個の層を最初に作り、フィルター3*3のフィルターを16個作成
    model.add(Convolution2D(16, 3, 3, border_mode="same", input_shape=in_shape)) 
    model.add(Activation("relu"))
    
    #2層目の畳み込み層
    model.add(Convolution2D(16, 3, 3, border_mode="same"))
    model.add(Activation("relu"))
    
     #プーリング層
    model.add(MaxPooling2D())
    
    #Dropoutとは過学習を防ぐためのもの 0.5は次のニューロンへのパスをランダムに半分にするという意味
    model.add(Dropout(0.5))
    
    #3層目の作成
    model.add(Convolution2D(32, 3, 3, border_mode="same"))
    model.add(Activation("relu"))
    
    #4層目の作成
    model.add(Convolution2D(32, 3, 3, border_mode="same"))
    model.add(Activation("relu"))
    
     #プーリング層
    model.add(MaxPooling2D())
    model.add(Dropout(0.5))
    
    #5層目
    model.add(Convolution2D(64, 3, 3, border_mode="same"))
    model.add(Activation("relu"))
    
    #6層目
    model.add(Convolution2D(64, 3, 3, border_mode="same"))
    model.add(Activation("relu"))
    
    #プーリング層
    model.add(MaxPooling2D())
    
    #Dropout
    model.add(Dropout(0.5))
    
    #7層目
    model.add(Convolution2D(128, 3, 3, border_mode="same"))
    model.add(Activation("relu"))
    
    #Dropout
    model.add(Dropout(0.5))
    
    #平坦化
    model.add(Flatten())
    
    #8層目 全結合層 FC
    model.add(Dense(100))
    model.add(Activation("relu"))
    
    #Dropout
    model.add(Dropout(0.5))
    
    #8層目 引数nub_classesとは分類の数を定義する。
    model.add(Dense(nb_classes))
    model.add(Activation('softmax'))
    
    #ここまででモデルの層完成
    
    #lossは損失関数を定義するところ
    model.compile(loss="categorical_crossentropy", 
        metrics   = ["accuracy"], 
        optimizer = "adam"
    )
    return model


# モデルを訓練する --- (※3)
def model_train(X, y):
    model = build_model(X.shape[1:])
    model.fit(X, y, batch_size=50, nb_epoch=100)
    
    #学習モデルの保存
    json_string = model.to_json()
     #モデルのファイル名 拡張子.json
    open('./idol_makedata/idol.json', 'w').write(json_string)
    
    # モデルを保存する --- (※4)
    hdf5_file = "./idol_makedata/idol-model.hdf5"
    model.save_weights(hdf5_file)
    return model

# モデルを評価する --- (※5)
def model_eval(model, X, y):
    score = model.evaluate(X, y)
    print('loss=', score[0])
    print('accuracy=', score[1])

if __name__ == "__main__":
    main()

結果は、

う〜ムゥ。

78%とは低いですね。

学習モデルをもう少し工夫する必要があるのかもしれません。

とりあえず今回はこれで良しとします。

 

画像の判定

学習モデルの作成が完了したら、早速画像の判定をやってみましょう。

適当に準備した画像から顔の識別をやってみたいと思います。

今回使った画像は以下の画像たちです。

ファイル名に女優名を付加することで、結果の確認を分かりやすくしました。

# coding:utf-8
import keras
import sys, os
import scipy
import scipy.misc
import numpy as np
from keras.models import model_from_json

import json
imsize = (50, 50)

#認識したい画像のパスを指定する
# ./blog_testpic/xxx.jpg といった指定を意味する
testpic = "./idol_makedata/testdata/"
#使用するモデルを指定する
keras_model = "./idol_makedata/idol.json"
keras_param = "./idol_makedata/idol-model.hdf5"

#画像の読み込み
#読み込んだ画像を32X32にする
def load_image(path):
    img = scipy.misc.imread(path, mode="RGB")
    img = scipy.misc.imresize(img, imsize)
    img = img / 255.0 #RGBの最大値を指定
    return img

#リストで結果を返す関数
def get_file(dir_path):
    filenames = os.listdir(dir_path)
    return filenames

#メイン開始
if __name__ == "__main__":

    #画像を読み込んで、ファイル名をリスト化する。
    pic = get_file(testpic)
    print(pic)
    
    #モデルの読み込み
    model = model_from_json(open(keras_model).read())
    model.load_weights(keras_param)
    model.summary()
    ##ここまでで実行するとモデルの形が結果に表示される
    
    #リスト化したファイルから読み込んで処理する
    for i in pic:
        print(i) # ファイル名の出力
        
        #画像ディレクトリにあるファイルのi番目を読み込む
        img = load_image(testpic + i)
        
        prd = model.predict(np.array([img]))
        print(prd)
        
        #確信度最大値を取得する
        prelabel = np.argmax(prd, axis=1)
        print(prelabel)
        
        # 各画像ファイルにairplaneなら0が、dogなら5、shipなら8のラベルが付いている
        if prelabel == 0:
            print(">>> 剛力彩芽")
        elif prelabel == 1:
            print(">>> 佐々木希")
        elif prelabel == 2:
            print(">>> 上戸彩")
        elif prelabel == 3:
            print(">>> 新垣結衣")
        elif prelabel == 4:
            print(">>> 渡辺麻友")

        print()
        print()

実行結果はこうなりました。

結果は、

ファイル名、確信度、学習済みモデルの結果

を表示するようにしています。

 

 

一部の女優を間違えていますね。

だいたい78%ぐらいの正答率ですね。

精度を上げるにはもう少し画像データを準備する必要があるのかもしれません。

今回は一人当たり150枚程度でしたから。

 

 

自分で集めた画像からの顔検出でした。

今後はSSDにもチャレンジしていきたいと思います。

 


その他の物体検出記事はこちらから

 

それではまた!

あなたにオススメの記事

コメント

  1. この記事へのコメントはありません。

  1. 2018年 11月 25日
PAGE TOP