はじめに
こんにちは、Hironsanです。
顔認識は画像中に映った人を検知し、人物の識別を行う技術です。顔認識の用途としては、監視カメラのシステムに組み込んでセキュリティ向上に役立てたり、ロボットに組み込んで家族の顔を認識させたりすることがあげられます。
今回はTensorFlowを使って畳み込みニューラルネットワークを構築し、既存のデータセットを使って顔認識器を作ってみます。
対象読者
- 畳み込みニューラルネットワーク(CNN)を知っている
- TensorFlowでどう書くかはわからない
CNNの理論については以下を見ればわかると思います。
準備
TensorFlowのインストール
TensorFlowのインストールは公式サイトが丁寧に解説しているのでそちらを参照してください。
データのダウンロード
まずはデータセットを用意します。今回は以下の顔画像データセットを使います。
このデータセットには40人分の画像が各10枚ずつ含まれています。各画像サイズは64x64でグレースケール画像です。
画像の読み込み
データセットを用意したら、画像を読み込みます。
PyFaceRecognizer/example/input_data.py
import input_data
dataset = input_data.read_data_sets('data/olivettifaces.mat')
ここで、datasetは学習データ、検証データ、テストデータを含んでいます。また、読み込んだ段階で画像サイズを32x32に縮小しています。
畳み込みニューラルネットワークの構築
畳み込みニューラルネットワーク(CNN)を用いて顔認識を行います。全体像としては以下のようになっています。
各層のconv, pool, fcはそれぞれ畳み込み層、プーリング層、全結合層を表しています。関数欄のReLは正規化線形関数を表しています。パラメータを表にすると以下のようになります。
層種・名称 | パッチ | ストライド | 出力マップサイズ | 関数 |
---|---|---|---|---|
data | - | - | 32 x 32 x 1 | - |
conv1 | 5 x 5 | 1 | 32 x 32 x 32 | ReL |
pool1 | 2 x 2 | 2 | 16 x 16 x 32 | - |
conv2 | 5 x 5 | 1 | 16 x 16 x 64 | ReL |
pool2 | 2 x 2 | 2 | 8 x 8 x 64 | - |
fc3 | - | - | 1 x 1 x 1024 | ReL |
fc4 | - | - | 1 x 1 x 40 | softmax |
コードで書いてあげると以下のようになります。ほぼそのままですね。
PyFaceRecognizer/example/run.py
def inference(input_placeholder, keep_prob):
W_conv1 = weight_variable([5, 5, 1, 32]) # 最初の2つはパッチサイズ。残りは入力チャネルと出力チャネルの数
b_conv1 = bias_variable([32])
x_image = tf.reshape(input_placeholder, [-1, 32, 32, 1]) # 第2、第3の次元は画像の幅と高さ、最後の次元はカラー・チャネルの数
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # たたみ込み
h_pool1 = max_pool_2x2(h_conv1) # maxプーリング
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
W_fc1 = weight_variable([8 * 8 * 64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 8 * 8 * 64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
W_fc2 = weight_variable([1024, 40])
b_fc2 = bias_variable([40])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2) # 出力そう
return y_conv
モデルの訓練と評価
inferenceにモデルのコードを書きました。次はモデルを訓練するためのコードを書いていきます。それがlossとtrainingです。lossではクロスエントロピーを計算し、trainingではAdamオプティマイザを使用してパラメータの更新を行っていきます。コードにすると以下の通りです。
def loss(output, supervisor_labels_placeholder):
cross_entropy = tf.reduce_mean(-tf.reduce_sum(supervisor_labels_placeholder * tf.log(output), reduction_indices=[1]))
return cross_entropy
def training(loss):
train_step = tf.train.AdamOptimizer(1e-4).minimize(loss)
return train_step
以上で定義した、inference, loss, trainingを用いて顔認識を行います。訓練プロセスの100反復ごとにログを出力します。テストするときはドロップアウトさせないように、keep_peobを1.0にしています。
with tf.Session() as sess:
output = inference(x, keep_prob)
loss = loss(output, y_)
training_op = training(loss)
init = tf.initialize_all_variables()
sess.run(init)
for step in range(1000):
batch = dataset.train.next_batch(40)
sess.run(training_op, feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
if step % 100 == 0:
print(sess.run(loss, feed_dict={x: batch[0], y_: batch[1], keep_prob: 1.0}))
correct_prediction = tf.equal(tf.argmax(output, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print('test accuracy %g' % accuracy.eval(feed_dict={x: dataset.test.images, y_: dataset.test.labels, keep_prob: 1.0}))
実行
実行結果は下のような感じです。クロスエントロピーが下がっている様子が確認できます。
9.8893
1.68918
0.602403
0.261183
0.0490791
0.0525591
0.0133087
0.0121071
0.00673524
0.00580989
ソースコード
以下のリポジトリからソースコードをダウンロードして動かすことができます。
おわりに
畳み込みニューラルネットワークを用いて既存の顔データセットに対して顔認識を行ってみました。
次はカメラからリアルタイムに取得した画像を使って、顔検知・顔認識をやってみたいと思います。