Skip to content

Commit cc32a80

Browse files
Add test ULSIF RULSIF
1 parent 81fe8e5 commit cc32a80

File tree

4 files changed

+102
-108
lines changed

4 files changed

+102
-108
lines changed

adapt/instance_based/_RULSIF.py

Lines changed: 40 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
class RULSIF(BaseAdaptEstimator):
1919
"""
20-
RULSIF: Relative least-squares importance fitting
20+
RULSIF: Relative Unconstrained Least-Squares Importance Fitting
2121
2222
RULSIF is an instance-based method for domain adaptation.
2323
@@ -35,12 +35,12 @@ class RULSIF(BaseAdaptEstimator):
3535
Where:
3636
3737
- :math:`x, x_i` are input instances.
38-
- :math:`X_T` is the target input data.
38+
- :math:`X_T` is the target input data of size :math:`n_T`.
3939
- :math:`\\theta_i` are the basis functions coefficients.
4040
- :math:`K(x, x_i) = \\text{exp}(-\\gamma ||x - x_i||^2)`
4141
for instance if ``kernel="rbf"``.
4242
43-
KLIEP algorithm consists in finding the optimal :math:`\\theta` according to
43+
RULSIF algorithm consists in finding the optimal :math:`\\theta` according to
4444
the quadratic problem
4545
4646
.. math::
@@ -52,22 +52,23 @@ class RULSIF(BaseAdaptEstimator):
5252
5353
.. math::
5454
55-
H_{ll'}= \\frac{\\alpha}{n_s} \sum_{x_i \\in X_S} K(x_i, x_l) K(x_i, x_l') + \\frac{1-\\alpha}{n_t} \\sum_{x_i \\in X_T} K(x_i, x_l) K(x_i, x_l')
55+
H_{ll'} = \\frac{\\alpha}{n_s} \sum_{x_i \\in X_S} K(x_i, x_l) K(x_i, x_l') + \\frac{1-\\alpha}{n_t} \\sum_{x_i \\in X_T} K(x_i, x_l) K(x_i, x_l') \\
5656
h_{l}= \\frac{1}{n_T} \sum_{x_i \\in X_T} K(x_i, x_l)
57-
58-
Where:
59-
60-
- :math:`X_T` is the source input data of size :math:`n_T`.
6157
6258
The above OP is solved by the closed form expression
6359
64-
- :math:\hat{\\theta}=(H+\\lambda I_{n_s})^{(-1)} h
60+
.. math::
61+
62+
\hat{\\theta}=(H+\\lambda I_{n_s})^{(-1)} h
6563
6664
Furthemore the method admits a leave one out cross validation score that has a clossed expression
67-
and can be used to select the appropriate parameters of the kernel function :math:`K` (typically, the paramter
65+
and can be used to select the appropriate parameters of the kernel function :math:`K` (typically, the parameter
6866
:math:`\\gamma` of the Gaussian kernel). The parameter is then selected using
6967
cross-validation on the :math:`J` score defined as follows:
70-
:math:`J = -\\frac{\\alpha}{2|X_S|} \\sum_{x \\in X_S} w(x)^2 - \frac{1-\\alpha}{2|X_T|} \\sum_{x \in X_T} w(x)^2 `
68+
69+
.. math::
70+
71+
J = -\\frac{\\alpha}{2|X_S|} \\sum_{x \\in X_S} w(x)^2 - \frac{1-\\alpha}{2|X_T|} \\sum_{x \in X_T} w(x)^2
7172
7273
Finally, an estimator is fitted using the reweighted labeled source instances.
7374
@@ -82,10 +83,14 @@ class RULSIF(BaseAdaptEstimator):
8283
Possible values: [‘additive_chi2’, ‘chi2’,
8384
‘linear’, ‘poly’, ‘polynomial’, ‘rbf’,
8485
‘laplacian’, ‘sigmoid’, ‘cosine’]
85-
86-
sigmas : float or list of float (default=None)
87-
Deprecated, please use the ``gamma`` parameter
88-
instead. (See below).
86+
87+
alpha : float (default=0.1)
88+
Trade-off parameter (between 0 and 1)
89+
90+
lambdas : float or list of float (default=1.)
91+
Optimization parameter. If a list is given,
92+
the best lambda will be selected on
93+
the unsupervised Leave-One-Out J-score.
8994
9095
max_centers : int (default=100)
9196
Maximal number of target instances use to
@@ -133,6 +138,7 @@ class RULSIF(BaseAdaptEstimator):
133138
parameter description).
134139
If a list is given, the LCV process is performed to
135140
select the best parameter ``degree``.
141+
136142
Attributes
137143
----------
138144
weights_ : numpy array
@@ -158,56 +164,40 @@ class RULSIF(BaseAdaptEstimator):
158164
159165
Examples
160166
--------
161-
>>> import numpy as np
162-
>>> from adapt.instance_based import KLIEP
163-
>>> np.random.seed(0)
164-
>>> Xs = np.random.randn(50) * 0.1
165-
>>> Xs = np.concatenate((Xs, Xs + 1.))
166-
>>> Xt = np.random.randn(100) * 0.1
167-
>>> ys = np.array([-0.2 * x if x<0.5 else 1. for x in Xs])
168-
>>> yt = -0.2 * Xt
169-
>>> kliep = KLIEP(sigmas=[0.1, 1, 10], random_state=0)
170-
>>> kliep.fit_estimator(Xs.reshape(-1,1), ys)
171-
>>> np.abs(kliep.predict(Xt.reshape(-1,1)).ravel() - yt).mean()
172-
0.09388...
173-
>>> kliep.fit(Xs.reshape(-1,1), ys, Xt.reshape(-1,1))
174-
Fitting weights...
175-
Cross Validation process...
176-
Parameter sigma = 0.1000 -- J-score = 0.059 (0.001)
177-
Parameter sigma = 1.0000 -- J-score = 0.427 (0.003)
178-
Parameter sigma = 10.0000 -- J-score = 0.704 (0.017)
179-
Fitting estimator...
180-
>>> np.abs(kliep.predict(Xt.reshape(-1,1)).ravel() - yt).mean()
181-
0.00302...
167+
>>> from sklearn.linear_model import RidgeClassifier
168+
>>> from adapt.utils import make_classification_da
169+
>>> from adapt.instance_based import RULSIF
170+
>>> Xs, ys, Xt, yt = make_classification_da()
171+
>>> model = RULSIF(RidgeClassifier(0.), Xt=Xt, kernel="rbf", alpha=0.1,
172+
... lambdas=[0.1, 1., 10.], gamma=[0.1, 1., 10.], random_state=0)
173+
>>> model.fit(Xs, ys);
174+
>>> model.score(Xt, yt)
175+
0.71
176+
182177
See also
183178
--------
184-
KMM
179+
ULSIF
180+
KLIEP
181+
185182
References
186183
----------
187-
.. [1] `[1] <https://proceedings.neurips.cc/paper/2011/file/
188-
d1f255a373a3cef72e03aa9d980c7eca-Paper.pdf>`_ \
184+
.. [1] `[1] <https://proceedings.neurips.cc/paper/2011/file/\
185+
d1f255a373a3cef72e03aa9d980c7eca-Paper.pdf>`_ \
189186
M. Yamada, T. Suzuki, T. Kanamori, H. Hachiya and M. Sugiyama. \
190-
"Relative Density-Ratio Estimation
191-
for Robust Distribution Comparison". In NIPS 2011
187+
"Relative Density-Ratio Estimation for Robust Distribution Comparison". In NIPS 2011
192188
"""
193189
def __init__(self,
194190
estimator=None,
195191
Xt=None,
196-
alpha=0.1,
197192
kernel="rbf",
198-
sigmas=None,
199-
lambdas=None,
193+
alpha=0.1,
194+
lambdas=1.,
200195
max_centers=100,
201196
copy=True,
202197
verbose=1,
203198
random_state=None,
204199
**params):
205200

