LoginSignup
11
5

More than 5 years have passed since last update.

TensorFlow.jsを使って手書き数字解析器を作る上で躓いたこととか

Last updated at Posted at 2018-04-09

この記事はここのコピーです。

今日はTensorFlow.jsという比較的新しくできた1 TensorFlowという機械学習に使えるライブラリのJavascript版を使ってみようということで機械学習分野のHelloWorldらしいMNISTを使っての手書き数字解析器を作りたいと思いました。

Github: https://github.com/CreativeGP/tensorflowjs-mnist/
Avaliable at: https://cretgp.com/lab/mnist/

制作の上ではこちらの記事を参考にさせていただきました。この記事はモデル作成からTensorFlow.jsを使ってWebアプリを作るところまでを幅広くカバーしているので同じような内容の記事が2つあってもデータの無駄、ということでこの記事はTensorFlow.jsを使う際に躓いたことに焦点を当てて書いています(先程の記事では大まかな手順だけ書かれていたところですね。

※なんかここから常体になってますが気にしないで下さい※

ImageDataの縮小

ユーザーから入力された200x200のCanvasの画像を推論にかけるには28x28に縮小しなければならない。CanvasからはImageDataというオブジェクトが取得してこれが各ピクセルの色データを保持している。

このやり方も情報が少なく、このページが唯一僕が見つけられるページだった。
ここのコメントにもあるようにこの回答は少し冗長なやり方をしているので改善したものをjsFiddleにうpしたので参考にしていただきたい。

入力用Tensorを作成

ImageDataはプロパティが全て読み取り専用だということで、つまりコンストラクタから生成しなければならないのだが、コンストラクタに指定できる配列のサイズが決まっていることと、型も決まっていることで扱いにくい。このような特性のせいで正規化をする際に小数の格納ができない。

なので最初にtf.fromPixels()を使ってTensorを生成しなければならないのだがここでハマった。

tf.fromPixels()はImageDataと1~4のチャンネル数をとる。チャンネル数が4の場合は

[255, 200, 0, 100, 50, 3, 80, 72, 40]:ImageData.data -> [[255, 200, 0, 100], [3, 80, 72, 40]]:Tensor

という風に色データが単純に4個ずつに区切られて入力される。この場合はモノクロなのでチャンネル数を1にするのだがこのときは、

[255, 200, 0, 100, 50, 3, 80, 72, 40]:ImageData.data -> [[255], [3]]:Tensor

という風に4個ずつに分解されたときの一番最初の要素だけを使ってTensorが構成される。しかし、Canvasから取得した色データはユーザーの入力が$4i + 3$番目に入っており、普通に渡したのでは期待する動作をしてくれないので、変換を行う必要があった2。行った変換は単純で

let monodata = [];
for (let i=0, len = imgdata.data.length/4; i < len; i += 1) {
    monodata.push(imgdata.data[i*4+3]);
    monodata.push(0);
    monodata.push(0);
    monodata.push(0);
}
let monoimgdata = new ImageData(new Uint8ClampedArray(monodata), 28, 28);

新たな配列を作ってそこにユーザーの入力データが$4i$番目に来るようにして後は0パディングするようにし、その後で新たなImageDataるようなものである。パディングはImageDataのコンストラクタに渡す配列の大きさは$width×height×4$のサイズしか許されなかった為必須である。

ようやくできたmonoimgdataをtf.fromPixelsに渡して、整形して、型を変換して3、正規化すれば4、入力用テンソルが完成する。

let input = tf.fromPixels(monoimgdata, 1).reshape([1, 28, 28, 1]).cast('float32').div(tf.scalar(255));

まとめ

作ってみた感想としては、なかなかいい制度ができにくいな、ってのが正直な感想です。僕が制作したのはWebのインターフェースのみなので改善のしようはアルゴリズムレベルでしかないとは思いますが特に「1」がひどいです。ぜひ試してみて下さい


  1. 一応TensorFlowのリポジトリは2017年8月くらいに初期コミットがされてますが、この親であるdeeplearn.jsはもっと前から開発がされているようです。 

  2. この変換はCanvasの塗る色の設定を変えることで不要になるかもしれない。 

  3. 理由は、この学習データを使った推論の入力Tensorはfloat32ではないといけないと怒られるということと、255で割ると小数が出てくるからということがある。 

  4. データは0~255の範囲にあるので255で割る。 

11
5
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
11
5