diff --git a/neural_nets/losses/losses.py b/neural_nets/losses/losses.py index 749b1eb..eebe48d 100644 --- a/neural_nets/losses/losses.py +++ b/neural_nets/losses/losses.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod import numpy as np -from tests import assert_is_binary, assert_is_stochastic +# from tests import assert_is_binary, assert_is_stochastic class ObjectiveBase(ABC): @@ -385,3 +385,66 @@ def grad(self, Y_fake, module, Y_real=None, gradInterp=None): else: raise ValueError("Unrecognized module: {}".format(module)) return grad + + +class Cosine(ObjectiveBase): + def __init__(self): + super().__init__() + + def __call__(self, y, y_pred): + return self.loss(y, y_pred) + + def __str__(self): + return "Cosine Proximity" + + @staticmethod + def loss(y, y_pred): + """ + Compute the Cosine distance between 1-D arrays. + + The Cosine distance between `u` and `v`, is defined as + .. math:: + 1 - \\frac{u \\cdot v} + {||u||_2 ||v||_2}. + where :math:`u \\cdot v` is the dot product of :math:`u` and + :math:`v`. + + Parameters + ---------- + y : numpy array of shape (n, m) + + y_pred : numpy array of shape (n, m) + + + Returns + ------- + loss : float + + """ + def l2_normalize(x, axis=-1): + y = np.max(np.sum(x ** 2, axis, keepdims=True), axis, keepdims=True) + return x / np.sqrt(y) + + y_true = l2_normalize(y, axis=-1) + y_pred = l2_normalize(y_pred, axis=-1) + return 1. - np.sum(y_true * y_pred, axis=-1) + + @staticmethod + def grad(y, y_pred, z, act_fn): + """ + + + Parameters + ---------- + y : numpy array of shape (n, m) + + y_pred : numpy array of shape (n, m) + + + Returns + ------- + grad : numpy array of shape (n, m) + + """ + return 0.0 + diff --git a/neural_nets/tests/tests.py b/neural_nets/tests/tests.py index 74b619f..fa361a2 100644 --- a/neural_nets/tests/tests.py +++ b/neural_nets/tests/tests.py @@ -122,6 +122,10 @@ def test_losses(N=50): time.sleep(1) test_WGAN_GP_loss(N) + print("Testing Cosine Loss") + time.sleep(1) + test_cosine_proximity(N) + def test_activations(N=50): print("Testing Sigmoid activation") @@ -397,6 +401,31 @@ def test_WGAN_GP_loss(N=None): i += 1 +def test_cosine_proximity(N=None): + from scipy.spatial.distance import cosine + from losses import Cosine + + N = np.inf if N is None else N + + mine = Cosine() + gold = cosine + + i = 1 + while i < N: + vector_length_max = 100 + + for j in range(2, vector_length_max): + x = np.random.uniform(0., 1., [j, ]) + y = np.random.uniform(0., 1., [j, ]) + + dist = mine(x, y) + dist_true = gold(x, y) + # print(dist, dist_true - 1.) + assert_almost_equal(dist, dist_true) + print('PASSED.') + i += 1 + + ####################################################################### # Loss Function Gradients # ####################################################################### @@ -565,7 +594,6 @@ def test_softsign_activation(N=None): i += 1 - ####################################################################### # Activation Gradients # #######################################################################