深度学习|反向传播算法(BP)原理推导及代码实现

请点击上面公众号,免费订阅。 

《实例》阐述算法,通俗易懂,助您对算法的理解达到一个新高度。包含但不限于:经典算法,机器学习,深度学习,LeetCode 题解,Kaggle 实战。期待您的到来!


01

回顾

昨天,分析了手写字数据集分类的原理,利用神经网络模型,编写了SGD算法的代码,分多个epochs,每个 epoch 又对 mini_batch 样本做多次迭代计算,详细的过程,请参考:

深度学习|神经网络模型实现手写字分类求解思路


这其中,非常重要的一个步骤,便是利用反向传播(BP)算法求权重参数的梯度,偏置量的梯度。下面根据以下几个问题展开BP算法:

  • 什么是BP算法?

  • 为什么叫做反向传播?

  • 如何构思BP算法的切入点?

  • 误差是如何传播开的?

  • 如何求出权重参数的梯度和偏置量的梯度?

  • 链式规则是怎么一回事?



02

统一符号表达

神经网络每个神经元的连接关系,用符合如何表达呢? 下面定义一种表达方式,如下图所示,含有一个隐含层的神经网络,图中标出的w的含义为:第三层的第2个神经元与第二层的第4个神经元间的权重参数。


再看下,标红色箭头的神经元的偏移量 b,如图所示进行标记,第二层中第3个神经元的偏移量;标绿色箭头的神经元的输出 a 为如下图所示标记,为第三层中第一个神经元的输出。


第 L 层第 j 个神经元的输出等于,前1层即 L-1 层中所有神经元的带权的输入和,然后再映射到sigmoid激活函数中,得到如下公式所述:

一定要仔细理解这种上述公式的各个符号表达,它是理解以下对 BP 算法论述的前提。



03

BP算法推导

3.1 公式1

如下图所示,有个精灵跑到了网络中,假设位于第L层,第 j 个神经元的门口处,它引起了一点扰动,

z的含义是加权输入项,容易得出这个扰动项对成本函数造成的损失可以定义为:

那么,类推的,可以看出在输出层 L, 误差项的定义表达为如下,第一个公式

    

上式是根据链式规则可以推导得出,成本函数的改变首先是有第L层第j个神经元的输出项影响的,然后第 j 个神经元的输出又受到第 L层第 j 个神经元的干扰 z 影响,因此得到上式。这个式子的意义是定义了第 L层第 j个神经元的误差项怎么求,注意这里L可是输出层哦,那么如何求出第 L-1层中某个神经元的损失项(误差项)呢?


3.2 公式2

这就用到第二个公式,它给出了怎么由第 L层的误差推导出第L-1层的误差,先给出第二个公式

那么,这个公式,是如何得出的呢?这里面,这个公式是相对最难想的,推导过程如下:

还是从损失项的定义入手,

由以上这几个式子,就可以得出公式2 。


3.3 公式3

那么有了以上的分析,我们便能求解处任意层的损失项了,可以得出成本函数对某层某个神经元的梯度为,这是第三个公式

还是可以由链式规则得出吧,如下推导过程:


3.4 公式4

成本函数对权重参数的梯度为,这是第四个公式

那么这个公式还是可以由链式规则得出,对其推导如下:

推导第三,四个公式,都用到了以下这个基本知识:


3.5 反向传播代码

根据这四个公式,可以得出BP算法的代码,每个步骤将公式放到上面,方便查看。


def backprop(self, x, y):

01 占位

        nabla_b = [np.zeros(b.shape) for b in self.biases]

        nabla_w = [np.zeros(w.shape) for w in self.weights]


02  前向传播求出每个神经元的输出项

        activation = x

        activations = [x] # 分层存储每层的输出项(对应上文中的 a)

        zs = [] # 分层存储每层的 z 向量(对应上文中的 z)

        for b, w in zip(self.biases, self.weights):

            z = np.dot(w, activation)+b

            zs.append(z)

            activation = sigmoid(z)

            activations.append(activation)       


            #activations[-1] 必须是最后一层

        delta = self.cost_derivative(activations[-1], y) * sigmoid_prime(zs[-1])


  03 求偏置量的梯度 

      

        nabla_b[-1] = delta


  04 求权重参数的梯度        

      

        nabla_w[-1] = np.dot(delta, activations[-2].transpose())

  05 反向传播,依次更新每层的每个神经元的权重和偏移量 

        # L = 1 表示最后一层神经元, L = 2 倒数第二层神经元

        for layer in range(2, self.num_layers):

            z = zs[-layer]

            sp = sigmoid_prime(z) #sigmoid函数的导数

            

            delta = np.dot(self.weights[-layer+1].transpose(), delta) * sp

            nabla_b[-layer] = delta

            nabla_w[-layer] = np.dot(delta, activations[-layer-1].transpose())

        return (nabla_b, nabla_w)


 def cost_derivative(self, output_activations, y):

        """

       

        """ 

        return (output_activations-y)


