TensorFlow2 + Keras による画像分類に挑戦1 ～とりあえず動かす～

# はじめに

TensorFlow2 + Keras を利用した画像分類（Google Colaboratory 環境）についての勉強メモ（第1弾）です。題材は、ド定番である手書き数字画像（**MNIST**）の分類です。

- TensorFlow2 + Keras による画像分類に挑戦 シリーズ
  - [1. とりあえず動かす](https://qiita.com/code0327/items/7d3c7bd3327ff049243a)
  - [2. 入力データを詳しくみてみる](https://qiita.com/code0327/items/2c969ca4675d5a3691ef)
  - [3. MNISTデータを可視化してみる](https://qiita.com/code0327/items/a6edd5b27256429c3819)
  - [4. 学習済みモデルで予測させてみる](https://qiita.com/code0327/items/1047adc050ab6d75ad5c)
  - [5. 分類に失敗する画像を観察してみる](https://qiita.com/code0327/items/5dfc1b2ed143c1f9bd2b)
  - [6. 自分で用意した画像の前処理・分類をしてみる](https://qiita.com/code0327/items/d1a6b63915871a17020a)
  - [7. 層タイプ・活性化関数について理解する](https://qiita.com/code0327/items/68dd7beb1196320c8c15)
  - [8. 最適化アルゴリズムと損失関数を選択する](https://qiita.com/code0327/items/4f8f656bed23140b8962)
  - [9. モデルの学習、セーブ＆ロードをしてみる](https://qiita.com/code0327/items/951f418c81dbbd65c1fc)

具体的には、次のような**「0」から「9」までの手書き文字を取り込んだ画像**（28x28pixel）を対象に、
![MNIST-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/69a6a105-a530-a267-e7e3-02ed7a1a0529.png)
それぞれの画像が**「0」から「9」のどれに分類できるか？**という問題（＝多クラス分類問題）について、TensorFlow2 + Keras によるディープラーニング（深層学習）でアプローチしてみようという内容です。

開発・実行の環境には、簡単・便利で無料の Google Colabo. を利用します。Google Colabo. の導入については、[こちら](https://qiita.com/code0327/items/4e5c4dbf1185e617a3e3) を参照ください。

今回の記事では、TensorFlow の [公式HP](https://www.tensorflow.org/?hl=ja) に掲載されているサンプルコードをコピーしてきて、Google Colab. のコードセルに貼り付け、問題なく実行できることを確認します。

そのうえで「**コードの各部分では何をやっているのか**」「**実行時に表示されるテキストは何を伝えているのか**」を緩くぼんやりと解説しています。

## TensorFlowとは

- 「テンソルフロー」または「テンサーフロー」と読む。
- Googleが開発した機械学習ライブラリで、ニューラルネットワーク（NN）の構築とトレーニング（＝学習／訓練）ができる。無論、トレーニングしたNNモデルを使って予測もできる。
- 2017年2月に 1.0 がリリース、2019年10月に 2.0 がリリースされた。
- TF2.0 では、Keras（後述）を統合して Pythonとの親和性が高まり、より使いやすく洗練されたものとなった（とのこと）。GPU対応も強化された（とのこと）。
  - PyTorch などの後発の機械学習ライブラリ勢力に負けないように開発が続いている。

## Keras

- 「ケラス」と読む。
- TensorFlow のほか、Theano にも対応したハイレベルのAPI。ラッパー。
- Python で書かれている。
- Keras 経由で TF を使うことで、簡潔で短いコードにより機械学習が実現できる。

# サンプルコードを試す

TensorFlowの公式HPの「[初心者のための TensorFlow 2.0 入門](https://www.tensorflow.org/tutorials/quickstart/beginner)」に、手書き数字の画像データセット（MINIST）を対象としたクラス分類（「0」から「9」のカテゴリに分類）のサンプルコード（わずか十数行）があります。これを Google Colab. に貼り付けて実行します。

## TFのバージョンを 1.x から 2.x に切り替える

TensorFlow2 を利用するため、次の**マジックコマンド**をコードセルのなかで実行します（コードセルに貼り付けて \[Ctrl\]+\[Enter\] で実行）。これを実行する理由は、2019/12/27の時点で、Google Colab. が TensorFlow **1.x** をデフォルトに設定しているためで、それを **2.x に切り替えるため**の処理です。

```py:GoogleColab.での準備
%tensorflow_version 2.x
```

問題なければ「`TensorFlow 2.x selected.`」のように表示されます。

なお、1.x の TF（TensorFlow） を実行すると「<font color='red'>The default version of TensorFlow in Colab will soon switch to TensorFlow 2.x.</font>」とメッセージがでるので、近いうちにこの手続きは不要になると思います（TF 2.x がデフォルトになると思います）。

## サンプルコードと実行

公式HPのサンプルコードに、少しだけコメントを付けています。

```py
import tensorflow as tf

# (1) 手書き数字画像のデータセット（MNIST）をダウンロード、変数に格納
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# (2) データの正規化（入力データに対する前処理）
x_train, x_test = x_train / 255.0, x_test / 255.0

# (3) NNモデルの構築
model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(input_shape=(28, 28)),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10, activation='softmax')
])

# (4) モデルのコンパイル（学習に関する設定も含む）
model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy'])

# (5) モデルのトレーニング（学習・訓練）
model.fit(x_train, y_train, epochs=5)

# (6) モデルの評価
model.evaluate(x_test,  y_test, verbose=2)
```

以上の短いプログラムのなかで、次のことを行なっています。

- 手書き数字画像のデータセットをダウンロードして、各変数に格納（データの準備）
  - `*_train`：トレーニング用（学習用、訓練用）のデータ
  - `*_test`：テスト用（評価用）のデータ<br>これらのデータの詳細については、第2回の「[～入力データを詳しくみてみる～](https://qiita.com/code0327/items/2c969ca4675d5a3691ef)」で解説します。
- データの正規化（入力データに対する前処理）
  - 0～255 の範囲の整数値を、0.0～1.0 の範囲の実数値に変換
- 機械学習させるためのニューラルネットワークモデルの構築
  - ここの詳細については、第7回の「[～層タイプ・活性化関数について理解する～](https://qiita.com/code0327/items/68dd7beb1196320c8c15)」で解説します。
- モデルのコンパイル（学習に関する設定も含む）
  - ここの詳細については、第8回の「[～最適化アルゴリズムと損失関数を選択する～](https://qiita.com/code0327/items/4f8f656bed23140b8962)」で解説します。
- トレーニング用データ（`*_train`）を使ったモデルの学習（→ **学習済みモデル**が完成）
- テスト用データ（`*_test`）を使ったモデルの評価（学習済みモデルによる画像分類の実行と、答え合わせ（採点））

プログラムの実行結果は、次のようになります。

```:実行結果
Train on 60000 samples
Epoch 1/5
60000/60000 [==============================] - 5s 82us/sample - loss: 0.2992 - accuracy: 0.9134
Epoch 2/5
60000/60000 [==============================] - 5s 78us/sample - loss: 0.1457 - accuracy: 0.9561
Epoch 3/5
60000/60000 [==============================] - 5s 78us/sample - loss: 0.1096 - accuracy: 0.9659
Epoch 4/5
60000/60000 [==============================] - 5s 78us/sample - loss: 0.0876 - accuracy: 0.9730
Epoch 5/5
60000/60000 [==============================] - 5s 80us/sample - loss: 0.0757 - accuracy: 0.9765
10000/10000 - 0s - loss: 0.0766 - accuracy: 0.9762
[0.07658648554566316, 0.9762]
```

意味的には・・・

- `Train on 60000 samples` ：60,000枚の手書き文字画像を使ってトレーニングしますよ。
- `Epoch x/5`：全体で5回繰り返して学習させるうちの x回目の学習ですよ。
- `5s 82us/sample - loss: 0.2992 - accuracy: 0.9134`：画像1枚あたり82$\mu$秒、全体（60,000枚）では約5秒の学習時間がかかりましたよ。そうやって学習させたモデルについて（トレーニング用データを使って評価した）性能は、損失関数値（loss）が 0.2992、正答率（accuracy）が0.9134 でしたよ。
  - 正答率が 0.9134 ということは、$60,000\times0.9134=54,804$ 枚の画像について正しく 0～9 の分類ができて、残りの $60,000-54,804=5,196$ 枚の画像については誤分類された、と解釈します。
- `10000/10000 - 0s - loss: 0.0766 - accuracy: 0.9762`：（トレーニング用として使ったものとは別の）テスト用の 10,000枚の画像で分類予測のテストをしました。テストには 0秒の時間がかかって、損失関数の評価値（loss）が 0.0766、正答率（accuracy）が 0.9762 でしたよ。

## 正答率（accuracy）とは

「精度」や「正解率」とも呼ばれます。正しく分類できた画像の割合を表します。例えば、100枚の画像のうち、98枚について正しく分類できれば正答率は $98/100=0.98$（=98%）となります。

正答率は、0.0 から 1.0 までの範囲をとり、（トレーニングに使用していないデータを使って評価したときに）**値が大きいほど（1.0に近いほど）優れたモデル**といえます。

## 損失関数値（loss）とは

正答率という観点だけではモデル（分類器）の優劣を測れない部分があります。例えば、次のような1枚の画像（正解は「3」）について、異なる2つのモデルを使って分類（予測）を行なうとします。

![ダウンロード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/78365f51-2a49-1718-a171-10f69e9ef610.png)

この画像に対して、モデルAは「3」と予測し、モデルBも「3」と予測したとします。正解は「3」なので、**いずれのモデルも正答率は 1.0** となります。この**正答率という指標だけを見れば**、2つのモデルは同じ程度に優れたモデルといえます。

しかし、モデルAの予測は「**8である確信が10%、3である確信が90%というなかで、3を選択したもの**」であり、一方で、モデルBの予測は「**8である確信が45%、3である確信が55%というなかで、3を出力したもの**」だったとしたら、どうでしょうか？

**同じ正答率 1.0 でも**、モデルAのほうが優れていると言えます。

しかし、正答率という指標では、このようなことは考慮できません。それを評価するためのものが**損失関数**であり、損失関数で評価した値が **loss** になります。

ここで扱っている手書き数字分類は「**多クラス分類問題**」というタイプに属し、通常、この問題の損失関数には**交差エントロピー誤差**（クロスエントロピー）という指標がよく使用されます。交差エントロピーは、ニューラルネットワークの出力層の各値と正解データを使って計算します）。詳しくは第8回の「[～最適化アルゴリズムと損失関数を選択する～](https://qiita.com/code0327/items/4f8f656bed23140b8962)」で解説しています。

基本的に損失関数値は 0.0 以上の値をとり、**損失関数値が小さいほど（0.0に近いほど）優れたモデル**と見なします。損失関数値は 1.0 を超える場合もあります。

# 次回

- 次回は、トレーニング用データ（`x_train`、`y_train`）、テスト用データ（`x_test`、`y_test`）の解説と matplotlib を使った可視化をしたいと思います。