Repository: pytorch/pytorch

Issue:
Poisson NLL loss
There is no log-likelihood loss with underlying target's Poisson distribution. Would be great to add. I can do it.


Discussion:
I'm not sure about where implementations of different losses are stored. To create new loss I need:

1.  to add new class to `torch/nn/modules/loss.py`
2. then register implementation of the loss somewhere in `torch/nn/_functions/thnn`
3. But what are the locations for these implementations? `torch/legacy` or `torch/nn/functional.py` or `torch/nn/_functions/loss.py` or some C code?
The C/Cuda implementation can be found in `libs/THNN` and `libs/THCUNN`, which are automatically wrapped in python via `torch/nn/_functions/thnn/auto.py`. If the function can be trivially implemented using autograd, the implementation could go to `torch/nn/functional`, followed by a wrapper in the modules. If you want to write the backprop by hand and in python (for example for optimization reasons), then you could write it in `torch/nn/_functions/loss.py` for example.

Resolving patch:
diff --git a/torch/nn/functional.py b/torch/nn/functional.py
--- a/torch/nn/functional.py
+++ b/torch/nn/functional.py
@@ -2,6 +2,7 @@
 
 from numbers import Integral
 import warnings
+import math
 
 import torch
 from . import _functions
@@ -589,6 +590,37 @@ def nll_loss(input, target, weight=None, size_average=True):
     return f(input, target)
 
 
