javascriptでTensorFlowモデルを使えるようにするTensorFlow.jsの基本的な使い方をまとめます
公式チュートリアル
https://www.tensorflow.org/js/tutorials
tensorflowjs APIドキュメント
https://js.tensorflow.org/api/latest/
実装例
https://github.com/tensorflow/tfjs-examples
試行環境
Windows10
Node.js版とブラウザ版
tensorflowjsはC++で書かれたtensorflowのwrapperですが、クライアントサイドのブラウザで動作するjavascript版とサーバーサイドで動作するNode.js版があります。Node.js版はローカル環境に最適化された追加ライブラリで高速に動作しますしCUDAを使ってGPUもフルに使えるようになっているんだそうです。ブラウザ版はCPUのみで比較してもNode.jsと比べると速度は劣りますがクライアントのリソースを使えるのが大きなメリットです。
pandasが使えるpython版と比べるとNode.js版でも使い勝手は悪いので、pythonでモデルを作ってコンバートしたものをNode.jsをバックエンドとする環境でNode.js上で使うか、サーバーサイドの負荷を低減するためにブラウザ上のjavascriptで使うというやり方が多くなるのではないかと思います。
python版tensorflowで作ったmodelを変換する
1. python版のモデルを用意する
model.save()で保存したh5ファイルを変換することが出来ます。
とりあえず入力1次元、出力1次元の回帰モデルを作って保存してみます。
import numpy as np
import pandas as pd
import tensorflow as tf
physical_devices = tf.config.list_physical_devices('GPU')
if len(physical_devices) > 0:
tf.config.experimental.set_memory_growth(physical_devices[0], True)
print('memory growth:', tf.config.experimental.get_memory_growth(physical_devices[0]))
x = np.array([1,2,3,4]).reshape(4, 1)
y = np.array([1,3,5,7]).reshape(4, 1)
inputs = tf.keras.layers.Input(shape=(1))
outputs = tf.keras.layers.Dense(1, activation='linear')(inputs)
model = tf.keras.models.Model(inputs=inputs, outputs=outputs)
opt = tf.optimizers.Adam(lr=0.1)
model.compile(optimizer=opt, loss='mse')
model.fit(x, y, epochs=100, batch_size=1, verbose=0)
print(model.predict(x))
model.save('../model.h5')
pd.DataFrame(x).to_csv('../x.csv')
2. モデルを変換するtensorflowjsパッケージをインストールする
pythonパッケージのtensorflowjsをpip installして使用します。ただし2020年2月現在、tensorflow 2.0.1~2.1.0とは依存パッケージが競合してしまいますので、tensorflowjs用のconda環境を作って専用にした方が良いと思います。なお、tensorflow 2.1.0で作ったモデルも現状は問題なく変換できるようです。
pip install tensorflowjs
3. モデルを変換する
tensorflowjsをインストールしたpythonにPATHが通った環境で、tensorflowjs_converterを使って変換します。以下の場合、カレントディレクトリのhoge.h5ファイルを読み込んで、/model/以下にモデル構造を記述したjsonファイルと重みを記録したbinファイルを書き出してくれます。
tensorflowjs_converter --input_format keras hoge.h5 model
ブラウザ版tensorflowjsで使う
tf.loadLayersModel()でjsonファイルのPATHを指定してやると保存したモデルを読んで、tf.tensorを放り込むと推定してくれます。ただし、ブラウザ上で動作するjavascriptはローカルのファイルを読めなくなっているので、面倒ですが適当なウェブサーバーを立ててドキュメントルート以下に上記で変換したモデルをフォルダごと置いてブラウザでURLを読み込むようにします。
└─document root
│ index.html
│
├─model
model.json
group1-shard1of1.bin
<!DOCTYPE html>
<html lang="jp">
<head>
<meta charset="utf-8">
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.0/dist/tf.min.js"></script>
<script>
async function run(){
// load model
const path = "http://localhost/model/model.json";
const model = await tf.loadLayersModel(path);
// predict
const xs = tf.tensor2d([1, 2, 3, 4], [4, 1]);
y_pred = await model.predict(xs);
y_pred.print();
// convert to array
const values = await y_pred.data();
const arr = await Array.from(values);
console.log(arr);
}
run();
</script>
</body>
</html>
Node.js版で使う
1. Node.jsをインストールする
Node.jsがまだ入ってないようならインストールします
https://nodejs.org/ja/
インストールするとPATHが通ってコマンドプロンプトから呼べるようになります
> node -v
v12.14.1
> npm -v
6.13.4
2. Node.jsにプロジェクトを作る
NodeJS環境を初期化します。適当なフォルダを作ってフォルダ内でnpm initすると必要なファイルが作成されます。package nameなどを聞かれますがすべてdefaultで大丈夫です。最後にIs this OK?にyesと回答すれば実行されます。
mkdir tensorflow-demo
cd tensorflow-demo/
npm init
3. Node.jsプロジェクトにtensorflowjsをインストールする
プロジェクトのディレクトリで以下を実行すればインストールされます。tfjs-nodeはNode.jsに最適化したライブラリで、ブラウザ版で実行するより高速に動作します。
npm install @tensorflow/tfjs-node
GPU+CUDAがある環境ではnpm install @tensorflow/tfjs-node-gpuすればGPUを使った高速演算ができると書かれているんですが、CUDA10.0をインストールしたWin10でやってみたらMODULE_NOT_FOUNDで動きませんでした。多分PATHが通ってないとかだと思いますが未確認です。
4. Node.jsでpython版modelを使う
tf.loadLayersModel()でjsonファイルのPATHを指定してやると保存したモデルを読んで、tf.tensorを放り込むと推定してくれます。ブラウザ上で動作する場合とは異なりウェブサーバーを立てる必要はありません。
└─project folder
│ index.js
│
├─model
model.json
group1-shard1of1.bin
// import
const tf = require('@tensorflow/tfjs');
require('@tensorflow/tfjs-node');
async function run(){
// load model
const path = "http://localhost/tensorflowjs/tfjs_001/model/model.json"
const model = await tf.loadLayersModel(path);
// predict
y_pred = await model.predict(tf.tensor2d([1, 2, 3, 4], [4, 1]));
y_pred.print();
// convert to array
const values = await y_pred.data();
const arr = await Array.from(values);
console.log(arr);
}
run();
index.jsと同じディレクトリからindex.jsを実行するとデモコードが動きます。
node index.js
Tensor
[[0.9999999],
[3 ],
[5.0000005],
[7.0000005]]
[ 0.9999998807907104, 3, 5.000000476837158, 7.000000476837158 ]
推定結果が表示されれば正常です。
tensorの計算
python版のtensorflowは適当に配列を放り込めばlistだろうがnp.arrayだろうが適当に処理してくれましたが、tensorflowjsではtf.tensor型に変換してからでないと扱えません。tf.tensorにするとjavascript標準の四則演算は出来なくなりますが、tf.add()やtf.sub()などで高速な演算が出来るようになっていますので不足はないと思われます。
四則演算以外にも三角関数とか最大最小とか色々計算できます。
詳しくは以下のAPIドキュメントを参照してください。
https://js.tensorflow.org/api/latest/
const x = tf.tensor1d([1,2,3])
const a = tf.scalar(4)
x.add(a).print()
x.sub(a).print()
x.mul(a).print()
x.div(a).print()
tensor同士の計算も同じようにできます。
const x1 = tf.tensor1d([1,2,3])
const x2 = tf.tensor1d([2,3,4])
console.log('add')
x1.add(x2).print();
console.log('sub')
x1.sub(x2).print();
console.log('mul')
x1.mul(x2).print();
console.log('div')
x1.div(x2).print();
tf.tensorを配列に戻すにはdataSync()メソッドを使います。
const b = tf.tensor1d([1,2,3])
console.log('b')
b.print()
const b_value = b.dataSync();
console.log('b_value')
console.log(b_value)
メモリの解放
tf.dispose()
openGLでtensorflowjsを使う場合、明示的に変数を消去しないとメモリが解放されない為、明示的にメモリを解放するためにtf.dispose()が用意されています。tf.memory()でメモリが正常にリリースされたか確認できるようになっていますので、tf.dispose()した場合としない場合を比較してみましょう。
for (let i = 0; i < 100; i++) {
const a = tf.tensor1d([1,2,3]);
}
console.log(tf.memory());
for (let i = 0; i < 100; i++) {
const a = tf.tensor1d([1,2,3]);
a.dispose()
}
console.log(tf.memory());
上記をそれぞれ実行するとtf.dispose()を使った場合にはtensorflowjsが保持するメモリがゼロになっている事が確認できます。
tf.tidy()
複数の演算を続けて行った場合に計算途中で生じる変数も残ってしまう為、tf.dity()でこれが残らないように処理するようです。
// y = 2 ^ 2 + 1
const y = tf.tidy(() => {
// a, b, and one will be cleaned up when the tidy ends.
const one = tf.scalar(1);
const a = tf.scalar(2);
const b = a.square();
console.log('numTensors (in tidy): ' + tf.memory().numTensors);
// The value returned inside the tidy function will return
// through the tidy, in this case to the variable y.
return b.add(one);
});
console.log('numTensors (outside tidy): ' + tf.memory().numTensors);
y.print();
CSVファイルの読み込み
CSVファイルはtf.data.csv(source)で読み込めます。sourceにPATHを指定して読み込みますが、ブラウザ上で動作するjavascriptはhttpで指定されたPATHしか読めないようになっていますので、ローカルでテストする際にもhttpサーバーに置いて実行する必要があります。Node.jsの場合はそういう制限はないので普通にローカルで実行できます。
take()メソットでデータの頭出し、toArray()メソッドで配列への変換ができます。
const df = tf.data.csv("http://localhost/hoge.csv")
console.log(df.take(10).toArray())
グラフを描く
javascriptにはpython勢の大好きなmatplotlibがないんですが、ありがたいことに可視化の為のメソッドを用意してくれています。tfvisをインポートすれば簡単にグラフを描画できます。
tfvis APIドキュメント
https://js.tensorflow.org/api_vis/latest/
Node.jsサーバーを立てる
ブラウザ版のテストをする際、Node.jsを使えば簡単にhttpサーバーを立てることができます
npm install http-server -g
http-server -g