From b958e199de9c971fe3f7381d897d7de8e866aaa1 Mon Sep 17 00:00:00 2001 From: William de Vazelhes Date: Mon, 13 May 2019 16:05:38 +0200 Subject: [PATCH 1/3] FIX: fix lsml inversion --- metric_learn/lsml.py | 4 ++-- test/metric_learn_test.py | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/metric_learn/lsml.py b/metric_learn/lsml.py index 1d66cbc0..b1523fa1 100644 --- a/metric_learn/lsml.py +++ b/metric_learn/lsml.py @@ -70,10 +70,10 @@ def _fit(self, quadruplets, weights=None): X = np.vstack({tuple(row) for row in quadruplets.reshape(-1, quadruplets.shape[2])}) prior_inv = np.atleast_2d(np.cov(X, rowvar=False)) - M = np.linalg.inv(prior_inv) + M = scipy.linalg.pinvh(prior_inv) else: M = self.prior - prior_inv = np.linalg.inv(self.prior) + prior_inv = scipy.linalg.pinvh(self.prior) step_sizes = np.logspace(-10, 0, 10) # Keep track of the best step size and the loss at that step. diff --git a/test/metric_learn_test.py b/test/metric_learn_test.py index a785d60d..e1fa2d28 100644 --- a/test/metric_learn_test.py +++ b/test/metric_learn_test.py @@ -74,6 +74,14 @@ def test_deprecation_num_labeled(self): 'removed in 0.6.0') assert_warns_message(DeprecationWarning, msg, lsml_supervised.fit, X, y) + def test_singular_covariance_does_not_diverge(self): + # Test that LSML does not diverge when using the covariance prior and + # when this covariance has null eigenvalues (See + # https://github.com/metric-learn/metric-learn/issues/202) + X, Y = make_classification(n_redundant=2, random_state=42) + lsml = LSML_Supervised() + lsml.fit(X, Y) + class TestITML(MetricTestCase): def test_iris(self): From 89f22b2e8fa9ab7c03ee6bffb4ca610539b44512 Mon Sep 17 00:00:00 2001 From: William de Vazelhes Date: Mon, 13 May 2019 16:07:27 +0200 Subject: [PATCH 2/3] Add todo --- test/metric_learn_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/metric_learn_test.py b/test/metric_learn_test.py index e1fa2d28..ffcdb98e 100644 --- a/test/metric_learn_test.py +++ b/test/metric_learn_test.py @@ -78,6 +78,7 @@ def test_singular_covariance_does_not_diverge(self): # Test that LSML does not diverge when using the covariance prior and # when this covariance has null eigenvalues (See # https://github.com/metric-learn/metric-learn/issues/202) + # TODO: remove when # 195 is merged X, Y = make_classification(n_redundant=2, random_state=42) lsml = LSML_Supervised() lsml.fit(X, Y) From 90a4c04349e009824001d05c31699537a976d9f4 Mon Sep 17 00:00:00 2001 From: William de Vazelhes Date: Mon, 13 May 2019 16:51:12 +0200 Subject: [PATCH 3/3] Add better test --- metric_learn/lsml.py | 4 ++-- test/metric_learn_test.py | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/metric_learn/lsml.py b/metric_learn/lsml.py index b1523fa1..7d81fdf5 100644 --- a/metric_learn/lsml.py +++ b/metric_learn/lsml.py @@ -39,7 +39,7 @@ def __init__(self, tol=1e-3, max_iter=1000, prior=None, verbose=False, tol : float, optional max_iter : int, optional prior : (d x d) matrix, optional - guess at a metric [default: inv(covariance(X))] + guess at a metric [default: pinvh(covariance(X))] verbose : bool, optional if True, prints information while learning preprocessor : array-like, shape=(n_samples, n_features) or callable @@ -126,7 +126,7 @@ def _total_loss(self, metric, vab, vcd, prior_inv): return self._comparison_loss(metric, vab, vcd) + reg_loss def _gradient(self, metric, vab, vcd, prior_inv): - dMetric = prior_inv - np.linalg.inv(metric) + dMetric = prior_inv - scipy.linalg.pinvh(metric) dabs = np.sum(vab.dot(metric) * vab, axis=1) dcds = np.sum(vcd.dot(metric) * vcd, axis=1) violations = dabs > dcds diff --git a/test/metric_learn_test.py b/test/metric_learn_test.py index ffcdb98e..518d9c1e 100644 --- a/test/metric_learn_test.py +++ b/test/metric_learn_test.py @@ -79,9 +79,12 @@ def test_singular_covariance_does_not_diverge(self): # when this covariance has null eigenvalues (See # https://github.com/metric-learn/metric-learn/issues/202) # TODO: remove when # 195 is merged - X, Y = make_classification(n_redundant=2, random_state=42) + rng = np.random.RandomState(42) + X, y = load_iris(return_X_y=True) + # we add a feature that is a linear combination of the two first ones + X = np.concatenate([X, X[:, :2].dot(rng.randn(2, 1))], axis=-1) lsml = LSML_Supervised() - lsml.fit(X, Y) + lsml.fit(X, y, random_state=rng) class TestITML(MetricTestCase):