一篇“**神经网络中的反向传播**”引发的学习血案

引言

最近不停的听到反向传播以及方向传播的优化方法的一些东西,久好奇翻了一番之前的看过的一篇文章

原文url:https://serokell.io/blog/understanding-backpropagation

还是先放上译文:

好的,以下是文章内容的中文翻译:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194

**神经网络中的反向传播**

**作者:** Irena Logunova

**发布日期:** 2023 年 12 月 19 日

**阅读时长:** 13 分钟

反向传播是神经网络深度学习的一个基本组成部分。自 21 世纪初以来,它的发展极大地促进了深度学习算法的广泛采用。在这篇文章中,我们将探讨这种方法的基本概念、应用和历史。

**什么是前向传播和反向传播?**

神经网络中的前向传播是指输入数据通过网络各层进行计算并产生输出的过程。每一层处理数据并将其传递到下一层,直到获得最终输出。在此过程中,网络学习识别数据中的模式和关系,通过反向传播调整其权重,以最小化预测输出和实际输出之间的差异。

(图片:前向传播和反向传播示意图)

反向传播过程包括计算预测输出和实际目标输出之间的误差,同时将信息反向传递到前馈网络中,从最后一层开始,一直到第一层。为了计算特定层的梯度,使用微积分的链式法则将所有后续层的梯度组合起来。

反向传播,也称为误差的反向传播,是一种广泛用于计算深度前馈神经网络中导数的技术。它在用于训练此类网络的各种监督学习算法中起着至关重要的作用。

神经网络的训练涉及使用梯度下降,这是一种迭代优化算法,用于发现可微函数的局部最小值。在训练过程中,计算损失函数来衡量网络预测与实际值之间的差异。反向传播能够计算损失函数相对于网络中每个权重的梯度。这种能力支持单独的权重更新,在多次训练迭代中逐步减少损失函数。

**反向传播过程是什么样的?**

反向传播的目的是通过微调神经网络的权重和偏置来最小化成本函数。这些调整的程度取决于成本函数相对于这些特定参数的梯度。通过链式法则计算梯度,反向传播有效地将误差信息向后传播到网络中。因此,网络可以沿梯度的相反方向迭代更新其参数。这个迭代过程使神经网络能够收敛到改进的性能和准确的预测。

神经网络中反向传播计算权重的梯度的基本步骤是前向传播和反向传播。

**前向传播**

在前向传播过程中,输入数据逐层通过网络传播,从输入层开始,一直到输出层。网络中的每个神经元接收输入,计算输入的加权和,应用激活函数,并将输出传递到下一层。这个过程一直持续到获得最终输出。前向传播根据当前权重计算网络的输出。

在我们进行反向传播之前,我们需要介绍计算最佳权重的最快方法,这对于复杂的多参数网络来说并非易事。这就是计算图发挥作用的地方。

**什么是计算图?**

计算图是一种有向图,用于表示模型内部执行的计算。该图通常以数据 (X) 和标签 (Y) 等输入开始。当我们从左到右移动图形时,会遇到表示计算函数所涉及的基本计算的节点。例如,存在用于输入 (X) 和权重矩阵 (W) 之间的矩阵乘法的节点、用于合页损失(用于 SVM 分类器)的红色节点以及用于模型中正则化项的绿色节点。该图以表示要在模型训练期间计算的标量损失 (L) 的输出节点结束。虽然这个计算图对于由于操作数量有限的线性模型来说似乎很简单,但对于具有多次计算的复杂模型来说,它变得更加复杂和关键。

(图片:深度网络示意图)

当我们向后移动时,为了计算最佳损失函数,计算图是一种实现最佳解决方案的方法,它可以显著减少所需的计算量。

这个过程在[这篇白皮书](this whitepaper)中有详细解释。

在每个节点上,反向模式微分会合并源自该节点的所有路径。我们不需要评估权重相互影响的所有可能组合,但由于有导数,我们可以通过仅计算每个节点的反向操作一次来获得正确的系数。

**反向传播**

在反向传播中,通过将误差反向传播到网络中来计算权重的梯度。它从输出层开始,一直到输入层。通过将网络的预测输出与真实输出或目标值进行比较来量化误差。使用微积分的链式法则计算损失函数相对于每个权重的梯度,这涉及计算每个层权重的偏导数。然后使用梯度来更新网络的权重,旨在最小化损失函数。

反向传播本质上决定了每个权重对总体误差的贡献程度,并相应地调整它们。通过迭代执行前向和反向传播,网络学习调整其权重,提高其做出准确预测的能力。

观看此视频,了解前向和反向传播的详细说明。

(视频:Backpropagation, step-by-step | DL3)

**反向传播算法有哪些类型?**

反向传播网络的两种主要类型是静态反向传播(提供即时映射)和循环反向传播(涉及定点学习)。

1. **静态反向传播:** 它通常用于前馈神经网络和一些卷积神经网络 (CNN),其中数据点之间没有时间依赖性。该算法累积一批数据点的损失函数梯度,然后对模型的参数执行一次更新。批处理过程有助于利用现代硬件中的并行处理能力,从而使大型数据集的训练过程更加高效。

  该算法能够解决静态分类问题,例如光学字符识别 (OCR)。
1. **循环反向传播:** 循环反向传播是用于循环神经网络 (RNN) 的反向传播算法的扩展。在 RNN 中,数据通过一系列相互连接的节点循环流动,从而使网络能够保留来自先前时间步的信息。

  循环反向传播涉及及时传播 RNN 中的误差信号。它计算损失函数相对于模型参数在多个时间步长的梯度,同时考虑到当前时间步长和先前时间步长之间的依赖关系和相互作用。此过程使网络能够学习和更新其参数,以提高其在需要顺序或时间依赖性的任务(例如自然语言处理、语音识别和时间序列预测)中的性能。

**为什么要使用反向传播?**

在前向传播完成后,将评估网络的误差,理想情况下应将其最小化。

如果当前误差很大,则表明网络尚未有效地从数据中学习。换句话说,当前的权重集不足以准确地最小化误差并生成精确的预测。因此,有必要更新神经网络权重以减少误差。

反向传播算法在权重更新中起着至关重要的作用,其目标是最小化误差。

**反向传播算法的优点**

反向传播具有以下几个关键优势:

1. **内存效率**

  与遗传算法等替代优化算法相比,它可以有效地计算导数,从而减少内存使用量。这在处理大型神经网络时特别有用。
1. **速度**

  它速度很快,特别是对于中小型神经网络。然而,随着层数和神经元数量的增加,计算更多导数会导致性能变慢。
1. **通用性**

  该算法适用于各种网络架构,包括卷积神经网络、生成对抗网络、全连接网络等。反向传播的通用性使其能够在各种场景中有效发挥作用。
1. **参数简单性**

  反向传播不需要调整特定参数,从而减少了开销。该过程中涉及的唯一参数与梯度下降算法相关,例如学习率。

在使用神经网络时,可以利用不同的算法来减少损失函数的输出和学习率,以提供更精确的结果。有许多替代方法可以修改神经网络的属性,例如 Adam(自适应矩估计),多年来一直是最新技术,Nesterov 加速梯度,AdaGrad 和 AdaDelta。

如果您想了解更多信息,请查看[不同优化器的详细说明](check out this detailed description)。

损失函数优化的最先进算法之一是 [Sophia 优化器](Sophia optimizer),由斯坦福大学研究人员于 2023 年 5 月发布。此类优化器的经典示例是成本函数,我们将在下面解释。

**计算反向传播:成本函数**

成本函数表示模型输出与所需输出之间差异的平方。

当将神经网络应用于具有相关像素值的数百万张图像时,我们可以假设预测输出和相应的实际值。

较小的成本函数表示模型在训练数据上的性能更好。此外,预计具有最小化成本函数的模型在未见过的数据上也表现良好。

成本函数接受所有输入(可能涉及数百万个参数),并产生一个值。该值作为指南,指示模型中需要多少改进。它通知模型其性能不佳,并且需要调整其权重和偏置。然而,仅仅告知模型其性能是不够的。我们还需要为模型提供一种使其能够最小化误差的方法。

这就是梯度下降和反向传播发挥作用的地方,它们提供了模型更新其参数和减少成本函数的方法。

**梯度下降**

为了实现更好的参数调整并最小化实际输出和训练输出之间的差异,我们采用了一种称为梯度下降的直观算法。目前,梯度下降是机器学习和深度学习中最流行的优化策略。该算法识别错误并有效地减少它们。

从数学上讲,它通过找到最小点来优化[凸函数](convex function)。

梯度的概念可以理解为衡量函数的输出在其输入略微修改时的变化程度。它也可以可视化为函数的斜率,其中较高的梯度表示更陡的斜率并有助于模型更快地学习。从比喻上讲,您可以将其比作下降到山谷的底部而不是爬上山顶。这是因为它是一种最小化给定函数的优化算法。

**梯度下降的类型**

现在让我们探讨不同类型的梯度下降。

(图片:梯度下降类型图)

**批量梯度下降**

批量大小是指单个批次中包含的训练示例的总数。由于无法一次性将整个数据集传递到神经网络,因此数据集被分成多个批次或子集。

在批量梯度下降中,使用完整数据集来计算成本函数的梯度。然而,这种方法可能很慢,因为它需要为每次更新计算整个数据集的梯度。这可能具有挑战性,尤其是在大型数据集的情况下。成本函数是在初始化参数后计算的,并且该过程涉及从磁盘读取所有记录到内存中。在每次迭代之后,采取一个步骤,然后重复该过程。

**小批量梯度下降**

小批量梯度下降是一种常用的算法,可提供更快、更准确的结果。数据集被分成小组或“n”个训练示例的批次。与批量梯度下降不同,小批量梯度下降不使用整个数据集。在每次迭代中,采用“n”个训练示例的子集来计算成本函数的梯度。这种方法减少了参数更新的方差,从而实现更稳定的收敛。此外,它可以利用优化的[矩阵运算](matrix operations),从而提高梯度计算的效率。

**随机梯度下降**

随机梯度下降 (SGD) 根据在每次迭代时为数据的随机子集计算的梯度来更新模型的参数,从而允许更快的计算。在每次训练迭代(或时期)中,从训练数据集中选择一批随机数据点。然后使用所选批次计算损失函数相对于模型参数的梯度。接下来,根据计算出的梯度更新模型的参数。在梯度的相反方向上执行更新,以朝着损失函数的最小值移动。对固定数量的迭代或直到满足收敛标准重复这些步骤。

**Tensorflow 和 Pytorch 中的反向传播**

反向传播算法是用于训练深度学习模型的关键技术。

**TensorFlow** 中,您可以通过定义神经网络模型、使用优化器和损失函数编译它、准备数据,然后使用 fit 函数训练模型来使用反向传播。TensorFlow 的自动微分处理训练期间梯度的计算,从而更容易地应用反向传播来有效地训练复杂模型。

要在 **PyTorch** 中使用反向传播,您需要定义神经网络架构和损失函数。在训练过程中,数据通过网络前向传递以进行预测,然后在通过网络各层反向计算梯度的过程中使用反向函数自动计算梯度。然后使用这些梯度通过优化算法(如随机梯度下降)更新模型的参数。

要了解有关在 PyTorch 中使用反向传播的更多信息,请观看本教程:

(视频:PyTorch Tutorial 04 - Backpropagation - Theory With Example)

**反向传播的应用**

