PoissonNLLLoss

真实标签服从泊松分布的负对数似然损失,神经网络的输出作为泊松分布的参数 λ \lambda λ

泊松分布是一种离散分布,概率计算如下:

P ( Y = k ) = λ k k ! e − λ P(Y=k)=\frac{\lambda^{k}}{k !} e^{-\lambda} P(Y=k)=k!λkeλ

对于包含 N N N个样本的batch数据 D ( x , y ) D(x, y) D(x,y) y y y是样本对应的类别标签,服从泊松分布。 x x x y y y的维度相同。

(1)若 x x x是神经网络的输出,且未进行归一化和对数化处理。第 n n n个样本对应的损失 l n l_{n} ln 为:

由泊松分布的公式得到: P ( Y = y n ) = x n y n y n ! e − x n P(Y=y_{n})=\frac{x_{n}^{y_{n}}}{y_{n}!} e^{-x_{n}} P(Y=yn)=yn!xnynexn
l n = − l o g P ( Y = y n ) = x n − y n l o g x n + l o g ( y n ! ) l_{n}=-logP(Y=y_{n}) =x_{n}-y_{n}logx_{n}+ log(y_{n}!) ln=logP(Y=yn)=xnynlogxn+log(yn!)

(2)若 x x x 是神经网络的输出,进行了归一化和对数化处理。第 n n n个样本对应的损失 l n l_{n} ln 为:

将(1) 公式中的 x n x_{n} xn 替换为 e x p ( x n ) exp(x_{n}) exp(xn) l o g x n logx_{n} logxn替换为 x n x_{n} xn
l n = − l o g P ( Y = y n ) = e x p ( x n ) − y n x n + l o g ( y n ! ) l_{n}=-logP(Y=y_{n}) =exp(x_{n}) -y_{n}x_{n}+ log(y_{n}!) ln=logP(Y=yn)=exp(xn)ynxn+log(yn!)

最后一项 l o g ( y n ! ) log(y_{n}!) log(yn!)可以省略或者用斯特林公式(Stirling’s formula)近似。

一个样本可能对应多个输出,每个输出都服从泊松分布,有自己的泊松分布参数。因此, l n l_{n} ln可能是一个值,也可能是一个向量。

class PoissonNLLLoss(_Loss):
    __constants__ = ['log_input', 'full', 'eps', 'reduction']
    def __init__(self, log_input=True, full=False, size_average=None,
                 eps=1e-8, reduce=None, reduction='mean'):
        super(PoissonNLLLoss, self).__init__(size_average, reduce, reduction)
        self.log_input = log_input
        self.full = full
        self.eps = eps
    def forward(self, log_input, target):
        return F.poisson_nll_loss(log_input, target, log_input=self.log_input, full=self.full,
                                  eps=self.eps, reduction=self.reduction)

pytorch中通过torch.nn.PoissonNLLLoss类实现,也可以直接调用F.poisson_nll_loss 函数,代码中的size_averagereduce已经弃用。reduction有三种取值mean, sum, none,对应不同的返回 ℓ ( x , y ) \ell(x, y) (x,y)。 默认为mean,对应于一般情况下 l o s s loss loss的计算

L = { l 1 , … , l N } L=\left\{l_{1}, \ldots, l_{N}\right\} L={l1,,lN}

