学习机器学习的勇气:从数学理论到编码实践解释反向传播

原文:towardsdatascience.com/courage-to-learn-ml-explain-backpropagation-from-mathematical-theory-to-coding-practice-21e670415378

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/9740356b1a6cbbc2c8a7e07a31746277.png

由作者使用 ChatGPT 创建的图像

欢迎回到“学习机器学习的勇气”的最新章节。在这个系列中,我旨在通过问答格式揭开复杂机器学习主题的神秘面纱,并使其变得引人入胜。

这次,我们的学习者正在探索反向传播,并选择通过编码来接近它。他发现了一个在Machine Learning Mastery上的 Python 教程,该教程从零开始使用基本的 Python 解释反向传播,不使用任何深度学习框架。发现代码有点令人困惑,他拜访了导师,寻求指导以更好地理解代码和反向传播的概念。

和往常一样,以下是今天我们将探讨的主题列表:

  • 理解反向传播及其与梯度下降的联系

  • 探索深度神经网络(DNNs)中对深度而非宽度的偏好,以及浅层、宽网络的罕见性。

  • 什么是链式法则?

  • 将反向传播的计算分解为 3 个组成部分,并逐一仔细检查。为什么叫反向传播?

  • 通过简单的 Python 代码理解反向传播

  • 梯度消失和激活函数中的常见偏好


让我们从基本的“为什么”开始——

什么是反向传播,它与梯度下降有何关系?

梯度下降是机器学习中的一个关键优化方法。它不仅限于训练深度神经网络(DNNs),还用于训练逻辑回归和线性回归等模型。其背后的基本思想是通过最小化预测值和真实标签之间的差异(预测误差),我们的模型将更接近底层真实模型。在梯度下降中,梯度,由∇𝜃𝐽(𝜃)表示,并由损失函数相对于每个参数的偏导数形成,指导参数的更新:𝜃 = 𝜃 – 𝜂⋅∇𝜃𝐽(𝜃)。这个过程类似于将视频游戏中的复杂动作分解成基本动作。

然而,在涉及多层和数百万个参数的深度神经网络(DNNs)中,计算每个参数的这些偏导数变得计算密集。特别是在分层结构中,区分每个层的错误贡献对于理解不同层的参数应该如何相对于整体损失变化至关重要。

这就是反向传播的作用。**它是一种高效计算深度神经网络梯度的方法。****反向传播帮助深度神经网络使用梯度下降来指导其学习过程,**计算偏导数,并更有效地调整每个参数。反向传播的关键在于其名称——它代表“错误反向传播”。这意味着该过程涉及将错误(当前预测与真实标签之间的差异)反向传播,并将梯度从输出层分布到输入层以及中间的隐藏层。这种分布的方向与用于生成预测的前向方向相反。

因此,训练深度神经网络困难的部分是由于多层。但在更多讨论反向传播之前,我很好奇为什么深度神经网络通常选择更深而不是更宽。为什么浅层但宽的网络不流行?

这个问题涉及到深度网络相对于浅层网络的偏好。在深入探讨之前,让我们将浅层网络定义为只有 1 或 2 个隐藏层。根据万能逼近定理,足够宽的单层网络在理论上可以逼近任何函数。然而,在实践中,由于计算需求高,在一个宽网络中拥有许多神经元并不总是实用的。

拥有多个层的深度网络通常比浅层网络更有效地使用更少的神经元来建模复杂函数。它们特别擅长学习不同层次的数据表示。例如,在 CNN 进行面部识别时,初始层可能学习简单的模式,如边缘,而深层可以识别更复杂的特征,如面部的一部分。

然而,浅层网络也有它们自己的优势。它们更容易训练,并且不会遇到深层网络的一些常见问题,如梯度消失或梯度爆炸。它们也更容易理解。但是,为了捕捉复杂函数,它们可能需要更多的神经元,这使得它们在某些任务上效率较低。

总结来说,深度网络通常更受欢迎,因为它们可以以层次化和结构化的方式学习复杂的模式,并且使用更少的神经元来高效地做到这一点。但是,浅层网络与深层网络的研究仍然是机器学习中的一个活跃领域。

