バッチ正規化

ReNomにおけるバッチ正規化の使い方

バッチ正規化は画像処理においてはよく使われる方法の一つになります。
よく言われているように、学習においては分布の違いの問題が起こり得ます。例えば学習で使うデータの分布と予測で使うデータの分布が異なることもあります。
さらにdeepなニューラルネットワークの隠れ層の学習の不安定さもよく言われている問題点になります。
バッチ正規化は主に標準化とアフィン変換から構成されています。これは入力データの分布を0平均1分散のガウス分布に変換し、アフィン変換によって適切な平均と分散を求めることと言えます。

バッチ正規化を使わないケースを考えるとパラメータの更新の時には入力データ(前の層の出力)に強く影響を受けて重みが更新されますが、前の層の重みも毎回学習の度に更新されるために、deepなニューラルネットワークにおいて入力のデータは毎回変わることが想定されます。そこでバッチ正規化はこの隠れ層の学習において、入力分布をガウス分布に変換し、入力データを制限することで入力データが学習の度に大きく変わることを防ぐ効果があり、それによって中間層の学習が安定化すると考えています。

Required libraries

  • matplotlib 2.0.2
  • numpy 1.12.1
  • scikit-learn 0.18.2
  • pillow 4.2.1
In [1]:
from __future__ import division, print_function
import os
import sys
import pickle

import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelBinarizer
from sklearn.metrics import confusion_matrix, classification_report

import renom as rm
from renom.optimizer import Sgd, Adam
from renom.cuda.cuda import set_cuda_active

GPU-enabled Computing

GPUを使用する時はset_cuda_active()をTrueに設定してください。それ以外の場合はFalseに設定してください。

In [2]:
set_cuda_active(True)

Load data

CIFARのWEBサイトからデータセットをダウンロード出来ます.データセットはpickleモジュールを用いて以下の様に展開することが出来ます.Tutorial1と同じように、前処理として、画像のリスケーリングと教師データの二値化を行います.また、畳込みニューラルネットワークではデータを4次元テンソルとして扱います.テンソルデータの構造はNCHW形式となっております.ここでN:バッチサイズ、C:チャネル数、H:画像の縦幅、W:画像の横幅です.

In [3]:
dir = "./cifar-10-batches-py/"
paths = ["data_batch_1", "data_batch_2", "data_batch_3",
         "data_batch_4", "data_batch_5"]

def unpickle(f):
    fo = open(f, 'rb')
    if sys.version_info.major == 2:
        # Python 2.7
        d = pickle.load(fo)
    elif sys.version_info.major == 3:
        # Python 3.4
        d = pickle.load(fo, encoding="latin-1")
    fo.close()
    return d

# Load train data.
data = list(map(unpickle, [os.path.join(dir, p) for p in paths]))
train_x = np.vstack([d["data"] for d in data])
train_y = np.vstack([d["labels"] for d in data])

# Load test data.
data = unpickle(os.path.join(dir, "test_batch"))
test_x = np.array(data["data"])
test_y = np.array(data["labels"])

# Rehsape and rescale image.
train_x = train_x.reshape(-1, 3, 32, 32)
train_y = train_y.reshape(-1, 1)
test_x = test_x.reshape(-1, 3, 32, 32)
test_y = test_y.reshape(-1, 1)

train_x = train_x / 255.
test_x = test_x / 255.

# Binalize
labels_train = LabelBinarizer().fit_transform(train_y)
labels_test = LabelBinarizer().fit_transform(test_y)

# Change types.
train_x = train_x.astype(np.float32)
test_x = test_x.astype(np.float32)
labels_train = labels_train.astype(np.float32)
labels_test = labels_test.astype(np.float32)

N = len(train_x)

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

畳み込みニューラルネットワークを定義します.使用するレイヤ数が多いので、下の例のようにSequentialモデルを用いて定義するのが簡単かもしれません.

In [4]:
class Cifar10(rm.Model):

    def __init__(self):
        super(Cifar10, self).__init__()
        self._l1 = rm.Conv2d(channel=32)
        self._l2 = rm.Conv2d(channel=32)
        self._l3 = rm.Conv2d(channel=64)
        self._l4 = rm.Conv2d(channel=64)
        self._l5 = rm.Dense(512)
        self._l6 = rm.Dense(10)
        self._sd = rm.SpatialDropout(dropout_ratio=0.25)
        self._pool = rm.MaxPool2d(filter=2, stride=2)

    def forward(self, x):
        t1 = rm.relu(self._l1(x))
        t2 = self._sd(self._pool(rm.relu(self._l2(t1))))
        t3 = rm.relu(self._l3(t2))
        t4 = self._sd(self._pool(rm.relu(self._l4(t3))))
        t5 = rm.flatten(t4)
        t6 = rm.dropout(rm.relu(self._l5(t5)))
        t7 = self._l6(t5)
        return t7

Sequentialモデルを用いたニューラルネットワークの定義

