【神经网络基础】
目录
一、神经网络的构成
1.1什么是神经网络?
1.2 激活函数
解释:激活函数由于对每层的输出数据进行变换,进而为整个网络注入了非线性因素。此时,神经网路就可以拟合各种曲线。
1. 没有引入非线性因素的网络等价于使用一个线性模型来拟合
2. 通过给网络输出增加激活函数, 实现引入非线性因素, 使得网络模型可以逼近任意函数, 提升网络对复杂问题的拟合能力.
1.2.1 Sigmoid
常见的激活函数-sigmoid 激活函数 :(适用于二分类的输出层)
① sigmoid 函数可以将任意的输入映射到 (0, 1) 之间,当输入的值大致在 <-6 或者 >6 时,意味着输入任何值得到的激 活值都是差不多的,这样会丢失部分的信息。比如:输入 100 和输出 10000 经过 sigmoid 的激活值几乎都是等于 1 的,但是输入的数据之间相差 100 倍的信息就丢失了。
② 对于 sigmoid 函数而言,输入值在 [-6, 6] 之间输出值才会有明显差异,输入值在 [-3, 3] 之间才会有比较好的效果。
③ 通过上述导数图像,我们发现导数数值范围是 (0, 0.25),当输入 <-6 或者 >6 时,sigmoid 激活函数图像的导数接近 为 0,此时网络参数将更新极其缓慢,或者无法更新。
④ 一般来说, sigmoid 网络在 5 层之内就会产生梯度消失现象。而且,该激活函数并不是以 0 为中心的,所以在实践 中这种激活函数使用的很少。sigmoid函数一般只用于二分类的输出层。
import torch
import matplotlib.pyplot as plt
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'
plt.rcParams['font.sans-serif'] = ['SimHei'] # 选择中文字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 创建画布和坐标轴
_, axes = plt.subplots(1, 2)
# 函数图像
x = torch.linspace(-20, 20, 1000)
y = torch.sigmoid(x)
axes[0].plot(x, y)
axes[0].grid()
axes[0].set_title('sigmoid 函数图像')
# 导数图像
x = torch.linspace(-20, 20, 1000, requires_grad=True)
torch.sigmoid(x).sum().backward()
axes[1].plot(x.detach(), x.grad)
axes[1].grid()
axes[1].set_title('sigmoid导数图像')
plt.show()
1.2.2 Tanh
常见的激活函数 -tanh 激活函数:
-
隐藏层中要使用指数型激活函数时,就选择tanh,不要使用sigmoid
-
[-1,1],关于0对称
-
导数相对于sigmoid大,更新速度快,迭代次数少
-
x远离0点时,梯度为0,梯度消失/弥散
① Tanh 函数将输入映射到 (-1, 1) 之间,图像以 0 为中心,在 0 点对称,当输入 大概<-3 或者>3 时将被映射为 -1 或者 1。其导数值范围 (0, 1),当输入的值大概 <-3 或者 > 3 时,其导数近似 0。
② 与 Sigmoid 相比,它是以 0 为中心的,且梯度相对于sigmoid大,使得其收敛速度要比Sigmoid 快,减少迭代次数。然而,从图中可以看出,Tanh 两侧的导数也为 0,同样会造成梯度消失。
③ 若使用时可在隐藏层使用tanh函数,在输出层使用sigmoid函数。
import torch
import matplotlib.pyplot as plt
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'
plt.rcParams['font.sans-serif'] = ['SimHei'] # 选择中文字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 创建画布和坐标轴
_, axes = plt.subplots(1, 2)
# 函数图像
x = torch.linspace(-20, 20, 1000)
y = torch.tanh(x)
axes[0].plot(x, y)
axes[0].grid()
axes[0].set_title('Tanh 函数图像')
# 导数图像
x = torch.linspace(-20, 20, 1000, requires_grad=True)
torch.tanh(x).sum().backward()
axes[1].plot(x.detach(), x.grad)
axes[1].grid()
axes[1].set_title('Tanh 导数图像')
plt.show()
1.2.3 ReLU
- 隐藏层使用,最多
- 小于0 ,取值为0 ;大于0 ,本身
- 导数:小于0 ,取值为0 ;大于0 ,为1
- 大于0 :不会梯度消失
- 小于0:
- 当某一部分神经元输出为0,神经元死亡,缓解过拟合
- 当大部分神经元输出为0,从头开始或换激活函数leakyrelu
- 相对于sigmoid: 计算简单,计算量小(函数和求导)
① ReLU 激活函数将小于 0 的值映射为 0,而大于 0 的值则保持不变,它更加重视正信号,而忽略负信号,这种激活函数运算更为简单,能够提高模型的训练效率。
② 当x<0时,ReLU导数为0,而当x>0时,则不存在饱和问题。所以,ReLU 能够在x>0时保持梯度不衰减,从而缓解梯度消失问题。然而,随着训练的推进,部分输入会落入小于0区域,导致对应权重无法更新。这种现象被称为“神经元死亡” 。
③ReLU是目前最常用的激活函数。与sigmoid相比,RELU的优势是:采用sigmoid函数,计算量大(指数运算),反向传播求误差梯度时,计算量相对大,而采用Relu激活函数,整个过程的计算量节省很多。 sigmoid函数反向传播时,很容易就会出现梯度消失的情况,从而无法完成深层网络的训练。 Relu会使一部分神经元的输出为0,这样就造成了网络的稀疏性,并且减少了参数的相互依存关系,缓解了过拟合问题的发生。
import torch
import matplotlib.pyplot as plt
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'
plt.rcParams['font.sans-serif'] = ['SimHei'] # 选择中文字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 创建画布和坐标轴
_, axes = plt.subplots(1, 2)
# 函数图像
x = torch.linspace(-20, 20, 1000)
y = torch.relu(x)
axes[0].plot(x, y)
axes[0].grid()
axes[0].set_title('ReLU函数图像')
# 导数图像
x = torch.linspace(-20, 20, 1000, requires_grad=True)
torch.relu(x).sum().backward()
axes[1].plot(x.detach(), x.grad)
axes[1].grid()
axes[1].set_title('ReLU导数图像')
plt.show()
1.2.4 softmax
常用的激活函数-SoftMax 激活函数:
-
多分类输出层
-
将输出层的加权和(scores/logits)转换概率值,概率值之和是1
-
选择概率最大的作为结果
-
多分类的目标值:类别标注的热编码结果
softmax用于多分类过程中,它是二分类函数sigmoid在多分类上的推广,目的是将多分类的结果以概率的形式展现出来 。计算方法如下图所示:
Softmax 就是将网络输出的 logits 通过 softmax 函数,就映射成为(0,1)的值,而这些值的累和为1(满足概率的性质),那么我们将它理解成概率,选取概率最大(也就是值对应最大的)节点,作为我们的预测目标类别。
1.2.5 其他激活函数
1.2.6 选择激活函数
对于隐藏层:
1. 优先选择ReLU激活函数
2. 如果ReLu效果不好,那么尝试其他激活,如Leaky ReLu等。
3. 如果你使用了ReLU, 需要注意一下Dead ReLU问题, 避免出现大的梯度从而导致过多的神经元死亡。
4. 少用使用sigmoid激活函数,可以尝试使用tanh激活函数
对于输出层:
1. 二分类问题选择sigmoid激活函数
2. 多分类问题选择softmax激活函数
3. 回归问题选择identity激活函数
1.3 参数初始化
①均匀分布初始化(对weight)
权重参数初始化从区间均匀随机取值。即在(-1/根号d,1/根号d)均匀分布中生成当前神经元的权重,其中d为每个神经元的输入数量
linear = nn.Linear(5, 3)#(in_features,out_features)
# 从0-1均匀分布产生参数
nn.init.uniform_(linear.weight)
print(linear.weight.data)
②正态分布初始化(对weight)
随机初始化从均值为0,标准差是1的高斯分布中取样,使用一些很小的值对参数W进行初始化
linear = nn.Linear(5, 3)
nn.init.normal_(linear.weight, mean=0, std=1)
print(linear.weight.data)
③全0初始化(对bias)
将神经网络中的所有权重参数初始化为0
linear = nn.Linear(5, 3)
nn.init.zeros_(linear.weight)
print(linear.weight.data)
④全1初始化(对bias)
将神经网络中的所有权重参数初始化为1
linear = nn.Linear(5, 3)
nn.init.ones_(linear.weight)
print(linear.weight.data)
⑤固定值初始化(对bais)
将神经网络中的所有权重参数初始化为某个固定值
linear = nn.Linear(5, 3)
nn.init.constant_(linear.weight, 5)
print(linear.weight.data)
⑥kaiming 初始化,也叫做 HE 初始化
HE 初始化分为正态分布的 HE 初始化、均匀分布的 HE 初始化.
--正态化的he初始化
stddev = sqrt(2 / fan_in)
linear = nn.Linear(5, 3)
nn.init.kaiming_normal_(linear.weight)
print('kaiming正态分布:',linear.weight.data)
--均匀分布的he初始化
它从 [-limit,limit] 中的均匀分布中抽取样本, limit是 sqrt(6 / fan_in)
linear = nn.Linear(5, 3)
nn.init.kaiming_uniform_(linear.weight)
print('kaiming均匀分布:',linear.weight.data)
--fan_in 输入神经元的个数
⑦xavier 初始化,也叫做 Glorot初始化
该方法也有两种,一种是正态分布的 xavier 初始化、一种是均匀分布的 xavier 初始化.
--正态化的Xavier初始化
stddev = sqrt(2 / (fan_in + fan_out))
linear = nn.Linear(5, 3)
nn.init.xavier_normal_(linear.weight)
print('xavier正态分布:',linear.weight.data)
--均匀分布的Xavier初始化
[-limit,limit] 中的均匀分布中抽取样本, limit 是 sqrt(6 / (fan_in + fan_out))
linear = nn.Linear(5, 3)
nn.init.xavier_uniform_(linear.weight)
print('xavier均匀分布:',linear.weight.data)
--fan_in 是输入神经元的个数, fan_out 是输出的神经元个数
总结:
1.4 模型构建
在pytorch中定义深度神经网络其实就是层堆叠的过程,继承自nn.Module,实现两个方法:
① __init__方法中定义网络中的层结构,主要是全连接层,并进行初始化
② forward方法,在实例化模型的时候,底层会自动调用该函数。该函数中可以定义学习率,
为初始化定义的layer传入数据等。
编码设计:
1.第一个隐藏层:权重初始化采用标准化的xavier初始化激活函数使用sigmoid
2.第二个隐藏层:权重初始化采用标准化的He初始化激活函数采用relu
3.out输出层线性层,假若二分类,采用softmax做数据归一化
'''
神经网络搭建流程
一个继承--继承自nn.moudle
两个方法 --__init__方法 网络层
--forward方法 串联网络层
'''
import torch
import torch.nn as nn
from torchsummary import summary # 计算模型参数,查看模型结构, pip install torchsummary
# 创建神经网络模型类
class Model(nn.Module):
# 初始化属性值
def __init__(self):
super(Model, self).__init__() # 调用父类的初始化属性值
self.linear1 = nn.Linear(3, 3) # 创建第一个隐藏层模型, 3个输入特征,3个输出特征
nn.init.xavier_normal_(self.linear1.weight) # 初始化权
# 创建第二个隐藏层模型, 3个输入特征(上一层的输出特征),2个输出特征
self.linear2 = nn.Linear(3, 2)
# 初始化权重
nn.init.kaiming_normal_(self.linear2.weight)
# 创建输出层模型
self.out = nn.Linear(2, 2)
# 创建前向传播方法,自动执行forward()方法
def forward(self, x):
# 数据经过第一个线性层
x = self.linear1(x)
# 使用sigmoid激活函数
x = torch.sigmoid(x)
# 数据经过第二个线性层
x = self.linear2(x)
# 使用relu激活函数
x = torch.relu(x)
# 数据经过输出层
x = self.out(x)
# 使用softmax激活函数
# dim=-1:每一维度行数据相加为1
x = torch.softmax(x, dim=-1)
return x
if __name__ == "__main__":
# 实例化model对象
my_model = Model()
# 随机产生数据
my_data = torch.randn(5, 3)
print("mydata shape", my_data.shape)
# 数据经过神经网络模型训练
output = my_model(my_data)
print("output shape-->", output.shape)
# 计算模型参数
# 计算每层每个神经元的w和b个数总和
summary(my_model, input_size=(3,), batch_size=5)
# 查看模型参数
print("======查看模型参数w和b======")
for name, parameter in my_model.named_parameters():
print(name, parameter)
二、损失函数
2.1 分类问题
2.1.1多分类(多分类交叉熵/softmax损失)
# 分类损失函数:交叉熵损失使用nn.CrossEntropyLoss()实现。nn.CrossEntropyLoss()=softmax + 损失计算
def test():
# 设置真实值: 可以是热编码后的结果也可以不进行热编码
# y_true = torch.tensor([[0, 1, 0], [0, 0, 1]], dtype=torch.float32)
# 注意的类型必须是64位整型数据
y_true = torch.tensor([1, 2], dtype=torch.int64)
y_pred = torch.tensor([[0.2, 0.6, 0.2], [0.1, 0.8, 0.1]], dtype=torch.float32)
# 实例化交叉熵损失
loss = nn.CrossEntropyLoss()
# 计算损失结果
my_loss = loss(y_pred, y_true).numpy()
print('loss:', my_loss)
2.1.2 二分类(二分类交叉熵/sigmoid损失)
def test2():
# 1 设置真实值和预测值
# 预测值 是sigmoid输出的结果
y_pred = torch.tensor([0.6901, 0.5459, 0.2469], requires_grad=True)
y_true = torch.tensor([0, 1, 0], dtype=torch.float32)
# 2 实例化二分类交叉熵损失
criterion = nn.BCELoss()
# 3 计算损失
my_loss = criterion(y_pred, y_true).detach().numpy()
print('loss:', my_loss)
2.2 回归问题
2.2.1 MSE(L2 loss)
采用均方误差方式:
def test4():
# 1 设置真实值和预测值
y_pred = torch.tensor([1.0, 1.0, 1.9], requires_grad=True)
y_true = torch.tensor([2.0, 2.0, 2.0], dtype=torch.float32)
# 2 实例MSE损失对象
loss = nn.MSELoss()
# 3 计算损失
my_loss = loss(y_pred, y_true).detach().numpy()
print('myloss:', my_loss)
2.2.2 MAE(L1 loss)
平均绝对值误差:
# 计算算inputs与target之差的绝对值
def test3():
# 1 设置真实值和预测值
y_pred = torch.tensor([1.0, 1.0, 1.9], requires_grad=True)
y_true = torch.tensor([2.0, 2.0, 2.0], dtype=torch.float32)
# 2 实例MAE损失对象
loss = nn.L1Loss()
# 3 计算损失
my_loss = loss(y_pred, y_true).detach().numpy()
print('loss:', my_loss)
2.2.3 Smooth L1
def test5():
# 1 设置真实值和预测值
y_true = torch.tensor([0, 3])
y_pred = torch.tensor ([0.6, 0.4], requires_grad=True)
# 2 示例损失对象
loss = nn.SmoothL1Loss()
# 3 计算损失
my_loss = loss(y_pred, y_true).detach().numpy()
print('loss:', my_loss)
三、优化方法
3.1 反向传播(BP算法)
什么是反向传播?
利用损失函数ERROR,从后往前,结合梯度下降法,依次求各个参数的偏导,并进行参数更新
什么是前向传播?
指的是数据输入的神经网络中,逐层向前传输,一直运算到输出层为止
反向传播(BP算法):
3.2 梯度下降的优化方法
梯度下降优化算法中,可能会碰到以下情况:
1. 碰到平缓区域,梯度值较小,参数优化变慢
2. 碰到 “鞍点” ,梯度为 0,参数无法优化
3. 碰到局部最小值,参数不是最优
3.2.1 指数加权平均
指数移动加权平均则是参考各数值,并且各数值的权重都不同,距离越远的数字对平均数计算的贡献就越小(权重较小),距离越近则对平均数的计算贡献就越大(权重越大)。比如:明天气温怎么样,和昨天气温有很大关系,而和一个月前的气温关系就小一些。
计算公式可以用下面的式子来表示:
3.2.2 动量算法Momentum
梯度计算公式:Dt = β * St-1 + (1- β) * Wt
1. St-1 表示历史梯度移动加权平均值
2. Wt 表示当前时刻的梯度值
3. Dt 为当前时刻的指数加权平均梯度值
4. β 为权重系数
假设:权重 β 为 0.9,例如:
第一次梯度值:s1 = d1 = w1
第二次梯度值:d2=s2 = 0.9 * s1 + w2 * 0.1
第三次梯度值:d3=s3 = 0.9 * s2 + w3 * 0.1
第四次梯度值:d4=s4 = 0.9 * s3 + w4 * 0.1
梯度下降公式中梯度的计算,就不再是当前时刻 t 的梯度值,而是历史梯度值的指数移动加权平
均值。公式修改为:
W_t+1 = W_t - a * Dt
3.2.3 AdaGrad
AdaGrad 通过对不同的参数分量使用不同的学习率,AdaGrad 的学习率总体会逐渐减小。
其计算步骤如下:
1. 初始化学习率 α、初始化参数 θ、小常数 σ = 1e-6
2. 初始化梯度累积变量 s = 0
3. 从训练集中采样 m 个样本的小批量,计算梯度 g
4. 累积平方梯度 s = s + g ⊙ g,⊙ 表示各个分量相乘
3.2.4 RMSProp
RMSProp 优化算法是对 AdaGrad 的优化. 最主要的不同是,其使用指数移动加权平均梯度替换历史梯度的平方和。其计算过程如下:
1. 初始化学习率 α、初始化参数 θ、小常数 σ = 1e-6
2. 初始化参数 θ
3. 初始化梯度累计变量 s
4. 从训练集中采样 m 个样本的小批量,计算梯度 g
5. 使用指数移动平均累积历史梯度,公式如下:
3.2.5 Adam
3.3 学习率衰减
3.3.1 等间隔衰减
3.3.2 指定间隔衰减
3.3.3 指数衰减
四、正则化
4.1 Dropout正则化
4.2 批量归一化
原文地址:https://blog.csdn.net/weixin_55448492/article/details/145122725
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!