现在,让我们详细探讨反向传播,并利用代码片段作为资源来加深我们对这一概念的理解。

如果你刚开始接触梯度(梯度下降)或需要复习,我的上一篇文章详细探讨了梯度下降和流行的优化器,可在这里找到。本节受 Hung-yi Lee 引人入胜的讲座启发,将我的个人见解与他的教诲相结合。对于那些精通中文的人来说,我强烈推荐他的引人入胜的机器学习讲座,可在YouTube上找到。

第一步是理解梯度在我们过程中的作用。梯度使我们能够计算损失相对于每一层每个参数的偏导数。

考虑这样一个场景,我们使用随机梯度下降(SGD)训练我们的模型,批量大小为 1。这意味着我们每次只使用一个样本来训练我们的网络。通过某种魔法,我们可以确定损失相对于任何权重的偏导数,无论其在网络中的深度如何。例如,在下面的网络中,我们假设我们已经知道了损失相对于第一层权重的偏导数。例如,第一层中 w1 的梯度,表示为 ∂L(θ)/∂w1。我们的目标是理解 ∂L(θ)/∂w1 实际上代表什么。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/c3cbfd0d1e39b75dfcee09c28745690e.png

我们将基于这个网络学习反向传播。来源:www.youtube.com/watch?v=ibJpTrp5mcE&t=1510s

作者注:在 Leetcode 上练习编码时,我学到了一个很有价值的技巧,那就是通过假设任务的一部分已经完成来处理递归函数。这种方法有助于使用假设部分的结果来解决更大的问题。采取假设知识的态度可以提供安慰,让你的心情放松,增强你在深入研究主题时的信心。本质上,这是关于“假装做到你做到”的问题解决甚至生活态度。

要将 ∂L(θ)/∂w1 分解成可理解的段落,我们必须探索偏导数的链式法则。因此,按照惯例,让我们从以下问题开始:

什么是链式法则?

链式法则是微积分中的一个基本技巧,用于计算复合函数的导数。为了说明链式法则是什么,考虑以下两个例子:

  • 如果 y = f(x) 且 x = g(t),这意味着 y = f(g(t))。因此,y 相对于 t 的导数(∂y/∂t)是 y 相对于 x 的导数(∂y/∂x)和 x 相对于 t 的导数(∂x/∂t)的乘积。所以,我们有 ∂y/∂t = ∂y/∂x * ∂x/∂t

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/a218fc96d015ebb90a9aca17a58572bc.png

作者创建的图像。

  • 如果 z = f(x, y),其中 y = g(t)和 x = q(t),则 z 相对于 t 的导数(∂z/∂t)是 z 相对于 y 的导数(即∂z/∂y)与 y 相对于 t 的导数(即∂y/∂t)的乘积之和,以及 z 相对于 x 的导数(即∂z/∂x)与 x 相对于 t 的导数(即∂x/∂t)的乘积。因此,我们得到 ∂z/∂t = (∂z/∂x * ∂x/∂t) + (∂z/∂y * ∂y/∂t)。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/fd789c3ea41ac8e06b26473fe21b0f24.png

为了可视化链式法则,想象导数就像风景中的水流。计算过程中一个元素导数就像追踪水流之旅。当函数嵌套在彼此内部时,将其想象成一条小溪流入河流,然后汇入大海。这代表了导数如何从小规模影响乘积到大规模影响的过程。当两条水流(导数)来自不同的源头并汇聚时,想象它们合并成一条单一的水流。这说明了在链式法则的上下文中两个导数的求和。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/fa8615963cd92b153c2d4ec408b1e99e.png

想象导数就像风景中的水流。图像由作者使用 ChatGPT 创建。

一种直观但技术上“不正确”的掌握链式法则的方法是将它比作分数操作。将其想象为一个场景,当分母和分子相乘时,分母与分子相互抵消。虽然这种可视化简化了概念,但重要的是要注意这是不正确的。尽管如此,它作为理解导数的有用辅助工具。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/541fb60fd371ffac4430be9d9c23aa82.png

