Source code for neograd.nn.loss
import numpy as np
from ..autograd import sum as _sum, log
from ..autograd.ops.operation import Operation
from .activations import Softmax
[docs]class Loss:
'''Base class of all loss functions
'''
def __call__(self, outputs, targets):
'''Abstracts the forward method
Args:
outputs (Tensor): Outputs of Layer/Container/Model/Operation
targets (Tensor): Targets to be evaluated against
Returns:
Tensor of the result
'''
return self.forward(outputs, targets)
[docs] def get_num_examples(self, outputs_shape):
'''Gathers the number of examples
If dimensions of outputs_shape is 0, ie it is a scalar, then num_examples is 1
Else the first dimension value of outputs_shape is taken as num_examples
Args:
outputs_shape (tuple of int): Shape of the outputs
Returns:
Number of examples
'''
if len(outputs_shape) in (0, 1):
return 1
else:
return outputs_shape[0]
# <------------MEANSQUAREDERROR------------>
[docs]class MSE(Loss):
'''Mean Squared Error
'''
[docs] def forward(self, outputs, targets):
'''Forward pass of MSE
Args:
outputs (Tensor): Outputs of Layer/Container/Model/Operation
targets (Tensor): Targets to be evaluated against
Returns:
Tensor of the result
'''
num_examples = self.get_num_examples(outputs.shape)
cost = (1/(2*num_examples))*_sum((outputs-targets)**2)
return cost
def __repr__(self):
return f'MSE()'
def __str__(self):
return 'MeanSquaredError'
# <------------BINARYCROSSENTROPY------------>
[docs]class BCE(Loss):
'''Binary Cross Entropy
'''
[docs] def forward(self, outputs, targets, epsilon=1e-9):
'''Forward pass of BCE
epsilon used to prevent log0
Args:
outputs (Tensor): Outputs of Layer/Container/Model/Operation
targets (Tensor): Targets to be evaluated against
epsilon (float): For numerical stability of log Defaults to 1e-9
Returns:
Tensor of the result
'''
num_examples = self.get_num_examples(outputs.shape)
entropy = _sum((outputs*log(targets+epsilon)) + ((1-outputs)*(log(1-targets+epsilon))))
cost = (-1/num_examples)*entropy
return cost
def __repr__(self):
return f'BCE()'
def __str__(self):
return 'BinaryCrossEntropy'
# <------------CROSSENTROPY------------>
[docs]class CE(Loss):
'''Cross Entropy
'''
[docs] def forward(self, outputs, targets, epsilon=1e-9):
'''Forward pass of CE
epsilon used to prevent log0
Args:
outputs (Tensor): Outputs of Layer/Container/Model/Operation
targets (Tensor): Targets to be evaluated against
epsilon (float): For numerical stability of log Defaults to 1e-9
Returns:
Tensor of the result
'''
num_examples = self.get_num_examples(outputs.shape)
entropy = _sum(targets*log(outputs+epsilon))
cost = (-1/num_examples)*entropy
return cost
def __repr__(self):
return 'CE()'
def __str__(self):
return 'CrossEntropy'
# <------------SOFTMAXCROSSENTROPY------------>
[docs]class SoftmaxCE(Operation, Loss):
'''Implements Softmax activation with CrossEntropyLoss
Purpose of this is to eliminate costly Jacobian calculation involved
with vanilla softmax activation. Since Softmax is most commonly used with
Cross Entropy loss, if both are combined in one single Operation, then the derivative
is a very minimal subtraction between the softmax output and the targets.
So many intermediate backward calculations can be prevented with this.
Parameters:
axis (int or tuple of int): Axis along which to calculate the Softmax
Defaults to None
epsilon to prevent log0
'''
def __init__(self, axis):
self.axis = axis
[docs] def forward(self, outputs, targets, epsilon=1e-9):
'''Calculates Softmax of inputs and the Cross Entropy loss
Args:
outputs (Tensor): Outputs of Layer/Container/Model/Operation
targets (Tensor): Targets to be evaluated against
epsilon (float): For numerical stability of log Defaults to 1e-9
Returns:
Tensor of the result
'''
num_examples = self.get_num_examples(outputs.shape)
probs = Softmax.calc_softmax(outputs.data, axis=self.axis)
entropy = np.sum(targets.data*np.log(probs+epsilon))
cost = (-1/num_examples)*entropy
return self.get_result_tensor(cost, outputs, targets)
[docs] def backward(self, outputs, targets):
'''Sets the grad_fn of outputs
Args:
outputs (Tensor): Tensor which is usually the outputs of the last layer
of the network
targets (Tensor): Targets to be evaluated against
'''
def sce_backward(ug):
num_examples = self.get_num_examples(outputs.shape)
probs = Softmax.calc_softmax(outputs.data, axis=self.axis)
return (ug/num_examples)*(probs-targets.data) # ug is a scalar(1 by default), because loss calculated in forward is a scalar
outputs.set_grad_fn(sce_backward)
assert targets.requires_grad is False, 'Targets Tensor should have requires_grad=False'