ブログ名

顔認証AI:学習用の実装 後編

はじめに

こんにちは! 「顔認証AI:学習用の実装 後編」 を開いていただきありがとうございます!

今回作るもの

前編で紹介した通り、今回は前回の続きの実装をやっていきます。

  1. 画像データの水増し
  2. 増やした画像を学習データとテストデータに振り分け
  3. 機械学習の下準備
  4. 機械学習

実装

import os
import cv2
import glob
from scipy import ndimage
from posix import listdir
import shutil
import random
import numpy as np
from tensorflow.keras.utils import to_categorical

from tensorflow.keras.layers import Input, Dense
from tensorflow.keras.layers import Activation, Conv2D, Dense, Flatten, MaxPooling2D
from tensorflow.keras.models import Sequential

# 加工前の画像ディレクトリのパス
original_dir_path = "./faces/original/"
# 加工後の画像ディレクトリのパス
train_dir_path = "./faces/train/"
# テストディレクトリのパス
test_dir_path = "./faces/test/"

Names = os.listdir(train_dir_path)
input_shape=(250,250,3)

# 教師データのラベル付け
name_count = 0
X_train = []
Y_train = []
for name in Names:
    # DS.Storeを取ってきたらcontinue
    if name == ".DS_Store":
        continue
    img_file_name_list=os.listdir(train_dir_path + name)
    print(name + ":学習用の画像の枚数は" + len(img_file_name_list) + "です。")
    # 画像読み込み
    for img_name in img_file_name_list:
        img = cv2.imread(train_dir_path)
        # 画像データない場合別の画像読み込みへ
        if img is None:
                print('image' + str(name_count) + ':NoImage')
                continue
        else:
            # 学習データを格納してラベル付け
            r,g,b = cv2.split(img)
            img = cv2.merge([r,g,b])
            X_train.append(img)
            Y_train.append(name_count)
    name_count += 1

# テストデータのラベル付け
name_count = 0
X_test = [] # 画像データ読み込み
Y_test = [] # ラベル(名前)
for name in Names:
    # DS.Storeを取ってきたらcontinue
    if name == ".DS_Store":
        continue
    img_file_name_list=os.listdir(test_dir_path + name)
    print(name + ":学習用の画像の枚数は" + len(img_file_name_list) + "です。")
    # 画像読み込み
    for img_name in img_file_name_list:
        img = cv2.imread(test_dir_path)
        # 画像データない場合別の画像読み込みへ
        if img is None:
                print(name + ':NoImage')
                continue
        else:
            # 学習データを格納してラベル付け
            r,g,b = cv2.split(img)
            img = cv2.merge([r,g,b])
            X_test.append(img)
            Y_test.append(name_count)
    name_count += 1

# 下準備
x_train=np.array(X_train)
x_test=np.array(X_test)
y_train = to_categorical(Y_train)
y_test = to_categorical(Y_test)

