walkingmask’s development log

IT系の情報などを適当に書いていきます

MENU

TensorFlow.js を使って MicroExpNet をブラウザ上で動かしてみた

作ったもの。

github.com

はじめに

clmtrackr.js という、ブラウザ上で表情認識できてしまう、驚異的な技術があります。

github.com

(デモはこちら)

Webカメラの映像に対してリアルタイムで表情認識を行えます。

ただ、中身を見ると、流行りのディープラーニングは使ってないようです。(SVMとかなり少ないパラメータで表情認識してる感じ。間違ってたらすみません)

そこで、ディープラーニングを使った表情認識モデルをWebブラウザ上で実行できないか興味を持ちました。

前々から、TensorFlow.js は知っていて、使ってみたいと思っていたので、こちらと組み合わせることも前提としました。

そうなると、モデルは軽量な方が良いと考えました。

調べると、MicroExpNet というモデルが「蒸留」という手法を使って、精度良いまま表情認識モデルを軽量化できたとのことでした。

そこで、今回は TensorFlow.js を使って、この MicroExpNet をブラウザ上で実行することを目的としました。

環境

以下を使用します。

  • Python 3.6.2
  • tensorflow 1.8.0
  • tensorflowjs 0.4.2
  • Flask 1.0.2 (静的ファイル配信用)

手順

まず、適当にディレクトリを作成して、microexpnet をクローンしておきます。

mkdir microexpnetjs
cd microexpnetjs
git clone https://github.com/cuguilke/microexpnet.git

microexpnet には、素晴らしいことに学習済みモデルが含まれています。

これを使いたいのですが、Saver.save で保存した形式のモデルファイルは、残念ながら tensorflowjs-converter では使えないようです。

そこで、pb ファイルを生成します。以下の記事に分けて書いたので、参考にしてください。

walkingmask.hatenablog.com

この pb ファイルを tensorflowjs-converter で変換します。

tensorflowjs_converter --input_format=tf_frozen_model --output_node_names='Add_1' ./output_graph.pb model

これで、model 以下に js から呼び出せる形式のモデルファイルが生成されました。

あとは、これを使うための JavaScript ファイルと、それを配信するための簡単な Web アプリを書きます。

microexpnet.js

const CLASSES = ["neutral", "anger", "contempt", "disgust", "fear", "happy", "sadness", "surprise"]
const MODEL_FILE_URL = 'static/js/model/tensorflowjs_model.pb'
const WEIGHT_MANIFEST_FILE_URL = 'static/js/model/weights_manifest.json'
const INPUT_NODE_NAME = 'Placeholder';
const OUTPUT_NODE_NAME = 'Add_1';
class MicroExpNet {
  constructor() {}
  async load() {
    this.model = await tf.loadFrozenModel(
      MODEL_FILE_URL,
      WEIGHT_MANIFEST_FILE_URL
    );
  }
  dispose() {
    if (this.model) {
      this.model.dispose();
    }
  }
  predict(input) {
    const reshapedInput = input.reshape([1, 84*84]);
    return this.model.execute(
      {[INPUT_NODE_NAME]: reshapedInput}, OUTPUT_NODE_NAME);
  }
  getTopKClass(logits) {
    const predictions = tf.tidy(() => {
      return logits.argMax(1);
    });
    const values = predictions.dataSync();
    predictions.dispose();
    return CLASSES[values[0]];
  }
}

index.html

<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.11.7"> </script>
<script src="static/js/microexpnet.js"> </script>
</head>
<body>
<img id="face" src="static/img/face.png"></img>
<div id="result"></div>
<canvas id="canvas"></canvas>
<script>
const faceE = document.getElementById('face');
faceE.onload = async () => {
  const resultE = document.getElementById('result')
  const microexpnet = new MicroExpNet();
  await microexpnet.load();
  const pixels = tf.fromPixels(faceE).toFloat();
  // rgb2gray
  const [r, g, b] = tf.split(pixels, [1, 1, 1], 2);
  const gray_pixels = r.mul(tf.scalar(0.2126))
    .add(g.mul(tf.scalar(0.7152)))
    .add(b.mul(tf.scalar(0.0722)))
  const result = microexpnet.predict(gray_pixels);
  const topClass = microexpnet.getTopKClass(result);
  resultE.innerText = topClass;
  microexpnet.dispose();
}
</script>
</body>
</html>

app.py

from flask import (
    Flask,
    render_template,
)

app = Flask(__name__)

@app.route('/')
def root():
    return render_template('index.html')

if __name__ == "__main__":
    app.run(debug=True)

だいたいこんな感じです。ディレクトリ構成などの詳しい情報はリポジトリを参考にしてください。

あとは、

python app.py

で Flask を起動した後に、http://localhost:5000 にすると、face.png に対する認識結果が出力されると思います。

おわりに

ということで、初めてtensorflowjsを使うとこからMicroExpNetの学習済みモデルを使って顔画像の表情認識までをやりました!

楽しかったー😄😄😄😄😄

だいぶ適当ではありますが、誰かの助けになれば幸いです。

今後の課題として、clmtracker のように、リアルタイムで顔を検出して、表情認識結果を数値で出力までやりたいですね。

顔検出は結局 clmtracker に頼ることになったり。。(OpenCV.js はビルドからしないといけないので手間かかりそう&重そう)

...

以下、余談です。

本日は Hackers Champloo 2018 に参加してきました。

hackers-champloo.org

2016以来の参加で、当時は専門学校の教室で開催していましたね。

からの、コンベンションセンターでの開催にまで大規模になっており、勢いを感じます。

これが平成最後の夏ですね。

友達に会ったり、お弁当を食べたり、内容の濃い発表を聞いてモチベートされました。

おかげで、tensorflowjsを触り初めて、PCの電池が切れて我慢できずに途中で帰ってしまったのですが、、、(笑)

個人的には、カンファレンスに参加したのだからもっと人と交流したいのですが、なかなか成長できなくて葛藤していたり。

内容はWeb開発寄りの発表が多いのですが、スポンサーの企業さんの中にはちゅらデータがいたり、機械学習エンジニアを募集しているところもあったり、時代の流れを感じます。

これが平成最後の夏ですね

個人的には、fujiwaraさんのGo言語の話や、motemenさんのエンジニアとしての道とかを興味深く聞いてました。なんにせよ豪華なラインナップ!

ともあれ、運営のみなさん、スポンサーのみなさん、そして参加者のみなさんもお疲れ様でした&また来年も会いましょう!

関連記事

walkingmask.hatenablog.com

walkingmask.hatenablog.com

参考文献