206-
if sigmas is not None:
207-
warnings.warn("The `sigmas` argument is deprecated, "
208-
"please use the `gamma` argument instead.",
209-
DeprecationWarning)
210-
211201
names = self._get_param_names()
212202
kwargs = {k: v for k, v in locals().items() if k in names}
213203
kwargs.update(params)
@@ -242,9 +232,6 @@ def fit_weights(self, Xs, Xt, **kwargs):
242232
kernel_params = {k: v for k, v in self.__dict__.items()
243233
if k in KERNEL_PARAMS[self.kernel]}
244234

245-
# Handle deprecated sigmas (will be removed)
246-
if (self.sigmas is not None) and (not "gamma" in kernel_params):
247-
kernel_params["gamma"] = self.sigmas
248235

249236
kernel_params_dict = {k:(v if hasattr(v, "__iter__") else [v]) for k, v in kernel_params.items()}
250237
lambdas_params_dict={"lamb":(self.lambdas if hasattr(self.lambdas, "__iter__") else [self.lambdas])}
@@ -325,7 +312,7 @@ def fit_weights(self, Xs, Xt, **kwargs):
325312
print("Parameters %s -- J-score = %.3f"% (str(aux_params),score))
326313
if self.j_scores_[str(aux_params)] > max_:
327314
self.best_params_ = aux_params
328-
max_ = self.j_scores_[str(aux_params)]
315+
max_ = self.j_scores_[str(aux_params)]
329316
else:
330317
self.best_params_ = {"k":params_comb_kernel[0],"lamb": lambdas_params_dict["lamb"]}
331318

