概要
顔認証の勉強中に、FaceNet: A Unified Embedding for Face Recognition and Clusteringを読む。そのメモ書き。
下記がその論文。
下記がその実装。
Abstract
まずはFaceNetとは何かについて。訳はかなり意訳。
that directly learns a mapping from face images to a compact Euclidean space where distances directly correspond to a measure of face similarity.
(こいつは顔の画像をコンパクトなユークリッド距離に変換するよ。生成した距離空間で距離を取ると顔の類似度と一致するよ)
we achieve state-of-the-art face recognition performance using only 128-bytes per face.
(1つの顔につき128バイトで十分な精度が出せるようになったよ)
ということで顔写真を128バイトのユークリッド距離に変換して、距離が近ければ同一人物という判定ができる代物らしい。
人の顔が128バイトの距離で表現できてしまうというのは、詳しくない人間としては浮世離れした世界の話のように見える。word2vecの話が出た時もそんな感じがしたけど。
Introduction
提供されるシステムの内容。
we present a unified system for face verification (is this the same person), recognition (who is thisperson) and clustering (find common people among thesefaces)
(同じ顔かどうか判定するface verificationシステム、この顔が誰か判定するrecognitionシステム、それから顔をクラスタリングするシステムを提案するよ)
A distance of 0.0 means the faces are identical, 4.0 corresponds to the opposite spectrum, two different identities. You can see that a threshold of 1.1 would classify every pair correctly.
(距離が0.0なら等しい、4.0だと正反対。1.1を閾値にするといい感じ)
Our triplets consist of two matching face thumbnails and a non-matching face thumbnail and the loss aims to separate the positive pair from the negative by a distance margin.
(画像A、画像Aと同じ人の画像、画像Aと違う人の画像の3つ組を作って距離を学習させるよ)
結果が距離空間になるわけなので、認証、判定、クラスタリングといった機能ができることは想像しやすい。
内容的に似ている芸能人を検索することもできると思われる。
判定の閾値は1.1になるらしい。
triplet loss
前述に出てきたtriplets(3つ組)という言葉が精度を上げるミソのところで、Triplet lossという名称で各所で取り上げられている。Triplet lossの詳細はPaperの3.1で詳しく説明されている。
が、説明読んでも理解が怪しいのでコードを読む。コメント空行除くとたった7行。
def triplet_loss(anchor, positive, negative, alpha): """Calculate the triplet loss according to the FaceNet paper Args: anchor: the embeddings for the anchor images. positive: the embeddings for the positive images. negative: the embeddings for the negative images. Returns: the triplet loss according to the FaceNet paper as a float tensor. """ with tf.variable_scope('triplet_loss'): pos_dist = tf.reduce_sum(tf.square(tf.subtract(anchor, positive)), 1) neg_dist = tf.reduce_sum(tf.square(tf.subtract(anchor, negative)), 1) basic_loss = tf.add(tf.subtract(pos_dist,neg_dist), alpha) loss = tf.reduce_mean(tf.maximum(basic_loss, 0.0), 0) return loss
https://github.com/davidsandberg/facenet/blob/master/src/facenet.py#L44
基準となるanchorという画像に対して、positiveな画像とnegativeな画像、あとalphaが渡されている。alphaはデフォルトだと0.2になっていた。
https://github.com/davidsandberg/facenet/blob/master/src/train_tripletloss.py#L446
tripletの選び方
3人組作れの時に良い組合せを選ぶことが重要らしい。全パターンを取るのは計算量が膨大だし、ランダムに適当に取るだけでは効率が悪い。
hard-positive(positiveで距離が最大になるヤツ)とhard-negative(negativeで距離が最小になるヤツ)で学習させたいけど、計算量が膨大になるのでやりづらい。
そこで実装ではpositiveについては全パターンの組合せを学習データとし、negativeについては下記のようにalpha以内の距離から選択している。
# 両者の距離を取る neg_dists_sqr = np.sum(np.square(embeddings[a_idx] - embeddings), 1) # 距離がalphaより近いものを取る all_neg = np.where(neg_dists_sqr-pos_dist_sqr<alpha)[0] # VGG Face selecction
We call these negative exemplars semi-hard, as they are further away from the anchor than the positive exemplar, but still hard because the squared distance is close to the anchor-positive distance.
我々はこのネガティブの標本をセミハードと呼んでる。ネガティブはポジティブよりはアンカーから遠くなるけど、それほど大きく距離が変わるわけではないので判別が難しい問題になる。
CNN
学習についてはDeep CNNを利用している。
具体的にはこういう流れらしい。
layer | size-in | size-out | kernel | param | FLPS |
---|---|---|---|---|---|
conv1 | 220×220×3 | 110×110×64 | 7×7×3,2 | 9K | 115M |
pool1 | 110×110×64 | 55×55×64 | 3×3×64,2 | 0 | |
rnorm1 | 55×55×64 | 55×55×64 | 0 | ||
conv2a | 55×55×64 | 55×55×64 | 1×1×64,1 | 4K | 13M |
conv2 | 55×55×64 | 55×55×192 | 3×3×64,1 | 111K | 335M |
rnorm2 | 55×55×192 | 55×55×192 | 0 | ||
pool2 | 55×55×192 | 28×28×192 | 3×3×192,2 | 0 | |
conv3a | 28×28×192 | 28×28×192 | 1×1×192,1 | 37K | 29M |
conv3 | 28×28×192 | 28×28×384 | 3×3×192,1 | 664K | 521M |
pool3 | 28×28×384 | 14×14×384 | 3×3×384,2 | 0 | |
conv4a | 14×14×384 | 14×14×384 | 1×1×384,1 | 148K | 29M |
conv4 | 14×14×384 | 14×14×256 | 3×3×384,1 | 885K | 173M |
conv5a | 14×14×256 | 14×14×256 | 1×1×256,1 | 66K | 13M |
conv5 | 14×14×256 | 14×14×256 | 3×3×256,1 | 590K | 116M |
conv6a | 14×14×256 | 14×14×256 | 1×1×256,1 | 66K | 13M |
conv6 | 14×14×256 | 14×14×256 | 3×3×256,1 | 590K | 116M |
pool4 | 14×14×256 | 7×7×256 | 3×3×256,2 | 0 | |
concat | 7×7×256 | 7×7×256 | 0 | ||
fc1 | 7×7×256 | 1×32×128 | maxout p=2 | 103M | 103M |
fc2 | 1×32×128 | 1×32×128 | maxout p=2 | 34M | 34M |
fc7128 | 1×32×128 | 1×1×128 | 524K | 0.5M | |
L2 | 1×1×128 | 1×1×128 | 0 | ||
total | 140M | 1.6B |
深い。見るからに重そう。こんなんうちの貧弱なサーバーで動かせるのだろうか。
Experiments
If not mentioned otherwisewe use between 100M-200M training face thumbnails consisting of about 8M different identities
(特に言及がなければ我々は800万人の個人の顔の1億〜2億のサムネイル画像を利用しているよ)
Googleの論文って個人じゃ手が出せない単位をしれっと使うよね。
A face detector is run on each image and a tight bounding box around each face is generated.
(すべての画像は顔認識で顔のところをBOXで抜いてあるよ)
Input sizes range from 96x96 pixels to 224x224 pixels in our experiments.
(経験的に顔のピクセルは96x96〜224x224を使ってるよ)
ここまでの内容でFaceNetの実装がどうなっているかはなんとなく想像できる情報は集まった。
Effect of CNN Model
JPEGの圧縮率による精度の変化について。
jpeg q | val-rate |
---|---|
10 | 67.3% |
20 | 81.4% |
30 | 83.9% |
50 | 85.5% |
70 | 86.1% |
90 | 86.5% |
10%とか無茶をするとかなりスコアが下がるけど普通に見られるレベルの圧縮をする分には影響は大きくはなさそう。
ピクセル数について。
pixels | val-rate |
---|---|
1,600 | 37.8% |
6,400 | 79.5% |
14,400 | 84.5% |
25,600 | 85.7% |
65,536 | 86.4% |
96x96=9216くらいはあると良さそう。
続いてトレーニングに使うイメージ数。
training images | VAL |
---|---|
2,600,000 | 76.3% |
26,000,000 | 85.1% |
52,000,000 | 85.1% |
260,000,000 | 86.2% |
100万ではもの足りない。自前で何かしらデータを集めてモデルを生成しようと思ったら、ある程度自動でデータを集められる仕組みを用意しないと無理そうだし、学習にかかるコンピュータリソースについても確保する必要がある。
手持ちのリソースでこれに対してどの程度アプローチできるか、要検討。
改定履歴
Author: Masato Watanabe, Date: 2019-05-01 記事投稿