LINE DEVELOPER DAYの顔認証受付の仕組み

Seungyoun Yi氏:こんにちは。顔認証技術のテーマで発表させていただきます。私はSeungyoun Yiです。よろしくお願いします。

(会場拍手)

今日はこちらの内容をご紹介していきます。

まず顔認証について、次に顔認証のコアであるエンジン(Face Engine)について、そして、エンジンを本日の受付でも利用した Face Sign にプロダクト化する方法についてお話しし、 最後にまとめとさせていただきます。

最初に顔認証についてお話しします。エレベータでの認証を顔認証に変えてみましょう。

ホテルなどではエレベータに乗ると、カードキーを取り出し、カードリーダで認証し、カードをしまってから、行き先ボタンを選ぶと思います。しかし、顔認証を使うと、エレベータに乗って顔認証するだけで行き先ボタンを選ぶことができます。

また、自動販売機での支払いも顔認証で行うことができます。

普通は現金で支払うところを、顔認証を使って支払いをします。まずは商品を選んで顔を認識させて、そして商品が出てくる。このフローでは、エレベータとは違って2つ目の認証が必要なことがあります。

というのも、支払いが絡んでいるからです。しかし、このフローも、顔認証を使うことでUXをシンプルにすることができます。

今回は、この顔認証技術を LINE DEVELOPER DAY の受付システムで使おうと考えました。ゴールは2つあります。

1つ目は受付時間を1秒以内に短縮すること、2つ目は氏名など個人情報を確認するフローを省くことです。これらを実現することで受付待ちの長い列を解消することを目指しました。

しかし、顔認証エンジンさえあれば、これら全ての問題を解決できるわけではありません。

顔認証エンジンの開発だけでなく、ユーザーが使いやすいサービスの開発も必要です。今回は、このプロジェクトを成功させるために、私たちが取り組んだエンジン開発とサービス開発の2つについてお話したいと思います。

顔認証エンジンの仕組み

まずは顔認証エンジンの仕組みについてお話します。

顔認証を実現するためには、まず顔を認識することが必要です。これには大きく3つのステップが必要です。まず画像から顔を検出し、次に特徴点を抽出したのちに、ゆがみなどを調整して正規化して、顔ごとの特徴量を抽出します。

今回、事前に登録された顔写真から特徴量を抽出し、ギャラリー(データベース)に登録しています。

受付で顔認証する時には、受付で撮影した顔写真から顔の特徴量を抽出して、ギャラリー(事前登録された顔の特徴量データベース)を探索し、最も類似度が高いユーザーを見つけてくるという仕組みになっています。類似度の高いユーザーを見つける際には、特徴量を用いて最近傍探索(Nearest neighbor search, NNS)を行います。

次に顔認証技術を実装する方法です。

フレームワークはTensorflowとCoreMLを採用し、顔の検出や認識には MLKitやiOS Vision Frameworkを利用しています。性能については、認証精度99%、認証速度0.1秒を目指して、エンジン開発を行いました。

また、このほかに、Windows、Linuxなどでも利用できるようにマルチプラットフォームにも対応しています。マルチプラットフォームのサポートのために、フロントエンドはC++やPython Frontend、その他さまざまな技術も利用しています。

顔認証エンジンの最適化について

次に顔認証エンジンの最適化についてお話します。

高精度かつ高速な顔認証をデバイスで実現するため、軽量モデルを採用しました。また、高速化のため、推論エンジンの最適化、レイヤーの最適化も行っています。順にご紹介していきます。

まず軽量モデルについてです。左上をご覧ください。一般的に、横軸のオペレーション数が多いほど、精度は高くなります。これはトレードオフの関係です。しかし、今回は、小さいオペレーション数で高い精度を実現する必要があるため、このトレードオフを押さえながらモデル開発をする必要があります。つまり、図の緑の枠を目指す必要があります。

これは、GPUベースの計算量の大きなモデルをモバイルデバイスで実装する時に問題になります。

最初に顔を検出するFace Detectorのモデルについてです。

よく利用されるモデルにPELEEというものがあります。このモデルは顔検出でよく利用されるものですが、私たちのモデルはPELEEと比べて、非常に軽量でCPUで動かすことができます。CPUを顔認証に使えば、スタンプや3DのグラフィックのレンダリングにGPUを使うことができます。今回のイベントのように受付などで利用する場合、手軽に使えるように、モバイル環境で使えるようにしています。

また、私たちのモデルは、現在報告されている CPUベースの FaceBoxes と比べても、1/10のサイズで、同等の精度を実現しています。

次に Face Landmark Detector です。

私たちのモデルは、従来のモデルと比べて、レイテンシーを1/46に減らし、モデルサイズを1/200と非常に小さくすることに成功しました。

次に Face Recognizer です。

ここでは、MobileFaceNet をベースのモデルとして採用しています。

