Deep Learning with Javascriptの勉強ノート

jupter notebookでjavascriptを使いたいと思っていたところ、以下のnotebookを見つけました。

この方法のポイントは、以下の通りです。

  • jupyter notebookのタイプをjavascriptにプリセット
  • javascriptカーネルをインストールする以下のセルを追加

以下にDeep Learning with Javascriptの2章の例題をGoogle Colabで実行してみます。 例題は、以下のGithubにありました。

準備(javascriptカーネルのインストール)

以下のセルを実行して、本ノートブックの再ロード(F5キーを押下)してください。

一度カーネル環境が作成された後(Google Colabのセッションが有効な間)は実行する必要はありません。

In [0]:
!npm install -g [email protected]
!npm cache verify
!npm install -g --unsafe-perm ijavascript
!ijsinstall --install=global
!jupyter-kernelspec list
ERROR! Session/line number was not unique in database. History logging moved to new session 59
/tools/node/bin/npm -> /tools/node/lib/node_modules/npm/bin/npm-cli.js
/tools/node/bin/npx -> /tools/node/lib/node_modules/npm/bin/npx-cli.js
+ [email protected]
added 325 packages from 161 contributors, removed 423 packages and updated 59 packages in 10.505s
Cache verified and compressed (~/.npm/_cacache):
Content verified: 78 (21053000 bytes)
Index entries: 79
Finished in 0.705s
/tools/node/bin/ijs -> /tools/node/lib/node_modules/ijavascript/bin/ijavascript.js
/tools/node/bin/ijsconsole -> /tools/node/lib/node_modules/ijavascript/bin/ijsconsole.js
/tools/node/bin/ijsinstall -> /tools/node/lib/node_modules/ijavascript/bin/ijsinstall.js
/tools/node/bin/ijskernel -> /tools/node/lib/node_modules/ijavascript/lib/kernel.js
/tools/node/bin/ijsnotebook -> /tools/node/lib/node_modules/ijavascript/bin/ijsnotebook.js

> [email protected] install /tools/node/lib/node_modules/ijavascript/node_modules/zeromq
> node scripts/prebuild-install.js || (node scripts/preinstall.js && node-gyp rebuild)



+ [email protected]
added 67 packages from 65 contributors in 4.07s

以降のセルからjavascriptが使えるようになります。

便利なユーティティ関数の定義

注意事項 constとlet文は使わず、varを使ってください

セル内でコマンドを実行するsh関数を定義します。

In [0]:
var { spawn } = require('child_process')
var sh = (cmd) => { 
    $$.async()
  var sp = spawn(cmd, { cwd: process.cwd(), stdio: 'pipe', shell: true, encoding: 'utf-8' })
  sp.stdout.on('data', data => console.log(data.toString()))
  sp.stderr.on('data', data => console.error(data.toString()))
  sp.on('close', () => $$.done())
}
var run_async = async (pf) => {
  $$.async()
  await pf()
  $$.done()
}
sh('npm init -y')
Wrote to /content/package.json:

{
  "name": "content",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}



2章

必要なライブラリのインストール

GPU版のTensorflowJSをインストールします。

  • tensorflow(GPU版)
  • plotly(notebook版)
In [35]:
sh('npm install @tensorflow/tfjs-node')
sh('npm install plotly-notebook-js')
npm
 WARN [email protected] No description

npm WARN [email protected] No repository field.


+ [email protected]
added 1 package from 1 contributor and audited 167 packages in 1.071s


1 package is looking for funding
  run `npm fund` for details


found 0 vulnerabilities


データの準備

以下のセルの実行でエラーになった場合には、ブラウザーのF5(再読み込み)を実行してもう一度試してみまてください。

In [19]:
var tf = require('@tensorflow/tfjs-node')
console.log(tf.backend().isUsingGpuDevice)
node-pre-gyp info This Node instance does not support builds for N-API version 4
node-pre-gyp info This Node instance does not support builds for N-API version 5
node-pre-gyp info This Node instance does not support builds for N-API version 4
node-pre-gyp info This Node instance does not support builds for N-API version 5
false
(node:247) Warning: N-API is an experimental feature and could change at any time.

テストデータとして、ファイルサイズとダウンドード時間の観測データをtrainData, testDataにセットします。

In [0]:
var trainData = {
  sizeMB: [0.080, 9.000, 0.001, 0.100, 8.000, 5.000, 0.100, 6.000, 0.050, 0.500,
    0.002, 2.000, 0.005, 10.00, 0.010, 7.000, 6.000, 5.000, 1.000, 1.000],
  timeSec: [0.135, 0.739, 0.067, 0.126, 0.646, 0.435, 0.069, 0.497, 0.068, 0.116,
    0.070, 0.289, 0.076, 0.744, 0.083, 0.560, 0.480, 0.399, 0.153, 0.149]
}
var testData = {
  sizeMB: [5.000, 0.200, 0.001, 9.000, 0.002, 0.020, 0.008, 4.000, 0.001, 1.000,
    0.005, 0.080, 0.800, 0.200, 0.050, 7.000, 0.005, 0.002, 8.000, 0.008],
  timeSec: [0.425, 0.098, 0.052, 0.686, 0.066, 0.078, 0.070, 0.375, 0.058, 0.136,
    0.052, 0.063, 0.183, 0.087, 0.066, 0.558, 0.066, 0.068, 0.610, 0.057]
}

tf.tensor2d関数を使って2次元テンソルに変換します。[20,1]はデータの次数(shape)を指定します。

