文章目录
本文是笔者为了完成毕业设计而进行学习的一个个人学习日记
图片和链接均源自网络,侵删
感知机模型
感知机模型(Perceptron Model) 是神经元的建模,包含输入、计算和输出。通常有多个输入值 x 0 , x 1 , … , x n x_0,x_1,\dots,x_n x0,x1,…,xn,对应的权重分别是 w 0 , w 1 , … , w n w_0,w_1,\dots,w_n w0,w1,…,wn,激活函数为 f f f,输出输出值为 y y y。
全连接神经网络详解:https://blog.csdn.net/ShiningLeeJ/article/details/126676581
如上图所示,对于这样一个简单的感知机,输出
y
y
y的计算公式为:
y
=
f
(
∑
i
=
0
n
w
i
x
i
)
y=f(\sum_{i=0}^nw_ix_i)
y=f(i=0∑nwixi)
激活函数
激活函数(Activation Function) 是一种添加到人工神经网络中的函数,其主要作用是对所有的隐藏层和输出层添加一个非线性的操作,使得神经网络的输出更为复杂、表达能力更强。
常见的激活函数有Sigmoid函数、Tanh函数、ReLU函数、Softmax函数等,分别在不同的场合有自己的作用和优缺点。
常用的激活函数合集(详细版):https://blog.csdn.net/caip12999203000/article/details/127067360
Sigmoid函数
Sigmoid函数是一个非常常见的激活函数,其值域为[0, 1],非常适合作为模型的输出函数用于输出一个(0, 1)范围内的概率值,可用于将预测概率作为输出的模型,比如用于表示二分类的类别或者用于表示置信度。
S
i
g
m
o
i
d
函数的公式:
f
(
x
)
=
1
1
+
e
−
x
,导函数:
f
′
(
x
)
=
e
−
x
(
1
+
e
−
x
)
2
=
(
1
−
f
(
x
)
)
Sigmoid函数的公式:f(x)=\frac{1}{1+e^{-x}},导函数:f'(x)=\frac{e^{-x}}{(1+e^{-x})^2}=(1-f(x))
Sigmoid函数的公式:f(x)=1+e−x1,导函数:f′(x)=(1+e−x)2e−x=(1−f(x))
然而,其导数的最大值只有0.25,且在 ∣ x ∣ > 5 |x|>5 ∣x∣>5时导数值就几乎接近于0。这种情况会导致训练过程中神经元处于饱和状态,权重几乎得不到更新,称为梯度消失。
在PyTorch中,Sigmoid函数的调用方式为:
import torch.nn.functional as F
output = F.sigmoid(input)
Tanh函数
Tanh函数的输出是S型曲线,具备打破网络层与网络层之间的线性关系,可以把网络层输出非线形地映射到 (−1, 1) 区间里。其具有很多神经网络所钟爱的特征。它完全可微分,反对称,且对称中心在原点。在一般的二元分类问题中,tanh 函数用于隐藏层,而 sigmoid 函数用于输出层,但这并不是固定的,需要根据特定问题进行调整。
T
a
n
h
函数的公式:
f
(
x
)
=
e
x
−
e
−
x
e
x
+
e
−
x
,导函数:
f
′
(
x
)
=
4
(
e
x
+
e
−
x
)
2
=
1
−
[
f
(
x
)
]
2
Tanh函数的公式:f(x)=\frac{e^x-e^{-x}}{e^x+e^{-x}},导函数:f'(x)=\frac4{(e^x+e^{-x})^2}=1-[f(x)]^2
Tanh函数的公式:f(x)=ex+e−xex−e−x,导函数:f′(x)=(ex+e−x)24=1−[f(x)]2
在分类任务中,Tanh函数逐渐取代Sigmoid函数作为标准的激活函数。可是当输入较大或较小时,其输出几乎是平滑的且梯度较小,不利于权重更新。此外,Tanh函数也存在梯度消失的问题。
在PyTorch中,Tanh函数的调用方式为:
import torch.nn.functional as F
output = F.tanh(input)
ReLU函数
ReLU函数在正输入时是线性的,收敛和计算速度快,并且由于正输入时导数为1,能够完整传递梯度,不存在梯度消失的问题,是使用最频繁的激活函数。
R
e
L
U
函数的公式:
f
(
x
)
=
{
x
,
x
≥
0
0
,
x
<
0
,导函数:
f
′
(
x
)
=
{
1
,
x
≥
0
0
,
x
<
0
ReLU函数的公式:f(x)=\begin{cases} x,& x\ge0 \\ 0,& x<0 \end{cases} ,导函数:f'(x)=\begin{cases} 1,& x\ge0 \\ 0,& x<0 \end{cases}
ReLU函数的公式:f(x)={x,0,x≥0x<0,导函数:f′(x)={1,0,x≥0x<0
ReLU的输入值为负的时候,输出始终为0,其一阶导数也始终为0,会导致神经元不能更新参数,这种现象叫做“Dead Neuron”。
在PyTorch中,ReLU函数的调用方式为:
import torch.nn.functional as F
output = F.relu(input)
Softmax函数
由于使用梯度无法求导,因此导函数图像是一个
y
=
0
y=0
y=0的直线。Softmax函数在零点不可微,在负输入的梯度为0,因此在该区域权重不会再反向传播期间更新,会产生永不激活的神经元。
S
o
f
t
m
a
x
函数的公式:
f
(
x
)
=
e
x
i
∑
i
=
0
n
e
x
i
Softmax函数的公式:f(x)=\frac{e^{x_i}}{\sum_{i=0}^ne^{x_i}}
Softmax函数的公式:f(x)=∑i=0nexiexi
由于预测结果总是转化为非负数,并且结果概率之和等于1,故很适合配合交叉熵损失函数做多分类模型。
在PyTorch中,Softmax函数的调用方式为:
import torch.nn.functional as F
output = F.softmax(input)
损失函数
损失函数(Loss Function)是用于计算结果与答案之间的差异的函数,深度学习是一种有监督的学习,在计算结果后,需要与标签(label)进行对比得到计算结果与答案之间的差异,称为损失(loss)。常见的损失函数如均方差损失函数、平均绝对误差损失函数、交叉熵损失函数等。
常用的损失函数合集:https://blog.csdn.net/caip12999203000/article/details/127307395
【深度学习】损失函数详解:https://blog.csdn.net/LogosTR_/article/details/126366482
均方差损失函数(MSE Loss)
均方差损失函数属于是一个万金油的损失函数,既可以处理分类问题,也可以处理回归问题。求的是预测值与真实值差的平均平方。
M
S
E
=
1
n
∑
i
=
0
n
(
y
i
−
y
^
i
)
2
MSE=\frac1n\sum_{i=0}^n(y_i-\hat{y}_i)^2
MSE=n1i=0∑n(yi−y^i)2
在PyTorch中均方差损失函数的调用方式为:
import torch
loss_function = torch.nn.MSELoss(reduction='mean')
平均绝对误差损失函数(L1 Loss)
指预测值和真实值之间距离平均值。
M
A
E
=
1
n
∑
i
=
0
n
∣
y
i
−
y
^
i
∣
MAE=\frac1n\sum_{i=0}^n|y_i-\hat{y}_i|
MAE=n1i=0∑n∣yi−y^i∣
在PyTorch中平均绝对误差损失函数的调用方式为:
import torch
loss_function = torch.nn.L1Loss(reduction='mean')
交叉熵损失函数(Cross Entropy Loss)
主要运用在多分类问题中。
C
r
o
s
s
E
n
t
r
o
p
y
=
−
y
×
log
(
y
^
)
CrossEntropy=-y\times\log(\hat y)
CrossEntropy=−y×log(y^)
在PyTorch中平交叉熵损失函数的调用方式为:
import torch
loss_function = torch.nn.CrossEntropyLoss()
前向传播
神经网络中数据的传播方式与感知机相同,被称为前向传播(Forward Propagation),但是会在累加时默认加上一个偏置值 b b b,即一个输入值为 b b b,权重值为1的输入节点。我们使用 b j l b^l_j bjl表示在第 l l l层的第 j j j个神经元的偏置值。
对于每条链接上的权重,我们使⽤ w j k l w^l_{jk} wjkl表示从第 ( l − 1 ) (l-1) (l−1)层的第 k k k个神经元,到第 l l l层的第 j j j个神经元的链接上的权重。例如,下面的 w 24 3 w^3_{24} w243表示第2层第4个神经元到第3层第二个神经元连接上的权重:
同理,我们可以用
a
j
l
a^l_j
ajl表示第
l
l
l层的第
j
j
j个神经元的激活值,那么,我们就可以用上一层的激活值
a
j
l
−
1
a^{l-1}_j
ajl−1和权重、偏置值等参数来表示
a
j
l
a^l_j
ajl(
f
f
f为激活函数):
a
j
l
=
f
(
∑
k
w
j
k
l
a
j
l
−
1
+
b
j
l
)
a^l_j=f(\sum_kw^l_{jk}a^{l-1}_j+b^l_j)
ajl=f(k∑wjklajl−1+bjl)
反向传播与优化器
在前向传播算出预测值之后,我们要使用损失函数得到的损失值反过来更新参数,即反向传播(Back Propagation)。一般情况下,使用的方法是梯度下降法,即权值和偏置矢量的修正值正比于当前位置上损失值的梯度,得到 Δ w j k = ∂ E ∂ w j k \Delta w_{jk}=\frac{\partial E}{\partial w_{jk}} Δwjk=∂wjk∂E和 Δ b j = ∂ E ∂ b j \Delta b_{j}=\frac{\partial E}{\partial b_{j}} Δbj=∂bj∂E。对于一个简单的单隐藏层FCNN,根据链式求导法则,可以得到权重的更新公式。在PyTorch中,我们通过选择不同的**优化器(Optimizer)**来实现,不同的优化器会通过loss值,用不同的方式调整模型参数的值。下面介绍不同的优化器及其优缺点。
传统梯度下降法
-
BGD(批量梯度下降法)
BGD在训练的时候选用所有的训练集进行计算,将全部的数据样本输入后进行一次更新,更新公式如下:
w k + 1 = w k − η ∗ ▽ f ( w k ) w^{k+1}=w^k-\eta*\bigtriangledown f(w^k) wk+1=wk−η∗▽f(wk)
其中, η \eta η为学习率, ▽ f ( w k ) \bigtriangledown f(w^k) ▽f(wk)为模型参数更新梯度。这个方法的最大问题就是容易落入局部最优点(local minimum)或者鞍点(saddle point)。落入后,会导致梯度为0,无法继续更新梯度。
-
SGD(随机梯度下降法)
为了解决BGD落入鞍点或局部最优点的问题,SGD引入了随机性,即将每个数据样本输入网络计算梯度后就进行一次更新,更新公式如下:
w k + 1 = w k − η ∗ ▽ f ( w k ; x i ; y i ) w^{k+1}=w^k-\eta*\bigtriangledown f(w^k;x^i;y^i) wk+1=wk−η∗▽f(wk;xi;yi)
由于要对每个样本都单独计算梯度,那么相当于引入了许多噪声,梯度下降时就会跳出鞍点和局部最优点。但要对每个样本都计算一次梯度就导致了时间复杂度较高,模型收敛较慢,而且loss和梯度会有大幅度的震荡。 -
MBGD(小批量梯度下降法)
MBGD在训练的时候只选择小部分数据进行训练,相当于缝合了SGD和BGD,将多个数据样本输入网络计算梯度后就进行一次更新,更新公式如下:
w k + 1 = w k − η ∗ ▽ f ( w k ; x i : i + b ; y i : i + b ) w^{k+1}=w^k-\eta*\bigtriangledown f(w^k;x^{i:i+b};y^{i:i+b}) wk+1=wk−η∗▽f(wk;xi:i+b;yi:i+b)
MBGD同时解决了两者的缺点,使得参数更新更稳定更快速,这也是我们最常用的方法,在PyTorch中SGD类也是指的MBGD(当然可以自己设置特殊的batch size,就会退化为SGD或BGD)。但同时,它也继承了BGD和SGD的缺点:第一,随机噪声的引入使得权值更新方向不一定正确;第二,无法解决局部最优的问题。
以上三种传统梯度下降法的PyTorch代码调用如下:
'''
params(iterable)- 参数组
lr(float)- 初始学习率,可按需随着训练过程不断调整学习率
momentum(float)- 动量,通常设置为0.9,0.8
dampening(float)- dampening for momentum,暂时不了解其功能
weight_decay(float)- 权值衰减系数,也就是L2正则项的系数
nesterov(bool)- bool 选项,是否使用NAG(Nesterov accelerated gradient)
'''
torch.optim.SGD(params, lr=lr, momentum=0, dampening=0, weight_decay=0, nesterov=False)
自适应学习率方法
-
AdaGrad算法
AdaGrad算法是梯度下降法的改进算法,其优点是可以自适应学习率。该优化算法在较为平缓处学习速率大,有比较高的学习效率,在陡峭处学习率小,在一定程度上可以避免越过极小值点。公式较为复杂,PyTorch代码调用如下:
''' lr_decay(float)– 学习率衰减系数 ''' torch.optim.Adagrad(params, lr=0.01, lr_decay=0, weight_decay=0, initial_accumulator_value=0)
-
RMSProp算法
RMSProp简单修改了Adagrad方法,结合梯度平方的指数移动平均数来调节学习率的变化。与AdaGrad不同,其更新不会让学习率单调变小。
''' alpha(float)- 平滑常数 eps(float)- 加到分母中防止为0 centered(bool)- 如果为真,计算中心RMSProp,梯度通过其方差的估计进行归一化 ''' torch.optim.RMSprop(params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False)
-
Adam算法
Adam本质上是带有动量项的RMSProp,它利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。Adam的优点主要在于经过偏置校正后,每一次迭代学习率都有个确定范围,使参数比较稳定。
''' betas(tuple[float, float])- 用于计算梯度及其平方的运行平均值的系数 amsgrad(bool)- 是否使用论文 On the Convergence of Adam and Beyond 中该算法的AMSGrad变体 ''' torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)
Adam实现简单,计算高效,对内存需求少,很适合应用于大规模的数据及参数的场景。
个人神经网络学习日记:
神经网络学习日记(一)——神经网络基本概念
神经网络学习日记(二)——全连接神经网络及Pytorch代码实现
神经网络学习日记(三)——CNN卷积神经网络
神经网络学习日记(四)——RNN、LSTM、BiLSTM、GRU
神经网络学习日记(五)——数据加载器(DataLoader)的调用
由于是第一次写博客,不知道是否有侵犯其他人文章版权的问题,如果有请务必联系我,谢谢!