常用的损失函数详解(供自己参考)
MSELoss(均方差损失函数)
一般用于回归问题上。
下面为代码实现:
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。
下面为代码实现:
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()的公式
下面为实现代码
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