5-3 pytorch中的损失函数

news2024/12/26 12:04:18

一般来说,监督学习的目标函数由损失函数正则化项组成。(Objective = Loss + Regularization)
Pytorch中的损失函数一般在训练模型时候指定。
注意Pytorch中内置的损失函数的参数和tensorflow不同,是y_pred在前,y_true在后,而Tensorflow是y_true在前,y_pred在后。
对于回归模型,通常使用的内置损失函数是均方损失函数nn.MSELoss
对于二分类模型,通常使用的是二元交叉熵损失函数nn.BCELoss (输入已经是sigmoid激活函数之后的结果) 或者 nn.BCEWithLogitsLoss (输入尚未经过nn.Sigmoid激活函数)
对于多分类模型,一般推荐使用交叉熵损失函数** nn.CrossEntropyLoss**。 (y_true需要是一维的,是类别编码。y_pred未经过nn.Softmax激活。)
此外,如果多分类的y_pred经过了nn.LogSoftmax激活,可以使用nn.NLLLoss损失函数(The negative log likelihood loss)。 这种方法和直接使用nn.CrossEntropyLoss等价。
如果有需要,也可以自定义损失函数,自定义损失函数需要接收两个张量y_pred,y_true作为输入参数,并输出一个标量作为损失函数值。
Pytorch中的正则化项一般通过自定义的方式和损失函数一起添加作为目标函数。
如果仅仅使用L2正则化,也可以利用优化器的weight_decay参数来实现相同的效果。

一、内置损失函数

内置的损失函数一般有类的实现和函数的实现两种形式。
如:nn.BCE F.binary_cross_entropy 都是二元交叉熵损失函数,前者是类的实现形式,后者是函数的实现形式。
实际上类的实现形式通常是调用函数的实现形式并用nn.Module封装后得到的
一般我们常用的是类的实现形式。它们封装在torch.nn模块下,并且类名以Loss结尾。
常用的一些内置损失函数说明如下。
nn.MSELoss(均方误差损失,也叫做L2损失,用于回归)
nn.L1Loss (L1损失,也叫做绝对值误差损失,用于回归)
nn.SmoothL1Loss (平滑L1损失,当输入在-1到1之间时,平滑为L2损失,用于回归)
**nn.BCELoss **(二元交叉熵,用于二分类,输入已经过nn.Sigmoid激活,对不平衡数据集可以用weigths参数调整类别权重)
nn.BCEWithLogitsLoss (二元交叉熵,用于二分类,输入未经过nn.Sigmoid激活)
nn.CrossEntropyLoss (交叉熵,用于多分类,要求label为稀疏编码,输入未经过nn.Softmax激活,对不平衡数据集可以用weigths参数调整类别权重)
nn.NLLLoss (负对数似然损失,用于多分类,要求label为稀疏编码,输入经过nn.LogSoftmax激活)
nn.KLDivLoss (KL散度损失,也叫
相对熵,等于交叉熵减去信息熵,用于标签为概率值的多分类,要求输入经过nn.LogSoftmax激活
)
nn.CosineSimilarity(余弦相似度,可用于多分类)
nn.AdaptiveLogSoftmaxWithLoss (一种适合非常多类别且类别分布很不均衡的损失函数,会自适应地将多个小类别合成一个cluster)
重点介绍一下 二元交叉熵、多元交叉熵、对数损失LogLoss、负对数似然损失NLLLoss、KL散度之间的区别和联系。

二元交叉熵

image.png
1e3a8c87772a313ee9de5054b4e6c4f.jpg

多元交叉熵

image.png
y i ^ \hat{y_i} yi^是一个长度的K的概率向量,当 y i y_i yi==k,即取到对应的类别时才有意义,所以 y i , k ^ \hat{y_{i,k}} yi,k^就表示 y i ^ \hat{y_i} yi^中的索引为k的值,这样就可以log了,对每一个样本都是如此处理,那么最后归一化。

logloss对数损失函数

