pytorch基础:模型的权值初始化与损失函数

一、权值初始化

在搭建好网络模型之后,一个重要的步骤就是对网络模型中的权值进行初始化。适当的权值初始化可以加快模型的收敛,而不恰当的权值初始化可能引发梯度消失或者梯度爆炸,最终导致模型无法收敛。

1.1 梯度消失与梯度爆炸
梯度消失:如果导数小于1,那么随着网络层数的增加,梯度更新信息会朝着指数衰减的方式减少这就是梯度消失。梯度消失时,越靠近输入层的参数w越是几乎纹丝不动。

梯度爆炸:在反向传播过程中需要对激活函数进行求导,如果导数大于1,那么随着网络层数的增加,梯度更新将会朝着指数爆炸的方式增加。梯度爆炸时,越是靠近输入层的参数w越是上蹿下跳。

二者问题问题都是因为网络太深,网络权值更新不稳定造成的。本质上是因为梯度反向传播中的连乘效应(小于1连续相乘多次)。

1.2 Xavier初始化
方差一致性原则:让每一个网络层输出值的方差尽量等于1 ,主要针对饱和激活函数,如sigmoid,tanh。

我们先尝试通过手动设置均匀分布来初始化:

    def initialize(self):
        for m in self.modules():
            # 判断这一层是否为线性层,如果为线性层则初始化权值
            if isinstance(m, nn.Linear):
               
                # 计算均匀分布的上限、下限
                 a = np.sqrt(6 / (self.neural_num + self.neural_num))
 
                 # 把a变换到 tanh,计算增益。观察数据输入到激活函数之后,标准差的变化
                 tanh_gain = nn.init.calculate_gain('tanh')
                 a *= tanh_gain
                # 均匀分布初始化权重
                 nn.init.uniform_(m.weight.data, -a, a)

这里用到一个函数nn.init.calculate_gain(nonlinearity,param=None)

作用:是计算激活函数的方差变化尺度,就是输入数据的方差除以经过激活函数之后的输出数据的方差。

  • nonlinearity:表示激活函数的名称,如tanh。
  • param: 表示激活函数的参数,如Leaky
    ReLU的negative_slop。

可以看到输出结果稳定在0.6左右:
在这里插入图片描述
我们再来直接调用Pytorch 提供的 Xavier 初始化方法

nn.init.xavier_uniform_(tensor, gain=1.0)

  def initialize(self):
        for m in self.modules():
            if isinstance(m, nn.Linear):
                # Xavier初始化权重
                tanh_gain = nn.init.calculate_gain('tanh')
                nn.init.xavier_uniform_(m.weight.data, gain=tanh_gain)

输出结果同上。

注意:2012年AlexNet出现之后,非饱和函数relu也用到了神经网络中,而Xavier初始化对于relu就不好使了,这会导致输出方差越来越大,层数多了依然会出现爆炸现象。如下图所示:
在这里插入图片描述

1.2Kaiming初始化

针对上述问题,何凯明等人就提出了针对Relu这种非饱和函数的Kaming初始化方法.
nn.init.kaiming_normal_(tensor, a=0, mode=‘fan_in’, nonlinearity=‘leaky_relu’)

    def initialize(self):
        for m in self.modules():
            if isinstance(m, nn.Linear):
 
                #Pytorch提供的初始化方法
                nn.init.kaiming_normal_(m.weight.data)
  

输出结果表明,Relu输出梯度爆炸问题得到了解决
在这里插入图片描述

1.4 十种权重初始化方法

Pytorch中提供了许多权重初始化方法,可以分为下面四大类

  • 针对饱和激活函数(sigmoid, tanh):Xavier均匀分布, Xavier正态分布

  • 针对非饱和激活函数(relu及变种):Kaiming均匀分布, Kaiming正态分布

  • 三个常用的分布初始化方法:均匀分布,正态分布,常数分布 三个特殊的矩阵初始化方法:正交矩阵初始化,单位矩阵初始化,稀疏矩阵初始化

2.损失函数

2.1损失函数初步介绍

损失函数: 衡量模型输出与真实标签的差异.而我们谈损失函数的时候,往往会有三个概念:损失函数,代价函数,目标函数.

