けやみぃアーカイブ

CV勉強中の大学生のアウトプットです

大人数の顔認識ができるシステムを作りたい

大人数の顔認識ができるシステムを作りたい!!!!!!!!!!!

と思ってこんな感じのシステムを作りました。djangoを使って実装しています。現時点では150人ほどの識別ができるはずです。 f:id:kym384:20200912180339j:plain

始まり編

ネットにあふれている「顔認識してみた」系の記事は、ほとんどが数人しか認識できないものだと思います。自作の顔認識モデルを学習させるときは最後にSoftmaxをつけるのが一般的だと思いますが、それによって認識できる人物が固定されてしまいます。後から認識させたい人物を増やしたときには再学習させないといけない…。転移学習などで早く終わるとしてもできる限り時間はかけたくありません。

そんな思いを抱きながら生きていたある日。FaceShifterという顔変換モデルを知ります。 ai-scholar.tech こちらは損失関数にIdentity lossを導入しています。つまり「モデルが生成した顔」は「交換する顔」と同一人物か?ということを表すための損失ですね。具体的にはArcfaceというモデルを通して顔画像から512次元ベクトルを生成し、2つのベクトルについてコサイン類似度をとります。Arcfaceとはなんじゃらほいということで以下のQiitaの記事を読みます。

qiita.com

この記事から引用

このペナルティにより、モデルは頑張って特徴ベクトルxのクラス内分散を小さくし、クラス間分散を大きくして、このマージンを稼ごうと頑張るわけです。これがSphereFace, CosFace, ArcFace系の手法の本質になります。

特徴ベクトルのクラス間分散を大きくする…。つまり特徴ベクトルを使ってもある程度は識別できそう。しかもFaceShifterでも使われているように、学習データにあまり依存しなさそう。(正しいのかはあんまりわかってない)

どっかからArcfaceの学習済みモデルを手に入れることができれば、1人の人物に対して数枚の画像を持ってきてArcfaceに通して512次元ベクトルにして、入力された画像に対してそれぞれの人物とのコサイン類似度をとれば、再学習もすることなしに大人数の識別ができるようになるのでは????と思って調べ始めました。

顔検出編

実際に呼び出してみるとこんな感じ。

この画像を入力すると… f:id:kym384:20200912173946j:plain

この画像が返ってくる。

f:id:kym384:20200912173939p:plain

ちゃんと真っすぐになってますし、よさげ。

顔認識してみる編

とりあえず適当に1人当たり2~3枚参照画像を集めます。ぱっと思い浮かんだのでJuice=Juice初期5人の画像にすることにしました。それから、先ほどのGithubのページからArcfaceの学習済みモデルをダウンロードして、集めた画像たちをモデルに通します。モデルの出力である512次元ベクトルをnpyファイルで保存しておきます。各人物に番号を振っておいて、「1.npy」のファイルには(画像の枚数, 512)というshapeのndarrayが保存される形です。

ようやく準備が終わったので認識してみましょう。最初に保存してあるnpyファイル(今回は5つ)を読み込み、axis=0で平均をとります。また、それぞれ正規化(大きさが1になるようにする)しておきます。これらを縦方向に結合して(5, 512)のshapeで保持しておきます。この行列を適当にWとしておきましょう。

次に入力する画像を用意します。これも同じくdlibで顔検出、補正、Arcfaceに通して512次元ベクトルにします。それでこっちも正規化。(1, 512)の行列xとしておきます。

そして、コサイン類似度をとります。それぞれ正規化をしておいたので、行列の積をとるだけでできます。W@x.T ですね。ちなみに@は np.dot の演算子です。それで得られた(5, 1)の行列に対して、np.argmaxしてやるだけで「最も似ている人」を算出できるはずです。やってみましょう。

ハロプロ公式サイトから金澤さんの画像を持ってきます。

www.helloproject.com

f:id:kym384:20200912182558j:plain

こちらの画像ですね。先ほどの処理を施せば、それぞれの人物についてのコサイン類似度が出て来ます。出力してやると…(ちなみにProgress barはtqdmというモジュール使ってます) f:id:kym384:20200912183121p:plain み、見づらい…。コピーしますね

宮崎由香 0.4567191558461361
宮本佳林 0.21137236629074796
金澤朋子 0.6679130527662338
高木紗友希 0.40638896454727513
植村あかり 0.5210983936574289

はい、ちゃんとコサイン類似度が最大のものが正解と一致していますね。いろいろ試してみましたが、同一人物であってもコサイン類似度は0.6~0.8くらいになります。(参照画像と同じ画像を使えば0.99になりますけど) 閾値の設定が難しいですが、とりあえずコサイン類似度が最大かつ0.5以上の場合に同一人物と判定することにしました。

やり残し編

参照画像を集めるのをもっと簡単にしたいですね。これはdjangoでURLを入力すれば勝手にベクトルにして保存してくれるようにしました。それから、名前を入力するとスクレイピングして自動で参照画像を集めてくれるようにもしました。ここら辺はそのうち書くと思います。

現在でまだ解決してないのは以下の3つ。

  • 認識できる人物もスクレイピング等で自動で集めてほしい
  • 参照画像は何枚が妥当なのか?
  • 参照画像のベクトルを平均しただけで、本当にその人を表すベクトルになってるのか?平均以外にもっと良い作り方はないのか?

後半2つに関しては面倒くさそうなのでやらないかも…。