PyTorch学习笔记-4.PyTorch损失优化

4.PyTorch损失优化

4.1.权值初始化

4.1.1.梯度消失与爆炸

对于一个含有多层隐藏层的神经网络来说,当梯度消失发生时,接近于输出层的隐藏层由于其梯度相对正常,所以权值更新时也就相对正常,但是当越靠近输入层时,由于梯度消失现象,会导致靠近输入层的隐藏层权值更新缓慢或者更新停滞。这就导致在训练时,只等价于后面几层的浅层网络的学习。梯度爆炸与之相反。

例如下图的神经网络:

其中,

求导得:

从上式可以看出,损失函数对求导是由多个求导累乘的结果,对于其中的每个求导,如果此部分小于1,那么随着层数增多,求出的梯度更新信息将会以指数形式衰减,即发生了梯度消失,如果此部分大于1,那么层数增多的时候,最终的求出的梯度更新将以指数形式增加,即发生梯度爆炸。

 

代码实现:

# -*- coding: utf-8 -*-
import torch
import random
import numpy as np
import torch.nn as nn

def set_seed(seed=1):
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)

set_seed(3)  # 设置随机种子

class MLP(nn.Module):
    def __init__(self, neural_num, layers):
        super(MLP, self).__init__()

# 构建layers层,每层neural_num个神经元的神经网络
        self.linears = nn.ModuleList([nn.Linear(neural_num, neural_num, bias=Falsefor in range(layers)])
        self.neural_num = neural_num

    def forward(self, x):
        for (i, linear) in enumerate(self.linears):
            x = linear(x)

        return x

    def initialize(self):
        for in self.modules():
            if isinstance(m, nn.Linear):
                # 参数初始化,对每层的权重初始化为均值为0,标准差为1的正太分布。
                nn.init.normal_(m.weight.data)  

# 层数
layer_nums = 100
# 每层神经元个数
neural_nums = 256
# 批大小
batch_size = 16

# 构建MLP模型
net = MLP(neural_nums, layer_nums)
net.initialize()

inputs = torch.randn((batch_size, neural_nums))  # normal: mean=0, std=1

output = net(inputs)
print(output)

tensor([[nan, nan, nan,  ..., nan, nan, nan],

        [nan, nan, nan,  ..., nan, nan, nan],

        ...,

        [nan, nan, nan,  ..., nan, nan, nan],

        [nan, nan, nan,  ..., nan, nan, nan]], grad_fn=<ReluBackward0>)

 

可以看到最终输出的全部为nan,可以打印每层的标准差,查看每层的输出情况,以及最终出现nan层的标准差,在MLP类forward方法中,遍历时进行输出

代码实现:

def forward(self, x):
    for (i, linear) in enumerate(self.linears):
        x = linear(x)

        # 打印每层标准差
        print("layer:{}, std:{}".format(i, x.std()))
        if torch.isnan(x.std()):
            # 判断如果当前层的标准差已经是nan,则打印当前层并跳出循环
            print("output is nan in {} layers".format(i))
            break

    return x

layer:0, std:15.959932327270508

layer:1, std:256.6237487792969

    ...

layer:29, std:1.322983152787379e+36

layer:30, std:2.0786820453988485e+37

layer:31, std:nan

output is nan in 31 layers

tensor([[        inf, -2.6817e+38,         inf,  ...,         inf,

                 inf,         inf],

        [       -inf,        -inf,  1.4387e+38,  ..., -1.3409e+38,

         -1.9659e+38,        -inf],

        ...,

        [        inf,         inf,        -inf,  ...,        -inf,

                 inf,  1.7432e+38]], grad_fn=<MmBackward>)

 

可以看到上面在第31层时,输出以及很大或者很小,为何会出现这样的问题?

首先,对于方差有

若E(X)=0  ,则

又对于第一层隐藏层的第一个元素:

则这个元素对应的方差为:

标准差则为

可以看到,每向后传播一层,则标准差扩大倍,因此,当层数很多时会变为无穷大。

为了解决上述问题,可以设置让每层传播时的方差变为1,即

得:

所以,对于上面的代码,可以改造为每层的权重为均值0,标准差,这样就可以避免每层标准差成倍增长的问题。

代码实现:

修改MLP类initialize中初始化权重的方法

def initialize(self):
    for in self.modules():
        if isinstance(m, nn.Linear):
            nn.init.normal_(m.weight.data, std=np.sqrt(1/self.neural_num))

layer:0, std:0.9974957704544067

layer:1, std:1.0024365186691284

    ...

layer:98, std:1.1617802381515503

layer:99, std:1.2215303182601929

tensor([[-1.0696, -1.1373,  0.5047,  ..., -0.4766,  1.5904, -0.1076],

        [ 0.4572,  1.6211,  1.9659,  ..., -0.3558, -1.1235,  0.0979],

        ...,

        [-0.5871, -1.3739, -2.9027,  ...,  1.6734,  0.5094, -0.9986]],

       grad_fn=<MmBackward>)

 

4.1.2.常用初始化方法

对于之前手动进行权值初始化,PyTorch提供了一些常用的权值初始化方法

Xavier初始化

方差一致性:保持数据尺度维持在恰当范围,通常方差为1
激活函数:饱和函数,如Sigmoid, Tanh

方差的计算法则为:

  

其中,表示输入层的神经元数量,表示输出层的神经元数量。

代码实现:

在之前的代码基础上,首先在MLP类forward方法中,为每层添加sigmoid激活函数

def forward(self, x):
    for (i, linear) in enumerate(self.linears):
        x = linear(x)

# 添加sigmoid激活函数
        x = torch.sigmoid(x)

 

然后,修改MLP类initialize中初始化权重的方法为Xavier初始化

def initialize(self):
    for in self.modules():
        if isinstance(m, nn.Linear):
            # 权重初始化使用xavier均匀分布进行初始化
            nn.init.xavier_uniform_(m.weight.data)

layer:0, std:0.20717598497867584

layer:1, std:0.1237645372748375

    ...

layer:98, std:0.12034578621387482

layer:99, std:0.11722493171691895

tensor([[0.5740, 0.5291, 0.8039,  ..., 0.4145, 0.3551, 0.7414],

        [0.5740, 0.5291, 0.8039,  ..., 0.4145, 0.3551, 0.7414],

        ...,

        [0.5740, 0.5291, 0.8039,  ..., 0.4145, 0.3551, 0.7414]],

       grad_fn=<SigmoidBackward>)

 

可以看到标准差始终控制在0.12左右

Kaiming初始化

差一致性:保持数据尺度维持在恰当范围,通常方差为1
激活函数: ReLU及其变种

方差的计算法则为:

 对于ReLU变种:

其中,表示输入层的神经元数量,a为激活函数在负半轴的斜率

代码实现:

在之前的代码基础上,首先在MLP类forward方法中,每层激活函数改为relu

def forward(self, x):
    for (i, linear) in enumerate(self.linears):
        x = linear(x)
        # 使用relu激活函数
        x = torch.relu(x)

然后,修改MLP类initialize中初始化权重的方法为Kaiming初始化

def initialize(self):
    for in self.modules():
        if isinstance(m, nn.Linear):
            # 权重初始化使用kaiming正太分布进行初始化
            nn.init.kaiming_normal_(m.weight.data)

layer:0, std:0.826629638671875

layer:1, std:0.8786815404891968

    ...

layer:98, std:0.6579315066337585

layer:99, std:0.6668476462364197

tensor([[0.0000, 1.3437, 0.0000,  ..., 0.0000, 0.6444, 1.1867],

        [0.0000, 0.9757, 0.0000,  ..., 0.0000, 0.4645, 0.8594],

        ...,

        [0.0000, 1.1807, 0.0000,  ..., 0.0000, 0.5668, 1.0600]],

       grad_fn=<ReluBackward0>)

 

可以看到标准差始终控制在0.5-1左右

 

十种初始化方法

1. Xavie r均匀分布
2. Xavie r正态分布
3. Kaiming均匀分布
4. Kaiming正态分布
5. 均匀分布
6. 正态分布
7. 常数分布
8. 正交矩阵初始化
9. 单位矩阵初始化
10. 稀疏矩阵初始化

4.2.损失函数

4.2.1.损失函数概述

损失函数:衡量模型输出与真实标签的差异

常见的概念:

损失函数(Loss Function): ,即一个样本预测值与真实值差异

代价函数(Cost Function): ,即所有样本的差异的平均

目标函数(Objective Function): Regularization,即代价函数+正则项

通常将损失函数和代价函数统称为损失函数,使用代价函数的计算结果。

4.2.2.交叉熵损失函数

交叉熵 = 信息熵 + 相对熵

熵:亦称为信息熵,用来描述事件的不确定性,事件越不确定,熵越大

熵的计算公式:

自信息:,表示单个事件的不确定性

相对熵:用来衡量两个分布之间的差异

计算公式:

其中表示真实的分布,表示模型输出的分布

交叉熵:

对相对熵公式展开得:

pytorch-09.ipynb是一个使用PyTorch库进行深度学习实践的笔记本文件。PyTorch是一个基于Python的深度学习框架,它提供了方便简洁的API接口,使得深度学习模型的构建和训练变得更加容易。 在这个笔记本文件中,我推测可能包括以下内容: 1. 张量的基本概念和操作:张量是PyTorch中最基本的数据类型,类似于Numpy中的多维数组。这个笔记本可能介绍如何创建和操作张量,以及张量在深度学习中的应用。 2. 自动梯度计算:PyTorch通过自动梯度计算(Autograd)模块实现了计算图和反向传播。这个笔记本可能介绍如何使用PyTorch的autograd模块来计算张量的导数,并利用导数进行模型参数的更新。 3. 模型构建和训练:深度学习模型的构建和训练是PyTorch的核心功能。这个笔记本可能介绍如何使用PyTorch构建各种类型的神经网络模型(如全连接网络、卷积神经网络和循环神经网络)并进行训练。 4. 数据加载和预处理:在深度学习中,数据的加载和预处理是非常重要的一步。这个笔记本可能介绍如何使用PyTorch的数据加载器和数据转换工具进行数据的加载和处理。 5. 模型性能评估和调优:在实际应用中,评估模型性能和进行调优是不可或缺的步骤。这个笔记本可能介绍如何使用PyTorch进行模型性能的评估,并介绍一些常见的调优方法,如学习率调整、正则化和dropout等。 总之,这个笔记本文件可能提供一些关于PyTorch库的基本操作和深度学习模型构建的实践指南,帮助读者更好地理解和应用PyTorch进行深度学习任务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值