6.2 重みの初期値

「ゼロから作るDeep Learning」 6章2節の内容を学習しながら、重みの初期値の影響を調べます。

6.2.2 隠れ層のアクティベーション分布

実験に必要な活性化関数などを定義します。

import numpy as np

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def ReLU(x):
    return np.maximum(0, x)

def tanh(x):
    return np.tanh(x)

def xavier(n):
    return np.sqrt(1 / n)

def he(n):
    return np.sqrt(2 / n)

[1] 2019-06-12 17:36:08 (15.5ms) python3 (15.5ms)

隠れ層を活性化させるジェネレータと可視化関数を定義します。

import matplotlib.pyplot as plt

input_data = np.random.randn(1000, 100)  # 1000個のデータ
node_num = 100  # 各隠れ層のノード(ニューロン)の数
hidden_layer_size = 5  # 隠れ層が5層

def evalute(activate, std):
    weight = np.random.randn(node_num, node_num) * std
    x = input_data
    for _ in range(hidden_layer_size):
        x = activate(x @ weight)
        yield x

def plot(activations, ylim=None):
    plt.figure(figsize=(12, 3))
    for i, a in enumerate(activations):
        plt.subplot(1, hidden_layer_size, i + 1)
        plt.title(str(i + 1) + "-layer")
        if i != 0:
            plt.yticks([], [])
        if ylim:
            plt.ylim(0, ylim)
        plt.hist(a.flatten(), 30, range=(0, 1))

[2] 2019-06-12 17:36:08 (141ms) python3 (156ms)

image/png

Figure 6.10 evalute(sigmoid, std=1)

image/png

Figure 6.11 evalute(sigmoid, std=0.01)

image/png

Figure 6.13 evalute(sigmoid, std=xavier(node_num))

6.2.3 ReLUの場合の重みの初期値

image/png

Figure 6.14.1 evalute(ReLU, std=0.01)

image/png

Figure 6.14.2 evalute(ReLU, std=xavier(node_num))

image/png

Figure 6.14.3 evalute(ReLU, std=he(node_num))

6.2.4 MNISTデータセットによる重みの初期値の比較

ch06/weight_init_compare.pyおよびcommon/multi_layer_net.pyを参考にして、重みの初期値が学習に与える影響について実験を行います。

Ivoryライブラリでは、重みを正規分布で初期化するとき、標準偏差には数値のほかに、XavierおよびHeの初期値を指定することもできます。

from ivory.layers.affine import Affine

affine = Affine((100, 50))
affine.set_variables()
W = affine.W.variable
print("Xavier", W.init("xavier").std())  # np.sqrt(1/100) ≒ 0.10
print("He    ", W.init("he").std())  # np.sqrt(2/100) ≒ 0.14

[9] 2019-06-12 17:36:11 (203ms) python3 (3.34s)

Xavier 0.098941
He     0.14444008

ここで注意することは、Variableインスタンスのinitメソッドは、初期化された値を返しますが、自分自身の値は更新しないことです。

以上の方法を使って、課題を準備します。

from ivory.core.model import sequential
from ivory.core.optimizer import SGD
from ivory.datasets.mnist import load_dataset

data = load_dataset(train_only=True)
data.batch_size = 128
data.epochs = -1
data.random = True

net = [
    ("input", 784),
    (4, "affine", 100, "relu"),
    ("affine", 10, "softmax_cross_entropy"),
]
model = sequential(net)
optimizer = SGD(learning_rate=0.01)
optimizer.set_model(model)

[10] 2019-06-12 17:36:12 (1.83s) python3 (5.17s)

続いて訓練を行うジェネレータ関数を定義します。

def train(std):
    print(std)

    for p in model.weights:
        if p.name == "W":
            p.variable.data = p.variable.init(std)  # 明示的に値を代入する必要がある
        else:
            p.variable.data = p.variable.init()  # バイアス

    for _, (x, t) in zip(range(2000), data):
        model.set_data(x, t)
        model.forward()
        yield model.loss
        model.backward()
        optimizer.update()

[11] 2019-06-12 17:36:14 (15.6ms) python3 (5.19s)

訓練を実行します。

stds = [0.01, "xavier", "he"]
result = [list(train(std)) for std in stds]

[12] 2019-06-12 17:36:14 (20.6s) python3 (25.8s)

0.01
xavier
he

可視化を行います。

from scipy.signal import savgol_filter

markers = ["o", "s", "d"]
for k, x in enumerate(savgol_filter(result, 9, 3)):
    plt.plot(x, marker=markers[k], markevery=100, label=str(stds[k]))
plt.xlabel("iterations")
plt.ylabel("loss")
plt.xlim(0, 2000)
plt.ylim(0, 2.5)
plt.legend()
plt.show()

[13] 2019-06-12 17:36:34 (328ms) python3 (26.1s)

image/png