反向传播广泛用于训练各种类型的神经网络,并且在最近深度学习的普及中发挥了重要作用。但是反向传播的应用范围更广,从天气预报到分析数值稳定性。以下是其在机器学习中的几个应用示例。

**人脸识别**

卷积神经网络是深度学习中用于图像处理和识别的首选技术,通常使用反向传播算法进行训练。在 Parkhi、Vedaldi 和 Zisserman 于 2015 年进行的一项[研究](study)中,他们开发了一个使用 18 层 CNN 和名人面部数据库的人脸识别系统。该网络使用反向传播在所有 18 层上进行训练,图像分批处理。研究人员使用了一种称为[三重损失](triplet loss)的损失函数来提高网络区分细微面部细微差别的能力。这涉及通过网络馈送三元组图像(例如,两张 Angelina Jolie 的图像和一张 Nicole Kidman 的图像),惩罚网络将同一人的图像错误分类为不同,以及将不同人的图像分类为相似。这个训练过程持续迭代,更新前一层的权重。

**NLP:语音识别**

反向传播已应用于训练各种 NLP 任务的神经网络,包括情感分析、语言翻译、文本生成和语音识别。使用反向传播训练的循环神经网络 (RNN) 通常用于 NLP 中的顺序数据处理。

例如,索尼开发了一个能够识别英语和日语有限命令的系统。该系统使用传入的声音信号分成时间窗口,应用[快速傅里叶变换](Fast Fourier Transform)提取基于频率的特征,然后将其输入到具有五层的神经网络中。反向传播用于训练这些层以理解日语命令,使用 softmax 交叉熵损失函数。研究人员能够通过再训练使同一网络适应识别英语命令,展示了迁移学习能力。

**事故预防**

由于地表资源的枯竭,地下矿井的数量正在增加。[本文](This paper)提供了一种改进地下矿井爆炸后重新进入时间预测的方法,这对于确保工人安全和生产力至关重要。目前,使用的方法(如固定时间间隔和经验公式)具有局限性,并且可能不具有普遍适用性。作者建议,反向传播神经网络可以成为一种解决方案。

**反向传播的历史**

在 19 世纪,法国数学家 Baron Augustin-Louis Cauchy 开发了一种称为梯度下降的方法来求解联立方程组。他的目标是解决涉及多个变量的复杂天文计算。Cauchy 的想法是求函数的导数并采取小步骤以最小化误差。

在接下来的一个世纪里,梯度下降方法在各个学科中得到了应用,为原本难以或无法通过代数解决的具有挑战性的问题提供了数值解。

1970 年,芬兰赫尔辛基大学的一名硕士生 Seppo Linnainmaa 提出了一种用于稀疏连接网络中误差反向传播的有效算法。尽管 Linnainmaa 没有特别提及神经网络,但他的工作为未来的发展奠定了基础。

在 20 世纪 80 年代,研究人员独立开发了时间反向传播,以支持循环神经网络的训练,进一步扩展了该算法的能力。

