9
9
from sklearn .utils import check_array , check_consistent_length
10
10
from sklearn .linear_model import MultiTaskLasso as MultiTaskLasso_sklearn
11
11
from sklearn .linear_model ._base import (
12
- _preprocess_data , LinearModel , RegressorMixin ,
12
+ LinearModel , RegressorMixin ,
13
13
LinearClassifierMixin , SparseCoefMixin , BaseEstimator
14
14
)
15
15
from sklearn .utils .extmath import softmax
@@ -98,6 +98,8 @@ def _glm_fit(X, y, model, datafit, penalty):
98
98
else :
99
99
X_ = X
100
100
101
+ n_samples , n_features = X_ .shape
102
+
101
103
penalty_jit = compiled_clone (penalty )
102
104
datafit_jit = compiled_clone (datafit , to_float32 = X .dtype == np .float32 )
103
105
if issparse (X ):
@@ -112,22 +114,24 @@ def _glm_fit(X, y, model, datafit, penalty):
112
114
w = model .coef_ [0 , :].copy ()
113
115
else :
114
116
w = model .coef_ .copy ()
115
- Xw = X_ @ w
117
+ if model .fit_intercept :
118
+ w = np .hstack ([w , model .intercept_ ])
119
+ Xw = X_ @ w [:w .shape [0 ] - model .fit_intercept ] + model .fit_intercept * w [- 1 ]
116
120
else :
117
121
# TODO this should be solver.get_init() do delegate the work
118
122
if y .ndim == 1 :
119
- w = np .zeros (X_ . shape [ 1 ] , dtype = X_ .dtype )
120
- Xw = np .zeros (X_ . shape [ 0 ] , dtype = X_ .dtype )
123
+ w = np .zeros (n_features + model . fit_intercept , dtype = X_ .dtype )
124
+ Xw = np .zeros (n_samples , dtype = X_ .dtype )
121
125
else : # multitask
122
- w = np .zeros ((X_ . shape [ 1 ] , y .shape [1 ]), dtype = X_ .dtype )
126
+ w = np .zeros ((n_features + model . fit_intercept , y .shape [1 ]), dtype = X_ .dtype )
123
127
Xw = np .zeros (y .shape , dtype = X_ .dtype )
124
128
125
129
# check consistency of weights for WeightedL1
126
130
if isinstance (penalty , WeightedL1 ):
127
- if len (penalty .weights ) != X . shape [ 1 ] :
131
+ if len (penalty .weights ) != n_features :
128
132
raise ValueError (
129
- "The size of the WeightedL1 penalty weights should be n_features, \
130
- expected %i, got %i" % (X_ .shape [1 ], len (penalty .weights )))
133
+ "The size of the WeightedL1 penalty weights should be n_features, "
134
+ " expected %i, got %i. " % (X_ .shape [1 ], len (penalty .weights )))
131
135
132
136
if is_classif :
133
137
solver = cd_solver # TODO to be be replaced by an instance of BaseSolver
@@ -141,15 +145,19 @@ def _glm_fit(X, y, model, datafit, penalty):
141
145
coefs , p_obj , kkt = solver (
142
146
X_ , y , datafit_jit , penalty_jit , w , Xw , max_iter = model .max_iter ,
143
147
max_epochs = model .max_epochs , p0 = model .p0 ,
144
- tol = model .tol , # ws_strategy =model.ws_strategy ,
148
+ tol = model .tol , fit_intercept = model .fit_intercept ,
145
149
verbose = model .verbose )
150
+ model .coef_ , model .stop_crit_ = coefs [:n_features ], kkt
151
+ if y .ndim == 1 :
152
+ model .intercept_ = coefs [- 1 ] if model .fit_intercept else 0.
153
+ else :
154
+ model .intercept_ = coefs [- 1 , :] if model .fit_intercept else np .zeros (
155
+ y .shape [1 ])
146
156
147
- model .coef_ , model .stop_crit_ = coefs , kkt
148
157
model .n_iter_ = len (p_obj )
149
- model .intercept_ = 0.
150
158
151
159
if is_classif and n_classes_ <= 2 :
152
- model .coef_ = coefs [np .newaxis , :]
160
+ model .coef_ = coefs [np .newaxis , :n_features ]
153
161
if isinstance (datafit , QuadraticSVC ):
154
162
if is_sparse :
155
163
primal_coef = ((yXT ).multiply (model .coef_ [0 , :])).T
@@ -1212,6 +1220,7 @@ def fit(self, X, y):
1212
1220
# TODO add predict_proba for LinearSVC
1213
1221
1214
1222
1223
+ # TODO we should no longer inherit from sklearn
1215
1224
class MultiTaskLasso (MultiTaskLasso_sklearn ):
1216
1225
r"""MultiTaskLasso estimator.
1217
1226
@@ -1291,7 +1300,6 @@ def fit(self, X, Y):
1291
1300
self :
1292
1301
The fitted estimator.
1293
1302
"""
1294
- # TODO check if we could just patch `bcd_solver_path` as we do in Lasso case.
1295
1303
# Below is copied from sklearn, with path replaced by our path.
1296
1304
# Need to validate separately here.
1297
1305
# We can't pass multi_output=True because that would allow y to be csr.
@@ -1312,9 +1320,10 @@ def fit(self, X, Y):
1312
1320
raise ValueError ("X and Y have inconsistent dimensions (%d != %d)"
1313
1321
% (n_samples , Y .shape [0 ]))
1314
1322
1315
- X , Y , X_offset , Y_offset , X_scale = _preprocess_data (
1316
- X , Y , self .fit_intercept , copy = False )
1323
+ # X, Y, X_offset, Y_offset, X_scale = _preprocess_data(
1324
+ # X, Y, self.fit_intercept, copy=False)
1317
1325
1326
+ # TODO handle and test warm start for MTL
1318
1327
if not self .warm_start or not hasattr (self , "coef_" ):
1319
1328
self .coef_ = None
1320
1329
@@ -1324,9 +1333,10 @@ def fit(self, X, Y):
1324
1333
max_epochs = self .max_epochs , p0 = self .p0 , verbose = self .verbose ,
1325
1334
tol = self .tol )
1326
1335
1327
- self .coef_ , self .dual_gap_ = coefs [..., 0 ], kkt [- 1 ]
1336
+ self .coef_ = coefs [:, :X .shape [1 ], 0 ]
1337
+ self .intercept_ = self .fit_intercept * coefs [:, - 1 , 0 ]
1338
+ self .stopping_crit = kkt [- 1 ]
1328
1339
self .n_iter_ = len (kkt )
1329
- self ._set_intercept (X_offset , Y_offset , X_scale )
1330
1340
1331
1341
return self
1332
1342
@@ -1368,4 +1378,5 @@ def path(self, X, Y, alphas, coef_init=None, **params):
1368
1378
penalty = compiled_clone (self .penalty )
1369
1379
1370
1380
return multitask_bcd_solver_path (X , Y , datafit , penalty , alphas = alphas ,
1371
- coef_init = coef_init , ** params )
1381
+ coef_init = coef_init ,
1382
+ fit_intercept = self .fit_intercept , tol = self .tol )
0 commit comments