In [0]:
var trainTensors = {
  sizeMB: tf.tensor2d(trainData.sizeMB, [20, 1]),
  timeSec: tf.tensor2d(trainData.timeSec, [20, 1])
}
var testTensors = {
  sizeMB: tf.tensor2d(testData.sizeMB, [20, 1]),
  timeSec: tf.tensor2d(testData.timeSec, [20, 1])  
}

データの可視化

trainDataとtestDataの分布をPlotlyライブラリを使って可視化(プロット)します。

plotlyに渡すデータ構造にするために、dataTraceTrain, dataTraceTestに必要な情報をセットします。

  • x: x軸要素
  • y: y軸要素
  • name: 凡例で使用する名称
  • mode: プロット方法で、ポイントをマークで表示(markers)を指定
  • type: プロットタイプで、散布図(scatter)を指定
  • marker: マークの種別を指定
In [0]:
var dataTraceTrain = {
  x: trainData.sizeMB,
  y: trainData.timeSec,
  name: 'trainData',
  mode: 'markers',
  type: 'scatter',
  marker: {symbol: "circle", size: 8}
};
var dataTraceTest = {
  x: testData.sizeMB,
  y: testData.timeSec,
  name: 'testData',
  mode: 'markers',
  type: 'scatter',
  marker: {symbol: "triangle-up", size: 10}
};
In [38]:
var Plot = require('plotly-notebook-js')
var NotebookPlot = Plot.createPlot().constructor
NotebookPlot.prototype._toHtml = NotebookPlot.prototype.render

Plot.createPlot([dataTraceTrain, dataTraceTest], {
  width: 700,
  title: 'File download duration',
  xaxis: {
     title: 'size (MB)'
   },
  yaxis: {
    title: 'time (sec)'
  }
})
Out[38]:

TensorflowJSを使った線形回帰

ニューラルネットワークの層を作成します。ここでは入力として1個の要素を持つ入れる配列を持つ、密結合の層(dense)を追加します。unitsで出力値が1個であると設定します。

オプティマイザーとして'sgd'(確率的勾配降下法) , 'meanAbsoluteError'(損失関数として絶対値の平均)を指定して、modelをコンパイルします。

In [0]:
var model = tf.sequential();
model.add(tf.layers.dense({
  units: 1,
  inputShape: [1],
}))
model.compile({ optimizer: 'sgd', loss: 'meanAbsoluteError' })

回帰で求めるのは、転送時間(timeSec)で、入力としてファイルサイズ(sizeMB)を与えます。

回帰分析ではニューラルネットの重みmと切片bで計算されます。 $$ timeSec = kernel * sizeMB + bias $$

setWeights関数で重みkの初期値と切片bの初期値をここでは0として開始します。

In [0]:
var k = 0
var b = 0
model.setWeights([tf.tensor2d([k], [1, 1]), tf.tensor1d([b])])

kerasと同様にfit関数で回帰を実行します。 出力のlossがEpoch毎に小さくなって収束しているのがわかります。

In [125]:
model.fit(trainTensors.sizeMB, trainTensors.timeSec, {epochs: 10})
Epoch 1 / 10

7ms 330us/step - loss=0.295 
Epoch 2 / 10

8ms 377us/step - loss=0.192 
Epoch 3 / 10

7ms 329us/step - loss=0.0899 
Epoch 4 / 10

6ms 307us/step - loss=0.0725 
Epoch 5 / 10

8ms 385us/step - loss=0.0632 
Epoch 6 / 10

6ms 321us/step - loss=0.0825 
Epoch 7 / 10

9ms 453us/step - loss=0.0370 
Epoch 8 / 10

8ms 386us/step - loss=0.0671 
Epoch 9 / 10

8ms 403us/step - loss=0.0411 
Epoch 10 / 10

8ms 410us/step - loss=0.0798 
Out[125]:
History {
  validationData: null,
  params: 
   { epochs: 10,
     initialEpoch: 0,
     samples: 20,
     steps: null,
     batchSize: 32,
     verbose: 1,
     doValidation: false,
     metrics: [ 'loss' ] },
  epoch: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
  history: 
   { loss: 
      [ 0.2950499951839447,
        0.19248802959918976,
        0.08992607891559601,
        0.0724872499704361,
        0.06317649036645889,
        0.08248651027679443,
        0.03697093203663826,
        0.06710976362228394,
        0.0411013700067997,
        0.07980944216251373 ] } }

学習したニューラルネットのモデルを表示するために、testTensorsで評価してみましょう。 0.013と小さな誤差にまとまっているみたいです。

In [127]:
model.evaluate(testTensors.sizeMB, testTensors.timeSec).print()
Tensor
    0.01322927325963974

新しいファイルサイズ(7.8)に対するダウンロード時間は、7.8を2重に鍵かっこ([])で括ってtensor2dに渡してpredict関数で計算することができます。グラフと比較すると0.59とよい値を予測していることがわかります。

In [129]:
model.predict(tf.tensor2d([[7.8]])).print()
Tensor
     [[0.5941021],]

回帰で求まったk, bの値をみてみましょう。

In [131]:
k = model.getWeights()[0].dataSync()[0]
b = model.getWeights()[1].dataSync()[0];
console.log(k, b)
0.06809000670909882 0.06300000101327896

回帰直線のプロット

kとbの値が求まったので、データと回帰直線を一緒にプロットしてみましょう。

In [138]:
var traceLine = {
  x: [0, 10],
  y: [b, b + (k * 10)],
  name: 'trace line',
  mode: 'lines'
}

Plot.createPlot([dataTraceTrain, dataTraceTest, traceLine], {
  width: 700,
  title: 'File download duration',
  xaxis: {
     title: 'size (MB)'
   },
  yaxis: {
    title: 'time (sec)'
  }
})
Out[138]:
In [0]: