TensorFlow.js搭載chrome拡張で誰でも笑顔体験


[Ateam Lifestyle x cyma Advent Calendar 2018](https://qiita.com/advent-calendar/2018/ateam-lifestyle-cyma)の17日目は、株式会社エイチームライフスタイルでWebエンジニアをしている@water_resistantが担当します。

## はじめに

皆さん機械学習と聞くと何を思い浮かべますか？

おそらく実装するためのサービスやフレームワークを思い浮かべる方が多いかと思います。

Watson、Google AutoML、AWS ML、ChainerやTensorFlowなど機械学習を取り巻く環境は様々です。

この記事ではその中から「[TensorFlow.js](https://js.tensorflow.org/)」という技術に絞ってお話します。

沢山の選択肢から1つの選択肢として考えてもらえるように書きました。
読了後に「面白そうだから、やってみよう」という**きっかけ**になると幸いです。

## この記事で対象とするユーザー層

- 機械学習で何ができるのか**体験してみたい**と思っている方
- 機械学習に興味があるが、python等ではなく**JavaScriptでできると嬉しい**と思う方
- 機械学習を**低スペックなマシン**でも実装してみたいと思う方
- まず**何から触っていけば**良いのか悩んでいる方

## 記事の構成

- **前半**
    - 自作Chrome拡張機能「[Dehehe](https://github.com/TakeshiOnishi/dehehe)」の紹介
        - 「できること」を体験するとイメージが湧きやすいと思ったので作成しました。
    - 面白さを知ってもらうことが目的のパートです。
- **後半**
    - 「[Dehehe](https://github.com/TakeshiOnishi/dehehe)」をどうやって作ったのか説明します。
    - 具体的に行うべきことを理解してもらうことが目的のパートです。

前半と後半の2部構成になっております。

前半は機械学習を体験できる内容にしました。

後半はその実装方法など深い内容を書いているので、前半読了後に興味があれば読んでみてください。

---

## 【前半】 TensorFlow.jsで何ができるのか体験する

体験してもらうために、TensorFlow.jsが使用されているChrome拡張機能を作りました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/6799/ab18cea1-bd08-b9ff-87cd-121244cd8cf8.png)
[GitHub Repository: TakeshiOnishi/dehehe](https://github.com/TakeshiOnishi/dehehe)

### どんな拡張機能か

Google検索結果にいるときに、常時Webカメラが起動します。
その際に、にっこり笑っていないと画面が暗くなり作業ができなくなる拡張機能です。


![qiita (1).png](https://qiita-image-store.s3.amazonaws.com/0/6799/da427f69-6b44-aef2-4af7-0d621ce2f5fd.png)

これをクリスマスまで使えば、かなりの笑顔力が身につくでしょう。

### 拡張機能の利用方法

1. 「[GitHub Repository: TakeshiOnishi/dehehe](https://github.com/TakeshiOnishi/dehehe)
」から拡張機能をダウンロードしてzipを解凍
1. Chromeを開きURLに「chrome://extensions」と入力
1. 右上のデベロッパーモードのチェックをオンにする
1. 解凍されたフォルダを選択する

 **(自己責任でお願いします)**
*※利用情報を収集する処理等は含めておりません。*

### DEMO

下のgif画像からも明らかなように**非常にニッコリしている**ので効果は明らかですね。
(今回のDEMO動画では弊社新卒のI君が顔出しを快く協力してくれました。 ありがとうございます。)

![ver2_conv.gif](https://qiita-image-store.s3.amazonaws.com/0/6799/a401df83-43f0-0d9e-8bcd-5fef5f6c6399.gif)


### 仕組み解説

「笑顔」を判定している部分に機械学習モデルを利用しております。

![qii2.png](https://qiita-image-store.s3.amazonaws.com/0/6799/53b86bd5-3a8d-3df5-05aa-b24d94d04438.png)

モデルを変更するだけで他にも色々なことができるので、様々な使いみちが考えられますね。

- 何かしらのユーザー操作をカメラで行えるようにするツール
- ユーザーの行動を見て、ページ操作に迷っている場合にヘルプを出すツール
- 渋い顔をしているとチームメンバーSlackに通知が飛ぶツール

また[公式サイトでは](https://js.tensorflow.org/)様々なデモがあるので、よりイメージが湧くと思います。
*個人的には[パックマン](https://storage.googleapis.com/tfjs-examples/webcam-transfer-learning/dist/index.html)がおすすめです。*

---

## === 前半と後半の境界 ===

ここからは実装の具体的な話になるので、難易度も上がってきます。

前半部分を見てもっと詳しく知りたいという人であればそのままお進みください。

---

## 【後半】 「Dehehe」の実装解説

### 使う言語・ツール

- HTML
- JavaScript
- Library
  - [TensorFlow.js](https://js.tensorflow.org/)
  - [tracking.js](https://trackingjs.com/)
  - [tensorflow/tfjs-converter](https://github.com/tensorflow/tfjs-converter)
- 学習モデル
  - [学習済みモデル(oarriaga/face_classification)](https://github.com/oarriaga/face_classification/blob/master/trained_models/fer2013_mini_XCEPTION.119-0.65.hdf5)
  - [学習用データ](https://www.kaggle.com/c/challenges-in-representation-learning-facial-expression-recognition-challenge)
- python3.5.2 (pip)

### 実装の流れ

1. 学習済みモデルを取得
1. 学習済みモデルをTensorFlow.js用に変換
1. Chrome拡張機能のベースを作成
1. JavaScriptを記述
  1. 学習モデルをロードする処理
  1. 顔を追跡するトラッキングの準備
  1. 取得された情報をtensor形式へ変換しモデルを呼び出す処理
  1. 判定された結果を元にDOMを書き換える処理

### 学習済みモデルを取得

学習済みモデルを[oarriaga/face_classification](https://github.com/oarriaga/face_classification/blob/master/trained_models/fer2013_mini_XCEPTION.119-0.65.hdf5)からダウンロードする。

上記モデルは以下のような[レイヤー](https://jp.techcrunch.com/2017/04/15/20170413neural-networks-made-easy/)を持っています。

input_1で64*64の画像を受け取り、推定された結果は7つのカテゴリに分類されて出てくるという実装です。

```bash
____________________________________________________________________________________________________
Layer (type)                     Output Shape          Param #     Connected to                     
====================================================================================================
input_1 (InputLayer)             (None, 64, 64, 1)     0                                            
____________________________________________________________________________________________________
conv2d_1 (Conv2D)                (None, 62, 62, 8)     72          input_1[0][0]                    
〜〜〜〜〜省略〜〜〜〜〜
____________________________________________________________________________________________________
conv2d_7 (Conv2D)                (None, 4, 4, 7)       8071        add_4[0][0]                      
____________________________________________________________________________________________________
global_average_pooling2d_1 (Glob (None, 7)             0           conv2d_7[0][0]                   
____________________________________________________________________________________________________
predictions (Activation)         (None, 7)             0           global_average_pooling2d_1[0][0] 
====================================================================================================
Total params: 58,423
Trainable params: 56,951
Non-trainable params: 1,472
```

自分で学習させる場合には、下にあるようなCSV形式のデータを利用します。

|[学習用元データ](https://www.kaggle.com/c/challenges-in-representation-learning-facial-expression-recognition-challenge/data)|データ内容|
|:--:|:--:|
|![image.png](https://qiita-image-store.s3.amazonaws.com/0/6799/691fcfe6-fa80-f6da-1d93-77490a22ed45.png)|![image.png](https://qiita-image-store.s3.amazonaws.com/0/6799/1b06abf7-c881-9465-4651-4d1b3265d054.png)|

また自分で学習させる際には高スペックPCが要求されますが、[Google Colaboratory](https://colab.research.google.com/)を使えば、無料で高性能環境を利用することができます。

[Google ColaboratoryのTPUを試してみる | Qiita](https://qiita.com/koshian2/items/25a6341c035e8a260a01)で詳しく紹介されています。

### 学習済みモデルをTensorFlow.js用に変換

ダウンロードした学習モデルはKerasで学習されたモデルなので、そのままではTensorFlow.jsで使用できません。

そのため、[tfjs-converter](https://github.com/tensorflow/tfjs-converter)を用いて変換します。

↓ pipで関連モジュールを用意

```python
# 仮想環境作成
# 必要なライブラリをpipでインストール
pip install keras==2.1.5 tensorflow==1.7.0  tensorflowjs==0.1.1
```

↓ TensorFlow.js用に変換([convert.py](https://github.com/TakeshiOnishi/dehehe/blob/master/trained_data/convert.py))

```python
from keras.models import load_model
import tensorflowjs as tfjs

# TensorFlow.js用にConvert
model = load_model('./fer2013_mini_XCEPTION.110-0.65.hdf5', compile=False)
tfjs.converters.save_keras_model(model, './output/')
```

↓ `output`ディレクトリに変換後ファイルが生成されます。

```bash
group1-shard1of1
group2-shard1of1
...省略...
group28-shard1of1
group29-shard1of1
model.json
```

複数ファイル出力されますが、実際にTensorFlow.jsで呼び出す際は`model.json`だけです。

というのも`model.json`を起点にして`group`ファイルがロードされていくからです。

webpackなどを触ったことがある人は馴染みがある考えかと思います。

### Chrome拡張機能のベースを作成

本筋から離れるのと先駆者が大勢居るので、今回は割愛します。

[Chromeのオリジナル拡張機能を開発しよう（ソースコードあり）|LIG](https://liginc.co.jp/web/tool/browser/163575)

### JavaScriptを記述

↓ 学習モデルをロードする処理

```javascript
// ChromeExtensions内部に配置した学習モデルを読み出し
model = await tf.loadModel(chrome.extension.getURL('trained_data/output/model.json'))
```

↓ 顔を追跡するトラッキングの準備

```javascript
function initTracker() {
  tracker = new tracking.ObjectTracker('face');
  tracker.setInitialScale(4);
  tracker.setStepSize(2);
  tracker.setEdgesDensity(0.1);
  tracking.track('#inputVideo', tracker, { camera: true, fps: 4 }); 
};
```

↓ 取得された情報をtensor形式へ変換しモデルを呼び出す処理

```javascript
// 64x64部分は学習済みモデルの入力層の要求データサイズです
let tensor = tf.fromPixels(canvas, 1).resizeNearestNeighbor([64,64]).toFloat();
let offset = tf.scalar(255);
tensor_image = tensor.div(offset).expandDims();
```

↓ 変換されたデータを使って、モデルから推論する

```javascript
let prediction = await model.predict(tensor).data();
let result = Array.from(prediction).map(function(p,i){
  return {
    probability: p,
    classNumber: i
  };
}).filter(obj => obj.classNumber == SMILE_NUMBER)
.shift()

probability = result.probability
```

↓ 判定された結果を元にDOMを書き換える処理

```javascript
// predict(input_video_canvas_tensor); 実行後
if(probability > 0.88) {
  // 笑顔度が高いとき
}else if(not_smile_counter++ > 4){
  // 連続して笑顔度が低いとき
}
```

## まとめ

- オフライン環境でも機械学習モデルを使うことができる。
- 変換は必要だけれど、JavaScriptだけで機械学習モデルを利用することができる。
- 低スペックのPCでも[Google Colaboratory](https://colab.research.google.com/)を使えば自分で学習させることもできる。
- [クソアプリ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/kuso-app)案件だったかもしれない点は反省。

## 最後に...

いかがでしたでしょうか。
後半に関してはかなり端折っていた部分も多かったので、分かりづらかったかもしれません。

ただこれをきっかけに「機械学習って面白そうだな」なんて少しでも思ってもらえたら、嬉しいです。

*(記述内容に指摘箇所などがあれば、是非コメントください!)*

**それでは、良い開発ライフを！**

[Ateam Lifestyle x cyma Advent Calendar 2018](https://qiita.com/advent-calendar/2018/ateam-lifestyle-cyma)、明日は @bayasist に書いてもらう予定です。お楽しみに！


## 参考リンク

- [oarriaga/faceclassification/Github](https://github.com/oarriaga/face_classification/)
- [PonDad/manatee](https://github.com/PonDad/manatee/tree/master/2_emotion_recognition-master)
- [TensorFlow.jsで「感情」を認識してみよう](https://book.mynavi.jp/manatee/detail/id=99887)
- [Chrome拡張機能のマニフェストファイルの書き方](https://qiita.com/mdstoy/items/9866544e37987337dc79)
- [Google Colaboratory](https://colab.research.google.com/)
- [いらすとや](https://www.irasutoya.com/)
- [kaggle](https://www.kaggle.com/)
- [TensorFlow.js Tutorial](https://js.tensorflow.org/tutorials/)

---

エイチームグループでは、一緒に働けるチャレンジ精神旺盛な仲間を募集しています。興味を持たれた方はぜひエイチームグループ採用サイトを御覧ください。
https://www.a-tm.co.jp/recruit/

---