adapt/instance_based/_ULSIF.py

Lines changed: 38 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
EPS = np.finfo(float).eps
1414

1515

16+
@make_insert_doc()
1617
class ULSIF(BaseAdaptEstimator):
1718
"""
18-
RULSIF: Relative least-squares importance fitting
19+
ULSIF: Unconstrained Least-Squares Importance Fitting
1920
20-
RULSIF is an instance-based method for domain adaptation.
21+
ULSIF is an instance-based method for domain adaptation.
2122
2223
The purpose of the algorithm is to correct the difference between
2324
input distributions of source and target domains. This is done by
@@ -33,12 +34,12 @@ class ULSIF(BaseAdaptEstimator):
3334
Where:
3435
3536
- :math:`x, x_i` are input instances.
36-
- :math:`X_T` is the target input data.
37+
- :math:`X_T` is the target input data of size :math:`n_T`.
3738
- :math:`\\theta_i` are the basis functions coefficients.
3839
- :math:`K(x, x_i) = \\text{exp}(-\\gamma ||x - x_i||^2)`
3940
for instance if ``kernel="rbf"``.
4041
41-
KLIEP algorithm consists in finding the optimal :math:`\\theta` according to
42+
ULSIF algorithm consists in finding the optimal :math:`\\theta` according to
4243
the quadratic problem
4344
4445
.. math::
@@ -50,26 +51,28 @@ class ULSIF(BaseAdaptEstimator):
5051
5152
.. math::
5253
53-
H_{ll'}= \\frac{\\alpha}{n_s} \sum_{x_i \\in X_S} K(x_i, x_l) K(x_i, x_l') + \\frac{1-\\alpha}{n_t} \\sum_{x_i \\in X_T} K(x_i, x_l) K(x_i, x_l')
54+
H_{ll'} = \\frac{1}{n_s} \sum_{x_i \\in X_S} K(x_i, x_l) K(x_i, x_l') \\
5455
h_{l}= \\frac{1}{n_T} \sum_{x_i \\in X_T} K(x_i, x_l)
5556
56-
Where:
57-
58-
- :math:`X_T` is the source input data of size :math:`n_T`.
5957
6058
The above OP is solved by the closed form expression
6159
62-
- :math:\hat{\\theta}=(H+\\lambda I_{n_s})^{(-1)} h
60+
.. math::
61+
62+
\hat{\\theta} = (H+\\lambda I_{n_s})^{(-1)} h
6363
64-
Furthemore the method admits a leave one out cross validation score that has a clossed expression
64+
Furthemore the method admits a leave one out cross validation score that has a closed form expression
6565
and can be used to select the appropriate parameters of the kernel function :math:`K` (typically, the paramter
6666
:math:`\\gamma` of the Gaussian kernel). The parameter is then selected using
6767
cross-validation on the :math:`J` score defined as follows:
68-
:math:`J = -\\frac{\\alpha}{2|X_S|} \\sum_{x \\in X_S} w(x)^2 - \frac{1-\\alpha}{2|X_T|} \\sum_{x \in X_T} w(x)^2 `
68+
69+
.. math::
70+
71+
J = -\\frac{1}{2 n_s} \\sum_{x \\in X_S} w(x)^2
6972
7073
Finally, an estimator is fitted using the reweighted labeled source instances.
7174
72-
RULSIF method has been originally introduced for **unsupervised**
75+
ULSIF method has been originally introduced for **unsupervised**
7376
DA but it could be widen to **supervised** by simply adding labeled
7477
target data to the training set.
7578
@@ -81,9 +84,10 @@ class ULSIF(BaseAdaptEstimator):
8184
‘linear’, ‘poly’, ‘polynomial’, ‘rbf’,
8285
‘laplacian’, ‘sigmoid’, ‘cosine’]
8386
84-
sigmas : float or list of float (default=None)
85-
Deprecated, please use the ``gamma`` parameter
86-
instead. (See below).
87+
lambdas : float or list of float (default=1.)
88+
Optimization parameter. If a list is given,
89+
the best lambda will be selected based on
90+
the unsupervised Leave-One-Out J-score.
8791
8892
max_centers : int (default=100)
8993
Maximal number of target instances use to
@@ -131,6 +135,7 @@ class ULSIF(BaseAdaptEstimator):
131135
parameter description).
132136
If a list is given, the LCV process is performed to
133137
select the best parameter ``degree``.
138+
134139
Attributes
135140
----------
136141
weights_ : numpy array
@@ -156,55 +161,38 @@ class ULSIF(BaseAdaptEstimator):
156161
157162
Examples
158163
--------
159-
>>> import numpy as np
160-
>>> from adapt.instance_based import KLIEP
161-
>>> np.random.seed(0)
162-
>>> Xs = np.random.randn(50) * 0.1
163-
>>> Xs = np.concatenate((Xs, Xs + 1.))
164-
>>> Xt = np.random.randn(100) * 0.1
165-
>>> ys = np.array([-0.2 * x if x<0.5 else 1. for x in Xs])
166-
>>> yt = -0.2 * Xt
167-
>>> kliep = KLIEP(sigmas=[0.1, 1, 10], random_state=0)
168-
>>> kliep.fit_estimator(Xs.reshape(-1,1), ys)
169-
>>> np.abs(kliep.predict(Xt.reshape(-1,1)).ravel() - yt).mean()
170-
0.09388...
171-
>>> kliep.fit(Xs.reshape(-1,1), ys, Xt.reshape(-1,1))
172-
Fitting weights...
173-
Cross Validation process...
174-
Parameter sigma = 0.1000 -- J-score = 0.059 (0.001)
175-
Parameter sigma = 1.0000 -- J-score = 0.427 (0.003)
176-
Parameter sigma = 10.0000 -- J-score = 0.704 (0.017)
177-
Fitting estimator...
178-
>>> np.abs(kliep.predict(Xt.reshape(-1,1)).ravel() - yt).mean()
179-
0.00302...
164+
>>> from sklearn.linear_model import RidgeClassifier
165+
>>> from adapt.utils import make_classification_da
166+
>>> from adapt.instance_based import ULSIF
167+
>>> Xs, ys, Xt, yt = make_classification_da()
168+
>>> model = ULSIF(RidgeClassifier(0.), Xt=Xt, kernel="rbf",
169+
... lambdas=[0.1, 1., 10.], gamma=[0.1, 1., 10.], random_state=0)
170+
>>> model.fit(Xs, ys);
171+
>>> model.score(Xt, yt)
172+
0.71
173+
180174
See also
181175
--------
182-
KMM
176+
RULSIF
177+
KLIEP
178+
183179
References
184180
----------
185-
.. [1] `[1] <https://proceedings.neurips.cc/paper/2011/file/
186-
d1f255a373a3cef72e03aa9d980c7eca-Paper.pdf>`_ \
187-
M. Yamada, T. Suzuki, T. Kanamori, H. Hachiya and M. Sugiyama. \
188-
"Relative Density-Ratio Estimation
189-
for Robust Distribution Comparison". In NIPS 2011
181+
.. [1] `[1] <https://www.jmlr.org/papers/volume10/kanamori09a/kanamori09a.pdf>`_ \
182+
Takafumi Kanamori, Shohei Hido, Masashi Sugiyama \
183+
"A Least-squares Approach to Direct Importance Estimation". In JMLR 2009
190184
"""
191185
def __init__(self,
192186
estimator=None,
193187
Xt=None,
194188
kernel="rbf",
195-
sigmas=None,
196-
lambdas=None,
189+
lambdas=1.,
197190
max_centers=100,
198191
copy=True,
199192
verbose=1,
200193
random_state=None,
201194
**params):
202195

