LoginSignup
4
0

More than 3 years have passed since last update.

Teachable Machine で出力した機械学習モデル(画像分類、TensorFlow.js用)を Node.js で動かすために公式情報をたどる&実際に試す

Last updated at Posted at 2021-04-18

タイトルの通りの内容で、以下の記事を書いた際にやった「ブラウザ上での実行」の部分を、「Node.js での実行」にしたものです。
Teachable Machine について等の話はこの記事では省略しますので、それらに関する情報をご覧になりたい方は以下の記事内のものをご覧ください。

●Teachable Machine で出力した機械学習モデル(画像分類、TensorFlow.js用)をダウンロードしてブラウザで動かす【2021年4月版】 - Qiita
 https://qiita.com/youtoy/items/3be502c6266cfb61e49a

まずは公式情報をたどる

冒頭に書いた記事では、Teachable Machine で出力した画像分類用の機械学習モデル(TensorFlow.js用)をローカルにダウンロードし、それを用いた推論をブラウザ上で行いました。JavaScript の処理を含む HTMLファイルを使った形です。

今回は、推論を行うプログラムを Node.js で実行します。そのやり方については、公式で掲載された FAQ の情報からたどって探していきます。

公式のよくある質問から情報をたどる

Teachable Machine のページの上部メニューを見ると、右のほうに「よくある質問」と書かれた部分があります。そしてその中を見ていくと「保存とエクスポート」と書かれた部分があり、さらにその中で「他のライブラリやプラットフォームでも Teachable Machine のモデルを使用できますか?」という質問がありました(※以下の画像の部分)。
Teachable_Machineのよくある質問.jpg

上記の画像中の文章で、「Teachable Machine コミュニティの Github リポジトリをご覧ください。」とあるので、その Github リポジトリのページへと進んでいきます。

Teachable Machine コミュニティの Github リポジトリ

上記の Github リポジトリを見ると、「Community Contributions and Projects」という項目の中で、以下の画像の記載を見つけました。
Node.js用のライブラリの話.jpg
この「Teachable Machine Node Library for image models」という部分をたどっていきます。

プロジェクトのページとその先

上記の続きで「tr7zw/teachablemachine-node-example」というページにたどり着きました。その説明を見ていこうとすると、以下の画像にあるように「最新版はまた別のページにある」という記載と誘導用のリンクが・・・。
別ページを参照するようにオススメ.jpg

上記のリンク先は「drinkspiller/teachablemachine-node-example」というページでした。さらにこの先へ誘導される、ということはなさそうです(笑)

このページで「How It Works」と書かれた部分を見てみます。
How It Works.jpg
リクエストに対して、JSON でレスポンスを返すような動きをしてくれそうな記載がありました。

サンプルを動かしてみる

上記の記載があった箇所の少し下を見ると、使い方などが書かれた部分がありました。
使い方等.jpg
その手順通りに進めてみます。

インストール

リポジトリの内容をクローンして、パッケージのインストールを行います。どこか適当なフォルダで、以下のコマンドを実行していきましょう。

# クローン
git clone https://github.com/drinkspiller/teachablemachine-node-example.git
# パッケージのインストール
cd teachablemachine-node-example/
npm install

自分の環境で実行したところ、以下のエラーが・・・。
npm installのエラー.jpg
少し調べてみると、自分が Node.js の v15 を使っているのが関係していそうで、Node.js を v14 にすると良さそうでした。Node.js のバージョン管理ツールを使っていたので、サクッと v14 に変更してから再度 npm install を実行して、その後は無事にインストールがエラーもなく完了となりました。

なお、今回の内容の package.json の中身を見てみると、dependencies は以下となっているようです。
dependencies.jpg

app.js の書きかえ

次の手順は、app.js の書きかえです。

以下の部分を見ると、モデルの URL の指定方法について書かれており、ファイルシステムのパスは利用不可と書いてあります。
URL指定についての説明.jpg
そして、デフォルトの設定として、事前に用意されたモデルの URL が別の行に書いてありました。具体的には以下の部分です。
書きかえをするURL.jpg
この部分を独自の機械学習モデル用の設定に置きかえます。

Teachable Machine の機械学習モデルをクラウドにアップロードするやり方をとっていた場合は、そのアップロード先の URL を上記の部分に書くだけで大丈夫です。
そのやり方がシンプルな方法ではありましたが、今回は、冒頭に書いた前の記事と同様に、ローカルに機械学習モデルの関連ファイル一式をダウンロードするやり方ができないかを試していきました。

手順が少なくてすみそうなやり方で進めてみます。適当にフォルダを作成し、その中にダウンロードした機械学習モデル関連のファイル一式を置きましょう(ここで「関連ファイル」と書いた部分の詳細は、冒頭に掲載した記事の「モデルのダウンロード」の部分をご参照ください)。
そして、そのファイル一式を置いたフォルダで、サーバーを立ち上げます。冒頭に掲載した記事の「機械学習モデルのファイルに関する準備をする(+ローカルでサーバーを動かす)」の部分にも書いている、ワンライナーでサーバーを立ち上げるコマンドを使うのが簡単かと思います。

