神经网络基础

发布于:2025-06-17 ⋅ 阅读:(23) ⋅ 点赞:(0)

一、神经网络介绍

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

W_{ij}^{new}=W_{ij}^{old}-\eta \frac{\partial E}{\partial w_{ij}}

梯度下降三大概念:①epoch:训练次数/轮次;②batch_size:每次迭代需要的样本数;③iteration:每次训练需要迭代多少次(样本数/batch_size,不能整除时结果需要+1)

正向传播和反向传播算法:注意细节,在反向传播时的梯度连乘问题,反向传播的路径不只一条。

2.梯度下降的优化方式

指数加权平均思想

Ⅰ.站在梯度角度

BGD批量梯度下降(准确但是慢)、SGD随机梯度下降(快但是不稳定),所以出现MBGD小批量梯度下降(快且稳定)。

momentum动量法:当前梯度是指数移动加权平均梯度

                                结合当前梯度*系数+历史加权平均:S_{t}=\left ( 1-\beta \right )*G_{t}+\beta *S_{t}-1

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()


网站公告

今日签到

点亮在社区的每一天
去签到