ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

《神经网络与深度学习》课后习题:反向传播与CNN/RNN推导整理

《神经网络与深度学习》课后习题:反向传播与CNN/RNN推导整理 1. 先想清楚为什么这本书的习题值得亲手过一遍邱锡鹏的《神经网络与深度学习》在圈子里被叫成蒲公英书封面那朵蒲公英几乎成了很多人的深度学习启蒙标记。我最初接触它的时候是把它当工具书用的——哪个概念卡住了就翻到对应章节看一遍。但真正让我把这本书吃透的反而是后来强迫自己把课后习题一道道推一遍。神经网络与深度学习这两个词看着简单可书里那些看似平淡的习题往往就是整个知识体系的骨架。这里先说清楚这篇文章是什么它不是标准答案公布也不是把网上零散的题解抄一遍。它是我在整理前馈神经网络、反向传播、卷积神经网络、循环神经网络这几块核心习题时踩过的坑、用过的验证手段、以及最后形成的一套整理方法论。适合谁看适合已经读过一遍书、想做课后题却发现答案对不上、自己推导总卡在某个维度上的同学也适合准备考试、准备复现经典模型的人。为什么值得自己动手因为这本书的习题设计有个很明显的特点它不太考你背没背下来而是考你能不能把一个公式从定义推到底。比如反向传播书上给的是结论式推导习题却要你把每一层的误差项、每个参数的偏导都老老实实展开。这类题做一遍你对梯度回传的直觉会完全不一样。我整理下来最大的感受是看懂的公式和推出来的公式是两回事。还有一点很现实。网上能找到的答案版本很多质量参差不齐有些甚至符号定义和书里不一致直接照着看反而会被带偏。所以我给自己定了个规矩凡是能自己推的绝不对现成答案推完再和别人的结果交叉验证。这个习惯看起来笨但对打牢基础特别有效。2. 整理前的准备工作分类框架、符号约定和验证工具工欲善其事先得把整理这件事本身规划好。我一开始是随手记做到第四章就乱了——同一个变量在两道题里意思不一样回头一看自己都懵。后来我停下来先搭框架再动笔效率高了很多。2.1 按知识模块给习题打标签我没有按章节顺序一道一道做而是先给所有题目打标签。大致分成了几类一是矩阵求导与线性模型类集中在第3章二是前馈网络与反向传播类第4章是重头三是卷积与池化相关的计算类四是循环网络和梯度相关问题五是概率图、无监督和强化学习这些偏分散的。打标签的好处是同一类题目的推导套路是高度相似的。比如反向传播那一类核心就是链式法则加误差项递推卷积尺寸计算那一类就是一个公式反复用。把它们放一起做思路能顺着走不用每道题都重新热身。我实测下来分类整理的时间成本大概只比乱序多做多花两成但复习时的检索效率能翻好几倍。2.2 符号约定统一避免自相矛盾这一步特别重要也是很多人忽略的。书里的符号体系是自洽的但你在不同章节抄来抄去很容易混。我给自己定了一套固定写法用 $z^{(l)}$ 表示第 $l$ 层的净输入也就是加权和$a^{(l)}$ 表示第 $l$ 层的激活输出$\delta^{(l)}$ 表示第 $l$ 层的误差项。权重矩阵统一写成 $W^{(l)}$偏置写 $b^{(l)}$。约定一旦定下来所有题目都用这一套哪怕和书里某处写法略有出入我也在笔记边上标注此处与教材符号对应关系。这样做的直接收益是推导到一半需要回看前面的步骤时不会因为符号打架而怀疑自己推错了。踩过的坑是我早期有两道题用了不同约定结果对比的时候以为结果矛盾重新检查了半小时才发现是自己作的。2.3 工具选型手写推导为主代码验证为辅推导必须手写这一点我不妥协。手写逼着你把每一步都写出来跳步就会露馅。但我加了一个辅助手段用 Python 和 NumPy 写小规模数值验证。原理很简单解析求导的结果如果是对的它应该和数值梯度的结果高度接近。数值梯度用的中心差分公式是$$ \frac{\partial f}{\partial x} \approx \frac{f(x\varepsilon) - f(x-\varepsilon)}{2\varepsilon} $$$\varepsilon$ 一般取 $10^{-4}$ 到 $10^{-6}$ 之间。这么做的好处是一道反向传播的题推完我不用凭感觉判断对不对跑一下看相对误差是不是在 $10^{-6}$ 量级是就基本稳了。这套手推加机验的组合是我整理过程中最有用的一招后面会专门展开讲。提示数值验证只是校验手段不能替代推导。它的作用是帮你快速定位错误而不是告诉你答案长什么样。3. 反向传播类习题从公式到误差项递推的完整拆解反向传播是整本书习题里最容易卡住、也最值得反复做的一类。它把前馈神经网络、链式法则、矩阵求导这些点全串起来了。我做这部分的时候前后返工了三次最后才把逻辑理顺。3.1 先把前向传播的维度关系写死很多人推反向传播卡住根子不在反向而在前向。前向传播里每个张量的形状如果没写清楚反向的矩阵转置、相乘顺序就一定会错。我的做法是每道题开头先列一张维度表。假设一个 $L$ 层前馈网络第 $l$ 层有 $n_l$ 个神经元批量大小为 $m$。那么输入 $X$ 的形状是 $m \times n_0$第 $l$ 层的权重 $W^{(l)}$ 形状是 $n_{l-1} \times n_l$净输入 $Z^{(l)}$ 和输出 $A^{(l)}$ 都是 $m \times n_l$。前向就是$$ Z^{(l)} A^{(l-1)} W^{(l)} \mathbf{1} b^{(l)T}, \quad A^{(l)} f(Z^{(l)}) $$这个维度关系一旦写明后面所有推导都有了锚点。我习惯在每道题的草稿纸边上把这张表画出来边推边对照。这个习惯救了我无数次——回传的梯度只要和对应的前向张量形状对不上立刻就知道哪一步错了。3.2 误差项 $\delta$ 的递推是核心反向传播的关键是定义第 $l$ 层的误差项 $\delta^{(l)} \partial L / \partial Z^{(l)}$。有了它参数梯度就能写成很干净的形式。输出层的误差项取决于损失函数比如用交叉熵配 Softmax 的时候$$ \delta^{(L)} \hat{Y} - Y $$这个结果非常漂亮也是为什么分类任务里 Softmax 加交叉熵是黄金搭档。隐藏层的误差项则靠递推$$ \delta^{(l)} \left( \delta^{(l1)} W^{(l1)T} \right) \odot f(Z^{(l)}) $$其中 $\odot$ 是逐元素相乘。拿到 $\delta^{(l)}$ 之后权重和偏置的梯度是$$ \frac{\partial L}{\partial W^{(l)}} A^{(l-1)T} \delta^{(l)}, \quad \frac{\partial L}{\partial b^{(l)}} \sum_{i1}^{m} \delta^{(l)}_i $$我整理这套公式的时候最大的体会是它其实只讲了一件事——梯度从后往前一层层传每传一层就乘一次权重转置、过一次激活导数。理解了这一点再复杂的网络结构也能套。书上有些习题会故意换激活函数、换损失函数让你重推本质就是换这两个乘子。3.3 用数值梯度校验每一层推导完别急着信。我通常会写一个三五层的极小网络随机初始化权重前向算一遍损失然后把手推的梯度公式用代码实现再和中心差分算出来的数值梯度逐个对比。实测中相对误差能压到 $10^{-7}$ 以下就说明推导没问题。如果某一层误差突然变大比如到了 $10^{-2}$那基本就是这一层的递推写错了常见问题包括漏乘激活导数、转置方向反了、偏置梯度的求和维度搞错。这套逐层对比的方法比笼统地看总损失能不能下降要可靠得多因为它能精确定位到出错的那一层。注意校验时务必关掉随机性固定随机种子否则前后两次前向结果不一致数值梯度会失真。4. 卷积神经网络类习题尺寸计算与参数量统计卷积神经网络这块的习题很多是算数题看着简单但特别容易算错。它不像反向传播那样需要长篇推导但公式一旦记混答案就全错。4.1 输出尺寸公式和它的直觉卷积输出的空间尺寸是这类题的高频考点。单看公式$$ O \frac{I - K 2P}{S} 1 $$其中 $I$ 是输入边长$K$ 是卷积核边长$P$ 是 padding$S$ 是步长。这个公式很多人背得滚瓜烂熟但我不建议死记。我的理解方式是先把输入用 padding 撑大变成 $I 2P$然后问一个长度 $K$ 的窗口每次挪 $S$ 步能完整放下几次答案就是 $(I 2P - K)/S 1$。这样即使题型变了比如给了输出尺寸反推 padding你也能自己列出来。习题里常见的坑是非整除的情况。比如 $I7, K3, P0, S2$按公式是 $(7-3)/2 1 3$正好整除。但如果换成 $I8$就是 $(8-3)/2 1 3.5$这时候框架的处理方式不一样有的向下取整有的会报错。整理时我会专门把这类不整除的边界情况单独列出来标注清楚。4.2 参数量的统计要分清权重和偏置参数量统计也是常考。一个标准卷积层输入通道 $C_{in}$输出通道 $C_{out}$卷积核 $K \times K$参数量是$$ \text{Params} C_{out} \times (C_{in} \times K \times K 1) $$那个 $1$ 是每个输出通道对应的一个偏置。这里的坑在于有人会忘记乘输出通道数或者忘记偏置。我的记法是一个卷积核负责生成一个输出通道的特征图它必须覆盖所有输入通道所以是 $C_{in} \times K \times K$再加上一个偏置最后有几组这样的核就乘 $C_{out}$。对比全连接层卷积的参数量优势就很直观了。这也是为什么图像处理任务更倾向用卷积神经网络而不是前馈神经网络全连接层在图像上参数量会爆炸而卷积通过权重共享和局部连接把参数量压下来了。习题里如果有比较类的题把两者的参数量算出来摆一起结论一目了然。4.3 池化与感受野的补充池化层的习题相对简单但要注意它没有可学习参数。最大池化和平均池化只做下采样尺寸公式和卷积一样只是没有 padding 时通常写成 $O (I - K)/S 1$。感受野的计算稍微绕一点但很值得掌握。第 $k$ 层的感受野可以通过递推得到$$ R_k R_{k-1} (K_k - 1) \times \prod_{i1}^{k-1} S_i $$这个公式在做深层网络到底看到多大范围的题时很有用。我建议把它和前面卷积尺寸公式放一起整理它们背后的直觉是一致的——都是窗口在空间上滑动的计数问题。5. 循环网络与梯度问题把 BPTT 拆成时间步来想循环神经网络相关的习题难度明显上了一个台阶因为多了时间这个维度。我一开始完全抓不住重点后来发现只要把它当成在时间上展开的前馈网络思路就清楚了。5.1 沿时间反向传播的误差项循环网络的反向传播叫 BPTT本质还是链式法则只是梯度要跨时间步累加。设隐藏状态 $h_t f(W h_{t-1} U x_t b)$损失是所有时间步损失之和。误差项 $\delta_t \partial L / \partial h_t$ 的递推是$$ \delta_t \frac{\partial L_t}{\partial h_t} \delta_{t1} \frac{\partial h_{t1}}{\partial h_t} $$后面那一项里 $\partial h_{t1} / \partial h_t W^T \odot f(\cdot)$ 之类。关键的直觉是误差会沿着时间链一路往回传每传一步就乘一次权重矩阵。时间步越多这个连乘就越长。5.2 梯度消失和梯度爆炸为什么会出现理解了上面的连乘梯度问题就顺理成章了。如果权重矩阵的谱范数长期大于 1连乘会让梯度指数级放大这就是梯度爆炸如果长期小于 1就会指数级缩小这就是梯度消失。习题里常让你分析这两种情况出现的条件我整理时会配一个数值小实验构造一个简单循环单元跑几十个时间步把梯度范数按时间步画出来看一眼就懂了。爆炸的处理手段是梯度裁剪把梯度的范数限制在一个阈值内import numpy as np def clip_grad(grad, threshold): norm np.linalg.norm(grad) if norm threshold: grad grad * (threshold / norm) return grad裁剪的逻辑很直白梯度方向不变只把长度压回阈值以内。消失的处理则更依赖结构设计比如引入门控机制。这部分我建议重点整理因为它把理论和工程实践连起来了。5.3 时间步的维度核对循环网络推导最容易错的地方是维度。我养成的习惯是把每个时间步的隐藏状态、输入、输出的形状都标出来尤其是批量维度。BPTT 里既有同一时间步内的矩阵乘又有跨时间步的累加这两件事混在一起时如果维度没标清楚很容易把累加写成覆盖。我一般会先只推单个样本、单个时间步确认无误再扩展到批量这样出错的概率低很多。6. 常见问题速查整理过程中反复踩的坑整理到后面我发现自己反复卡在几个固定的地方。把它们做成速查表之后返工率明显下降了。这部分可能是整篇里最实用的内容因为都是我亲身撞过的墙。问题现象可能原因排查方法反向推导结果和数值梯度差很多漏乘激活函数导数逐层对比误差项 $\delta$权重梯度形状和前向张量对不上转置方向反了检查 $A^{(l-1)T}\delta^{(l)}$ 的维度卷积输出尺寸算成小数步长与核不匹配核对是否整除确认取整规则参数量统计对不上漏算偏置或输出通道逐个通道数一遍BPTT 梯度越推越大权重谱范数大于 1算梯度范数随时间的变化Softmax 梯度推导出现负号混乱混淆了 $\hat{y}-y$ 的方向从小规模数值例子验证损失函数下降但准确率不升学习率或初始化问题调整学习率、换初始化6.1 别忽视边界情况很多题目的标准答案看起来很简洁但简洁的背后往往省略了对边界情况的讨论。比如整除问题、除零问题、空集问题。我整理的时候会专门给每道题加一栏边界情况把这些补充上。这不只是为了答案好看而是真正理解一个公式适用范围的过程。读懂公式的边界比背下公式本身更重要。6.2 符号对不上时先怀疑自己有几次我推导结果和参考解答不一致第一反应是答案错了。结果查下来十次里有九次是我自己的符号定义和它不一样或者我漏了一个求和项。所以后来我定了一条规矩结果不一致时先逐行对比中间步骤尤其是每一层的定义而不是直接看最终表达式。最终表达式一样不代表过程一样过程一样才是真的对上了。提示整理笔记时把已知条件、假设、结论、验证方式四样分开写复习时一眼就能看出这道题当时卡在哪。7. 一些让整理这件事长期受益的习惯整理习题这件事做一次不难难的是坚持并且越做越顺。我摸索出几个小习惯分享出来。第一个是给每道题写一句话的题眼。比如某道题的核心是用链式法则把输出误差传回隐藏层把这句话写在题目最上面复习时扫一眼就能回忆起来。第二个是把推导和验证结果放一起。光有推导久了会怀疑自己当时推得对不对把当时的数值验证结果一起留存心里就有底。第三个是定期回头重做错题而不是只看一遍。我大概每隔两三周会把卡壳过的题目重推一次能重现出来才算真的掌握。说到底神经网络与深度学习这类内容最大的门槛不是概念有多难而是从看懂到能推能算之间那道沟。习题就是填这道沟的工具。花时间把这本书的习题亲手过一遍可能比再刷一门新课都值。我自己在这一遍整理里对前馈网络、卷积网络、循环网络这几块的直觉比之前单纯读书时清晰了不止一个层次。后面如果有精力还打算把图神经网络和注意力机制那几章的习题也补齐把整套笔记串成一份能长期迭代的复习资料。
返回列表