自学内容网 自学内容网

【神经网络基础】

目录

一、神经网络的构成

1.1什么是神经网络?

1.2 激活函数

1.2.1 Sigmoid

1.2.2 Tanh

1.2.3 ReLU

1.2.4 softmax

1.2.5 其他激活函数

1.2.6 选择激活函数

1.3 参数初始化

1.4 模型构建

二、损失函数

2.1 分类问题

2.1.1多分类(多分类交叉熵/softmax损失)

2.1.2 二分类(二分类交叉熵/sigmoid损失)

2.2 回归问题

2.2.1 MSE(L2 loss)

2.2.2 MAE(L1 loss)

2.2.3 Smooth L1

三、优化方法

3.1 反向传播

3.2 梯度下降的优化方法 

3.2.1 指数加权平均

3.2.2 动量算法Momentum

 3.2.3 AdaGrad

 3.2.4 RMSProp

3.2.5 Adam 

3.3 学习率衰减 

3.3.1 等间隔衰减

3.3.2 指定间隔衰减

3.3.3 指数衰减

四、正则化

4.1 Dropout正则化

4.2 批量归一化


一、神经网络的构成

1.1什么是神经网络?

人工神经网络( Artificial Neural Network, 简写为ANN)也简称为神经网络(NN),是一种模仿生物神经网络结构和功能的计算模型。由神经元(加权和 + 激活函数)构成
神经网络中信息只向一个方向移动,即从输入节点向前移动,通过隐藏节点,再向输出节点移动。
其中的基本部分是 :
1. 输入层 : 即输入 x 的那一层
2. 输出层 : 即输出 y 的那一层
3. 隐藏层 : 输入层和输出层之间都是隐藏层
特点是:
同一层的神经元之间没有连接。
N 层的每个神经元和第 N-1 层 的所有神经元相连(这就是 full connected 的含义 ) ,这就是全连接神经网络。
N-1 层神经元的输出就是第 N 层神经元的输入。
每个连接都有一个权重值( w 系数和 b 系数)。

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

常用的激活函数 -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] 中的均匀分布中抽取样本, limitsqrt(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)!