Lesson 16

深層学習をしてみよう

Lesson 16 Chapter 1
手書き数字認識

Lesson 16 では TensorFlow を用いて深層学習を実際に行ってみましょう。 まずは、手書き数字(0~9)の画像を受け取り、どの数字が書かれているか(の確率分布)を出力するモデルを深層学習により構築してみます。 学習のためのデータとしては、今回はMNIST データベースの手書き数字の画像データセット(http://yann.lecun.com/exdb/mnist/)を使用します。 これは TensorFlow 経由でロードすることができます。

ではコードを見ていきましょう。ますは必要なライブラリをインポートします。

Python
from tensorflow.keras import layers, models, losses, datasets
import matplotlib.pyplot as plt

次に MNIST の手書き数字のデータセットをロードします。

Python
(x_train, y_train), (x_test, y_test) = datasets.mnist.load_data()
print("学習データの形状")
print(x_train.shape, y_train.shape)
print("テストデータの形状")
print(x_test.shape, y_test.shape)

print("画像データの例")
print(x_train[0])

x_train, x_test = x_train / 255.0, x_test / 255.0
print("浮動小数点数化したあとの学習データの例")
print(x_train[0])
print("正解ラベルの例")
print(y_train[0:10])

このデータセットではあらかじめ学習用のデータとテスト用のデータが分割されているので、 それらを(x_train, y_train), (x_test, y_test) = datasets.mnist.load_data()として各々格納します。 ではそれぞれどのようなデータになっているか確認しましょう。 まずそれぞれの形状をx_train.shapeなどとしてチェックします。 すると、学習用の画像データが 60,000 枚、テスト用の画像データが 10,000 枚あって、1枚1枚が 28x28 の形をしていることがわかります。 x_train[0]などとするとわかるように、画像データの各点の値(画素値)は0から255の値をとっています。 ニューラルネットワークが入力として扱うのは主に浮動小数点数なので、これを 255.0 で割って0から1の値に変換(正規化)しているのがx_train, x_test = x_train / 255.0, x_test / 255.0という箇所です。 またy_trainなどは対応するデータの正解ラベルが整数として並んでいることがわかります。

学習を行う上では必要ないですが、一旦手書き数字のデータセットがどのような見た目をしているかを表示させてみましょう。

Python
plt.figure(figsize=(10, 4))
for i in range(10):
    plt.subplot(2, 5, i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(x_train[i], cmap=plt.cm.binary)
    plt.xlabel(y_train[i])
plt.show()

まずplt.figure(figsize=(10, 4))によって画像サイズを見やすいように調整しています。 その後学習データの先頭 10 個について、正解ラベルを(横軸の名前として)添えて画像として表示しています。 このときplt.imshow(x_train[i], cmap=plt.cm.binary)とすることで白黒の画像を出力できます。 なおplt.subplot(2, 5, i+1)は2行5列に分割して位置を指定しており、 plt.xticks([])などは目盛りの指定を空配列にすることで目盛りの表示を消しています。

表示された結果を見ると、確かに手書き数字の画像と対応する数字がセットになっていることがわかります。

L16C1_number.png MNIST の手書き数字データセット(http://yann.lecun.com/exdb/mnist/)の一部(Matplotlib により作成)

次に全結合型ニューラルネットワークの構築をします。 Tensorflow の Keras の API を使うことで、簡単にニューラルネットワークモデルを作ることができます。

Python
model = models.Sequential([
    layers.Flatten(input_shape=(28, 28)),
    layers.Dense(128, activation='relu'),
    layers.Dropout(0.2),
    layers.Dense(10),
    layers.Softmax()
])

今回はSequentialモデルによるニューラルネットワークの構築をしています。 これはニューラルネットワークの層を直列に並べることでモデルを構築します。 ちなみに、もう一つ Functional API というものを使うやり方もあり、こちらは主にニューラルネットワークが途中で枝分かれするなど複雑なモデルを作る場合に使います。

それぞれの行が何を表しているかを説明します。 今回は入力の形状が 28x28 であり、そのまま全結合層に入力することができないので、Flattenでこれを1次元の配列になるように平坦化します (input_shape=(28, 28)のように入力の形状を指定しています)。 次のDenseで全結合層を定義しています。 128はノードの数を表し、activation='relu'は活性化関数としてReLU関数を用いることを指定しています。 3つ目のDropout(0.2)は、Lesson 4 で学んだ正則化のためのドロップアウトの層で、 直前の全結合層Dense(128, activation='relu')においてランダムに無効化するノードの割合を0.2に設定しています。 そして次に再び全結合層Dense(10)が続きますが、 これはノードを 10 個として直後のソフトマックス層Softmaxと組み合わせることで、 0~9の 10 個の数字それぞれの確率を出力するようにしています。

モデルの構築ができたところで、学習に関するいくつかの設定を行います。

Python
model.compile(optimizer='sgd', loss=losses.SparseCategoricalCrossentropy(), metrics=['accuracy'])

設定を行うにはmodel.compileを使います。 optimizer='sgd'は、最適化のために SGD を使うことを指定しています。 なお他にも RMSprop や Adam などを使うこともできます。 またloss=losses.SparseCategoricalCrossentropy()とすることで、損失関数としてクロスエントロピー誤差を用いるようにしています。 そしてmetrics=['accuracy']は、自動で正解率を計算するようにしています。

それでは準備が整ったので、いよいよこのニューラルネットワークモデルを学習させてみましょう。

Python
history = model.fit(x_train, y_train, epochs=10)

TensorFlow では上記のようにmodel.fitとするだけで学習させることができます。 epochs=10というのは、エポック数を 10 に指定しています。 学習の様子が自動で出力されるようになっているので、それを見ながら学習が終わるのを待ちましょう。

学習が終わったら、学習過程を図示します。 model.fitの返り値を格納したhistoryを使うことで、学習過程における損失関数の値や正解率を取得できます。

Python
loss_history = history.history['loss']
accuracy_history = history.history['accuracy']
plt.plot(loss_history, label='loss')
plt.plot(accuracy_history, label='accuracy')
plt.legend()
plt.show()

history.history['loss']で損失関数の、 history.history['accuracy']で正解率のエポックごとの値を取得し、それぞれラベルをつけてプロットします。 なおplt.legend()でそれらの凡例を表示しています。 以下に実行例を示します。

L16C1_graph.png 学習過程の図示(Matplotlib により作成)

学習が進むにつれ、損失関数の値は下がり、正解率は上がっていることがわかります。

最後に、テストデータを用いてモデルを評価します。

Python
model.evaluate(x_test, y_test)

上記のように、model.evaluateによってテストデータによるモデル評価を行うことができます。 正解率はおよそ 95% 程度になるでしょう。手書き数字をある程度高い精度で判定できるニューラルネットワークモデルを構築することができました。 興味があれば、全結合層のノードの数やoptimizerなどを変更し、学習後の正解率を比較してみましょう。

Lesson 16 Chapter 2
画像分類

Chapter1に続いて、画像分類のタスクを深層学習によって解きます。 今回は、MNIST のデータセットよりも少し難易度が高いとされる Fashion MNIST(https://github.com/zalandoresearch/fashion-mnist)のデータセットを用います。 これはバッグやサンダル、ズボンなどの身に着けるものの画像が入ったデータセットです。 分類の数は10クラスで MNIST(0~9)と同様ですが、いずれもやや複雑な画像になっています。 このタスクを解くために、画像分類に特化した畳み込みニューラルネットワーク(CNN)を使ってみましょう。

ではコードを見ていきましょう。お決まりですが、必要なライブラリをまずはインポートします。

Python
from tensorflow.keras import layers, models, losses, datasets
import matplotlib.pyplot as plt

次に、データをロードし、前処理を行います。

Python
(x_train, y_train), (x_test, y_test) = datasets.fashion_mnist.load_data()
print("学習データの形状")
print(x_train.shape, y_train.shape)
print("テストデータの形状")
print(x_test.shape, y_test.shape)

x_train = x_train.reshape((60000, 28, 28, 1))
x_test = x_test.reshape((10000, 28, 28, 1))

x_train, x_test = x_train / 255.0, x_test / 255.0

学習データやテストデータの数および形状は MNIST の場合と同じです。 ただし今回は畳み込みニューラルネットワークを使う都合上、画像データの形状を少し変えておく必要があります。 それを行っているのがx_train = x_train.reshape((60000, 28, 28, 1))などの行です。 詳しくは後で説明します。

データの準備ができたので、今回もそれらを画像として表示させてみましょう。

Python
classes = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
plt.figure(figsize=(10,4))
for i in range(10):
    plt.subplot(2,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(x_train[i], cmap=plt.cm.binary)
    plt.xlabel(classes[y_train[i]])
plt.show()

Fashion MNIST ではラベルのデータが整数になっていますが、これをわかりやすくするために整数から対応するクラスの名前への変換を行うためのclassesを最初に用意しています。 あとは MNIST のときと同様にして、画像を 10 枚表示させています。表示の結果は以下のようになります。

L16C2_fashion.png Fashion MNIST のデータセット(https://github.com/zalandoresearch/fashion-mnist/)の一部(Matplotlib により作成)

それでは畳み込みニューラルネットワークの構築に移りましょう。次のコードを見てください。

Python
model = models.Sequential([
    layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
    layers.MaxPooling2D((2, 2)),
    layers.Conv2D(64, (3, 3), activation='relu'),
    layers.Flatten(),
    layers.Dense(128, activation='relu'),
    layers.Dense(10),
    layers.Softmax()
])

まずConv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1))についてです。 Conv2Dは畳み込み層を使うためのもので、いくつかのパラメータを設定できます。 最初の32はフィルタの数、次の(3, 3)はフィルタの形状、 activation='relu'は活性化関数の指定、input_shape=(28, 28, 1)は入力の形状の指定です。 通常Conv2Dを使う際には、その入力の形状はチャネル数を考慮したものになっている必要があり、それが白黒画像では1(RGB画像なら3)なので、 先ほどx_train.reshape((60000, 28, 28, 1))などとしておく必要がありました。

次にMaxPooling2D((2, 2))についてです。 これは Max プーリングを行うためのもので、(2, 2)でウインドウのサイズを指定しています。

そして再び畳み込み層Conv2Dを使い、その後は全結合層およびソフトマックス層によって 10 個のクラスそれぞれの確率を出力するようにしています。 なお一般に、最初の層以外は入力の形状を指定する必要がありません(最初の層への入力の形さえわかればそれ以降は計算によって求まるからです)。

モデルを構築した後、簡単にモデルの概要を見るための機能が用意されています。

Python
model.summary()

上記のようにすると、モデルの各層におけるパラメータの数や出力の形状、そして総パラメータ数などが出力されます。 今回のモデルのパラメータ数はおよそ100万程度であることがわかります。

それではモデルの設定をし、学習をさせてみましょう。

Python
model.compile(optimizer='adam', loss=tf.keras.losses.SparseCategoricalCrossentropy(), metrics=['accuracy'])
history = model.fit(x_train, y_train, epochs=5, validation_split=0.1)

今回は最適化のために Adam を用いることにしました。 また学習中に、学習データの一部をモデルの性能の検証用のデータとして用いるために、 model.fitのところでvalidation_split=0.1としています。 これにより学習データの1割が検証用のデータとして用いられることになります。

出力される学習の様子を見ると、学習データについての損失関数などの値の他に検証データに対する値も出力されていることが分かるでしょう。 なお今回はエポック数は 5 にしていますが、学習にはやや時間がかかります。

学習が終わったら、その過程を図示しましょう。 今回は検証用のデータについても学習過程を図示してみます。

Python
loss_history = history.history['loss']
accuracy_history = history.history['accuracy']
plt.plot(loss_history, label='loss')
plt.plot(accuracy_history, label='accuracy')
plt.legend()
plt.show()
Python
val_loss_history = history.history['val_loss']
val_accuracy_history = history.history['val_accuracy']
plt.plot(val_loss_history, label='val_loss')
plt.plot(val_accuracy_history, label='val_accuracy')
plt.legend()
plt.show()

検証データを用いたときは、history.history['val_loss']などとして検証データに関する学習過程のデータを取得できます。 それぞれの実行例は次のようになります。

L16C2_graph1.png L16C2_graph2.png

学習過程の図示(いずれも Matplotlib により作成)

ではこのCNNのモデルをテストデータによって評価しましょう。

model.evaluate(x_test, y_test)

正解率はおよそ 92% 程度になるでしょう。もっと正解率を高くするにはどうすればよいでしょうか。 モデルの構造や学習の設定についてさまざまな組み合わせを試してみましょう。

この Lesson 16 では TensorFlow を使って深層学習を行いました。 今回は全結合型ニューラルネットワークや畳み込みニューラルネットワークを使ってみましたが、 他にもリカレントニューラルネットワークやその改良などを用いたモデルを簡単に使うことができます。 ぜひこれまでに学んだことを活かして、色々なモデルによる深層学習を実践していきましょう。