Pytorch学习笔记(6):模型的权值初始化与损失函数

 

目录

前期回顾 

 一、权值初始化

1.1 梯度消失与梯度爆炸

1.2 Xavier初始化

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

1.3 Kaiming初始化 

nn.init.kaiming_normal_(tensor, a=0, mode=‘fan_in’, nonlinearity=‘leaky_relu’)

1.4 十种权重初始化方法

 二、损失函数

2.1 损失函数初步介绍

 2.2 交叉熵损失CrossEntropyLoss

 nn.CrossEntropyLoss

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

(1)nn.NLLLoss

(2)nn.BCELoss

(3)nn.BCEWithLogists Loss

(4)nn.L1Loss

(5)nn.MSELoss

(6)nn.SmoothL1Loss

(7)nn.PoissonNLLLoss

(8)nn.KLDivLoss

(9)nn.MarginRankingLoss

(10)nn.MultiLabelMarginLoss

(11)nn.SoftMarginLoss

(12)nn.MultiLabelSortMarginLoss

(13)nn.MultiMarginLoss(hingLoss)

(14)nn.TripletMarginLoss

(15)nn.HingeEmbeddingLoss

(16)nn.CosineEmbeddingLoss

(17)nn.CTCLoss

总结


前期回顾 :

Pytorch学习笔记(1):基本概念、安装、张量操作、逻辑回归
Pytorch学习笔记(2):数据读取机制(DataLoader与Dataset)
Pytorch学习笔记(3):图像的预处理(transforms)
Pytorch学习笔记(4):模型创建(Module)、模型容器(Containers)、AlexNet构建
Pytorch学习笔记(5):torch.nn---网络层介绍(卷积层、池化层、线性层、激活函数层)


 一、权值初始化

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

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.3 Kaiming初始化 

针对上述的问题,何恺明等大佬就提出了针对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)

                # 下面这个是手动写出的,这两句话其实作用一样,不过自己写还得计算出标准差
                # nn.init.normal_(m.weight.data, std=np.sqrt(2 / self.neural_num))     

输出结果我们可以发现,妈妈再也不用担心使用Relu激活发生爆炸了~


1.4 十种权重初始化方法

Pytorch里面提供了很多权重初始化的方法,可以分为下面的四大类:

  • 针对饱和激活函数(sigmoid, tanh):Xavier均匀分布, Xavier正态分布
  • 针对非饱和激活函数(relu及变种):Kaiming均匀分布, Kaiming正态分布
  • 三个常用的分布初始化方法:均匀分布,正态分布,常数分布
  • 三个特殊的矩阵初始化方法:正交矩阵初始化,单位矩阵初始化,稀疏矩阵初始化

 二、损失函数

2.1 损失函数初步介绍

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

  • 损失函数(Loss Function):是计算一个样本的模型输出与真实标签的差异 Loss=f(y^{​{\color{Green} }\Lambda },y)
  • 代价函数(Cost Function):是计算整个样本集的模型输出与真实标签的差异,是所有样本损失函数的平均值。\cos t=\frac{1}{N} \sum_{i}^{N}f(y_{i}^{\Lambda },y_{i})
  • 目标函数(Objective Function):就是代价函数加上正则项。Obj=Cost+Regularization

    (实际上哈,我们一般在衡量模型输出和真实标签的差异的时候,往往都直接说成损失函数,没啥区别。)


  2.2 交叉熵损失CrossEntropyLoss

 nn.CrossEntropyLoss

功能: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)

输出结果:

每个样本的loss、loss之和、loss取平均如下图所示

手动计算是否准确:

# --------------------------------- 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)

输出结果:

和之前相同

 接下来,我们再对每个样本进行权值的缩放

代码如下:

# ----------------------------------- 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)

输出结果:

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

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

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

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


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

(1)nn.NLLLoss

功能:实现负对数似然函数中的负号功能

主要参数:

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

具体代码如下:

# ----------------------------------- 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)

输出结果:

第一个值的标签是0,则对第一个数据的index=0的位置取负号

第二个和第三个值得标签是1,则对第二个和第三个数据的index=1的位置取负号

 


(2)nn.BCELoss

功能:二分类交叉熵

 yn是标签,yn = 0或者yn = 1 

主要参数

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

注意事项:输入值取值在[0,1],需要符合概率取值

具体代码如下:

# ----------------------------------- 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)

输出结果:

每个神经元一一计算loss


(3)nn.BCEWithLogists Loss

BCELoss中我们发现,如果输入数据不在[0,1]区间内就会报错,针对这一问题提出了nn.BCEWithLogists Loss

功能:结合Sigmoid与二分类交叉熵

主要参数

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

注意事项:网络最后不加sigmoid函数

具体代码如下:

