反向传播法是神经网络的基础了,但是很多人在学的时候总是会遇到一些问题,或者说看书上一堆推导公式感觉很复杂,其实仔细看,就是一个链式求导法则反复用。本篇会以最详细的方式为大家讲解反向传播法,也会有简单的反向传播代码实现,咱们别急,等我慢慢道来。


目录

  • 1.前向传播
  • 2.反向传播
  • 3.代码实现简单的反向传播


1.前向传播

首先我们来看一张简单的两层神经网络图(自己制作的,有点丑)

消息传递神经网络mpnn 神经网络传播算法_神经网络


我先给小伙伴们解释一下图中参数的意思

消息传递神经网络mpnn 神经网络传播算法_反向传播_02消息传递神经网络mpnn 神经网络传播算法_神经网络_03是输入层和隐藏层的偏置值(也可以先不看偏置值),下面推导默认使用偏置值,只不过b用消息传递神经网络mpnn 神经网络传播算法_深度学习_04表示的,这里面消息传递神经网络mpnn 神经网络传播算法_python_05就是b,消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_06默认为1,
消息传递神经网络mpnn 神经网络传播算法_深度学习_07消息传递神经网络mpnn 神经网络传播算法_神经网络_08消息传递神经网络mpnn 神经网络传播算法_python_09消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_10是输入的n个特征值,消息传递神经网络mpnn 神经网络传播算法_神经网络_11消息传递神经网络mpnn 神经网络传播算法_python_12消息传递神经网络mpnn 神经网络传播算法_反向传播_13消息传递神经网络mpnn 神经网络传播算法_python_14是隐藏层经过激活函数后的m个输出值,消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_15消息传递神经网络mpnn 神经网络传播算法_神经网络_16消息传递神经网络mpnn 神经网络传播算法_深度学习_17是输出层经过激活函数后的消息传递神经网络mpnn 神经网络传播算法_python_18个预测值

消息传递神经网络mpnn 神经网络传播算法_神经网络_19表示从输入层第i个神经元到隐藏层第j个神经元的权重

消息传递神经网络mpnn 神经网络传播算法_反向传播_20表示从隐藏层第j个神经元到输出层第k个神经元的权重

了解了这些参数的意思,我们先简单讲解前向传播,这个对小伙伴们来说应该很简单吧!

(1)输入层---->隐藏层:

消息传递神经网络mpnn 神经网络传播算法_神经网络_21     消息传递神经网络mpnn 神经网络传播算法_反向传播_22
消息传递神经网络mpnn 神经网络传播算法_反向传播_23
消息传递神经网络mpnn 神经网络传播算法_python_24            消息传递神经网络mpnn 神经网络传播算法_反向传播_22

(2)隐藏层---->输出层:

消息传递神经网络mpnn 神经网络传播算法_python_26     消息传递神经网络mpnn 神经网络传播算法_反向传播_27
消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_28
消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_29            消息传递神经网络mpnn 神经网络传播算法_反向传播_27

上面公式中:
消息传递神经网络mpnn 神经网络传播算法_python_31消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_32相当于线性回归(消息传递神经网络mpnn 神经网络传播算法_深度学习_33)的输出结果,消息传递神经网络mpnn 神经网络传播算法_python_34是激活函数,这里我们使用消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_35激活函数,也就是消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_36消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_37消息传递神经网络mpnn 神经网络传播算法_神经网络_38都是隐藏层和输出层经过激活函数后的结果,最后得到的消息传递神经网络mpnn 神经网络传播算法_神经网络_38就是正向传播最后得到的预测值。

2.反向传播

接下来我们先定义一个损失函数,最简单也是大家最熟悉的均方差损失函数,也就是消息传递神经网络mpnn 神经网络传播算法_深度学习_40,这里的消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_41就是真实值(标签值),消息传递神经网络mpnn 神经网络传播算法_反向传播_42就是上面正向传播得到的预测值,消息传递神经网络mpnn 神经网络传播算法_反向传播_43是为了求导方便,不影响最后的损失对结果的度量,那么这里的消息传递神经网络mpnn 神经网络传播算法_反向传播_44
又因为:

消息传递神经网络mpnn 神经网络传播算法_反向传播_45          消息传递神经网络mpnn 神经网络传播算法_深度学习_46
消息传递神经网络mpnn 神经网络传播算法_python_47          消息传递神经网络mpnn 神经网络传播算法_神经网络_48

那么最终的消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_49就等价于:

消息传递神经网络mpnn 神经网络传播算法_反向传播_44             消息传递神经网络mpnn 神经网络传播算法_深度学习_51
   消息传递神经网络mpnn 神经网络传播算法_python_52
   消息传递神经网络mpnn 神经网络传播算法_神经网络_53
   消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_54
   消息传递神经网络mpnn 神经网络传播算法_python_55

得到消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_49的完整式子后,我们的目标肯定是消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_49越小越好,这就需要优化消息传递神经网络mpnn 神经网络传播算法_神经网络_58,用的最多的当然就是梯度下降法了,不停地迭代更新消息传递神经网络mpnn 神经网络传播算法_神经网络_58,直到得到最合适的消息传递神经网络mpnn 神经网络传播算法_神经网络_58值使得消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_49最小,由梯度下降法的参数更新公式,可以得到:

消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_62     消息传递神经网络mpnn 神经网络传播算法_神经网络_63

消息传递神经网络mpnn 神经网络传播算法_python_64      消息传递神经网络mpnn 神经网络传播算法_神经网络_65

消息传递神经网络mpnn 神经网络传播算法_python_66      消息传递神经网络mpnn 神经网络传播算法_神经网络_67

消息传递神经网络mpnn 神经网络传播算法_深度学习_68      消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_69

其中消息传递神经网络mpnn 神经网络传播算法_神经网络_70是学习率,自己设定,那么:

消息传递神经网络mpnn 神经网络传播算法_反向传播_71      消息传递神经网络mpnn 神经网络传播算法_python_72      消息传递神经网络mpnn 神经网络传播算法_神经网络_73

消息传递神经网络mpnn 神经网络传播算法_深度学习_74       消息传递神经网络mpnn 神经网络传播算法_深度学习_75      消息传递神经网络mpnn 神经网络传播算法_深度学习_76

我们令:

消息传递神经网络mpnn 神经网络传播算法_神经网络_77       消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_78

消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_79       消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_80

我们把消息传递神经网络mpnn 神经网络传播算法_深度学习_81称作学习信号,代入式子消息传递神经网络mpnn 神经网络传播算法_反向传播_82

消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_83

消息传递神经网络mpnn 神经网络传播算法_python_84

现在我们来看式子消息传递神经网络mpnn 神经网络传播算法_反向传播_82

消息传递神经网络mpnn 神经网络传播算法_python_86

消息传递神经网络mpnn 神经网络传播算法_神经网络_87

消息传递神经网络mpnn 神经网络传播算法_神经网络_88

消息传递神经网络mpnn 神经网络传播算法_反向传播_89

将上面两个式子代回消息传递神经网络mpnn 神经网络传播算法_反向传播_82

消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_36

消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_92

消息传递神经网络mpnn 神经网络传播算法_反向传播_93

消息传递神经网络mpnn 神经网络传播算法_神经网络_94
   消息传递神经网络mpnn 神经网络传播算法_反向传播_95

得到了最后的学习信号表达式,就可以代回梯度下降参数更新公式里面了:

消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_96

消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_97

消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_62

消息传递神经网络mpnn 神经网络传播算法_python_64

接下来求消息传递神经网络mpnn 神经网络传播算法_消息传递神经网络mpnn_100,这个比上面的更简单,因为我们知道偏置的权重系数是1,所以就有:

消息传递神经网络mpnn 神经网络传播算法_深度学习_101

消息传递神经网络mpnn 神经网络传播算法_反向传播_102

再更新偏置值:

消息传递神经网络mpnn 神经网络传播算法_python_66

消息传递神经网络mpnn 神经网络传播算法_深度学习_68

这个时候两层的权重和偏置就更新了一次,小伙伴们要要使用其他激活函数或者损失函数,只需要修改对应位置激活函数的导函数和损失函数的导函数就可以了,我们也可以发现参数更新的速度(收敛速度)跟学习率消息传递神经网络mpnn 神经网络传播算法_神经网络_70有关系,所以lr初始值尽量从小开始设置,上面的推导过程写得很详细,第一次看可能会比较昏,不妨动手跟着算一遍就会很清楚了,矩阵的反向传播也是同样的,过程差不多,大家可以自行推导,下面我将用python代码实现反向传播过程

3.代码实现简单的反向传播

import numpy as np


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


def d_sigmoid(x):
    s = sigmoid(x)
    return s * (np.ones(s.shape) - s)


def mean_square_loss(s, y):
    return np.sum(np.square(s - y) / 2)


def d_mean_square_loss(s, y):
    return s - y


def forward(W1, W2, b1, b2, X, y):
    # 输入层到隐藏层
    y1 = np.matmul(X, W1) + b1  # [2, 3]
    z1 = sigmoid(y1)  # [2, 3]
    # 隐藏层到输出层
    y2 = np.matmul(z1, W2) + b2  # [2, 2]
    z2 = sigmoid(y2)  # [2, 2]
    # 求均方差损失
    loss = mean_square_loss(z2, y)
    return y1, z1, y2, z2, loss


def backward_update(epochs, lr=0.01):
# 随便创的数据和权重,偏置值,小伙伴们也可以使用numpy的ranodm()进行随机初始化
    X = np.array([[0.6, 0.1], [0.3, 0.6]])
    y = np.array([0, 1])
    W1 = np.array([[0.4, 0.3, 0.6], [0.3, 0.4, 0.2]])
    b1 = np.array([0.4, 0.1, 0.2])
    W2 = np.array([[0.2, 0.3], [0.3, 0.4], [0.5, 0.3]])
    b2 = np.array([0.1, 0.2])
    # 先进行一次前向传播
    y1, z1, y2, z2, loss = forward(W1, W2, b1, b2, X, y)
    for i in range(epochs):
        # 求得隐藏层的学习信号(损失函数导数乘激活函数导数)
        ds2 = d_mean_square_loss(z2, y) * d_sigmoid(y2)
        # 根据上面推导结果式子(2.4不看学习率)--->(学习信号乘隐藏层z1的输出结果),注意形状需要转置
        dW2 = np.matmul(z1.T, ds2)
        # 对隐藏层的偏置梯度求和(式子2.6),注意是对列求和
        db2 = np.sum(ds2, axis=0)
        # 式子(2.5)前两个元素相乘
        dx = np.matmul(ds2, W2.T)

        # 对照式子(2.3)
        ds1 = d_sigmoid(y1) * dx
        # 式子(2.5)
        dW1 = np.matmul(X.T, ds1)
        # 对隐藏层的偏置梯度求和(式子2.7),注意是对列求和
        db1 = np.sum(ds1, axis=0)
        # 参数更新
        W1 = W1 - lr * dW1
        b1 = b1 - lr * db1
        W2 = W2 - lr * dW2
        b2 = b2 - lr * db2

        y1, z1, y2, z2, loss = forward(W1, W2, b1, b2, X, y)
        # 每隔100次打印一次损失
        if i % 100 == 0:
            print('第%d批次' % (i / 100))
            print('当前损失为:{:.4f}'.format(loss))
            print(z2)
            # sigmoid激活函数将结果大于0.5的值分为正类,小于0.5的值分为负类
            z2[z2 > 0.5] = 1
            z2[z2 < 0.5] = 0
            print(z2)


if __name__ == '__main__':
    backward_update(epochs=50001, lr=0.01)

消息传递神经网络mpnn 神经网络传播算法_反向传播_106