# モデルの定義
model = Sequential()
model.add(Conv2D(input_shape=input_shape, filters=32,kernel_size=(3, 3),
                 strides=(1, 1), padding="same"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(filters=32, kernel_size=(3, 3),
                 strides=(1, 1), padding="same"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(filters=32, kernel_size=(3, 3),
                 strides=(1, 1), padding="same"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(256))
model.add(Activation("relu"))
model.add(Dense(128))
model.add(Activation('relu'))
model.add(Dense(len(Names)))
model.add(Activation('softmax'))

# モデルを読み込む
model.compile(optimizer='sgd',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# データを使って学習開始
history = model.fit(x_train, y_train, batch_size=70,
                    epochs=350, verbose=1, validation_data=(x_test, y_test))

# 精度の確認
score = model.evaluate(x_test, y_test, batch_size=32, verbose=0)
print('validation loss:{0[0]}\nvalidation accuracy:{0[1]}'.format(score))

import matplotlib.pyplot as plt
#acc, val_accのプロット
plt.plot(history.history["acc"], label="acc", ls="-", marker="o")
plt.plot(history.history["val_acc"], label="val_acc", ls="-", marker="x")
plt.ylabel("accuracy")
plt.xlabel("epoch")
plt.legend(loc="best")
plt.show()

#モデルを保存
model.save("Model.h5")

実装の解説

それでは解説していきます。

各種定義

import os
import cv2
import glob
from scipy import ndimage
from posix import listdir
import shutil
import random
import numpy as np
from tensorflow.keras.utils import to_categorical

from tensorflow.keras.layers import Input, Dense
from tensorflow.keras.layers import Activation, Conv2D, Dense, Flatten, MaxPooling2D
from tensorflow.keras.models import Sequential

# 加工前の画像ディレクトリのパス
original_dir_path = "./faces/original/"
# 加工後の画像ディレクトリのパス
train_dir_path = "./faces/train/"
# テストディレクトリのパス
test_dir_path = "./faces/test/"

Names = os.listdir(train_dir_path)
input_shape=(250,250,3)

各種インポートと定義。Namesに学習する人物の名前を入れていること以外は前回と同じなので解説は省略します。

教師データの紐付け

# 教師データのラベル付け
name_count = 0
X_train = []# 画像データ読み込み
Y_train = []# ラベル(名前)
for name in Names:
    # DS.Storeを取ってきたらcontinue
    if name == ".DS_Store":
        continue
    img_file_name_list=os.listdir(train_dir_path + name)
    print(name + ":学習用の画像の枚数は" + len(img_file_name_list) + "です。")
    # 画像読み込み
    for img_name in img_file_name_list:
        img = cv2.imread(train_dir_path)
        # 画像データない場合別の画像読み込みへ
        if img is None:
                print('image' + str(name_count) + ':NoImage')
                continue
        else:
            # 学習データを格納してラベル付け
            r,g,b = cv2.split(img)
            img = cv2.merge([r,g,b])
            X_train.append(img)
            Y_train.append(name_count)
    name_count += 1

「顔認証AIブログ第4回:機械学習の概要」 でも少し触れましたが、今回行う学習は「教師あり学習」になります。教師あり学習はデータと答えがセットになっているものなので、本記事の学習でもデータと答えを紐づけるという作業が必要になります。そして、それを行なっているのが↑この部分になります。

この答えというのは一般的にラベルと呼ばれたりします。まずはX_trainY_train にそれぞれ画像データとラベルを入れていきましょう。

紐付けといってもイメージが湧きづらいと思うので例を出すと、
例:)
Aさん、Bさん、Cさん、計3人の画像データが保存されている場合、Aさんを識別する番号として0、Bさんを識別する番号として1、Cさんには2といったように固有の番号が割り当てられます。この数字がラベルになります。
そして、学習データ格納用のX_trainにデータが入った時、同時にラベル格納用のY_trainにラベルが入ります。 例えばX_trainにAさんのデータが入るとY_trainに0が、X_trainにCさんのデータが入るとY_trainに2が入るといった感じです。
これによってX_trainの中のデータが誰の画像かわからない時に、Y_trainを見れば識別することができるというわけです。

テストデータの紐付け

# テストデータのラベル付け
name_count = 0
X_test = [] # 画像データ読み込み
Y_test = [] # ラベル(名前)
for name in Names:
    # DS.Storeを取ってきたらcontinue
    if name == ".DS_Store":
        continue
    img_file_name_list=os.listdir(test_dir_path + name)
    print(name + ":学習用の画像の枚数は" + len(img_file_name_list) + "です。")
    # 画像読み込み
    for img_name in img_file_name_list:
        img = cv2.imread(test_dir_path)
        # 画像データない場合別の画像読み込みへ
        if img is None:
                print(name + ':NoImage')
                continue
        else:
            # 学習データを格納してラベル付け
            r,g,b = cv2.split(img)
            img = cv2.merge([r,g,b])
            X_test.append(img)
            Y_test.append(name_count)
    name_count += 1

↑ここではテストデータの紐付けを行なっています。やっていることは教師データの紐付けと同じなので解説は省略します。

機械学習

# 下準備
x_train=np.array(X_train)
x_test=np.array(X_test)
y_train = to_categorical(Y_train)
y_test = to_categorical(Y_test)

# モデルの定義
model = Sequential()
model.add(Conv2D(input_shape=input_shape, filters=32,kernel_size=(3, 3),
                 strides=(1, 1), padding="same"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(filters=32, kernel_size=(3, 3),
                 strides=(1, 1), padding="same"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(filters=32, kernel_size=(3, 3),
                 strides=(1, 1), padding="same"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(256))
model.add(Activation("relu"))
model.add(Dense(128))
model.add(Activation('relu'))
model.add(Dense(len(Names)))
model.add(Activation('softmax'))

# モデルを読み込む
model.compile(optimizer='sgd',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# データを使って学習開始
history = model.fit(X_train, y_train, batch_size=70,
                    epochs=350, verbose=1, validation_data=(X_test, y_test))

↑次に今回のブログの一番の肝の部分を解説していきます。

# 下準備
x_train=np.array(X_train)
x_test=np.array(X_test)
y_train = to_categorical(Y_train)
y_test = to_categorical(Y_test)

↑まずは下準備です。先ほど作ったX_train,Y_train,X_test,Y_testを決まった形に変更していきます。X_train,X_testはとりあえず配列へ、Y_train,Y_testは少し特殊な形に変えていきます。
先ほどの例であげたAさん、Bさん、Cさんはそれぞれ0,1,2というラベルを持っていたと思います。それがY_train,Y_testに入っていたわけですね。そこで to_categorical()
Y_train,Y_testに使用することで0,1,2という値を、それぞれ[1, 0, 0],[0, 1, 0],[0, 0, 1]という具合に表すことができます。

# モデルの定義
model = Sequential()
model.add(Conv2D(input_shape=input_shape, filters=32,kernel_size=(3, 3),
                 strides=(1, 1), padding="same"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(filters=32, kernel_size=(3, 3),
                 strides=(1, 1), padding="same"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(filters=32, kernel_size=(3, 3),
                 strides=(1, 1), padding="same"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(256))
model.add(Activation("relu"))
model.add(Dense(128))
model.add(Activation('relu'))
model.add(Dense(len(Names)))
model.add(Activation('softmax'))

↑ここでは機械学習を行うモデルを作成していきます。以前にも解説した機械学習の概要のように、層を形成してデータをどのように学習させたいかを決めていきます。

model = Sequential()
model.add(Conv2D(input_shape=input_shape, filters=32,kernel_size=(3, 3),
                 strides=(1, 1), padding="same"))

まず、Sequential()で層の最初を決定します。次に様々な層をどんどん積み重ねていきます。層を加えるのはmodel.add()、2次元の畳み込み層の形成はConv2D()で行なっていきます。

2次元の畳み込み層の形成には細かい指定をすることができます。

  • filters
    畳み込みにおける出力フィルタの数

  • kernel_size
    畳み込みを行う大きさ

  • padding

そして最初の層は入力層なので、input_shapeという引数に入力される画像の形を教えておきます。

model.add(MaxPooling2D(pool_size=(2, 2)))

ここではMaxpoolingを使用して2x2でプーリング処理を行います。
ここまでの層を3回に分けて作成していきます。

f:id:shizuuuka0202:20200114232035p:plain ↑イメージ的にはこんな感じ。

model.add(Flatten())

↑次に作った層をFlatten()によって平坦化します。平坦化とは2次元配列を1次元配列にするイメージです。この実装では出力フィルターが32個ある層を3つ作ったので、32x32x32で32768ですね。

model.add(Dense(256))

↑Dense()では前の層のフィルターを引数の256個のフィルターに全結合します。

model.add(Activation("relu"))

↑次にActivation()を使って活性化関数を実行します。活性化関数とは層の出力に関数を与えて、出力される値を操作するものです。種類は複数あり、その目的によって使用される関数が分けられます。ここではRelu関数を使用します。

これらの層をくみあわせ、

model.add(Flatten())
model.add(Dense(256))
model.add(Activation("relu"))
model.add(Dense(128))
model.add(Activation('relu'))
model.add(Dense(len(Names)))
model.add(Activation('softmax'))

図にすると、 f:id:shizuuuka0202:20200114232144p:plain このようになります。

# モデルを読み込む
model.compile(optimizer='sgd',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

次に作ったモデルをコンパイルします。

# データを使って学習開始
history = model.fit(x_train, y_train, batch_size=70,
                    epochs=350, verbose=1, validation_data=(x_test, y_test))

いよいよ先ほどラベルづけされたデータを与えて学習を開始していきます。

# 精度の確認
score = model.evaluate(x_test, y_test, batch_size=32, verbose=0)
print('validation loss:{0[0]}\nvalidation accuracy:{0[1]}'.format(score))

今度はテストデータを使用して精度を測っていきます。

import matplotlib.pyplot as plt
#acc, val_accのプロット
plt.plot(history.history["acc"], label="acc", ls="-", marker="o")
plt.plot(history.history["val_acc"], label="val_acc", ls="-", marker="x")
plt.ylabel("accuracy")
plt.xlabel("epoch")
plt.legend(loc="best")
plt.show()

これによって学習データがグラフで表示され、結果を確認することができます。

最後に

次回は最初に作った顔データ収集用のコードを使って、学習させていきたいと思います!


前の記事へ 目次に戻る