# ---------------------------- 3 BCE with Logis 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
 
    # inputs = torch.sigmoid(inputs)
 
    weights = torch.tensor([1, 1], dtype=torch.float)
 
    loss_f_none_w = nn.BCEWithLogitsLoss(weight=weights, reduction='none')
    loss_f_sum = nn.BCEWithLogitsLoss(weight=weights, reduction='sum')
    loss_f_mean = nn.BCEWithLogitsLoss(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(loss_none_w, loss_sum, loss_mean)

输出结果:

 更改权重:

# --------------------------------- pos weight

    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
    # inputs = torch.sigmoid(inputs)

    weights = torch.tensor([1], dtype=torch.float)
    pos_w = torch.tensor([3], dtype=torch.float)        # 3

    loss_f_none_w = nn.BCEWithLogitsLoss(weight=weights, reduction='none', pos_weight=pos_w)
    loss_f_sum = nn.BCEWithLogitsLoss(weight=weights, reduction='sum', pos_weight=pos_w)
    loss_f_mean = nn.BCEWithLogitsLoss(weight=weights, reduction='mean', pos_weight=pos_w)

    # 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("\npos_weights: ", pos_w)
    print(loss_none_w, loss_sum, loss_mean)

若pos_weight设置为3

则对正样本所在的神经元乘3这个系数

target = torch.tensor([[1, 0], [1, 0], [0, 1], [0, 1]], dtype=torch.float)

则对样本标签为1的位置进行3倍操作

我们来对比一下输出结果:


(4)nn.L1Loss

功能:计算input与target之差的绝对值

 主要参数

  • reduction:计算模式,可为none/sum/mean
    • none:逐个元素计算
    • sum:返回所有元素求和,返回标量
    • mean:加权平均,返回标量(默认)

具体代码如下:

set_seed(1)  # 设置随机种子
 
# ------------------------------------ 4 L1 loss ----------------------------------------------
 
    inputs = torch.ones((2, 2))
    target = torch.ones((2, 2)) * 3
 
    loss_f = nn.L1Loss(reduction='none')
    loss = loss_f(inputs, target)
 
    print("input:{}\ntarget:{}\nL1 loss:{}".format(inputs, target, loss))

输出结果: 


(5)nn.MSELoss

功能:计算input与target之差的平方

 主要参数

  • reduction:计算模式,可为none/sum/mean
    • none:逐个元素计算
    • sum:返回所有元素求和,返回标量
    • mean:加权平均,返回标量(默认)

具体代码如下:

    inputs = torch.ones((2, 2))
    target = torch.ones((2, 2)) * 3

    loss_f = nn.L1Loss(reduction='none')
    loss = loss_f(inputs, target)
    print("input:{}\ntarget:{}\nL1 loss:{}".format(inputs, target, loss))

# ---------------------- 5 MSE loss ---------------------------------------

    loss_f_mse = nn.MSELoss(reduction='none')
    loss_mse = loss_f_mse(inputs, target)

    print("MSE loss:{}".format(loss_mse))

输出结果: 


(6)nn.SmoothL1Loss

功能:创建一个标准,如果绝对元素误差低于β,则使用平方项,否则使用L1项。它对异常值的敏感度低于torch.nn.MSELoss,并且在某些情况下可以防止爆炸梯度

 主要参数

  • reduction:计算模式,可为none/sum/mean
    • none:逐个元素计算
    • sum:返回所有元素求和,返回标量
    • mean:加权平均,返回标量(默认)

采用这种平滑的损失函数可以减轻离群点带来的影响。 

具体代码如下:

# --------------------------------- 6 Smooth L1 loss --------------------------------------------

    inputs = torch.linspace(-3, 3, steps=500)
    target = torch.zeros_like(inputs)

    loss_f = nn.SmoothL1Loss(reduction='none')

    loss_smooth = loss_f(inputs, target)

    loss_l1 = np.abs(inputs.numpy())

    plt.plot(inputs.numpy(), loss_smooth.numpy(), label='Smooth L1 Loss')
    plt.plot(inputs.numpy(), loss_l1, label='L1 loss')
    plt.xlabel('x_i - y_i')
    plt.ylabel('loss value')
    plt.legend()
    plt.grid()
    plt.show()

输出: 


(7)nn.PoissonNLLLoss

功能:泊松分布的负对数似然损失函数, 分类里面如果发现数据的类别服从泊松分布,可以使用这个损失函数

主要参数

  • log_input:输入是否为对数形式,决定计算公式
  • full:计算所有loss,默认为False
  • eps:修正项,避免log(input)为nan

具体代码如下:

# -------------------------------- 7 Poisson NLL Loss ----------------------------------------------

    inputs = torch.randn((2, 2))
    target = torch.randn((2, 2))

    loss_f = nn.PoissonNLLLoss(log_input=True, full=False, reduction='none')
    loss = loss_f(inputs, target)
    print("input:{}\ntarget:{}\nPoisson NLL loss:{}".format(inputs, target, loss))

输出: 


(8)nn.KLDivLoss

功能:计算KLD(divergence),KL散度,相对熵

主要参数

  • reduction:计算模式,可为none/sum/mean/batchmean
    • batchmean:batchsize维度求平均值
    • none:逐个元素计算
    • sum:返回所有元素求和,返回标量
    • mean:加权平均,返回标量(默认)

注意事项:需提前将输入计算log-probabilities,如通过nn.logsoftmax()

具体代码如下:

# ------------------------------ 8 KL Divergence Loss ----------------------------------------------

    inputs = torch.tensor([[0.5, 0.3, 0.2], [0.2, 0.3, 0.5]])
    inputs_log = torch.log(inputs)
    target = torch.tensor([[0.9, 0.05, 0.05], [0.1, 0.7, 0.2]], dtype=torch.float)

    loss_f_none = nn.KLDivLoss(reduction='none')
    loss_f_mean = nn.KLDivLoss(reduction='mean')
    loss_f_bs_mean = nn.KLDivLoss(reduction='batchmean')

    loss_none = loss_f_none(inputs, target)
    loss_mean = loss_f_mean(inputs, target)
    loss_bs_mean = loss_f_bs_mean(inputs, target)

    print("loss_none:\n{}\nloss_mean:\n{}\nloss_bs_mean:\n{}".format(loss_none, loss_mean, loss_bs_mean))

输出:


(9)nn.MarginRankingLoss

功能:计算两个向量之间的相似度,用于排序任务

特别说明:该方法计算两组数据之间的差异,返回一个n*n的loss矩阵,类似于相关性矩阵那种。

​ 

主要参数

  • margin:边界值,x1与x2之间的差异值
  • reduction:计算模式,可为none/sum/mean

计算公式如下:

loss(x,y)=max(0,−y∗(x1−x2)+margin)

  • y = 1时,希望x1比x2大,当x1>x2时,不产生loss
  • y = -1时,希望x2比x1大,当x2>x1时,不产生loss

具体代码段如下:

# ----------------------------- 10 Margin Ranking Loss --------------------------------------------

    x1 = torch.tensor([[1], [2], [3]], dtype=torch.float)
    x2 = torch.tensor([[2], [2], [2]], dtype=torch.float)

    target = torch.tensor([1, 1, -1], dtype=torch.float)

    loss_f_none = nn.MarginRankingLoss(margin=0, reduction='none')

    loss = loss_f_none(x1, x2, target)

    print(loss)

输出: 

 (图片来源:翻滚的小@强)


(10)nn.MultiLabelMarginLoss

功能:多标签边界损失函数, 这是一个多标签分类,就是一个样本可能属于多个类,和多分类任务还不一样。(多标签问题)

举例:四分类任务,样本x属于0类和3类,标签:[0,3,-1,-1],不是[1,0,0,1]

​ 

主要参数

  • reduction:计算模式,可为none/sum/mean

具体代码如下:

# ------------------------- 10 Multi Label Margin Loss ----------------------------------

    x = torch.tensor([[0.1, 0.2, 0.4, 0.8]])
    y = torch.tensor([[0, 3, -1, -1]], dtype=torch.long)

    loss_f = nn.MultiLabelMarginLoss(reduction='none')

    loss = loss_f(x, y)

    print(loss)

输出: 

 解释:我们希望标签所在的神经元要比非标签所在的神经元的输出值要尽量的大,当这个差大于1了, 我们根据max(0, 1-差值),才发现不会有损失产生, 当这个差值小或者非标签所在的神经元比标签所在神经元大的时候,都会产生损失。 所以上面那个例子,我们想让第0个神经元的值要比第1个,第2个大一些,第3个神经元的值要比第1个,第2个大一些,这才能说明这个样本属于第0类和第3类,才是我们想要的结果。 


(11)nn.SoftMarginLoss

功能:计算二分类的logistic损失

主要参数

  • reduction:计算模式,可为none/sum/mean

 具体代码如下:

# -------------------------------- 11 SoftMargin Loss -----------------------------------------
 
    inputs = torch.tensor([[0.3, 0.7], [0.5, 0.5]])
    target = torch.tensor([[-1, 1], [1, -1]], dtype=torch.float)
 
    loss_f = nn.SoftMarginLoss(reduction='none')
 
    loss = loss_f(inputs, target)
 
    print("SoftMargin: ", loss)

输出: 


(12)nn.MultiLabelSortMarginLoss

功能:SoftMarginLoss多标签版本

主要参数

  • weight:各类别的loss设置权值
  • reduction:计算模式,可为none/sum/mean

具体代码如下:

# --------------------------- 12 MultiLabel SoftMargin Loss -----------------------------------------
    inputs = torch.tensor([[0.3, 0.7, 0.8]])
    target = torch.tensor([[0, 1, 1]], dtype=torch.float)

    loss_f = nn.MultiLabelSoftMarginLoss(reduction='none')

    loss = loss_f(inputs, target)

    print("MultiLabel SoftMargin: ", loss)

 输出: 

  (图片来源:翻滚的小@强)


(13)nn.MultiMarginLoss(hingLoss)

功能:计算多分类的折页损失

主要参数

  • p:可选1或2
  • weight:各类别的loss设置权值
  • margin:边界值
  • reduction:计算模式,可为none/sum/mean

具体代码如下: 

# --------------------------- 13 Multi Margin Loss ----------------------------------------

    x = torch.tensor([[0.1, 0.2, 0.7], [0.2, 0.5, 0.3]])
    y = torch.tensor([1, 2], dtype=torch.long)

    loss_f = nn.MultiMarginLoss(reduction='none')

    loss = loss_f(x, y)

    print("Multi Margin Loss: ", loss)

输出:


(14)nn.TripletMarginLoss

功能:计算三元组损失,人脸验证中常用

主要参数

  • p:范数的阶,默认为2
  • margin:边界值
  • reduction:计算模式,可为none/sum/mean

三元组在做这么个事情, 我们在做人脸识别训练模型的时候,往往需要把训练集做成三元组(A, P, N), A和P是同一个人, A和N不是同一个, 然后训练我们的模型

 具体代码如下:

# ------------------- 14 Triplet Margin Loss -----------------------------------------

    anchor = torch.tensor([[1.]])
    pos = torch.tensor([[2.]])
    neg = torch.tensor([[0.5]])
 
    loss_f = nn.TripletMarginLoss(margin=1.0, p=1)
 
    loss = loss_f(anchor, pos, neg)
 
    print("Triplet Margin Loss", loss)

输出: 


(15)nn.HingeEmbeddingLoss

功能:计算两个输入的相似性,常用于非线性embedding和半监督学习

主要参数

  • margin:边界值
  • reduction:计算模式,可为none/sum/mean

 具体代码如下:

# ---------------------------- 15 Hinge Embedding Loss -----------------------------------------
 
    inputs = torch.tensor([[1., 0.8, 0.5]])
    target = torch.tensor([[1, 1, -1]])
 
    loss_f = nn.HingeEmbeddingLoss(margin=1, reduction='none')
 
    loss = loss_f(inputs, target)
 
    print("Hinge Embedding Loss", loss)

输出: 


(16)nn.CosineEmbeddingLoss

功能:采用余弦相似度计算两个输入的相似性

主要参数

  • margin:可取值[-1,1],推荐为[0,0.5]
  • reduction:计算模式,可为none/sum/meam

具体代码如下:

# -------------------------- 16 Cosine Embedding Loss -----------------------------------------
 
    x1 = torch.tensor([[0.3, 0.5, 0.7], [0.3, 0.5, 0.7]])
    x2 = torch.tensor([[0.1, 0.3, 0.5], [0.1, 0.3, 0.5]])
 
    target = torch.tensor([[1, -1]], dtype=torch.float)
 
    loss_f = nn.CosineEmbeddingLoss(margin=0., reduction='none')
 
    loss = loss_f(x1, x2, target)
 
    print("Cosine Embedding Loss", loss)

(17)nn.CTCLoss

功能:计算CTC损失,解决时序类数据的分类

主要参数

  • blank:blank label
  • zero_infinity:无穷大的值或梯度置0
  • reduction:计算模式,可为none/sum/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)

输出: 


总结:

分类问题

  • 二分类单标签问题: nn.BCELoss, nn.BCEWithLogitsLoss, nn.SoftMarginLoss
  • 二分类多标签问题:nn.MultiLabelSoftMarginLoss
  • 多分类单标签问题: nn.CrossEntropyLoss, nn.NLLLoss, nn.MultiMarginLoss
  • 多分类多标签问题: nn.MultiLabelMarginLoss,
  • 不常用:nn.PoissonNLLLoss, nn.KLDivLoss

回归问题: nn.L1Loss, nn.MSELoss, nn.SmoothL1Loss
时序问题:nn.CTCLoss
人脸识别问题:nn.TripletMarginLoss
半监督Embedding问题(输入之间的相似性): nn.MarginRankingLoss, nn.HingeEmbeddingLoss, nn.CosineEmbeddingLoss


本文参考:

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

[PyTorch 学习笔记] 4.2 损失函数 - 知乎 (zhihu.com)


猜你喜欢

转载自blog.csdn.net/weixin_43334693/article/details/129091637