203-
if sigmas is not None:
204-
warnings.warn("The `sigmas` argument is deprecated, "
205-
"please use the `gamma` argument instead.",
206-
DeprecationWarning)
207-
208196
names = self._get_param_names()
209197
kwargs = {k: v for k, v in locals().items() if k in names}
210198
kwargs.update(params)
@@ -239,10 +227,6 @@ def fit_weights(self, Xs, Xt, **kwargs):
239227
kernel_params = {k: v for k, v in self.__dict__.items()
240228
if k in KERNEL_PARAMS[self.kernel]}
241229

242-
# Handle deprecated sigmas (will be removed)
243-
if (self.sigmas is not None) and (not "gamma" in kernel_params):
244-
kernel_params["gamma"] = self.sigmas
245-
246230
kernel_params_dict = {k:(v if hasattr(v, "__iter__") else [v]) for k, v in kernel_params.items()}
247231
lambdas_params_dict={"lamb":(self.lambdas if hasattr(self.lambdas, "__iter__") else [self.lambdas])}
248232
options = kernel_params_dict
@@ -343,7 +327,7 @@ def predict_weights(self, X=None):
343327
Return fitted source weights
344328
345329
If ``None``, the fitted source weights are returned.
346-
Else, sample weights are computing using the fitted
330+
Else, sample weights are computed using the fitted
347331
``thetas_`` and the chosen ``centers_``.
348332
349333
Parameters

