Skip to content
Snippets Groups Projects
Select Git revision
  • 9d1dcff196895bc9781170fe28336a6bdc2c8ed8
  • master default protected
2 results

functions.py

Blame
  • functions.py 17.48 KiB
    """
    Definition of different functions with forward and backward.
    
    Author: Joao A. Candido Ramos
    """
    
    import numpy as np
    
    from variable import Variable
    
    
    class _Function:
        def __init__(self, name, x, y=None):
            self.name = name
            self.x = x
            self.y = y
    
        def forward(self):
            self.x.add_child(self)
            if self.y is not None:
                self.y.add_child(self)
            result_variable = Variable(self.result)
            result_variable.grad_fn = self
            return result_variable
    
        def backward(self, grad, retain_graph):
            self._backward(grad)
            self.x.update_grad(self.dx, child=self, retain_graph=retain_graph)
            if self.y is not None:
                self.y.update_grad(self.dy, child=self, retain_graph=retain_graph)
    
            self.x.backward(retain_graph=retain_graph)
            if self.y is not None:
                self.y.backward(retain_graph=retain_graph)
    
    
    class Add(_Function):
        """Adition of two elements."""
    
        def __init__(self, x, y):
            super().__init__("Add", x, y)
            self.result = x.data + y.data
    
        def _backward(self, grad):
            self.dx = grad
            self.dy = grad
    
    
    class Sub(_Function):
        """Substraction of two elements."""
    
        def __init__(self, x, y):
            super().__init__("Sub", x, y)
            #######################################################################
            # Implement the forward pass and put the result in self.result.
            # The notbook provide you the formulas for this operation.
            #######################################################################
            self.result = self.x.data - self.y.data
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
        def _backward(self, grad):
            #######################################################################
            # Implement the derivative dx for this opetation and add the
            # result of the chain rule on self.dx.
            #######################################################################
            self.dx = grad
            self.dy = -grad
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
    
    class Mul(_Function):
        """Element-wise multiplication."""
    
        def __init__(self, x, y):
            super().__init__("Mul", x, y)
            #######################################################################
            # Implement the forward pass and put the result in self.result.
            # The notbook provide you the formulas for this operation.
            #######################################################################
            self.result = x.data * y.data
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
        def _backward(self, grad):
            #######################################################################
            # Implement the derivative dx for this opetation and add the
            # result of the chain rule on self.dx.
            #######################################################################
            self.dx = grad * self.y.data
            self.dy = grad * self.x.data
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
    
    class Div(_Function):
        """Element-wise divide."""
    
        def __init__(self, x, y):
            super().__init__("Div", x, y)
            #######################################################################
            # Implement the forward pass and put the result in self.result.
            # The notbook provide you the formulas for this operation.
            #######################################################################
            self.result = self.x.data / self.y.data
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
        def _backward(self, grad):
            #######################################################################
            # Implement the derivative dx for this opetation and add the
            # result of the chain rule on self.dx.
            #######################################################################
            self.dx = grad * (1/self.y.data)
            self.dy = grad * ((-1/(self.y.data)**2) * self.x.data)
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
    
    class MatMul(_Function):
        """Matrice multiplication."""
    
        def __init__(self, x, y):
            super().__init__("MatMul", x, y)
            #######################################################################
            # Implement the forward pass and put the result in self.result.
            # The notbook provide you the formulas for this operation.
            #######################################################################
            self.result = np.dot(self.x.data, self.y.data)
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
        def _backward(self, grad):
            #######################################################################
            # TODO: Implement the derivative dx for this opetation and add the
            # result of the chain rule on self.dx.
            #######################################################################
            self.dx = None
            self.dy = None
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
    
    class Exp(_Function):
        """Exponential function."""
    
        def __init__(self, x):
            super().__init__("Exp", x)
            #######################################################################
            # TODO: Implement the forward pass and put the result in self.result.
            # The notbook provide you the formulas for this operation.
            #######################################################################
            self.result = np.exp(self.x.data)
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
        def _backward(self, grad):
            #######################################################################
            # TODO: Implement the derivative dx for this opetation and add the
            # result of the chain rule on self.dx.
            #######################################################################
            self.dx = np.exp(self.x.data)
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
    
    class Log(_Function):
        """Logarithmic function."""
    
        def __init__(self, x):
            super().__init__("Exp", x)
            #######################################################################
            # TODO: Implement the forward pass and put the result in self.result.
            # The notbook provide you the formulas for this operation.
            #######################################################################
            self.result = 1/(self.x.data)
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
        def _backward(self, grad):
            #######################################################################
            # TODO: Implement the derivative dx for this opetation and add the
            # result of the chain rule on self.dx.
            #######################################################################
            self.dx = 1/(self.x.data)
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
    
    class Sin(_Function):
        """Sinus function."""
    
        def __init__(self, x):
            super().__init__("Sin", x)
            #######################################################################
            # TODO: Implement the forward pass and put the result in self.result.
            # The notbook provide you the formulas for this operation.
            #######################################################################
            self.result = np.cos(self.x.data)
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
        def _backward(self, grad):
            #######################################################################
            # TODO: Implement the derivative dx for this opetation and add the
            # result of the chain rule on self.dx.
            #######################################################################
            self.dx = np.cos(self.x.data)
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
    
    class Cos(_Function):
        """Cosinus function."""
    
        def __init__(self, x):
            super().__init__("Cos", x)
            #######################################################################
            # TODO: Implement the forward pass and put the result in self.result.
            # The notbook provide you the formulas for this operation.
            #######################################################################
            self.result = -np.sin(self.x.data)
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
        def _backward(self, grad):
            #######################################################################
            # TODO: Implement the derivative dx for this opetation and add the
            # result of the chain rule on self.dx.
            #######################################################################
            self.dx = -np.sin(self.x.data)
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
    
    class Tan(_Function):
        """Tangent function."""
    
        def __init__(self, x):
            super().__init__("Tan", x)
            #######################################################################
            # TODO: Implement the forward pass and put the result in self.result.
            # The notbook provide you the formulas for this operation.
            #######################################################################
            self.result = 1/np.square(np.cos(self.x.data))
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
        def _backward(self, grad):
            #######################################################################
            # TODO: Implement the derivative dx for this opetation and add the
            # result of the chain rule on self.dx.
            #######################################################################
            self.dx = 1/np.square(np.cos(self.x.data))
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
    
    # ACTIVATIONS
    
    class Sigmoid(_Function):
        """Sigmoid."""
    
        def __init__(self, x):
            super().__init__("Sigmoid", x)
            #######################################################################
            # TODO: Implement the forward pass and put the result in self.result.
            # The notbook provide you the formulas for this operation.
            #######################################################################
            self.result = None
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
        def _backward(self, grad):
            #######################################################################
            # TODO: Implement the derivative dx for this opetation and add the
            # result of the chain rule on self.dx.
            #######################################################################
            self.dx = None
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
    
    class Tanh(_Function):
        """Tanh."""
    
        def __init__(self, x):
            super().__init__("Tanh", x)
            #######################################################################
            # TODO: Implement the forward pass and put the result in self.result.
            # The notbook provide you the formulas for this operation.
            #######################################################################
            self.result = None
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
        def _backward(self, grad):
            #######################################################################
            # TODO: Implement the derivative dx for this opetation and add the
            # result of the chain rule on self.dx.
            #######################################################################
            self.dx = None
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
    
    class Softmax(_Function):
        """Softmax."""
    
        def __init__(self, x, dim):
            super().__init__("Softmax", x)
            self.dim = dim
            x_norm = x.data - np.max(x.data)
            exp = np.exp(x_norm)
            self.result = exp / np.sum(exp, axis=dim, keepdims=True)
    
        def _backward(self, grad):
            # q_i(delta_{i,j} - q_j)
            if self.dim == 0:
                res = self.result.T
                (N, D) = res.shape
                grad = grad.T
            elif self.dim == 1:
                res = self.result
                (N, D) = res.shape
            else:
                raise NotImplementedError(
                    "Backward for dim > 1 not implemented, Sorry :(")
    
            self.dx = res[:, None, :]
            self.dx = np.tensordot(self.dx, self.dx, axes=((1), (1)))
            self.dx = self.dx.swapaxes(1, 2)[np.arange(N), np.arange(N)]
    
            diag = np.tile(np.eye(D), (N, 1)).reshape(N, D, D)
            diag = res[:, :, None] * diag
    
            self.dx -= diag
            self.dx *= -1
    
            # chain rule
            self.dx = grad.dot(self.dx)[np.arange(N), np.arange(N)]
            if self.dim == 0:
                self.dx = self.dx.T
    
    
    class ReLu(_Function):
        """ReLu."""
    
        def __init__(self, x):
            super().__init__("ReLu", x)
            #######################################################################
            # TODO: Implement the forward pass and put the result in self.result.
            # The notbook provide you the formulas for this operation.
            #######################################################################
            self.result = None
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################
    
        def _backward(self, grad):
            #######################################################################
            # TODO: Implement the derivative dx for this opetation and add the
            # result of the chain rule on self.dx.
            #######################################################################
            self.dx = None
            #######################################################################
            # --------------------------- END OF YOUR CODE ------------------------
            #######################################################################