一緒に書く習慣をつけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加して6日目です。クリックしてイベントの詳細をご覧ください。
ゼロからのバックプロパゲーションの構築
順方向伝搬では、非表示レイヤーを持つ入力レイヤーを出力レイヤーに接続します。バックプロパゲーションでは、反対のプロセスを使用します。ニューラルネットワークの各重みは、一度に少しずつ変更されます。ウェイト値の変更は、最終的な損失値(損失の増加または減少)に影響を与えるため、損失を減らす方向にウェイトを更新する必要があります。毎回重みを少しずつ更新し、重みの更新による誤差の変化を測定することで、次のことが可能になります。
- 体重の更新の方向を決定する
- 重みの更新の大きさを決定する
バックプロパゲーションを実装する前に、まずニューラルネットワークのもう1つの重要な概念である学習率を理解しましょう。学習率は、より安定したアルゴリズムを構築するのに役立ちます。たとえば、ウェイトの更新のサイズを変更する場合、一度に大きな変更を加えることはありませんが、ウェイトをゆっくり更新するために、より慎重なアプローチを取ります。これにより、モデルの安定性が向上します。後の学習では、学習率が安定性の向上にどのように役立つかについても見ていきます。エラーを減らすために重みを更新するプロセス全体は最急降下法と呼ばれ、確率的勾配降下法はエラーを最小限に抑える手段です。より直感的には、勾配は差(つまり、実際の値と予測値の差)を表し、下降は差の減少を表します。ランダムは、トレーニングするランダムサンプルを選択し、それに基づいて決定を行うことを表します。確率的勾配降下法に加えて、損失値を減らすために使用できる他の多くの最適化手法があります。後の研究では、さまざまな最適化手法について説明します。
バックプロパゲーションは次のように機能します。
- 損失値は、順方向伝搬プロセスを使用して計算されます。
- すべての重みを少し変更します。
- 損失関数に対する体重変化の影響を計算します。
- 重みの更新によって損失値が増加するか減少するかに応じて、重み値は損失減少の方向に更新されます。
1と呼ばれる、データセット内のすべてのデータに対して1つのトレーニングプロセス(順方向伝搬+逆方向伝搬)を実行しますepoch
。ニューラルネットワークでのバックプロパゲーションの理解をさらに固めるために、既知の単純な関数を当てはめて、重みがどのように導出されるかを見てみましょう。当てはめる関数が
、重みとバイアスの値(それぞれ3と0)を期待します。
バツ | 1 | 3 | 4 | 8 | 10 |
---|---|---|---|---|---|
Y | 3 | 9 | 12 | 24 | 30 |
上記のデータセットは線形回帰として表すことができます 、計算を試みます と の値(2と0であることがわかっていますが、目的は最急降下法を使用してこれらの値を取得する方法を研究することです)については、 と パラメータは次のようにランダムに初期化されます および 値次に、ニューラルネットワークで重みがどのように計算されるかを明確に理解するために、バックプロパゲーションアルゴリズムを最初から構築します。簡単にするために、隠れ層のない単純なニューラルネットワークを構築します。
- 次のようにデータセットを初期化します。
x = np.array([[1], [3], [4], [8], [10]])
y = np.array([[3], [9], [12], [24], [30]])
复制代码
- 重みとバイアス値をランダムに初期化します(決定しようとした後) 方程式の と の最適値には、1つの重みと1つのバイアスのみが必要です)。
w = np.array([[[2.269]], [[1.01]]])
复制代码
- ニューラルネットワークを定義し、二乗誤差損失値を計算します。
import numpy as np
def feed_forward(inputs, outputs, weights):
out = np.dot(inputs, weights[0]) + weights[1]
squared_error = np.square(out - outputs)
return squared_error
复制代码
上記のコードでは、入力は行列にランダムに初期化された重み値を掛けてから、ランダムに初期化されたバイアス値を加算したものです。出力値が取得されると、実際の値と予測値の差の二乗誤差値を計算できます。
- 少量增加每个权重和偏置值,并针对每个权重和偏差更新一次计算一个平方误差损失值。
如果平方误差损失值随权重的增加而减小,则权重值应增加,权重值应增加的大小与权重变化减少的损失值的大小成正比。反之亦然。另外,通过学习率确保增加的权重值小于因权重变化而导致的损失值变化,这样可以确保损失值更平稳地减小。 接下来,创建一个名为 update_weights
的函数,该函数执行反向传播过程以更新在权重,该函数运行 epochs
次:
from copy import deepcopy
def update_weights(inputs, outputs, weights, epochs):
for epoch in range(epochs):
复制代码
- 将输入通过神经网络传递,以计算权重未更新时的损失:
org_loss = feed_forward(inputs, outputs, weights)
复制代码
- 确保对权重列表进行深复制,由于权重将在后续步骤中进行操作,深复制可解决由于子变量的更改而影响父变量的问题:
wts_tmp = deepcopy(weights)
wts_tmp2 = deepcopy(weights)
复制代码
- 循环遍历所有权重值,然后对其进行较小的更改 (
+0.0001
):
for ix, wt in enumerate(weights):
wts_tmp[ix] += 0.0001
复制代码
- 当权重修改后,计算更新的前向传播损失。计算由于权重的微小变化而造成的损失变化,因为我们要计算所有输入采样的均方误差,因此将损失的变化除以输入的数据数量:
loss = feed_forward(inputs, outputs, wts_tmp)
del_loss = np.sum(org_loss - loss)/(0.0001*len(inputs))
复制代码
以较小的值更新权重,然后计算其对损失值的影响,等效于计算权重变化的导数(即反向梯度传播)。
- 通过损失变化来更新权重。通过将损失的变化乘以一个很小的数字(0.01)来缓慢更新权重,这就是学习率参数:
wts_tmp2[ix] += del_loss*0.01
wts_tmp = deepcopy(weights)
复制代码
- 返回更新的权重和偏差值:
weights = deepcopy(wts_tmp2)
return wts_tmp2
复制代码
整体 update_weights()
函数如下所示:
from copy import deepcopy
def update_weights(inputs, outputs, weights, epochs):
for epoch in range(epochs):
org_loss = feed_forward(inputs, outputs, weights)
wts_tmp = deepcopy(weights)
wts_tmp2 = deepcopy(weights)
for ix, wt in enumerate(weights):
wts_tmp[ix] += 0.0001
loss = feed_forward(inputs, outputs, wts_tmp)
del_loss = np.sum(org_loss - loss)/(0.0001*len(inputs))
wts_tmp2[ix] += del_loss*0.01
wts_tmp = deepcopy(weights)
weights = deepcopy(wts_tmp2)
return wts_tmp2
复制代码
通过更新网络 1000 次,查看训练后网络中的参数和偏置值:
weights = update_weights(x, y, w, 1000)
print(weights)
复制代码
打印权重如下所示,可以看到其与预期的结果 (w=3.0
, b=0.0
) 非常接近:
[[[2.99929065]]
[[0.00478785]]]
复制代码
ニューラルネットワークのもう1つの重要なパラメータはbatch size
、損失値を計算するときに考慮する必要があるバッチサイズ()です。上記の例では、すべてのデータの損失値を同時に計算します。ただし、数万のデータがある場合、損失値を計算するときに大量のデータを追加することによる増分の寄与は、トレーニングの困難を引き起こし、計算するメモリ制限を超えることさえあるため、通常epoch
、データは複数のbatch
送信に分割されますトレーニング用にネットワークに入ると、モデルを構築するときに一般的に使用されるbatch
サイズはです。