adapt/instance_based/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
from ._nearestneighborsweighting import NearestNeighborsWeighting
1111
from ._balancedweighting import BalancedWeighting
1212
from ._iwn import IWN
13+
from ._ulsif import ULSIF
14+
from ._rulsif import RULSIF
1315

1416
__all__ = ["LDM", "KLIEP", "KMM", "TrAdaBoost", "TrAdaBoostR2",
1517
"TwoStageTrAdaBoostR2", "WANN", "NearestNeighborsWeighting",
16-
"BalancedWeighting", "IWN"]
18+
"BalancedWeighting", "IWN", "ULSIF", "RULSIF"]

tests/test_ulsif.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from sklearn.linear_model import RidgeClassifier
2+
from adapt.utils import make_classification_da
3+
from adapt.instance_based import ULSIF, RULSIF
4+
5+
Xs, ys, Xt, yt = make_classification_da()
6+
7+
8+
def test_ulsif():
9+
model = ULSIF(RidgeClassifier(0.), Xt=Xt[:73], kernel="rbf",
10+
lambdas=[0.1, 1., 10.], gamma=[0.1, 1., 10.], random_state=0)
11+
model.fit(Xs, ys);
12+
model.predict(Xs)
13+
model.score(Xt, yt)
14+
15+
16+
def test_rulsif():
17+
model = RULSIF(RidgeClassifier(0.), Xt=Xt, kernel="rbf", alpha=0.1,
18+
lambdas=[0.1, 1., 10.], gamma=[0.1, 1., 10.], random_state=0)
19+
model.fit(Xs[:73], ys[:73]);
20+
model.predict(Xs)
21+
model.score(Xt, yt)

0 commit comments

Comments
 (0)