+def poisson_nll_loss(input, target, log_input=True, full=False, size_average=True):
+    r"""Poisson negative log likelihood loss.
+
+    See :class:`~torch.nn.PoissonNLLLoss` for details.
+
+    Args:
+        input: expectation of underlying Poisson distribution.
+        target: random sample :math:`target \sim Pois(input)`.
+        log_input: if True the loss is computed as
+            `exp(input) - target * input`, if False then loss is
+            `input - target * log(input)`.
+        full: whether to compute full loss, i. e. to add the Stirling
+            approximation term
+            `target * log(target) - target + 0.5 * log(2 * pi * target)`.
+        size_average: By default, the losses are averaged over observations for
+            each minibatch. However, if the field sizeAverage is set to False,
+            the losses are instead summed for each minibatch.
+    """
+    if log_input:
+        loss = torch.exp(input) - target * input
+    else:
+        loss = input - target * torch.log(input)
+    if full:
+        mask = target > 1
+        loss[mask] += (target * torch.log(target) - target + 0.5 * torch.log(2 * math.pi * target))[mask]
+    if size_average:
+        return torch.mean(loss)
+    else:
+        return torch.sum(loss)
+
+
 def kl_div(input, target, size_average=True):
     r"""The `Kullback-Leibler divergence`_ Loss.
 
diff --git a/torch/nn/modules/__init__.py b/torch/nn/modules/__init__.py
--- a/torch/nn/modules/__init__.py
+++ b/torch/nn/modules/__init__.py
@@ -8,7 +8,7 @@
 from .loss import L1Loss, NLLLoss, KLDivLoss, MSELoss, BCELoss, BCEWithLogitsLoss, NLLLoss2d, \
     CosineEmbeddingLoss, HingeEmbeddingLoss, MarginRankingLoss, \
     MultiLabelMarginLoss, MultiLabelSoftMarginLoss, MultiMarginLoss, \
-    SmoothL1Loss, SoftMarginLoss, CrossEntropyLoss, TripletMarginLoss
+    SmoothL1Loss, SoftMarginLoss, CrossEntropyLoss, TripletMarginLoss, PoissonNLLLoss
 from .container import Container, Sequential, ModuleList, ParameterList
 from .pooling import AvgPool1d, AvgPool2d, AvgPool3d, MaxPool1d, MaxPool2d, MaxPool3d, \
     MaxUnpool1d, MaxUnpool2d, MaxUnpool3d, FractionalMaxPool2d, LPPool2d, AdaptiveMaxPool1d, \
@@ -32,7 +32,7 @@
     'Sigmoid', 'Tanh', 'Softmax', 'Softmax2d', 'LogSoftmax', 'ELU', 'SELU', 'GLU', 'Hardshrink',
     'LeakyReLU', 'LogSigmoid', 'Softplus', 'Softshrink', 'PReLU', 'Softsign', 'Softmin',
     'Tanhshrink', 'RReLU', 'L1Loss', 'NLLLoss', 'KLDivLoss', 'MSELoss', 'BCELoss', 'BCEWithLogitsLoss',
-    'NLLLoss2d', 'CosineEmbeddingLoss', 'HingeEmbeddingLoss', 'MarginRankingLoss',
+    'NLLLoss2d', 'PoissonNLLLoss', 'CosineEmbeddingLoss', 'HingeEmbeddingLoss', 'MarginRankingLoss',
     'MultiLabelMarginLoss', 'MultiLabelSoftMarginLoss', 'MultiMarginLoss', 'SmoothL1Loss',
     'SoftMarginLoss', 'CrossEntropyLoss', 'Container', 'Sequential', 'ModuleList',
     'ParameterList', 'AvgPool1d', 'AvgPool2d', 'AvgPool3d', 'MaxPool1d', 'MaxPool2d',
diff --git a/torch/nn/modules/loss.py b/torch/nn/modules/loss.py
--- a/torch/nn/modules/loss.py
+++ b/torch/nn/modules/loss.py
@@ -135,6 +135,47 @@ class NLLLoss2d(_WeightedLoss):
     pass
 
 
+class PoissonNLLLoss(_Loss):
+    r"""Negative log likelihood loss with Poisson distribution of target.
+
+    The loss can be described as::
+
+        target ~ Pois(input)
+        loss(input, target) = input - target * log(input) + log(target!)
+
+    The last term can be omitted or approximised with Stirling formula. The
+    approximation is used for target values more than 1. For targets less or
+    equal to 1 zeros are added to the loss.
+
+    Args:
+        log_input (bool, optional): if True the loss is computed as
+            `exp(input) - target * input`, if False the loss is
+            `input - target * log(input)`.
+        full (bool, optional): whether to compute full loss, i. e. to add the
+            Stirling approximation term
+            `target * log(target) - target + 0.5 * log(2 * pi * target)`.
+        size_average (bool, optional): By default, the losses are averaged over
+            observations for each minibatch. However, if the field size_average
+            is set to False, the losses are instead summed for each minibatch.
+
+    Examples::
+        >>> loss = nn.PoissonNLLLoss()
+        >>> log_input = autograd.Variable(torch.randn(5, 2), requires_grad=True)
+        >>> target = autograd.Variable(torch.randn(5, 2))
+        >>> output = loss(log_input, target)
+        >>> output.backward()
+    """
+    def __init__(self, log_input=True, full=False, size_average=True):
+        super(PoissonNLLLoss, self).__init__()
+        self.log_input = log_input
+        self.full = full
+        self.size_average = size_average
+
+    def forward(self, log_input, target):
+        _assert_no_grad(target)
+        return F.poisson_nll_loss(log_input, target, self.log_input, self.full, self.size_average)
+
+
 class KLDivLoss(_WeightedLoss):
     r"""The `Kullback-Leibler divergence`_ Loss
 


Test patch:
diff --git a/test/test_nn.py b/test/test_nn.py
--- a/test/test_nn.py
+++ b/test/test_nn.py
@@ -3401,6 +3401,24 @@ def add_test(test):
     ),
 ]
 
+
+new_criterion_tests = [
+    dict(
+        module_name='PoissonNLLLoss',
+        input=torch.randn(2, 3, 4, 5),
+        target=torch.randn(2, 3, 4, 5).floor_().abs_(),
+        desc='reduced_loss',
+    ),
+    dict(
+        module_name='PoissonNLLLoss',
+        constructor_args=(False, True, True),
+        input=torch.randn(2, 3, 4, 5).abs_().add_(0.001),
+        target=torch.randn(2, 3, 4, 5).floor_().abs_(),
+        desc='full_loss',
+    ),
+]
+
+
 for test_params in module_tests + new_module_tests:
     # TODO: CUDA is not implemented yet
     if 'constructor' not in test_params: