Teachable Machineで読み手の表情から「記事の面白さ」を可視化してみた

[![Image from Gyazo](https://i.gyazo.com/95f8da94329670ca0daebb146b43a086.gif)](https://gyazo.com/95f8da94329670ca0daebb146b43a086)
## 「俺の記事って…面白いか…？」
私は駆出しの広報担当として社内報など色々な記事や文章を作成しています。
頭の引き出しをすべてひっくり返しながら原稿を毎度書いているのですが、原稿案が出来たときに常に感じることが有ります。
そうです、「これ面白いか…？」という漠然とした不安です。

もちろん自分で推敲してみたり同僚に意見をもらったりしているのですが、それらも経験や勘といった主観に頼っています。「少しでも客観的に記事への反応を取得できないものか…。」その時思いました。読んでいる人の表情から反応を数値化できれば良いのではないかと。

そこで今回はTeachabl Machineで作成した画像判別モデルを活用し、感じた感情をAmbientで可視化してみました。　

## 出来たこと

* **文章等を読んでいる際の表情から以下の定義で感情を読み取るWebアプリケーションを作成しました。**
 * 面白い　：画面に正対している+笑顔
 * 関心　　：画面に正対している
 * 疑問　　：画面に正対している+眉をひそめている
 * 興味なし：画面に正対していない  

* **WebアプリケーションからNode-REDを経由してデータ可視化ツールであるAmbientへ判定結果を連携し、グラフ化しました。**



## 使用ツール
* Heroku
* Node-RED v2.1.5
* Teachable Machine
* CodePen
* Ambient

## 事前準備

1. **データを可視化するために下記のリンクを参考にAmbientに登録し、チャネル生成をしてください。**  
リンク：[Ambientを使ってみる]( https://ambidata.io/docs/gettingstarted/) 
1. **Node-REDにAmbientとデータ連携を図るため、Ambientノードを追加してください。**  
リンク：[Node-REDでAmbientにデーター送信して可視化する]( https://ambidata.io/samples/dataprocess/node-red/)
　　
1. **表情を判定するモデルをTeachable Machineで作成し、保存してください。**  
リンク：[Teachable Machine](https://teachablemachine.withgoogle.com/) 

## 処理の概要

### 作成した画像判別するモデルで表情を読み込むWebアプリケーションを作成  
Webカメラに映った表情から事前に準備したモデルを使用して、感情を判定してくれるWebアプリケーションを作成します。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="wvPKgaY" data-user="itotakaya" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/itotakaya/pen/wvPKgaY">
  興味関心判定機</a> by ItoTakaya (<a href="https://codepen.io/itotakaya">@itotakaya</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

上にもありますがJavaScriptは以下の通りです。
別のアプリケーションに流用する際の差替え箇所を追記しています。

```JavaScript
// 作成したモデルのURL
const imageModelURL = 'モデルのURLを貼り付け';

// メインの関数（ここでは定義しているだけでボタンクリックされたら実行）
async function start() {
  // カメラからの映像ストリーム取得
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: false,
    video: true,
  });
  
  // 「id="webcam"」となっているパーツ（videoタグ）を取得
  const video = document.getElementById('webcam');
  
  // videoにカメラ映像ストリームをセット
  video.srcObject = stream;
  
  // Googleのサーバーにアップロードした自作モデルを読み込みにいきます
  const classifier = ml5.imageClassifier(imageModelURL + 'model.json', video, () => {
    // 読み込みが完了次第ここが実行されます
    console.log('モデルの読み込みが完了しました');
  });
  
  // 繰り返し処理
  function loop() {
  // 推論を実行し、エラーがあればerrに、結果をresultsに格納して、
  // 推論が完了次第 { } の中身を実行します
  classifier.classify(async (err, results) => {
    // 結果のresultsは配列ですが、先頭に中身があれば以下の処理を実行します
    if (results[0]) {
      // Node-REDの/receiverへ連携するデータを指定します
      await axios.post('Node-REDのURLを貼り付け/receiver', results[0]);
      const myHand = results[0].label;
      // 結果を入れる変数を用意
      let result = '';
      // 判定を実行
      if (myHand == 'おもしろい'
      ) {
        result = 'おもしろい';
      } else if(myHand == '関心'
      ) {
        result = '関心';
        } else if(myHand == '疑問'
      ) {
        result = '疑問';
      } else if (myHand == '興味なし'
      ) {
        result = '興味なし';
      } else  (
        // その他のデータが来た場合
        result = '……'
       )
      // HTML上に結果を表示
      const message = `あなた「${myHand}」`;
      document.getElementById("result").textContent = message;
    }
    // 推論終了5秒後に自分の関数を実行（ループになる）
    setTimeout(loop, 5000);
  });
}

  // 最初の繰り返し処理を実行
  loop();
}
```

### Webアプリケーションから受領した判定結果をNode-REDで処理
[![Image from Gyazo](https://i.gyazo.com/725ec326cd2870d6ad1cc868db08357d.png)](https://gyazo.com/725ec326cd2870d6ad1cc868db08357d)

1. Webアプリケーションから`/receiver`に連携された判定結果を`http in`ノードで受領します。  
その際、Webアプリケーションのループ処理を継続させるため、`http response`ノードで返送します。
1. 連携された判定結果の名称を取得し、Ambientへ連携するデータ処理の分岐を実施します。
1. Ambientで受領できるデータ形式へ加工した後、チャネルIDとキーを設定した`Ambient`ノードで連携します。  
また、`function`ノードのコードは以下の通りです。

```ruby:function

var dvalue="Ambientへ連携する数値データ格納場所";

msg.payload={
        "d1":dvalue,
        "d2":0,
        "d3":0,
        "d4":0,
    };
    return msg;
```
なお、`d1`等のタグはAmbientに連携する項目に応じて変更ください。


### 取得した結果をAmbientのボードで表示
Ambientに情報が連携できたらチャネルのボード一覧より表示するグラフ等の設定をします。
今回はAIが感情を判定した際の確信度をメーター表示するようにしてみました。
[![Image from Gyazo](https://i.gyazo.com/cee21b8e105e461e23bc1a3481748dcc.png)](https://gyazo.com/cee21b8e105e461e23bc1a3481748dcc)



## 改善点

1. **感情の判定精度制度が低い**  
寝ぐせがついた状態だと判定が甘くなってしまいました。
Teachabl Machineに画像データを1,920個を投入したのですが、顔の角度や明るさ等が影響しているようです。
1. **Ambientに反映される感情が一種類に限定される**  
WebアプリケーションからNode-REDへ連携されるデータの順番が判定時の確信度別になるため、複数データの反映が出来ていません。条件分岐を再度見直す必要があります。

## 今後の方針
機械学習という手段を学んだことで今まで主観でしか判断できないと考えていた事象も検討の余地があると気づきました。

今回の改善点を踏まえ、画像判別の精度向上およびフローの見直しによる完成度の向上を目指すとともに、主観的な判断をしてきた事項で数値化できる基準が他に無いか見直していこうと思います。

閲覧ありがとうございました！