函数名定义
损失函数(Loss Function)计算一个样本的模型输出与真实标签的差异Loss = f(y^,y)
代价函数 (Cost Function)计算整个样本的模型输出与真实标签的差异,是所有样本损失函数的平均值 在这里插入图片描述
目标函数(objective Function)代价函数加上正则项.实际上就直接说成损失函数

2.2交叉熵损失CrossEntropyLoss

nn.CrossEntropyLoss
function:nn.LogSoftmax()与nn.NULLLoss()结合,进行交叉熵计算
在这里插入图片描述

主要参数:

weight:各类别的loss设置权限
ignore_index:忽略某个类别
reduction:计算模式,可为none/sum/mean
none:逐个元素计算
sum:返回所有元素求和,返回标量
mean:加权平均,返回标量(默认)
 

在这里插入图片描述
注意:使用nn.LogSoftmax()将概率归一化,应为交叉熵损失函数一般用在分类任务当中,而分类任务通常需要计算两个输出的概率值,所以交叉熵损失函数用来衡量两个概率分布之间的差异,交叉熵值越低,说明两个概率分布越近。

熵之间的关系

熵:用来描述整个概率分布的不确定性,熵越大,不确定性越高

信息熵 :自信息用于描述单个事件的不确定性,信息熵就是求自信息的期望

相对熵:也被称为 KL 散度,用于衡量两个分布的相似性(距离)

交叉熵 = 信息熵 + 相对熵

优化交叉熵:等价于优化相对熵

这里是引用

在这里插入图片描述

# 构建虚拟数据
 
# 这里就是模型预测的输出, 这里是两个类,可以看到模型输出是数值,我们得softmax一下转成分布
inputs = torch.tensor([[1, 2], [1, 3], [1, 3]], dtype=torch.float)
# 标签。这里的类型必须是long, 两个类0和1
target = torch.tensor([0, 1, 1], dtype=torch.long)
 
# ---------------- CrossEntropy loss: reduction ----------------
 
    # 三种模式的损失函数
    loss_f_none = nn.CrossEntropyLoss(weight=None, reduction='none')
    loss_f_sum = nn.CrossEntropyLoss(weight=None, reduction='sum')
    loss_f_mean = nn.CrossEntropyLoss(weight=None, reduction='mean')
 
    # forward
    loss_none = loss_f_none(inputs, target)
    loss_sum = loss_f_sum(inputs, target)
    loss_mean = loss_f_mean(inputs, target)
 
    # view
    print("Cross Entropy Loss:\n ", loss_none, loss_sum, loss_mean)

reslt shown below:
在这里插入图片描述
路人贾的手写算法:

# --------------------------------- compute by hand
 
    idx = 0
 
    input_1 = inputs.detach().numpy()[idx]      # [1, 2]
    target_1 = target.numpy()[idx]              # [0]
 
    # 第一项
    x_class = input_1[target_1]
 
    # 第二项
    sigma_exp_x = np.sum(list(map(np.exp, input_1)))
    log_sigma_exp_x = np.log(sigma_exp_x)
 
    # 输出loss
    loss_1 = -x_class + log_sigma_exp_x
 
    print("第一个样本loss为: ", loss_1)

her result:
在这里插入图片描述
接下来我们对每个样本进行权值缩放:

# ----------------------------------- weight -----------------------------------
 
    # def loss function
    weights = torch.tensor([1, 2], dtype=torch.float)
    # weights = torch.tensor([0.7, 0.3], dtype=torch.float)
 
    #有几个类,weight就要设几个值
    loss_f_none_w = nn.CrossEntropyLoss(weight=weights, reduction='none')
    loss_f_sum = nn.CrossEntropyLoss(weight=weights, reduction='sum')
    loss_f_mean = nn.CrossEntropyLoss(weight=weights, reduction='mean')
 
    # forward
    loss_none_w = loss_f_none_w(inputs, target)
    loss_sum = loss_f_sum(inputs, target)
    loss_mean = loss_f_mean(inputs, target)
 
    # view
    print("\nweights: ", weights)
    print(loss_none_w, loss_sum, loss_mean)
 
 
