Skip to content

Commit 8ab3762

Browse files
Update RegularTransfer linear
1 parent a5e97f5 commit 8ab3762

File tree

3 files changed

+98
-45
lines changed

3 files changed

+98
-45
lines changed

adapt/instance_based/_kliep.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ class KLIEP(BaseAdaptEstimator):
6363
The above OP is solved through gradient ascent algorithm.
6464
6565
Furthemore a LCV procedure can be added to select the appropriate
66-
parameters of the kernel function math::`K` (typically, the paramter
67-
math::`\\gamma` of the Gaussian kernel). The parameter is then selected using
66+
parameters of the kernel function :math:`K` (typically, the paramter
67+
:math:`\\gamma` of the Gaussian kernel). The parameter is then selected using
6868
cross-validation on the :math:`J` score defined as follows:
6969
:math:`J = \\frac{1}{|\\mathcal{X}|} \\sum_{x \\in \\mathcal{X}} \\text{log}(w(x))`
7070
@@ -84,7 +84,7 @@ class KLIEP(BaseAdaptEstimator):
8484
8585
sigmas : float or list of float (default=None)
8686
Deprecated, please use the ``gamma`` parameter
87-
instead.
87+
instead. (See below).
8888
8989
cv : int (default=5)
9090
Cross-validation split parameter.

adapt/parameter_based/_regular.py

Lines changed: 64 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
import numpy as np
66
from sklearn.exceptions import NotFittedError
7-
from scipy.optimize import minimize
7+
from sklearn.preprocessing import LabelBinarizer
8+
from scipy.sparse.linalg import lsqr
89
import tensorflow as tf
910
from tensorflow.keras import Sequential
1011
from tensorflow.keras.layers import Flatten, Dense
@@ -161,37 +162,63 @@ def fit(self, Xt=None, yt=None, **fit_params):
161162
self.estimator_ = check_estimator(self.estimator,
162163
copy=self.copy,
163164
force_copy=True)
164-
165+
165166
if self.estimator_.fit_intercept:
167+
intercept_ = np.reshape(
168+
self.estimator_.intercept_,
169+
np.ones(self.estimator_.coef_.shape).mean(-1, keepdims=True).shape)
166170
beta_src = np.concatenate((
167-
self.estimator_.intercept_ * np.ones(yt.shape).mean(0, keepdims=True),
168-
self.estimator_.coef_.transpose()
169-
))
171+
intercept_,
172+
self.estimator_.coef_
173+
), axis=-1)
170174
Xt = np.concatenate(
171175
(np.ones((len(Xt), 1)), Xt),
172176
axis=-1)
173177
else:
174-
beta_src = self.estimator_.coef_.transpose()
178+
beta_src = self.estimator_.coef_
175179

176-
func = self._get_func(Xt, yt, beta_src)
180+
yt_ndim_below_one_ = False
181+
if yt.ndim <= 1:
182+
yt = yt.reshape(-1, 1)
183+
yt_ndim_below_one_ = True
184+
185+
if beta_src.ndim <= 1:
186+
beta_src.reshape(1, -1)
187+
188+
if beta_src.shape[0] != yt.shape[1]:
189+
raise ValueError("The number of features of `yt`"
190+
" does not match the number of coefs in 'estimator', "
191+
"expected %i, got %i"(beta_src.shape[0], yt.shape[1]))
192+
193+
if beta_src.shape[1] != Xt.shape[1]:
194+
beta_shape = beta_src.shape[1]; Xt_shape = Xt.shape[1]
195+
if self.estimator_.fit_intercept:
196+
beta_shape -= 1; Xt_shape -= 1
197+
raise ValueError("The number of features of `Xt`"
198+
" does not match the number of coefs in 'estimator', "
199+
"expected %i, got %i"(beta_shape, Xt_shape))
200+
201+
beta_tgt = []
202+
for i in range(yt.shape[1]):
203+
sol = lsqr(A=Xt, b=yt[:, i], damp=self.lambda_, x0=beta_src[i, :])
204+
beta_tgt.append(sol[0])
205+
206+
beta_tgt = np.stack(beta_tgt, axis=0)
177207

178-
beta_tgt = minimize(func, beta_src)['x']
179-
beta_tgt = beta_tgt.reshape(beta_src.shape)
180-
181208
if self.estimator_.fit_intercept:
182-
self.estimator_.intercept_ = beta_tgt[0]
183-
self.estimator_.coef_ = beta_tgt[1:].transpose()
209+
self.coef_ = beta_tgt[:, 1:]
210+
self.intercept_ = beta_tgt[:, 0]
184211
else:
185-
self.estimator_.coef_ = beta_tgt.transpose()
212+
self.coef_ = beta_tgt
213+
214+
if yt_ndim_below_one_:
215+
self.coef_ = self.coef_.reshape(-1)
216+
self.intercept_ = self.intercept_[0]
217+
218+
self.estimator_.coef_ = self.coef_
219+
if self.estimator_.fit_intercept:
220+
self.estimator_.intercept_ = self.intercept_
186221
return self
187-
188-
189-
def _get_func(self, Xt, yt, beta_src):
190-
def func(beta):
191-
beta = beta.reshape(beta_src.shape)
192-
return (np.linalg.norm(Xt.dot(beta) - yt) ** 2 +
193-
self.lambda_ * np.linalg.norm(beta - beta_src) ** 2)
194-
return func
195222

