ハードウェア技術者のスキルアップ日誌

某家電メーカーの技術者がスキルアップのために勉強したことを記録するブログです

FaceNet(顔認識)を動かしてみた

YOLOやSSDなどディープラーニングのネットワークをいくつか試してきましたが、
今回は顔認識のニューラルネットワークであるFaceNetを動かしてみましたので
手順を記録しておきます。

FaceNetの概要

FaceNetは2015年にGoogleが発表した顔認証用のニューラルネットワークです。

FaceNetの論文はこちらから参照できます。
https://arxiv.org/pdf/1503.03832.pdf

 

Siamese Network

FaceNetはSiamese(シャム) Networkをベースにしており、
入力画像がどのクラスに属するかではなく、
画像同士が似ているか似てないかを表す距離を学習します。

   f:id:masashi_k:20190725220116p:plain

クラスに分類する場合、候補となる人のどれに当たるかを分類することになるため、
各人の画像を大量に収集して学習する必要があります。
さらに、候補者が増えた場合は、再学習が必要です。
一方、Siamese Networkのように画像間の距離を算出する場合は、
その距離により2つの顔画像が同一人物かどうかを判断できるので
少ない画像で判別が可能となります。
画像は1枚でも判定が可能でそのような学習タスクをOne Shot Learningと呼びます。

 

Triplet Loss

Siamese Networkでは2つの画像のペアで学習を行いますが、
FaceNetでは3つの画像の組み合わせで学習を行います。
その時に使用する損失関数がTriplet Lossと呼ばれるもので、
基準となるAnchorに対して、同一ラベルのものを近くに、異なるラベルのものを
遠くに置くように学習します。

f:id:masashi_k:20190725222629p:plain

 

FaceNetのネットワーク構成

ネットワーク構成は以下のようになっています。
顔のみを切り出し、正規化した画像を入力します。
DEEP ARCHITECTUREの部分が特徴量を抽出するCNNで、
論文上ではZeiler&Fergus、Inceptionを使用しています。
他のCNNを使用することも可能と思われます。
その出力CNNのに対してL2ノルムを取り、Triplet Lossを計算します。

f:id:masashi_k:20190725222937p:plain

 

OpenFace

オープンソースの顔認識技術としてOpenFaceというものもあります。http://cmusatyalab.github.io/openface/

こちらはFaceNet論文を元にしたオープンソースの実装です。
DNN(Deep Neural Network)を用い、顔から128次元の特徴ベクトルを抽出します。

f:id:masashi_k:20190725223903j:plain

         OpenFaceのサイト(上記URL)から引用

 

FaceNetを動かしてみた

GITHUBで公開されているこちらのレポジトリを試してみました。

github.com

FaceNetは顔認識のニューラルネットワークですが、実際に認識をさせるためには
写真から顔の部分を切り出す必要があります。
このため、facenetではMTCNNという顔検知のニューラルネットワークを前段で
使用しており、その結果をfacenetに入力する形になっています。

MTCNNの論文
https://kpzhang93.github.io/MTCNN_face_detection_alignment/paper/spl.pdf

 

ネットワークの動かし方はこちらのサイトがとても参考になりました。

appliedmachinelearning.blog


Face_IDという別のレポジトリに関するものですが、
facenet(小文字表記はレポジトリ名)をベースに修正されているようで、
共通している部分が多いです。

上記サイトでは2つの命題について記載されています。
①Face verification:登録された顔が画像の中にあるかを判別
②Face identification:あらかじめ登録された顔のデータベースから
          どれに当てはまるかを識別
今回は①を試してみました 

環境

 OS:Windows 10 Home (64bit)
 Python 3.5
 Anaconda 4.2.0
 Tensorflow 1.12.0

 

動かし方

GITHUBからレポジトリをCloneして任意の場所に解凍します。
https://github.com/davidsandberg/facenet