In [5]:
sequential = rm.Sequential([
        rm.Conv2d(channel=32),
        rm.BatchNormalize(),
        rm.Relu(),
        rm.Conv2d(channel=32),
        rm.BatchNormalize(),
        rm.Relu(),
        rm.MaxPool2d(filter=2, stride=2),
        rm.Dropout(dropout_ratio=0.25),
        rm.Conv2d(channel=64),
        rm.BatchNormalize(),
        rm.Relu(),
        rm.Conv2d(channel=64),
        rm.BatchNormalize(),
        rm.Relu(),
        rm.MaxPool2d(filter=2, stride=2),
        rm.Dropout(dropout_ratio=0.25),
        rm.Flatten(),
        rm.Dense(512),
        rm.Relu(),
        rm.Dropout(dropout_ratio=0.5),
        rm.Dense(10),
    ])

インスタンス化

In [6]:
# Choose neural network.
#network = Cifar10()
network = sequential
optimizer = Adam()

学習ループ

学習ループの中では、モデルのバリデーションを行うことを推奨します.モデルの学習過程を確認でき、過学習を早期に発見できる可能性があります.

In [7]:
# Hyper parameters
batch = 128
epoch = 20

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 = train_x[perm[j * batch:(j + 1) * batch]]
        responce_batch = labels_train[perm[j * batch:(j + 1) * batch]]

        # Loss function
        network.set_models(inference=False)
        with network.train():
            l = rm.softmax_cross_entropy(network(train_batch), responce_batch)

        # Back propagation
        grad = l.grad()

        # Update
        grad.update(optimizer)
        loss += l.as_ndarray()

    train_loss = loss / (N // batch)

    # Validation
    test_loss = 0
    M = len(test_x)
    network.set_models(inference=True)
    for j in range(M//batch):
        test_batch = test_x[j * batch:(j + 1) * batch]
        test_label_batch = labels_test[j * batch:(j + 1) * batch]
        prediction = network(test_batch)
        test_loss += rm.softmax_cross_entropy(prediction, test_label_batch).as_ndarray()
    test_loss /= (j+1)

    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:1.562733 test_loss:1.274054
epoch 001 train_loss:1.266876 test_loss:1.160762
epoch 002 train_loss:1.160666 test_loss:1.098638
epoch 003 train_loss:1.098712 test_loss:1.064242
epoch 004 train_loss:1.047783 test_loss:1.001854
epoch 005 train_loss:1.010702 test_loss:1.019032
epoch 006 train_loss:0.975439 test_loss:0.969194
epoch 007 train_loss:0.945626 test_loss:0.967939
epoch 008 train_loss:0.925219 test_loss:0.940706
epoch 009 train_loss:0.900114 test_loss:0.937168
epoch 010 train_loss:0.882823 test_loss:0.920384
epoch 011 train_loss:0.862696 test_loss:0.978997
epoch 012 train_loss:0.845808 test_loss:0.915230
epoch 013 train_loss:0.838032 test_loss:0.920868
epoch 014 train_loss:0.820599 test_loss:0.901738
epoch 015 train_loss:0.805465 test_loss:0.897759
epoch 016 train_loss:0.792367 test_loss:0.893579
epoch 017 train_loss:0.778425 test_loss:0.906781
epoch 018 train_loss:0.773982 test_loss:0.906565
epoch 019 train_loss:0.759154 test_loss:0.893892

モデルの評価

最後にモデルの評価を行います。

In [8]:
network.set_models(inference=True)
predictions = np.argmax(network(test_x).as_ndarray(), axis=1)

# Confusion matrix and classification report.
print(confusion_matrix(test_y, predictions))
print(classification_report(test_y, 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()
[[728  14  46  35   9   6  23  14  85  40]
 [ 22 732  10  22   1   3  11   1  37 161]
 [ 65   2 535  98  78  72  77  47  11  15]
 [ 17   3  55 580  44 166  85  25   8  17]
 [ 20   3  87 109 538  39 113  76  10   5]
 [ 13   2  50 227  31 577  38  45   7  10]
 [  5   3  32  73  19  19 835   3   2   9]
 [ 10   3  28  67  41  68  13 750   3  17]
 [ 51  28  23  20   3   3  15   3 819  35]
 [ 37  44   5  25   6   8  13  20  30 812]]
             precision    recall  f1-score   support

          0       0.75      0.73      0.74      1000
          1       0.88      0.73      0.80      1000
          2       0.61      0.54      0.57      1000
          3       0.46      0.58      0.51      1000
          4       0.70      0.54      0.61      1000
          5       0.60      0.58      0.59      1000
          6       0.68      0.83      0.75      1000
          7       0.76      0.75      0.76      1000
          8       0.81      0.82      0.81      1000
          9       0.72      0.81      0.77      1000

avg / total       0.70      0.69      0.69     10000

../../../_images/notebooks_basic_batch_normalization_notebook_16_1.png