神经网路反向传播算法
以下是一个简单的反向传播算法的神经网络的实现:
假设我们有一个三层的神经网络,输入层有3个神经元,隐层有4个神经元,输出层有2个神经元。我们使用sigmoid函数作为激活函数。
- 初始化权重和偏置
我们需要随机初始化权重和偏置。假设输入层到隐层的权重矩阵为W1,大小为4x3,隐层到输出层的权重矩阵为W2,大小为2x4。偏置向量b1和b2都是大小为4x1和2x1的列向量。
import numpy as np
# 随机初始化权重和偏置
W1 = np.random.randn(4, 3)
W2 = np.random.randn(2, 4)
b1 = np.zeros((4, 1))
b2 = np.zeros((2, 1))
- 前向传播
对于一个输入样本x,我们需要执行前向传播来计算输出层的预测值。假设输入样本为大小为3x1的列向量。我们将它传递给隐藏层和输出层,并使用sigmoid函数作为激活函数。
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def forward_propagation(x, W1, W2, b1, b2):
# 隐藏层输入
z1 = np.dot(W1, x) + b1
# 隐藏层输出
a1 = sigmoid(z1)
# 输出层输入
z2 = np.dot(W2, a1) + b2
# 输出层输出
a2 = sigmoid(z2)
return a1, a2
x = np.random.randn(3, 1)
a1, a2 = forward_propagation(x, W1, W2, b1, b2)
- 计算误差
我们需要计算预测值和实际值之间的误差。假设我们有一个大小为2x1的目标向量y。
y = np.array([[0.5], [0.8]])
我们可以使用平方误差函数来计算误差。
def compute_cost(a2, y):
m = y.shape[1]
cost = np.sum(np.square(a2 - y)) / (2 * m)
return cost
cost = compute_cost(a2, y)
- 反向传播
现在我们可以执行反向传播来计算梯度,并更新权重和偏置。我们需要计算输出层的误差项d2和隐藏层的误差项d1。对于输出层的误差项d2,我们可以使用下面的公式:
d2 = (a2 - y) * sigmoid(z2) * (1 - sigmoid(z2))
对于隐藏层的误差项d1,我们可以使用下面的公式:
d1 = np.dot(W2.T, d2) * sigmoid(z1) * (1 - sigmoid(z1))
现在我们可以计算权重和偏置的梯度了。
def backward_propagation(x, y, a1, a2, W1, W2, b1, b2):
m = y.shape[1]
# 计算输出层误差项
d2 = (a2 - y) * sigmoid(z2) * (1 - sigmoid(z2))
# 计算隐藏层误差项
d1 = np.dot(W2.T, d2) * sigmoid(z1) * (1 - sigmoid(z1))
# 计算权重和偏置的梯度
dW2 = np.dot(d2, a1.T) / m
db2 = np.sum(d2, axis=1, keepdims=True) / m
dW1 = np.dot(d1, x.T) / m
db1 = np.sum(d1, axis=1, keepdims=True) / m
return dW1, db1, dW2, db2
dW1, db1, dW2, db2 = backward_propagation(x, y, a1, a2, W1, W2, b1, b2)
- 更新权重和偏置
现在我们可以使用梯度下降来更新权重和偏置。
def update_parameters(W1, b1, W2, b2, dW1, db1, dW2, db2, learning_rate):
# 更新权重和偏置
W1 = W1 - learning_rate * dW1
b1 = b1 - learning_rate * db1
W2 = W2 - learning_rate * dW2
b2 = b2 - learning_rate * db2
return W1, b1, W2, b2
learning_rate = 0.01
W1, b1, W2, b2 = update_parameters(W1, b1, W2, b2, dW1, db1, dW2, db2, learning_rate)
以上就是一个简单的反向传播算法的神经网络的实现。
m = y.shape[1]是干嘛的
m是代表样本数的参数,y的shape是(2, 1),其中2表示有2个输出,1表示只有1个样本。因此,m=y.shape[1]就是将样本数赋值给m。在计算代价函数和梯度时,需要对每个样本的误差进行平均,所以需要知道样本数m。
为什么对于输出层的误差项d2= (a2 - y) * sigmoid(z2) * (1 - sigmoid(z2))
对于输出层的误差项
d
2
d_2
d2,我们可以根据链式法则写出:
d
2
=
∂
J
∂
z
2
=
∂
J
∂
a
2
⋅
∂
a
2
∂
z
2
d_2 = \frac{\partial J}{\partial z_2} = \frac{\partial J}{\partial a_2} \cdot \frac{\partial a_2}{\partial z_2}
d2=∂z2∂J=∂a2∂J⋅∂z2∂a2
其中,
J
J
J是代价函数,
a
2
a_2
a2是输出层的输出结果,
z
2
z_2
z2是经过输出层的加权输入。根据神经网络的定义,我们知道:
a
2
=
σ
(
z
2
)
a_2 = \sigma(z_2)
a2=σ(z2)
其中,
σ
\sigma
σ是sigmoid函数,其表达式为:
σ
(
x
)
=
1
1
+
e
−
x
\sigma(x) = \frac{1}{1+e^{-x}}
σ(x)=1+e−x1
对sigmoid函数求导可得:
∂
σ
(
x
)
∂
x
=
σ
(
x
)
⋅
(
1
−
σ
(
x
)
)
\frac{\partial \sigma(x)}{\partial x} = \sigma(x) \cdot (1-\sigma(x))
∂x∂σ(x)=σ(x)⋅(1−σ(x))
将上述式子代入
d
2
d_2
d2的计算式中,可得:
d
2
=
∂
J
∂
a
2
⋅
σ
(
z
2
)
⋅
(
1
−
σ
(
z
2
)
)
=
(
a
2
−
y
)
⋅
σ
(
z
2
)
⋅
(
1
−
σ
(
z
2
)
)
d_2 = \frac{\partial J}{\partial a_2} \cdot \sigma(z_2) \cdot (1-\sigma(z_2)) = (a_2 - y) \cdot \sigma(z_2) \cdot (1-\sigma(z_2))
d2=∂a2∂J⋅σ(z2)⋅(1−σ(z2))=(a2−y)⋅σ(z2)⋅(1−σ(z2))
其中,
y
y
y是样本的真实输出,即目标值。所以,对于输出层的误差项
d
2
d_2
d2,我们可以通过求解代价函数关于输出层输出结果
a
2
a_2
a2的偏导数,再乘以sigmoid函数的导数得到。
为什么隐藏层的误差项d1 = np.dot(W2.T, d2) * sigmoid(z1) * (1 - sigmoid(z1))
对于隐藏层的误差项
d
1
d_1
d1,我们同样可以根据链式法则写出:
d
1
=
∂
J
∂
z
1
=
∂
J
∂
a
1
⋅
∂
a
1
∂
z
1
d_1 = \frac{\partial J}{\partial z_1} = \frac{\partial J}{\partial a_1} \cdot \frac{\partial a_1}{\partial z_1}
d1=∂z1∂J=∂a1∂J⋅∂z1∂a1
其中,
a
1
a_1
a1是隐藏层的输出结果,
z
1
z_1
z1是经过隐藏层的加权输入,
J
J
J是代价函数。根据神经网络的定义,我们知道:
a
1
=
σ
(
z
1
)
a_1 = \sigma(z_1)
a1=σ(z1)
对sigmoid函数求导可得:
∂
σ
(
x
)
∂
x
=
σ
(
x
)
⋅
(
1
−
σ
(
x
)
)
\frac{\partial \sigma(x)}{\partial x} = \sigma(x) \cdot (1-\sigma(x))
∂x∂σ(x)=σ(x)⋅(1−σ(x))
将上述式子代入
d
1
d_1
d1的计算式中,可得:
d
1
=
∂
J
∂
a
1
⋅
σ
(
z
1
)
⋅
(
1
−
σ
(
z
1
)
)
=
(
∑
i
=
1
m
W
2
,
i
⋅
d
2
,
i
)
⋅
σ
(
z
1
)
⋅
(
1
−
σ
(
z
1
)
)
d_1 = \frac{\partial J}{\partial a_1} \cdot \sigma(z_1) \cdot (1-\sigma(z_1)) = \left(\sum_{i=1}^{m} W_{2,i} \cdot d_{2,i}\right) \cdot \sigma(z_1) \cdot (1-\sigma(z_1))
d1=∂a1∂J⋅σ(z1)⋅(1−σ(z1))=(i=1∑mW2,i⋅d2,i)⋅σ(z1)⋅(1−σ(z1))
其中,
W
2
,
i
W_{2,i}
W2,i表示第二层权重矩阵的第
i
i
i列,
d
2
,
i
d_{2,i}
d2,i表示第
i
i
i个样本在第二层的误差项。因此,我们可以用矩阵乘法
np.dot
(
W
2
.
T
,
d
2
)
\text{np.dot}(W_2.T, d_2)
np.dot(W2.T,d2)来计算
∑
i
=
1
m
W
2
,
i
⋅
d
2
,
i
\sum_{i=1}^{m} W_{2,i} \cdot d_{2,i}
∑i=1mW2,i⋅d2,i。所以,对于隐藏层的误差项
d
1
d_1
d1,我们可以通过求解代价函数关于隐藏层输出结果
a
1
a_1
a1的偏导数,再乘以sigmoid函数的导数得到。
如何计算权重和偏置的梯度
权重和偏置的梯度可以通过反向传播算法计算得到。具体计算方法如下:
-
首先,根据输入样本和当前的权重和偏置,前向计算出每一层的加权输入 z z z和输出 a a a。
-
然后,计算输出层的误差项 d 2 d_2 d2,即 d 2 = ( a 2 − y ) ⋅ σ ( z 2 ) ⋅ ( 1 − σ ( z 2 ) ) d_2 = (a_2 - y) \cdot \sigma(z_2) \cdot (1-\sigma(z_2)) d2=(a2−y)⋅σ(z2)⋅(1−σ(z2)),其中, y y y是样本的真实输出。
-
接着,计算隐藏层的误差项 d 1 d_1 d1,即 d 1 = np.dot ( W 2 . T , d 2 ) ⋅ σ ( z 1 ) ⋅ ( 1 − σ ( z 1 ) ) d_1 = \text{np.dot}(W_2.T, d_2) \cdot \sigma(z_1) \cdot (1-\sigma(z_1)) d1=np.dot(W2.T,d2)⋅σ(z1)⋅(1−σ(z1)),其中, W 2 W_2 W2是第二层权重矩阵。
-
然后,根据误差项 d d d和输出 a a a,计算每一层的偏置梯度 ∂ J ∂ b \frac{\partial J}{\partial b} ∂b∂J,其中, J J J是代价函数, ∂ J ∂ b \frac{\partial J}{\partial b} ∂b∂J表示代价函数关于偏置 b b b的偏导数。具体计算公式为: ∂ J ∂ b = d \frac{\partial J}{\partial b} = d ∂b∂J=d。
-
最后,根据误差项 d d d和输入 x x x,计算每一层的权重梯度 ∂ J ∂ W \frac{\partial J}{\partial W} ∂W∂J,其中, ∂ J ∂ W \frac{\partial J}{\partial W} ∂W∂J表示代价函数关于权重 W W W的偏导数。具体计算公式为: ∂ J ∂ W = x T ⋅ d \frac{\partial J}{\partial W} = x^T \cdot d ∂W∂J=xT⋅d。
以上计算步骤可以通过循环实现,从输出层到输入层依次计算每一层的梯度。最终得到的权重和偏置梯度可以用来更新模型的参数,从而使代价函数最小化。
为什么权重和偏置的梯度
dW2 = np.dot(d2, a1.T) / m
db2 = np.sum(d2, axis=1, keepdims=True) / m
dW1 = np.dot(d1, x.T) / m
db1 = np.sum(d1, axis=1, keepdims=True) / m
这里的权重和偏置的梯度是通过反向传播算法计算得到的。在训练神经网络时,我们需要通过不断调整权重和偏置的值来最小化代价函数。而梯度是代价函数对参数的偏导数,它表示了在当前点上函数增加最快的方向。因此,我们可以利用梯度信息来调整参数,使它们朝着更小的代价函数方向移动。
具体地,在反向传播算法中,我们首先计算了每一层的误差项 d d d,然后根据误差项和输入 x x x或输出 a a a计算了每一层的权重和偏置梯度。这些梯度告诉我们,如果我们想要减小代价函数,应该如何调整权重和偏置的值。例如,如果 d W 2 dW2 dW2是正的,那么我们需要减小 W 2 W2 W2的值,从而使代价函数减小。同样,如果 d b 2 db2 db2是正的,我们需要减小 b 2 b2 b2的值。通过这种方式,我们可以利用反向传播算法计算出梯度,并使用梯度下降等优化算法更新模型的参数,从而训练出更好的神经网络模型。
dW2 = np.dot(d2, a1.T) / m中,a1.T什么意思
在神经网络中, a ( l ) a^{(l)} a(l)表示第 l l l层的输出, z ( l ) z^{(l)} z(l)表示第 l l l层的加权输入。在反向传播算法中,我们需要计算每一层的误差项 d ( l ) d^{(l)} d(l),其中 d ( l ) d^{(l)} d(l)表示第 l l l层的误差项。
在计算第二层的权重梯度 d W 2 dW2 dW2时,我们需要用到第一层的输出 a ( 1 ) a^{(1)} a(1)。而 a ( 1 ) a^{(1)} a(1)是一个行向量,需要转置成列向量才能与误差项 d ( 2 ) d^{(2)} d(2)相乘。因此, a ( 1 ) T a^{(1)T} a(1)T表示将 a ( 1 ) a^{(1)} a(1)转置成列向量的结果,即第一层输出的转置。
具体地, a ( 1 ) T a^{(1)T} a(1)T的形状是 ( n ( 1 ) , 1 ) (n^{(1)}, 1) (n(1),1),其中 n ( 1 ) n^{(1)} n(1)表示第一层的神经元数量。 d ( 2 ) d^{(2)} d(2)的形状是 ( n ( 2 ) , m ) (n^{(2)}, m) (n(2),m),其中 n ( 2 ) n^{(2)} n(2)表示第二层的神经元数量, m m m表示训练样本数量。因此, d ( 2 ) , a ( 1 ) T ˙ \dot{d^{(2)}, a^{(1)T}} d(2),a(1)T˙的形状是 ( n ( 2 ) , n ( 1 ) ) (n^{(2)}, n^{(1)}) (n(2),n(1)),即第二层权重矩阵的形状,表示每个神经元与前一层的所有神经元之间的权重。
通过上述计算,我们得到了第二层权重的梯度 d W 2 dW2 dW2,它的形状与权重矩阵 W 2 W2 W2相同。
db2 = np.sum(d2, axis=1, keepdims=True) / m中,axis=1、keepdims=True什么意思
在神经网络反向传播算法中,计算偏置 b b b的梯度时,需要将误差项 d d d沿着样本维度求和,然后再除以样本数量 m m m,即 d B = 1 m ∑ i = 1 m d ( i ) dB = \frac{1}{m}\sum_{i=1}^{m}d^{(i)} dB=m1∑i=1md(i),其中 d ( i ) d^{(i)} d(i)是第 i i i个样本的误差项。
在使用Numpy进行计算时,可以使用np.sum
函数对误差项沿着样本维度进行求和。axis=1
表示沿着第二个维度(即列维度)进行求和,从而得到每个神经元的误差项和。keepdims=True
表示保持结果的维度不变,即保持二维数组的形状。这样得到的结果的形状是
(
n
(
l
)
,
1
)
(n^{(l)}, 1)
(n(l),1),即每个神经元的误差项和。
然后再将这个结果除以样本数量 m m m,即可得到偏置 b b b的梯度。最终得到的 d B dB dB的形状与偏置 b b b的形状相同,即 ( n ( l ) , 1 ) (n^{(l)}, 1) (n(l),1),其中 n ( l ) n^{(l)} n(l)表示第 l l l层的神经元数量。
db2 = np.sum(d2, axis=1, keepdims=True) / m中,为什么沿着第二个维度(即列维度)进行求和
在神经网络中,每个神经元都对应一个偏置 b b b。对于第 l l l层的第 j j j个神经元,它的偏置 b j ( l ) b_j^{(l)} bj(l)对应的是第 l l l层的所有神经元与第 l + 1 l+1 l+1层的第 j j j个神经元之间的连接,即权重 W j , : ( l ) W_{j,:}^{(l)} Wj,:(l)。
在计算偏置 b j ( l ) b_j^{(l)} bj(l)的梯度时,需要将所有样本中第 l + 1 l+1 l+1层的第 j j j个神经元的误差项 d j ( l + 1 ) d_j^{(l+1)} dj(l+1)相加,从而得到 d B j ( l ) dB_j^{(l)} dBj(l)。因此,需要沿着样本维度(即第二个维度)对误差项 d d d进行求和,从而得到每个神经元的误差项和。
具体地,假设误差项 d ( l + 1 ) d^{(l+1)} d(l+1)的形状是 ( n ( l + 1 ) , m ) (n^{(l+1)}, m) (n(l+1),m),其中 n ( l + 1 ) n^{(l+1)} n(l+1)表示第 l + 1 l+1 l+1层的神经元数量, m m m表示样本数量。那么,沿着第二个维度求和后得到的结果的形状是 ( n ( l + 1 ) , 1 ) (n^{(l+1)}, 1) (n(l+1),1),即每个神经元的误差项和。最终再将这个结果除以样本数量 m m m,即可得到每个神经元的平均误差项,即偏置 b j ( l ) b_j^{(l)} bj(l)的梯度。