以下にコマンドの部分のみ記載しますが、どれを使っても http://localhost:8080/ でローカルにサーバーが立ち上がります。

# 2.x系
$ python -m SimpleHTTPServer 8080
# 3.x系
$ python -m http.server 8080
# npmのバージョン5.2.0で導入された「npx」を利用
$ npx install http-server

このコマンドを機械学習モデル関連のファイルが置かれた場所で実行していれば、 http://localhost:8080/ にアクセスすることで、機械学習モデル関連のファイルを参照可能になります。app.js の中の URL を指定する部分は、以下のように書きかえれば OK です。

function configureEndPoints() {
  addEndpoint('test',
      'http://localhost:8080/');
}

addEndpoint('test', ... の部分は、この後の手順に関わってくる設定ですが、デフォルトのままで使っていきます。

以下に、ここで用いたソースコード全体を掲載します(サンプルをほぼそのまま使ったような感じですが)。

const canvas = require("canvas");
const express = require("express");
const JSDOM = require("jsdom").JSDOM;
require("@tensorflow/tfjs-node");
const tmImage = require("@teachablemachine/image");

const app = express();

function init() {
  configureBodyParser();
  configureEndPoints();
  configureBrowserPolyFills();

  app.listen(3000, () => {
    console.log("Server running on port 3000");
  });
}

async function addEndpoint(name, baseUrl) {
  const modelURL = baseUrl + "model.json";
  const metadataURL = baseUrl + "metadata.json";
  const model = await tmImage.load(modelURL, metadataURL);
  app.post("/" + name, (request, response) => {
    const base64Image = Buffer.from(request.body).toString("base64");
    const contentType = request.get("Content-Type");
    getPrediction(model, base64Image, contentType, (output) => {
      response.send(output);
    });
  });
}

function configureBodyParser() {
  app.use(require("body-parser").raw({ type: "image/jpeg", limit: "3MB" }));
}

function configureBrowserPolyFills() {
  global.window = new JSDOM(`
  <body>
    <script>
    document.body.appendChild(document.createElement("hr"));
    </script>
  </body>`).window;
  global.document = window.document;
  global.fetch = require("node-fetch");
  global.HTMLVideoElement = class HTMLVideoElement {};
}

function configureEndPoints() {
  addEndpoint("test", "http://localhost:8080/");
}

async function getPrediction(model, imageData, contentType, responseFunction) {
  const imageCanvas = canvas.createCanvas(64, 64);
  const canvasContext = imageCanvas.getContext("2d");

  const canvasImage = new canvas.Image();
  canvasImage.onload = async () => {
    canvasContext.drawImage(canvasImage, 0, 0, 64, 64);

    const prediction = await model.predict(imageCanvas);
    console.log(prediction);
    responseFunction(prediction);
  };

  canvasImage.onerror = (error) => {
    throw error;
  };

  canvasImage.src = `data:${contentType};base64,` + imageData;
}

init();

Node.js でプログラムを実行

ここまで準備ができたら、app.js を実行します。 node app.js を実行すると以下のような出力がされて、先ほどローカルで実行したサーバーとは別のサーバーが待ち受け状態になります(こちらのサーバーのポート番号は、app.js のデフォルト設定を変更せずにいた場合 3000番になります)。
app.jsの実行.jpg

画像をポストして結果を得る

あとは、先ほどの app.js を実行して立ち上がったサーバーのほうに、画像を POST で送信すれば OK です。今回参照している GitHub のリポジトリのページでは、curl を利用した方法のコマンドの例が掲載されています。
POSTの例.jpg

自分は Mac で試しているので、上記の上側のコマンド例を利用しました。とりあえず動作確認ができれば良かったので、そのコマンド(※以下)をそのまま流用します。

以下のコマンドで、POST する画像のパスが '@./sample_images/person-using-iphone-1194760.jpg' となっていますが、これは今回クローンしたリポジトリのファイル・フォルダ一式の中で、app.js と同じ階層にあるフォルダの中に置かれた画像の 1つを指定しているようです。

$ curl -X POST -H "Content-Type: image/jpeg" --data-binary '@./sample_images/person-using-iphone-1194760.jpg' http://localhost:3000/test

リポジトリのほうで具体的に見ていくと、 teachablemachine-node-example/sample_images/ の中にある画像のうちの 1つです。
sample_images.jpg

これをそのまま流用するために、上記の curlコマンドを実行する場所は app.js と同じ階層にします。これで既に用意されている画像を流用して POST を実行できそうです。試してみた際の出力は、以下となりました。
POSTした結果.jpg

上記のとおり、レスポンスとして推論の結果が JSON形式で得られました。
今回使ったモデルは「Class 1、Class 2」という、Teachable Machine のデフォルト設定のクラス名を使った 2つのクラスを用意していたのですが、出力ではそれらのラベル対する confidence の値が出力されたのが確認できたと思います。

終わりに

今回、Teachable Machine で出力した画像分類用の機械学習モデル(TensorFlow.js用)を Node.js で動かすことができました。

個人的には「もう少しだけ手順をシンプルにできないかな」と思う部分があるので、改善の余地がないかをもう少し見ていこうと思っています。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0