6.2 重みの初期値 【ディープラーニング入門:Pythonによる理論と実装(第6章 学習に関するスキル)】
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
return 1 / (1 + np.exp(-x))
消失勾配
x = np.random.randn(1000, 100) # 1000个数据,每个数据100个特征
hidden_layer_size = 5 # 5个隐藏层
node_num = 100 # 每个隐藏层100个节点
activations = {
}
for i in range(hidden_layer_size):
if i!=0:
x = activations[i-1]
w = np.random.randn(node_num, node_num) * 1
z = np.dot(x, w)
a = sigmoid(z)
activations[i] = a
for i, a in activations.items():
plt.subplot(1 ,len(activations), i+1)
plt.title(str(i+1) + '-layer')
plt.hist(a.flatten(), 30, range=(0,1))
plt.show()
各レイヤーの活性値は0と1の方向に配分されます。ここで使用するシグモイド関数は S 型関数です。出力が 0 または 1 に近づき続けると、その導関数の値は 0 に近づきます。そのため、1 と 0 に偏ったデータ分布により、勾配の値が発生します。連続的に変化するバックプロパゲーション。小さく、最終的には消滅します。勾配消失問題として知られています。
重みを標準偏差 0.01 のガウス分布に変更した後
限られた表現力
x = np.random.randn(1000, 100) # 1000个数据,每个数据100个特征
hidden_layer_size = 5 # 5个隐藏层
node_num = 100 # 每个隐藏层100个节点
activations = {
}
for i in range(hidden_layer_size):
if i!=0:
x = activations[i-1]
w = np.random.randn(node_num, node_num) * 0.01
z = np.dot(x, w)
a = sigmoid(z)
activations[i] = a
for i, a in activations.items():
plt.subplot(1 ,len(activations), i+1)
plt.title(str(i+1) + '-layer')
plt.hist(a.flatten(), 30, range=(0,1))
plt.show()
今回は活性値の分布が0.5付近に集中しています。勾配消失の問題は発生しませんが、活性値の分布が偏り、表現力に大きな問題があります。なぜそんなことを言うのですか?なぜなら、すべてがほぼ同じ値を出力する複数のニューロンが存在する場合、それらは存在する意味がなくなるからです。たとえば、100 個のニューロンがすべてほぼ同じ値を出力している場合、1 個のニューロンも基本的に同じことを言うことができます。そのため、活性値が分布に偏ると「表現力が限られる」という問題が生じる。
各層の活性値の分布には適切な幅が必要です。なぜ?層間で多様なデータを渡すことで、ニューラル ネットワークが効率的に学習できるためです。逆に偏ったデータを流すと、勾配の消失や「表現力の制限」といった問題が発生し、学習がスムーズに進まない可能性があります。
ザビエルの初期値
各層の活性値が同じ幅の分布を示すようにするために、適切な重み付けスケールに押し付けられます。推定された結論は、前の層のノード数が n の場合、初期値には1 n \frac{1}{\sqrt{n}}の標準偏差が使用されるということです。n1分布
x = np.random.randn(1000, 100) # 1000个数据,每个数据100个特征
hidden_layer_size = 5 # 5个隐藏层
node_num = 100 # 每个隐藏层100个节点
activations = {
}
for i in range(hidden_layer_size):
if i!=0:
x = activations[i-1]
w = np.random.randn(node_num, node_num) / np.sqrt(node_num)
z = np.dot(x, w)
a = sigmoid(z)
activations[i] = a
for i, a in activations.items():
plt.subplot(1 ,len(activations), i+1)
plt.title(str(i+1) + '-layer')
# if i != 0: plt.yticks([], [])
# plt.ylim(0, 6000)
plt.hist(a.flatten(), 30, range=(0,1))
plt.show()
レイヤーが奥になるほど、画像の歪みは大きくなりますが、以前よりも広く分布します。層間で受け渡されるデータには適度な幅があるため、シグモイド関数の表現力に制限がなくなり、効率的な学習が期待できます。
胡散臭い
ターン関数に置き換えると、形状は釣鐘型?活性化関数として使用される関数は、原点に対して対称である性質を有することが好ましい。
def tanh(x):
return np.tanh(x)
x = np.random.randn(1000, 100) # 1000个数据,每个数据100个特征
hidden_layer_size = 5 # 5个隐藏层
node_num = 100 # 每个隐藏层100个节点
activations = {
}
for i in range(hidden_layer_size):
if i!=0:
x = activations[i-1]
w = np.random.randn(node_num, node_num) / np.sqrt(node_num)
z = np.dot(x, w)
a = tanh(z)
activations[i] = a
for i, a in activations.items():
plt.subplot(1 ,len(activations), i+1)
plt.title(str(i+1) + '-layer')
# if i != 0: plt.yticks([], [])
# plt.ylim(0, 6000)
plt.hist(a.flatten(), 30, range=(0,1))
plt.show()
ReLUの重み初期値
Xaiver の初期値は活性化関数が一次関数であることに基づいて導出されており、シグモイドとタンハ関数は左右対称で中心付近は一次関数とみなせるため、ザイバー。
活性化関数がReLUを使用する場合、He初期値と呼ばれるReLU専用の初期値を使用することを推奨します。
前の層のノード数が n の場合、He の初期値には2 n \sqrt{\frac{2}{n}}の標準偏差が使用されます。n2
Xaiver の初期値が1 n \sqrt{\frac{1}{n}}の場合n1とすると、ReLUの負の値領域の値が0なので、より広範囲にするためには2倍の係数が必要になると説明できます
def ReLU(x):
return np.maximum(0 ,x)
重みの初期値は標準偏差 0.01 のガウス分布です。
x = np.random.randn(1000, 100) # 1000个数据,每个数据100个特征
hidden_layer_size = 5 # 5个隐藏层
node_num = 100 # 每个隐藏层100个节点
activations = {
}
for i in range(hidden_layer_size):
if i!=0:
x = activations[i-1]
w = np.random.randn(node_num, node_num) * 0.01
z = np.dot(x, w)
a = ReLU(z)
activations[i] = a
for i, a in activations.items():
plt.subplot(1 ,len(activations), i+1)
plt.title(str(i+1) + '-layer')
# if i != 0: plt.yticks([], [])
# plt.xlim(0.1, 1)
plt.ylim(0, 7000)
plt.hist(a.flatten(), 30, range=(0,1))
plt.show()
体重の初期値がザイバーの初期値の場合
x = np.random.randn(1000, 100) # 1000个数据,每个数据100个特征
hidden_layer_size = 5 # 5个隐藏层
node_num = 100 # 每个隐藏层100个节点
activations = {
}
for i in range(hidden_layer_size):
if i!=0:
x = activations[i-1]
w = np.random.randn(node_num, node_num) * np.sqrt(1.0 / node_num)
z = np.dot(x, w)
a = ReLU(z)
activations[i] = a
for i, a in activations.items():
plt.subplot(1 ,len(activations), i+1)
plt.title(str(i+1) + '-layer')
# if i != 0: plt.yticks([], [])
# plt.xlim(0.1, 1)
plt.ylim(0, 7000)
plt.hist(a.flatten(), 30, range=(0,1))
plt.show()
重みの初期値がHeの初期値の場合
x = np.random.randn(1000, 100) # 1000个数据,每个数据100个特征
hidden_layer_size = 5 # 5个隐藏层
node_num = 100 # 每个隐藏层100个节点
activations = {
}
for i in range(hidden_layer_size):
if i!=0:
x = activations[i-1]
w = np.random.randn(node_num, node_num) * np.sqrt(2.0 / node_num)
z = np.dot(x, w)
a = ReLU(z)
activations[i] = a
for i, a in activations.items():
plt.subplot(1 ,len(activations), i+1)
plt.title(str(i+1) + '-layer')
# if i != 0: plt.yticks([], [])
plt.xlim(0.1, 1)
plt.ylim(0, 7000)
plt.hist(a.flatten(), 30, range=(0,1))
plt.show()
要約する
- std=0.01の場合、各層の活性化値は非常に小さく、ニューラルネットワーク上に送信される値も非常に小さく、逆伝播時の重みの傾きも非常に小さいことがわかります。
- 初期値がXaiverの場合、層数が深くなるにつれて徐々に偏りが大きくなります
- 初期値が He の場合、各層の分布の幅は同じになります