重みの正則化

ReNomを使ったL2正則化の手法

このチュートリアルでは全結合ニューラルネットワークを用いて数字認識モデルを構築します. チュートリアルを通して以下の点を紹介します.

  • 正則化としてのL2ノルムの計算方法

必要なライブラリ

  • 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データのロード

最初に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)

L2正則化を用いたニューラルネットワークモデル

Weight decayは正則化の手法の一つです.
過学習が起こる理由はいくつかあると思いますが,その理由の一つにデータのアンバランスがあると思っています.
毎エポックでバックプロパゲーションによってパラメータを学習して更新していきますが,
特に量の多いデータのクラスに対しては大きくパラメータが更新されていくことになります.
そこでもしアンバランスなデータでパラメータの更新にある程度制約を持たせたいときにはweight decayを用いることをお勧めします.
In [3]:
class Mnist(rm.Model):

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

    def forward(self, x):
        out = self._layer2(rm.relu(self._layer1(x)))
        return out

    def weight_decay(self):
        weight_decay = rm.sum(self._layer1.params.w**2) + rm.sum(self._layer2.params.w**2)
        return weight_decay

インスタンス化

In [4]:
# Choose neural network.
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.337253 test_loss:0.191109
epoch 001 train_loss:0.189557 test_loss:0.148555
epoch 002 train_loss:0.149692 test_loss:0.122226
epoch 003 train_loss:0.128814 test_loss:0.103989
epoch 004 train_loss:0.115948 test_loss:0.096587
epoch 005 train_loss:0.107144 test_loss:0.089462
epoch 006 train_loss:0.099740 test_loss:0.086061
epoch 007 train_loss:0.094767 test_loss:0.082598
epoch 008 train_loss:0.090281 test_loss:0.080142
epoch 009 train_loss:0.086551 test_loss:0.081469

モデルの評価

学習が終了したモデルを評価します. ここでは混同行列を用いて,各クラスに属するテストデータの正解割合を表示しました. また,テストデータに対する,再現率,適合率,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()
[[707   0   1   1   2   2   2   0   5   0]
 [  0 797   2   0   0   1   1   1   1   0]
 [  1   0 663   1   0   0   1   9   1   0]
 [  0   1   8 690   0   8   1   7   5   3]
 [  0   2   0   0 631   0   3   0   0  10]
 [  2   1   2   1   0 655   7   0   2   1]
 [  1   0   1   0   3   1 689   0   3   0]
 [  0   0   5   0   1   0   0 708   0   3]
 [  1   3   6   2   2   8   2   1 630   3]
 [  0   1   0   1  12   2   1  10   2 659]]
             precision    recall  f1-score   support

        0.0       0.99      0.98      0.99       720
        1.0       0.99      0.99      0.99       803
        2.0       0.96      0.98      0.97       676
        3.0       0.99      0.95      0.97       723
        4.0       0.97      0.98      0.97       646
        5.0       0.97      0.98      0.97       671
        6.0       0.97      0.99      0.98       698
        7.0       0.96      0.99      0.97       717
        8.0       0.97      0.96      0.96       658
        9.0       0.97      0.96      0.96       688

avg / total       0.98      0.98      0.98      7000

../../../_images/notebooks_basic_algorithm_weight_decay_notebook_12_1.png