196223

197224

@@ -201,26 +228,23 @@ class RegularTransferLC(RegularTransferLR):
201228
Regular Transfer for Linear Classification
202229
203230
RegularTransferLC is a parameter-based domain adaptation method.
204-
205-
The method is based on the assumption that a good target estimator
206-
can be obtained by adapting the parameters of a pre-trained source
207-
estimator using a few labeled target data.
208-
209-
The approach consist in fitting a linear estimator on target data
210-
according to an objective function regularized by the euclidean
211-
distance between source and target parameters:
231+
232+
This classifier first converts the target values into ``{-1, 1}``
233+
and then treats the problem as a regression task
234+
(multi-output regression in the multiclass case). It then fits
235+
the target data as a ``RegularTransferLR`` regressor, i.e it
236+
performs the following optimization:
212237
213238
.. math::
214239
215-
\\beta_T = \\underset{\\beta \\in \\mathbb{R}^p}{\\text{argmin}}
216-
\\, \\ell(\\beta, X_T, y_T) + \\lambda ||\\beta - \\beta_S||^2
240+
\\beta_T = \\underset{\\beta \in \\mathbb{R}^p}{\\text{argmin}}
241+
\\, ||X_T\\beta - y_T||^2 + \\lambda ||\\beta - \\beta_S||^2
217242
218243
Where:
219244
220-
- :math:`\\ell` is the log-likelihood function.
221245
- :math:`\\beta_T` are the target model parameters.
222246
- :math:`\\beta_S = \\underset{\\beta \\in \\mathbb{R}^p}{\\text{argmin}}
223-
\\, \\ell(\\beta, X_S, y_S)` are the source model parameters.
247+
\\, ||X_S\\beta - y_S||^2` are the source model parameters.
224248
- :math:`(X_S, y_S), (X_T, y_T)` are respectively the source and
225249
the target labeled data.
226250
- :math:`p` is the number of features in :math:`X_T`
@@ -270,13 +294,13 @@ class RegularTransferLC(RegularTransferLR):
270294
"""
271295
### TODO reshape yt for multiclass.
272296

273-
def _get_func(self, Xt, yt, beta_src):
274-
def func(beta):
275-
beta = beta.reshape(beta_src.shape)
276-
return (np.sum(np.log(1 + np.exp(
277-
-(2*yt-1) * Xt.dot(beta)))) +
278-
self.lambda_ * np.linalg.norm(beta - beta_src) ** 2)
279-
return func
297+
def fit(self, Xt=None, yt=None, **fit_params):
298+
Xt, yt = self._get_target_data(Xt, yt)
299+
Xt, yt = check_arrays(Xt, yt)
300+
301+
_label_binarizer = LabelBinarizer(pos_label=1, neg_label=-1)
302+
yt = _label_binarizer.fit_transform(yt)
303+
return super().fit(Xt, yt, **fit_params)
280304

281305

282306
@make_insert_doc(["task"], supervised=True)

tests/test_regular.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,20 @@ def test_regularlr_fit():
6767
model = RegularTransferLR(lr, lambda_=1.)
6868
model.fit(Xt, yt_reg)
6969
assert np.abs(model.estimator_.coef_[0] - 4) < 1
70+
71+
72+
def test_regularlr_multioutput():
73+
np.random.seed(0)
74+
X = np.random.randn(100, 5)+2.
75+
y = X[:, :2]
76+
lr = LinearRegression()
77+
lr.fit(X, y)
78+
model = RegularTransferLR(lr, lambda_=1.)
79+
model.fit(X, y)
80+
assert np.abs(model.predict(X) - y).sum() < 2
81+
assert np.all(model.coef_.shape == (2, 5))
82+
assert np.all(model.intercept_.shape == (2,))
83+
assert model.score(X, y) > 0.9
7084

7185

7286
def test_regularlc_fit():
@@ -85,8 +99,23 @@ def test_regularlc_fit():
8599

86100
model = RegularTransferLC(lr, lambda_=1.2)
87101
model.fit(Xt, yt_classif)
88-
assert np.abs(
89-
(model.predict(Xt) == yt_classif.ravel()).sum() - 55) < 2
102+
assert (model.predict(Xt) == yt_classif.ravel()).sum() > 95
103+
104+
105+
def test_regularlc_multiclass():
106+
np.random.seed(0)
107+
X = np.random.randn(100, 5)
108+
y = np.zeros(len(X))
109+
y[X[:, :2].sum(1)<0] = 1
110+
y[X[:, 3:].sum(1)>0] = 2
111+
lr = LogisticRegression(penalty='none', solver='lbfgs')
112+
lr.fit(X, y)
113+
model = RegularTransferLC(lr, lambda_=1.)
114+
model.fit(X, y)
115+
assert (model.predict(X) == y).sum() > 90
116+
assert np.all(model.coef_.shape == (3, 5))
117+
assert np.all(model.intercept_.shape == (3,))
118+
assert model.score(X, y) > 0.9
90119

91120

92121
def test_regularnn_fit():

0 commit comments

Comments
 (0)