def test_edge_cases_model_evaluate(self):
        """`Model.evaluate`: Edge Case Validator.

        Tests the behavior of `evaluate` with edge cases.

        Raises:
            Exception: If at least one `Exception` raised is not of the expected
                kind.

        """
        X = random_matrix(self.data_shape)
        """np.matrix: Random-valued feature set."""
        Y = random_matrix((self.data_shape[0], 1))
        """np.matrix: Random-valued observation set."""
        params = compose(tuple, map)(random_matrix, self.shapes)
        """tuple of np.matrix: Random-valued parameters."""

        with self.assertRaises(_InvalidFeatureSetError):
            # Empty matrix instead of matrix `X`.
            self.model.evaluate(np.matrix([[]]), Y, params=params)

        with self.assertRaises(_InvalidObservationSetError):
            # Empty matrix instead of matrix `Y`.
            self.model.evaluate(X, np.matrix([[]]), params=params)

        with self.assertRaises(_InvalidModelParametersError):
            # Empty matrix instead of matrix `Y`.
            self.model.evaluate(X, Y, params=(np.matrix([[]]), ))
Esempio n. 2
0
        def action():
            """Gradient Checker Update Action.

            Defines the routine to run after the feature sets and parameters
            have been validated.

            Returns:
                (list of float, list of float): The evaluation error along with
                    the predicted observations.

            """
            grad_norms = [0.0 for i in range(perturbations)]
            """list of float: Norms of all analytical gradients."""
            ngrad_norms = [0.0 for i in range(perturbations)]
            """list of float: Norms of all numerical gradients."""

            for i in range(perturbations):
                X = random_matrix((n, d), min_val=0.0, max_val=1.0)
                """np.matrix: Random-valued feature set."""
                Y = random_matrix((n, 1), min_val=0.0, max_val=1.0)
                """np.matrix: Random-valued observation set."""

                grads = self.gradient(X, Y)
                """tuple of np.matrix: Analytical gradients."""
                ngrads = self.numerical_gradient(X, Y)
                """tuple of np.matrix: Numberical gradients."""

                # Add gradient norms to norm totals.
                for j in range(len(grads)):
                    grad_norms[i] += np.linalg.norm(grads[j])**2
                    ngrad_norms[i] += np.linalg.norm(ngrads[j])**2

            return grad_norms, ngrad_norms
    def test_invalid_args_model_train(self):
        """`Model.train`: Argument Validator.

        Tests the behavior of `train` with invalid argument counts and values.

        Raises:
            Exception: If at least one `Exception` raised is not of the expected
                kind.

        """
        n, d = self.data_shape
        """(int, int): Number of data points and number of features."""

        X = random_matrix(self.data_shape)
        """np.matrix: Random-valued feature set."""

        Y = random_matrix((n, 1))
        """np.matrix: Random-valued observation set."""

        with self.assertRaises(TypeError):
            # No arguments.
            self.model.train()

        with self.assertRaises(TypeError):
            # Too many arguments.
            self.model.train(X, Y, Y)

        with self.assertRaises(TypeError):
            # Kwarg.
            self.model.train(X, Y, key="value")

        with self.assertRaises(_InvalidFeatureSetError):
            # `None` instead of feature set `X`.
            self.model.train(None, Y)

        with self.assertRaises(_InvalidFeatureSetError):
            # ndarray instead of feature set `X`.
            self.model.train(np.zeros((n, d)), Y)

        with self.assertRaises(_InvalidObservationSetError):
            # `None` instead of observation set `Y`.
            self.model.train(X, None)

        with self.assertRaises(_InvalidObservationSetError):
            # ndarray instead of observation set `Y`.
            self.model.train(X, np.zeros((n, 1)))

        with self.assertRaises(_IncompatibleDataSetsError):
            # Incompatible feature set.
            self.model.train(random_matrix((d, n)), Y)

        with self.assertRaises(_IncompatibleDataSetsError):
            # Incompatible observation set.
            self.model.train(X, random_matrix((n + 1, 1)))
    def test_random_model_augment(self):
        """`Model.augment`: Randomized Validator.

        Tests the behavior of `augment` by feeding it randomly generated
        arguments.

        Raises:
            AssertionError: If `augment` needs debugging.

        """
        for i in range(self.n_tests):
            X = random_matrix(self.data_shape)
            """np.matrix: Random-valued matrix."""

            new_X = self.model.augment(X)
            """np.matrix: Test input."""

            # Augmentation should also be a matrix.
            self.assertIsInstance(X, np.matrix)

            # Total number of values in augmented matrix should be greter than
            # or equal to the number of values in the original matrix.
            if new_X.shape[0] != X.shape[0] or new_X.shape[1] != X.shape[1]:
                self.assertGreaterEqual(new_X.shape[0], X.shape[0])
                self.assertGreaterEqual(new_X.shape[1], X.shape[1])
