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 = "."
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 = 50

optimizer = Sgd(lr = 0.01)

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)

        # 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)
    if i % 2 == 0:
        print("epoch %03d train_loss:%f test_loss:%f"%(i, train_loss, test_loss))
epoch 000 train_loss:0.708555 test_loss:0.491806
epoch 002 train_loss:0.400918 test_loss:0.404904
epoch 004 train_loss:0.367667 test_loss:0.379439
epoch 006 train_loss:0.352874 test_loss:0.367108
epoch 008 train_loss:0.340097 test_loss:0.359081
epoch 010 train_loss:0.334252 test_loss:0.351308
epoch 012 train_loss:0.327346 test_loss:0.342508
epoch 014 train_loss:0.322593 test_loss:0.340577
epoch 016 train_loss:0.321203 test_loss:0.344014
epoch 018 train_loss:0.316652 test_loss:0.336845
epoch 020 train_loss:0.311172 test_loss:0.336718
epoch 022 train_loss:0.311073 test_loss:0.336991
epoch 024 train_loss:0.307484 test_loss:0.336896
epoch 026 train_loss:0.305593 test_loss:0.331858
epoch 028 train_loss:0.303051 test_loss:0.326330
epoch 030 train_loss:0.301192 test_loss:0.329767
epoch 032 train_loss:0.299760 test_loss:0.325156
epoch 034 train_loss:0.298049 test_loss:0.330092
epoch 036 train_loss:0.297320 test_loss:0.325677
epoch 038 train_loss:0.295978 test_loss:0.319460
epoch 040 train_loss:0.293667 test_loss:0.326050
epoch 042 train_loss:0.294690 test_loss:0.320485
epoch 044 train_loss:0.292206 test_loss:0.326908
epoch 046 train_loss:0.292942 test_loss:0.329125
epoch 048 train_loss:0.291288 test_loss:0.323323

Inference Mode

ReNom DLには学習時と推論時で異なる動作をするものが存在します。
例えば、Dropoutは学習時に確率的に特定のユニットを使わないような学習を行います。
しかし、推論時には様々なユニットで学習された結果をアンサンブル学習として用いることでDropoutは効果を発揮するため、推論のときのみ、確率的にユニットを削減したくないので、学習時と推論時で動作が変わります。
In this case, ReNom is set to be learning mode, so we have to change to be inference mode according to bellow command.
In [6]:
network.set_models(inference=True)

モデルの評価

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

In [7]:
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()
[[662   0   2   2   0   7   2   2   1   0]
 [  0 749   4   1   2   4   1   1  11   1]
 [  7   7 602  10  10   4   8  12  19   2]
 [  1   3  13 644   0  34   2   5  19   2]
 [  0   3   1   1 667   2   8   1   9  19]
 [  3   3   4  16  13 570  11   2  15   5]
 [  3   3   5   0   8   9 664   0   4   0]
 [  2   6  10   3   9   0   0 690   4  25]
 [  3  15   6  15   2  20   4   5 573  14]
 [  6   4   1  10  27   6   1  23   8 603]]
             precision    recall  f1-score   support

        0.0       0.96      0.98      0.97       678
        1.0       0.94      0.97      0.96       774
        2.0       0.93      0.88      0.91       681
        3.0       0.92      0.89      0.90       723
        4.0       0.90      0.94      0.92       711
        5.0       0.87      0.89      0.88       642
        6.0       0.95      0.95      0.95       696
        7.0       0.93      0.92      0.93       749
        8.0       0.86      0.87      0.87       657
        9.0       0.90      0.88      0.89       689

avg / total       0.92      0.92      0.92      7000

../../../_images/notebooks_basic_algorithm_dropout_notebook_14_1.png