Join us at TensorFlow World, Oct 28-31. Use code TF20 for 20% off select passes. Register now

はじめてのニューラルネットワーク:分類問題の初歩

View on TensorFlow.org Run in Google Colab View source on GitHub

このガイドでは、スニーカーやシャツなど、身に着けるものの写真を分類するニューラルネットワークのモデルを訓練します。すべての詳細を理解できなくても問題ありません。TensorFlowの全体を早足で掴むためのもので、詳細についてはあとから見ていくことになります。

このガイドでは、TensorFlowのモデルを構築し訓練するためのハイレベルのAPIである tf.kerasを使用します。

from __future__ import absolute_import, division, print_function, unicode_literals

# TensorFlow と tf.keras のインポート
import tensorflow as tf
from tensorflow import keras

# ヘルパーライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt

print(tf.__version__)
2.0.0-rc1

ファッションMNISTデータセットのロード

このガイドでは、Fashion MNISTを使用します。Fashion MNISTには10カテゴリーの白黒画像70,000枚が含まれています。それぞれは下図のような1枚に付き1種類の衣料品が写っている低解像度(28×28ピクセル)の画像です。

Fashion MNIST sprite
Figure 1. Fashion-MNIST samples (by Zalando, MIT License).
 

Fashion MNISTは、画像処理のための機械学習での"Hello, World"としてしばしば登場するMNIST データセットの代替として開発されたものです。MNISTデータセットは手書きの数字(0, 1, 2 など)から構成されており、そのフォーマットはこれから使うFashion MNISTと全く同じです。

Fashion MNISTを使うのは、目先を変える意味もありますが、普通のMNISTよりも少しだけ手応えがあるからでもあります。どちらのデータセットも比較的小さく、アルゴリズムが期待したとおりに機能するかどうかを確かめるために使われます。プログラムのテストやデバッグのためには、よい出発点になります。

ここでは、60,000枚の画像を訓練に、10,000枚の画像を、ネットワークが学習した画像分類の正確性を評価するのに使います。TensorFlowを使うと、下記のようにFashion MNISTのデータを簡単にインポートし、ロードすることが出来ます。

fashion_mnist = keras.datasets.fashion_mnist