此图像展示了理解链式法则的一种简单但错误的方法。图像由作者创建。

链式法则是如何应用于我们计算 ∂L(θ)/∂w1 的?

回到我们的神经网络,将其内部连接想象成水流是有帮助的。在这里,网络的边缘象征着从输入到输出的方向性流动,激活函数就像复杂的门,改变水流的属性。为了理解第一层(w1)中的权重如何影响损失,我们追踪从损失到 w1 的“水流”。为了更好地理解第一层权重(w1)如何影响损失,设想追踪从损失到 w1 的“水流”。在下面的网络图中,红色线条展示了从输出到 w1 的水流,其中 z’和 z”是两个在激活门之前汇聚的独立水流。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/35f1cd35066a1bac57f0ff73f3e72fce.png

图片来源 www.youtube.com/watch?v=ibJpTrp5mcE&t=1510s,作者标注。

这种流流动类比帮助我们更直观地重新解释相对于第一层权重的损失偏导数(∂L(θ)/∂w1)。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/9b13808316e0ee5dad1e7c2fe0373590.png

然后,我们可以使用链式法则将 ∂L(θ)/∂w1,即损失相对于权重的偏导数,分解成三个部分:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/41440c553c0cde6a9d6e095047584aec.png

  • 相对于当前层的整体损失的偏导数,考虑到它在后续层作为输入的角色。这反映了可以由该层影响或贡献的损失部分。需要注意的是,当前层的输出受前一层的影响。

  • 激活函数输出 a 相对于其输入 z 的偏导数,其中 a = 𝜎(z)。由于激活函数就像一个改变流特性的门,因此在将损失分配给不同层时,理解它对流的影响变得至关重要。

  • 神经元的直接输出 z 相对于其权重 w1 的偏导数。在公式 z_=_w_1_x_1+_w_2_x_2+_b 中,z 是 w1 的直接结果,它代表了在遇到激活函数之前的流。

将偏导数分解成单个组成部分后,我们可以通过分别处理每一部分来解决问题。让我们从最简单的部分开始,提出以下问题:

我们如何计算 ∂z/∂w1,即 z 相对于直接决定它的权重的偏导数?

∂z/∂w1 代表了输出 z 在激活函数之前的梯度相对于直接与其计算相关的权重。由于 z,计算为 z_=_w_1_x_1+_w_2_x_2+_b,是输入和权重的线性组合,因此偏导数 ∂z/∂w1 简单地是给定权重 w1 相关的输入 x1。这表明 z 相对于其直接权重的梯度不依赖于其他层或损失函数(错误)。 它是一个直接的关系:偏导数等于前向传递中 w1 乘以的输入。对于偏置项 b,导数始终为 1,因为偏置是一个常数项。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/5ee00353417045e572ed9bf421144a6d.png

图片来源 www.youtube.com/watch?v=ibJpTrp5mcE&t=1510s,作者标注。

为了通过例子进行说明,让我们考虑两种场景。首先考虑第一种场景,其中 z 位于网络的第一个隐藏层。如图所示,z 及其输入用红色标记,我们可以通过直接使用相关输入的值来计算导数。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/189915affeb2f581c1f547c1ac294e11.png

如果该层是其他不直接与输入数据关联的隐藏层,那么这个原理也适用于这些隐藏层。线性函数相对于其权重的偏导数是该权重的输入。在图中,对于网络中的后续层,如 z’z”,相对于它们相关权重的偏导数,例如 _w_3 和 _w_4,是前一层激活函数的输出,在这种情况下是 a。我在蓝色中突出了这些关系。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/621d8e089932f968ac5b67dc5b9471eb.png

总结来说,**计算 z(激活前的输出)相对于其权重的偏导数是直接的:它是该权重的输入值。**相对于偏置的导数始终为 1。一个人可以沿着网络图中权重的边缘追踪,以找到相应的输入,而这个输入就是输出 z 相对于权重的偏导数的值。这一部分的反向传播过程不需要从损失中获取任何“反向”信息,而是在处理输入数据的前向传递过程中确定的。