1986 年,美国心理学家 David Rumelhart 和他的同事发表了一篇极具影响力的论文,将 Linnainmaa 的反向传播算法应用于多层神经网络。这标志着一个重大的突破;随后的几年见证了基于该算法的进一步发展。例如,[Yann LeCun 1989 年的论文](Yann LeCun's 1989 paper)展示了反向传播在卷积神经网络中用于手写数字识别的应用。

近年来,反向传播在深度神经网络的高效训练中发挥着至关重要的作用。虽然为了并行化算法和利用多个 GPU 已经进行了修改,但 Linnainmaa 开发并由 Rumelhart 推广的原始反向传播算法仍然是当代基于深度学习的人工智能的基础。

如果您想了解更多关于反向传播的详细的、基于数学的解释,请查看[这篇文章](this article)。

**结论**

反向传播作为深度学习的核心原理,在神经网络参与的许多领域中发挥着重要作用。通过促进神经网络内权重的微调,它有助于为几乎所有行业生成准确的预测和分类。然而,与任何技术一样,必须平衡效率和复杂性,了解算法的局限性,例如梯度下降与反向传播无法识别它是在误差函数的全局最小值还是局部最小值上工作,以及难以跨越误差函数景观中的平台。

为了克服这些局限性,已经出现了新的方法和其他优化器,这些方法在最新的科学论文中得到了广泛的体现。查看下面的一些出版物。

*   [符号优化算法发现](Symbolic Discovery of Optimization Algorithms)
*   [Adam 优化器](Adam Optimizer)
*   [没有反向传播的优化](Optimization without backpropagation)

然后。不小心。。。真的不小心整理了一下反向传播的涉及的知识篇。。。。真的不小心的。。。就整理了下面一堆东西了。😑太难了。以为AI 是风(疯)口。。。就扎进去了。。。现在真的疯了。。。。。。如果你也疯了。。请往下看

疯点目录

根据提供的文章内容,整理出反向传播算法(Backpropagation)的学习知识目录列表,采用层级结构展示:

  • 一级知识点 1:神经网络基础

    • 二级知识点 1.1:神经元模型 (Neuron Model)

      • 三级知识点 1.1.1:输入和输出

      • 三级知识点 1.1.2:权重 (Weights) 和阈值 (Biases/Threshold)

      • 三级知识点 1.1.3:激活函数 (Activation Function)

    • 二级知识点 1.2:神经网络结构 (Neural Network Structure)

      • 三级知识点 1.2.1:输入层 (Input Layer)

      • 三级知识点 1.2.2:隐藏层 (Hidden Layer)

      • 三级知识点 1.2.3:输出层 (Output Layer)

      • 三级知识点 1.2.4:多层感知器 (Multilayer Perceptron, MLP)

      • 三级知识点1.2.5: 前馈神经网络

    • 二级知识点 1.3:常见的神经网络类型

      • 三级知识点 1.3.1:卷积神经网络 (CNN)

      • 三级知识点 1.3.2: 循环神经网络(RNN)

  • 一级知识点 2:前向传播 (Forward Propagation)

    • 二级知识点 2.1:前向传播过程

    • 二级知识点 2.2:计算图 (Computational Graph)

  • 一级知识点 3:反向传播 (Backpropagation)

    • 二级知识点 3.1:反向传播的目的:计算梯度,更新权重

    • 二级知识点 3.2:链式法则 (Chain Rule)

    • 二级知识点 3.3:反向传播过程

    • 二级知识点 3.4:反向传播的类型

      • 三级知识点 3.4.1:静态反向传播 (Static Backpropagation)

      • 三级知识点 3.4.2:循环反向传播 (Recurrent Backpropagation)

    • 二级知识点3.5: BP神经网络

  • 一级知识点 4:优化算法 (Optimization Algorithms)

    • 二级知识点 4.1:梯度下降 (Gradient Descent)

      • 三级知识点 4.1.1:批量梯度下降 (Batch Gradient Descent)

      • 三级知识点 4.1.2:小批量梯度下降 (Mini-batch Gradient Descent)

      • 三级知识点 4.1.3:随机梯度下降 (Stochastic Gradient Descent)

    • 二级知识点 4.2:其他优化器 (Optimizers)

      • 三级知识点 4.2.1:Adam (Adaptive Moment Estimation)

      • 三级知识点 4.2.2:其他 (AdaGrad, AdaDelta, Nesterov Accelerated Gradient, Sophia, etc.)

  • 一级知识点5: 损失函数

    • 二级知识点 5.1: 损失函数的定义

    • 二级知识点 5.2: 不同任务的损失函数

      • 三级知识点 5.2.1: 分类任务

      • 三级知识点 5.2.2: 回归任务

  • 一级知识点 6:反向传播的应用

    • 二级知识点 6.1:人脸识别 (Face Recognition)

    • 二级知识点 6.2:自然语言处理:语音识别 (NLP: Speech Recognition)

    • 二级知识点 6.3: 事故预防

  • 一级知识点 7:反向传播的历史

  • 一级知识点8: 超参数

    • 二级知识点 8.1: 输入图像大小

    • 二级知识点 8.2: 学习率

    • 二级知识点 8.3: 正则化参数

    • 二级知识点 8.4: 神经网络层数

    • 二级知识点 8.5: 批处理大小(batch size)

    • 二级知识点 8.6: 卷积层参数

    • 二级知识点 8.7: 池化层参数

    • 二级知识点 8.8: 迭代周期

好的!虽然有点疯的目录看起来很鼓噪,那就让我们告别严肃的学术氛围,来点轻松幽默的风格,开启反向传播的学习之旅!

哇哦!这篇文章真是信息量爆炸,就像一口气吞下了一整本神经网络的“天书”!😵‍💫 别担心,虽然内容有点多,但咱们可以像剥洋葱一样,一层一层地揭开反向传播的神秘面纱。🧅

不过,在正式开始“剥洋葱”之前,我们需要先整理一下“工具箱”🧰,也就是梳理一下知识点。毕竟,磨刀不误砍柴工嘛!🔪

想象一下,反向传播就像是一位“调皮”的神经网络教练,它会根据网络“学员”的表现(预测结果)来“敲打”它们,让它们不断改进,最终成为“学霸”!👨‍🎓👩‍🎓

那么,这位“教练”究竟有哪些“独门秘籍”呢?🤔 让我们一起列个清单,把这些“秘籍”分门别类,变成我们的学习路线图!🗺️

接下来,我将按照之前提供的层级结构知识目录列表,开始 Step-by-step 的学习引导。准备好了吗?Let’s go! 🚀

神经网络基础

一级知识点 1:神经网络基础 🧠

在深入了解反向传播之前,我们需要先熟悉一下神经网络的基础知识。这就像盖房子要先打好地基一样重要!🧱

二级知识点 1.1:神经元模型 💡

神经元是神经网络的基本组成单位。你可以把它想象成一个“小精灵”🧚‍♀️,它接收一些输入信号,然后根据自己的“心情”(权重和偏置)做出反应,产生一个输出信号。

  • 三级知识点 1.1.1:输入和输出 ➡️⬅️ 神经元接收来自其他神经元的信号作为输入,然后产生一个信号作为输出。就像你听到朋友的笑话(输入),然后哈哈大笑(输出)一样。😆

  • 三级知识点 1.1.2:权重 (Weights) 和阈值 (Biases/Threshold) ⚖️ 每个输入信号都有一个权重,表示这个信号的重要性。权重越大,这个信号就越重要。阈值就像是一个“门槛”,只有当输入的加权和超过这个门槛时,神经元才会被“激活”,产生输出。 可以这样理解: 权重好比你对不同朋友的信任度,信任度越高,他们的话就越重要。 阈值好比你的笑点,只有笑话足够好笑(超过笑点),你才会笑出来。😄

  • 三级知识点 1.1.3:激活函数 (Activation Function) 🔥 激活函数就像是神经元的“情绪调节器”,它将输入的加权和转换成一个输出信号。激活函数有很多种,每种都有不同的“调节”效果。 例如,Sigmoid 函数可以将输入压缩到 0 到 1 之间,就像把神经元的“情绪”控制在一个温和的范围内。😌

二级知识点 1.2:神经网络结构 🏗️

神经网络是由许多神经元相互连接而成的。这些神经元按照不同的层次排列,形成了不同的网络结构。

  • 三级知识点 1.2.1:输入层 (Input Layer) 📥 输入层负责接收外部数据。就像你的眼睛 👀 和耳朵 👂 负责接收外界的信息一样。

  • 三级知识点 1.2.2:隐藏层 (Hidden Layer) 🕵️ 隐藏层是神经网络的“大脑”,负责处理输入数据,提取特征。隐藏层可以有很多层,层数越多,网络就越复杂,处理能力也越强。就像福尔摩斯的大脑一样,层层推理,最终找出真相!🔍

  • 三级知识点 1.2.3:输出层 (Output Layer) 📤 输出层负责产生网络的最终输出。就像你的嘴巴 👄,负责说出你的想法。

  • 三级知识点 1.2.4:多层感知器 (Multilayer Perceptron, MLP) 🏢 多层感知器是一种常见的神经网络结构,它由多个层次的神经元组成,每一层的神经元都与下一层的所有神经元相连。 你可以把多层感知器想象成一栋大楼,每一层都有很多房间(神经元),每个房间都与下一层的所有房间相连。🏢

  • 三级知识点 1.2.5:前馈神经网络 ➡️ 在前馈神经网络中,信息只能从输入层流向输出层,不能反向流动。就像单行道一样,只能前进,不能后退。🚗

二级知识点1.3: 常见的神经网络类型 * 三级知识点 1.3.1:卷积神经网络 (CNN) 🖼️ 卷积神经网络特别擅长处理图像。 比如识别人脸, 自动驾驶。 * 三级知识点 1.3.2:循环神经网络 (RNN) ✍️ 循环神经网络更适合处理有顺序的信息, 比如分析一段话, 预测下一秒钟的股票价格。

前向传播 (Forward Propagation)

一级知识点 2:前向传播 (Forward Propagation) ➡️

前向传播是神经网络处理信息的过程,就像水流顺着管道流动一样自然。💧

  • 二级知识点 2.1:前向传播过程 🚶

    1. 数据输入: 首先,我们将数据(例如一张图片 🖼️ 或一段文字 📝)输入到神经网络的输入层。

    2. 逐层传递: 数据从输入层开始,逐层通过隐藏层。每一层的神经元都会对接收到的数据进行加权求和,然后通过激活函数进行处理,产生输出。就像接力赛一样,每一棒的选手(神经元)都会把接力棒(数据)传递给下一棒。🏃‍♀️🏃‍♂️

    3. 输出结果: 最后,数据到达输出层,产生网络的最终输出结果。这个结果可能是对图片的分类、对文字的翻译等等。

  • 二级知识点 2.2:计算图 (Computational Graph) 📊

    为了更好地理解前向传播和后续的反向传播过程,我们可以使用计算图来表示。

    • 计算图是一种有向图,它将计算过程表示为一系列节点和边。

    • 节点表示计算操作(例如加法、乘法、激活函数等),边表示数据流动的方向。

    • 通过计算图,我们可以清晰地看到数据是如何在神经网络中流动的,以及每个节点是如何参与计算的。

    举个例子:假设我们要计算 y = (x + w) * b,我们可以用计算图表示为:

    x —-(+)—-(*)—- y
    |     ^     ^
    |     |     |
    w —-|     b —-|

    在这个图中,xwb 是输入节点,(+)(*) 是计算节点,y 是输出节点。数据从输入节点流向输出节点,完成了计算过程。

前向传播就像是神经网络的“思考”过程,它根据输入数据和当前的权重,一步一步地计算出最终的输出结果。🤔

现在,你已经了解了前向传播的过程了!请你思考一下:

  • 你能用自己的话描述一下前向传播的过程吗?🗣️

  • 你能画出一个简单的神经网络的计算图吗?✍️

范例

好的,为了让你更好地理解知识点,我将结合之前的神经网络基础和前向传播内容,给出一些更具体的范例,并配以图示和代码片段(Python + PyTorch)进行说明。

一级知识点 1:神经网络基础 🧠

二级知识点 1.1:神经元模型 💡

  • 范例: 假设我们有一个简单的神经元,用于判断一封邮件是否是垃圾邮件。

    • 输入 (Inputs):
      • x1: 邮件中包含“免费”一词的次数 (例如:3)
      • x2: 邮件中包含链接的数量 (例如:5)
      • x3: 发件人是否在已知的垃圾邮件发送者列表中 (1 表示是,0 表示否) (例如:1)
    • 权重 (Weights):
      • w1: 0.8 (表示“免费”一词的重要性)
      • w2: 0.5 (表示链接数量的重要性)
      • w3: 1.2 (表示发件人是否在垃圾邮件列表中的重要性)
    • 偏置 (Bias):
      • b: -1.0
    • 激活函数 (Activation Function):
      • Sigmoid 函数:σ(z) = 1 / (1 + exp(-z)) (将输出压缩到 0-1 之间)

    计算过程:

    1. 加权和 (Weighted Sum):
      z = (x1 * w1) + (x2 * w2) + (x3 * w3) + b
      z = (3 * 0.8) + (5 * 0.5) + (1 * 1.2) + (-1.0) = 2.4 + 2.5 + 1.2 - 1.0 = 5.1
    2. 激活函数 (Activation):
      output = σ(z) = 1 / (1 + exp(-5.1)) ≈ 0.994

    结论: 输出值接近 1,表示这个神经元认为这封邮件很可能是垃圾邮件。

    PyTorch 代码片段:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    import torch
    import torch.nn as nn

    # 定义神经元 (单层线性模型 + Sigmoid 激活函数)
    class Neuron(nn.Module):
    def __init__(self):
    super().__init__()
    self.linear = nn.Linear(3, 1) # 输入维度为 3,输出维度为 1
    self.sigmoid = nn.Sigmoid()

    def forward(self, x):
    return self.sigmoid(self.linear(x))

    # 创建神经元实例
    neuron = Neuron()

    # 设置权重和偏置 (手动设置,实际应用中通过训练学习)
    neuron.linear.weight.data = torch.tensor([[0.8, 0.5, 1.2]])
    neuron.linear.bias.data = torch.tensor([-1.0])

    # 输入数据
    x = torch.tensor([3.0, 5.0, 1.0]) # 注意:输入数据类型需为浮点数

    # 计算输出
    output = neuron(x)
    print(f"神经元输出: {output.item():.3f}") # 输出: 神经元输出: 0.994

二级知识点 1.2:神经网络结构 🏗️

  • 范例: 假设我们要构建一个简单的多层感知器 (MLP) 来识别手写数字(0-9)。

    • 输入层 (Input Layer): 784 个神经元 (28x28 像素的图像)
    • 隐藏层 (Hidden Layer): 128 个神经元 (使用 ReLU 激活函数)
    • 输出层 (Output Layer): 10 个神经元 (分别代表 0-9 十个数字,使用 Softmax 激活函数)

    图示:

    1
    2
    3
    4
    5
    6
    [输入层]      [隐藏层]       [输出层]
    (784 个) (128 个) (10 个)
    O O O ... O O O O ... O O O O ... O
    O O O ... O O O O ... O O O O ... O
    ... ... ...
    O O O ... O O O O ... O O O O ... O

    PyTorch 代码片段:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    import torch
    import torch.nn as nn

    # 定义多层感知器 (MLP)
    class MLP(nn.Module):
    def __init__(self):
    super().__init__()
    self.flatten = nn.Flatten() # 将 28x28 的图像展平为 784 维向量
    self.linear1 = nn.Linear(784, 128) # 输入层到隐藏层
    self.relu = nn.ReLU() # ReLU 激活函数
    self.linear2 = nn.Linear(128, 10) # 隐藏层到输出层
    self.softmax = nn.Softmax(dim=1) # Softmax 激活函数 (dim=1 表示对每一行进行 Softmax)

    def forward(self, x):
    x = self.flatten(x)
    x = self.relu(self.linear1(x))
    x = self.softmax(self.linear2(x))
    return x

    # 创建 MLP 实例
    mlp = MLP()

    # 模拟输入数据 (一张 28x28 的手写数字图像)
    input_data = torch.randn(1, 28, 28) # (batch_size, height, width)

    # 计算输出
    output = mlp(input_data)
    print(f"MLP 输出: {output}") # 输出一个 1x10 的概率分布

一级知识点 2:前向传播 ➡️

二级知识点 2.1:前向传播过程 🚶

  • 范例: 沿用上面的 MLP 识别手写数字的例子,我们来看前向传播的具体过程:

    1. 输入图像: 将一张 28x28 的手写数字图像 (例如数字 “3”) 输入到 MLP 的输入层。图像的每个像素值 (0-255) 对应输入层的一个神经元。
    2. 展平: 将 28x28 的二维图像展平为 784 维的一维向量。
    3. 输入层到隐藏层:
      • 对 784 维的输入向量进行加权求和:每个输入值乘以对应的权重,然后加上偏置。
      • 应用 ReLU 激活函数:将加权和的结果输入到 ReLU 函数中,得到隐藏层的输出。
    4. 隐藏层到输出层:
      • 对 128 维的隐藏层输出进行加权求和:每个隐藏层输出值乘以对应的权重,然后加上偏置。
      • 应用 Softmax 激活函数:将加权和的结果输入到 Softmax 函数中,得到一个 10 维的概率分布向量。向量的每个元素表示对应数字的概率。
    5. 输出结果: 输出层产生一个 10 维的概率分布向量,其中概率最高的元素对应的数字就是 MLP 的预测结果。例如,如果输出向量中第 4 个元素 (索引为 3) 的概率最高,则 MLP 预测这张图片是数字 “3”。

二级知识点 2.2:计算图 📊
前向传播流程图与神经网络结构图基本一致.

反向传播 (Backpropagation)

一级知识点 3:反向传播 (Backpropagation) 🔄

如果说前向传播是神经网络的“思考”过程,那么反向传播就是神经网络的“反思”和“学习”过程。🤔

  • 二级知识点 3.1:反向传播的目的:计算梯度,更新权重 🎯

    • 计算梯度 (Calculate Gradients): 反向传播的核心目的是计算损失函数相对于神经网络中每个权重和偏置的梯度。梯度表示了损失函数的变化趋势,指明了权重和偏置应该如何调整才能减少损失。

      • 你可以把梯度想象成一个“指南针”🧭,它指引着权重和偏置朝着“损失最小化”的方向前进。
    • 更新权重 (Update Weights): 一旦计算出梯度,就可以使用优化算法(例如梯度下降)来更新神经网络中的权重和偏置。通过不断地调整权重和偏置,神经网络的预测结果会越来越准确。

      • 这个过程就像是“雕刻家”👨‍🎨 不断地调整“雕塑”的细节,使其越来越接近理想的形状。
  • 二级知识点 3.2:链式法则 (Chain Rule) 🔗

    反向传播算法的核心是微积分中的链式法则。链式法则用于计算复合函数的导数。

    • 简单例子: 假设你有两个函数:y = f(u)u = g(x),那么 y 关于 x 的导数可以通过链式法则计算: dy/dx = (dy/du) * (du/dx)

    • 在神经网络中: 神经网络可以看作是一个非常复杂的复合函数。每一层都可以看作是一个函数,将前一层的输出作为输入,产生当前层的输出。反向传播利用链式法则,从输出层开始,逐层计算损失函数相对于每个权重和偏置的梯度。

  • 二级知识点 3.3:反向传播过程 ⏪

    1. 前向传播: 首先,进行一次前向传播,计算出网络的输出和损失。

    2. 计算输出层梯度: 计算损失函数相对于输出层神经元输出的梯度。

    3. 反向传播梯度: 从输出层开始,逐层向前计算梯度。

      • 使用链式法则,计算损失函数相对于每一层权重和偏置的梯度。

      • 将梯度传递到前一层。

    4. 更新权重和偏置: 使用优化算法(例如梯度下降),根据计算出的梯度更新权重和偏置。

    形象比喻: 想象你正在玩一个“猜数字”游戏。🤖

    1. 前向传播: 你猜一个数字 (输入),然后朋友告诉你猜的数字是大了还是小了 (输出/损失)。

    2. 反向传播: 你根据朋友的反馈 (损失),反思自己猜数字的策略 (权重),并调整自己的策略 (更新权重),以便下次猜得更准。

  • 二级知识点 3.4:反向传播的类型

    • 三级知识点 3.4.1:静态反向传播 (Static Backpropagation) 处理每一次输入都是独立的,没有前后关联的情况。

    • 三级知识点 3.4.2:循环反向传播 (Recurrent Backpropagation) 用于处理序列数据,如文本或时间序列,其中当前输入与之前的输入有关联。

  • 二级知识点3.5: BP神经网络 使用反向传播算法的神经网络。

反向传播是神经网络学习的核心,它使得神经网络能够从错误中学习,不断提高自己的性能。💪

现在,你已经了解了反向传播的基本原理和过程。请思考:

  • 你能用自己的话解释反向传播的目的和过程吗?

  • 链式法则在反向传播中起到了什么作用?

范例

好的,我们来为反向传播 (Backpropagation) 这一关键概念提供更具体的范例,并结合 PyTorch 代码进行说明,让它更易于理解。

一级知识点 3:反向传播 (Backpropagation) 🔄

二级知识点 3.1:反向传播的目的:计算梯度,更新权重 🎯

  • 范例: 假设我们有一个非常简单的神经网络,只有一个输入 x、一个权重 w、一个偏置 b 和一个输出 y。我们的目标是预测 y_true

    • 模型: y = w * x + b
    • 损失函数 (Loss Function): 均方误差 (Mean Squared Error, MSE):L = (y - y_true)^2
    • 目标: 找到合适的 wb,使得损失函数 L 最小化。

    计算梯度 (Calculate Gradients):

    1. ∂L/∂y: 损失函数 L 相对于模型输出 y 的梯度。
      ∂L/∂y = 2 * (y - y_true)
    2. ∂y/∂w: 模型输出 y 相对于权重 w 的梯度。
      ∂y/∂w = x
    3. ∂y/∂b: 模型输出 y 相对于偏置 b 的梯度。
      ∂y/∂b = 1
    4. ∂L/∂w: 损失函数 L 相对于权重 w 的梯度 (使用链式法则)。
      ∂L/∂w = (∂L/∂y) * (∂y/∂w) = 2 * (y - y_true) * x
    5. ∂L/∂b: 损失函数 L 相对于偏置 b 的梯度 (使用链式法则)。
      ∂L/∂b = (∂L/∂y) * (∂y/∂b) = 2 * (y - y_true) * 1 = 2 * (y - y_true)

    更新权重 (Update Weights):

    • 学习率 (Learning Rate): lr (例如:0.1)
    • 权重更新: w = w - lr * (∂L/∂w)
    • 偏置更新: b = b - lr * (∂L/∂b)

    PyTorch 代码片段:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    import torch

    # 输入、权重、偏置、真实值
    x = torch.tensor(2.0, requires_grad=True) # requires_grad=True 表示需要计算梯度
    w = torch.tensor(1.0, requires_grad=True)
    b = torch.tensor(0.5, requires_grad=True)
    y_true = torch.tensor(5.0)

    # 前向传播
    y = w * x + b

    # 计算损失
    loss = (y - y_true)**2

    # 反向传播 (自动计算梯度)
    loss.backward()

    # 打印梯度
    print(f"∂L/∂w: {w.grad}") # ∂L/∂w: -8.0
    print(f"∂L/∂b: {b.grad}") # ∂L/∂b: -4.0

    # 更新权重和偏置 (手动更新,实际应用中通常使用优化器)
    lr = 0.1
    with torch.no_grad(): # 在更新权重和偏置时,不需要计算梯度
    w -= lr * w.grad
    b -= lr * b.grad

    # 清空梯度 (重要!否则梯度会累积)
    w.grad.zero_()
    b.grad.zero_()

    print(f"更新后的权重 w: {w.item()}") # 更新后的权重 w: 1.8
    print(f"更新后的偏置 b: {b.item()}") # 更新后的偏置 b: 0.9

二级知识点 3.2:链式法则 (Chain Rule) 🔗

  • 范例: 假设我们有一个稍微复杂一点的神经网络:

    • 第一层: y1 = w1 * x + b1
    • 第二层: y2 = w2 * y1 + b2
    • 损失函数: L = (y2 - y_true)^2

    我们要计算损失函数 L 相对于 w1 的梯度 ∂L/∂w1

    链式法则分解:

    1. ∂L/∂y2: 损失函数 L 相对于第二层输出 y2 的梯度。
      ∂L/∂y2 = 2 * (y2 - y_true)
    2. ∂y2/∂y1: 第二层输出 y2 相对于第一层输出 y1 的梯度。
      ∂y2/∂y1 = w2
    3. ∂y1/∂w1: 第一层输出 y1 相对于权重 w1 的梯度。
      ∂y1/∂w1 = x
    4. ∂L/∂w1: 损失函数 L 相对于权重 w1 的梯度 (使用链式法则)。
      ∂L/∂w1 = (∂L/∂y2) * (∂y2/∂y1) * (∂y1/∂w1) = 2 * (y2 - y_true) * w2 * x

    PyTorch 代码片段 (演示自动微分):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import torch

    # 输入、权重、偏置、真实值
    x = torch.tensor(2.0, requires_grad=True)
    w1 = torch.tensor(1.0, requires_grad=True)
    b1 = torch.tensor(0.5, requires_grad=True)
    w2 = torch.tensor(2.0, requires_grad=True)
    b2 = torch.tensor(1.0, requires_grad=True)
    y_true = torch.tensor(8.0)

    # 前向传播
    y1 = w1 * x + b1
    y2 = w2 * y1 + b2

    # 计算损失
    loss = (y2 - y_true)**2

    # 反向传播 (自动计算梯度)
    loss.backward()

    # 打印梯度
    print(f"∂L/∂w1: {w1.grad}") # ∂L/∂w1: -24.0

    # (省略权重和偏置的更新步骤,与前面的例子类似)

二级知识点 3.3:反向传播过程 ⏪

  • 结合 MLP 的范例:

    回顾一下我们之前用于识别手写数字的 MLP(输入层 784 个神经元,隐藏层 128 个神经元,输出层 10 个神经元)。

    反向传播过程:

    1. 前向传播: 输入一张手写数字图像,经过 MLP 的前向传播,得到输出层的预测概率分布。
    2. 计算损失: 使用交叉熵损失函数 (Cross-Entropy Loss) 计算预测概率分布与真实标签 (one-hot 编码) 之间的差异。
    3. 计算输出层梯度: 计算损失函数相对于输出层神经元输出的梯度。
    4. 反向传播梯度 (隐藏层到输出层):
      • 计算损失函数相对于输出层权重和偏置的梯度 (使用链式法则)。
      • 计算损失函数相对于隐藏层输出的梯度 (使用链式法则)。
    5. 反向传播梯度 (输入层到隐藏层):
      • 计算损失函数相对于隐藏层权重和偏置的梯度 (使用链式法则)。
      • 计算损失函数相对于输入层输出的梯度 (使用链式法则,但通常不需要更新输入层的权重)。
    6. 更新权重和偏置: 使用优化算法 (例如 Adam),根据计算出的梯度更新所有层的权重和偏置。

优化算法 (Optimization Algorithms)

OK!准备好迎接优化算法了吗?它们可是反向传播的“神助攻”!💪

一级知识点 4:优化算法 (Optimization Algorithms) ⚙️

优化算法在神经网络训练中扮演着至关重要的角色。它们利用反向传播计算出的梯度,来更新网络的权重和偏置,目标是找到使损失函数最小化的参数值。

  • 二级知识点 4.1:梯度下降 (Gradient Descent) ⛰️

    梯度下降是最基本、最常用的优化算法。它就像一个“探险家”🚶,沿着梯度的反方向(最陡峭的下坡方向)前进,一步一步地寻找“山谷”(损失函数的最小值)。

    • 核心思想:

      • 计算损失函数相对于每个参数(权重和偏置)的梯度。

      • 沿着梯度的反方向更新参数:参数 = 参数 - 学习率 * 梯度

      • 学习率 (Learning Rate) 控制着每次更新的步长。学习率太大可能会“跨过”最小值,太小则可能导致收敛速度过慢。

    • 三级知识点 4.1.1:批量梯度下降 (Batch Gradient Descent) 🐢

      • 每次迭代使用整个训练数据集来计算梯度和更新参数。

      • 优点: 稳定,能够保证收敛到局部最小值(对于凸函数,可以收敛到全局最小值)。

      • 缺点: 速度慢,特别是对于大型数据集,计算量巨大。

    • 三级知识点 4.1.2:小批量梯度下降 (Mini-batch Gradient Descent) 🐇

      • 每次迭代使用训练数据集的一个子集(称为“批次” batch)来计算梯度和更新参数。

      • 优点: 速度比批量梯度下降快,计算量较小,同时又能保持一定的稳定性。

      • 缺点: 可能会在最小值附近震荡。

    • 三级知识点 4.1.3:随机梯度下降 (Stochastic Gradient Descent) 🚀

      • 每次迭代只使用一个训练样本来计算梯度和更新参数。

      • 优点: 速度非常快,适合于大型数据集和在线学习。

      • 缺点: 非常不稳定,可能会在最小值附近剧烈震荡,甚至无法收敛。

  • 二级知识点 4.2:其他优化器 (Optimizers) ✨

    除了梯度下降,还有许多更高级、更有效的优化算法。它们通常在梯度下降的基础上进行改进,以解决梯度下降的局限性,例如收敛速度慢、容易陷入局部最小值等问题。

    • 三级知识点 4.2.1:Adam (Adaptive Moment Estimation) 🤖

      • Adam 是一种自适应学习率优化算法,它结合了动量法 (Momentum) 和 RMSprop 的思想。

      • 优点: 收敛速度快,对超参数的设置不敏感,通常表现良好。

      • 原理 (简要):

        • 动量 (Momentum): 考虑了之前梯度的累积效应,有助于加速收敛并减少震荡。

        • RMSprop: 对每个参数使用不同的学习率,根据参数梯度的历史平方均值进行调整,有助于处理稀疏梯度和非平稳目标函数。

    • 三级知识点 4.2.2:其他 (AdaGrad, AdaDelta, Nesterov Accelerated Gradient, Sophia, etc.) 🤓

      • AdaGrad: 自适应学习率算法,对不频繁更新的参数使用更大的学习率,对频繁更新的参数使用更小的学习率。

      • AdaDelta: AdaGrad 的改进版,解决了 AdaGrad 学习率单调递减的问题。

      • Nesterov Accelerated Gradient (NAG): 在动量法的基础上进行改进,通过在计算梯度时“向前看一步”,提高了收敛速度。

      • Sophia: 一种新的二阶优化算法, 在一些情况下能获得比Adam更好的结果.

优化算法的选择对于神经网络的训练至关重要。选择合适的优化算法可以加速训练过程,提高模型性能。

现在,你已经了解了优化算法的基本概念和常见类型。请思考:

  • 你能用自己的话解释梯度下降算法的原理吗?

  • 不同的梯度下降变体(批量、小批量、随机)有什么区别?

  • Adam 优化器有哪些优点?

范例

好的,我们来为优化算法 (Optimization Algorithms) 提供更具体的范例,并结合 PyTorch 代码进行说明,让它们更加生动易懂。

一级知识点 4:优化算法 (Optimization Algorithms) ⚙️

二级知识点 4.1:梯度下降 (Gradient Descent) ⛰️

  • 范例: 假设我们要使用梯度下降来解决一个简单的线性回归问题。

    • 模型: y = w * x + b
    • 数据集: 假设我们有一些 (x, y) 数据点,例如:
      • (x=1, y=2)
      • (x=2, y=4)
      • (x=3, y=5)
      • (x=4, y=4)
      • (x=5, y=5)
    • 损失函数: 均方误差 (MSE):L = (1/n) * Σ(y_pred - y_true)^2 (n 是样本数量)
    • 目标: 找到合适的 wb,使得损失函数 L 最小化。

    批量梯度下降 (Batch Gradient Descent):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    import torch
    import numpy as np

    # 数据集
    x = torch.tensor([1, 2, 3, 4, 5], dtype=torch.float32)
    y = torch.tensor([2, 4, 5, 4, 5], dtype=torch.float32)

    # 初始化权重和偏置
    w = torch.tensor(0.0, requires_grad=True)
    b = torch.tensor(0.0, requires_grad=True)

    # 学习率
    lr = 0.01

    # 迭代次数
    epochs = 100

    for epoch in range(epochs):
    # 前向传播
    y_pred = w * x + b

    # 计算损失
    loss = torch.mean((y_pred - y)**2)

    # 反向传播 (自动计算梯度)
    loss.backward()

    # 更新权重和偏置 (使用梯度下降)
    with torch.no_grad():
    w -= lr * w.grad
    b -= lr * b.grad

    # 清空梯度 (重要!)
    w.grad.zero_()
    b.grad.zero_()

    if (epoch + 1) % 10 == 0:
    print(f"Epoch {epoch+1}/{epochs}, Loss: {loss.item():.4f}, w: {w.item():.4f}, b: {b.item():.4f}")

    # 最终的 w 和 b
    print(f"最终的 w: {w.item():.4f}, 最终的 b: {b.item():.4f}")

    小批量梯度下降 (Mini-batch Gradient Descent):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    import torch
    import numpy as np

    # 数据集
    x = torch.tensor([1, 2, 3, 4, 5], dtype=torch.float32)
    y = torch.tensor([2, 4, 5, 4, 5], dtype=torch.float32)

    # 初始化权重和偏置
    w = torch.tensor(0.0, requires_grad=True)
    b = torch.tensor(0.0, requires_grad=True)

    # 学习率
    lr = 0.01

    # 批次大小
    batch_size = 2

    # 迭代次数
    epochs = 100

    for epoch in range(epochs):
    # 随机打乱数据
    indices = np.arange(len(x))
    np.random.shuffle(indices)
    x_shuffled = x[indices]
    y_shuffled = y[indices]

    for i in range(0, len(x), batch_size):
    # 获取当前批次的数据
    x_batch = x_shuffled[i:i+batch_size]
    y_batch = y_shuffled[i:i+batch_size]

    # 前向传播
    y_pred = w * x_batch + b

    # 计算损失
    loss = torch.mean((y_pred - y_batch)**2)

    # 反向传播
    loss.backward()

    # 更新权重和偏置
    with torch.no_grad():
    w -= lr * w.grad
    b -= lr * b.grad

    # 清空梯度
    w.grad.zero_()
    b.grad.zero_()

    if (epoch + 1) % 10 == 0:
    print(f"Epoch {epoch+1}/{epochs}, Loss: {loss.item():.4f}, w: {w.item():.4f}, b: {b.item():.4f}")

    # 最终的 w 和 b
    print(f"最终的 w: {w.item():.4f}, 最终的 b: {b.item():.4f}")

    随机梯度下降 (Stochastic Gradient Descent):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
       import torch
    import numpy as np

    # 数据集
    x = torch.tensor([1, 2, 3, 4, 5], dtype=torch.float32)
    y = torch.tensor([2, 4, 5, 4, 5], dtype=torch.float32)

    # 初始化权重和偏置
    w = torch.tensor(0.0, requires_grad=True)
    b = torch.tensor(0.0, requires_grad=True)

    # 学习率
    lr = 0.01

    # 迭代次数 (每个样本都迭代一次)
    epochs = 100

    for epoch in range(epochs):
    for i in range(len(x)):
    # 获取当前样本
    x_sample = x[i]
    y_sample = y[i]
    #前向传播
    y_pred = w* x_sample + b

    #计算损失
    loss = (y_pred - y_sample)**2

    #反向传播
    loss.backward()

    #更新权重和偏置
    with torch.no_grad():
    w -= lr* w.grad
    b -= lr* b.grad
    #清空梯度
    w.grad.zero_()
    b.grad.zero_()

    if (epoch + 1) % 10 == 0:
    print(f"Epoch {epoch+1}/{epochs}, w: {w.item():.4f}, b: {b.item():.4f}")

    print(f"最终的 w: {w.item():.4f}, 最终的 b: {b.item():.4f}")

二级知识点 4.2:其他优化器 (Optimizers) ✨

  • 范例: 使用 PyTorch 内置的优化器 (Adam)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    import torch
    import torch.nn as nn
    import torch.optim as optim

    # 数据集 (同上)
    x = torch.tensor([1, 2, 3, 4, 5], dtype=torch.float32)
    y = torch.tensor([2, 4, 5, 4, 5], dtype=torch.float32)

    # 定义一个简单的线性模型
    class LinearRegression(nn.Module):
    def __init__(self):
    super().__init__()
    self.linear = nn.Linear(1, 1) # 输入维度为 1,输出维度为 1

    def forward(self, x):
    return self.linear(x)

    # 创建模型实例
    model = LinearRegression()

    # 定义优化器 (Adam)
    optimizer = optim.Adam(model.parameters(), lr=0.01)

    # 迭代次数
    epochs = 100

    for epoch in range(epochs):
    # 前向传播
    y_pred = model(x.unsqueeze(1)) # unsqueeze(1) 将 x 从 [5] 变为 [5, 1]

    # 计算损失
    loss = torch.mean((y_pred.squeeze() - y)**2) # squeeze() 将 y_pred 从 [5, 1] 变为 [5]

    # 反向传播
    loss.backward()

    # 更新参数 (使用优化器)
    optimizer.step()

    # 清空梯度
    optimizer.zero_grad()

    if (epoch + 1) % 10 == 0:
    print(f"Epoch {epoch+1}/{epochs}, Loss: {loss.item():.4f}, w: {model.linear.weight.item():.4f}, b: {model.linear.bias.item():.4f}")

    # 最终的 w 和 b
    print(f"最终的 w: {model.linear.weight.item():.4f}, 最终的 b: {model.linear.bias.item():.4f}")

通过这些范例和代码,应该对梯度下降的不同变体以及如何使用 PyTorch 内置的优化器有了更深入的理解。请注意,实际应用中,我们通常会使用 PyTorch 提供的 nn.Modulenn.Linearoptim 等模块来构建模型和优化器,而不需要手动实现梯度下降的细节。

在下一阶段,我们将学习损失函数, 这是反向传播的”指挥官”!👨‍✈️

损失函数

好的!让我们进入下一个关键环节:损失函数!🎯

一级知识点 5:损失函数 (Loss Functions) 📉

损失函数是神经网络训练的“指挥官”,它告诉神经网络当前的表现如何,以及距离“完美”还有多远。

  • 二级知识点 5.1:损失函数的定义 📝

    • 损失函数是一个衡量模型预测输出与真实标签之间差异的函数。

    • 损失函数的值越小,表示模型的预测结果越接近真实标签,模型的性能越好。

    • 损失函数的选择取决于具体的任务类型(例如分类、回归等)。

  • 二级知识点 5.2:不同任务的损失函数 ➗➖➕

    • 三级知识点 5.2.1:分类任务 (Classification) 🐱🐶

      • 交叉熵损失 (Cross-Entropy Loss): 用于多分类问题,衡量预测概率分布与真实标签分布之间的差异。

        • 公式 (二分类): - (y_true * log(y_pred) + (1 - y_true) * log(1 - y_pred))

          • y_true: 真实标签 (0 或 1)

          • y_pred: 预测概率 (0-1 之间)

        • 公式 (多分类): - Σ(y_true_i * log(y_pred_i))

          • y_true_i: 真实标签的 one-hot 编码的第 i 个元素

          • y_pred_i: 预测概率分布的第 i 个元素

        • PyTorch: torch.nn.CrossEntropyLoss() (多分类,内部会自动计算 Softmax) 或 torch.nn.BCELoss() (二分类,需要手动计算 Sigmoid)

      • Hinge Loss: 常用于支持向量机 (SVM),目标是最大化分类边界。

        • PyTorch: torch.nn.HingeEmbeddingLoss()
    • 三级知识点 5.2.2:回归任务 (Regression) 🏠📈

      • 均方误差 (Mean Squared Error, MSE): 计算预测值与真实值之间差的平方的平均值。

        • 公式: (1/n) * Σ(y_pred - y_true)^2

        • PyTorch: torch.nn.MSELoss()

      • 平均绝对误差 (Mean Absolute Error, MAE): 计算预测值与真实值之间差的绝对值的平均值。

        • 公式: (1/n) * Σ|y_pred - y_true|

        • PyTorch: torch.nn.L1Loss()

      • Huber Loss: MSE 和 MAE 的结合,对异常值更鲁棒。

        • PyTorch: torch.nn.SmoothL1Loss()

形象比喻:

  • 分类任务: 想象你在玩一个“猜动物”的游戏。

    • 交叉熵损失: 就像猜错的“惩罚”,猜得越离谱,“惩罚”越大。
  • 回归任务: 想象你在玩一个“扔飞镖”的游戏。

    • MSE: 就像计算所有飞镖偏离靶心的距离的平方的平均值。

    • MAE: 就像计算所有飞镖偏离靶心的距离的平均值。

损失函数为神经网络提供了学习的目标,反向传播算法利用损失函数的梯度来更新网络参数,优化算法则负责具体的更新过程。

现在,你已经了解了损失函数的作用和常见类型。请思考:

  • 你能用自己的话解释损失函数的作用吗?

  • 对于分类任务和回归任务,分别应该选择什么样的损失函数?

范例

好的,让我们通过一些具体的例子来加深对损失函数的理解,并结合 PyTorch 代码进行演示。

一级知识点 5:损失函数 (Loss Functions) 📉

二级知识点 5.1:损失函数的定义 📝

  • 范例: 假设我们正在训练一个模型来预测房价。

    • 模型输出: 模型的预测房价 (例如:350,000 美元)
    • 真实标签: 房屋的实际价格 (例如:380,000 美元)
    • 损失函数: 我们可以使用均方误差 (MSE) 来衡量模型预测的准确性。
      • 计算: (350,000 - 380,000)^2 = 900,000,000 (这里为了简化,我们只计算了一个样本的损失)

    这个例子中,损失函数的值 (900,000,000) 越大,表示模型的预测结果与真实价格之间的差距越大,模型的性能越差。

二级知识点 5.2:不同任务的损失函数 ➗➖➕

  • 三级知识点 5.2.1:分类任务 (Classification) 🐱🐶

    • 范例: 我们要构建一个图像分类器,将图像分为猫或狗两类。

      • 模型输出: 模型输出一个包含两个元素的向量,分别表示图像是猫和狗的概率。例如:[0.8, 0.2] (表示模型认为这张图片是猫的概率为 80%,是狗的概率为 20%)
      • 真实标签:
        • 如果图片是猫,则真实标签为 [1, 0] (one-hot 编码)
        • 如果图片是狗,则真实标签为 [0, 1] (one-hot 编码)
      • 损失函数: 我们可以使用交叉熵损失 (Cross-Entropy Loss)。

      PyTorch 代码 (二分类):

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      import torch
      import torch.nn as nn

      # 模型输出 (假设模型已经输出了预测概率)
      y_pred = torch.tensor([0.8, 0.2]) # 预测为猫的概率为 0.8,狗的概率为 0.2
      y_pred = torch.sigmoid(y_pred) # 需要手动计算 Sigmoid

      # 真实标签 (假设这张图片是猫)
      y_true = torch.tensor([1.0, 0.0]) # one-hot 编码

      # 计算二元交叉熵损失 (Binary Cross-Entropy Loss)
      bce_loss = nn.BCELoss()
      loss = bce_loss(y_pred, y_true)

      print(f"二元交叉熵损失: {loss.item():.4f}")

      PyTorch 代码 (多分类):

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      import torch
      import torch.nn as nn

      # 模型输出 (假设模型已经输出了预测概率)
      y_pred = torch.tensor([[0.8, 0.2], [0.3, 0.7]]) # 两张图片,第一张预测为猫的概率高,第二张预测为狗的概率高
      #y_pred = torch.softmax(y_pred, dim=1) # nn.CrossEntropyLoss 已经包含了 softmax,所以这里不需要

      # 真实标签 (假设第一张图片是猫,第二张图片是狗)
      y_true = torch.tensor([0, 1]) # 类别索引

      # 计算交叉熵损失 (Cross-Entropy Loss)
      ce_loss = nn.CrossEntropyLoss()
      loss = ce_loss(y_pred, y_true)

      print(f"交叉熵损失: {loss.item():.4f}")
  • 三级知识点 5.2.2:回归任务 (Regression) 🏠📈

    • 范例: 我们要构建一个模型来预测房价 (与之前的例子相同)。

      • 模型输出: 模型预测的房价 (例如:350,000 美元)
      • 真实标签: 房屋的实际价格 (例如:380,000 美元)
      • 损失函数:
        • 均方误差 (MSE): (350,000 - 380,000)^2 = 900,000,000
        • 平均绝对误差 (MAE): |350,000 - 380,000| = 30,000

      PyTorch 代码 (MSE):

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      import torch
      import torch.nn as nn

      # 模型输出 (假设模型已经输出了预测值)
      y_pred = torch.tensor([350000.0])

      # 真实标签
      y_true = torch.tensor([380000.0])

      # 计算均方误差 (MSE)
      mse_loss = nn.MSELoss()
      loss = mse_loss(y_pred, y_true)

      print(f"均方误差: {loss.item():.4f}")

      PyTorch 代码 (MAE):

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      import torch
      import torch.nn as nn

      # 模型输出 (假设模型已经输出了预测值)
      y_pred = torch.tensor([350000.0])

      # 真实标签
      y_true = torch.tensor([380000.0])

      # 计算平均绝对误差 (MAE)
      mae_loss = nn.L1Loss()
      loss = mae_loss(y_pred, y_true)

      print(f"平均绝对误差: {loss.item():.4f}")

通过这些范例和代码,你可以看到不同类型的损失函数是如何计算的,以及如何在 PyTorch 中使用它们。在实际应用中,你需要根据你的任务类型选择合适的损失函数。
在下一阶段,我们将学习反向传播的一些实际应用!🌟

实际应用(一小部分)

太好了!接下来,让我们一起探索反向传播在现实世界中的应用,看看它如何大显身手!🌟

一级知识点 6:反向传播的应用 🌍

反向传播算法不仅仅是理论上的概念,它已经在许多领域取得了显著的成果。

  • 二级知识点 6.1:人脸识别 (Face Recognition) 👤📸

    • 原理: 卷积神经网络 (CNN) 结合反向传播算法,可以学习人脸图像的特征,从而实现人脸识别。

    • 过程:

      1. 数据收集: 收集大量人脸图像,并标注每个图像对应的人物身份。

      2. 构建 CNN 模型: 设计一个 CNN 模型,通常包含多个卷积层、池化层和全连接层。

      3. 前向传播: 将人脸图像输入到 CNN 模型中,进行前向传播,得到模型的预测输出(例如,属于每个人物身份的概率)。

      4. 计算损失: 使用交叉熵损失函数等,计算模型预测输出与真实标签之间的差异。

      5. 反向传播: 使用反向传播算法,计算损失函数相对于模型中每个权重和偏置的梯度。

      6. 更新参数: 使用优化算法(例如 Adam),根据计算出的梯度更新模型的权重和偏置。

      7. 重复步骤 3-6: 不断迭代,直到模型收敛或达到预定的训练轮数。

    • 应用: 手机解锁、门禁系统、安防监控等。

  • 二级知识点 6.2:自然语言处理:语音识别 (NLP: Speech Recognition) 🗣️📱

    • 原理: 循环神经网络 (RNN) 或 Transformer 模型结合反向传播算法,可以将语音信号转换为文本。

    • 过程:

      1. 数据收集: 收集大量语音数据,并标注每个语音片段对应的文本内容。

      2. 构建模型: 设计一个 RNN 或 Transformer 模型。

      3. 特征提取: 将语音信号转换为声学特征(例如梅尔频率倒谱系数 MFCC)。

      4. 前向传播: 将声学特征输入到模型中,进行前向传播,得到模型的预测输出(例如,每个时间步的文本预测概率)。

      5. 计算损失: 使用交叉熵损失函数等,计算模型预测输出与真实标签之间的差异。

      6. 反向传播: 使用反向传播算法(例如 BPTT,随时间反向传播),计算损失函数相对于模型中每个权重和偏置的梯度。

      7. 更新参数: 使用优化算法(例如 Adam),根据计算出的梯度更新模型的权重和偏置。

      8. 重复步骤 4-7: 不断迭代,直到模型收敛或达到预定的训练轮数。

    • 应用: 语音助手(如 Siri、Alexa)、语音输入法、实时字幕等。

  • 二级知识点 6.3: 事故预防

    • 原理: 利用反向传播算法训练的模型能够预测潜在的危险情况,从而采取预防措施,减少事故发生的可能性。

    • 应用: 通过分析数据来预测矿井事故, 提前采取措施。

反向传播的应用远不止这些,它还广泛应用于图像生成、机器翻译、推荐系统、自动驾驶、医疗诊断、金融预测等众多领域。随着深度学习技术的不断发展,反向传播算法将在更多领域发挥重要作用。

现在,你已经了解了反向传播的一些实际应用。请思考:

  • 你能举出其他一些反向传播的应用例子吗?

  • 你认为反向传播算法在未来还可能有哪些应用?

范例

好的,让我们通过一些具体的例子和代码片段,来展示反向传播在实际应用中的强大功能。

一级知识点 6:反向传播的应用 🌍

二级知识点 6.1:人脸识别 (Face Recognition) 👤📸

  • 范例: 使用 PyTorch 和预训练的 CNN 模型 (例如 FaceNet) 进行人脸识别。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    import torch
    import torchvision
    from torchvision import transforms
    from PIL import Image

    # 加载预训练的 FaceNet 模型 (这里使用 torchvision 提供的 resnet18 作为示例)
    # 实际应用中,你可能需要下载并加载更专业的 FaceNet 模型
    model = torchvision.models.resnet18(pretrained=True)
    model.fc = torch.nn.Linear(512, 128) # 修改最后一层,输出 128 维的特征向量
    model.eval() # 设置为评估模式

    # 定义图像预处理步骤
    transform = transforms.Compose([
    transforms.Resize((160, 160)), # 调整图像大小
    transforms.ToTensor(), # 转换为 Tensor
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 标准化
    ])

    # 加载两张人脸图像 (例如,image1.jpg 和 image2.jpg)
    image1 = Image.open("image1.jpg")
    image2 = Image.open("image2.jpg")

    # 对图像进行预处理
    image1_tensor = transform(image1)
    image2_tensor = transform(image2)

    # 将图像输入到模型中,获取特征向量
    with torch.no_grad():
    feature_vector1 = model(image1_tensor.unsqueeze(0)) # unsqueeze(0) 增加批次维度
    feature_vector2 = model(image2_tensor.unsqueeze(0))

    # 计算两个特征向量之间的距离 (例如,欧氏距离)
    distance = torch.norm(feature_vector1 - feature_vector2)

    # 设置一个阈值,判断两张图片是否属于同一个人
    threshold = 1.0 # 这个阈值需要根据实际情况调整

    if distance < threshold:
    print("这两张图片可能是同一个人。")
    else:
    print("这两张图片可能不是同一个人。")

    print(f"两张图片特征向量之间的距离: {distance.item():.4f}")

    代码解释:

    1. 加载预训练模型: 我们使用 torchvision 提供的预训练 resnet18 模型作为示例。实际应用中,你可能需要下载并加载更专业的 FaceNet 模型,例如在 VGGFace2 数据集上预训练的模型。
    2. 修改最后一层: 我们将 resnet18 模型的最后一层 (全连接层) 修改为输出 128 维的特征向量。这是 FaceNet 模型常用的特征向量维度。
    3. 图像预处理: 我们定义了一系列图像预处理步骤,包括调整图像大小、转换为 Tensor 和标准化。这些步骤对于提高模型的性能非常重要。
    4. 特征提取: 我们将预处理后的图像输入到模型中,获取 128 维的特征向量。
    5. 距离计算: 我们计算两个特征向量之间的欧氏距离。距离越小,表示两张人脸图像越相似。
    6. 阈值判断: 我们设置一个阈值,如果距离小于阈值,则认为两张图片属于同一个人。

二级知识点 6.2:自然语言处理:语音识别 (NLP: Speech Recognition) 🗣️📱

  • 范例: 使用 PyTorch 和预训练的语音识别模型 (例如 Wav2Vec2) 进行语音识别。由于从头训练一个语音识别模型非常复杂,并且需要大量的计算资源,一般情况下我们都会使用预训练模型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    import torch
    from transformers import Wav2Vec2ForCTC, Wav2Vec2Processor

    # 加载预训练的 Wav2Vec2 模型和处理器
    processor = Wav2Vec2Processor.from_pretrained("facebook/wav2vec2-base-960h")
    model = Wav2Vec2ForCTC.from_pretrained("facebook/wav2vec2-base-960h")
    model.eval() # 设置为评估模式

    # 加载音频文件 (例如,audio.wav,需要是单声道、16kHz 采样率的音频)
    # 这里我们使用 torchaudio 来加载音频,你需要先安装 torchaudio:pip install torchaudio
    import torchaudio
    waveform, sample_rate = torchaudio.load("audio.wav")

    # 如果音频不是 16kHz 采样率,进行重采样
    if sample_rate != 16000:
    waveform = torchaudio.functional.resample(waveform, sample_rate, 16000)

    # 将音频数据转换为模型所需的输入格式
    input_values = processor(waveform, return_tensors="pt", padding="longest").input_values

    # 将音频数据输入到模型中,获取 logits
    with torch.no_grad():
    logits = model(input_values).logits

    # 对 logits 进行解码,获取预测的文本
    predicted_ids = torch.argmax(logits, dim=-1)
    transcription = processor.batch_decode(predicted_ids)[0]

    print(f"语音识别结果: {transcription}")

    代码解释:

    1. 加载预训练模型和处理器: 我们使用 Hugging Face Transformers 库提供的 Wav2Vec2ForCTCWav2Vec2ProcessorWav2Vec2ForCTC 是一个预训练的语音识别模型,Wav2Vec2Processor 用于将音频数据转换为模型所需的输入格式。
    2. 加载音频文件: 我们使用 torchaudio 库加载音频文件。你需要确保音频文件是单声道、16kHz 采样率的。如果不是,需要进行重采样。
    3. 数据预处理: 我们使用 processor 将音频数据转换为模型所需的输入格式。
    4. 模型预测: 我们将预处理后的音频数据输入到模型中,获取 logitslogits 是模型输出的原始值,还没有经过 Softmax 转换。
    5. 解码: 我们使用 torch.argmaxlogits 进行解码,获取预测的文本 ID。然后,我们使用 processor.batch_decode 将文本 ID 转换为文本。

这些范例展示了反向传播在人脸识别和语音识别中的应用。请注意,这些只是简化的示例,实际应用中可能需要更复杂的模型、更多的数据预处理步骤以及更精细的调参。

反向传播📣的历史

说到这里,那就顺带看看,一起回顾反向传播算法的发展历程,了解这段精彩的历史!📜

一级知识点 7:反向传播的历史 🕰️

反向传播算法并非一蹴而就,它的发展历经了数十年的时间,凝聚了许多研究者的智慧和努力。

  • 19 世纪:梯度下降法的雏形 📉

    • Baron Augustin-Louis Cauchy (法国数学家): 提出了梯度下降法 (Gradient Descent) 的思想,用于解决复杂的数学问题。这为反向传播算法奠定了基础。
  • 1970 年:反向传播的早期探索 🌱

    • Seppo Linnainmaa (芬兰硕士生): 提出了一种用于稀疏连接网络的误差反向传播算法。虽然他没有明确提到神经网络,但他的工作为反向传播算法的提出奠定了基础。
  • 20 世纪 80 年代:反向传播的突破 🚀

    • 多位研究者独立研究: 独立开发了时间反向传播 (Backpropagation Through Time, BPTT) 算法,用于训练循环神经网络 (RNN)。

    • 1986 年:David Rumelhart 及其同事: 发表了一篇具有里程碑意义的论文,将 Linnainmaa 的反向传播算法应用于多层神经网络,并证明了其有效性。这篇论文被认为是反向传播算法的正式提出,极大地推动了神经网络的发展。

    • 1989 年:Yann LeCun: 将反向传播算法应用于卷积神经网络 (CNN),用于手写数字识别,取得了显著的成果。

  • 21 世纪:反向传播的广泛应用和发展 🌐

    • 随着深度学习的兴起,反向传播算法成为训练各种类型神经网络的核心算法。

    • 研究者们不断对反向传播算法进行改进和优化,例如:

      • 并行化计算: 利用 GPU 等硬件加速反向传播的计算速度。

      • 优化算法: 提出各种新的优化算法(如 Adam、RMSprop 等),以提高训练效率和模型性能。

      • 梯度消失/爆炸问题: 提出各种解决方案(如 ReLU 激活函数、Batch Normalization、残差连接等),以缓解梯度消失/爆炸问题。

反向传播算法的发展历程是一个不断探索、不断创新的过程。正是由于这些研究者的努力,我们才能够利用深度学习技术解决各种复杂的问题。

现在,你已经了解了反向传播算法的历史。请思考:

  • 反向传播算法的发展历程中,有哪些关键的里程碑事件?

  • 反向传播算法的提出对神经网络的发展产生了什么影响?

我们还剩下最后一个知识点了:超参数!

超参数

好的!让我们来了解神经网络中的“神秘”参数——超参数!🧙‍♂️

一级知识点 8:超参数 (Hyperparameters) ⚙️

超参数是神经网络中非常重要的概念。它们不是通过训练数据学习得到的,而是在训练之前由人工设置的参数。超参数的选择对模型的性能有很大的影响。

  • 二级知识点 8.1:输入图像大小 (Input Image Size) 🖼️

    • 定义: 输入到神经网络的图像的尺寸(宽度和高度)。

    • 影响:

      • 较大的图像通常包含更多的细节信息,可能有助于提高模型的性能,但也会增加计算量和内存消耗。

      • 较小的图像计算量较小,训练速度更快,但可能会丢失一些细节信息。

    • 选择: 需要根据具体的任务和数据集进行权衡。通常需要进行实验来确定最佳的输入图像大小。

    • 示例: 对于 ImageNet 数据集,常用的输入图像大小为 224x224 或 299x299。对于 MNIST 数据集,输入图像大小为 28x28。

  • 二级知识点 8.2:学习率 (Learning Rate) 🏃

    • 定义: 控制权重更新的步长。

    • 影响:

      • 学习率过大可能导致模型在最小值附近震荡,甚至无法收敛。

      • 学习率过小可能导致模型收敛速度过慢,需要更长的训练时间。

    • 选择: 通常需要通过实验来确定最佳的学习率。可以使用学习率衰减策略,在训练过程中逐渐减小学习率。

    • 示例: 常用的学习率范围为 0.1 到 0.0001。

  • 二级知识点 8.3:正则化参数 (Regularization Parameter) 🏋️

    • 定义: 用于控制模型复杂度,防止过拟合。

    • 影响:

      • 正则化参数越大,对模型复杂度的惩罚越大,模型越倾向于选择更简单的模型。

      • 正则化参数越小,对模型复杂度的惩罚越小,模型可能更容易过拟合。

    • 选择: 需要根据具体的任务和数据集进行权衡。通常需要进行实验来确定最佳的正则化参数。

    • 示例: 常用的正则化方法包括 L1 正则化和 L2 正则化。

  • 二级知识点 8.4:神经网络层数 (Number of Layers) 🧱

    • 定义: 神经网络中隐藏层的数量。

    • 影响:

      • 层数越多,模型的表示能力越强,但也越容易过拟合。

      • 层数越少,模型的表示能力越弱,可能无法很好地拟合数据。

    • 选择: 需要根据具体的任务和数据集进行权衡。通常需要进行实验来确定最佳的层数。

    • 示例: 对于简单的任务,可以使用较少的层数(例如 1-2 层)。对于复杂的任务,可能需要使用较多的层数(例如几十层甚至上百层)。

  • 二级知识点 8.5:批处理大小 (Batch Size) 📦

    • 定义: 每次迭代中使用的训练样本数量。

    • 影响:

      • 批处理大小越大,梯度估计越准确,训练越稳定,但内存消耗越大,每次迭代的时间越长。

      • 批处理大小越小,内存消耗越小,每次迭代的时间越短,但梯度估计可能不够准确,训练可能不稳定。

    • 选择: 需要根据具体的硬件条件和数据集大小进行权衡。通常需要进行实验来确定最佳的批处理大小。

    • 示例: 常用的批处理大小范围为 32 到 1024。

  • 二级知识点 8.6:卷积层参数 (Convolutional Layer Parameters) 🧱

    • 卷积核大小 (Kernel Size): 卷积核的尺寸(例如 3x3、5x5)。

    • 步长 (Stride): 卷积核在图像上移动的步长(例如 1、2)。

    • 填充 (Padding): 在图像边缘填充像素的方式(例如 “valid”、“same”)。

    • 卷积核数量 (Number of Filters): 卷积层中卷积核的数量。

  • 二级知识点 8.7:池化层参数 (Pooling Layer Parameters) 🧱

    • 池化核大小 (Kernel Size): 池化核的尺寸(例如 2x2、3x3)。

    • 步长 (Stride): 池化核移动的步长(例如 2、3)。

    • 池化类型: 最大池化, 均值池化等

  • 二级知识点 8.8:迭代周期 (Epochs) 🔁

    • 定义: 将所有训练数据过一遍叫做一个周期。

    • 影响:

      • 周期数越多, 模型训练时间越长, 容易过拟合。

      • 周期数越少, 模型可能无法完全学习到数据中的规律

范例

好的,我们来为超参数提供一些更具体的范例,并结合 PyTorch 代码进行说明。

一级知识点 8:超参数 (Hyperparameters) ⚙️

二级知识点 8.1:输入图像大小 (Input Image Size) 🖼️

  • 范例: 假设我们要训练一个图像分类模型,使用 CIFAR-10 数据集。CIFAR-10 数据集中的图像大小为 32x32 像素。

    • 情况 1: 我们将输入图像大小设置为 32x32(原始大小)。
      • 优点: 不需要对图像进行额外的缩放操作,保留了原始图像的所有信息。
      • 缺点: 如果模型比较复杂,计算量可能会比较大。
    • 情况 2: 我们将输入图像大小设置为 64x64。
      • 优点: 可能会提高模型的性能(如果模型能够学习到更精细的特征)。
      • 缺点: 增加了计算量和内存消耗。需要对原始图像进行上采样操作,可能会引入一些噪声。
    • 情况 3: 我们将输入图像大小设置为 16x16。
      • 优点: 减少了计算量和内存消耗。
      • 缺点: 可能会降低模型的性能(丢失了一些细节信息)。需要对原始图像进行下采样操作,可能会导致信息损失。

    PyTorch 代码 (调整输入图像大小):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import torchvision.transforms as transforms

    # 定义图像预处理步骤
    transform = transforms.Compose([
    transforms.Resize((64, 64)), # 将图像大小调整为 64x64
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
    ])

    # 加载 CIFAR-10 数据集,并应用预处理步骤
    trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
    download=True, transform=transform)

