目录
[1.2 激活函数](#1.2 激活函数)
[1.2.1 Sigmoid](#1.2.1 Sigmoid)
[1.2.2 Tanh](#1.2.2 Tanh)
[1.2.3 ReLU](#1.2.3 ReLU)
[1.2.4 softmax](#1.2.4 softmax)
[1.2.5 其他激活函数](#1.2.5 其他激活函数)
[1.2.6 选择激活函数](#1.2.6 选择激活函数)
[1.3 参数初始化](#1.3 参数初始化)
[1.4 模型构建](#1.4 模型构建)
[2.1 分类问题](#2.1 分类问题)
[2.1.2 二分类(二分类交叉熵/sigmoid损失)](#2.1.2 二分类(二分类交叉熵/sigmoid损失))
[2.2 回归问题](#2.2 回归问题)
[2.2.1 MSE(L2 loss)](#2.2.1 MSE(L2 loss))
[2.2.2 MAE(L1 loss)](#2.2.2 MAE(L1 loss))
[2.2.3 Smooth L1](#2.2.3 Smooth L1)
[3.1 反向传播](#3.1 反向传播)
[3.2 梯度下降的优化方法](#3.2 梯度下降的优化方法)
[3.2.1 指数加权平均](#3.2.1 指数加权平均)
[3.2.2 动量算法Momentum](#3.2.2 动量算法Momentum)
[3.2.3 AdaGrad](#3.2.3 AdaGrad)
[3.2.4 RMSProp](#3.2.4 RMSProp)
[3.2.5 Adam](#3.2.5 Adam)
[3.3 学习率衰减](#3.3 学习率衰减)
[3.3.1 等间隔衰减](#3.3.1 等间隔衰减)
[3.3.2 指定间隔衰减](#3.3.2 指定间隔衰减)
[3.3.3 指数衰减](#3.3.3 指数衰减)
[4.1 Dropout正则化](#4.1 Dropout正则化)
[4.2 批量归一化](#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 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****函数一般只用于二分类的输出层。
python
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对称
-
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函数。
python
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,这样就造成了网络的稀疏性,并且减少了参数的相互依存关系,缓解了过拟合问题的发生。
python
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 选择激活函数
对于隐藏层:
-
优先选择ReLU激活函数
-
如果ReLu效果不好,那么尝试其他激活,如Leaky ReLu等。
-
如果你使用了ReLU, 需要注意一下Dead ReLU问题, 避免出现大的梯度从而导致过多的神经元死亡。
-
少用使用sigmoid激活函数,可以尝试使用tanh激活函数
对于输出层:
-
二分类问题选择sigmoid激活函数
-
多分类问题选择softmax激活函数
-
回归问题选择identity激活函数
1.3 参数初始化
①均匀分布初始化(对weight)
权重参数初始化从区间均匀随机取值。即在(-1/根号d,1/根号d)均匀分布中生成当前神经元的权重,其中d为每个神经元的输入数量
python
linear = nn.Linear(5, 3)#(in_features,out_features)
# 从0-1均匀分布产生参数
nn.init.uniform_(linear.weight)
print(linear.weight.data)

②正态分布初始化(对weight)
随机初始化从均值为0,标准差是1的高斯分布中取样,使用一些很小的值对参数W进行初始化
python
linear = nn.Linear(5, 3)
nn.init.normal_(linear.weight, mean=0, std=1)
print(linear.weight.data)

③全0初始化(对bias)
将神经网络中的所有权重参数初始化为0
python
linear = nn.Linear(5, 3)
nn.init.zeros_(linear.weight)
print(linear.weight.data)

④全1初始化(对bias)
将神经网络中的所有权重参数初始化为1
python
linear = nn.Linear(5, 3)
nn.init.ones_(linear.weight)
print(linear.weight.data)

⑤固定值初始化(对bais)
将神经网络中的所有权重参数初始化为某个固定值
python
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)
python
linear = nn.Linear(5, 3)
nn.init.kaiming_normal_(linear.weight)
print('kaiming正态分布:',linear.weight.data)

--均匀分布的he初始化
它从 [-limit,limit] 中的均匀分布中抽取样本, limit是 sqrt(6 / fan_in)
python
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))
python
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)) ```python 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损失)**   ```python # 分类损失函数:交叉熵损失使用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损失)**  ```python 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) 采用均方误差方式:  ```python 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) 平均绝对值误差:  ```python # 计算算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  ```python 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 批量归一化 