深度学习基础1
目录
1. 深度学习的定义
2.神经网络
2.1. 感知神经网络
2.2 人工神经元
2.2.1 构建人工神经元
2.2.2 组成部分
2.2.3 数学表示
2.2.4 对比生物神经元
2.3 深入神经网络
2.3.1 基本结构
2.3.2 网络构建
2.3.3 全连接神经网络
3.神经网络的参数初始化
3.1 固定值初始化
3.1.1 全零初始化
3.1.2 全1初始化
3.1.3 任意常数初始化
3.2 随机初始化
3.2.1 均匀初始化
3.2.2 正态分布初始化
3.3 Xavier 初始化 / Glorot 初始化
3.4 He初始化 / kaiming 初始化
4.激活函数
4.1 非线性
4.2 非线性可视化
4.3 常见激活函数
4.3.1 Sigmoid
4.2.2 Tanh
4.2.3 ReLU
2.2.4 LeakyReLU
4.2.5 Softmax
4.4 选择激活函数
4.4.1 隐藏层
4.4.2 输出层
1. 深度学习的定义
传统机器学习算法依赖人工设计特征、提取特征,而深度学习依赖算法自动提取特征。深度学习模仿人类大脑的运行方式,从大量数据中学习特征,这也是深度学习被看做黑盒子、可解释性差的原因。
人工智能、机器学习和深度学习之间的关系:
2.神经网络
深度学习(Deep Learning)是神经网络的一个子领域,主要关注更深层次的神经网络结构,也就是深层神经网络(Deep Neural Networks,DNNs)。
2.1. 感知神经网络
神经网络(Neural Networks)是一种模拟人脑神经元网络结构的计算模型,用于处理复杂的模式识别、分类和预测等任务。生物神经元如下图:
生物学:
人脑可以看做是一个生物神经网络,由众多的神经元连接而成
-
树突:从其他神经元接收信息的分支
-
细胞核:处理从树突接收到的信息
-
轴突:被神经元用来传递信息的生物电缆
-
突触:轴突和其他神经元树突之间的连接
人脑神经元处理信息的过程:
-
多个信号到达树突,然后整合到细胞体的细胞核中
-
当积累的信号超过某个阈值,细胞被激活
-
产生一个输出信号,由轴突传递。
神经网络由多个互相连接的节点(即人工神经元)组成。
2.2 人工神经元
人工神经元(Artificial Neuron)是神经网络的基本构建单元,模仿了生物神经元的工作原理。其核心功能是接收输入信号,经过加权求和和非线性激活函数处理后,输出结果。
2.2.1 构建人工神经元
人工神经元接受多个输入信息,对它们进行加权求和,再经过激活函数处理,最后将这个结果输出。
2.2.2 组成部分
-
输入(Inputs): 代表输入数据,通常用向量表示,每个输入值对应一个权重。
-
权重(Weights): 每个输入数据都有一个权重,表示该输入对最终结果的重要性。
-
偏置(Bias): 一个额外的可调参数,作用类似于线性方程中的截距,帮助调整模型的输出。
-
加权求和: 神经元将输入乘以对应的权重后求和,再加上偏置。
-
激活函数(Activation Function): 用于将加权求和后的结果转换为输出结果,引入非线性特性,使神经网络能够处理复杂的任务。常见的激活函数有Sigmoid、ReLU(Rectified Linear Unit)、Tanh等。
2.2.3 数学表示
2.2.4 对比生物神经元
人工神经元和生物神经元对比如下表:
生物神经元 | 人工神经元 |
---|---|
细胞核 | 节点 (加权求和 + 激活函数) |
树突 | 输入 |
轴突 | 带权重的连接 |
突触 | 输出 |
2.3 深入神经网络
神经网络是由大量人工神经元按层次结构连接而成的计算模型。每一层神经元的输出作为下一层的输入,最终得到网络的输出。
2.3.1 基本结构
神经网络有下面三个基础层(Layer)构建而成:
-
输入层(Input): 神经网络的第一层,负责接收外部数据,不进行计算。
-
隐藏层(Hidden): 位于输入层和输出层之间,进行特征提取和转换。隐藏层一般有多层,每一层有多个神经元。
-
输出层(Output): 网络的最后一层,产生最终的预测结果或分类结果
2.3.2 网络构建
我们使用多个神经元来构建神经网络,相邻层之间的神经元相互连接,并给每一个连接分配一个权重,经典如下:
注意:同一层的各个神经元之间是没有连接的。
2.3.3 全连接神经网络
全连接(Fully Connected,FC)神经网络是前馈神经网络的一种,每一层的神经元与上一层的所有神经元全连接,常用于图像分类、文本分类等任务。
特点:
-
全连接层: 层与层之间的每个神经元都与前一层的所有神经元相连。
-
权重数量: 由于全连接的特点,权重数量较大,容易导致计算量大、模型复杂度高。
-
学习能力: 能够学习输入数据的全局特征,但对于高维数据却不擅长捕捉局部特征。
计算步骤:
(1)数据传递: 输入数据经过每一层的计算,逐层传递到输出层。
(2)激活函数: 每一层的输出通过激活函数处理。
(3)损失计算: 在输出层计算预测值与真实值之间的差距,即损失函数值。
(4)反向传播(Back Propagation): 通过反向传播算法计算损失函数对每个权重的梯度,并更新权重以最小化损失。
3.神经网络的参数初始化
神经网络的参数初始化是训练深度学习模型的关键步骤之一。初始化参数(通常是权重和偏置)会对模型的训练速度、收敛性以及最终的性能产生重要影响。
官方文档参考:torch.nn.init — PyTorch 2.5 documentation
3.1 固定值初始化
固定值初始化是指在神经网络训练开始时,将所有权重或偏置初始化为一个特定的常数值。
3.1.1 全零初始化
将神经网络中的所有权重参数初始化为0。
方法:将所有权重初始化为零。
缺点:导致对称性破坏,每个神经元在每一层中都会执行相同的计算,模型无法学习。
应用场景:通常不用来初始化权重,但可以用来初始化偏置。
import torch
import torch.nn as nnlinear =nn.Linear(in_features=6,out_features=4)
nn.init.zeros_(linear.weight)print(linear.weight)
3.1.2 全1初始化
全1初始化会导致网络中每个神经元接收到相同的输入信号,进而输出相同的值,这就无法进行学习和收敛。所以全1初始化只是一个理论上的初始化方法,但在实际神经网络的训练中并不适用。
linear =nn.Linear(in_features=6,out_features=4)
nn.init.ones_(linear.weight)print(linear.weight)
3.1.3 任意常数初始化
将所有参数初始化为某个非零的常数。虽然不同于全0和全1,但这种方法依然不能避免对称性破坏的问题。
linear =nn.Linear(in_features=6,out_features=4)
nn.init.constant_(linear.weight,0.54)print(linear.weight)
3.2 随机初始化
方法:将权重初始化为随机的小值,通常从正态分布或均匀分布中采样。
应用场景:这是最基本的初始化方法,通过随机初始化避免对称性破坏。
3.2.1 均匀初始化
linear =nn.Linear(in_features=6,out_features=4)
nn.init.uniform_(linear.weight)print(linear.weight)
3.2.2 正态分布初始化
linear =nn.Linear(in_features=6,out_features=4)
nn.init.normal_(linear.weight,mean=4,std=2)print(linear.weight)
3.3 Xavier 初始化 / Glorot 初始化
方法:根据输入和输出神经元的数量来选择权重的初始值。权重从以下分布中采样:
优点:平衡了输入和输出的方差,适合Sigmoid 和 Tanh 激活函数。
应用场景:常用于浅层网络或使用Sigmoid 、Tanh 激活函数的网络。
linear = nn.Linear(in_features=6,out_features=4)
nn.init.xavier_normal_(linear.weight)print(linear.weight)linear = nn.Linear(in_features=6,out_features=4)
nn.init.xavier_uniform_(linear.weight)print(linear.weight)
3.4 He初始化 / kaiming 初始化
方法:专门为 ReLU 激活函数设计。权重从以下分布中采样:
优点:适用于ReLU 和 Leaky ReLU 激活函数。
应用场景:深度网络,尤其是使用 ReLU 激活函数时。
linear = nn.Linear(in_features=6,out_features=4)
nn.init.kaiming_normal_(linear.weight)print(linear.weight)linear = nn.Linear(in_features=6,out_features=4)
nn.init.kaiming_uniform_(linear.weight)print(linear.weight)
4.激活函数
激活函数的作用是在隐藏层引入非线性,使得神经网络能够学习和表示复杂的函数关系,使网络具备非线性能力,增强其表达能力。
4.1 非线性
如果在隐藏层不使用激活函数,那么整个神经网络会表现为一个线性模型。
线性模型中无论网络多少层:
- 无法捕捉数据中的非线性关系。
- 激活函数是引入非线性特性、使神经网络能够处理复杂问题的关键。
4.2 非线性可视化
可以通过可视化的方式去理解非线性的拟合能力
A Neural Network Playground
4.3 常见激活函数
激活函数通过引入非线性来增强神经网络的表达能力,对于解决线性模型的局限性至关重要。由于反向传播算法(BP)用于更新网络参数,因此激活函数必须是可微的,也就是说能够求导的。
4.3.1 Sigmoid
Sigmoid激活函数是一种常见的非线性激活函数。它将输入映射到0到1之间的值,因此非常适合处理概率问题,比如二分类问题。
公式:
其中,e 是自然常数(约等于2.718),x 是输入。
特征:
- 将任意实数输入映射到 (0, 1)之间。
- sigmoid函数一般只用于二分类的输出层。
- 微分性质: 导数计算比较方便,可以用自身表达式来表示:
缺点:
-
梯度消失: 在输入非常大或非常小时,Sigmoid函数的梯度会变得非常小,接近于0。这导致在反向传播过程中,梯度逐渐衰减。最终使得早期层的权重更新非常缓慢,进而导致训练速度变慢甚至停滞。
-
信息丢失:输入的数据相差巨大,但输出结果变化小。
-
计算成本高: 由于涉及指数运算。
函数绘制:
import torch
import matplotlib.pyplot as plt# 中文
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False_,ax = plt.subplots(1,2)# 1.绘制sigmoid函数
x = torch.linspace(-10,10,100)
y = torch.sigmoid(x)ax[0].grid(True)
ax[0].set_title('sigmoid函数曲线图')
ax[0].set_xlabel('x')
ax[0].set_ylabel('y')
ax[0].plot(x,y)# 2.绘制sigmoid导函数
x1 = torch.linspace(-10,10,100)y1 =torch.sigmoid(x1)*(1-torch.sigmoid(x1))ax[1].grid(True)
ax[1].set_title('sigmoid函数曲线图')
ax[1].set_xlabel('x1')
ax[1].set_ylabel('y1')
ax[1].plot(x1,y1)
ax[1].lines[0].set_color('red')plt.show()
4.2.2 Tanh
tanh(双曲正切)是一种常见的非线性激活函数,常用于神经网络的隐藏层。tanh 函数也是一种S形曲线,输出范围为(−1,1)。
公式:
特征:
-
输出范围: 将输入映射到(-1, 1)之间。相比于Sigmoid函数,这种零中心化的输出有助于加速收敛。
-
对称性: Tanh函数关于原点对称,因此在输入为0时,输出也为0。这种对称性有助于在训练神经网络时使数据更平衡。
-
平滑性: Tanh函数在整个输入范围内都是连续且可微的,这使其非常适合于使用梯度下降法进行优化。
缺点:
-
梯度消失: 虽然一定程度上改善了梯度消失问题,但在输入值非常大或非常小时导数还是非常小,这在深层网络中仍然是个问题。
-
计算成本: 由于涉及指数运算。
函数绘制:
4.2.3 ReLU
ReLU(Rectified Linear Unit)全称是修正线性单元。
公式:
ReLU 函数定义如下:
即ReLU对输入x进行非线性变换:
特征:
-
计算简单:ReLU 的计算非常简单,只需要对输入进行一次比较运算,这在实际应用中大大加速了神经网络的训练。
-
ReLU 函数的导数是分段函数:
-
缓解梯度消失问题:相比于 Sigmoid 和 Tanh 激活函数,ReLU 在正半区的导数恒为 1,这使得深度神经网络在训练过程中可以更好地传播梯度,不存在饱和问题。
-
稀疏激活:ReLU在输入小于等于 0 时输出为 0,这使得 ReLU 可以在神经网络中引入稀疏性(即一些神经元不被激活),这种稀疏性可以提升网络的泛化能力。
缺点:
神经元死亡:由于ReLU在x≤0时输出为0,如果某个神经元输入值是负,那么该神经元将永远不再激活,成为“死亡”神经元。随着训练的进行,网络中可能会出现大量死亡神经元,会降低模型的表达能力。
函数绘图:
# 创建子图
fig, ax = plt.subplots(1, 2)# 1. 绘制 relu 函数
x = torch.linspace(-10, 10, 100)
y = torch.relu(x)ax[0].grid(True)
ax[0].set_title('relu函数曲线图')
ax[0].set_xlabel('x')
ax[0].set_ylabel('y')
ax[0].plot(x, y, label='relu函数')# 2. 绘制 relu 导函数
x1 = torch.linspace(-10, 10, 100, requires_grad=True)
torch.relu(x1).sum().backward()ax[1].grid(True)
ax[1].set_title('relu导函数曲线图')
ax[1].set_xlabel('x1')
ax[1].set_ylabel('y1')
ax[1].plot(x1.detach().numpy(), x1.grad.detach().numpy(), color='red', label='relu导函数')# 添加图例
ax[0].legend()
ax[1].legend()plt.tight_layout()
plt.show()
2.2.4 LeakyReLU
Leaky ReLU是一种对 ReLU 函数的改进,旨在解决 ReLU 的一些缺点,特别是Dying ReLU 问题。Leaky ReLU 通过在输入为负时引入一个小的负斜率来改善这一问题。
公式:
Leaky ReLU 函数的定义如下:
其中,\alpha 是一个非常小的常数(如 0.01),它控制负半轴的斜率。这个常数 \alpha是一个超参数,可以在训练过程中可自行进行调整。
特征:
-
避免神经元死亡:通过在负半区域引入一个小的负斜率,这样即使输入值小于等于零,Leaky ReLU仍然会有梯度,允许神经元继续更新权重,避免神经元在训练过程中完全“死亡”的问题。
-
计算简单:Leaky ReLU 的计算与 ReLU 相似,只需简单的比较和线性运算,计算开销低。
缺点:
-
参数选择:\alpha 是一个需要调整的超参数,选择合适的\alpha 值可能需要实验和调优。
-
出现负激活:如果\alpha 设定得不当,仍然可能导致激活值过低。
函数绘制:
import torch
import torch.nn.functional as F
import matplotlib.pyplot as plt# 创建子图
fig, ax = plt.subplots(1, 2)# 1. 绘制 leaky_relu 函数
x = torch.linspace(-5, 5, 200, requires_grad=True)
slope = 0.03
y = F.leaky_relu(x, slope)ax[0].grid(True)
ax[0].set_title('leaky_relu函数曲线图')
ax[0].set_xlabel('x')
ax[0].set_ylabel('y')
ax[0].plot(x.detach().numpy(), y.detach().numpy(), label='leaky_relu函数')# 2. 绘制 leaky_relu 导函数
y.sum().backward() # 计算梯度ax[1].grid(True)
ax[1].set_title('leaky_relu导函数曲线图')
ax[1].set_xlabel('x')
ax[1].set_ylabel('y')
ax[1].plot(x.detach().numpy(), x.grad.detach().numpy(), color='red', label='leaky_relu导函数')# 添加图例
ax[0].legend()
ax[1].legend()plt.tight_layout()
plt.show()
4.2.5 Softmax
Softmax激活函数通常用于分类问题的输出层,它能够将网络的输出转换为概率分布,使得输出的各个类别的概率之和为 1。Softmax 特别适合用于多分类问题。
公式:
特征:
-
将输出转化为概率:通过Softmax,可以将网络的原始输出转化为各个类别的概率,从而可以根据这些概率进行分类决策。
-
概率分布:Softmax的输出是一个概率分布,即每个输出值\text{Softmax}(z_i)都是一个介于0和1之间的数,并且所有输出值的和为 1
-
突出差异:Softmax会放大差异,使得概率最大的类别的输出值更接近1,而其他类别更接近0。
-
在实际应用中,Softmax常与交叉熵损失函数Cross-Entropy Loss结合使用,用于多分类问题。在反向传播中,Softmax的导数计算是必需的。
缺点:
(1)数值不稳定性:在计算过程中,如果z_i的数值过大,e^{z_i}可能会导致数值溢出。因此在实际应用中,经常会对z_i进行调整,如减去最大值以确保数值稳定。
解释:
- z_i-\max(z)是一个非正数,那么e^{z_i - \max(z)}的值就位于0到1之间,有效避免了数值溢出。
- 这中调整不会改变Softmax的概率分布结果,因为从数学的角度讲相当于分子、分母都除以了e^{\max(z)}。
(2)难以处理大量类别:Softmax在处理类别数非常多的情况下(如大模型中的词汇表)计算开销会较大。
代码示例:
x = torch.Tensor([[0.8,10,2,4.5],[1.32,6.0,7.71,11]])
y = nn.Softmax(x)
print(y)x = torch.Tensor([[0.8,10,2,4.5],[1.32,6.0,7.71,11]])
y = F.softmax(x)
print(y)
4.4 选择激活函数
更多激活函数可以查看官方文档:
torch.nn — PyTorch 2.5 documentation
4.4.1 隐藏层
-
优先选ReLU;
-
如果ReLU效果较差,那么尝试其他激活函数,如Leaky ReLU等;
-
使用ReLU时注意神经元死亡问题, 避免出现过多神经元死亡;
-
不使用sigmoid,尝试使用tanh;
4.4.2 输出层
-
二分类问题选择sigmoid激活函数;
-
多分类问题选择softmax激活函数;
-
回归问题选择identity激活函数;