ℓ ( x , y ) = { L ⁡ ,  if reduction  =  ’none’  mean ⁡ ( L ) ,  if reduction  =  ’mean’  sum ⁡ ( L ) ,  if reduction  =  ’sum’  \ell(x, y)=\left\{\begin{array}{ll}\operatorname L, & \text { if reduction }=\text { 'none' } \\ \operatorname{mean}(L), & \text { if reduction }=\text { 'mean' } \\ \operatorname{sum}(L), & \text { if reduction }=\text { 'sum' }\end{array} \right. (x,y)=L,mean(L),sum(L), if reduction = ’none’  if reduction = ’mean’  if reduction = ’sum’ 

log_input 对应与输入是否进行对数化。

full表示loss计算是否保留 l o g ( y n ! ) log(y_{n}!) log(yn!)。 如果保留使用

  • y n ≤ 1 y_{n}\leq 1 yn1 l o g ( y n ! ) log(y_{n}!) log(yn!) 近似为0。
  • y n > 1 y_{n}>1 yn>1,使用斯特林公式(Stirling’s formula), l o g ( y n ! ) log(y_{n}!) log(yn!) 近似为 y n ∗ l o g ( y n ) − y n + 0.5 ∗ l o g ( 2 π y n ) . y_{n}∗log(y_{n})−y_{n}+0.5∗log(2\pi y_{n}). ynlog(yn)yn+0.5log(2πyn).

eps是为了防止 x n = = 0 x_{n}==0 xn==0时, l o g x n logx_{n} logxn计算出错

代码示例:

import torch
import torch.nn as nn
import math


def validate_loss(output, target, flag, full, eps=1e-08):
    val = 0
    for li_x, li_y in zip(output, target):
        for i, xy in enumerate(zip(li_x, li_y)):
            x, y = xy
            if flag:
                loss_val = math.exp(x) - y * x
                if full:
                    if y <= 1:
                        loss_val = math.exp(x) - y * x + 0
                    else:
                        loss_val = math.exp(x) - y * x + \
                                   y * math.log(y) - y + 0.5 * math.log(2 * math.pi * y)
            else:
                loss_val = x - y * math.log(x + eps)
                if full:
                    if y <= 1:
                        loss_val = x - y * math.log(x + eps) + 0
                    else:
                        loss_val = x - y * math.log(x + eps) + \
                                   y * math.log(y) - y + 0.5 * math.log(2 * math.pi * y)
            val += loss_val
    return val / output.nelement()


log_input = True
full = True
loss = nn.PoissonNLLLoss(log_input=log_input, full=full)
input_src = torch.Tensor([[0.8, 0.9, 0.3],
                          [0.8, 0.9, 0.3],
                          [0.8, 0.9, 0.3],
                          [0.8, 0.9, 0.3]])
target = torch.Tensor([[1, 3, 5], [1, 0, 6], [1, 4, 5], [1, 1, 7]])
print(input_src.size())
print(target.size())
output = loss(input_src, target)
print(output.item())
# 验证
validateloss = validate_loss(input_src, target, log_input, full)
print(validateloss.item())
# none
loss = nn.PoissonNLLLoss(log_input=log_input, full=full, reduction="none")
output = loss(input_src, target)
print(output)

结果输出:

torch.Size([4, 3])
torch.Size([4, 3])
3.0318071842193604
3.0318076610565186
tensor([[1.4255, 1.5237, 4.6207],
        [1.4255, 2.4596, 6.1152],
        [1.4255, 2.0169, 4.6207],
        [1.4255, 1.5596, 7.7631]])

GaussianNLLLoss

真实标签服从高斯分布的负对数似然损失,神经网络的输出作为高斯分布的均值和方差。

对于包含 N N N个样本的batch数据 D ( x , v a r , y ) D(x, var, y) D(x,var,y) x x x神经网络的输出,作为高斯分布的均值, v a r var var神经网络的输出,作为高斯分布的方差, y y y是样本对应的标签,服从高斯分布。 x x x y y y的维度相同, v a r var var x x x的维度相同,或者最后一个维度不同且最后一个维度为1,可以进行broadcast。

服从高斯分布的标签对应的概率是积分的形式(不懂如何推导,望指教)。 这里仅仅给出结论,具体可参考论文Estimating the mean and variance of the target probability distribution

n n n个样本对应的损失 l n l_{n} ln 为:

l n = 0.5 ∗ ( l o g ( m a x ( v a r n , e p s ) ) + ( x n − y n ) m a x ( v a r n , e p s ) ) l_{n}=0.5*\left(log(max(var_{n},eps)) + \frac{(x_{n}-y_{n})}{max(var_{n},eps)}\right) ln=0.5(log(max(varn,eps))+max(varn,eps)(xnyn))

e p s eps eps是为了防止 v a r n var_{n} varn为0

代码示例:

import torch
import torch.nn.functional as F
import torch.nn as nn
import math

torch.manual_seed(20)
loss = nn.GaussianNLLLoss(reduction='mean')
input = torch.randn(5, 2, requires_grad=True)
# 高斯分布的均值,神经网络输出
var = torch.ones(5, 2, requires_grad=True)  # 序列中标签对应的方差不同
# 高斯分布的方差,神经网络的输出
target = torch.randn(5, 2)
output = loss(input, target, var)
print(output.item())

var = torch.ones(5, 1, requires_grad=True)  # 序列中标签对应的方差相同
output = loss(input, target, var)
print(output.item())

var = torch.ones(5, requires_grad=True)  # 序列中标签对应的方差相同
output = loss(input, target, var)
print(output.item())

loss = nn.GaussianNLLLoss(reduction='none')
var = torch.ones(5, requires_grad=True)  # 序列中标签对应的方差相同
output = loss(input, target, var)
print(output)

结果输出:

2.7238247394561768
2.7238247394561768
2.7238247394561768
tensor([1.5049, 2.6215, 1.1505, 6.3066, 2.0357], grad_fn=<MulBackward0>)
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