神经网络基础——激活函数的选择、参数初始化

一、神经网络

1、神经网络

        人工神经网络(Artificial Neural Network,即ANN也简称为神经网络(NN是一种模仿生物神经网络结构 和功能的计算模型。

 2、基本部分

        输入层:输入 x

        输出层:输出 y

        隐藏层:输入与输出之间所有层

3、特点

        同一层的神经元之间没有连接

        第 N 层的每个神经元和第 N-1层 的所有神经元相连(full connected),即全连接神经网络

        第 N-1层神经元的输出就是第 层神经元的输入

        每个连接都有一个权重值(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] 之间才会有比较好的效果

        通过上述导数图像,我们发现 导数 数值范围是 (0, 0.25) ,当输入 <-6 或者 >6 时, sigmoid 激活函数图像的 导数接近为 0 ,此时网络参数 更新 极其 缓慢 ,或者 无法更新
        一般来说, sigmoid 网络在 5 层之内  就会产生 梯度消失 现象。而且,该激活函数并不是以 0 为中心的,所以在实践中这种激活函数使用的很少。sigmoid 函数一般只用于 二分类 输出层
        2、tanh 激活函数

                公式:

                求导公式:

                函数图像:

        Tanh 函数将 输入 映射到 (-1, 1) 之间 ,图像以 0 为中心,在 0 点对称,当输入 大概<-3 或者
>3 时将被映射为 -1 或者 1。 导数值 范围 (0, 1) ,当输入的值大概 <-3 或者 > 3 时,其导数
近似 0。
        与 Sigmoid 相比,它是 以 0 为中心 的,且梯度相对于sigmoid大,使得其收敛速度要比
Sigmoid ,减少迭代次数。然而,从图中可以看出,Tanh 两侧的导数也为 0,同样会造成
梯度消失。
        若使用时可在 隐藏层 使用 tanh函数 ,在 输出层使用sigmoid函数
        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. 二分类 问题选择 sigmoid激活函数
        2. 多分类 问题选择 softmax激活函数
        3. 回归 问题选择 identity 激活函数

三、参数初始化

        1、均匀分布 初始化

        权重参数初始化从区间均匀随机取值,即在(\frac{-1}{\sqrt{d}}\frac{1}{\sqrt{d}})均匀分布中生成当前神经元的权重(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(方差)=  \sqrt{\frac{2}{input}}

        均匀分布HE初始化:从 [ -limit,limit ] 的均匀分布中抽取样本,limit =  \sqrt{\frac{6}{input}}

        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(方差)=  \sqrt{\frac{2}{input+output}}

        均匀分布Xavier初始化:从 [ -limit,limit ] 的均匀分布中抽取样本,limit =  \sqrt{\frac{6}{input+output}}

        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 构建网络模型时,每个网络层的参数都有默认的初始化方法,优先选择
kaming的初始化、xavier初始化方式。

四、网络搭建 和 参数计算

在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 out

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)
    # 查看模型参数
    for name,parameter in my_model.named_parameters():
        print(name,parameter)

输出结果:

 神经网络的优缺点

 优点
    1. 精确度高、性能好、效果好
    2. 拟合任意非线性的关系
    3. 框架多,无需自己造轮子

 缺点
    1. 黑箱,可解释性差
    2. 网络结构复杂,超参数多(超参数需要人工设置)
    3. 需要大量的数据进行训练,训练时间长,对算力有较高要求
    4. 小数据集容易过拟合

  • 32
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值