Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2. [MRG] Bilinear similarity #329

Open
wants to merge 65 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 58 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
4b7cdec
Remove 3.9 from compatibility
mvargas33 Sep 15, 2021
0147c0c
FIrst draft of bilinear mixin
mvargas33 Sep 17, 2021
ec09f59
Fix score_pairs
mvargas33 Sep 17, 2021
ec49397
Two implementations for score_pairs
mvargas33 Sep 17, 2021
2f3c3e1
Generalized toy tests
mvargas33 Sep 17, 2021
c21d283
Handmade tests incorporated
mvargas33 Sep 17, 2021
dbe2a7a
Fix identation for bilinear
Sep 21, 2021
ee5c5ee
Add performance test to choose between two methods for bilinear calc
Sep 21, 2021
9a10e06
Found an efficient way to compute Bilinear Sim for n pairs
Sep 22, 2021
b1edc46
Update method's descriptions
Sep 22, 2021
ae562e6
Following the correct testing structure
Sep 22, 2021
7ebc026
Fix identation
Sep 22, 2021
1d752f7
Add more tests. Fix 4 to 2 identation
Sep 23, 2021
45c9b97
Minor flake8 fix
Sep 23, 2021
407f910
Commented each test
Sep 23, 2021
80c9085
All tests have been generalized
Sep 23, 2021
90ac550
Fix flake8 identation
Sep 23, 2021
68eeda9
Minor details
Sep 23, 2021
c47797c
Remove 3.9 from compatibility
mvargas33 Sep 15, 2021
e07b11a
First draft of refactoring BaseMetricLearner and Mahalanobis Learner
Oct 1, 2021
8210acd
Avoid warning related to score_pairs deprecation in tests of pair_cal…
Oct 6, 2021
11b5df6
Minor fix
Oct 6, 2021
06b7131
Replaced score_pairs with pair_distance in tests
Oct 6, 2021
d5cb8b4
Replace score_pairs with pair_distance inb docs.
Oct 6, 2021
2f61e7b
Fix weird commit
Oct 8, 2021
9dd38aa
Fix weird commit
Oct 8, 2021
5f68ed2
Update classifiers to use pair_similarity
Oct 8, 2021
3d6450b
Updated rst docs
Oct 8, 2021
7bce493
Fix identation
Oct 8, 2021
7e6584a
Update docs of score_pairs, get_metric
Oct 11, 2021
0b58f45
Add deprecation Test. Fix identation
Oct 11, 2021
d4d3a9c
Merge branch 'master' into score-deprecation
Oct 11, 2021
d27bdf5
Merge branch 'score-deprecation' into feat-bilinear
Oct 11, 2021
78a205c
Refactor to use pair_similarity instead of score_pairs
Oct 11, 2021
dde3576
Add more testing. Test refactor TBD
Oct 13, 2021
3020110
Tests are now parametrized
Oct 13, 2021
2746668
Add bilinear in introduction
Oct 15, 2021
920e504
Minor comment on use case
Oct 15, 2021
7a24319
More changes to sueprvised
Oct 15, 2021
2f8ee76
Changes in weakly Supervised
Oct 15, 2021
60c88a6
Merge remote-tracking branch 'upstream/master' into score-deprecation
Oct 19, 2021
8c55970
Fixed changes requested 1
Oct 19, 2021
787a8d1
Fixed changes requested 2
Oct 19, 2021
e14f956
Add equivalence test, p_dist == p_score
Oct 19, 2021
0941a32
Fix tests and identation.
Oct 19, 2021
b019d85
Fixed changes requested 3
Oct 20, 2021
74df897
Fix identation
Oct 20, 2021
c62a4e7
Last requested changes
Oct 21, 2021
526e4ba
Merge branch 'score-deprecation' into feat-bilinear
Oct 21, 2021
2199724
Replaced pair_similarity for paiir_score
Oct 21, 2021
249e0fe
Last small detail
Oct 21, 2021
80f31ba
Merge branch 'score-deprecation' into feat-bilinear
Oct 21, 2021
8df44a4
Merge remote-tracking branch 'upstream/master' into feat-bilinear
Oct 26, 2021
eef13bb
Classifiers only test classifiers methods now. + Standard doctrings now.
Oct 26, 2021
b952af0
Work in tests. More comments. Some refactors
Oct 26, 2021
7cc0d5e
Learner lists for M and B learners. Separated test by kind. Mock clas…
Oct 27, 2021
5f6bdc2
Moved mocks to test_utils.py, then refactor test_bilinear_mixin.py
Oct 27, 2021
100a05d
Merge branch 'master' into feat-bilinear
Nov 3, 2021
3bf5eae
Resolved observations in interoduction.rst
Nov 8, 2021
acfd54b
Resolved all observations for supervised.rst and weakly_s.rst
Nov 8, 2021
69bd9fe
Spellcheck
Nov 9, 2021
ade34cc
Moved common test to test_base_metric.py . Refactor preprocessor test…
Nov 9, 2021
7cfd432
Fix docs annotations
Nov 18, 2021
0ac9e7a
Second chunks of annotations.
Nov 18, 2021
98340b0
Merge branch 'master' into feat-bilinear
perimosocordiae Jun 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion doc/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ measuring the agreement with the training data.
Mahalanobis Distances
=====================

