99from sklearn .utils import check_array , check_consistent_length
1010from sklearn .linear_model import MultiTaskLasso as MultiTaskLasso_sklearn
1111from sklearn .linear_model ._base import (
12- _preprocess_data , LinearModel , RegressorMixin ,
12+ LinearModel , RegressorMixin ,
1313 LinearClassifierMixin , SparseCoefMixin , BaseEstimator
1414)
1515from sklearn .utils .extmath import softmax
@@ -98,6 +98,8 @@ def _glm_fit(X, y, model, datafit, penalty):
9898 else :
9999 X_ = X
100100
101+ n_samples , n_features = X_ .shape
102+
101103 penalty_jit = compiled_clone (penalty )
102104 datafit_jit = compiled_clone (datafit , to_float32 = X .dtype == np .float32 )
103105 if issparse (X ):
@@ -112,22 +114,24 @@ def _glm_fit(X, y, model, datafit, penalty):
112114 w = model .coef_ [0 , :].copy ()
113115 else :
114116 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 ]
116120 else :
117121 # TODO this should be solver.get_init() do delegate the work
118122 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 )
121125 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 )
123127 Xw = np .zeros (y .shape , dtype = X_ .dtype )
124128
125129 # check consistency of weights for WeightedL1
126130 if isinstance (penalty , WeightedL1 ):
127- if len (penalty .weights ) != X . shape [ 1 ] :
131+ if len (penalty .weights ) != n_features :
128132 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 )))
131135
132136 if is_classif :
133137 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):
141145 coefs , p_obj , kkt = solver (
142146 X_ , y , datafit_jit , penalty_jit , w , Xw , max_iter = model .max_iter ,
143147 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 ,
145149 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 ])
146156
147- model .coef_ , model .stop_crit_ = coefs , kkt
148157 model .n_iter_ = len (p_obj )
149- model .intercept_ = 0.
150158
151159 if is_classif and n_classes_ <= 2 :
152- model .coef_ = coefs [np .newaxis , :]
160+ model .coef_ = coefs [np .newaxis , :n_features ]
153161 if isinstance (datafit , QuadraticSVC ):
154162 if is_sparse :
155163 primal_coef = ((yXT ).multiply (model .coef_ [0 , :])).T
@@ -1212,6 +1220,7 @@ def fit(self, X, y):
12121220 # TODO add predict_proba for LinearSVC
12131221
12141222
1223+ # TODO we should no longer inherit from sklearn
12151224class MultiTaskLasso (MultiTaskLasso_sklearn ):
12161225 r"""MultiTaskLasso estimator.
12171226
@@ -1291,7 +1300,6 @@ def fit(self, X, Y):
12911300 self :
12921301 The fitted estimator.
12931302 """
1294- # TODO check if we could just patch `bcd_solver_path` as we do in Lasso case.
12951303 # Below is copied from sklearn, with path replaced by our path.
12961304 # Need to validate separately here.
12971305 # We can't pass multi_output=True because that would allow y to be csr.
@@ -1312,9 +1320,10 @@ def fit(self, X, Y):
13121320 raise ValueError ("X and Y have inconsistent dimensions (%d != %d)"
13131321 % (n_samples , Y .shape [0 ]))
13141322
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)
13171325
1326+ # TODO handle and test warm start for MTL
13181327 if not self .warm_start or not hasattr (self , "coef_" ):
13191328 self .coef_ = None
13201329
@@ -1324,9 +1333,10 @@ def fit(self, X, Y):
13241333 max_epochs = self .max_epochs , p0 = self .p0 , verbose = self .verbose ,
13251334 tol = self .tol )
13261335
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 ]
13281339 self .n_iter_ = len (kkt )
1329- self ._set_intercept (X_offset , Y_offset , X_scale )
13301340
13311341 return self
13321342
@@ -1368,4 +1378,5 @@ def path(self, X, Y, alphas, coef_init=None, **params):
13681378 penalty = compiled_clone (self .penalty )
13691379
13701380 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