# --------------------------------- compute by hand
 
    weights = torch.tensor([1, 2], dtype=torch.float)
    weights_all = np.sum(list(map(lambda x: weights.numpy()[x], target.numpy())))  # [0, 1, 1]  # [1 2 2]
    mean = 0
    loss_f_none = nn.CrossEntropyLoss(reduction='none')
    loss_none = loss_f_none(inputs, target)
    loss_sep = loss_none.detach().numpy()
    for i in range(target.shape[0]):
 
        x_class = target.numpy()[i]
        tmp = loss_sep[i] * (weights.numpy()[x_class] / weights_all)
        mean += tmp
 
    print(mean)

result below:

在这里插入图片描述设置的时候,target那里第一个标签为0,权重为1;后两个标签为1,权重为2。所以分母不再是3个样本,而是1+2+2, 毕竟后两个样本权为2, 一个样本顶第一个的这样的2个。

可以观察到,第一个loss没有变;第二个和第三个loss变为原来的2倍

所以mean模式下求平均不是除以样本的个数,而是样本所占的权值的总份数。

加权之后:sum直接求和,mean需要求加权平均

2.3 剩余的17种损失函数介绍

在这里插入图片描述

# ----------------------------------- 1 NLLLoss -----------------------------------
 
    weights = torch.tensor([1, 1], dtype=torch.float)
 
    loss_f_none_w = nn.NLLLoss(weight=weights, reduction='none')
    loss_f_sum = nn.NLLLoss(weight=weights, reduction='sum')
    loss_f_mean = nn.NLLLoss(weight=weights, reduction='mean')
 
    # forward
    loss_none_w = loss_f_none_w(inputs, target)
    loss_sum = loss_f_sum(inputs, target)
    loss_mean = loss_f_mean(inputs, target)
 
    # view
    print("\nweights: ", weights)
    print("NLL Loss", loss_none_w, loss_sum, loss_mean)

在这里插入图片描述
在这里插入图片描述

# ----------------------------------- 2 BCE Loss -----------------------------------
 
    inputs = torch.tensor([[1, 2], [2, 2], [3, 4], [4, 5]], dtype=torch.float)
    target = torch.tensor([[1, 0], [1, 0], [0, 1], [0, 1]], dtype=torch.float)
 
    target_bce = target
 
 
    # itarget 将输入值压缩到[0-1]
    inputs = torch.sigmoid(inputs)
 
    weights = torch.tensor([1, 1], dtype=torch.float)
 
    loss_f_none_w = nn.BCELoss(weight=weights, reduction='none')
    loss_f_sum = nn.BCELoss(weight=weights, reduction='sum')
    loss_f_mean = nn.BCELoss(weight=weights, reduction='mean')
 
    # forward
    loss_none_w = loss_f_none_w(inputs, target_bce)
    loss_sum = loss_f_sum(inputs, target_bce)
    loss_mean = loss_f_mean(inputs, target_bce)
 
    # view
    print("\nweights: ", weights)
    print("BCE Loss", loss_none_w, loss_sum, loss_mean)

在这里插入图片描述

在这里插入图片描述

# --------------------------------- 17 CTC Loss -----------------------------------------
    T = 50      # Input sequence length
    C = 20      # Number of classes (including blank)
    N = 16      # Batch size
    S = 30      # Target sequence length of longest target in batch
    S_min = 10  # Minimum target length, for demonstration purposes
 
    # Initialize random batch of input vectors, for *size = (T,N,C)
    inputs = torch.randn(T, N, C).log_softmax(2).detach().requires_grad_()
 
    # Initialize random batch of targets (0 = blank, 1:C = classes)
    target = torch.randint(low=1, high=C, size=(N, S), dtype=torch.long)
 
    input_lengths = torch.full(size=(N,), fill_value=T, dtype=torch.long)
    target_lengths = torch.randint(low=S_min, high=S, size=(N,), dtype=torch.long)
 
    ctc_loss = nn.CTCLoss()
    loss = ctc_loss(inputs, target, input_lengths, target_lengths)
 
    print("CTC loss: ", loss)

在这里插入图片描述

本文参考
系统学习Pytorch笔记六:模型的权值初始化与损失函数介绍

[PyTorch 学习笔记] 4.2 损失函数 - 知乎 (zhihu.com)
https://jrs0511.blog.csdn.net/article/details/129091637 路人贾

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天道酬勤 2024

下一个构架师就是你~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值