因此,计算 ∂z/∂w1 并不是一个“反向”过程。它实际上等于神经元的输入。那么,激活输出相对于其输入的偏导数,∂a/∂z 呢?

相比于 ∂z/∂w1,计算 ∂a/∂z 更加直接,因为 a 只是 z 的函数结果。具体来说,a = σ(z),其中 σ(z) 代表激活函数。对于那些不熟悉激活函数的人来说,激活函数如 σ_(_z) 被用来向神经网络引入非线性。用我们关于水流的水流类比,你可以将其视为一个‘水门’,以非线性的方式改变计算的水流,使网络能够捕捉到非线性复杂模式。这种非线性对于网络捕捉复杂、非线性的潜在模式至关重要。

由于激活函数在神经网络中至关重要,并且将计算转换为非线性,因此 ∂a/∂z 的导数是反向传播中的关键组成部分。它有助于在反向遍历网络时调整梯度。给定 a = σ(z),导数 ∂a/∂z 简单地是 σ′(z),即激活函数相对于原始输入 z 的导数。

计算 σ′(z) 是相当简单的。它涉及到将输入 z 插入到激活函数的导数中。激活函数的导数,是一个不需要任何关于当前预测损失的任何信息的另一个函数。例如,如果激活函数是 sigmoid 函数,其导数 σ′(z) 可以用函数本身来定义,即 _ σ(z)_=σ(z)(1σ(z))**。在实践中,我们的程序可以在训练之前定义 σ’(z)。然后我们将 z 输入到 σ’(z) 中以获得偏导数 ∂a/∂z。

**因此,像 ∂z/∂w1 一样,∂a/∂z 不需要损失信息,可以在前向传播过程中计算。**这个导数随后在反向传播的链式法则中用于找到损失相对于权重的梯度。

考虑到我们到目前为止的讨论,当计算 ∂L/∂w1 时,三个组成部分中的两个不需要损失信息或来自后续层的输入。那么,为什么我们称计算过程为“反向传播”呢?

我很高兴你注意到了这一点。让我们回顾一下到目前为止的讨论:

我们已经确定,计算 ∂z/∂_w_1 是直接的,因为它与权重 w1 相关的输入。同样,对于激活函数的输出 a,表示为 a_=σ(z),其导数 ∂a_/∂z 就是 σ_′(_z),这是激活函数的导数。这两个组成部分的计算与损失函数无关,可以在训练过程之前预先定义或在前向传播过程中预先计算。

现在,剩余的部分涉及 ∂L/∂a,它告诉我们激活输出 a 的变化如何影响整体损失 L。想象一下整个神经网络就像一个蛋糕工厂的生产线,负责烘焙、包装和将蛋糕运送到商店。如果一个蛋糕损坏到达(类似于一个糟糕的预测),有必要回溯并确定哪个阶段的过程导致了损坏以及程度如何。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/c3f472e2643b0f1108a269a475a1426a.png

想象一下整个神经网络就像一个蛋糕工厂的生产线。由作者使用 ChatGPT 创建的图像。

在反向传播中,我们使用链式法则来分解 ∂L/∂a。在我们的示例网络中,a 是与两个输出 z*′ 和 z*′′ 相关的输入的一部分。然后,当我们试图了解 a 对损失有多大贡献时,我们需要从这两个输出中收集信息来衡量其影响。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/9394843b7667bb1b7a313cb1386f82b7.png

图片来源 www.youtube.com/watch?v=ibJpTrp5mcE&t=1510s,由作者标注。

这类似于蛋糕生产线,其中∂z′/∂a 和∂z′′/∂a 代表 a 对每个输出的贡献,我们必须从损失通过这些渠道追溯影响。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/4b1fd5fa2e2a5f14ac5d12b352cfefd5.png

给定

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/0f4a1dcd8686695ba62f18410cf33f3c.png

我们将这些与激活函数的导数∂a/∂z 结合起来得到

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/b56eec7b992d718c8c6be6d4792b4517.png

