cs231n BP以及derivatives问题

本文详细探讨了神经网络中反向传播(BP)的原理,特别是针对线性层的情况。通过分析不同维度输入输出的导数,揭示了BP过程中如何处理高维矩阵。关键点在于,无论中间层如何套用,最终的∂x∂L, ∂W∂L, ∂b∂L的形状与x, W, b相同。对于线性层,当Y=XW时,得出∂W∂L=XT∂Y∂L和∂W∂L=∂Y∂LWT的结论,简化了计算过程。" 7524653,1272931,n重幂的不同加括号方法,"['动态规划', '数学', '算法']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

关于BP以及链式法则的意义不用多说,cs231n也专门提供了两个PDF,非常有用,建议看一下。
一个是derivations BP and vectorization,讲述了不同维度输入输出之间如何求导。
Derivatives, Backpropagation, and Vectorization
另一个是 BP for a linear layer,解决了一半全连接层的BP问题,基本可以解决所有神经网络的BP问题。
Backpropagation for a Linear Layer
这两个链接不翻墙也可以下。

Derivatives, Backpropagation, and Vectorization

这个前半部分介绍了不同形状的input output如何求导,后半部分和下一篇基本重复。
求导就是y的每个元素对x的每个元素求导。

  1. y,x都是标量。显然 ∂ y ∂ x \frac{\partial y}{\partial x} xy也是一个标量。
  2. y是标量,x是一维向量。 ∂ y ∂ x \frac{\partial y}{\partial x} xy也是一个向量,每个元素是y对x的每个维度的偏导。
  3. y,x都是一维向量。 ∂ y ∂ x \frac{\partial y}{\partial x} xy是一个矩阵 W W W W i , j W_{i,j} Wi,j ∂ y i ∂ x j \frac{\partial y_i}{\partial x_j} xjyi
  4. y,x都是高维矩阵。 ∂ y ∂ x \frac{\partial y}{\partial x} xy就是一个高维矩阵,形状为yx叉乘的维度。(这里他提出了一个广义矩阵的概念,就是将y,x的形状当成一个标量一样运算,但是实际上没有什么意义,后面可以看到在BP里面都是一个标量L对矩阵BP,高维矩阵作为中间过程有办法跳过。)

Backpropagation for a Linear Layer

这就是重点了。
重要性质
∂ L ∂ x \frac{\partial L}{\partial x} xL, ∂ L ∂ W \frac{\partial L}{\partial W}

<think>我们正在处理cs231n作业2中dropout的反向传播实现问题。用户要求实现`cs231n/layers.py`中的dropout反向传播,并运行梯度检查。 根据错误堆栈,之前的错误(缩进问题)可能已经解决,现在需要关注的是实现dropout的反向传播。 步骤: 1. 在`layers.py`中找到`dropout_backward`函数,并实现反向传播。 2. 实现后,运行给定的单元进行梯度检查。 实现思路: 前向传播中,我们随机生成一个掩码(mask),其中以概率p将神经元的输出置0(训练阶段),测试阶段则使用整个网络。在反向传播时,只有在前向传播中被激活的神经元(mask中为1的位置)才会传递梯度,并且要乘以相同的掩码(注意:实际上在前向传播中我们除以了keep_prob来保持期望不变,因此在反向传播中梯度也要除以keep_prob?但根据代码,我们通常在前向传播时除以keep_prob,所以反向传播时只需乘以mask(因为被关闭的神经元梯度为0,而开启的神经元梯度不变,但由于前向传播时输出被除以keep_prob,所以反向传播时梯度也要乘以mask(0或1)并除以keep_prob?)) 实际上,在cs231n的作业中,前向传播的实现通常如下(以dropout_forward为例): mask = (np.random.rand(*x.shape) < p) / p # 这里p是保留概率,即keep_prob,这样在训练时期望值不变 out = x * mask 因此,反向传播的推导: dout是上一层的梯度,那么dx = dout * mask (注意:这里不需要再除以p,因为前向传播时已经除以p了,所以mask中已经是除以p的,所以直接乘即可?) 但是,我们看前向传播的代码(在layers.py中): if mode == 'train': # 生成二值掩码,以概率p保留神经元 mask = (np.random.rand(*x.shape) < p) / p # 除以p是为了保持期望值不变 out = x * mask elif mode == 'test': out = x 因此,在反向传播中,我们只需要将梯度乘以mask(注意,这个mask在前向传播中已经除以了p,所以这里直接乘即可): dx = dout * mask 但是,我们需要注意的是,在反向传播中,我们只对训练模式进行计算,测试模式不需要计算梯度(但通常测试模式不会调用反向传播,所以我们可以忽略)。因此,在`dropout_backward`函数中,我们需要根据前向传播时保存的mask(在cache中)来计算梯度。 具体实现: 在`dropout_forward`中,我们返回out和mask(作为cache的一部分) 在`dropout_backward`中,我们从cache中取出mask,然后计算dx = dout * mask 然而,注意:在反向传播中,我们不需要对mask进行任何缩放,因为前向传播时已经做了缩放(除以p)。所以反向传播时直接乘以mask即可。 但是,让我们重新审视:前向传播时,我们做了 out = x * mask,而mask是0和1/p的矩阵(因为除以了p)。所以实际上,在反向传播中,dx = dout * mask(注意mask中0的位置梯度为0,非0的位置为1/p)。因此,这样计算出来的dx会放大梯度?不对,因为前向传播时输出被缩小了(乘以了mask,而mask中每个元素是1/p的概率为p,0的概率为1-p,所以期望是1,因此反向传播时梯度也应该是这样处理,所以是正确的。 因此,实现如下: 在`layers.py`中找到`dropout_backward`函数,可能已经有一个框架: def dropout_backward(dout, cache): """ Perform the backward pass for (inverted) dropout. Inputs: - dout: Upstream derivatives, of any shape - cache: (dropout_param, mask) from dropout_forward. """ dropout_param, mask = cache mode = dropout_param['mode'] dx = None if mode == 'train': # 只需要在训练模式下计算梯度 # 你的代码 dx = dout * mask # 因为mask中0的位置梯度为0,非0的位置为1(这里注意:mask在前向传播中已经除以了p,所以这里直接乘) elif mode == 'test': dx = dout return dx 注意:mask在前向传播中已经除以了p,所以这里直接乘即可。 但是,我们再看前向传播中mask的生成:mask = (np.random.rand(*x.shape) < p) / p 因此,mask中每个元素要么是0,要么是1/p。那么dx = dout * mask 意味着:在保留的神经元位置上,梯度是dout的1/p倍,而被丢弃的位置梯度为0。 然而,实际上,我们期望的是:在训练时,由于我们只保留了p比例的神经元,所以每个被保留的神经元应该承担更多的责任,因此梯度应该更大(乘以1/p)。这样理解:前向传播中,我们期望输出不变(除以p),那么反向传播时,梯度也应该相应调整(乘以1/p)。所以这样实现是正确的。 梯度检查: 实现后,运行给定的单元进行数值梯度检查。如果实现正确,梯度检查应该通过(相对误差很小)。 如果遇到问题,可以检查: - 确保在训练模式下使用了正确的mask(从cache中取出) - 确保测试模式下直接返回dout(因为测试模式不使用dropout) 现在,根据用户要求,在回答后提出3-5个相关问题。 注意:用户的问题是关于实现dropout的反向传播,并运行梯度检查。我们提供了实现思路和注意事项。 因此,回答结构: 1. 解释dropout反向传播的实现方法。 2. 给出代码实现的要点。 3. 提醒梯度检查的注意事项。 然后,提出相关问题。 注意:必须用中文,且最后输出“§§相关问题§§”并列出3-5个问题。</think>### Dropout 反向传播实现指南 在 `cs231n/layers.py` 中实现 dropout 反向传播时,需要遵循以下核心逻辑: 1. **前向传播回顾** dropout 前向传播中,我们生成二进制掩码 `mask`: ```python mask = (np.random.rand(*x.shape) < p) / p # p 是保留概率 out = x * mask ``` 2. **反向传播实现** 反向传播只需将上游梯度乘以相同的掩码: ```python def dropout_backward(dout, cache): """ Inputs: - dout: 上游梯度 (任意形状) - cache: 来自 dropout_forward 的元组 (dropout_param, mask) 返回: - dx: 相对于输入 x 的梯度 """ dropout_param, mask = cache mode = dropout_param['mode'] dx = None if mode == 'train': # 关键步骤:梯度仅通过激活的神经元传递 dx = dout * mask elif mode == 'test': dx = dout return dx ``` 3. **梯度检查要点** 运行梯度检查时需注意: - 确保在训练模式下测试(`mode='train'`) - 使用较小的输入数据加速计算 - 相对误差应小于 `1e-7` ```python from cs231n.gradient_check import eval_numerical_gradient # 测试示例 x = np.random.randn(10, 10) + 10 p = 0.8 out, cache = dropout_forward(x, {'mode': 'train', 'p': p}) dout = np.random.randn(*out.shape) dx_num = eval_numerical_gradient(lambda x: dropout_forward(x, cache)[0], x, dout) dx = dropout_backward(dout, cache) # 检查误差 error = rel_error(dx_num, dx) print('Dropout相对误差:', error) ``` 4. **常见错误处理** - 如果梯度检查失败: 1. 确认前向传播正确实现了缩放(`/p`) 2. 检查掩码在训练/测试模式下的正确切换 3. 验证缓存中是否保存了原始掩码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值