モデルの最適化にはさまざまな方法がありますが、中でも畳み込み層が最も計算量が多く、またこれまで紹介してきた、いずれのモデルにも共通している構成要素であるため、この畳み込み層を最適化することが最も効果が大きいと考え、注力しました。

推論エンジンの最適化

次は推論エンジンの最適化です。いろいろな最適化がありますが、Depthwise separable convolutionというものがあります。

これは2つのステップに分かれています。畳み込み層のインプットを16、アウトプットを16とした場合、これをSpatial方向とChannel方向に分けて計算します。

こうすることで畳み込み演算の計算量が半分になります。しかし、計算量が少ないからといって、必ずしも処理が早いわけではないため、注意が必要です。使用するフレームワークやモデル、デバイスなどさまざまな条件でテストが必要です。

その結果PytorchとTensorflowのいずれも、畳み込み数が多い方が、レイテンシーが少なく、処理が速くなることがわかりました。

つまり、モデルを圧縮するだけでは、パラメータが少ない、あるいは演算が少ないからといって必ずしも速くはならず、適切な最適化にはあらゆる検討が必要になるということです。

私たちのエンジンでは、最適化によって、Depthwise separable convolutionによる軽量化かつ高速なモデルを構築することができました。

我々は主にiOSとAndroidにフォーカスをして、こちらのテストを行いました。

レイヤーの最適化

次にレイヤーの最適化です。3つの畳み込み演算の実装方法を検討しました。

1つ目は基本的な実装方法である For Loop、2つ目は線形代数、機械学習、統計、その他の多くの分野で一般的に利用される General Matrix Multiply(GEMM)、3つ目は乗算を減少させる Winograd です。

1つ目は、for文を使ってCNN演算が記述されます。速度を上げるために、AVXやNeonなどのIntelまたはAMDが提供するSIMDが使われます。しかし、この実装はfor文が多重になり、計算に非常に時間がかかってしまうため、あまり現実的な方法ではありません。

2つ目は、重みと特徴マップを一度に展開して計算できます。 GEMMは、各ベクトルに最適化されたチャンク単位に分割されます。このようなチャンクで操作すると、メモリの使用量が少なくなり、モバイルでも簡単に使用できます。

3つ目は、Winograd畳み込みです。数学的に乗算を乗算と加算に変換することで、以前よりも計算量を減らすことができます。

ここまで3つの実装方法を見てきましたが、どの方法が良いのでしょうか?

例えば、チャネルが16以上で、幅と高さが16と128のとき、上のグラフから他のものよりも速くなることがわかります。

3×3の畳み込み層では、その幅と高さが8以上のときは、CPUでGEMMベースのほうがより速くなりました。

1×1の畳み込みでは、Direct convolutionとGEMMベースの畳み込みを比べると、チャネルが64以上において、GEMMベースの方が速くなることがわかりました。

これまでさまざまな条件で最適化の方法を検証しましたが、条件によって最適解が異なることがわかります。

ある条件では、iPhoneでは1.3倍、Androidでは1.2倍、CPUでは2.7倍速くなり、パフォーマンスも改善しました。

これまで、CNN演算の実装方法について見てきましたが、このほかにも Layer Fusion という方法もあります。

畳み込み演算は途中のデータ量が大きくなるため、メモリでのデータ転送もコストがかかります。そのため、複数の畳み込み演算を統合することでこのコストを軽減できます。

レイヤーを統合したことで、iOS、Android、CPUでそれぞれ1.1倍、1.05倍、1.19倍速くなりました。

この改善率は、数値としては、小さいですが、人間の認識スピードから考えると十分に効果を実感できるものです。

速度計測に含まれない隠しレイヤーについて

次は、ボーナス(追加での改善点)です。顔認識のファーストステップである顔検出では、速度計測の指標には含まれない隠れたレイヤーがあります。

参考論文には含まれていませんでしたが、NMS_TOP_K を5000から1,250に変化させることで、精度はほとんど変わることなく、スピードはCPUで6倍、そしてAndroidは2.4倍速くなることがわかりました。bounding_boxの算出も大きな処理を伴うため、改善余地が大きいかもしれません。

また、浮動小数点でもスピード改善の余地があると考えました。浮動小数点は、まずそのSignビットが1ビット。それから指数ビットが8ビット、そしてFractionビットとして23ビットがあり、こういうかたちで表現できます。

特別なケースがあって、0も存在します。

それから非正規化数があります。非正規化数は、チップによって使い方が違うことがわかりました。

我々のモデルでは、このようにいろいろな指数化数値がありまして、実験をしてみました。

浮動小数点で速度が遅いとき、ある特定のデバイスで起こるのかを調べるために、正規化表現に変換しました。これはほぼゼロに近くなりました。非正規化数を正規化表現に変えました。

その結果、精度にはほとんど影響を与えずに速度を5倍に改善することができました。