image.png
这同样是 y i ^ \hat{y_i} yi^取索引为其类别的值,所以与交叉熵等价。(每个样本对其标签对应类别的预测概率值求对数,求平均再取负数即可)

pytorch中的 nn.NLLLoss 和 nn.CrossEntropyLoss有什么区别和联系?

如果多分类的y_pred经过了nn.LogSoftmax激活,可以使用nn.NLLLoss损失函数(The negative log likelihood loss)。 这种方法和直接使用nn.CrossEntropyLoss等价。

image.png

KL散度的计算公式是什么?有什么现实含义?和交叉熵有什么关系?

image.png
KL散度等于交叉熵减去信息熵。
image.png

nn.BCELoss() 和 nn.BCEWithLogitsLoss() 关系:

import numpy as np
import pandas as pd
import torch 
from torch import nn 
import torch.nn.functional as F 

# nn.BCELoss() 和 nn.BCEWithLogitsLoss() 关系

y_pred = torch.tensor([5.0,3,10,-5,-3,-10.0])
y_true = torch.tensor([1.0,1,1,0,0,0])

bce = nn.BCELoss()(torch.sigmoid(y_pred),y_true) # 输入已经是sigmoid激活函数之后的结果
print(bce)


bce_logits = nn.BCEWithLogitsLoss()(y_pred,y_true) # 输入尚未经过nn.Sigmoid激活函数
print(bce_logits)

# 结果一致

image.png
nn.CrossEntropyLoss() 和 nn.NLLLoss() 关系

y_pred = torch.tensor([[10.0,0.0,-10.0],[8.0,8.0,8.0]])
y_true = torch.tensor([0,2])

# 直接调用交叉熵损失
ce = nn.CrossEntropyLoss()(y_pred,y_true)
print(ce)

# 等价于先计算nn.LogSoftmax激活,再调用nn.NLLLoss
y_pred_logsoftmax = nn.LogSoftmax(dim = 1)(y_pred)
nll = nn.NLLLoss()(y_pred_logsoftmax,y_true)
print(nll)

image.png
nn.CrossEntropyLoss() 和 KLDivLoss 关系:

import torch.nn.functional as F 

y_pred = torch.tensor([[10.0,0.0,-10.0],[8.0,8.0,8.0]],requires_grad=True)
y_true = torch.tensor([0,2])

ce = nn.CrossEntropyLoss(reduction="mean")(y_pred,y_true)
print(ce)


#KLDivLoss要求target为向量形式编码且preds经过LogSoftmax激活
pred = F.log_softmax(y_pred,dim=1)
target = F.one_hot(y_true).float()
kl = nn.KLDivLoss(reduction="batchmean")(pred,target)
print(kl)

image.png

二、自定义损失函数

自定义损失函数接收两个张量y_pred,y_true作为输入参数,并输出一个标量作为损失函数值。
也可以对nn.Module进行子类化,重写forward方法实现损失的计算逻辑,从而得到损失函数的类的实现。
下面演示两个比较著名的范例。

自定义损失函数之FocalLoss范例

