目录
一、深度学习概述
人工智能、机器学习和深度学习之间的关系:
机器学习是实现人工智能的一种途径,深度学习是机器学习的子集:
传统机器学习算法依赖人工设计特征、提取特征,而深度学习依赖算法自动提取特征。深度学习模仿人类大脑的运行方式,从大量数据中学习特征,这也是深度学习被看做黑盒子、可解释性差的原因。
深度学习的主要应用领域
-
图像处理:分类、目标检测、图像分割(语义分割)
-
自然语言处理:LLM、NLP、Transformer
-
语音识别:对话机器人、智能客服(语音+NLP)
-
自动驾驶:语义分割(行人、车辆、实线等)
-
LLM:大Large语言Language模型Model
-
机器人:非常火的行业
二、神经网络
1、感知神经网络
神经网络(Neural Networks)是一种模拟人脑神经元网络结构的计算模型,用于处理复杂的模式识别、分类和预测等任务。
生物学:
人脑可以看做是一个生物神经网络,由众多的神经元连接而成
-
树突:从其他神经元接收信息的分支
-
细胞核:处理从树突接收到的信息
-
轴突:被神经元用来传递信息的生物电缆
-
突触:轴突和其他神经元树突之间的连接
人脑神经元处理信息的过程:
-
多个信号到达树突,然后整合到细胞体的细胞核中
-
当积累的信号超过某个阈值,细胞就会被激活
-
产生一个输出信号,由轴突传递。
神经网络由多个互相连接的节点(即人工神经元)组成。
2、人工神经元
人工神经元(Artificial Neuron)是神经网络的基本构建单元,模仿了生物神经元的工作原理。其核心功能是接收输入信号,经过加权求和和非线性激活函数处理后,输出结果。
1.构建
人工神经元接受多个输入信息,对它们进行加权求和,再经过激活函数处理,最后将这个结果输出。
2.组成
-
输入(Inputs): 代表输入数据,通常用向量表示,每个输入值对应一个权重。
-
权重(Weights): 每个输入数据都有一个权重,表示该输入对最终结果的重要性。
-
偏置(Bias): 一个额外的可调参数,作用类似于线性方程中的截距,帮助调整模型的输出。
-
加权求和: 神经元将输入乘以对应的权重后求和,再加上偏置。
-
激活函数(Activation Function): 用于将加权求和后的结果转换为输出结果,引入非线性特性,使神经网络能够处理复杂的任务。常见的激活函数有Sigmoid、ReLU(Rectified Linear Unit)、Tanh等。
3.数学表示
如果有 n 个输入 ,权重分别为
,偏置为b,则神经元的输出y:
其中, 是激活函数
3、深入神经网络
神经网络是由大量人工神经元按层次结构连接而成的计算模型。每一层神经元的输出作为下一层的输入,最终得到网络的输出。
1.基本结构
神经网络有下面三个基础层(Layer)构建而成:
-
输入层(Input): 神经网络的第一层,负责接收外部数据,不进行计算。
-
隐藏层(Hidden): 位于输入层和输出层之间,进行特征提取和转换。隐藏层一般有多层,每一层有多个神经元。
-
输出层(Output): 网络的最后一层,产生最终的预测结果或分类结果
2.网络构建
我们使用多个神经元来构建神经网络,相邻层之间的神经元相互连接,并给每一个连接分配一个权重:
注意:同一层的各个神经元之间没有连接。
3.全连接神经网络
全连接(Fully Connected,FC)神经网络是前馈神经网络的一种,每一层的神经元与上一层的所有神经元全连接,常用于图像分类、文本分类等任务。
特点
-
全连接层: 层与层之间的每个神经元都与前一层的所有神经元相连。
-
权重数量: 由于全连接的特点,权重数量较大,容易导致计算量大、模型复杂度高。
-
学习能力: 能够学习输入数据的全局特征,但对于高维数据却不擅长捕捉局部特征(如图像就需要CNN)。
计算步骤
-
数据传递: 输入数据经过每一层的计算,逐层传递到输出层。
-
激活函数: 每一层的输出通过激活函数处理。
-
损失计算: 在输出层计算预测值与真实值之间的差距,即损失函数值。
-
反向传播(Back Propagation): 通过反向传播算法计算损失函数对每个权重的梯度,并更新权重以最小化损失。
三、数据处理
数据分类
-
Ecxel表格的数据;
-
图片是数据;
-
文本信息是数据;
-
官方数据集是数据;
-
make_regression模拟的也是数据
数据加载可参照:
PyTorch基础学习04_手动构建模型&官方模型定义组件-优快云博客
中的数据加载器案例 。
四、参数初始化
神经网络的参数初始化是训练深度学习模型的关键步骤之一。初始化参数(通常是权重和偏置)会对模型的训练速度、收敛性以及最终的性能产生重要影响。
官方文档参考:torch.nn.init — PyTorch 2.5 documentation
1、固定值初始化
固定值初始化是指在神经网络训练开始时,将所有权重或偏置初始化为一个特定的常数值。这种初始化方法虽然简单,但在实际深度学习应用中通常并不推荐。
1.全零初始化
将神经网络中的所有权重参数初始化为0。
方法:将所有权重初始化为零。
缺点:导致对称性破坏,每个神经元在每一层中都会执行相同的计算,模型无法学习。
应用场景:通常不用来初始化权重,但可以用来初始化偏置。
import torch
import torch.nn as nn
def test01():
model = nn.Linear(5,1) # 5个特征,1个输出(输出一个神经元)
"""权重参数全零初始化"""
nn.init.zeros_(model.weight)
# 手动修改weight
# model.weight.data.fill_(0)
print(model.weight)
2.全1初始化
全1初始化会导致网络中每个神经元接收到相同的输入信号,进而输出相同的值,这就无法进行学习和收敛。所以全1初始化只是一个理论上的初始化方法,但在实际神经网络的训练中并不适用。
def test02():
model = nn.Linear(5,1) # 输入的数据每一条有5个特征,1个输出(输出一个神经元)
"""全1初始化"""
nn.init.ones_(model.weight)
# 手动修改weight
# model.weight.data.fill_(1)
print(model.weight)
3.任意常数初始化
将所有参数初始化为某个非零的常数(如 0.1,-1 等)。虽然不同于全0和全1,但这种方法依然不能避免对称性破坏的问题。
def test03():
model = nn.Linear(5,1) # 5个特征,1个输出(输出一个神经元)
"""任意值初始化"""
nn.init.constant_(model.weight,5) # 以5为初始值
print(model.weight)
def mytest():
model = nn.Linear(6,8)
"""手动初始化"""
model.weight.data = torch.tensor([[1,2,3,4,5,6],
[1,2,3,4,5,6],
[1,2,3,4,5,6],
[1,2,3,4,5,6],
[1,2,3,4,5,6],
[1,2,3,4,5,6],
[1,2,3,4,5,6],
[1,2,3,4,5,6]]) # 8行6列
print(model.weight)
2、随机初始化
方法:将权重初始化为随机的小值,通常从正态分布或均匀分布中采样。
应用场景:这是最基本的初始化方法,通过随机初始化避免对称性破坏。
def test04():
model = nn.Linear(5,3) # 5个特征,3个输出(输出3个神经元,3套w) 3行5列
"""随机初始化"""
nn.init.uniform_(model.weight)
print(model.weight)
3、正态分布初始化
def test05():
model = nn.Linear(6,8) # 8行6列
"""正态分布初始化"""
nn.init.normal_(model.weight,mean=1,std=0.2)
print(model.weight)
4、Xavier初始化
也叫做Glorot初始化。
方法:根据输入和输出神经元的数量来选择权重的初始值。权重从以下分布中采样:
或者
其中是当前层的输入神经元数量,
是输出神经元数量。
优点:平衡了输入和输出的方差,适合 Sigmoid 和 Tanh 激活函数。
应用场景:常用于浅层网络或使用 Sigmoid 、Tanh 激活函数的网络。
def test06():
model01 = nn.Linear(6,8)
"""xavier初始化"""
# 正态分布
nn.init.xavier_normal_(model01.weight)
print(model01.weight)
model02 = nn.Linear(6,8)
"""xavier初始化"""
# 均匀分布
nn.init.xavier_uniform_(model02.weight)
print(model02.weight)
5、He初始化
也叫kaiming 初始化。
方法:专门为 ReLU 激活函数设计。权重从以下分布中采样:
其中是当前层的输入神经元数量。
优点:适用于ReLU 和 Leaky ReLU 激活函数。
应用场景:深度网络,尤其是使用 ReLU 激活函数时。
def test07():
model01 = nn.Linear(6,8)
"""kaiming初始化"""
# 正态分布
nn.init.kaiming_normal_(model01.weight,nonlinearity='relu')
print(model01.weight)
model02 = nn.Linear(6,8)
"""kaiming初始化"""
# 均匀分布
nn.init.kaiming_uniform_(model02.weight,nonlinearity='relu')
print(model02.weight)
6、总结
在使用Torch构建网络模型时,每个网络层的参数都有默认的初始化方法,同时还可以通过以上方法来对网络参数进行初始化。
五、激活函数
激活函数的作用是在隐藏层引入非线性,使得神经网络能够学习和表示复杂的函数关系,使网络具备非线性能力,增强其表达能力。
1、基础概念
如果在隐藏层不使用激活函数,那么整个神经网络会表现为一个线性模型。
单层网络(输入层到输出层),如果没有激活函数:
两层网络,且每层都没有激活函数:、
,代入得到:
可以写为,
,所以输出
是输入
的线性变换。
多层网络,同理有
简化为
如此可以看得出来,无论网络多少层,如果没有激活函数,意味着:
整个网络就是线性模型,无法捕捉数据中的非线性关系。
激活函数是引入非线性特性、使神经网络能够处理复杂问题的关键。
可通过非线性可视化的方式去理解非线性的拟合能力:A Neural Network Playground
2、常见激活函数
激活函数通过引入非线性来增强神经网络的表达能力,对于解决线性模型的局限性至关重要。由于反向传播算法(BP)用于更新网络参数,因此激活函数必须是可微的,也就是说能够求导的。
1.sigmoid
Sigmoid激活函数是一种常见的非线性激活函数,特别是在早期神经网络中应用广泛。它将输入映射到0到1之间的值,因此非常适合处理概率问题。
公式
特征
-
将任意实数输入映射到 (0, 1)之间,因此非常适合处理概率场景。
-
sigmoid函数一般只用于二分类的输出层。
-
微分性质: 导数计算比较方便,可以用自身表达式来表示。
缺点
-
梯度消失:
-
在输入非常大或非常小时,Sigmoid函数的梯度会变得非常小,接近于0。这导致在反向传播过程中,梯度逐渐衰减。
-
最终使得早期层的权重更新非常缓慢,进而导致训练速度变慢甚至停滞。
-
-
信息丢失:输入100和输入10000经过sigmoid的激活值几乎都是等于 1 的,但是输入的数据却相差 100 倍。
-
计算成本高: 由于涉及指数运算,Sigmoid的计算比ReLU等函数更复杂,尽管差异并不显著。
代码
import torch
import matplotlib.pyplot as plt
def test01():
"""
Sigmoid激活函数 torch.sigmoid(x)
其将输入映射到0到1之间的值, 因此非常适合处理概率问题
"""
# 一行两列绘制图像
_,ax = plt.subplots(1,2)
# 绘制函数图像
x = torch.linspace(-10,10,100)
y = torch.sigmoid(x)
# 在第一行第一列绘制sigmoid函数曲线图
ax[0].set_xlabel("x")
ax[0].set_ylabel("y")
ax[0].set_title("sigmoid")
ax[0].plot(x,y)
# 绘制sigmoid导数曲线图
x = torch.linspace(-10,10,100,requires_grad=True)
# y = torch.sigmoid(x) * (1 - torch.sigmoid(x))
# 自动求导 sum() 需要标量才能反向传播
torch.sigmoid(x).sum().backward()
# 在第一行第二列绘制sigmoid导数曲线图
ax[1].set_xlabel("x")
ax[1].set_ylabel("y")
ax[1].set_title("sigmoid gradient")
# ax[1].plot(x.detach().numpy(), y.detach().numpy())
# 用自动求导的结果绘制曲线图
ax[1].plot(x.detach().numpy(), x.grad.detach().numpy())
# 设置曲线颜色
ax[1].lines[0].set_color("red")
plt.show()
2.tanh
tanh(双曲正切)是一种常见的非线性激活函数,常用于神经网络的隐藏层。tanh 函数也是一种S形曲线,输出范围为(−1,1)。
公式
特征
-
输出范围: 将输入映射到$$(-1, 1)$$之间,因此输出是零中心的。相比于Sigmoid函数,这种零中心化的输出有助于加速收敛。
-
对称性: Tanh函数关于原点对称,因此在输入为0时,输出也为0。这种对称性有助于在训练神经网络时使数据更平衡。
-
平滑性: Tanh函数在整个输入范围内都是连续且可微的,这使其非常适合于使用梯度下降法进行优化。
缺点
-
梯度消失: 虽然一定程度上改善了梯度消失问题,但在输入值非常大或非常小时导数还是非常小,这在深层网络中仍然是个问题。
-
计算成本: 由于涉及指数运算,Tanh的计算成本还是略高,尽管差异不大。
代码
import torch
import matplotlib.pyplot as plt
def test02():
"""
tanh激活函数 torch.tanh(x)
其将输入映射到-1到1之间的值,因此也称为双曲正切函数
是一种常见的非线性激活函数,常用于神经网络的隐藏层
"""
# 一行两列绘制图像
_,ax = plt.subplots(1,2)
# 绘制函数图像
x = torch.linspace(-10,10,100)
y = torch.tanh(x)
# 在第一行第一列绘制tanh函数曲线图
ax[0].set_xlabel("x")
ax[0].set_ylabel("y")
ax[0].set_title("tanh")
ax[0].plot(x,y)
# 绘制tanh导数曲线图
x = torch.linspace(-10,10,100,requires_grad=True)
# y = torch.tanh(x) * (1 - torch.tanh(x))
# 自动求导 sum() 需要标量才能反向传播
torch.tanh(x).sum().backward()
# 在第一行第二列绘制tanh导数曲线图
ax[1].set_xlabel("x")
ax[1].set_ylabel("y")
ax[1].set_title("tanh gradient")
# ax[1].plot(x.detach().numpy(), y.detach().numpy())
# 用自动求导的结果绘制曲线图
ax[1].plot(x.detach().numpy(), x.grad.detach().numpy())
# 设置曲线颜色
ax[1].lines[0].set_color("red")
plt.show()
3.ReLU
ReLU(Rectified Linear Unit)是深度学习中最常用的激活函数之一,它的全称是修正线性单元。ReLU 激活函数的定义非常简单,但在实践中效果非常好。
公式
即对输入x进行线性变换:
特征
-
计算简单:ReLU 的计算非常简单,只需要对输入进行一次比较运算,这在实际应用中大大加速了神经网络的训练。
-
ReLU 函数的导数是分段函数:
-
缓解梯度消失问题:相比于 Sigmoid 和 Tanh 激活函数,ReLU 在正半区的导数恒为 1,这使得深度神经网络在训练过程中可以更好地传播梯度,不存在饱和问题。
-
稀疏激活:ReLU在输入小于等于 0 时输出为 0,这使得 ReLU 可以在神经网络中引入稀疏性(即一些神经元不被激活),这种稀疏性可以提升网络的泛化能力。
缺点
神经元死亡:由于ReLU在 x≤0 时输出为0,如果某个神经元输入值是负,那么该神经元将永远不再激活,成为“死亡”神经元。随着训练的进行,网络中可能会出现大量死亡神经元,从而会降低模型的表达能力。
代码
import torch
import matplotlib.pyplot as plt
import torch.nn.functional as F
def test03():
# 优先选择
"""
ReLU激活函数 torch.nn.functional.relu(x)
ReLU(Rectified Linear Unit)函数,也称为修正线性单元函数,是一种常用的非线性激活函数。
在ReLU函数中,如果输入x大于0,则输出为x,否则输出为0。
ReLU函数在深度学习中常用于隐藏层,因为它能够有效防止梯度消失,
从而提高模型的训练速度和稳定性。
"""
# 一行两列绘制图像
_,ax = plt.subplots(1,2)
# 绘制函数图像
x = torch.linspace(-10,10,100)
y = torch.nn.functional.relu(x)
# 在第一行第一列绘制relu函数曲线图
ax[0].set_xlabel("x")
ax[0].set_ylabel("y")
ax[0].set_title("relu")
ax[0].plot(x,y)
# 绘制relu导数曲线图
x = torch.linspace(-10,10,100,requires_grad=True)
# 自动求导
torch.nn.functional.relu(x).sum().backward()
# 在第一行第二列绘制relu导数曲线图
ax[1].set_xlabel("x")
ax[1].set_ylabel("y")
ax[1].set_title("relu gradient")
# 用自动求导的结果绘制曲线图
ax[1].plot(x.detach().numpy(), x.grad.detach().numpy())
# 设置曲线颜色
ax[1].lines[0].set_color("red")
plt.show()
4.Leaky ReLU
Leaky ReLU是一种对 ReLU 函数的改进,旨在解决 ReLU 的一些缺点,特别是Dying ReLU 问题。Leaky ReLU 通过在输入为负时引入一个小的负斜率来改善这一问题。
公式
其中, 是一个非常小的常数(如 0.01),它控制负半轴的斜率。这个常数
是一个超参数,可以在训练过程中可自行进行调整。
特征
-
避免神经元死亡:通过在 x≤0 区域引入一个小的负斜率,这样即使输入值小于等于零,Leaky ReLU仍然会有梯度,允许神经元继续更新权重,避免神经元在训练过程中完全“死亡”的问题。
-
计算简单:Leaky ReLU 的计算与 ReLU 相似,只需简单的比较和线性运算,计算开销低。
缺点
-
参数选择:
是一个需要调整的超参数,选择合适的
值可能需要实验和调优。
-
出现负激活:如果
设定得不当,仍然可能导致激活值过低。
代码
import torch
import matplotlib.pyplot as plt
def test04():
"""
Leaky ReLU激活函数 torch.nn.functional.leaky_relu(x)
Leaky ReLU(Leaky Rectified Linear Unit)函数是一种改进的ReLU函数,
它允许在负输入上保留部分输出,从而解决了ReLU函数的梯度消失问题。
在Leaky ReLU函数中,如果输入x大于0,则输出为x,否则输出为α*x,
其中α是一个很小的正数。
"""
# 一行两列绘制图像
_,ax = plt.subplots(1,2)
# 绘制函数图像
x = torch.linspace(-10,10,100)
y = torch.nn.functional.leaky_relu(x)
# 在第一行第一列绘制leaky_relu函数曲线图
ax[0].set_xlabel("x")
ax[0].set_ylabel("y")
ax[0].set_title("leaky_relu")
ax[0].plot(x,y)
# 绘制leaky_relu导数曲线图
x = torch.linspace(-10,10,100,requires_grad=True)
# 自动求导
torch.nn.functional.leaky_relu(x).sum().backward()
# 在第一行第二列绘制leaky_relu导数曲线图
ax[1].set_xlabel("x")
ax[1].set_ylabel("y")
ax[1].set_title("leaky_relu gradient")
# 用自动求导的结果绘制曲线图
ax[1].plot(x.detach().numpy(), x.grad.detach().numpy())
# 设置曲线颜色
ax[1].lines[0].set_color("red")
plt.show()
5.softmax
Softmax激活函数通常用于分类问题的输出层,它能够将网络的输出转换为概率分布,使得输出的各个类别的概率之和为 1。Softmax 特别适合用于多分类问题。
公式
假设神经网络的输出层有n个节点,每个节点的输出为z_i,则 Softmax 函数的定义如下:
特征
-
将输出转化为概率:通过Softmax,可以将网络的原始输出转化为各个类别的概率,从而可以根据这些概率进行分类决策。
-
概率分布:Softmax的输出是一个概率分布,即每个输出值
都是一个介于0和1之间的数,并且所有输出值的和为 1:
-
突出差异:Softmax会放大差异,使得概率最大的类别的输出值更接近1,而其他类别更接近0。
-
在实际应用中,Softmax常与交叉熵损失函数Cross-Entropy Loss结合使用,用于多分类问题。在反向传播中,Softmax的导数计算是必需的。
缺点
-
数值不稳定性:在计算过程中,如果z_i的数值过大,
可能会导致数值溢出。因此在实际应用中,经常会对z_i进行调整,如减去最大值以确保数值稳定。
-
难以处理大量类别:Softmax在处理类别数非常多的情况下(如大模型中的词汇表)计算开销会较大。
代码
import torch
import matplotlib.pyplot as plt
def test05():
"""
Softmax激活函数 torch.nn.functional.softmax(x)
Softmax激活函数通常用于分类问题的输出层,
它能够将网络的输出转换为概率分布,使得输出的各个类别的概率之和为 1
Softmax 特别适合用于多分类问题
"""
# 一行两列绘制图像
_,ax = plt.subplots(1,2)
# 绘制函数图像
x = torch.linspace(-10,10,10)
y = torch.nn.functional.softmax(x)
# 在第一行第一列绘制leaky_relu函数曲线图
ax[0].set_xlabel("x")
ax[0].set_ylabel("y")
ax[0].set_title("softmax")
ax[0].plot(x,y)
# 绘制softmax导数曲线图
x = torch.linspace(-10,10,10,requires_grad=True)
# 自动求导
torch.nn.functional.softmax(x).sum().backward()
# 在第一行第二列绘制leaky_relu导数曲线图
ax[1].set_xlabel("x")
ax[1].set_ylabel("y")
ax[1].set_title("softmax gradient")
# 用自动求导的结果绘制曲线图
ax[1].plot(x.detach().numpy(), x.grad.detach().numpy())
# 设置曲线颜色
ax[1].lines[0].set_color("red")
plt.show()
6.激活函数的选择
更多激活函数的参考官方文档:torch.nn — PyTorch 2.5 documentation
隐藏层
-
优先选ReLU;
-
如果ReLU效果不咋地,那么尝试其他激活,如Leaky ReLU等;
-
使用ReLU时注意神经元死亡问题, 避免出现过多神经元死亡;
-
不使用sigmoid,尝试使用tanh;
输出层
-
二分类问题选择sigmoid激活函数;
-
多分类问题选择softmax激活函数;
-
回归问题选择identity激活函数;