|
6 | 6 | from six.moves import xrange
|
7 | 7 | from sklearn.metrics import pairwise_distances
|
8 | 8 | from sklearn.datasets import load_iris, make_classification, make_regression
|
9 |
| -from numpy.testing import assert_array_almost_equal |
| 9 | +from numpy.testing import assert_array_almost_equal, assert_array_equal |
10 | 10 | from sklearn.utils.testing import assert_warns_message
|
11 | 11 | from sklearn.exceptions import ConvergenceWarning
|
12 | 12 | from sklearn.utils.validation import check_X_y
|
@@ -138,6 +138,99 @@ def test_iris(self):
|
138 | 138 | csep = class_separation(nca.transform(self.iris_points), self.iris_labels)
|
139 | 139 | self.assertLess(csep, 0.20)
|
140 | 140 |
|
| 141 | + def test_finite_differences(self): |
| 142 | + """Test gradient of loss function |
| 143 | +
|
| 144 | + Assert that the gradient is almost equal to its finite differences |
| 145 | + approximation. |
| 146 | + """ |
| 147 | + # Initialize the transformation `M`, as well as `X` and `y` and `NCA` |
| 148 | + X, y = make_classification() |
| 149 | + M = np.random.randn(np.random.randint(1, X.shape[1] + 1), X.shape[1]) |
| 150 | + mask = y[:, np.newaxis] == y[np.newaxis, :] |
| 151 | + nca = NCA() |
| 152 | + nca.n_iter_ = 0 |
| 153 | + |
| 154 | + def fun(M): |
| 155 | + return nca._loss_grad_lbfgs(M, X, mask)[0] |
| 156 | + |
| 157 | + def grad(M): |
| 158 | + return nca._loss_grad_lbfgs(M, X, mask)[1].ravel() |
| 159 | + |
| 160 | + # compute relative error |
| 161 | + rel_diff = check_grad(fun, grad, M.ravel()) / np.linalg.norm(grad(M)) |
| 162 | + np.testing.assert_almost_equal(rel_diff, 0., decimal=6) |
| 163 | + |
| 164 | + def test_simple_example(self): |
| 165 | + """Test on a simple example. |
| 166 | +
|
| 167 | + Puts four points in the input space where the opposite labels points are |
| 168 | + next to each other. After transform the same labels points should be next |
| 169 | + to each other. |
| 170 | +
|
| 171 | + """ |
| 172 | + X = np.array([[0, 0], [0, 1], [2, 0], [2, 1]]) |
| 173 | + y = np.array([1, 0, 1, 0]) |
| 174 | + nca = NCA(num_dims=2,) |
| 175 | + nca.fit(X, y) |
| 176 | + Xansformed = nca.transform(X) |
| 177 | + np.testing.assert_equal(pairwise_distances(Xansformed).argsort()[:, 1], |
| 178 | + np.array([2, 3, 0, 1])) |
| 179 | + |
| 180 | + def test_singleton_class(self): |
| 181 | + X = self.iris_points |
| 182 | + y = self.iris_labels |
| 183 | + |
| 184 | + # one singleton class: test fitting works |
| 185 | + singleton_class = 1 |
| 186 | + ind_singleton, = np.where(y == singleton_class) |
| 187 | + y[ind_singleton] = 2 |
| 188 | + y[ind_singleton[0]] = singleton_class |
| 189 | + |
| 190 | + nca = NCA(max_iter=30) |
| 191 | + nca.fit(X, y) |
| 192 | + |
| 193 | + # One non-singleton class: test fitting works |
| 194 | + ind_1, = np.where(y == 1) |
| 195 | + ind_2, = np.where(y == 2) |
| 196 | + y[ind_1] = 0 |
| 197 | + y[ind_1[0]] = 1 |
| 198 | + y[ind_2] = 0 |
| 199 | + y[ind_2[0]] = 2 |
| 200 | + |
| 201 | + nca = NCA(max_iter=30) |
| 202 | + nca.fit(X, y) |
| 203 | + |
| 204 | + # Only singleton classes: test fitting does nothing (the gradient |
| 205 | + # must be null in this case, so the final matrix must stay like |
| 206 | + # the initialization) |
| 207 | + ind_0, = np.where(y == 0) |
| 208 | + ind_1, = np.where(y == 1) |
| 209 | + ind_2, = np.where(y == 2) |
| 210 | + X = X[[ind_0[0], ind_1[0], ind_2[0]]] |
| 211 | + y = y[[ind_0[0], ind_1[0], ind_2[0]]] |
| 212 | + |
| 213 | + EPS = np.finfo(float).eps |
| 214 | + A = np.zeros((X.shape[1], X.shape[1])) |
| 215 | + np.fill_diagonal(A, |
| 216 | + 1. / (np.maximum(X.max(axis=0) - X.min(axis=0), EPS))) |
| 217 | + nca = NCA(max_iter=30, num_dims=X.shape[1]) |
| 218 | + nca.fit(X, y) |
| 219 | + assert_array_equal(nca.transformer_, A) |
| 220 | + |
| 221 | + def test_one_class(self): |
| 222 | + # if there is only one class the gradient is null, so the final matrix |
| 223 | + # must stay like the initialization |
| 224 | + X = self.iris_points[self.iris_labels == 0] |
| 225 | + y = self.iris_labels[self.iris_labels == 0] |
| 226 | + EPS = np.finfo(float).eps |
| 227 | + A = np.zeros((X.shape[1], X.shape[1])) |
| 228 | + np.fill_diagonal(A, |
| 229 | + 1. / (np.maximum(X.max(axis=0) - X.min(axis=0), EPS))) |
| 230 | + nca = NCA(max_iter=30, num_dims=X.shape[1]) |
| 231 | + nca.fit(X, y) |
| 232 | + assert_array_equal(nca.transformer_, A) |
| 233 | + |
141 | 234 |
|
142 | 235 | class TestLFDA(MetricTestCase):
|
143 | 236 | def test_iris(self):
|
|
0 commit comments