Teachable Machine で出力した機械学習モデル（画像分類、TensorFlow.js用）をダウンロードしてブラウザで動かす【2021年4月版】

内容は表題の通りです。

【追記】 その後、Node.js で実行する話も試して記事にしました。
　●Teachable Machine で出力した機械学習モデル（画像分類、TensorFlow.js用）を Node.js で動かすために公式情報をたどる＆実際に試す - Qiita
　　https://qiita.com/youtoy/items/e47fe723771b308c486e

## Teachable Machine について
「[Teachable Machine](https://teachablemachine.withgoogle.com/)」 はブラウザ上で画像・音・姿勢推定の機械学習が行える仕組みで、ブラウザ上で学習を行えるだけでなく、そのままブラウザ上で推論を実行してみることができたり、機械学習モデルをファイルとして出力できたり（また、機械学習モデルを別途利用できるようクラウドにアップロードできたり）もします。
ちなみに、Googleさんが提供しているものです。
![Teachable Machine.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/9fa84931-91c3-8f73-4d23-3f3fcfe1002e.jpeg)

扱える種類は、2021年4月17日時点では「画像・音の分類、姿勢推定」の 3つです。
![3種類のプロジェクト.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f122e509-954e-29f4-a344-ab78a711cc88.jpeg)

## 過去に Teachable Machine 関連でやったこと
自分は以前も Teachable Machine を使ったことがあり、その際に例えば以下の記事などを書いたりしました。

- [toio を音で制御してみた（Audio用の Teachable Machine でベルやタンバリンの音を機械学習） - Qiita](https://qiita.com/youtoy/items/37f70bb4ce630e6cbd92)
- [独自拡張版 Scratch で Teachable Machine拡張を使った音（異なる音階）でのロボットカー操作 - Qiita](https://qiita.com/youtoy/items/d67ba4e6e457b037068b)
- [Node-RED で Teachable Machine を試す（Node-RED のセットアップ後にノードを追加してサンプルを動かす） - Qiita](https://qiita.com/youtoy/items/102c9ab8b5f25d542056)

今回扱うのは、3種類の中の画像分類で、実行環境は Webブラウザになります。

## 今回の記事のメインの内容
### ブラウザ上での学習
この記事では、画像分類の学習の過程は省略します。
公式のチュートリアルや、既にいろいろ公開されている Web上の記事でやり方が解説されているので、そちらをご参照ください（例えば以下など）。

●Google Teachable MachineでAIを体験してみよう | 子ども向けプログラミング学習サイトならメクルン[Mekurun]
　https://mekurun.com/tips/teachablemachine/
●Google Teachable Machine で画像認識｜miine｜note
　https://note.com/y_miine/n/n617a3a793c78

### モデルのダウンロード
ここでは、画像の学習が終わった後である、という前提で話を進めます。

機械学習モデルの利用方法について、「クラウドにアップロードしてアップロード先の URL を参照して使う方法」と「ファイルとしてダウンロードして自分がホスティングする方法」の 2つがありますが、今回は後者のほうのファイルをダウンロードするやり方で進めていきます。

Teachable Machine の学習等を行うページで、そのページの画面右上に以下の画像で示した部分があるので、赤枠で示した「モデルをエクスポートする」というボタンを押します。
![モデルのエクスポートボタン.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/3ad5924f-7291-b502-05ef-8aa48d196485.jpeg)

その後の画面では、機械学習モデルの出力形式などが複数選べますが、今回は以下の赤枠で示した「TensorFlow.js」を選び、赤い下線で示した「ダウンロード」を選択して「モデルをダウンロード」と書かれたボタンを押してください。
![モデルのダウンロード.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/9a67c731-287b-99f1-5f3e-f66b81dd7174.jpeg)

そうすると ZIPファイルがダウンロードでき、その ZIPファイルを解凍すると以下のような複数のファイルが得られると思います。
![モデルのデータ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/733cf0ee-3538-3e62-3b38-7b4da6175cd3.jpeg)
これらのファイルを、この後で利用していきます。

### HTMLファイル（＋JavaScript）と機械学習モデルに関する準備する ＋α
#### HTMLファイル（＋JavaScript）
ブラウザで動かすために、HTMLファイル（※JavaScriptの処理が書かれたもの）を用意します。名前は「index.html」にしておくと後が少しだけ楽です。
この HTMLファイル を実行する際には、上の手順でダウンロードした機械学習モデルのファイルに関する準備も必要ですが、それは後で書きます。

以下は、Teachable Machine のモデルダウンロード時に表示されるサンプルを、そのまま使った形です（JavaScript のサンプルとして表示されていたほうの、タイトルを変えたくらいという内容）。この中の `const URL = "./my_model/";` という部分は、これ以降の手順に少し関わってきます。

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>画像分類</title>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.3.1/dist/tf.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@teachablemachine/image@0.8/dist/teachablemachine-image.min.js"></script>
  </head>
  <body>
    <h1>Teachable Machine Image Model</h1>
    <button type="button" onclick="init()">Start</button>
    <div id="webcam-container"></div>
    <div id="label-container"></div>
    <script type="text/javascript">
      const URL = "./my_model/";
      let model, webcam, labelContainer, maxPredictions;

      async function init() {
        const modelURL = URL + "model.json";
        const metadataURL = URL + "metadata.json";

        model = await tmImage.load(modelURL, metadataURL);
        maxPredictions = model.getTotalClasses();

        const flip = true;
        webcam = new tmImage.Webcam(200, 200, flip);
        await webcam.setup();
        await webcam.play();
        window.requestAnimationFrame(loop);

        document.getElementById("webcam-container").appendChild(webcam.canvas);
        labelContainer = document.getElementById("label-container");
        for (let i = 0; i < maxPredictions; i++) {
          labelContainer.appendChild(document.createElement("div"));
        }
      }

      async function loop() {
        webcam.update();
        await predict();
        window.requestAnimationFrame(loop);
      }

      async function predict() {
        const prediction = await model.predict(webcam.canvas);
        for (let i = 0; i < maxPredictions; i++) {
          const classPrediction =
            prediction[i].className +
            ": " +
            prediction[i].probability.toFixed(2);
          labelContainer.childNodes[i].innerHTML = classPrediction;
        }
      }
    </script>
  </body>
</html>
```

以下は、Teachable Machine のモデルダウンロード時に表示されるサンプルで、p5.js という記載で表示されていたサンプルです。

こちらはタイトル以外も少し変更をしていて、p5.min.js と ml5.min.js を記事執筆時点の CDN で指定できるものの最新版にしています。それに伴い、Teachable Machine のサンプルでは読み込むように指定されていた p5.dom.min.js は不要になりました（特定のバージョン以降から p5.min.js の中に取り込まれているようです）。
先ほどと同様、この中の `const URL = "./my_model/";` という部分は、これ以降の手順に少し関わってきます。

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>画像分類</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
    <script src="https://unpkg.com/ml5@0.6.1/dist/ml5.min.js"></script>
  </head>
  <body>
    <h1>画像分類</h1>
    <script>
      let classifier;
      let imageModelURL = "./my_model/";

      let video;
      let flippedVideo;
      let label = "";

      function preload() {
        classifier = ml5.imageClassifier(imageModelURL + "model.json");
      }

      function setup() {
        createCanvas(320, 260);
        video = createCapture(VIDEO);
        video.size(320, 240);
        video.hide();

        flippedVideo = ml5.flipImage(video);
        classifyVideo();
      }

      function draw() {
        background(0);
        image(flippedVideo, 0, 0);

        fill(255);
        textSize(16);
        textAlign(CENTER);
        text(label, width / 2, height - 4);
      }

      function classifyVideo() {
        flippedVideo = ml5.flipImage(video);
        classifier.classify(flippedVideo, gotResult);
        flippedVideo.remove();
      }

      function gotResult(error, results) {
        if (error) {
          console.error(error);
          return;
        }
        console.log(results[0]);
        label = results[0].label;
        classifyVideo();
      }
    </script>
  </body>
</html>
```

#### 機械学習モデルのファイルに関する準備をする（＋ローカルでサーバーを動かす）
ここで、機械学習モデルのファイルに関する準備します。利用するのは、前の手順で ZIPファイルでダウンロードし、その ZIPファイルを解凍して得られた複数のファイルです。
それらのファイルを「my_model」という名前のフォルダに入れて、上記の HTML ファイルと同じ階層に置いてください。

あとは「上の手順で準備した HTMLファイルをブラウザで開くだけ」と言いたいところですが、ファイルをそのままブラウザで開いてしまうと、2種類あったどちらの HTMLファイルでも CORS 関連のエラーが出てしまいます（ブラウザの開発者ツールのコンソールにエラーが表示されます）。
それに対する対応として、ローカルでサーバーを動かしましょう。

やり方は複数ありますが、もし Python が入っているならワンライナーでサーバーを実行できるコマンド（以下のもの）を利用するのが早いと思います。

```bash
# 2.x系
$ python -m SimpleHTTPServer 8080
# 3.x系
$ python -m http.server 8080
```

もし、Python以外を使う方法で Node.js を使うなら、例えば `npx install http-server` を実行するのが簡単かもしれません（もちろん、それ以外の方法を用いても問題はないです）。

HTMLファイル・「my_model」という名前のフォルダがある階層で上記を実行してから、ブラウザの URL に `http://localhost:8080/` を入力して HTMLファイルにアクセスしましょう。これは、HTMLファイルの名前を「index.html」にしている前提で書いていますが、違う名前にしている場合は、先ほどの URL の後にファイル名も指定してアクセスしてください。

2種類ある HTMLファイルのどちらも、ローカルにある機械学習モデルを読み込んで、推論の処理が行われるのを確認できると思います（※画面上での推論結果の表示方法は、それぞれ少し違いがあります）。

## 終わりに
今回、Teachable Machine で出力した画像分類用の機械学習モデルを、ブラウザ上で簡易に実行できる手順を試しました。

今後は、今回の内容に手を加えたり、その他には Node.js で動かす方法を試せれば、と思っています。