Dropout

mnistを使った全結合NNにおけるDropoutの使い方

Dropoutは過学習を抑制する手法としてとても重要になります.
このチュートリアルでは全結合ニューラルネットワークを用いて数字認識モデルを構築します. チュートリアルを通して以下の点を紹介します.
  • Dropoutの使い方

必要なライブラリ

  • matplotlib 2.0.2
  • numpy 1.12.1
  • scikit-learn 0.18.2
In [1]:
from __future__ import division, print_function
import matplotlib.pyplot as plt
import numpy as np

from sklearn.datasets import fetch_mldata
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelBinarizer
from sklearn.metrics import confusion_matrix, classification_report

import renom as rm
from renom.optimizer import Sgd

データの読み込み

最初にMNISTデータ・セットをダウンロードし,学習ができるように前処理を施します.今回はsckit-learnの fetch_mldata 関数を使用します.

MNISTデータセットは70000枚の数字画像からなります.まずは,データセットを学習用とテスト様に分割します.次にデータ255で割ることで,画像を0-1の間にスケーリングします.更に,教師データを二値化します.このようにして,データの前処理が完了しました.

In [2]:
# Datapath must point to the directory containing the mldata folder.
data_path = "../dataset"
mnist = fetch_mldata('MNIST original', data_home=data_path)

X = mnist.data
y = mnist.target

# Rescale the image data to 0 ~ 1.
X = X.astype(np.float32)
X /= X.max()

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1)
labels_train = LabelBinarizer().fit_transform(y_train).astype(np.float32)
labels_test = LabelBinarizer().fit_transform(y_test).astype(np.float32)

# Training data size.
N = len(X_train)

ニューラルネットワークの定義とdropout

Dropoutは過学習を抑制する手法として重要になります.
Dropout ratioは各レイヤーにおいてどれくらユニットをdropさせるかを決定します.
異なるユニットを学習のたびにdropさせることは異なるモデルのアンサンブルを取っているとみなすこともできるため,頑健な推定ができると言われています.
Over fittingしやすいデータや実行するたびに結果が大きく変わるモデルについてはアンサンブルとしてdropoutを適用することも選択肢の一つとして考えるべきだと思います.
In [3]:
class Mnist(rm.Model):

    def __init__(self):
        super(Mnist, self).__init__()
        self._layer1 = rm.Dense(100)
        self._layer2 = rm.Dense(10)
        self._dropout1 = rm.Dropout(dropout_ratio=0.5)

    def forward(self, x):
        t1 = self._dropout1(self._layer1(x))
        out = self._layer2(t1)
        return out

インスタンス化

In [4]:
network = Mnist()

学習ループ

ここまでで,ニューラルネットワークを構築することが出来ました. 次に学習ループを定義します. ここではミニバッチを用いた確率的勾配降下法(SGD)による学習を行います. 学習のたびに学習データから重複なしにデータをランダムに取り出しそのデータを用いて重みを更新します. このような確率的な重み更新方法を取ることで学習曲線はスムーズではなくなりますが,結果的に学習の結果が改善します.

In [5]:
# Hyper parameters
batch = 64
epoch = 10

optimizer = Sgd(lr = 0.1)

learning_curve = []
test_learning_curve = []

for i in range(epoch):
    perm = np.random.permutation(N)
    loss = 0
    for j in range(0, N // batch):
        train_batch = X_train[perm[j * batch:(j + 1) * batch]]
        responce_batch = labels_train[perm[j * batch:(j + 1) * batch]]

        # The computational graph is only generated for this block:
        with network.train():
            l = rm.softmax_cross_entropy(network(train_batch), responce_batch)
            if hasattr(network, "weight_decay"):
                l += 0.0001 * network.weight_decay()

        # Back propagation
        grad = l.grad()

        # Update
        grad.update(optimizer)

        # Changing type to ndarray is recommended.
        loss += l.as_ndarray()

    train_loss = loss / (N // batch)

    # Validation
    test_loss = rm.softmax_cross_entropy(network(X_test), labels_test).as_ndarray()
    test_learning_curve.append(test_loss)
    learning_curve.append(train_loss)
    print("epoch %03d train_loss:%f test_loss:%f"%(i, train_loss, test_loss))
epoch 000 train_loss:0.457225 test_loss:0.412944
epoch 001 train_loss:0.374044 test_loss:0.377608
epoch 002 train_loss:0.356384 test_loss:0.364094
epoch 003 train_loss:0.350757 test_loss:0.368991
epoch 004 train_loss:0.342420 test_loss:0.367725
epoch 005 train_loss:0.337317 test_loss:0.377109
epoch 006 train_loss:0.335588 test_loss:0.361930
epoch 007 train_loss:0.332697 test_loss:0.351402
epoch 008 train_loss:0.329190 test_loss:0.356135
epoch 009 train_loss:0.329743 test_loss:0.367464

モデルの評価

学習が終了したモデルを評価します. ここでは混同行列を用いて,各クラスに属するテストデータの正解割合を表示しました. また,テストデータに対する,再現率,適合率,F1スコアについても同様に表示しています.

In [6]:
predictions = np.argmax(network(X_test).as_ndarray(), axis=1)

# Confusion matrix and classification report.
print(confusion_matrix(y_test, predictions))
print(classification_report(y_test, predictions))

# Learning curve.
plt.plot(learning_curve, linewidth=3, label="train")
plt.plot(test_learning_curve, linewidth=3, label="test")
plt.title("Learning curve")
plt.ylabel("error")
plt.xlabel("epoch")
plt.legend()
plt.grid()
plt.show()
[[675   0   4   2   2  13   3   2   3   2]
 [  0 774   6   2   1   5   3   3  14   0]
 [  6   3 649  14  18   4   6  11  18   4]
 [  4   5  22 591   2  29   1   7  16  10]
 [  3   3   2   0 633   0   7   6   3  26]
 [ 10   5  10  10  13 517   9   4  41   7]
 [  8   4   6   1  18  14 621   1   7   1]
 [  1   1   8   4   5   0   0 670   1  24]
 [  5  18  16  13   8   9   3   1 603  15]
 [  3   5   0   7  23   5   0  34   6 588]]
             precision    recall  f1-score   support

        0.0       0.94      0.96      0.95       706
        1.0       0.95      0.96      0.95       808
        2.0       0.90      0.89      0.89       733
        3.0       0.92      0.86      0.89       687
        4.0       0.88      0.93      0.90       683
        5.0       0.87      0.83      0.85       626
        6.0       0.95      0.91      0.93       681
        7.0       0.91      0.94      0.92       714
        8.0       0.85      0.87      0.86       691
        9.0       0.87      0.88      0.87       671

avg / total       0.90      0.90      0.90      7000

../../../_images/notebooks_basic_dropout_notebook_12_1.png