日常pytorch编写“自创”的MRR损失函数
Posted 囚生CY
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了日常pytorch编写“自创”的MRR损失函数相关的知识,希望对你有一定的参考价值。
序言
最近重新开始学习Pytorch, 期初抽时间把 PyTorch 中文文档给过了一遍, 主要是把几个重要的模块中的方法熟悉了一下, markdown记了数千行, 但是纸上谈兵的学习方法终究还是浮于表面, 在阅读 RE2RNN 和 RE-NET 的过程中, 笔者发现已经无法理解作者模型代码里的逻辑(菜), 很是苦恼;
之前翻pypi发现PyTorch从1.4.0
后就没有给windows的wheel安装文件了, Tensorflow的GPU版本更新到2.0.x
版本之后就不再支持CUDA9的显卡配置了, 加上旧笔记本一直没有安装Anaconda, Python的版本停滞在3.6.1
, 里的Tensorflow和PyTorch的版本一直停留在1.8.0
和1.4.0
, 之后其实更新的方法跟之前有很大出入, 许多最新论文的项目代码已经不能再跑通; 新笔记本还没舍得装CUDA, 不过最近笔者发现一个 repository 囊括了从最老到最新, 所有平台上各种Python版本的torch和torchvision的wheel安装文件, 实在是帮了大忙;
于是本文主要围绕一个由MRR指标(Section 2.1)衍生出来的MRR损失函数的自定义编写问题展开, 第一部分从PyTorch的损失函数及优化器使用开始, 第三部分就笔者近期记录的一些零散的torch和torchvision库的使用点作为补充结束, 也许会持续更新, 也许会新开博客记录, 只是持续更新总成空谈, 很无奈; 第二部分将详细解析该损失函数的纯torch函数的编写思路, 并介绍一种Python内置的比较少见的排序方法, 希望能对各位朋友有所帮助;
目录
1 PyTorch中的损失函数与优化器
1.1 torch.nn中定义的损失函数
PyTorch中文文档
文档中似乎没有写出所有的损失函数, 详细可以查看E:\\Anaconda3\\Lib\\site-packages\\torch\\nn\\modules\\loss.py
中的定义;
class torch.nn.L1Loss(size_average=True)
: 绝对平均误差(MAE);torch.nn.MSELoss(size_average=True)
: 均方误差(MSE);torch.nn.CrossEntropyLoss(weight=None, size_average=True)
: 交叉熵损失(常用);
weight
可以输入一个1D张量, 包含n个权重值, 作为n个类别各自的权重, 这往往再样本标签分布不均时是很有意义的;
torch.nn.NLLLoss(weight=None, size_average=True)
: 负对数似然损失;torch.nn.NLLLoss2d(weight=None, size_average=True)
: 通常针对图片类型的2D负对数似然损失;
- 使用示例:
m = nn.Conv2d(16, 32, (3, 3)).float() loss = nn.NLLLoss2d() # input is of size nBatch x nClasses x height x width input = autograd.Variable(torch.randn(3, 16, 10, 10)) # each element in target has to have 0 <= value < nclasses target = autograd.Variable(torch.LongTensor(3, 8, 8).random_(0, 4)) output = loss(m(input), target) output.backward()
class torch.nn.KLDivLoss(weight=None, size_average=True)
: KL散度损失;
- KL散度即相对熵, 与交叉熵类似, 通常是针对离散概率分布的估计损失;
- 如真实分布为 [ 0.1 , 0.4 , 0.5 ] [0.1, 0.4, 0.5] [0.1,0.4,0.5], 预测分布为 [ 0.4 , 0.2 , 0.4 ] [0.4, 0.2, 0.4] [0.4,0.2,0.4], 则KL散度计算公式如下: D K L ( P ∣ Q ) = 0.1 × log ( 0.1 0.4 ) + 0.4 × log ( 0.4 0.2 ) + 0.5 × log ( 0.5 0.4 ) = 0.25 D K L ( Q ∣ P ) = 0.4 × log ( 0.4 0.1 ) + 0.2 × log ( 0.2 0.4 ) + 0.4 × log ( 0.4 0.5 ) = 0.327 (1) D_KL(P|Q)=0.1×\\log(\\frac0.10.4)+0.4×\\log(\\frac0.40.2)+0.5×\\log(\\frac0.50.4)=0.25\\\\D_KL(Q|P)=0.4×\\log(\\frac0.40.1)+0.2×\\log(\\frac0.20.4)+0.4×\\log(\\frac0.40.5)=0.327\\tag1 DKL(P∣Q)=0.1×log(0.40.1)+0.4×log(0.20.4)+0.5×log(0.40.5)=0.25DKL(Q∣P)=0.4×log(0.10.4)+0.2×log(0.40.2)+0.4×log(0.50.4)=0.327(1)
- 由(1)式可以看出KL散度是不满足交换律的, 这是一般用交叉熵而非相对熵的原因之一;
- 另一个原因是, 从计算过程可以看出, 如果有某个离散分布概率为0, KL散度将趋于无穷;
torch.nn.BCELoss(weight=None, size_average=True)
: 二进制交叉熵;
- 文档中表述为用于计算Auto-Encoder的Reconstruction Error;
torch.nn.MarginRankingLoss(margin=0, size_average=True)
torch.nn.HingeEmbeddingLoss(size_average=True)
torch.nn.MultiLabelMarginLoss(size_average=True)
class torch.nn.SmoothL1Loss(size_average=True)
torch.nn.SoftMarginLoss(size_average=True)
torch.nn.MultiLabelSoftMarginLoss(weight=None, size_average=True)
torch.nn.CosineEmbeddingLoss(margin=0, size_average=True)
torch.nn.MultiMarginLoss(p=1, margin=1, weight=None, size_average=True)
1.2 torch.optim中定义的优化器
PyTorch中文文档
文档中似乎没有详细写出所有的优化器, 常用的是Adam, 可以观察E:\\Anaconda3\\Lib\\site-packages\\torch\\optim
目录下的所有函数即可;
torch.optim.Adadelta(params, lr=1.0, rho=0.9, eps=1e-6, weight_decay=0)
torch.optim.Adagrad(params, lr=1.0, lr_decay=0, weight_decay=0, initial_accumulator_value=0, eps=1e-10)
torch.optim.Adam(params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0, amsgrad=False)
torch.optim.Adadelta(params, lr=1.0, rho=0.9, eps=1e-6, weight_decay=0)
torch.optim.Adamax(params, lr=2e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0)
torch.optim.AdamW(params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=1e-2, amsgrad=False)
torch.optim.ASGD(params, lr=1e-2, lambd=1e-4, alpha=0.75, t0=1e6, weight_decay=0)
torch.optim.LBFGS(params, lr=1, max_iter=20, max_eval=None, tolerance_grad=1e-7, tolerance_change=1e-9, history_size=100, line_search_fn=None)
torch.optim.RMSprop(params, lr=1e-2, alpha=0.99, eps=1e-8, weight_decay=0, momentum=0, centered=False)
torch.optim.Rprop(params, lr=1e-2, etas=(0.5, 1.2), step_sizes=(1e-6, 50))
torch.optim.SGD(params, lr=required, momentum=0, dampening=0, weight_decay=0, nesterov=False)
torch.optim.SparseAdam(params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8)
上述所有的params参数一般设置为
model.parameters()
;
1.3 损失函数与优化器在模型训练中的使用方法
以下代码转载自 CSDN@dongyangY 的博客 使用PyTorch实现CNN , 笔者觉得写得非常详细, 非常适合初学者对Pytorch从数据处理到模型训练以及最终的评估全流程的熟悉, 侵删!
import torch
from torch.utils import data
from torch.autograd import Variable
import torchvision as tv
from torchvision.datasets import mnist
from maplotlib import pyplot as plt
data_path = r'D:\\code\\python\\project\\other\\torch\\data'
model_saving_path = r'D:\\code\\python\\project\\other\\torch\\model\\cnnnet.model'
# 导入数据
transformer = tv.transforms.Compose([
tv.transforms.ToTensor(),
tv.transforms.Normalize([.5], [.5])
])
train_data = mnist.MNIST(data_path, train=True, transform=transformer, download=False)
test_data = mnist.MNIST(data_path, train=False, transform=transformer, download=False)
train_loader = data.DataLoader(train_data, batch_size=128, shuffle=True)
test_loader = data.DataLoader(test_data, batch_size=100, shuffle=True)
# 构建网络模型
class CNNnet(torch.nn.Module):
def __init__(self):
super(CNNnet,self).__init__()
self.conv1 = torch.nn.Sequential(
torch.nn.Conv2d(in_channels=1, out_channels=16, kernel_size=3, stride=2, padding=1),
torch.nn.BatchNorm2d(16),
torch.nn.ReLU(),
)
self.conv2 = torch.nn.Sequential(
torch.nn.Conv2d(in_channels=16, out_channels=32, kernel_size=3, stride=2, padding=1),
torch.nn.BatchNorm2d(32),
torch.nn.ReLU()
)
self.conv3 = torch.nn.Sequential(
torch.nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=2, padding=1),
torch.nn.BatchNorm2d(64),
torch.nn.ReLU()
)
self.conv4 = torch.nn.Sequential(
torch.nn.Conv2d(in_channels=64, out_channels=64, kernel_size=2, stride=2, padding=0),
torch.nn.BatchNorm2d(64),
torch.nn.ReLU()
)
self.mlp1 = torch.nn.Linear(in_features=2*2*64, out_features=100)
self.mlp2 = torch.nn.Linear(in_features=100, out_features=10)
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x = self.conv3(x)
x = self.conv4(x)
x = self.mlp1(x.view(x.size(0), -1))
x = self.mlp2(x)
return x
model = CNNnet()
# 定义损失函数与优化器
loss_func = torch.nn.CrossEntropyLoss()
opt = torch.optim.Adam(model.parameters(), lr=0.001)
# 训练网络: 损失函数调用后需要backward传播, 优化器则每次需要清零后再前进一步
loss_count = []
for epoch in range(2):
for i, (x, y) in enumerate(train_loader):
batch_x = Variable(x) # torch.Size([128, 1, 28, 28])
batch_y = Variable(y) # torch.Size([128])
out = model(batch_x) # 获取最后输出: torch.Size([128, 10])
loss = loss_func(out, batch_y) # 获取损失
opt.zero_grad() # 清空上一步残余更新参数值
loss.backward() # 误差反向传播, 计算参数更新值
opt.step() # 将参数更新值施加到net的parmeters上
if i % 20 == 0:
loss_count.append(loss)
print(':\\t'.format(i), loss.item())
torch.save(model, model_saving_path)
if i % 100 == 0:
for a,b in test_loader:
test_x = Variable(a)
test_y = Variable(b)
out = model(test_x)
# print('test_out:\\t', torch.max(out,1)[1])
# print('test_y:\\t', test_y)
accuracy = torch.max(out, 1)[1].numpy() == test_y.numpy()
print('accuracy:\\t', accuracy.mean())
break
# 损失函数绘图
plt.figure('PyTorch_CNN_Loss')
plt.plot(loss_count, label='Loss')
plt.xlabel('step')
plt.ylabel('loss value')
plt.legend()
plt.show()
1.4 自定义损失函数的编写方法
- 方法: 继承
torch.nn.Module
类后, 编写其中的forward
函数, 输入参数为真实标签值与预测标签值; - 以交叉熵损失的源码为例:
from .. import functional as F
class CrossEntropyLoss(_WeightedLoss):
__constants__ = ['ignore_index', 'reduction']
ignore_index: int
def __init__(self, weight: Optional[Tensor] = None, size_average=None, ignore_index: int = -100,
reduce=None, reduction: str = 'mean') -> None:
super(CrossEntropyLoss, self).__init__(weight, size_average, reduce, reduction)
self.ignore_index = ignore_index
def forward(self, input: Tensor, target: Tensor) -> Tensor:
return F.cross_entropy(input, target, weight=self.weight,
ignore_index=self.ignore_index, reduction=self.reduction)
- 以下代码转载自 CSDN@我的辉 的博客 pytorch系列教程(四)-自定义损失函数 , 侵删!
class Loss_yolov1(nn.Module):
def __init__(self):
super(Loss_yolov1,self).__init__()
def forward(self, pred, labels):
"""
:param pred: (batchsize,30,7,7)的网络输出数据
:param labels: (batchsize,30,7,7)的样本标签数据
:return: 当前批次样本的平均损失
"""
num_gridx, num_gridy = labels.size()[-2:] # 划分网格数量
num_b = 2 # 每个网格的bbox数量
num_cls = 20 # 类别数量
noobj_confi_loss = 0. # 不含目标的网格损失(只有置信度损失)
coor_loss = 0. # 含有目标的bbox的坐标损失
obj_confi_loss = 0. # 含有目标的bbox的置信度损失
class_loss = 0. # 含有目标的网格的类别损失
n_batch = labels.size()[0] # batchsize的大小
# 可以考虑用矩阵运算进行优化, 提高速度, 为了准确起见, 这里还是用循环
for i in range(n_batch): # batchsize循环
for n in range(7): # x方向网格循环
for m in range(7): # y方向网格循环
if labels[i,4,m,n]==1:# 如果包含物体
# 将数据(px,py,w,h)转换为(x1,y1,x2,y2)
# 先将px,py转换为cx,cy, 即相对网格的位置转换为标准化后实际的bbox中心位置cx,xy
# 然后再利用(cx-w/2,cy-h/2,cx+w/2,cy+h/2)转换为xyxy形式, 用于计算iou
bbox1_pred_xyxy = ((pred[i,0,m,n]+m)/num_gridx - pred[i,2,m,n]/2,(pred[i,1,m,n]+n)/num_gridy - pred[i,3,m,n]/2,
(pred[i,0,m,n]+m)以上是关于日常pytorch编写“自创”的MRR损失函数的主要内容,如果未能解决你的问题,请参考以下文章