二级知识点 8.2:学习率 (Learning Rate) 🏃

  • 范例: 我们使用 PyTorch 构建一个简单的线性回归模型。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    import torch
    import torch.nn as nn
    import torch.optim as optim

    # ... (省略数据加载和模型定义部分,参考之前的线性回归例子) ...
    # 定义一个简单的线性模型
    class LinearRegression(nn.Module):
    def __init__(self):
    super().__init__()
    self.linear = nn.Linear(1, 1) # 输入维度为 1,输出维度为 1

    def forward(self, x):
    return self.linear(x)

    # 创建模型实例
    model = LinearRegression()
    # 情况 1:学习率设置为 0.1 (较大)
    optimizer = optim.SGD(model.parameters(), lr=0.1)

    # 情况 2:学习率设置为 0.01 (适中)
    # optimizer = optim.SGD(model.parameters(), lr=0.01)

    # 情况 3:学习率设置为 0.001 (较小)
    # optimizer = optim.SGD(model.parameters(), lr=0.001)

    # ... (省略训练循环部分,参考之前的线性回归例子) ...
    • 情况 1 (lr=0.1): 模型可能会在最小值附近震荡,甚至无法收敛。
    • 情况 2 (lr=0.01): 模型可能会比较平稳地收敛到最小值。
    • 情况 3 (lr=0.001): 模型可能会收敛得很慢,需要更长的训练时间。

