常用的损失函数详解

429 阅读3分钟

常用的损失函数详解(供自己参考)

MSELoss(均方差损失函数)

一般用于回归问题上。

5093f5a6881b3a3086a6ae2ae1f00d0.jpg

下面为代码实现:

import torch
import torch.nn as nn

torch.manual_seend(15)
#模拟网络输出值
output = torch.randn([3,2])
#模拟真实值
target = torch.randn([3,2])


def my_mse_loss(output,target):
    #先取output与target差的平方,再相加取平均
    return torch.mean(torch.pow(output-target,2))

mse_loss = nn.MSELoss()
#手写的mseLoss
my_mse_loss = my_mse_loss(output,target)
#pytorch官方的mseLoss
mse_loss = mse_loss(output,target)
#输出
print(my_mse_loss)
print(mse_loss)
#------------------
#tensor(3.1192)
#tensor(3.1192)

BCELoss(二分类交叉熵损失)

用于二分类问题以及多标签分类问题上,多个类存在互斥。一般在使用BCELoss前会先将预测值进行Sigmoid固定在0-1之间。之后的BCELoss中存在log计算,复数取log为Nan。

1660719390337.jpg

下面为代码实现:

import torch
import torch.nn as nn


torch.manual_seed(15)
#模拟网络输出值
output = torch.randn([2,3])
#模拟真实值,真实值是one-hot [0,0,0,0,1]这种
target = torch.tensor(([0,0,1],[0,1,0]),dtype=torch.float32)

def my_bceloss(output,target):
    
    return torch.mean(-(target * torch.log(output) + (1-target)*torch.log(1-output)))
    
    
    
#sigmoid
sigmoid = nn.Sigmoid()
#将输出值取sigmoid
output = sigmoid(output)
#手写的bceloss输出
my_output = my_bceloss(output,target)
#pytorch bceloss
p_bceloss = nn.BCELoss()
p_output = p_bceloss(output,target)

#输出
print(my_output)
print(p_output)
#------------------
#tensor(0.8001)
#tensor(0.8001)

CrossEntropyLoss(交叉熵损失函数)

用于多分类问题,多个类不互斥。输入可以是负值,因为会先经过softmax进行指数运算,然后取对数,进行nllLoss计算

下图为CrossEntropyLoss()的公式 1660722089772.jpg

image.png

下面为实现代码

import torch
import torch.nn as nn

torch.manual_seed(15)
#模拟网络输出值
output = torch.randn([2,3])
#模拟真实值,类型不能为float,维度只能是1
target = torch.tensor([1,2]))


#会先取输出结果的softmax
def my_softmax(output):
    output_exp = torch.exp(output)
    partition = output_exp.sum(dim=1,keepdim=True)
    output_exp_softmax = output_exp / partition
    return output_exp_softmax

#交叉熵计算
def my_celoss(output,target):
    #softmax
    softmax_output = my_softmax(output)
    #取log
    log_softmax_output = torch.log(softmax_output)
    #nllloss
    #原理是,比如有三个类,网络的输出为两张图片经过softmax和log以后
    #为[-1.8161,-0.4363,-1.6558] [-0.9686,-0.8112,-1.7370]
    #target为[1,2],那么就取第一张图片的第二个类-0.4363,第二张图片的第三个类-1.7370,去除负号相加,然后取平均。
    loss = 0
    for i in range(output.shape[0]):
        for j in range(output.shape[1]):
            if target[i] == j:
                loss = -1 * log_softmax_output[i][j] + loss
                
    loss = loss / target.shape[0]            
     
p_ce = nn.CrossEntropyLoss()
#pytorch的CrossEntropyLoss
p_ce_loss = p_ce(output,target)
#手写的CrossEntropyLoss
m_ce_loss = my_celoss(output,target)

#输出
print(p_ce_loss)
print(m_ce_loss)
#------------------
#tensor(1.2335)
#tensor(1.2335)

FocalLoss(权衡正负样本)

FocalLoss用在特征点置信度计算loss上,因为特征点置信度是正负样本都会参与loss计算。

FocalLoss的两个权重:

1.控制正负样本数量的权重

2.控制容易分类和难分类样本的权重

只需要将交叉熵计算的loss乘上这两个权重就可以了

正负样本数量权重:

#在目标检测网络中,不同大小的特征图上都会有成千上万个先验框。
#当先验框与真实框匹配,说明这个先验框是正样本,其他没有匹配上的则是负样本。
#这会导致正负样本的数量相差很大,形成正负样本不平衡的情况。
#想要降低负样本的影响,可以在常规的损失函数前增加一个系数αt。
#当label=1的时候,αt=α;
#当label=otherwise的时候,αt=1 - α。



#obj_mask 为置信度掩码,特征点置信度为1的时候为True,置信度为0的时候为False
obj_mask = y_true[...:4] == 1
#通过obj_mask判断,为1则直接赋值为alpha,为False则赋值为1-alpha
at = torch.where(obj_mask, torch.ones_like(conf) * self.alpha, torch.ones_like(conf) * (1 - self.alpha))

容易分类和难分类权重:

#正样本和负样本都存在容易分类和难分类的情况。
#正样本容易分类:置信度很高。难分类:置信度低。
#负样本容易分类:置信度低。   难分类:置信度很高
#对于正样本而言,1-p的值越大,样本越难分类。
#对于负样本而言,p的值越大,样本越难分类

#通过obj_mask判断,
#(p就是网络预测的特征点的置信度)
#为True则说明是正样本,正样本对1-p进行判断
#为False则说明是负样本,负样本对p进行判断
#最后回乘上一个gamma系数
pt = torch.where(obj_mask, torch.ones_like(conf) - conf, conf) ** self.gamma
#网格置信度(原交叉熵loss * at * pt)
loss_conf = celoss * at * pt