def test_kernel_types(self): """ This test checks that KernelPCovR can handle all kernels passable to sklearn kernel classes, including callable kernels """ def _linear_kernel(X, Y): return X @ Y.T kernel_params = { "poly": { "degree": 2 }, "rbf": { "gamma": 3.0 }, "sigmoid": { "gamma": 3.0, "coef0": 0.5 }, } for kernel in [ "linear", "poly", "rbf", "sigmoid", "cosine", _linear_kernel ]: with self.subTest(kernel=kernel): kpcovr = KernelPCovR(mixing=0.5, n_components=2, regressor=KernelRidge(kernel=kernel, **kernel_params.get( kernel, {})), kernel=kernel, **kernel_params.get(kernel, {})) kpcovr.fit(self.X, self.Y)
def test_nonfitted_failure(self): """ This test checks that KernelPCovR will raise a `NonFittedError` if `transform` is called before the model is fitted """ kpcovr = KernelPCovR(mixing=0.5, n_components=2, tol=1e-12) with self.assertRaises(exceptions.NotFittedError): _ = kpcovr.transform(self.X)
def test_no_arg_predict(self): """ This test checks that KernelPCovR will raise a `ValueError` if `predict` is called without arguments """ kpcovr = KernelPCovR(mixing=0.5, n_components=2, tol=1e-12) kpcovr.fit(self.X, self.Y) with self.assertRaises(ValueError): _ = kpcovr.predict()
def test_T_shape(self): """ This test checks that KernelPCovR returns a latent space projection consistent with the shape of the input matrix """ n_components = 5 kpcovr = KernelPCovR(mixing=0.5, n_components=n_components, tol=1e-12) kpcovr.fit(self.X, self.Y) T = kpcovr.transform(self.X) self.assertTrue(check_X_y(self.X, T, multi_output=True)) self.assertTrue(T.shape[-1] == n_components)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.random_state = np.random.RandomState(0) self.error_tol = 1e-6 self.X, self.Y = get_dataset(return_X_y=True) # for the sake of expedience, only use a subset of the dataset idx = self.random_state.choice(len(self.X), 1000) self.X = self.X[idx] self.Y = self.Y[idx] # artificial second property self.Y = np.array([ self.Y, self.X @ self.random_state.randint(-2, 2, (self.X.shape[-1], )) ]).T self.Y = self.Y.reshape(self.X.shape[0], -1) self.X = SFS().fit_transform(self.X) self.Y = SFS(column_wise=True).fit_transform(self.Y) self.model = lambda mixing=0.5, regressor=KernelRidge( alpha=1e-8), **kwargs: KernelPCovR(mixing, regressor=regressor, svd_solver=kwargs.pop( "svd_solver", "full"), **kwargs)
def test_lr_with_x_errors(self): """ This test checks that KernelPCovR returns a non-null property prediction and that the prediction error increases with `mixing` """ prev_error = -1.0 for i, mixing in enumerate(np.linspace(0, 1, 6)): kpcovr = KernelPCovR(mixing=mixing, n_components=2, tol=1e-12) kpcovr.fit(self.X, self.Y) error = (np.linalg.norm(self.Y - kpcovr.predict(self.X))**2.0 / np.linalg.norm(self.Y)**2.0) with self.subTest(error=error): self.assertFalse(np.isnan(error)) with self.subTest(error=error, alpha=round(mixing, 4)): self.assertGreaterEqual(error, prev_error - self.error_tol) prev_error = error
def test_linear_matches_pcovr(self): """ This test checks that KernelPCovR returns the same results as PCovR when using a linear kernel """ ridge = RidgeCV(fit_intercept=False, alphas=np.logspace(-8, 2)) ridge.fit(self.X, self.Y) # common instantiation parameters for the two models hypers = dict( mixing=0.5, n_components=1, ) # computing projection and predicton loss with linear KernelPCovR # and use the alpha from RidgeCV for level regression comparisons kpcovr = KernelPCovR(regressor=KernelRidge(alpha=ridge.alpha_, kernel="linear"), kernel="linear", fit_inverse_transform=True, **hypers) kpcovr.fit(self.X, self.Y) ly = (np.linalg.norm(self.Y - kpcovr.predict(self.X))**2.0 / np.linalg.norm(self.Y)**2.0) # computing projection and predicton loss with PCovR ref_pcovr = PCovR(**hypers, regressor=ridge, space="sample") ref_pcovr.fit(self.X, self.Y) ly_ref = (np.linalg.norm(self.Y - ref_pcovr.predict(self.X))**2.0 / np.linalg.norm(self.Y)**2.0) t_ref = ref_pcovr.transform(self.X) t = kpcovr.transform(self.X) K = kpcovr._get_kernel(self.X) k_ref = t_ref @ t_ref.T k = t @ t.T lk_ref = np.linalg.norm(K - k_ref)**2.0 / np.linalg.norm(K)**2.0 lk = np.linalg.norm(K - k)**2.0 / np.linalg.norm(K)**2.0 rounding = 3 self.assertEqual( round(ly, rounding), round(ly_ref, rounding), ) self.assertEqual( round(lk, rounding), round(lk_ref, rounding), )
def test_reconstruction_errors(self): """ This test checks that KernelPCovR returns a non-null reconstructed X and that the reconstruction error decreases with `mixing` """ prev_error = 10.0 prev_x_error = 10.0 for i, mixing in enumerate(np.linspace(0, 1, 6)): kpcovr = KernelPCovR(mixing=mixing, n_components=2, fit_inverse_transform=True, tol=1e-12) kpcovr.fit(self.X, self.Y) t = kpcovr.transform(self.X) K = kpcovr._get_kernel(self.X) x = kpcovr.inverse_transform(t) error = np.linalg.norm(K - t @ t.T)**2.0 / np.linalg.norm(K)**2.0 x_error = np.linalg.norm(self.X - x)**2.0 / np.linalg.norm( self.X)**2.0 with self.subTest(error=error): self.assertFalse(np.isnan(error)) with self.subTest(error=error, alpha=round(mixing, 4)): self.assertLessEqual(error, prev_error + self.error_tol) with self.subTest(error=x_error): self.assertFalse(np.isnan(x_error)) with self.subTest(error=x_error, alpha=round(mixing, 4)): self.assertLessEqual(x_error, prev_x_error + self.error_tol) prev_error = error prev_x_error = x_error
def test_none_regressor(self): kpcovr = KernelPCovR(mixing=0.5, regressor=None) kpcovr.fit(self.X, self.Y) self.assertTrue(kpcovr.regressor is None) self.assertTrue(kpcovr.regressor_ is not None)