一篇“**神经网络中的反向传播**”引发的学习血案
引言
最近不停的听到反向传播以及方向传播的优化方法的一些东西,久好奇翻了一番之前的看过的一篇文章
原文url:https://serokell.io/blog/understanding-backpropagation
还是先放上译文:
好的,以下是文章内容的中文翻译:
1 |
|
然后。不小心。。。真的不小心整理了一下反向传播的涉及的知识篇。。。。真的不小心的。。。就整理了下面一堆东西了。😑太难了。以为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:前向传播过程 🚶
数据输入: 首先,我们将数据(例如一张图片 🖼️ 或一段文字 📝)输入到神经网络的输入层。
逐层传递: 数据从输入层开始,逐层通过隐藏层。每一层的神经元都会对接收到的数据进行加权求和,然后通过激活函数进行处理,产生输出。就像接力赛一样,每一棒的选手(神经元)都会把接力棒(数据)传递给下一棒。🏃♀️🏃♂️
输出结果: 最后,数据到达输出层,产生网络的最终输出结果。这个结果可能是对图片的分类、对文字的翻译等等。
二级知识点 2.2:计算图 (Computational Graph) 📊
为了更好地理解前向传播和后续的反向传播过程,我们可以使用计算图来表示。
计算图是一种有向图,它将计算过程表示为一系列节点和边。
节点表示计算操作(例如加法、乘法、激活函数等),边表示数据流动的方向。
通过计算图,我们可以清晰地看到数据是如何在神经网络中流动的,以及每个节点是如何参与计算的。
举个例子:假设我们要计算
y = (x + w) * b
,我们可以用计算图表示为:x —-(+)—-(*)—- y
| ^ ^
| | |
w —-| b —-|在这个图中,
x
、w
、b
是输入节点,(+)
和(*)
是计算节点,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 之间)
计算过程:
- 加权和 (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 - 激活函数 (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
26import 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- 输入 (Inputs):
二级知识点 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 ... OPyTorch 代码片段:
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
28import 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 识别手写数字的例子,我们来看前向传播的具体过程:
- 输入图像: 将一张 28x28 的手写数字图像 (例如数字 “3”) 输入到 MLP 的输入层。图像的每个像素值 (0-255) 对应输入层的一个神经元。
- 展平: 将 28x28 的二维图像展平为 784 维的一维向量。
- 输入层到隐藏层:
- 对 784 维的输入向量进行加权求和:每个输入值乘以对应的权重,然后加上偏置。
- 应用 ReLU 激活函数:将加权和的结果输入到 ReLU 函数中,得到隐藏层的输出。
- 隐藏层到输出层:
- 对 128 维的隐藏层输出进行加权求和:每个隐藏层输出值乘以对应的权重,然后加上偏置。
- 应用 Softmax 激活函数:将加权和的结果输入到 Softmax 函数中,得到一个 10 维的概率分布向量。向量的每个元素表示对应数字的概率。
- 输出结果: 输出层产生一个 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:反向传播过程 ⏪
前向传播: 首先,进行一次前向传播,计算出网络的输出和损失。
计算输出层梯度: 计算损失函数相对于输出层神经元输出的梯度。
反向传播梯度: 从输出层开始,逐层向前计算梯度。
使用链式法则,计算损失函数相对于每一层权重和偏置的梯度。
将梯度传递到前一层。
更新权重和偏置: 使用优化算法(例如梯度下降),根据计算出的梯度更新权重和偏置。
形象比喻: 想象你正在玩一个“猜数字”游戏。🤖
前向传播: 你猜一个数字 (输入),然后朋友告诉你猜的数字是大了还是小了 (输出/损失)。
反向传播: 你根据朋友的反馈 (损失),反思自己猜数字的策略 (权重),并调整自己的策略 (更新权重),以便下次猜得更准。
二级知识点 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
- 目标: 找到合适的
w
和b
,使得损失函数L
最小化。
计算梯度 (Calculate Gradients):
- ∂L/∂y: 损失函数
L
相对于模型输出y
的梯度。∂L/∂y = 2 * (y - y_true)
- ∂y/∂w: 模型输出
y
相对于权重w
的梯度。∂y/∂w = x
- ∂y/∂b: 模型输出
y
相对于偏置b
的梯度。∂y/∂b = 1
- ∂L/∂w: 损失函数
L
相对于权重w
的梯度 (使用链式法则)。∂L/∂w = (∂L/∂y) * (∂y/∂w) = 2 * (y - y_true) * x
- ∂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
33import 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
。链式法则分解:
- ∂L/∂y2: 损失函数
L
相对于第二层输出y2
的梯度。∂L/∂y2 = 2 * (y2 - y_true)
- ∂y2/∂y1: 第二层输出
y2
相对于第一层输出y1
的梯度。∂y2/∂y1 = w2
- ∂y1/∂w1: 第一层输出
y1
相对于权重w1
的梯度。∂y1/∂w1 = x
- ∂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
24import 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 个神经元)。
反向传播过程:
- 前向传播: 输入一张手写数字图像,经过 MLP 的前向传播,得到输出层的预测概率分布。
- 计算损失: 使用交叉熵损失函数 (Cross-Entropy Loss) 计算预测概率分布与真实标签 (one-hot 编码) 之间的差异。
- 计算输出层梯度: 计算损失函数相对于输出层神经元输出的梯度。
- 反向传播梯度 (隐藏层到输出层):
- 计算损失函数相对于输出层权重和偏置的梯度 (使用链式法则)。
- 计算损失函数相对于隐藏层输出的梯度 (使用链式法则)。
- 反向传播梯度 (输入层到隐藏层):
- 计算损失函数相对于隐藏层权重和偏置的梯度 (使用链式法则)。
- 计算损失函数相对于输入层输出的梯度 (使用链式法则,但通常不需要更新输入层的权重)。
- 更新权重和偏置: 使用优化算法 (例如 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 是样本数量) - 目标: 找到合适的
w
和b
,使得损失函数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
41import 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
55import 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
44import 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
47import 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.Module
、nn.Linear
、optim
等模块来构建模型和优化器,而不需要手动实现梯度下降的细节。
在下一阶段,我们将学习损失函数, 这是反向传播的”指挥官”!👨✈️
损失函数
好的!让我们进入下一个关键环节:损失函数!🎯
一级知识点 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()
- PyTorch:
三级知识点 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()
- PyTorch:
形象比喻:
分类任务: 想象你在玩一个“猜动物”的游戏。
- 交叉熵损失: 就像猜错的“惩罚”,猜得越离谱,“惩罚”越大。
回归任务: 想象你在玩一个“扔飞镖”的游戏。
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
15import 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
15import 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
- 均方误差 (MSE):
PyTorch 代码 (MSE):
1
2
3
4
5
6
7
8
9
10
11
12
13
14import 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
14import 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) 结合反向传播算法,可以学习人脸图像的特征,从而实现人脸识别。
过程:
数据收集: 收集大量人脸图像,并标注每个图像对应的人物身份。
构建 CNN 模型: 设计一个 CNN 模型,通常包含多个卷积层、池化层和全连接层。
前向传播: 将人脸图像输入到 CNN 模型中,进行前向传播,得到模型的预测输出(例如,属于每个人物身份的概率)。
计算损失: 使用交叉熵损失函数等,计算模型预测输出与真实标签之间的差异。
反向传播: 使用反向传播算法,计算损失函数相对于模型中每个权重和偏置的梯度。
更新参数: 使用优化算法(例如 Adam),根据计算出的梯度更新模型的权重和偏置。
重复步骤 3-6: 不断迭代,直到模型收敛或达到预定的训练轮数。
应用: 手机解锁、门禁系统、安防监控等。
二级知识点 6.2:自然语言处理:语音识别 (NLP: Speech Recognition) 🗣️📱
原理: 循环神经网络 (RNN) 或 Transformer 模型结合反向传播算法,可以将语音信号转换为文本。
过程:
数据收集: 收集大量语音数据,并标注每个语音片段对应的文本内容。
构建模型: 设计一个 RNN 或 Transformer 模型。
特征提取: 将语音信号转换为声学特征(例如梅尔频率倒谱系数 MFCC)。
前向传播: 将声学特征输入到模型中,进行前向传播,得到模型的预测输出(例如,每个时间步的文本预测概率)。
计算损失: 使用交叉熵损失函数等,计算模型预测输出与真实标签之间的差异。
反向传播: 使用反向传播算法(例如 BPTT,随时间反向传播),计算损失函数相对于模型中每个权重和偏置的梯度。
更新参数: 使用优化算法(例如 Adam),根据计算出的梯度更新模型的权重和偏置。
重复步骤 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
43import 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}")代码解释:
- 加载预训练模型: 我们使用
torchvision
提供的预训练resnet18
模型作为示例。实际应用中,你可能需要下载并加载更专业的 FaceNet 模型,例如在 VGGFace2 数据集上预训练的模型。 - 修改最后一层: 我们将
resnet18
模型的最后一层 (全连接层) 修改为输出 128 维的特征向量。这是 FaceNet 模型常用的特征向量维度。 - 图像预处理: 我们定义了一系列图像预处理步骤,包括调整图像大小、转换为 Tensor 和标准化。这些步骤对于提高模型的性能非常重要。
- 特征提取: 我们将预处理后的图像输入到模型中,获取 128 维的特征向量。
- 距离计算: 我们计算两个特征向量之间的欧氏距离。距离越小,表示两张人脸图像越相似。
- 阈值判断: 我们设置一个阈值,如果距离小于阈值,则认为两张图片属于同一个人。
- 加载预训练模型: 我们使用
二级知识点 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
29import 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}")代码解释:
- 加载预训练模型和处理器: 我们使用 Hugging Face Transformers 库提供的
Wav2Vec2ForCTC
和Wav2Vec2Processor
。Wav2Vec2ForCTC
是一个预训练的语音识别模型,Wav2Vec2Processor
用于将音频数据转换为模型所需的输入格式。 - 加载音频文件: 我们使用
torchaudio
库加载音频文件。你需要确保音频文件是单声道、16kHz 采样率的。如果不是,需要进行重采样。 - 数据预处理: 我们使用
processor
将音频数据转换为模型所需的输入格式。 - 模型预测: 我们将预处理后的音频数据输入到模型中,获取
logits
。logits
是模型输出的原始值,还没有经过 Softmax 转换。 - 解码: 我们使用
torch.argmax
对logits
进行解码,获取预测的文本 ID。然后,我们使用processor.batch_decode
将文本 ID 转换为文本。
- 加载预训练模型和处理器: 我们使用 Hugging Face Transformers 库提供的
这些范例展示了反向传播在人脸识别和语音识别中的应用。请注意,这些只是简化的示例,实际应用中可能需要更复杂的模型、更多的数据预处理步骤以及更精细的调参。
反向传播📣的历史
说到这里,那就顺带看看,一起回顾反向传播算法的发展历程,了解这段精彩的历史!📜
一级知识点 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
12import 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)- 情况 1: 我们将输入图像大小设置为 32x32(原始大小)。
二级知识点 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
26import 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
27import 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
60import 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个周期,最终模型的效果可能会有显著差别。
这些范例展示了不同超参数对模型的影响。在实际应用中,你需要根据具体的任务、数据集和硬件条件,通过实验来确定最佳的超参数组合。通常会使用网格搜索、随机搜索或贝叶斯优化等方法来进行超参数调优。
为了让疯🉐更全面,我将为你提供一个综合性的代码示例,涵盖以下内容:
注意:⚠️ 这只是一个很简单 ⚠️ 的一个架构示例代码
- 构建一个简单的多层感知器 (MLP) 模型 (PyTorch)
- 使用模拟数据进行训练
- 展示前向传播、反向传播、损失函数计算和优化器更新的过程
- 包含对关键步骤的注释
1 | import torch |
代码解释:
- 模型定义 (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. 其他:
本报告可能包含对未来趋势的预测,这些预测基于作者的判断和假设,不构成任何形式的保证。
作者保留随时修改本免责声明的权利。
请在使用本报告前仔细阅读并理解本免责声明。如果您不同意本免责声明的任何条款,请勿使用本报告。
一篇“**神经网络中的反向传播**”引发的学习血案
install_url
to use ShareThis. Please set it in _config.yml
.