下面是一个Focal Loss的自定义实现示范。Focal Loss是一种对binary_crossentropy的改进损失函数形式。
它在样本不均衡和存在较多易分类的样本时相比binary_crossentropy具有明显的优势。
它有两个可调参数,alpha参数和gamma参数。其中alpha参数主要用于衰减负样本的权重,gamma参数主要用于衰减容易训练样本的权重。
从而让模型更加聚焦在正样本和困难样本上。这就是为什么这个损失函数叫做Focal Loss。
详见《5分钟理解Focal Loss与GHM——解决样本不平衡利器》
https://zhuanlan.zhihu.com/p/80594704
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
Focal Loss的引入主要是为了解决难易样本数量不平衡(注意,有区别于正负样本数量不平衡
image.png
但这并不能解决全部问题。根据正、负、难、易,样本一共可以分为以下四类:
image.png
尽管 平衡了正负样本,但对难易样本的不平衡没有任何帮助。而实际上,目标检测中大量的候选目标都是像下图一样的易分样本。
image.png
这些样本的损失很低,但是由于数量极不平衡,易分样本的数量相对来讲太多,最终主导了总的损失。而易分样本(即,置信度高的样本)对模型的提升效果非常小,模型应该主要关注与那些难分样本
image.png
image.png

import torch 
from torch import nn 
class FocalLoss(nn.Module):

    def __init__(self,gamma=2.0,alpha=0.75):
        super().__init__()
        self.gamma = gamma
        self.alpha = alpha

    def forward(self,y_pred,y_true):
        bce = torch.nn.BCELoss(reduction = "none")(y_pred,y_true) # 当reduction='none'时,输出是对每一个样本预测的损失
        p_t = (y_true * y_pred) + ((1 - y_true) * (1 - y_pred))
        alpha_factor = y_true * self.alpha + (1 - y_true) * (1 - self.alpha)
        modulating_factor = torch.pow(1.0 - p_t, self.gamma)
        loss = torch.mean(alpha_factor * modulating_factor * bce)
        return loss
#困难样本
y_pred_hard = torch.tensor([[0.5],[0.5]])
y_true_hard = torch.tensor([[1.0],[0.0]])

#容易样本
y_pred_easy = torch.tensor([[0.9],[0.1]])
y_true_easy = torch.tensor([[1.0],[0.0]])

focal_loss = FocalLoss()
bce_loss = nn.BCELoss()


print("focal_loss(easy samples):", focal_loss(y_pred_easy,y_true_easy))
print("bce_loss(easy samples):", bce_loss(y_pred_easy,y_true_easy))

print("focal_loss(hard samples):", focal_loss(y_pred_hard,y_true_hard))
print("bce_loss(hard samples):", bce_loss(y_pred_hard,y_true_hard))


#可见 focal_loss让容易样本的权重衰减到原来的 0.0005/0.1054 = 0.00474
#而让困难样本的权重只衰减到原来的 0.0866/0.6931=0.12496

# 因此相对而言,focal_loss可以衰减容易样本的权重。

image.png

SCELoss

image.png

def ce(y,p):
    p = torch.clamp(p,min=1e-4,max=1-1e-4)
    y = torch.clamp(y,min=1e-4,max=1-1e-4)
    return -y*torch.log(p) - (1-y)*torch.log(1-p)

def rce(y,p):
    return ce(p,y)

#正常标签
y = torch.tensor(1.0)
p = torch.tensor(0.8)
print(rce(y,p)/ce(y,p))


#噪声标签
y = torch.tensor(0.0)
p = torch.tensor(0.8)
print(rce(y,p)/ce(y,p))

import torch 
from torch import nn
import  torch.nn.functional as F 

class SCELoss(nn.Module):
    def __init__(self, num_classes=10, a=1, b=1):
        super(SCELoss, self).__init__()
        self.num_classes = num_classes
        self.a = a #两个超参数
        self.b = b
        self.cross_entropy = nn.CrossEntropyLoss()

    def forward(self, pred, labels):
        # CE 部分,正常的交叉熵损失
        ce = self.cross_entropy(pred, labels)

        # RCE
        pred = F.softmax(pred, dim=1)
        pred = torch.clamp(pred, min=1e-4, max=1.0)
        label_one_hot = F.one_hot(labels, self.num_classes).float().to(pred.device)
        label_one_hot = torch.clamp(label_one_hot, min=1e-4, max=1.0) #最小设为 1e-4,即 A 取 -4
        rce = (-1 * torch.sum(pred * torch.log(label_one_hot), dim=1))

        loss = self.a * ce + self.b * rce.mean()
        return loss
    

三、L1和L2正则化

做正则化的时候,是不需要考虑偏置bias的,因为我们正则化是减轻过拟合,让模型方法更加稳定,也就是更加平滑,泛化能力更强,而bias的作用只是把方法上下移动,对平滑却没啥作用。
正则化为什么能缓解过拟合?
给loss function加上正则化项,能使得新得到的优化目标函数h = f+normal,需要在f和normal中做一个权衡(trade-off),如果还像原来只优化f的情况下,那可能得到一组解比较复杂,使得正则项normal比较大,那么h就不是最优的,因此可以看出加正则项能让解更加简单,符合奥卡姆剃刀理论,同时也比较符合在偏差和方差(方差表示模型的复杂度)分析中,通过降低模型复杂度,得到更小的泛化误差,降低过拟合程度。
L1正则化和L2正则化:
L1正则化就是在loss function后边所加正则项为L1范数,加上L1范数容易得到稀疏解(0比较多)。L2正则化就是loss function后边所加正则项为L2范数的平方,加上L2正则相比于L1正则来说,得到的解比较平滑(不是稀疏),但是同样能够保证解中接近于0(但不是等于0,所以相对平滑)的维度比较多,降低模型的复杂度。
原理参考:参考文章《L1正则化与L2正则化》:https://zhuanlan.zhihu.com/p/35356992

import torch 
# L2正则化
def L2Loss(model,alpha):
    l2_loss = torch.tensor(0.0, requires_grad=True)
    for name, param in model.named_parameters():
        if 'bias' not in name: #一般不对偏置项使用正则
            l2_loss = l2_loss + (0.5 * alpha * torch.sum(torch.pow(param, 2))) # L2范数的平方
    return l2_loss

# L1正则化
def L1Loss(model,beta):
    l1_loss = torch.tensor(0.0, requires_grad=True)
    for name, param in model.named_parameters():
        if 'bias' not in name:
            l1_loss = l1_loss +  beta * torch.sum(torch.abs(param)) # L1范数
    return l1_loss

四、L1L2正则项使用完整范例¶

准备数据

image.png

ds = TensorDataset(X,Y)

ds_train, ds_val = torch.utils.data.random_split(ds,[int(len(ds)*0.7),len(ds)-int(len(ds)*0.7)])
dl_train = DataLoader(ds_train,batch_size = 100,shuffle=True,num_workers=2)
dl_val = DataLoader(ds_val,batch_size = 100,num_workers=2)

features,labels = next(iter(dl_train))

image.png

定义模型

class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(2,4)
        self.fc2 = nn.Linear(4,8) 
        self.fc3 = nn.Linear(8,1)
        
    def forward(self,x):
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        y = self.fc3(x)
        return y
        
net = Net() 

from torchkeras import summary

summary(net,features);

image.png

训练模型

# L2正则化
def L2Loss(model,alpha):
    l2_loss = torch.tensor(0.0, requires_grad=True)
    for name, param in model.named_parameters():
        if 'bias' not in name: #一般不对偏置项使用正则
            l2_loss = l2_loss + (0.5 * alpha * torch.sum(torch.pow(param, 2)))
    return l2_loss

# L1正则化
def L1Loss(model,beta):
    l1_loss = torch.tensor(0.0, requires_grad=True)
    for name, param in model.named_parameters():
        if 'bias' not in name:
            l1_loss = l1_loss +  beta * torch.sum(torch.abs(param))
    return l1_loss

from torchkeras import KerasModel
from torchkeras.metrics import AUC

net = Net()

# 将L2正则和L1正则添加到FocalLoss损失,一起作为目标函数
def focal_loss_with_regularization(y_pred,y_true):
    y_probs = torch.sigmoid(y_pred)
    focal = FocalLoss()(y_probs,y_true) 
    l2_loss = L2Loss(net,0.001) #注意设置正则化项系数
    l1_loss = L1Loss(net,0.001)
    total_loss = focal + l2_loss + l1_loss # 原来损失 + L1正则化 + L2正则化
    return total_loss


optimizer = torch.optim.Adam(net.parameters(),lr = 0.002) # 优化器
model = KerasModel(net=net,
                   loss_fn = focal_loss_with_regularization ,
                   metrics_dict = {"auc":AUC()},
                   optimizer= optimizer )


dfhistory = model.fit(train_data=dl_train,
      val_data=dl_val,
      epochs=20,
      ckpt_path='checkpoint',
      patience=3,
      monitor='val_auc',
      mode='max',
      plot=True,
      cpu=True
    )

image.png

五、通过优化器实现L2正则化

如果仅仅需要使用L2正则化,那么也可以利用优化器的weight_decay参数来实现
weight_decay参数可以设置参数在训练过程中的衰减,这和L2正则化的作用效果等价。

before L2 regularization:
gradient descent: w = w - lr * dloss_dw
after L2 regularization:
gradient descent: w = w - lr * (dloss_dw+betaw) = (1-lrbeta)w - lrdloss_dw
so (1-lr*beta)is the weight decay ratio.

Pytorch的优化器支持一种称之为Per-parameter options的操作,就是对每一个参数进行特定的学习率,权重衰减率指定,以满足更为细致的要求。

weight_params = [param for name, param in model.named_parameters() if "bias" not in name]
bias_params = [param for name, param in model.named_parameters() if "bias" in name]

optimizer = torch.optim.SGD([{'params': weight_params, 'weight_decay':1e-5},
                             {'params': bias_params, 'weight_decay':0}],
                            lr=1e-2, momentum=0.9)

参考:https://github.com/lyhue1991/eat_pytorch_in_20_days

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1020828.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

系统IO和标准IO

一.系统IO 系统 I/O(Input/Output)是计算机操作系统提供给应用程序的一种输入和输出方式。它通过系统调用(系统内核提供的函数)来实现数据的读取和写入。系统 I/O 可以用于与文件、设备(例如磁盘驱动器、网络接口、串…

哪些情况可以使用自动化测试?

通常,软件测试的测试方式分为人工测试和自动化测试,人工测试是由测试人员编写并执行测试用例,然后观察测试结果与预期结果是否一致的过程;自动化测试是通过测试工具来代替或辅助人工去验证系统功能是否有问题的过程。 采用自动化测试需要满足…

blender怎么设置中文界面

你们知道Blender软件是什么吗?你知道blender怎么设置中文界面吗?Blender是个GNU的3D绘图软件,建模、算图、动画等功能都相当的完整,可以说已经具有了一般商业软件的规模。Blender大部分的功能都有热键,操作起来相当地轻…

Nano 编辑器中,怎样保存和退出

使用git 修改提交记录时,使用命令: git commit --amend 弹出了nano编辑器,第一次使用的时候不知道怎么保存退出,现在记录下: 1.修改完毕后使用Ctrl x,然后会弹出 点击Y后,界面会退回到如下 这时候点击E…

springboot和vue:四、web入门(静态资源访问+文件上传+拦截器)

静态资源访问 使用IDEA创建Spring Boot项目,会默认创建出classpath:/static/目录,静态资源一般放在这个目录下即可。如果默认的静态资源过滤策略不能满足开发需求,也可以自定义静态资源过滤策略。 在application.properties中定义过滤规则和…

数据驱动 vs 关键字驱动:对搭建UI自动化测试框架的探索

UI自动化测试用例剖析 让我们先从分析一端自动化测试案例的代码开始我们的旅程。以下是我之前写的一个自动化测试的小Demo。这个Demo基于Selenium与Java。由于现在Selenium在自动化测试的统治地位,并且随着Selenium 4的即将发布,在未来很长的一段时间里…

【C#】XML的基础知识以及读取XML文件

最近在学读取文件 目录 介绍特点结构XML的语法规则XML 命名规则 C#操作XML新建读取第一种第二种第三种 读取属性 介绍 XML (可扩展标记语言,eXtensible Markup Language) 是一种标记语言,它被设计用来传输和存储数据。 特点 可扩展性:由于…

C++---异常处理

异常处理 异常处理try语句块和throw表达式异常的抛出和捕获异常的抛出和匹配原则 异常安全异常规范标准异常 异常处理 异常是指存在于运行时的反常行为,这些行为超出了函数正常功能的范围。当程序的某部分检测到一个他无法处理的问题时,需要用到异常处理…

港联证券:停牌后复牌股价怎么算?

股票停牌是指买卖所或证券公司暂停一只股票的买卖,并不再出现在股票商场上。停牌的原因或许是公司内部事务调整、财政审计、重大事件或公司被收购等。当一家公司的股票停牌时,这对持有该公司股票的投资者或许会带来一些影响。因而,了解停牌后…

最新医疗界AI资讯,远程评估帕金森病症状的AI工具问世

原创 | 文 BFT机器人 1、AI模型快速评估,自动生成评估报告 罗切斯特大学研究人员开发的一种人工智能工具可以帮助帕金森病患者在几分钟内远程评估其症状的严重程度。《npj数字医学》杂志上的一项研究介绍了这种新工具,它能让用户在网络摄像头前通过敲击…

如何验证高压放大器的性能好坏呢

验证高压放大器的性能好坏,就需要考虑一系列关键指标和测试方法。这些指标包括频率响应、增益、失真、输出功率、噪声以及稳定性等,使我们能够全面评估放大器的性能和质量。下面西安安泰电子将介绍如何验证高压放大器的性能,并针对不同指标提…

眼镜镜片 光致变色镜片

声明 本文是学习GB-T 9105-2023 眼镜镜片 光致变色镜片. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 6 标志 6.1 通用要求 镜片的表面、包装或附带文件中,应标明下列参数: a) 产品名称、商标; b) 制造商或供…