二级知识点 8.3:正则化参数 (Regularization Parameter) 🏋️

  • 范例: 我们在 PyTorch 中构建一个带有 L2 正则化的线性回归模型。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    import torch
    import torch.nn as nn
    import torch.optim as optim

    # ... (省略数据加载和模型定义部分) ...
    # 定义一个简单的线性模型
    class LinearRegression(nn.Module):
    def __init__(self):
    super().__init__()
    self.linear = nn.Linear(1, 1) # 输入维度为 1,输出维度为 1

    def forward(self, x):
    return self.linear(x)

    # 创建模型实例
    model = LinearRegression()
    # 定义优化器,并设置 L2 正则化参数 (weight_decay)
    # 情况 1:正则化参数设置为 0.1 (较大)
    optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay=0.1)

    # 情况 2:正则化参数设置为 0.01 (适中)
    # optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay=0.01)

    # 情况 3:正则化参数设置为 0 (无正则化)
    # optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay=0)

    # ... (省略训练循环部分) ...
    • 情况 1 (weight_decay=0.1): 模型更倾向于选择较小的权重,有助于防止过拟合。
    • 情况 2 (weight_decay=0.01): 正则化的效果适中。
    • 情况 3 (weight_decay=0): 没有正则化,模型可能更容易过拟合。

