TensorFlow.JSで画像の認識（分類）を学習させてみた（初歩の初歩／その４）

#４、TensorFlow.JSで画像の認識（分類）を学習
　定番であるcifar-10を使用した画像認識の学習をTensorFlow.JSで作成しました。　今回も[PNG画像ファイル](https://pjreddie.com/projects/cifar-10-dataset-mirror/)を使用しましたので学習データの取り扱いは容易かと思います。　但し、データの読み込み及び学習には長時間を要しますが！　実際の動作は、ここ（[TensorFlow.JSで画像認識を学習させてみた／８中間層](http://hal322.html.xdomain.jp/TensorFlowJS/300_cifar_with_TensorFlowJS.html)）をクリックして下さい。　Windows 10の環境で「Firefox / Ver. 65.0.2 (64ビット)」及び 「Google Chrome / Ver. 73.0.3683.86 (64ビット)」で動作を確認しました。　また、Android 7.0でも動作しましたが、iOSでは上手く動作しませんでした。 
　本プログラムの作成手順は、前回の「TensorFlow.JSで手入力数字の認識を学習させてみた（初歩の初歩／その３）」と同様ですが次の通りです。

##４－１、準備
　今回も学習データ、検証データ合わせて６万のPNG画像ファイルがありますので、次にようなファイルリスト（Ａ列）とラベルデータ（Ｂ列）をセットにしたCSVファイルをEXCEL+VBAで作成しました。　なお、このCSVファイル内容を変更あるいは追加することで、任意の画像ファイルを学習データとすることが出来ます。

![cifar_File_List.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408707/676f9f9c-1ce6-d23a-939a-b142599289db.png)

##４－２、学習モデルの作成
　cifar-10の学習モデルについても色々なサイトで解説されていますのでここでは割愛します。　入力データはカラーのPNG画像ですので、３２(dw：横画素数)×３２(dh：縦画素数)×３（ＲＧＢの３原色）で１画像分のデータとなり、教師データは１０個のラベル（airplane, automobile, bird, cat, deer, dog, frog, horse, ship, truck）に対応したコード（例：airplaneは「1,0,0,0,0,0,0,0,0,0」、truckは「0,0,0,0,0,0,0,0,0,1」の配列）にしています。　このため、評価結果は各配列に対応したそれぞれの確率で出力されます。　以下に学習モデルを示しますが、各パラメータは最適化したものではありません。

```javascript:学習モデルの作成 
let model = tf.sequential();
  model.add(
    tf.layers.conv2d({
      inputShape: [dw, dh, 3],
      kernelSize: 5,
      filters: 16,
      activation: "relu"
    })
  );
  model.add(tf.layers.maxPooling2d({ poolSize: 2, strides: 2 }));
  model.add(tf.layers.conv2d({ kernelSize: 5, filters: 20, activation: "relu" }));
  model.add(tf.layers.maxPooling2d({ poolSize: 2, strides: 2 }));
  model.add(tf.layers.conv2d({ kernelSize: 5, filters: 20, activation: "relu" }));
  model.add(tf.layers.flatten({}));
  model.add(tf.layers.dense({ units: 64, activation: "relu" }));
  model.add(tf.layers.dense({ units: 10, activation: "softmax" }));
```

##４－３、学習用データ、検証用データの読込
　読込んだ画像データはCANVASに３２×３２画素で表示させ、ＲＧＢ毎に数値データ化してxTrainDataに格納しました。　教師データとしてのラベル・コードは、yTrainDataに格納しています。　また、検証用データ（xTestDataとyTestData）も同様に処理しました。

```javascript:学習用データ、検証用データの読込 
    var ctx_train = canvas_train.getContext('2d');
    var Train_img = new Image();
    Train_img.src = "./Data/cifar/train/" + Temp_file;
    Train_img.onload = function() {
	ctx_train.drawImage(Train_img, 0, 0, dw * ds, dh * ds);
    };
    for(z=0; z<=2; z++) {
	for(y=0; y<dh; y++) {
	  for(x=0; x<dw; x++) {
	    Temp_img = ctx_train.getImageData((x * ds), (y * ds), 1, 1);
	    xTrainData.push( Temp_img.data[z] / 255 );
	  }
	}
    }
	Temp_label = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0];
	if(Train_File_List[ Temp_num ][1] == Label[1]) {
	    Temp_label = [0, 1, 0, 0, 0, 0, 0, 0, 0, 0];
	} else if (Train_File_List[ Temp_num ][1] == Label[2]) {
	    Temp_label = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0];
	} else if (Train_File_List[ Temp_num ][1] == Label[3]) {
	    Temp_label = [0, 0, 0, 1, 0, 0, 0, 0, 0, 0];
	} else if (Train_File_List[ Temp_num ][1] == Label[4]) {
	    Temp_label = [0, 0, 0, 0, 1, 0, 0, 0, 0, 0];
	} else if (Train_File_List[ Temp_num ][1] == Label[5]) {
	    Temp_label = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0];
	} else if (Train_File_List[ Temp_num ][1] == Label[6]) {
	    Temp_label = [0, 0, 0, 0, 0, 0, 1, 0, 0, 0];
	} else if (Train_File_List[ Temp_num ][1] == Label[7]) {
	    Temp_label = [0, 0, 0, 0, 0, 0, 0, 1, 0, 0];
	} else if (Train_File_List[ Temp_num ][1] == Label[8]) {
	    Temp_label = [0, 0, 0, 0, 0, 0, 0, 0, 1, 0];
	} else if (Train_File_List[ Temp_num ][1] == Label[9]) {
	    Temp_label = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
	} else {
	    Temp_label = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0];
	}
	yTrainData.push(Temp_label);
```

##４－４、学習データと検証データをTensorFlow.JS用に変換
　画像データであるxTrainDataとxTestDataは tf.tensor4d（データ数、画像の横画素数、縦画素数、色数の４個）で、ラベルデータであるyTrainDataとyTestDataは tf.tensor2d（データ数、ラベル数の２個）でTensorFlow.JS用に変換しました。　今回の色数はＲＧＢの３となります。

```javascript:学習データと検証データをTensorFlow.JS用に変換 
  const xTrain = tf.tensor4d( xTrainData , [ (xTrainData.length/dw/dh/3), dw, dh, 3 ], 'float32' );
  const yTrain = tf.tensor2d( yTrainData, [ yTrainData.length, Label.length ], 'int32' );
  const xTest = tf.tensor4d( xTestData , [ (xTestData.length/dw/dh/3), dw, dh, 3 ], 'float32' );
  const yTest = tf.tensor2d( yTestData, [ yTestData.length, Label.length ], 'int32' );
```

##４－５、学習の実行
　作成した学習データ xTrain と yTrain を使用して学習させ、検証データとして xTest と yTest を指定しています。　なお、callbacksの中身は、途中経過の表示です。

```javascript:学習の実行 
// 学習過程の設定
  model.compile({
    loss: "categoricalCrossentropy",
    optimizer: tf.train.sgd( optimize ),
    metrics: ['accuracy']
  });

// 学習と学習経過をdspに格納
  const history = await model.fit( xTrain, yTrain, {
        batchSize: BatchSize,
	epochs: epochData,
	validationData:[ xTest, yTest ],
	shuffle: true,
	callbacks: {
	    onEpochEnd: async (epoch, logs) => {
		sampleData_x01.push(Train_count * 1.0 + epoch * 1.0);
		sampleData_y01.push(logs.loss);
		sampleData_z01.push(logs.val_loss);
		sampleData_x02.push(Train_count * 1.0 + epoch * 1.0);
		sampleData_y02.push(logs.acc);
		sampleData_z02.push(logs.val_acc);
		Learn_Chart_01();
		Learn_Chart_02();
		if(((epoch + 1) % 10) == 0) {
		    dsp = dsp + ("      " + (Train_count + epoch)).slice(-7) + " : " + (logs.loss + "000000000000").slice(0,12) + " / " + (logs.acc + "000000000000").slice(0,12) + "  /  " + (logs.val_loss + "000000000000").slice(0,12) + " / " + (logs.val_acc + "000000000000").slice(0,12) + "\n";
		    document.getElementById("dump").value = dsp;
		}
	    },
	}
  });
```

##４－６、評価用データの読込
　評価用データは、サンプルとして準備した幾つかの画像あるいはローカルPC等に保存されている任意の画像（任意のサイズ）が使用できます。　評価用画像は一旦大きめの画像と３２×３２のサイズの画像に表示された後、学習用画像と同一サイズ（３２×３２）の画像を使用して数値データ化しています。

```javascript:評価用データの読込 
    var EvData = [];
    for(z=0; z<=2; z++) {
      for(y=0; y<dh; y++) {
	for(x=0; x<dw; x++) {
	    Temp_img = ctx_Ev_32.getImageData(x, y, 1, 1);
	    EvData.push( Temp_img.data[z] / 255 );
	}
      }
    }
```

```javascript:評価用サンプルデータの表示
function Ev_data() {
    var Ev_image = new Array();
    Ev_image[0] = "sample_01.jpg";
    Ev_image[1] = "sample_02.jpg";
    Ev_image[2] = "sample_03.png";
    Ev_image[3] = "sample_04.png";
    Ev_image[4] = "sample_05.png";
    Ev_image[5] = "sample_06.jpg";
    Ev_image[6] = "sample_07.png";
    Ev_image[7] = "sample_08.png";
    Ev_image[8] = "sample_09.jpg";
    Ev_image[9] = "sample_10.png";
    var Temp_img_file = Ev_image[Math.floor(Math.random() *10)];
    var Temp_img = new Image();
    Temp_img.src = "./Data/" + Temp_img_file;
    Temp_img.onload = function() {
	ctx_Ev.drawImage(Temp_img, 0, 0, dw * ds_Ev, dh * ds_Ev);
	ctx_Ev_32.drawImage(Temp_img, 0, 0, dw, dh);
    }
}
```

##４－７、評価用データをTensorFlow.JS用に変換
　学習用データと同様に評価用データもTensorFlow.JS用に変換します。　評価用データ数は１個ですので、tf.tensor4dのパラメータもそれに合わせています。

```javascript:評価用データをTensorFlow.JS用に変換 
    const inputData = tf.tensor4d( EvData, [ 1, dw, dh, 3 ] );
```

##４－８、評価の実行
　resultsに１０個のラベルに対応した評価結果（それぞれの確率）が配列として格納されます。

```javascript:評価の実行 
    const outputData = model.predict(inputData);
// 評価結果の表示
    var results = outputData.dataSync();
    document.getElementById('result_00').value = (Label[0] + ": " + Math.floor(results[0] * 100000 ) / 1000 + " %");
    document.getElementById('result_01').value = (Label[1] + ": " + Math.floor(results[1] * 100000 ) / 1000 + " %");
    document.getElementById('result_02').value = (Label[2] + ": " + Math.floor(results[2] * 100000 ) / 1000 + " %");
    document.getElementById('result_03').value = (Label[3] + ": " + Math.floor(results[3] * 100000 ) / 1000 + " %");
    document.getElementById('result_04').value = (Label[4] + ": " + Math.floor(results[4] * 100000 ) / 1000 + " %");
    document.getElementById('result_05').value = (Label[5] + ": " + Math.floor(results[5] * 100000 ) / 1000 + " %");
    document.getElementById('result_06').value = (Label[6] + ": " + Math.floor(results[6] * 100000 ) / 1000 + " %");
    document.getElementById('result_07').value = (Label[7] + ": " + Math.floor(results[7] * 100000 ) / 1000 + " %");
    document.getElementById('result_08').value = (Label[8] + ": " + Math.floor(results[8] * 100000 ) / 1000 + " %");
    document.getElementById('result_09').value = (Label[9] + ": " + Math.floor(results[9] * 100000 ) / 1000 + " %");
```


　実際の動作は、ここ（[TensorFlow.JSで画像認識を学習させてみた／８中間層](http://hal322.html.xdomain.jp/TensorFlowJS/300_cifar_with_TensorFlowJS.html)）をクリックして下さい。


***
###TensorFlow.JSを使用してみたの記事内容
１、[TensorFlow.JSで簡単な関数を学習させてみた](https://qiita.com/halboujp/items/34771a814b41e36245d4)
２、[TensorFlow.JSでアヤメの分類を学習させてみた](https://qiita.com/halboujp/items/e224f0ff0cb5b9784d95)
３、[TensorFlow.JSで手入力数字の認識を学習させてみた](https://qiita.com/halboujp/items/075d74a08c4144cd79cd)
４、TensorFlow.JSで画像の認識（分類）を学習させてみた


ここに記載したHTML/JavaScriptの動作を確認したい方は、[「http://hal322.html.xdomain.jp/」](http://hal322.html.xdomain.jp/)を参照ください。