Esempio n. 5
0
    def _gradient_checker(self, perturbations, shape=DEFAULT_SHAPE_CHECKER):
        """Model Gradient Validator.

        Checks the accuracy of the analytical computation of all gradients
        needed by the model by juxtaposing the norms of all numerical gradients
        with those of the analytical gradient.

        Args:
            perturbations (int): Number of comparison points to consider in
                norm computation.
            shape ((int, int), optional): Number of data points and number of
                features. Defaults to `DEFAULT_SHAPE_CHECKER`.

        Returns:
            (list of float, list of float): Norms for all analytical and
                numerical gradients, respectively.

        """
        n, d = shape
        """(int, int): Number of data points and number of features."""
        def action():
            """Gradient Checker Update Action.

            Defines the routine to run after the feature sets and parameters
            have been validated.

            Returns:
                (list of float, list of float): The evaluation error along with
                    the predicted observations.

            """
            grad_norms = [0.0 for i in range(perturbations)]
            """list of float: Norms of all analytical gradients."""
            ngrad_norms = [0.0 for i in range(perturbations)]
            """list of float: Norms of all numerical gradients."""

            for i in range(perturbations):
                X = random_matrix((n, d), min_val=0.0, max_val=1.0)
                """np.matrix: Random-valued feature set."""
                Y = random_matrix((n, 1), min_val=0.0, max_val=1.0)
                """np.matrix: Random-valued observation set."""

                grads = self.gradient(X, Y)
                """tuple of np.matrix: Analytical gradients."""
                ngrads = self.numerical_gradient(X, Y)
                """tuple of np.matrix: Numberical gradients."""

                # Add gradient norms to norm totals.
                for j in range(len(grads)):
                    grad_norms[i] += np.linalg.norm(grads[j])**2
                    ngrad_norms[i] += np.linalg.norm(ngrads[j])**2

            return grad_norms, ngrad_norms

        params = (random_matrix((d, 1), min_val=0.0, max_val=1.0), )
        """tuple of np.matrix: Random-valued parameters."""

        return self._update_model(action, params=params)
    def test_random_model_train(self):
        """`Model.train`: Randomized Validator.

        Tests the behavior of `train` by feeding it randomly
        generated arguments.

        Raises:
            AssertionError: If `train` needs debugging.

        """
        for i in range(self.n_tests):
            X = random_matrix(self.data_shape)
            """np.matrix: Random-valued feature set."""
            Y = random_matrix((self.data_shape[0], 1))
            """np.matrix: Random-valued observation set."""

            # Model parameters should be uninitialized at this point.
            self.assertIsNone(self.model.params)

            self.model.init_params(X)

            # Model parameters should be set at this point.
            self.assertIsNotNone(self.model.params)

            err = self.model.evaluate(X, Y)[0]
            """float: Evaluation error prior to training."""
            train_err = self.model.train(X, Y)
            """float: Test input."""

            # Evaluation error should be number.
            self.assertEqual(type(train_err), np.float64)

            # Evaluation error prior to training should larger than after
            # training.
            self.assertLess(train_err, err)

            del self.model.params
    def test_random_model_predict(self):
        """`Model.predict`: Randomized Validator.

        Tests the behavior of `predict` by feeding it randomly generated
        arguments.

        Raises:
            AssertionError: If `predict` needs debugging.

        """
        for i in range(self.n_tests):
            X = random_matrix(self.data_shape)
            """np.matrix: Random-valued feature set."""

            params = compose(tuple, map)(random_matrix, self.shapes)
            """tuple of np.matrix: Random-valued parameters."""

            # First, test `params` as a method argument.
            Y_hat1 = self.model.predict(X, params=params)
            """np.matrix: Test input 1."""

            # Gradients should be a tuple.
            self.assertIsInstance(Y_hat1, np.matrix)

            # All params should have a gradient.
            self.assertEqual(Y_hat1.shape, (X.shape[0], 1))

            # Model parameters should not be set at this point.
            self.assertIsNone(self.model.params)

            # Finally, test `params` as attribute.
            self.model.params = params

            Y_hat2 = self.model.predict(X)
            """np.matrix: Test input 2."""

            # Gradients should be a tuple.
            self.assertIsInstance(Y_hat2, np.matrix)

            # All params should have a gradient.
            self.assertEqual(Y_hat2.shape, (X.shape[0], 1))

            # Model parameters should be set at this point.
            self.assertIsNotNone(self.model.params)

            # Norms of test inputs should match.
            self.assertEqual(np.linalg.norm(Y_hat1), np.linalg.norm(Y_hat2))

            del self.model.params
    def test_invalid_args_model_augment(self):
        """`Model.augment`: Argument Validator.

        Tests the behavior of `augment` with invalid argument counts and values.

        Raises:
            Exception: If at least one `Exception` raised is not of the expected
                kind.

        """
        n, d = self.data_shape
        """(int, int): Number of data points and number of features."""

        with self.assertRaises(TypeError):
            # No arguments.
            self.model.augment()

        with self.assertRaises(TypeError):
            # More parameters than expected.
            self.model.augment(12, 12)

        with self.assertRaises(_InvalidFeatureSetError):
            # `None` instead of matrix `X`.
            self.model.augment(None)

        with self.assertRaises(_InvalidFeatureSetError):
            # Integer instead of matrix `X`.
            self.model.augment(123)

        with self.assertRaises(_InvalidFeatureSetError):
            # 1-Tuple of a matrix instead of matrix `X`.
            self.model.augment((random_matrix((n, d)), ))

        with self.assertRaises(_InvalidFeatureSetError):
            # List of an empty list instead of matrix `X`.
            self.model.augment([[]])

        with self.assertRaises(_InvalidFeatureSetError):
            # Array instead of matrix `X`.
            self.model.augment(np.ndarray(n))
    def test_edge_cases_model_predict(self):
        """`Model.predict`: Edge Case Validator.

        Tests the behavior of `predict` with edge cases.

        Raises:
            Exception: If at least one `Exception` raised is not of the expected
                kind.

        """
        X = random_matrix(self.data_shape)
        """np.matrix: Random-valued feature set."""

        params = compose(tuple, map)(random_matrix, self.shapes)
        """tuple of np.matrix: Random-valued parameters."""

        with self.assertRaises(_InvalidFeatureSetError):
            # Empty feature set.
            self.model.predict(np.matrix([[]]), params=params)

        with self.assertRaises(_InvalidModelParametersError):
            # Empty parameters.
            self.model.predict(X, params=(np.matrix([[]]), ))
    def test_random_model_numerical_gradient(self):
        """`Model.numerical_gradient`: Randomized Validator.

        Tests the behavior of `numerical_gradient` by feeding it randomly
        generated arguments.

        Raises:
            AssertionError: If `numerical_gradient` needs debugging.

        """
        for i in range(self.n_tests):
            X = random_matrix(self.data_shape)
            """np.matrix: Random-valued feature set."""

            Y = random_matrix((self.data_shape[0], 1))
            """np.matrix: Random-valued observation set."""

            random_params = compose(tuple, map)(random_matrix, self.shapes)
            """tuple of np.matrix: Random-valued parameters."""

            # First, test `params` as a method argument.
            result1 = self.model.numerical_gradient(X, Y, params=random_params)
            """float: Test input 1."""

            # Gradients should be a tuple.
            self.assertIsInstance(result1, tuple)

            # All params should have a gradient.
            self.assertEqual(len(result1), len(self.shapes))

            # All gradients should matrices.
            for g in result1:
                self.assertIsInstance(g, np.matrix)

            # Model parameters should not be set at this point.
            self.assertIsNone(self.model.params)

            # Finally, test `params` as attribute.
            self.model.params = random_params

            result2 = self.model.numerical_gradient(X, Y)
            """float: Test input 2."""

            # Gradients should be a tuple.
            self.assertIsInstance(result2, tuple)

            # All params should have a gradient.
            self.assertEqual(len(result2), len(self.shapes))

            # All gradients should matrices.
            for g in result2:
                self.assertIsInstance(g, np.matrix)

            # Model parameters should be set at this point.
            self.assertIsNotNone(self.model.params)

            norm1 = compose(sum, map)(np.linalg.norm, result1)
            """float: Sum of `result1`'s gradient norms."""
            norm2 = compose(sum, map)(np.linalg.norm, result2)
            """float: Sum of `result2`'s gradient norms."""

            # Norms of test inputs should match.
            self.assertEqual(norm1, norm2)

            del self.model.params
    def test_random_model_evaluate(self):
        """`Model.evaluate`: Randomized Validator.

        Tests the behavior of `evaluate` by feeding it randomly
        generated arguments.

        Raises:
            AssertionError: If `evaluate` needs debugging.

        """
        for i in range(self.n_tests):
            X = random_matrix(self.data_shape)
            """np.matrix: Random-valued feature set."""

            Y = random_matrix((self.data_shape[0], 1))
            """np.matrix: Random-valued observation set."""

            random_params = compose(tuple, map)(random_matrix, self.shapes)
            """tuple of np.matrix: Random-valued parameters."""

            # First, test `params` as a method argument.
            result1 = self.model.evaluate(X, Y, params=random_params)
            """float: Test input 1."""

            # Gradients should be a tuple.
            self.assertIsInstance(result1, tuple)

            # All params should have a gradient.
            self.assertEqual(len(result1), 2)

            err1, Y_hat1 = result1
            """(float, np.matrix): Evaluation error and predicted observations
            of test 1."""

            # Evaluation error should be a float.
            self.assertEqual(type(err1), np.float64)

            # Prediction set should be a matrix.
            self.assertIsInstance(Y_hat1, np.matrix)

            # Model parameters should not be set at this point.
            self.assertIsNone(self.model.params)

            # Finally, test `params` as attribute.
            self.model.params = random_params

            result2 = self.model.evaluate(X, Y)
            """float: Test input 2."""

            # Gradients should be a tuple.
            self.assertIsInstance(result2, tuple)

            # All params should have a gradient.
            self.assertEqual(len(result2), 2)

            err2, Y_hat2 = result2
            """(float, np.matrix): Evaluation error and predicted observations
            of test 2."""

            # Evaluation error should be a float.
            self.assertEqual(type(err2), np.float64)

            # Prediction set should be a matrix.
            self.assertIsInstance(Y_hat2, np.matrix)

            # Model parameters should be set at this point.
            self.assertIsNotNone(self.model.params)

            # Evaluation errors should match.
            self.assertEqual(err1, err2)

            # Norms of test inputs should match.
            self.assertEqual(np.linalg.norm(Y_hat1), np.linalg.norm(Y_hat2))

            r = self.model.regularization()
            """float: L2 parameter regularization."""

            (err_no_reg, Y_hat_no_reg) = self.model.evaluate(X,
                                                             Y,
                                                             regularize=False)
            """(float, np.matrix): Evaluation error and predicted observations
            of test 2."""

            # Evaluation with no regularization should comply with the following
            # equation.
            self.assertEqual(err1, err_no_reg + r)

            # Predicted observations should still be identical, though.
            self.assertEqual(np.linalg.norm(Y_hat1),
                             np.linalg.norm(Y_hat_no_reg))

            del self.model.params
    def test_invalid_args_model_predict(self):
        """`Model.predict`: Argument Validator.

        Tests the behavior of `predict` with invalid argument counts
        and values.

        Raises:
            Exception: If at least one `Exception` raised is not of the expected
                kind.

        """
        n, d = self.data_shape
        """(int, int): Number of data points and number of features."""

        X = random_matrix(self.data_shape)
        """np.matrix: Random-valued feature set."""

        params = compose(tuple, map)(random_matrix, self.shapes)
        """tuple of np.matrix: Random-valued parameters."""

        with self.assertRaises(TypeError):
            # No arguments.
            self.model.predict()

        with self.assertRaises(TypeError):
            # Too many arguments.
            self.model.predict(X, X, params=params)

        with self.assertRaises(_IncompleteModelError):
            # Params not set.
            self.model.predict(X)

        with self.assertRaises(TypeError):
            # Invalid kwarg.
            self.model.predict(X, params=params, key="value")

        with self.assertRaises(_InvalidFeatureSetError):
            # `None` instead of feature set `X`.
            self.model.predict(None, params=params)

        with self.assertRaises(_InvalidFeatureSetError):
            # ndarray instead of feature set `X`.
            self.model.predict(np.zeros((n, d)), params=params)

        with self.assertRaises(_InvalidModelParametersError):
            # Incompatible observation set.
            self.model.predict(X, params=(random_matrix((n + 1, 1)), ))

        with self.assertRaises(_IncompleteModelError):
            # None instead of model parameters `params`.
            self.model.predict(X, params=None)

        with self.assertRaises(_InvalidModelParametersError):
            # List instead of model parameters tuple `params`.
            self.model.predict(X, params=list(params))

        with self.assertRaises(_InvalidModelParametersError):
            # List of ndarray instead of np.matrix tuple `params`.
            self.model.predict(X,
                               params=compose(tuple, map)(np.zeros,
                                                          self.shapes))
    def test_invalid_args_model_evaluate(self):
        """`Model.evaluate`: Argument Validator.

        Tests the behavior of `evaluate` with invalid argument counts and
        values.

        Raises:
            Exception: If at least one `Exception` raised is not of the expected
                kind.

        """
        n, d = self.data_shape
        """(int, int): Number of data points and number of features."""

        X = random_matrix(self.data_shape)
        """np.matrix: Random-valued feature set."""
        Y = random_matrix((n, 1))
        """np.matrix: Random-valued observation set."""
        params = compose(tuple, map)(random_matrix, self.shapes)
        """tuple of np.matrix: Random-valued parameters."""

        with self.assertRaises(TypeError):
            # No arguments.
            self.model.evaluate()

        with self.assertRaises(TypeError):
            # Too many arguments.
            self.model.evaluate(X, Y, Y, params=params)

        with self.assertRaises(_IncompleteModelError):
            # Params not set.
            self.model.evaluate(X, Y)

        with self.assertRaises(TypeError):
            # Invalid kwarg.
            self.model.evaluate(X, Y, params=params, key="value")

        with self.assertRaises(_InvalidFeatureSetError):
            # `None` instead of feature set `X`.
            self.model.evaluate(None, Y, params=params)

        with self.assertRaises(_InvalidFeatureSetError):
            # ndarray instead of feature set `X`.
            self.model.evaluate(np.zeros((n, d)), Y, params=params)

        with self.assertRaises(_InvalidObservationSetError):
            # `None` instead of observation set `Y`.
            self.model.evaluate(X, None, params=params)

        with self.assertRaises(_InvalidObservationSetError):
            # ndarray instead of observation set `Y`.
            self.model.evaluate(X, np.zeros((n, 1)), params=params)

        with self.assertRaises(_IncompatibleDataSetsError):
            # Incompatible feature set.
            self.model.evaluate(random_matrix((d, n)), Y, params=params)

        with self.assertRaises(_IncompatibleDataSetsError):
            # Incompatible observation set.
            self.model.evaluate(X, random_matrix((n + 1, 1)), params=params)

        with self.assertRaises(_IncompleteModelError):
            # None instead of model parameters `params`.
            self.model.evaluate(X, Y, params=None)

        with self.assertRaises(_InvalidModelParametersError):
            # List instead of model parameters tuple `params`.
            self.model.evaluate(X, Y, params=list(params))

        with self.assertRaises(_InvalidModelParametersError):
            # List of ndarray instead of np.matrix tuple `params`.
            self.model.evaluate(X,
                                Y,
                                params=compose(tuple, map)(np.zeros,
                                                           self.shapes))

        with self.assertRaises(TypeError):
            # None instead of string `loss_fn`.
            self.model.evaluate(X, Y, params=params, loss_fn=None)

        with self.assertRaises(TypeError):
            # Integer instead of string `loss_fn`.
            self.model.evaluate(X, Y, params=params, loss_fn=123)

        with self.assertRaises(AttributeError):
            # Non-existent loss function.
            self.model.evaluate(X, Y, params=params, loss_fn="non-existent")