Hi. I also had a hard time grasping this, especially because of confusion between CrossEntropyLoss and NLLLoss. Not sure if implementation of negative log likelihood loss was ever explained in courses. In short - CrossEntropyLoss = LogSoftmax + NLLLoss. Here is a quick example with NLLLoss implemenation:

```
import torch
torch.manual_seed(1)
def NLLLoss(logs, targets):
out = torch.zeros_like(targets, dtype=torch.float)
for i in range(len(targets)):
out[i] = logs[i][targets[i]]
return -out.sum()/len(out)
x = torch.randn(3, 5)
y = torch.LongTensor([0, 1, 2])
cross_entropy_loss = torch.nn.CrossEntropyLoss()
log_softmax = torch.nn.LogSoftmax(dim=1)
x_log = log_softmax(x)
nll_loss = torch.nn.NLLLoss()
print("Torch CrossEntropyLoss: ", cross_entropy_loss(x, y))
print("Torch NLL loss: ", nll_loss(x_log, y))
print("Custom NLL loss: ", NLLLoss(x_log, y))
# Torch CrossEntropyLoss: tensor(1.8739)
# Torch NLL loss: tensor(1.8739)
# Custom NLL loss: tensor(1.8739)
```

NLL loss also supports ‘reduce’ parameter which is equal to True by default. In this case it would be something like this:

```
def NLLLoss(logs, targets, reduce=True):
out = torch.zeros_like(targets, dtype=torch.float)
for i in range(len(targets)):
out[i] = logs[i][targets[i]]
return -(out.sum()/len(out) if reduce else out)
```