这是理解为什么这个过程被称为反向传播的关键。

这个公式表明∂L/∂z受到下一层梯度的影響,展示了计算的“反向”方面。这是理解为什么这个过程被称为反向传播的关键。

为了更新整个神经网络中的权重,我们计算损失相对于每个权重的偏导数,即∂L/∂w。从输入层到输出层的正向计算将是不高效的,需要从输入层开始对每一层重复计算∂L/∂z。使用上述函数,一种更有效的方法是从输出层开始计算∂L/∂z,并向后移动到输入层。这种方法涉及存储结果并重用当前层的∂L/∂z 来计算前一层。请注意,为了找到(n-1)层的∂L/∂z,我们只需要相对于第 n 层的损失导数。因此,这个过程允许我们只计算一次每一层的导数,通过一个单一、优雅的反向传递。

因此,我们递归地反向计算每一层的∂L/∂z。那么,我们如何计算相对于输出层 z 的损失偏导数,它作为∂L/∂z 的第一个值来启动这个过程?

让我们考虑输出层的∂L/∂z 计算。在这里,z 是输出层的输入,它通过激活函数σ(z)被转换成预测 y-hat。然后,预测(y-hat)被用来计算损失,表示为L(y, y-hat),其中 L 是损失函数。然后我们会应用链式法则来定义最后一层的∂L/∂z 的计算为两部分。

  • 损失函数相对于网络预测的导数,这取决于损失函数的类型。例如,对于回归任务,我们通常使用 MSE(均方误差)。而对于多类分类,我们会选择 CE(交叉熵)作为损失函数。然后对于单次预测,损失相对于预测的偏导数将是

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/0fd09ff76a11865eccf6f6296a339a8a.png

  • 预测(y-hat)相对于最后一层激活输入的导数,类似于∂a/∂z,是最后一层激活函数σ(z)的导数。

我们迄今为止关于反向传播的讨论主要集中在使用 SGD(随机梯度下降)且批大小 = 1。如果我们使用较大的批大小进行训练,这会改变计算吗?

你是对的,使用较大批大小的计算确实略有不同,但我们讨论的反向传播的基本计算仍然有效。它主要涉及一个额外的加和步骤。

当批大小 > 1 时,正向传播过程中的损失是批内所有样本的平均损失。因此,损失相对于权重的偏导数 ∂L/∂w1 是批内所有数据点单个损失导数(∂l/∂w)的平均值。

从数学上讲,对于批大小为 n,损失 L 对权重 w1 的梯度计算如下

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/b021ab73a8e4a89cfd21aed66788067f.png

这里 i 代表批内第 i 个样本的损失。需要注意的是,我们在这里使用的是梯度的平均值。通过计算平均值,我们确保权重的更新反映了批内所有样本损失减少的平均方向。

好的,随着我们的讨论,让我们将这一知识应用到理解你拥有的代码上。这是一种掌握复杂概念的好方法。像往常一样,让我们将其转化为一个问题:

根据我们的讨论,如何将这些元素结合起来构建反向传播计算?如何利用我们的见解来解码这个代码片段?

让我们一步一步地查看计算步骤。这里显示的所有代码均来自机器学习掌握的 Python 教程。

定义激活函数及其导数

代码使用 sigmoid 函数作为激活函数。为了计算激活输出相对于其输入的导数,代码将 sigmoid 函数定义为 transfer,并为其导数定义了 transfer_derivative 函数。

# Transfer neuron activation
def transfer(activation):
  return 1.0 / (1.0 + exp(-activation))

# Calculate the derivative of an neuron output
def transfer_derivative(output):
 return output * (1.0 - output)

计算 ∂L/∂a。

backward_propagate_error 函数涉及从输出层到输入层的反向循环。在函数中,变量 error 代表 ∂L/∂a。它区分输出层和隐藏层以计算 ∂L/∂a。

  • **对于输出层,**误差仅仅是输出值与期望值之间的差异。这里使用的损失函数是预测误差(y – y_hat)。这个损失函数的导数是 -1。因此,∂L/∂a 的计算为 neuron['output'] - expected[j]。这部分代码可能有些令人困惑,因为我们通常使用 MSE(均方误差)和 CE(交叉熵)作为损失函数。此外,代码中损失相对于预测误差的偏导数并不明显。