②以下のURLから学習済みの重みをダウンロードし、facenetの下に保存します。
 データセットCASIA-WebFaceとVGGFace2の2種類あります。
https://drive.google.com/file/d/1R77HmFADxe87GmoLwzfgMu_HY0IhcyBz/view
https://drive.google.com/file/d/1EXPBSXwTaqrSC0OhUdXNmKSh9qJUQ55-/view

③Anacondaプロンプトを起動し、プロジェクトディレクトリに移動します。
Requirements.txtに記載されているライブラリをインストールします。
必要なライブラリは以下の通りです。

  Tensorflow
  scipy
  scikit-learn
  opencv-python
  h5py 
  matplotlib
  Pillow
  requests
  psutil

※以下のコマンドで自動で必要なライブラリをインストールできますが、
Tensorflowのバージョンが1.7.0になってしまうので、ダウングレードが不要であれば、
Requirements.txtを修正のうえ、実行してください。
私の環境では1.12.0でも問題なく動作しました。

$ pip install -r requirements.txt

 

④サンプルスクリプトcompare.pyを実行

$ python [重みファイルのパス] [比較したい画像ファイルパス(2つ以上も可)] \
$ --image_size 160 --margin 32 --gpu_memory_fraction 0

重みファイルのパス:②で取得したファイルのパスを指定
比較したい画像パス:顔の類似度を計算したい画像ファイルを指定
 手元に画像がない場合はサンプルとしてfacenet/data/imagesの中に画像があります。
--image_size:画像サイズ デフォルトは160pixel
--margin:顔検知した際、検知した場所から何pixel外側をクロップするか
     デフォルト 44pixel
--gpu_memory_fraction:GPUメモリ使用量の設定

具体的にはこのようになります。

$ python src/compare.py src/20180402-114759 \
$ data/images/Anthony_Hopkins_0001.jpg \
$ data/images/Anthony_Hopkins_0002.jpg \
$ --image_size 160 --margin 32 --gpu_memory_fraction 0

結果は以下の通りとなります。

Images:
0: data\images\Anthony_Hopkins_0001.jpg
1: data\images\Anthony_Hopkins_0002.jpg

Distance matrix
     0            1
0   0.0000   0.8516
1   0.8516   0.0000

Anthony_Hopkinsの画像を二枚入力し、0と1の画像の距離が0.856となっています。
距離が短いほど2つの顔が類似しているということになります。

同一人物だけだと面白くないので別の顔画像で試した結果も示します。
Face_IDのレポジトリにはもう少しサンプル画像が入っていますので、
それを使って試しました。

結果はこちら。

Images:
0: facenet/dataset/test-images/mark1.jpeg
1: facenet/dataset/test-images/mark.jpeg
2: facenet/dataset/test-images/bradley.jpeg
3: facenet/dataset/test-images/hritik.jpeg

 

Distance matrix
        0         1         2         3

0    0.0000    0.6942    1.4113    1.4643
1    0.6942    0.0000    1.4722    1.4246
2    1.4113    1.4722    0.0000    1.1274
3    1.4643    1.4246    1.1274    0.0000

0,1がマークザッカーバーグで同一人物、2,3は別の人物です。
結果を見ると0-1の距離が約0.7と短く、それ以外の距離はすべて1以上です。
FaceNetの論文では1.1が閾値としてよいとされているので、
確かに同一人物では画像間の距離が1.1以下となっていることを確認できました。

まとめ

GITHUBに公開されているFaceNetのレポジトリを実際に動かしてみて、
顔同士の距離が算出され、それが人の類似度になっていることを確認しました。
次は、このサンプルコードを使って、ラズパイでなにか作ってみたいと思います。

参考サイト

https://qiita.com/koshian2/items/554b3cbef6aeb1aab10d

https://www.slideshare.net/kaorunasuno/20150611-nasuno

https://blog.imind.jp/entry/2019/05/01/142232

https://blog.imind.jp/entry/2019/05/06/192603