(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
32768/29515 [=================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
26427392/26421880 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
8192/5148 [===============================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
4423680/4422102 [==============================] - 0s 0us/step

ロードしたデータセットは、NumPy配列になります。

  • train_imagestrain_labels の2つの配列は、モデルの訓練に使用される訓練用データセットです。
  • 訓練されたモデルは、 test_imagestest_labels 配列からなるテスト用データセットを使ってテストします。

画像は28×28のNumPy配列から構成されています。それぞれのピクセルの値は0から255の間の整数です。ラベル(label)は、0から9までの整数の配列です。それぞれの数字が下表のように、衣料品のクラス(class)に対応しています。

Label Class
0 T-shirt/top
1 Trouser
2 Pullover
3 Dress
4 Coat
5 Sandal
6 Shirt
7 Sneaker
8 Bag
9 Ankle boot

画像はそれぞれ単一のラベルに分類されます。データセットには上記のクラス名が含まれていないため、後ほど画像を出力するときのために、クラス名を保存しておきます。

class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

データの観察

モデルの訓練を行う前に、データセットのフォーマットを見てみましょう。下記のように、訓練用データセットには28×28ピクセルの画像が60,000枚含まれています。

train_images.shape
(60000, 28, 28)

同様に、訓練用データセットには60,000個のラベルが含まれます。

len(train_labels)
60000

ラベルはそれぞれ、0から9までの間の整数です。

train_labels
array([9, 0, 0, ..., 3, 0, 5], dtype=uint8)

テスト用データセットには、10,000枚の画像が含まれます。画像は28×28ピクセルで構成されています。

test_images.shape
(10000, 28, 28)

テスト用データセットには10,000個のラベルが含まれます。

len(test_labels)
10000

データの前処理

ネットワークを訓練する前に、データを前処理する必要があります。最初の画像を調べてみればわかるように、ピクセルの値は0から255の間の数値です。

plt.figure()
plt.imshow(train_images[0])
plt.colorbar()
plt.gca().grid(False)
plt.show()

png

ニューラルネットワークにデータを投入する前に、これらの値を0から1までの範囲にスケールします。そのためには、画素の値を255で割ります。

訓練用データセットテスト用データセットは、同じように前処理することが重要です。

train_images = train_images / 255.0

test_images = test_images / 255.0

訓練用データセットの最初の25枚の画像を、クラス名付きで表示してみましょう。ネットワークを構築・訓練する前に、データが正しいフォーマットになっていることを確認します。

plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    plt.imshow(train_images[i], cmap=plt.cm.binary)
    plt.xlabel(class_names[train_labels[i]])
plt.show()

png

モデルの構築

ニューラルネットワークを構築するには、まずモデルの階層を定義し、その後モデルをコンパイルします。

層の設定

ニューラルネットワークを形作る基本的な構成要素は(layer)です。層は、入力されたデータから「表現」を抽出します。それらの「表現」は、今取り組もうとしている問題に対して、より「意味のある」ものであることが期待されます。

ディープラーニングモデルのほとんどは、単純な層の積み重ねで構成されています。tf.keras.layers.Dense のような層のほとんどには、訓練中に学習されるパラメータが存在します。

model = keras.Sequential([
    keras.layers.Flatten(input_shape=(28, 28)),
    keras.layers.Dense(128, activation=tf.nn.relu),
    keras.layers.Dense(10, activation=tf.nn.softmax)
])

このネットワークの最初の層は、tf.keras.layers.Flatten です。この層は、画像を(28×28ピクセルの)2次元配列から、28×28=784ピクセルの、1次元配列に変換します。この層が、画像の中に積まれているピクセルの行を取り崩し、横に並べると考えてください。この層には学習すべきパラメータはなく、ただデータのフォーマット変換を行うだけです。

ピクセルが1次元化されたあと、ネットワークは2つの tf.keras.layers.Dense 層となります。これらの層は、密結合あるいは全結合されたニューロンの層となります。最初の Dense 層には、128個のノード(あるはニューロン)があります。最後の層でもある2番めの層は、10ノードのsoftmax層です。この層は、合計が1になる10個の確率の配列を返します。それぞれのノードは、今見ている画像が10個のクラスのひとつひとつに属する確率を出力します。

モデルのコンパイル

モデルが訓練できるようになるには、いくつかの設定を追加する必要があります。それらの設定は、モデルのコンパイル(compile)時に追加されます。

  • 損失関数(loss function) —訓練中にモデルがどれくらい正確かを測定します。この関数の値を最小化することにより、訓練中のモデルを正しい方向に向かわせようというわけです。
  • オプティマイザ(optimizer)—モデルが見ているデータと、損失関数の値から、どのようにモデルを更新するかを決定します。
  • メトリクス(metrics) —訓練とテストのステップを監視するのに使用します。下記の例ではaccuracy (正解率)、つまり、画像が正しく分類された比率を使用しています。
model.compile(optimizer=tf.keras.optimizers.Adam(),
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

モデルの訓練

ニューラルネットワークの訓練には次のようなステップが必要です。

  1. モデルに訓練用データを投入します—この例では train_imagestrain_labels の2つの配列です。
  2. モデルは、画像とラベルの対応関係を学習します。
  3. モデルにテスト用データセットの予測(分類)を行わせます—この例では test_images 配列です。その後、予測結果と test_labels 配列を照合します。

訓練を開始するには、model.fit メソッドを呼び出します。モデルを訓練用データに "fit"(適合)させるという意味です。

model.fit(train_images, train_labels, epochs=5)
WARNING: Logging before flag parsing goes to stderr.
W0809 13:43:14.101130 140247098349312 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/ops/math_grad.py:1250: add_dispatch_support.<locals>.wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

Train on 60000 samples
Epoch 1/5
60000/60000 [==============================] - 6s 93us/sample - loss: 0.5008 - accuracy: 0.8241
Epoch 2/5
60000/60000 [==============================] - 5s 83us/sample - loss: 0.3758 - accuracy: 0.8654
Epoch 3/5
60000/60000 [==============================] - 5s 83us/sample - loss: 0.3394 - accuracy: 0.8769
Epoch 4/5
60000/60000 [==============================] - 5s 80us/sample - loss: 0.3170 - accuracy: 0.8836
Epoch 5/5
60000/60000 [==============================] - 5s 83us/sample - loss: 0.2979 - accuracy: 0.8909

<tensorflow.python.keras.callbacks.History at 0x7f8dc86ee908>

モデルの訓練の進行とともに、損失値と正解率が表示されます。このモデルの場合、訓練用データでは0.88(すなわち88%)の正解率に達します。

正解率の評価

次に、テスト用データセットに対するモデルの性能を比較します。

test_loss, test_acc = model.evaluate(test_images, test_labels)

print('Test accuracy:', test_acc)
10000/10000 [==============================] - 1s 63us/sample - loss: 0.3747 - accuracy: 0.8665
Test accuracy: 0.8665

ご覧の通り、テスト用データセットでの正解率は、訓練用データセットでの正解率よりも少し低くなります。この訓練時の正解率とテスト時の正解率の差は、過学習(over fitting)の一例です。過学習とは、新しいデータに対する機械学習モデルの性能が、訓練時と比較して低下する現象です。

予測する

モデルの訓練が終わったら、そのモデルを使って画像の分類予測を行うことが出来ます。

predictions = model.predict(test_images)

これは、モデルがテスト用データセットの画像のひとつひとつを分類予測した結果です。最初の予測を見てみましょう。

predictions[0]
array([6.5743676e-05, 1.2119241e-08, 1.1929819e-05, 1.6608800e-07,
       2.9963689e-07, 3.4419000e-03, 6.6265682e-05, 1.0476879e-02,
       1.5764357e-05, 9.8592108e-01], dtype=float32)

予測結果は、10個の数字の配列です。これは、その画像が10の衣料品の種類のそれぞれに該当するかの「確信度」を表しています。どのラベルが一番確信度が高いかを見てみましょう。

np.argmax(predictions[0])
9

というわけで、このモデルは、この画像が、アンクルブーツ、class_names[9] である可能性が最も高いと判断したことになります。これが正しいかどうか、テスト用ラベルを見てみましょう。

test_labels[0]
9

10チャンネルすべてをグラフ化してみることができます。

def plot_image(i, predictions_array, true_label, img):
    predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]
    plt.grid(False)
    plt.xticks([])
    plt.yticks([])
    
    plt.imshow(img, cmap=plt.cm.binary)
    
    predicted_label = np.argmax(predictions_array)
    if predicted_label == true_label:
        color = 'blue'
    else:
        color = 'red'
    
    plt.xlabel("{} {:2.0f}% ({})".format(class_names[predicted_label],
                                    100*np.max(predictions_array),
                                    class_names[true_label]),
                                    color=color)

def plot_value_array(i, predictions_array, true_label):
    predictions_array, true_label = predictions_array[i], true_label[i]
    plt.grid(False)
    plt.xticks([])
    plt.yticks([])
    thisplot = plt.bar(range(10), predictions_array, color="#777777")
    plt.ylim([0, 1])
    predicted_label = np.argmax(predictions_array)
    
    thisplot[predicted_label].set_color('red')
    thisplot[true_label].set_color('blue')

0番目の画像と、予測、予測配列を見てみましょう。

i = 0
plt.figure(figsize=(6,3))
plt.subplot(1,2,1)
plot_image(i, predictions, test_labels, test_images)
plt.subplot(1,2,2)
plot_value_array(i, predictions,  test_labels)
plt.show()

png

i = 12
plt.figure(figsize=(6,3))
plt.subplot(1,2,1)
plot_image(i, predictions, test_labels, test_images)
plt.subplot(1,2,2)
plot_value_array(i, predictions,  test_labels)
plt.show()

png

予測の中のいくつかの画像を、予測値とともに表示してみましょう。正しい予測は青で、誤っている予測は赤でラベルを表示します。数字は予測したラベルのパーセント(100分率)を示します。自信があるように見えても間違っていることがあることに注意してください。

# X個のテスト画像、予測されたラベル、正解ラベルを表示します。
# 正しい予測は青で、間違った予測は赤で表示しています。
num_rows = 5
num_cols = 3
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
for i in range(num_images):
    plt.subplot(num_rows, 2*num_cols, 2*i+1)
    plot_image(i, predictions, test_labels, test_images)
    plt.subplot(num_rows, 2*num_cols, 2*i+2)
    plot_value_array(i, predictions, test_labels)
plt.show()

png

最後に、訓練済みモデルを使って1枚の画像に対する予測を行います。

# テスト用データセットから画像を1枚取り出す
img = test_images[0]

print(img.shape)
(28, 28)

tf.keras モデルは、サンプルの中のバッチ(batch)あるいは「集まり」について予測を行うように作られています。そのため、1枚の画像を使う場合でも、リスト化する必要があります。

# 画像を1枚だけのバッチのメンバーにする
img = (np.expand_dims(img,0))

print(img.shape)
(1, 28, 28)

そして、予測を行います。

predictions_single = model.predict(img)

print(predictions_single)
[[6.5743676e-05 1.2119241e-08 1.1929844e-05 1.6608800e-07 2.9963689e-07
  3.4419014e-03 6.6265631e-05 1.0476879e-02 1.5764374e-05 9.8592108e-01]]
plot_value_array(0, predictions_single, test_labels)
_ = plt.xticks(range(10), class_names, rotation=45)
plt.show()

png

model.predict メソッドの戻り値は、リストのリストです。リストの要素のそれぞれが、バッチの中の画像に対応します。バッチの中から、(といってもバッチの中身は1つだけですが)予測を取り出します。

prediction = predictions[0]

np.argmax(prediction)
9

というわけで、モデルは9というラベルを予測しました。

#@title MIT License
#
# Copyright (c) 2017 François Chollet
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.