ニートがプログラミングするブログ(はてな出張所)

ニートがプログラミングするブログです。今は主にコンピュータビジョンに関することをやっています。

ディープラーニングを使わない顔認識3 CNN編

「ゼロから作るDeepLearning」を読んで畳み込みニューラルネットワーク(CNN)を実装したので顔認識で試してみました。
この本自体もgithubにあるソースコードも読みやすいのでお勧めの一冊です。
(私は、amazonでは品切れだったので、yodobashi.comで買いました。yodobashi.comには今も在庫があるようです。)
今回はCNNを使っていますが、3回しか畳み込み+プーリングを行わなわず、そこまでディープではないので、ディープラーニングを使わないというタイトルにしました。
その結果sugyanさんのアイドルデータセットのcase5で98%超の精度が出ました。
また、トレーニング全体が終わるまでに1日程度かかりました。
以下に方法を書いていきます。

1.データの水増し data augmentation
2.顔検出 face detection
3.顔の特徴点検出 facial landmarks detection
4.顔の位置合わせ face alignment
以上の4つのステップは以前の記事とほぼ同じです。
ただし、データの水増しは左右反転のみです。
画像サイズは約200x200です。

5.パッチの切り取り crop patches
ステップ4で検出した各特徴点のうち顔の輪郭部分は使わず、それ以外の部分から15箇所を適当に選びます。
それらの点を中心として60x60のパッチを切り取り40x40に縮小します。

6.特徴量の抽出 feature extraction
特徴量の抽出にはCNNを使いました。
CNNには様々なネットワーク構造が考えられますが、DeepID2+に近い方法を取りました。
DeepIDシリーズにはDeepID[1]、DeepID2[2]、DeepID2+[3]、DeepID3[4]などの方法があります。
どのDeepIDでも基本的にはネットワーク構造は同じです。
(DeepID3は他3つのネットワーク構造をさらに深くしたものです。)
今回使ったネットワークの基本形は、input->conv->relu->pool->conv->relu->pool->conv->relu->pool->affine->relu->affine->softmaxwithloss、です。
ちなみにaffineは全結合層のことです。
DeepIDシリーズでは4回畳み込みを行い、4回目の畳み込みの出力と3回目のプーリングの出力を隠れ層につなぐ、という方法を取っています。

f:id:suzuichiblog:20161031220725j:plain


[1]の図2
しかしそれを実装するのは少し面倒なので上記のような一般的なCNNの構造にしました。
一般的なCNNでは最終層からのみ教師信号を送りますが、DeepID2+では途中からも教師信号を送ります。
上の構造では最後のプーリング層の後に隠れ層・出力層があり、そこから教師信号を送っています。
これを、全てのプーリング層の後に隠れ層・出力層を追加して、そこからも教師信号を送ります。
図にすると次のようになります。

f:id:suzuichiblog:20161031220744j:plain


[4]の図2
さらにDeepID2+ではidentificationの教師信号だけでなく、verificationの教師信号も送ります。
identificationとは入力された顔の画像が誰であるかを答えるものです。
入力画像が、その人に対応するノードの出力が1に、それ以外の出力が0に近づくように学習します。
一方でverificationは入力された二つの顔画像が同一人物であるかを答えるというものです。
二枚の画像の類似度(今回はユークリッド距離)が、同じ人ならば0に近づくように、違う人ならばある閾値以上の値となるように学習します。
verificationの教師信号も追加したネットワーク構造は次のようになります。
ただし、verificationの教師信号には定数λ=0.05を掛けて逆伝播しています。
下図でIdはidentificationの教師信号、Veはverificationの教師信号を表しています。

f:id:suzuichiblog:20161031220755j:plain


[3]の図2
各層の設定は次の通りです。
input:3チャネル(カラー画像)、サイズ40x40
conv:フィルタサイズ5x5、ストライド1、パディングなし、出力チャネルは入力層から近い順に20・40・60
pool:フィルタサイズ2x2、ストライド2、パディングなし、maxプーリング
affine(隠れ層):ノード数100
affine(出力層):ノード数40(40人の分類問題なので)
DeepID2+では最後の隠れ層(のReLU後)の値を特徴ベクトルとして扱いますが、ここでは全ての隠れ層(のReLU後)の特徴ベクトル(3箇所)を連結したものを特徴として扱います。
つまり3x100で300次元の特徴ベクトルになるということです。

入力画像の前処理としては、正規化と平均減算を行いました。
正規化とは各値が[0,1]となるようにすることです。
画像では各値が[0,255]という値を取っているので、各値を255で除算しました。
平均減算は、データごとに別々に平均を求め、その平均を各値から引くというものです。

そして、ステップ5で作った15種類のパッチを使い別々にCNNをトレーニングします。
すると画像1枚につき特徴ベクトルとして300x15=4500次元のベクトルが出来ます。

7.識別器 classifier
ステップ6で得られた4500次元の特徴ベクトルを使い、二値分類器であるlogistic regressionを人数分(ここでは40人分)作ります。

8.テスト test
ステップ6で作ったCNNにテスト画像を入れて特徴ベクトルを計算し、それをステップ7で作ったlogistic regressionに入れます。
その結果一番高い値を取ったクラスを、その画像が所属するクラスとします。


以上のような方法を取ることで結構良い精度の結果を得ることが出来ます。
ただ切り取るパッチのサイズや数をどうすれば良いかというのがよくわかりません。
あとはこれが1600クラス分類にも適用できるかが問題となります。
またWebサービスとして我慢できるスピードで出来るかも問題となります。
とりあえず作ってみて、AV女優顔認識システムにCNN版を追加してみようと思います。

 

2016年11月9日追記:
1600人分類をしましたが、いくつか変更を加えました。
まずは、CNNの畳み込み層の出力チャネルを10・20・30としました。
次に、隠れ層の後の活性化関数をReLUから恒等関数(入力と出力が同じ)にしました。
CNNのトレーニングに4日、識別器(logistic regression)のトレーニングに3日程度かかりました。
以前よりは精度が改善されたと思いますが、まだまだ精度が低い気がします。




参考文献
[1]Sun, Yi, Xiaogang Wang, and Xiaoou Tang. "Deep learning face representation from predicting 10,000 classes." Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition. 2014.
[2]Sun, Yi, et al. "Deep learning face representation by joint identification-verification." Advances in Neural Information Processing Systems. 2014.
[3]Sun, Yi, Xiaogang Wang, and Xiaoou Tang. "Deeply learned face representations are sparse, selective, and robust." Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition. 2015.
[4]Sun, Yi, et al. "Deepid3: Face recognition with very deep neural networks." arXiv preprint arXiv:1502.00873 (2015).