天天看点

神经网络反向传播算法推导

反向传播是多数神经网络进行参数更新的基本方法,它的本质是巧妙利用了高数中的链式法则,下面对这一方法进行推导:

(1)符号说明

神经网络反向传播算法推导

:神经元的激活函数

神经网络反向传播算法推导

:神经网络的权重向量

神经网络反向传播算法推导

:神经网络的偏置向量

神经网络反向传播算法推导

:某层的输入向量

神经网络反向传播算法推导

:某层的输出向量

(2)损失函数

假设神经网络的损失函数为

神经网络反向传播算法推导

,那么定义其损失函数为:

神经网络反向传播算法推导

其中,

神经网络反向传播算法推导

为期望的输出值,

神经网络反向传播算法推导

是神经网络的预测输出值。

方向传播算法是通过改变网络中的权重参数

神经网络反向传播算法推导

和偏置

神经网络反向传播算法推导

来改变损失函数的方法。

(3)4个基本方程推导

1. 输出层误差

首先定义第

神经网络反向传播算法推导

层的第

神经网络反向传播算法推导

个神经元的误差为:

神经网络反向传播算法推导

在这里可能很多人会觉得不能理解,具体可以参考这篇文章 http://neuralnetworksanddeeplearning.com/chap2.html

神经网络反向传播算法推导
神经网络反向传播算法推导

根据定义,对损失函数求导可得:

神经网络反向传播算法推导

其中

神经网络反向传播算法推导

度量了损失函数在神经元输出的函数的变化程度,

神经网络反向传播算法推导

则度量了激活函数在神经元加权输入处的变化程度。

对输出层误差向量化,得到输出层的误差公式BP1,如下:

神经网络反向传播算法推导

2. 第

神经网络反向传播算法推导

层误差

非输出层的误差依赖于下一层的误差,也就是网络第

神经网络反向传播算法推导

层的误差依赖于第

神经网络反向传播算法推导

层的误差,则有:

神经网络反向传播算法推导

上式利用了链式法则,前一次的输出可以作用于后一层的输入,又因为:

神经网络反向传播算法推导

所以有:

神经网络反向传播算法推导

带入之前的算是可以得到网络第

神经网络反向传播算法推导

层的第

神经网络反向传播算法推导

个神经元误差为:

神经网络反向传播算法推导

向量化后可以得到网络中第1层的误差公式BP2:

神经网络反向传播算法推导

公式BP2充分体现了误差反向传播的特点,只要知道

神经网络反向传播算法推导

层的误差就可以推导出

神经网络反向传播算法推导

层的误差,以此类推,最后能够从网络的输出层倒推直到输入层的误差,这就是误差反向传播的含义,另外从误差传播的公式中我们可以注意到含有激活函数的导师,一般我们采用的sigmoid的激活函数的导数最大为0.25,每反向传播一次梯度最多保存25%,因此当神经网络隐藏层增多后会产生梯度消失问题。

3. 损失函数关于偏置

神经网络反向传播算法推导

的偏导

同理,根据链式求导法则可得:

神经网络反向传播算法推导

又因为:

神经网络反向传播算法推导

带入得到:

神经网络反向传播算法推导

最终损失函数在网络中任意偏置等于该神经元上的误差,可以通过误差直接得到偏置的倒数。

4. 损失函数关于权值

神经网络反向传播算法推导

的偏导

神经网络反向传播算法推导
神经网络反向传播算法推导
神经网络反向传播算法推导

从公式中可以知道损失函数关于权重的偏导,连接了第1层第j个神经元的误差以及上一层第i个神经元的输出。

至此反向传播的4个基本公式都已经推导完毕。

(4)反向传播算法的实现代码示例

import numpy as np
import pprint
pp = pprint.PrettyPrinter(indent=4)

# 定义神经网络的模型架构 [input, hidden, output]
network_sizes = [3,4,2]

# 初始化该神经网络的参数
sizes = network_sizes
num_layers = len(sizes)
biases = [np.random.randn(h, 1) for h in sizes[1:]]
weights = [np.random.randn(y, x) for x, y in zip(sizes[:-1], sizes[1:])]

def loss_der(network_y, real_y):
    """
    返回损失函数的偏导,损失函数使用 MSE
    L = 1/2(network_y-real_y)^2
    delta_L = network_y-real_y
    """
    return (network_y - real_y)


def sigmoid(z):
    """激活函数使用 sigmoid."""
    return 1.0 / (1.0 + np.exp(-z))


def sigmoid_der(z):
    """sigmoid函数的导数 derivative of sigmoid."""
    return sigmoid(z) * (1 - sigmoid(z))


def backprop(x, y):
    """
    根据损失函数 C通过反向传播算法返回
    """
    """Return a tuple "(nabla_b, nabla_w)" representing the
    gradient for the cost function C_x.  "nabla_b" and
    "nabla_w" are layer-by-layer lists of numpy arrays, similar
    to "self.biases" and "self.weights"."""
    
    # 初始化网络参数的导数 权重w的偏导和偏置b的偏导
    delta_w = [np.zeros(w.shape) for w in weights]
    delta_b = [np.zeros(b.shape) for b in biases]
    
    # 向前传播 feed forward
    activation = x     # 把输入的数据作为第一次激活值
    activations = [x]  # 存储网络的激活值
    zs = []            # 存储网络的加权输入值 (z=wx+b)
    
    for w, b in zip(weights, biases):
        z = np.dot(w, activation) + b
        activation = sigmoid(z)
        
        activations.append(activation)
        zs.append(z)
        
    # 反向传播 back propagation 
    # BP1 计算输出层误差
    delta_L = loss_der(activations[-1], y) * sigmoid_der(zs[-1])
    # BP3 损失函数在输出层关于偏置的偏导
    delta_b[-1] = delta_L
    # BP4 损失函数在输出层关于权值的偏导
    delta_w[-1] = np.dot(delta_L, activations[-2].transpose())

    delta_l = delta_L
    for l in range(2, num_layers):
        # BP2 计算第l层误差
        z = zs[-l] 
        sp = sigmoid_der(z)
        delta_l = np.dot(weights[-l + 1].transpose(), delta_l) * sp
        # BP3 损失函数在l层关于偏置的偏导
        delta_b[-l] = delta_l
        # BP4 损失函数在l层关于权值的偏导
        delta_w[-l] = np.dot(delta_l, activations[-l - 1].transpose())

    return (delta_w, delta_b)


training_x = np.random.rand(3).reshape(3,1)
training_y = np.array([0, 1]).reshape(2,1)
print("training data x:\n{},\n training data y:\n{}".format(training_x, training_y))
backprop(training_x, training_y)
           

输出的结果为:

training data x:
[[0.69316998]
 [0.16969983]
 [0.51637141]],
 training data y:
[[0]
 [1]]

([array([[-0.00122639, -0.00030024, -0.00091359],
         [-0.0252308 , -0.00617693, -0.01879548],
         [-0.0039671 , -0.00097121, -0.00295526],
         [ 0.01921057,  0.00470307,  0.01431076]]),
  array([[ 0.05610549,  0.02035511,  0.06627431,  0.01132567],
         [-0.11372293, -0.04125875, -0.13433461, -0.02295656]])],
 [array([[-0.00176925],
         [-0.03639915],
         [-0.00572312],
         [ 0.02771408]]), array([[ 0.07169137],
         [-0.14531471]])])
           

 参考:

《深度学习原理与实践》第2章

继续阅读