一、神经网络
1、神经网络
人工神经网络(Artificial Neural Network,即ANN)也简称为神经网络(NN)是一种模仿生物神经网络结构 和功能的计算模型。
2、基本部分
输入层:输入 x
输出层:输出 y
隐藏层:输入与输出之间所有层
3、特点
同一层的神经元之间没有连接
第 N 层的每个神经元和第 N-1层 的所有神经元相连(full connected),即全连接神经网络
第 N-1层神经元的输出就是第 N 层神经元的输入
每个连接都有一个权重值(w系数和b系数)
二、激活函数
用于对每层的输出数据进行变换,进而为整个网络注入了非线性因素。此时, 神经网络就可以拟合各种曲线
1、sigmoid 激活函数
公式:
求导公式:
绘制函数图像:
import torch
import matplotlib.pyplot as plt# 函数图像
x = torch.linspace(-20,20,1000)
# 输入值x 通过 sigmoid函数 转换成 激活值y
y = torch.sigmoid(x)# 创建画布、坐标轴
plt.plot(x,y)
plt.grid()
plt.show()# 导数图像
x = torch.linspace(-20,20,1000,requires_grad=True)
# 自动微分
torch.sigmoid(x).sum().backward()plt.plot(x.detach(),x.grad)
plt.grid()
plt.show()
sigmoid 函数可以将任意的输入映射到 (0, 1) 之间,当输入的值大致在 <-6 或者 >6 时,意味着输入任何值 得到的激活值都是差不多的,这样会丢失部分信息。比如:输入 100 和输出 10000 经过 sigmoid 的激活值几乎都是等于 1 的,但是输入的数据之间相差 100 倍的信息就丢失了。
对于 sigmoid 函数而言,输入值在 [-6, 6] 之间输出值才会有明显差异,输入值在 [-3, 3] 之间才会有比较好的效果。
2、tanh 激活函数
公式:
求导公式:
函数图像:
3、ReLU 激活函数
公式: f (x) = max (0,x)
求导公式: f '(x) = 0 或 1
函数图像:
ReLU 激活函数将小于 0 的值映射为 0,而大于 0 的值则保持不变,它更加重视正信号,而忽略负信号,这种激活函数运算更为简单,能够提高模型的训练效率
当x<0时,ReLU导数为0,而当x>0时,则不存在饱和问题。所以,ReLU 能够在x>0时保持梯度不衰减,从而缓解梯度消失问题。然而,随着训练的推进,部分输入会落入小于0区域,导致对应权重无法更新。这种现象被称为“神经元死亡”
ReLU是目前最常用的激活函数。与sigmoid相比,ReLU的优势是:采用sigmoid函数,计算量大(指数运算),反向传播求误差梯度时,计算量相对大,而采用Relu激活函数,整个过程的计算量节省很多。 sigmoid函数反向传播时,很容易就会出现梯度消失的情况,从而无法完成深层网络的训练。 Relu会使一部分神经元的输出为0,这样就造成了网络的稀疏性,并且减少了参数的相互依存关系,缓解了过拟合问题的发生。
4、SoftMax 激活函数
softmax用于多分类过程中,它是二分类函数sigmoid在多分类上的推广,目的是将多分类的结果以概率的形式展现出来,公式如下:
Softmax 就是将网络输出的 logits 通过 softmax 函数,就映射成为(0,1)的值,而这些值的累和 为1(满足概率的性质),那么我们将它理解成概率,选取概率最大(也就是值对应最大的)节点,作为我们的预测目标类别。
scores = torch.tensor([0.2, 0.02, 0.15, 0.15, 1.3, 0.5, 0.06, 1.1, 0.05, 3.75])
probabilities = torch.softmax(scores,dim=0)
print(probabilities)
输出结果:
5、其他激活函数
6、选择方法
对于 隐藏层
1. 优先选择 ReLU激活函数
2. 如果ReLu效果不好,那么尝试其他激活,如Leaky ReLu等。
3. 如果使用了ReLU, 需要注意Dead ReLU问题, 避免出现大的梯度从而导致过多的神经元死亡。
4. 少用sigmoid激活函数,可以尝试使用tanh激活函数
对于 输出层
三、参数初始化
1、均匀分布 初始化
权重参数初始化从区间均匀随机取值,即在(,)均匀分布中生成当前神经元的权重(d为每个神经元的输入数量)
import torch
import torch.nn.functional as F
import torch.nn as nn
# 均匀分布 随机初始化
def test01():linear = nn.Linear(5, 3)# 从 0 ~ 1 均匀分布产生参数nn.init.uniform_(linear.weight)print(linear.weight.data)
2、正态分布 初始化
随机初始化从均值为0,标准差为1的高斯分布中取样,使用一些很小的值对参数W进行初始化
# 正态分布随机初始化
def test05():linear = nn.Linear(5, 3)nn.init.normal_(linear.weight, mean=0, std=1)print(linear.weight.data)
3、全0 初始化
将神经网络中的所有权重参数初始化为 0
# 全0初始化
def test03():linear = nn.Linear(5, 3)nn.init.zeros_(linear.weight)print(linear.weight.data)
4、全1 初始化
将神经网络中的所有权重参数初始化为 1
# 全1初始化
def test04():linear = nn.Linear(5, 3)nn.init.ones_(linear.weight)print(linear.weight.data)
5、固定值初始化
将神经网络中的所有权重参数初始化为 某个固定值
# 固定初始化
def test02():linear = nn.Linear(5, 3)nn.init.constant_(linear.weight, 5)print(linear.weight.data)
6、kaiming 初始化
正态化的HE初始化:均值为0,stddev(方差)=
均匀分布的HE初始化:从 [ -limit,limit ] 的均匀分布中抽取样本,limit =
input:输入神经元的个数
# kaiming 初始化
def test06():# kaiming 正态分布初始化linear = nn.Linear(5, 3)nn.init.kaiming_normal_(linear.weight)print(linear.weight.data)# kaiming 均匀分布初始化linear = nn.Linear(5, 3)nn.init.kaiming_uniform_(linear.weight)print(linear.weight.data)
7、xavier 初始化
正态化的Xavier初始化:均值为0,stddev(方差)=
均匀分布的Xavier初始化:从 [ -limit,limit ] 的均匀分布中抽取样本,limit =
input:输入神经元的个数,output:输出神经元的个数
# xavier 初始化
def test07():# xavier 正态分布初始化linear = nn.Linear(5, 3)nn.init.xavier_normal_(linear.weight)print(linear.weight.data)# xavier 均匀分布初始化linear = nn.Linear(5, 3)nn.init.xavier_uniform_(linear.weight)print(linear.weight.data)
四、网络搭建 和 参数计算
在pytorch中定义深度神经网络其实就是层堆叠的过程,继承自nn.Module,实现两个方法:
1. __init__方法中定义网络中的层结构,主要是全连接层,并进行初始化
2. forward方法,在实例化模型的时候,底层会自动调用该函数。该函数中可以定义学习率,
及数据传输方式。
构建如下图所示的神经网络模型:
编码设计如下:
1. 第1个隐藏层:权重采用标准化的 xavier初始化,激活函数 使用 sigmoid
2. 第2个隐藏层:权重采用标准化的 HE初始化,激活函数 用 relu
3. out 输出层:采用 softmax 做数据归一化
import torch
import torch.nn as nn
from torchsummary import summary # 计算模型参数,查看模型结构# 构建神经网络
class model(nn.Module):# 初始化属性值def __init__(self):# 调用父类的初始化属性值super(model,self).__init__()# 创建第一个隐藏层模型, 3个输入特征,3个输出特征self.layer1 = nn.Linear(3,3)# 初始化权重nn.init.xavier_normal_(self.layer1.weight)# 创建第二个隐藏层模型self.layer2 = nn.Linear(3,2)# 初始化权重nn.init.kaiming_normal_(self.layer2.weight)# 创建输出层模型self.out = nn.Linear(2,2)# 创建前向传播方法,自动执行forward()方法def forward(self,x):# 数据经过第一个线性层h1 = self.layer1(x)# 使用sigmoid激活函数h1 = torch.sigmoid(h1)# 数据经过第二个线性层h2 = self.layer2(h1)# 使用relu激活函数h2 = torch.relu(h2)# 数据经过输出层out = self.out(h2)# 使用softmax激活函数out = torch.softmax(out,dim=-1)return outif __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)# 查看模型参数for name,parameter in my_model.named_parameters():print(name,parameter)
输出结果:
神经网络的优缺点:
优点
1. 精确度高、性能好、效果好
2. 拟合任意非线性的关系
3. 框架多,无需自己造轮子
缺点
1. 黑箱,可解释性差
2. 网络结构复杂,超参数多(超参数需要人工设置)
3. 需要大量的数据进行训练,训练时间长,对算力有较高要求
4. 小数据集容易过拟合