一、神经网络介绍
1.概述
神经网络是深度学习计算模型,仿生生物学神经元构造
2.如何构造神经网络模型
① 由多个神经元构成,神经元内部分为以下两部分
加权求和(线性因素):内部状态值1,内部状态值偷梯度
激活函数(非线性因素):激活值1,激活值梯度
② 大体分为三个部分:输入层(输入特征值,一层,一个特征对应一个神经元)
隐藏层(提取复杂特征值,多层,每一层有多少神经元就提取多少特征)
输出层(输出模型预测结果)
③ 注意:1.同一层神经元相互之间没有连接;
2.全连结接受数据是二维的;
3.上层的输出是下层的输入;
4.每个连接都有一个权重;
5.隐藏层数决定了深度
3.激活函数
① 概念
神经网络中的激活函数(Activation Function)决定了神经元的输出特性,不同的激活函数在值域、导数(梯度)、计算效率等方面有显著差异。
② 作用:引入了非线性因素
③ 激活函数对比总结:
④ 函数详解
Ⅰ. 激活函数sigmoid
正负样本都考虑,把对应值转变到对应值范围:(0,1),导数范围:(0,0.25]
用途:可以用于隐藏层,但是主要用于输出层,解决二分类问题
缺点:梯度消失问题严重,非零中心化
#导包
import torch
from matplotlib import pyplot as plt
#解决中文乱码问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 创建画布(一行两列)
fig,ax = plt.subplots(1,2)
# 准备x轴的值
x = torch.linspace(-20,20,1000)
# 计算y轴的值
y = torch.sigmoid(x)
# 绘函数图
ax[0].plot(x,y)
ax[0].set_title('函数图像')
ax[0].grid()
# 自动微分计算导数值
x = torch.linspace(-20,20,1000,requires_grad=True)
torch.sigmoid(x).sum().backward()
# 绘导数图像
ax[1].plot(x.detach(),x.grad)
ax[1].set_title('导数图像')
ax[1].grid()
plt.show()
Ⅱ. 激活函数tanh
正负样本都考虑,把对应值转变到范围:(-1,1),导数范围:(0,1]
用途:可以用于输出层,但一般用于浅隐藏层
相比sigmoid:tanh引入了零中心化,但是梯度消失问题依然存在(比sigmoid有所缓解)
#导包
import torch
from matplotlib import pyplot as plt
#解决中文乱码问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 创建画布(一行两列)
fig,ax = plt.subplots(1,2)
# 准备x轴的值
x = torch.linspace(-20,20,1000)
# 计算y轴的值
y = torch.tanh(x)
# 绘函数图
ax[0].plot(x,y)
ax[0].set_title('函数图像')
ax[0].grid()
# 自动微分计算导数值
x = torch.linspace(-20,20,1000,requires_grad=True)
torch.tanh(x).sum().backward()
# 绘导数图像
ax[1].plot(x.detach(),x.grad)
ax[1].set_title('导数图像')
ax[1].grid()
plt.show()
Ⅲ. 激活函数relu
不考虑负样本,直接把正样本数据转变到对应值范围:[0,+∞),导数范围:{0,1}
用途:主要就是用于隐藏层,且是最常用的!!因为不考虑负样本导致部分神经元死亡,有效缓解过拟合问题。
相比sigmoid:有效缓解了梯度消失问题。
#导包
import torch
from matplotlib import pyplot as plt
#解决中文乱码问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 创建画布(一行两列)
fig,ax = plt.subplots(1,2)
# 准备x轴的值
x = torch.linspace(-20,20,1000)
# 计算y轴的值
y = torch.relu(x)
# 绘函数图
ax[0].plot(x,y)
ax[0].set_title('函数图像')
ax[0].grid()
# 自动微分计算导数值
x = torch.linspace(-20,20,1000,requires_grad=True)
torch.relu(x).sum().backward()
# 绘导数图像
ax[1].plot(x.detach(),x.grad)
ax[1].set_title('导数图像')
ax[1].grid()
plt.show()
Ⅳ.激活函数softmax
值范围:(0,1),所有概率和为1,导数范围:复杂
用途:主要用于输出层,解决多分类问题
注意:因为所有的概率和为1,最后选择其中最大概率作为预测结果
import torch
scores = torch.tensor([0.2,0.02,0.15,1.3,0.5,0.06,1.1,0.05,3.75])
#计算softmax结果,一般建议结果按行概率和为1
result = torch.softmax(scores,dim=0)
#此处因为只有1个维度,所以dim给值为0默认为一个维度,而不区分行列
print(result)
print(result.sum())
print('----------------------------------')
scores = torch.tensor([[0.3,0.24,2,-1.1],[0.2,0.6,1.2,1.4]])
#举例按列概率和为1
result = torch.softmax(scores,dim=0)
print(result)
结果:
4.参数初始化
Ⅰ. 随机均匀初始化
import torch
# 创建一个隐藏层
linear1 = torch.nn.Linear(5, 3)
# 随机初始化
torch.nn.init.uniform_(linear1.weight)
print(linear1.weight)
print(linear1.bias)
Ⅱ. 正态分布初始化(注意:传入均值和标准差)
# 创建一个隐藏层
linear1 = torch.nn.Linear(5, 3)
# 随机初始化
torch.nn.init.normal_(linear1.weight, mean=0, std=1)
print(linear1.weight)
print(linear1.bias)
Ⅲ. 全0初始化
# 创建一个隐藏层
linear1 = torch.nn.Linear(5, 3)
# 随机初始化
torch.nn.init.zeros_(linear1.weight)
print(linear1.weight)
print(linear1.bias)
Ⅳ. 全1初始化
# 创建一个隐藏层
linear1 = torch.nn.Linear(5, 3)
# 随机初始化
torch.nn.init.ones_(linear1.weight)
print(linear1.weight)
print(linear1.bias)
Ⅴ. 固定值初始化(注意:传入固定值)
# 创建一个隐藏层
linear1 = torch.nn.Linear(5, 3)
# 随机初始化
torch.nn.init.constant_(linear1.weight, 2.6)
print(linear1.weight)
print(linear1.bias)
Ⅵ. kaiming初始化(注意:正态分布不需均值,标准差)
# 创建一个隐藏层
linear1 = torch.nn.Linear(5, 3)
# 随机初始化
torch.nn.init.kaiming_normal_(linear1.weight)
print(linear1.weight)
print(linear1.bias)
print('----------------------------')
# 创建一个隐藏层
linear1 = torch.nn.Linear(5, 3)
# 随机初始化
torch.nn.init.kaiming_uniform_(linear1.weight)
print(linear1.weight)
print(linear1.bias)
Ⅶ. xavier初始化(注意:正态分布不需要均值,标准差)
# 创建一个隐藏层
linear1 = torch.nn.Linear(5, 3)
# 随机初始化
torch.nn.init.xavier_normal_(linear1.weight)
print(linear1.weight)
print(linear1.bias)
print('----------------------------')
# 创建一个隐藏层
linear1 = torch.nn.Linear(5, 3)
# 随机初始化
torch.nn.init.xavier_uniform_(linear1.weight)
print(linear1.weight)
print(linear1.bias)
5.搭建神经网络模型
步骤:1.自定义模型类继承Module类
2.重写Init魔法方法和forward前向传播方法
3.创建模型对象并使用模型对象
# 导包
from torch.nn import Module, Linear
import torch
#需要安装torchsummary
from torchsummary import summary
# TODO 1.自定义模型类继承Module类
class My_Model(Module):
# TODO 2.重写init魔法方法和forward前向传播方法
def __init__(self, *args, **kwargs):
# 1.调用父类的init初始化方法
super().__init__(*args, **kwargs)
# TODO 定义神经网络结构
self.linear1 = Linear(3, 3)
self.linear2 = Linear(3, 2)
self.out = Linear(2, 2)
# 3.参数初始化(生成权重矩阵和偏置矩阵)
# 隐藏层初始化权重矩阵
torch.nn.init.xavier_normal_(self.linear1.weight)
torch.nn.init.kaiming_normal_(self.linear2.weight)
# 隐藏层初始化偏置矩阵
torch.nn.init.zeros_(self.linear1.bias)
torch.nn.init.zeros_(self.linear2.bias)
def forward(self, x):
# TODO 前向传播计算(每层都是加权求和+激活函数)
x = torch.sigmoid(self.linear1(x))
x = torch.relu(self.linear2(x))
# 此处-1代表最后一维, 也就是按照每个样本概率和为1.
x = torch.softmax(self.out(x), dim=-1)
# 返回结果
return x
# TODO 3.创建模型对象并使用模型对象
# 创建模型对象
model = My_Model() # 自动调用init魔法方法
# 准备数据集(正态分布数据)
torch.manual_seed(66)
data = torch.randn(5, 3) # 5个样本,3个特征
print(data)
# 把数据传入模型对象
output = model(data) # 自动调用forward方法
print(output)
print('============================================================')
# TODO summary()查看模型参数
summary(model, (3,), batch_size=5) # 第1层:12,第2层:8,第3层:6
print('============================================================')
# TODO 遍历查看模型名字和对应参数
for name, param in model.named_parameters():
print(f'参数名称: {name}, 参数值: {param}')
print('---------------------------------')
二、损失函数
概念:衡量模型参数质量的函数,根据损失函数计算损失值, 结合反向传播算法以及梯度下降算法实现参数更新。
分类损失函数:
① 多分类交叉熵损失函数
nn.CrossEntropyLoss(reduction='mean')
实现softmax激活值计算+损失计算
# 导包
import torch
# 准备真实值
# 方式1: 使用热编码方式
y_true = torch.tensor([[0, 1, 0], [0, 0, 1]], dtype=torch.float)
# 方式2: 使用正确值索引方式 1 2
# [[0, 1, 0], [0, 0, 1]]
# y_true = torch.tensor([1, 2], dtype=torch.int64)
# 准备预测值(开启自动微分)
y_pred = torch.tensor([[0.2, 0.6, 0.2], [0.1, 0.1, 0.8]], requires_grad=True, dtype=torch.float32)
# 计算损失
# 创建损失函数对象
criterion = torch.nn.CrossEntropyLoss()
# 计算损失
loss = criterion(y_pred, y_true)
# 打印损失
print(loss)
② 二分类交叉熵损失函数
nn.BCELoss(reduction='mean')
# 导包
import torch
# 准备真实值
# 方式1: 使用热编码方式
y_true = torch.tensor([0, 1, 0], dtype=torch.float)
# 方式2: 使用正确值索引方式 1 2
# [[0, 1, 0], [0, 0, 1]]
# y_true = torch.tensor([1, 2], dtype=torch.int64)
# 准备预测值(开启自动微分)
y_pred = torch.tensor([0.2, 0.6, 0.2], requires_grad=True, dtype=torch.float32)
# 计算损失
# 创建损失函数对象
criterion = torch.nn.BCELoss()
# 计算损失
loss = criterion(y_pred, y_true)
# 打印损失
print(loss)
回归损失函数:
# 导包
import torch
# TODO 0.准备数据
# 准备真实值
y_true = torch.tensor([2, 2, 2], dtype=torch.float32)
# 准备预测值
y_pred = torch.tensor([1.2, 1.7, 1.9], requires_grad=True)
① MAE平均绝对误差损失函数
nn.L1Loss()
计算平均绝对误差,
导数为-1/1, 或者0点不可导用0代替,
对异常样本效果比较好, 不会放大异常样本误差,
L1 正则化, 将特征权重置为0, 特征筛选
# TODO 1.MAE损失函数
# 创建MAE损失函数
mae_loss_func = torch.nn.L1Loss()
# 计算MAE损失
mae_loss = mae_loss_func(y_pred, y_true)
print('MAE损失:', mae_loss)
② MSE均方误差损失函数
nn.MSEloss()
计算均方误差
任意位置都有导数, loss越大导数越大, loss越小导数越小
对异常样本效果不好, 放大样本误差
L2正则化, 将特征权重接近0
# TODO 2.MSE损失函数
# 创建MSE损失函数
mse_loss_func = torch.nn.MSELoss()
# 计算MSE损失
mse_loss = mse_loss_func(y_pred, y_true)
print('MSE损失:', mse_loss)
③ Smooth L1损失函数
任意位置都有导数, [-1,1]导数减小, 小于-1或大于1导数为-1或1
对异常样本效果好, 不会产生梯度爆炸, 不容易跳过最低点
nn.SmoothL1loss()
特殊的huber loss,固定了超参数
# TODO 3.SmoothL1损失函数
# 创建SmoothL1损失函数
sl1_loss_func = torch.nn.SmoothL1Loss()
# 计算SmoothL1损失
sl1_loss = sl1_loss_func(y_pred, y_true)
print('SmoothL1损失:', sl1_loss)
三、网络模型优化方法
1.梯度下降公式和概念
公式:W1=W0-lr*grad
梯度下降三大概念:①epoch:训练次数/轮次;②batch_size:每次迭代需要的样本数;③iteration:每次训练需要迭代多少次(样本数/batch_size,不能整除时结果需要+1)
正向传播和反向传播算法:注意细节,在反向传播时的梯度连乘问题,反向传播的路径不只一条。
2.梯度下降的优化方式
指数加权平均思想
Ⅰ.站在梯度角度
BGD批量梯度下降(准确但是慢)、SGD随机梯度下降(快但是不稳定),所以出现MBGD小批量梯度下降(快且稳定)。
momentum动量法:当前梯度是指数移动加权平均梯度
结合当前梯度*系数+历史加权平均:
SGD+动量法: 核心思想就是指数加权平均优化梯度,能够追求更好的泛化性能。
Ⅱ.站在学习率角度
自适应
①adagrad:核心思想是梯度平方和(弊端是学习率过早衰减)
自动调整学习率, 初始训练时学习率大, 后期训练学习率小;新学习率=原始学习率 / 历史梯度平方。
适用于高维稀疏数据或文本数据
②RMSprop:核心思想就是基于Adagrad引入了指数加权平均(缓解学习率过早衰减)
自动调整学习率, 初始训练时学习率大, 后期训练学习率小;新学习率=原始学习率 / 历史指数加权平均梯度平方;避免adagrad优化方法的学习率下降过快, 导致模型震荡。
适用于高维稀疏数据或文本数据。
③adam:核心思想就是动量法+RMSprop
优先选择,适合大多数深度学习任务。
手动指定
①等间隔学习率衰减:训练次数(步长)相等, 学习率随着训练次数减小
scheduer = optim.lr_scheduler.StepLR(optimizer=optimizer, step_size=50, gamma=0.5)
②指定间隔学习率衰减:自定义训练次数(步长), 学习率随着训练次数减小
optim.lr_scheduler.MultiStepLR(optimizer=optimizer, milestones=[50, 100, 160], gamma=0.5, last_epoch=-1)
③指数学习率衰减:按指数减小, 前期减小快, 后期减小慢
optim.lr_scheduler.ExponentialLR(optimizer=optimizer, gamma=0.9)
3.正则化
概念
一种解决过拟合提高模型泛化能力的策略。
作用
训练模型时防止出现过拟合。
策略
①范数正则化
L1正则化和L2正则化。
②随机失活正则化(nn.Dropout(p=))
dropout层:以指定p概率让神经元随机失活;因为每批次训练时失活的神经元不是固定, 得到不同的子网络, 防止预测结果过分依赖某些神经元。
注意:在激活层之后创建dropout层;未失活的神经元输出除以(1-p);dropout在测试集上不生效。
切换训练模式:model.train()
切换测试模式:model.eval()
③批量归一正则化(BN层)
主要解决内部协变量偏移问题。
将输入特征减去均值除以标准差,使得数据均值0,方差1,避免内部协变量偏移。
对线性结果进行标准化处理,根据每批样本的均值和标准差计算标准化的值。
作用:正则化减少噪声影响导致的训练模型效果降低问题;加快模型收敛速度。
注意:在激活层前使用(卷积层后/线性层后),多数在计算机视觉领域使用,可以引入γ和β可学习参数, 不同层的样本分布在不同范围内(不同层使用的激活函数不同); 可以补回标准化丢失的信号。
API:nn.BatchNorm1d() nn.BatchNorm2d() nn.BatchNorm3d()