Python面向对象编程:深入理解类、对象、继承和多态

💂 个人网站:【工具大全】【游戏大全】【神级源码资源网】🤟 前端学习课程:👉【28个案例趣学前端】【400个JS面试题】💅 寻找学习交流、摸鱼划水的小伙伴,请点击【摸鱼学习交流群】 面向对象编程&#xff0…

数据结构-----队列

目录 前言 队列 定义 队列的定义和操作方法 队列节点的定义 操作方式 顺序表实现队列(C/C代码) 链表实现队列(C/C代码) Python语言实现队列 前言 排队是我们日常生活中必不可少的一件事,去饭堂打饭的时候排队&a…

laravel框架 - 事件与监听器

一,绑定事件与监听器 在app\Providers下的EventServiceProvider.php中添加我们定义的事件与监听器 protected $listen [Registered::class > [SendEmailVerificationNotification::class,],App\ebvent\RegisterMessage>[//事件App\listeners\SendMessage//监…

【笔试强训选择题】Day43.习题(错题)解析

作者简介:大家好,我是未央; 博客首页:未央.303 系列专栏:笔试强训选择题 每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!!!&#xff…

为什么qt设置了utf-8 bom 格式后还是有乱码

有乱码 void SingleApplication::_showInstanceRunningDialog() {// 创建一个提示窗口QMessageBox msgBox;msgBox.setIcon(QMessageBox::Information);msgBox.setWindowTitle("应用已运行");msgBox.setText("应用程序已经在运行中。");msgBox.setStandardB…

计算机毕业设计 基于SSM+Vue的医院门诊互联电子病历管理信息系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ 🍅文末获取源码联系🍅 👇🏻 精…

GE PRG-MODEM 调节器模块

GE PRG-MODEM 调节器模块通常是工业自动化和控制系统中的一种通信设备,用于远程监测、控制和数据传输。以下是可能包括在该模块中的一些产品特点: 通信接口:PRG-MODEM 模块通常配备各种通信接口,例如串行接口(RS-232、…

docker报错Error response from daemon: Container xxx is not running

1. 问题 在移植了docker后,执行了sudo docker run --name myrosort -p 80:80 -d rosort 指令运行名为myrosort的容器,通过sudo docker ps -a也可以看到确实运行了 (base) neousysneousys-Nuvo-5000:~/wqw/docker/20230915$ sudo docker run --name myr…