def backward_propagate_error(network, expected):
 for i in reversed(range(len(network))):
  layer = network[i]
  # calculate the loss forr each layer
  if i != len(network)-1:
   ...
  else: 
      # ∂L/∂a of the output layer  
   for j in range(len(layer)):
    neuron = layer[j]
    errors.append(neuron['output'] - expected[j]) 
  • **对于隐藏层,**它是基于下一层的权重和 delta(代表 ∂L/∂z)计算的。
# Backpropagate error and store in neurons
def backward_propagate_error(network, expected):
 for i in reversed(range(len(network))):
  layer = network[i]
  errors = list()
  # calculate the loss forr each layer
  if i != len(network)-1:
   ...
        # ∂L/∂a of the hidden layer  
    for neuron in network[i + 1]: 
     error += (neuron['weights'][j] * neuron['delta']) 
    errors.append(error)
  else: 
     ... 

计算 ∂L/∂z。

在这里,我们计算 ∂L/∂z 为 ∂L/∂a(在代码中用 errors 表示)乘以 σ’(z),并将结果存储为 neuron['delta']

# Backpropagate error and store in neurons
def backward_propagate_error(network, expected):
  for i in reversed(range(len(network))):
  layer = network[i]
  errors = list()
 # calculate the partial derivative of the loss with repsect to the input before activation
  for j in range(len(layer)):
   neuron = layer[j] 
   neuron['delta'] = errors[j] * transfer_derivative(neuron['output']) 

计算 ∂L/∂w 以调整权重。

最后,我们会使用 ∂L/∂w = ∂L/∂z* ∂z/∂w 来更新相应层的权重。权重根据 delta(代表 ∂L/∂z)和输入进行调整,而对于偏置项,我们只需要 delta

# Update network weights with error
def update_weights(network, row, l_rate):
  for i in range(len(network)):
    inputs = row[:-1]
    if i != 0:
      inputs = [neuron['output'] for neuron in network[i - 1]]
    for neuron in network[i]:
      for j in range(len(inputs)):
        neuron['weights'][j] -= l_rate * neuron['delta'] * inputs[j] 
      neuron['weights'][-1] -= l_rate * neuron['delta'] # bias term the input is 1

注意,这里的代码基于批量大小为 1 的 SGD。因此,代码不包括涉及多个样本的平均损失或部分导数的计算。

你知道,反向传播有点像是前向传播的逆过程,不是吗?这是一种公平的看法吗?

反向传播的计算类似于前向传播的反向版本,主要是因为:

  • 激活函数的导数反映了激活函数在前向传播中的作用。

  • ∂L/∂z 的计算类似于前向传播中计算 z,将前一层输出的乘积和权重相加。对于反向传播,我们使用下一层的输出。

然而,它们仍然相当不同:

  • 不同的起点。 前向传播从输入数据 X 开始,而反向传播从损失开始,这使得损失函数的选择至关重要。

  • 不同的目的。 前向传播旨在根据给定数据生成预测。而反向传播的目标是训练模型。我们通过比较预测值和实际值来调整模型的参数来实现这一点。

  • 计算依赖性。 反向传播需要前向传播的结果,并使用链式法则整合前向传播的元素和后续层的损失部分导数。因此,反向传播与前向传播过程密不可分。

为什么理解反向传播的计算细节对我们来说很重要?

即使有像 PyTorch 和 TensorFlow 这样的深度学习框架,理解反向传播也是至关重要的。对其计算的深入了解提供了对深度学习中各种挑战和训练技巧的直观理解,而无需记住它们。从理解反向传播中获得的关键见解是

反向传播如何影响激活函数的选择?