好了,以上就是,BP算法的详细推导过程,谢谢您的阅读!



请记住:每天一小步,日积月累一大步!


《实例》阐述算法,通俗易懂,助您对算法的理解达到一个新高度。包含但不限于:经典算法,机器学习,深度学习,LeetCode 题解,Kaggle 实战。期待您的到来!



反向传播算法(Backpropagation)是一种用于训练神经网络的常见优化算法。它通过计算损失函数相对于每个参数的梯度,并使用梯度下降来更新参数。下面我将给出反向传播算法的公式推导及示例代码。 1. 反向传播算法公式推导: 首先,定义神经网络的损失函数为L,该函数是由网络输出和真实标签之间的差异计算得出。假设神经网络有多个隐藏层,每个隐藏层的参数为W和b。 1.1 前向传播: 首先,我们通过前向传播计算每一层的输出值。假设输入为x,第l层的输出为a[l],则有: a = x z[l] = W[l] * a[l-1] + b[l] a[l] = g(z[l]) 其中,g()是激活函数。 1.2 反向传播: 接下来,我们需要计算损失函数相对于每个参数的梯度,然后使用梯度下降更新参数。假设我们有L层神经网络,则有以下公式: 输出层的梯度: dz[L] = dL / da[L] * g'(z[L]) 隐藏层的梯度: dz[l] = (W[l+1]的转置 * dz[l+1]) * g'(z[l]) 参数梯度: dW[l] = dz[l] * a[l-1的转置] db[l] = dz[l] 更新参数: W[l] = W[l] - learning_rate * dW[l] b[l] = b[l] - learning_rate * db[l] 其中,dL / da[L]是损失函数对输出层输出的导数,g'()是激活函数的导数。 2. 反向传播算法示例代码: 下面是一个使用反向传播算法进行训练的示例代码: ```python # 假设网络有三个隐藏层 hidden_layers = [10, 20, 30] output_size = 2 # 初始化参数 parameters = {} layers_dims = [input_size] + hidden_layers + [output_size] L = len(layers_dims) - 1 for l in range(1, L + 1): parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l-1]) * 0.01 parameters['b' + str(l)] = np.zeros((layers_dims[l], 1)) # 前向传播 def forward_propagation(X, parameters): caches = [] A = X for l in range(1, L): Z = np.dot(parameters['W' + str(l)], A) + parameters['b' + str(l)] A = sigmoid(Z) cache = (Z, A) caches.append(cache) Z = np.dot(parameters['W' + str(L)], A) + parameters['b' + str(L)] AL = softmax(Z) cache = (Z, AL) caches.append(cache) return AL, caches # 反向传播 def backward_propagation(AL, Y, caches): grads = {} dZ = AL - Y m = AL.shape[1] grads['dW' + str(L)] = 1/m * np.dot(dZ, caches[-1][1].T) grads['db' + str(L)] = 1/m * np.sum(dZ, axis=1, keepdims=True) for l in reversed(range(1, L)): dA_prev = np.dot(parameters['W' + str(l+1)].T, dZ) dZ = dA_prev * sigmoid_derivative(caches[l-1][0]) grads['dW' + str(l)] = 1/m * np.dot(dZ, caches[l-1][1].T) grads['db' + str(l)] = 1/m * np.sum(dZ, axis=1, keepdims=True) return grads # 参数更新 def update_parameters(parameters, grads, learning_rate): for l in range(1, L+1): parameters['W' + str(l)] -= learning_rate * grads['dW' + str(l)] parameters['b' + str(l)] -= learning_rate * grads['db' + str(l)] return parameters # 训练模型 def train_model(X, Y, learning_rate, num_iterations): for i in range(num_iterations): AL, caches = forward_propagation(X, parameters) cost = compute_cost(AL, Y) grads = backward_propagation(AL, Y, caches) parameters = update_parameters(parameters, grads, learning_rate) if i % 100 == 0: print("Cost after iteration {}: {}".format(i, cost)) return parameters # 使用示例 parameters = train_model(X_train, Y_train, learning_rate=0.01, num_iterations=1000) ``` 这是一个简单的反向传播算法示例代码,其中的sigmoid()、softmax()、sigmoid_derivative()和compute_cost()函数需要根据具体情况自行实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值