二级知识点 8.4:神经网络层数 (Number of Layers) 🧱

  • 范例: 我们使用 PyTorch 构建不同层数的 MLP。
  • 二级知识点 8.5:批处理大小
  • 二级知识点 8.6: 卷积层参数
  • 二级知识点 8.7: 池化层参数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    import torch
    import torch.nn as nn

    # 情况 1:单层 MLP
    class MLP1(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
    super().__init__()
    self.linear = nn.Linear(input_size, output_size)

    def forward(self, x):
    return self.linear(x)

    # 情况 2:两层 MLP
    class MLP2(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
    super().__init__()
    self.linear1 = nn.Linear(input_size, hidden_size)
    self.relu = nn.ReLU()
    self.linear2 = nn.Linear(hidden_size, output_size)

    def forward(self, x):
    x = self.relu(self.linear1(x))
    return self.linear2(x)

    # 情况 3:三层 MLP
    class MLP3(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
    super().__init__()
    self.linear1 = nn.Linear(input_size, hidden_size)
    self.relu1 = nn.ReLU()
    self.linear2 = nn.Linear(hidden_size, hidden_size)
    self.relu2 = nn.ReLU()
    self.linear3 = nn.Linear(hidden_size, output_size)

    def forward(self, x):
    x = self.relu1(self.linear1(x))
    x = self.relu2(self.linear2(x))
    return self.linear3(x)

    # 情况4: 含有卷积层和池化层的神经网络
    class CNN(nn.Module):
    def __init__(self):
    super().__init__()
    self.conv1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1) # 卷积层
    self.relu1 = nn.ReLU() #激活函数
    self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2) #最大池化层
    self.conv2 = nn.Conv2d(16, 32, 3, 1, 1)
    self.relu2 = nn.ReLU()
    self.pool2 = nn.MaxPool2d(2, 2)
    self.flatten = nn.Flatten()
    self.fc = nn.Linear(32*8*8, 10) #全连接层

    def forward(self, x):
    x = self.relu1(self.conv1(x)) # [batch_size, 3, 32, 32] -> [batch_size, 16, 32, 32]
    x = self.pool1(x) # [batch_size, 16, 32, 32] -> [batch_size, 16, 16, 16]
    x = self.relu2(self.conv2(x)) # [batch_size, 16, 16, 16] -> [batch_size, 32, 16, 16]
    x = self.pool2(x) # [batch_size, 32, 16, 16] -> [batch_size, 32, 8, 8]
    x = self.flatten(x) # [batch_size, 32, 8, 8] -> [batch_size, 32*8*8]
    x = self.fc(x) # [batch_size, 32*8*8] -> [batch_size, 10]
    return x
    • 情况 1 (MLP1): 模型比较简单,可能无法很好地拟合复杂的数据。
    • 情况 2 (MLP2): 模型的表示能力有所增强。
    • 情况 3 (MLP3): 模型的表示能力更强,但也更容易过拟合。
    • 情况4:
      * 卷积核大小为 3x3, 步长为1, 填充为1
      * 池化核大小为 2x2, 步长为2

二级知识点 8.8:迭代周期 (Epochs) 🔁

假设其他参数不变的情况下, 神经网络训练5个周期和500个周期,最终模型的效果可能会有显著差别。

这些范例展示了不同超参数对模型的影响。在实际应用中,你需要根据具体的任务、数据集和硬件条件,通过实验来确定最佳的超参数组合。通常会使用网格搜索、随机搜索或贝叶斯优化等方法来进行超参数调优。


为了让疯🉐更全面,我将为你提供一个综合性的代码示例,涵盖以下内容:

注意:⚠️ 这只是一个很简单 ⚠️ 的一个架构示例代码

  1. 构建一个简单的多层感知器 (MLP) 模型 (PyTorch)
  2. 使用模拟数据进行训练
  3. 展示前向传播、反向传播、损失函数计算和优化器更新的过程
  4. 包含对关键步骤的注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import torch
import torch.nn as nn
import torch.optim as optim

# 1. 定义 MLP 模型
class MLP(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super(MLP, self).__init__()
self.fc1 = nn.Linear(input_size, hidden_size) # 全连接层 1
self.relu = nn.ReLU() # ReLU 激活函数
self.fc2 = nn.Linear(hidden_size, output_size) # 全连接层 2

def forward(self, x):
out = self.fc1(x) # 第一层
out = self.relu(out) # ReLU 激活
out = self.fc2(out) # 第二层
return out

# 2. 准备数据 (模拟数据)
input_size = 10
output_size = 2
batch_size = 32

# 生成随机输入数据 (模拟 100 个样本)
x = torch.randn(100, input_size)
# 生成随机标签 (二分类问题,0 或 1)
y = torch.randint(0, 2, (100,)).long() #需要时long 类型

# 3. 定义模型、损失函数和优化器
hidden_size = 64
model = MLP(input_size, hidden_size, output_size)
criterion = nn.CrossEntropyLoss() # 交叉熵损失函数 (适用于多分类问题)
optimizer = optim.Adam(model.parameters(), lr=0.001) # Adam 优化器

# 4. 训练模型
epochs = 100
for epoch in range(epochs):
# 将数据分成批次
for i in range(0, 100, batch_size):
# 获取当前批次的输入和标签
x_batch = x[i:i+batch_size]
y_batch = y[i:i+batch_size]

# 前向传播
outputs = model(x_batch)

# 计算损失
loss = criterion(outputs, y_batch)

# 反向传播 (计算梯度)
loss.backward()

# 更新参数 (使用优化器)
optimizer.step()

# 清空梯度 (重要!)
optimizer.zero_grad()

# 打印每个 epoch 的损失
if (epoch+1) % 10 == 0:
print(f'Epoch [{epoch+1}/{epochs}], Loss: {loss.item():.4f}')

print('训练完成!')

# 5. (可选) 在测试集上评估模型
# ... (这里省略了测试集的代码,你需要准备一个测试集并进行类似的前向传播和损失计算) ...

代码解释:

  • 模型定义 (MLP 类):
    • __init__: 定义了模型的结构,包括两个全连接层 (fc1 和 fc2) 和一个 ReLU 激活函数。
    • forward: 定义了前向传播的过程,数据依次经过 fc1、ReLU 和 fc2。
  • 数据准备:
    • x: 模拟的输入数据,形状为 (100, 10),表示 100 个样本,每个样本有 10 个特征。
    • y: 模拟的标签,形状为 (100,),表示 100 个样本的类别标签 (0 或 1)。
  • 模型、损失函数和优化器:
    • model: 创建 MLP 模型实例。
    • criterion: 使用交叉熵损失函数 (CrossEntropyLoss),适用于多分类问题(这里我们是二分类,但 CrossEntropyLoss 也可以用)。
    • optimizer: 使用 Adam 优化器,并传入模型的参数和学习率。
  • 训练循环:
    • epochs: 训练的轮数。
    • 批次划分: 将数据分成多个批次,每次迭代只使用一个批次的数据。
    • 前向传播: outputs = model(x_batch),将输入数据传入模型,得到输出。
    • 计算损失: loss = criterion(outputs, y_batch),计算模型输出与真实标签之间的损失。
    • 反向传播: loss.backward(),自动计算损失函数相对于模型参数的梯度。
    • 更新参数: optimizer.step(),使用优化器根据梯度更新模型的参数。
    • 清空梯度: optimizer.zero_grad(),在每次更新参数后,需要清空梯度,否则梯度会累积。
  • 代码涵盖了所有之前的知识点范例

这个代码示例展示了一个完整的神经网络训练流程,包括模型定义、数据准备、前向传播、反向传播、损失函数计算和优化器更新。你可以运行这段代码,观察模型的训练过程和损失的变化。

请注意,这只是一个非常基础的示例。在实际应用中,你可能需要处理更复杂的数据集、构建更复杂的模型、调整更多的超参数,并使用更高级的技术来提高模型的性能。

恭喜你!你已经完成了所有知识点的学习!🎉🎉🎉 你现在对神经网络、反向传播算法以及相关的概念有了更深入的了解。也正式开始风(疯)口浪尖般的生活了。如果你有任何问题,或者想进一步探索某个方面,请随时告诉我!😊

免责声明

本报告(“一篇“神经网络中的反向传播”引发的学习血案”)由[ViniJack.SJX] 根据公开可获得的信息以及作者的专业知识和经验撰写,旨在提供关于原理、技术、相关框架和工具的分析和信息。

1. 信息准确性与完整性:

  • 作者已尽最大努力确保报告中信息的准确性和完整性,但不对其绝对准确性、完整性或及时性做出任何明示或暗示的保证。

  • 报告中的信息可能随时间推移而发生变化,作者不承担更新报告内容的义务。

  • 报告中引用的第三方信息(包括但不限于网站链接、项目描述、数据统计等)均来自公开渠道,作者不对其真实性、准确性或合法性负责。

2. 报告用途与责任限制:

  • 本报告仅供参考和学习之用,不构成任何形式的投资建议、技术建议、法律建议或其他专业建议。

  • 读者应自行判断和评估报告中的信息,并根据自身情况做出决策。

  • 对于因使用或依赖本报告中的信息而导致的任何直接或间接损失、损害或不利后果,作者不承担任何责任。

3. 技术使用与合规性:

  • 本报告中提及的任何爬虫框架、工具或技术,读者应自行负责其合法合规使用。

  • 在使用任何爬虫技术时,读者应遵守相关法律法规(包括但不限于数据隐私保护法、知识产权法、网络安全法等),尊重网站的服务条款和robots协议,不得侵犯他人合法权益。

  • 对于因读者违反相关法律法规或不当使用爬虫技术而导致的任何法律责任或纠纷,作者不承担任何责任。

4. 知识产权:

  • 本报告的版权归作者所有,未经作者书面许可,任何人不得以任何形式复制、传播、修改或使用本报告的全部或部分内容。

  • 报告中引用的第三方内容,其知识产权归原作者所有。

5. 其他:

  • 本报告可能包含对未来趋势的预测,这些预测基于作者的判断和假设,不构成任何形式的保证。

  • 作者保留随时修改本免责声明的权利。

请在使用本报告前仔细阅读并理解本免责声明。如果您不同意本免责声明的任何条款,请勿使用本报告。

一篇“**神经网络中的反向传播**”引发的学习血案

http://acorner.ac.cn/2025/02/25/一篇“**神经网络中的反向传播**”引发的学习血案/

作者

ViniJack.SJX

发布于

2025-02-25

更新于

2025-02-25

许可协议

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

评论

You forgot to set the shortname for Disqus. Please set it in _config.yml.
You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.