回想我们关于计算 ∂a/∂z 的讨论。在代码中实现反向传播时,为了节省内存,我们会定义激活函数及其导数,这就是为什么教程通常会列出导数和激活函数。这些导数与激活函数本身一样重要,通常在建模之前就预先定义。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/ff844631af9cfbe39ad4055856854025.png

激活函数及其导数。来源:dwaithe.github.io/images/activationFunctions.png

考虑在反向传播过程中计算 ∂L/∂z。这个计算依赖于后续层的输出,并且有一个模式:**早期层的梯度是每个连续层激活函数导数的乘积。**例如,假设我们的示例网络在 z’和 z”之后还有一层,权重为 w_5 和 w6,这个计算就变成了这些导数的复杂乘积。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/f60a06183fd23b5e66b96a45144cd81d.png

如果我们选择 sigmoid 和 tanh 作为我们的激活函数,那么它们可能会导致梯度消失问题。因为 sigmoid 和 tanh 等函数的导数范围非常小。sigmoid 的导数介于 0 和 0.25 之间,而 tanh 的导数介于 0 和 1 之间。**因此,当它们相乘时,其乘积会逐渐减小,导致梯度逐渐减小。**这种梯度幅度的减小导致早期层接收到的梯度更新非常小,使它们远离有效的学习和参数调整。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/c4731eecb7f992f2eacc313e0f73a440.png

来源:i0.wp.com/neptune.ai/wp-content/uploads/2022/10/Vanishing-and-Exploding-Gradients-in-Neural-Network-Models-Debugging-Monitoring-and-Fixing-Practical-Guide_7.png?resize=636%2C497&ssl=1

一种解决方案是使用导数范围更广的激活函数。例如,ReLU 会返回 1 或 0,有效地解决了梯度消失问题。然而,ReLU 有其缺点,因为它可能导致导数的乘积变为 0。因此,神经元不会更新,变得不活跃,无法对模型学习做出贡献。这个问题被称为“死亡 ReLU”。

总结来说,对反向传播的深入了解可以非常有帮助。它是开发有效的神经网络模型和解决训练问题的基石。

(除非另有说明,所有图片均为作者提供)


如果你喜欢这个系列towardsdatascience.com/tagged/courage-to-learn-ml,记得你的互动——点赞、评论和关注——不仅仅是支持;它们是推动这个系列继续进行并激励我持续分享的动力。

本系列的其他文章:


如果您喜欢这篇文章,您可以在LinkedIn上找到我。

参考:

从零开始实现反向传播算法

YouTube 视频嵌入

YouTube 视频嵌入

【RIS 辅助的 THz 混合场波束斜视下的信道估计与定位】在混合场波束斜视效应下,利用太赫兹超大可重构智能表面感知用户信道与位置(Matlab代码实现)内容概要:本文围绕“IS 辅助的 THz 混合场波束斜视下的信道估计与定位”展开,重点研究在太赫兹(THz)通信系统中,由于混合近场与远场共存导致的波束斜视效应下,如何利用超大可重构智能表面(RIS)实现对用户信道状态信息和位置的联合感知与精确估计。文中提出了一种基于RIS调控的信道参数估计算法,通过优化RIS相移矩阵提升信道分辨率,并结合信号到达角(AoA)、到达时间(ToA)等信息实现高精度定位。该方法在Matlab平台上进行了仿真验证,复现了SCI一区论文的核心成果,展示了其在下一代高频通信系统中的应用潜力。; 适合人群:具备通信工程、信号处理或电子信息相关背景,熟悉Matlab仿真,从事太赫兹通信、智能反射面或无线定位方向研究的研究生、科研人员及工程师。; 使用场景及目标:① 理解太赫兹通信中混合场域波束斜视问题的成因与影响;② 掌握基于RIS的信道估计与用户定位联合实现的技术路径;③ 学习并复现高水平SCI论文中的算法设计与仿真方法,支撑学术研究或工程原型开发; 阅读建议:此资源以Matlab代码实现为核心,强调理论实践结合,建议读者在理解波束成形、信道建模和参数估计算法的基础上,动手运行和调试代码,深入掌握RIS在高频通信感知一体化中的关键技术细节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值