In the metric-learn package, all algorithms currently implemented learn
In the metric-learn package, most algorithms currently implemented learn
so-called Mahalanobis distances. Given a real-valued parameter matrix
:math:`L` of shape ``(num_dims, n_features)`` where ``n_features`` is the
number features describing the data, the Mahalanobis distance associated with
Expand Down Expand Up @@ -79,6 +79,34 @@ necessarily the identity of indiscernibles.
parameterizations are equivalent. In practice, an algorithm may thus solve
the metric learning problem with respect to either :math:`M` or :math:`L`.

.. _bilinear_similarity:

Bilinear Similarity
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved
===================

Some algorithms in the package don't learn a distance or pseudo-distance, but
a similarity. The idea is that two pairs are closer if their similarity value
is high, and viceversa. Given a real-valued parameter matrix :math:`W` of shape
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved
``(n_features, n_features)`` where ``n_features`` is the number features
describing the data, the Bilinear Similarity associated with :math:`W` is
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved
defined as follows:

.. math:: S_W(x, x') = x^T W x'
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved

The matrix :math:`W` is not required to be positive semi-definite (PSD), so
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved
none of the distance properties are satisfied: nonnegativity, identity of
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved
indiscernibles, symmetry and triangle inequality.

This allows some algorithms to optimize :math:`S_W` in an online manner using a
simple and efficient procedure, and thus can be applied to problems with
millions of training instances and achieves state-of-the-art performance
on an image search task using :math:`k`-NN.
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved

It also allows to be applied in contexts where the triangle inequality is
violated by visual judgements and the goal is to approximate perceptual
similarity. For intance, a man and a horse are both similar to a centaur,
but not to one another.
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved

.. _use_cases:

Use-cases
Expand All @@ -103,6 +131,10 @@ examples (for code illustrating some of these use-cases, see the
the data into a new embedding space before feeding it into another machine
learning algorithm.

The most common use-case of metric-learn would be to learn a Mahalanobis metric,
then transform the data to the learned space, and then resolve one of the task
above.
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved

The API of metric-learn is compatible with `scikit-learn
<https://scikit-learn.org/>`_, the leading library for machine
learning in Python. This allows to easily pipeline metric learners with other
Expand Down
65 changes: 54 additions & 11 deletions doc/supervised.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,32 +41,43 @@ two numbers.

Fit, transform, and so on
-------------------------
The goal of supervised metric-learning algorithms is to transform
Generally, the goal of supervised metric-learning algorithms is to transform
points in a new space, in which the distance between two points from the
same class will be small, and the distance between two points from different
classes will be large. To do so, we fit the metric learner (example:
`NCA`).
classes will be large.

But there are also some algorithms that learn a similarity, not a distance,
thus the points cannot be transformed into a new space. In this case, the
utility comes at using the similarity bewtween points directly.

Mahalanobis learners can transform points into a new space, and to do so,
we fit the metric learner (example:`NCA`).
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved

>>> from metric_learn import NCA
>>> nca = NCA(random_state=42)
>>> nca.fit(X, y)
NCA(init='auto', max_iter=100, n_components=None,
preprocessor=None, random_state=42, tol=None, verbose=False)


Now that the estimator is fitted, you can use it on new data for several
purposes.

First, you can transform the data in the learned space, using `transform`:
Here we transform two points in the new embedding space.
First, your mahalanobis learner can transform the data in
the learned space, using `transform`: Here we transform two points in
the new embedding space.

>>> X_new = np.array([[9.4, 4.1], [2.1, 4.4]])
>>> nca.transform(X_new)
array([[ 5.91884732, 10.25406973],
[ 3.1545886 , 6.80350083]])

Also, as explained before, our metric learners has learn a distance between
points. You can use this distance in two main ways:
.. warning::

If you try to use `transform` with a similarity learner, an error will
appear, as you cannot transform the data using them.

Also, as explained before, mahalanobis metric learners learn a distance
between points. You can use this distance in two main ways:
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved

- You can either return the distance between pairs of points using the
`pair_distance` function:
Expand All @@ -76,7 +87,8 @@ array([0.49627072, 3.65287282, 6.06079877])

- Or you can return a function that will return the distance (in the new
space) between two 1D arrays (the coordinates of the points in the original
space), similarly to distance functions in `scipy.spatial.distance`.
space), similarly to distance functions in `scipy.spatial.distance`. To
do that, use the `get_metric` method.

>>> metric_fun = nca.get_metric()
>>> metric_fun([3.5, 3.6], [5.6, 2.4])
Expand All @@ -94,17 +106,48 @@ This is useful because `pair_score` matches the **score** semantic of
scikit-learn's `Classification metrics
<https://scikit-learn.org/stable/modules/model_evaluation.html#classification-metrics>`_.

For similarity learners `pair_distance` is not available, as they don't learn
a distance. Intead you use `pair_score` that has the same behaviour but
for similarity.
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved

>>> algorithm.pair_score([[[3.5, 3.6], [5.6, 2.4]], [[1.2, 4.2], [2.1, 6.4]], [[3.3, 7.8], [10.9, 0.1]]])
array([-0.2312, 705.23, -72.8])

.. warning::

If you try to use `pair_distance` with a similarity learner, an error
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved
will appear, as they don't learn a distance nor a pseudo-distance.
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved

You can also call `get_metric` with similarity learners, and you will get
a function that will return the similarity bewtween 1D arrays.

>>> similarity_fun = algorithm.get_metric()
>>> similarity_fun([3.5, 3.6], [5.6, 2.4])
-0.04752

For similarity learners and mahalanobis learners, `pair_score` is
available. You can interpret that this function returns the **score**
between points: the more the **score**, the closer the pairs and vice-versa.
For mahalanobis learners, it is equal to the inverse of the distance.

.. note::

If the metric learner that you use learns a :ref:`Mahalanobis distance
<mahalanobis_distances>` (like it is the case for all algorithms
currently in metric-learn), you can get the plain learned Mahalanobis
<mahalanobis_distances>`, you can get the plain learned Mahalanobis
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved
matrix using `get_mahalanobis_matrix`.

>>> nca.get_mahalanobis_matrix()
array([[0.43680409, 0.89169412],
[0.89169412, 1.9542479 ]])

If the metric learner that you use learns a :ref:`Bilinear similarity
<bilinear_similarity>`, you can get the plain learned Bilinear
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved
matrix using `get_bilinear_matrix`.

>>> algorithm.get_bilinear_matrix()
array([[-0.72680409, -0.153213],
[1.45542269, 7.8135546 ]])


Scikit-learn compatibility
--------------------------
Expand Down
79 changes: 61 additions & 18 deletions doc/weakly_supervised.rst
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,16 @@ through the argument `preprocessor` (see below :ref:`fit_ws`)
Fit, transform, and so on
-------------------------

The goal of weakly-supervised metric-learning algorithms is to transform
points in a new space, in which the tuple-wise constraints between points
are respected.
Generally, the goal of weakly-supervised metric-learning algorithms is to
transform points in a new space, in which the tuple-wise constraints between
points are respected.

But there are also some algorithms that learn a similarity, not a distance,
thus the points cannot be transformed into a new space. But the goal is the
same: respect the maximum number of constraints while learning the similarity.

Weakly-supervised mahalanobis learners can transform points into a new space,
and to do so, we fit the metric learner (example:`MMC`).
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved

>>> from metric_learn import MMC
>>> mmc = MMC(random_state=42)
Expand All @@ -144,18 +151,23 @@ Or alternatively (using a preprocessor):
>>> mmc = MMC(preprocessor=X, random_state=42)
>>> mmc.fit(pairs_indice, y)


Now that the estimator is fitted, you can use it on new data for several
purposes.

First, you can transform the data in the learned space, using `transform`:
Here we transform two points in the new embedding space.
First, your mahalanobis learner can transform the data in
the learned space, using `transform`: Here we transform two points in
the new embedding space.

>>> X_new = np.array([[9.4, 4.1, 4.2], [2.1, 4.4, 2.3]])
>>> mmc.transform(X_new)
array([[-3.24667162e+01, 4.62622348e-07, 3.88325421e-08],
[-3.61531114e+01, 4.86778289e-07, 2.12654397e-08]])

.. warning::

If you try to use `transform` with a similarity learner, an error will
appear, as you cannot transform the data using them.

Also, as explained before, our metric learner has learned a distance between
mvargas33 marked this conversation as resolved.
Show resolved Hide resolved
points. You can use this distance in two main ways:

Expand All @@ -166,10 +178,10 @@ points. You can use this distance in two main ways:
... [[1.2, 4.2, 7.7], [2.1, 6.4, 0.9]]])
array([7.27607365, 0.88853014])

- Or you can return a function that will return the distance
(in the new space) between two 1D arrays (the coordinates of the points in
the original space), similarly to distance functions in
`scipy.spatial.distance`. To do that, use the `get_metric` method.
- Or you can return a function that will return the distance (in the new
space) between two 1D arrays (the coordinates of the points in the original
space), similarly to distance functions in `scipy.spatial.distance`. To
do that, use the `get_metric` method.

>>> metric_fun = mmc.get_metric()
>>> metric_fun([3.5, 3.6, 5.2], [5.6, 2.4, 6.7])
Expand All @@ -187,17 +199,48 @@ array([-0.49627072, -3.65287282, -6.06079877])
scikit-learn's `Classification metrics
<https://scikit-learn.org/stable/modules/model_evaluation.html#classification-metrics>`_.

For similarity learners `pair_distance` is not available, as they don't learn
a distance. Intead you use `pair_score` that has the same behaviour but
for similarity.

>>> algorithm.pair_score([[[3.5, 3.6], [5.6, 2.4]], [[1.2, 4.2], [2.1, 6.4]], [[3.3, 7.8], [10.9, 0.1]]])
array([-0.2312, 705.23, -72.8])

.. warning::

If you try to use `pair_distance` with a similarity learner, an error
will appear, as they don't learn a distance nor a pseudo-distance.

You can also call `get_metric` with similarity learners, and you will get
a function that will return the similarity bewtween 1D arrays.

>>> similarity_fun = algorithm.get_metric()
>>> similarity_fun([3.5, 3.6], [5.6, 2.4])
-0.04752

For similarity learners and mahalanobis learners, `pair_score` is
available. You can interpret that this function returns the **score**
between points: the more the **score**, the closer the pairs and vice-versa.
For mahalanobis learners, it is equal to the inverse of the distance.

.. note::

If the metric learner that you use learns a :ref:`Mahalanobis distance
<mahalanobis_distances>` (like it is the case for all algorithms
currently in metric-learn), you can get the plain Mahalanobis matrix using
`get_mahalanobis_matrix`.

>>> mmc.get_mahalanobis_matrix()
array([[ 0.58603894, -5.69883982, -1.66614919],
[-5.69883982, 55.41743549, 16.20219519],
[-1.66614919, 16.20219519, 4.73697721]])
<mahalanobis_distances>`, you can get the plain learned Mahalanobis
matrix using `get_mahalanobis_matrix`.

>>> mmc.get_mahalanobis_matrix()
array([[ 0.58603894, -5.69883982, -1.66614919],
[-5.69883982, 55.41743549, 16.20219519],
[-1.66614919, 16.20219519, 4.73697721]])

If the metric learner that you use learns a :ref:`Bilinear similarity
<bilinear_similarity>`, you can get the plain learned Bilinear
matrix using `get_bilinear_matrix`.

>>> algorithm.get_bilinear_matrix()
array([[-0.72680409, -0.153213],
[1.45542269, 7.8135546 ]])

.. _sklearn_compat_ws:

Expand Down
Loading