11import numpy as np
2- from copy import deepcopy
32from textwrap import dedent
43
5- from ya_glm .info import is_multi_response
6- from ya_glm .opt .linear_regression import LinRegLoss , LinRegMultiRespLoss
7- from ya_glm .opt .huber_regression import HuberRegLoss , HuberRegMultiRespLoss
8- from ya_glm .opt .multinomial import MultinomialLoss
9- from ya_glm .opt .poisson_regression import PoissonRegLoss , \
10- PoissonRegMultiRespLoss
11- from ya_glm .opt .quantile_regression import QuantileRegLoss
12-
13- from ya_glm .opt .logistic_regression import LogRegLoss
14- from ya_glm .opt .penalty import LassoPenalty , RidgePenalty , \
4+ from ya_glm .opt .penalty .vec import LassoPenalty , RidgePenalty , \
155 WithIntercept , TikhonovPenalty
16- from ya_glm .opt .GroupLasso import GroupLasso
17- from ya_glm .opt .mat_penalty import MultiTaskLasso , NuclearNorm , \
6+ from ya_glm .opt .penalty . GroupLasso import GroupLasso
7+ from ya_glm .opt .penalty . mat_penalty import MultiTaskLasso , NuclearNorm , \
188 MatricizeEntrywisePen , \
199 MatWithIntercept
2010from ya_glm .opt .utils import decat_coef_inter_vec , decat_coef_inter_mat
2111from ya_glm .opt .fista import solve_fista
22- from ya_glm .opt .base import Func , Sum
12+ from ya_glm .opt .base import Sum
13+ from ya_glm .opt .glm_loss .get import get_glm_loss , safe_is_multi_response , \
14+ _LOSS_FUNC_CLS2STR
2315
2416
2517_solve_glm_params = dedent ("""
3830fit_intercept: bool
3931 Whether or not to fit an intercept.
4032
33+ sample_weight: None or array-like, shape (n_samples,)
34+ Individual weights for each sample.
35+
4136lasso_pen: None, float
4237 (Optional) The L1 penalty parameter value.
4338
7267intercept_init: None, float
7368 (Optional) Initialization for the intercept.
7469
75- precomp_lip: None, float
76- (Optional) Precomputed Lipschitz constant.
77-
7870xtol: float, None
7971 The change in X stopping criterion. If provided, we terminate the algorithm if |x_new - x_current|_max < x_tol
8072
@@ -109,6 +101,7 @@ def solve_glm(X, y,
109101 loss_func = 'lin_reg' ,
110102 loss_kws = {},
111103 fit_intercept = True ,
104+ sample_weight = None ,
112105
113106 lasso_pen = None ,
114107 lasso_weights = None ,
@@ -121,7 +114,6 @@ def solve_glm(X, y,
121114
122115 coef_init = None ,
123116 intercept_init = None ,
124- precomp_lip = None ,
125117 xtol = 1e-4 ,
126118 rtol = None ,
127119 atol = None ,
@@ -136,8 +128,9 @@ def solve_glm(X, y,
136128
137129 # get loss function object
138130 loss_func = get_glm_loss (loss_func = loss_func , loss_kws = loss_kws ,
139- X = X , y = y , fit_intercept = fit_intercept ,
140- precomp_lip = precomp_lip )
131+ X = X , y = y ,
132+ fit_intercept = fit_intercept ,
133+ sample_weight = sample_weight )
141134
142135 if _LOSS_FUNC_CLS2STR [type (loss_func )] == 'quantile' :
143136 raise NotImplementedError ("fista solver does not support quantile loss" )
@@ -149,9 +142,14 @@ def solve_glm(X, y,
149142 #####################
150143 # set initial value #
151144 #####################
152- init_val = process_init (X = X , y = y , loss_func = loss_func ,
153- fit_intercept = fit_intercept ,
154- coef_init = coef_init , intercept_init = intercept_init )
145+ if coef_init is None or intercept_init is None :
146+ init_val = loss_func .default_init ()
147+ else :
148+ init_val = loss_func .cat_intercept_coef (intercept_init , coef_init )
149+
150+ # init_val = process_init(X=X, y=y, loss_func=loss_func,
151+ # fit_intercept=fit_intercept,
152+ # coef_init=coef_init, intercept_init=intercept_init)
155153
156154 #############################
157155 # pre process penalty input #
@@ -294,7 +292,7 @@ def solve_glm_path(X, y,
294292 lasso_pen_seq = None , ridge_pen_seq = None ,
295293 loss_func = 'lin_reg' , loss_kws = {},
296294 fit_intercept = True ,
297- precomp_lip = None ,
295+ sample_weight = None ,
298296 # generator=True,
299297 check_decr = True ,
300298 ** kws ):
@@ -335,8 +333,9 @@ def solve_glm_path(X, y,
335333
336334 # this will precompute the lipschitz constant
337335 loss_func = get_glm_loss (loss_func = loss_func , loss_kws = loss_kws ,
338- X = X , y = y , fit_intercept = fit_intercept ,
339- precomp_lip = precomp_lip )
336+ X = X , y = y ,
337+ fit_intercept = fit_intercept ,
338+ sample_weight = sample_weight )
340339
341340 # possibly get initializers
342341 if 'coef_init' in kws :
@@ -371,125 +370,58 @@ def solve_glm_path(X, y,
371370 yield fit_out , params
372371
373372
374- _LOSS_FUNC_STR2CLS = {'lin_reg' : LinRegLoss ,
375- 'lin_reg_mr' : LinRegMultiRespLoss ,
376- 'huber_reg' : HuberRegLoss ,
377- 'huber_reg_mr' : HuberRegMultiRespLoss ,
378- 'log_reg' : LogRegLoss ,
379- 'multinomial' : MultinomialLoss ,
380- 'poisson' : PoissonRegLoss ,
381- 'poisson_mr' : PoissonRegMultiRespLoss ,
382- 'quantile' : QuantileRegLoss
383- }
384-
385- _LOSS_FUNC_CLS2STR = {v : k for (k , v ) in _LOSS_FUNC_STR2CLS .items ()}
386-
387-
388- def get_glm_loss (X , y ,
389- loss_func = 'lin_reg' , loss_kws = {},
390- fit_intercept = True , precomp_lip = None ):
391- """
392- Returns an GLM loss function object.
393-
394- Parameters
395- ----------
396- X: array-like, shape (n_samples, n_features)
397- The training covariate data.
398-
399- y: array-like, shape (n_samples, )
400- The training response data.
401-
402- fit_intercept: bool
403- Whether or not to fit an intercept.
404-
405- loss_func: str
406- Which GLM loss function to use.
407- Must be one of ['linear_regression', 'logistic_regression'].
408- This may also be an instance of ya_glm.opt.base.Func.
409-
410- precomp_lip: None, float
411- (Optional) Precomputed Lipchitz constant
412-
413- Output
414- ------
415- glm_loss: ya_glm.opt.Func
416- The GLM loss function object.
417- """
418-
419- if isinstance (loss_func , Func ):
420- return loss_func
421-
422- assert loss_func in _LOSS_FUNC_STR2CLS .keys ()
423- obj_class = _LOSS_FUNC_STR2CLS [loss_func ]
424-
425- kws = {'X' : X , 'y' : y , 'fit_intercept' : fit_intercept ,
426- ** loss_kws }
427-
428- if precomp_lip is not None :
429- kws ['lip' ] = precomp_lip
430-
431- return obj_class (** kws )
432-
433-
434- def safe_is_multi_response (loss_func ):
435- if isinstance (loss_func , Func ):
436- return is_multi_response (_LOSS_FUNC_CLS2STR [type (loss_func )])
437- else :
438- return is_multi_response (loss_func )
439-
440-
441- def process_init (X , y , loss_func , fit_intercept = True , coef_init = None ,
442- intercept_init = None ):
443- """
444- Processes the initializer.
445-
446- Parameters
447- ----------
448-
449- Outout
450- ------
451- init_val: array-like
452- The initial value. Shape is (n_features, ), (n_features + 1, )
453- (n_features, n_responses) or (n_features, n_responses + 1)
454- """
455-
456- if coef_init is None or (fit_intercept and intercept_init is None ):
457-
458- # determine the coefficient shape
459- if safe_is_multi_response (loss_func ):
460- coef_shape = (X .shape [1 ], y .shape [1 ])
461- else :
462- coef_shape = X .shape [1 ]
463-
464- # initialize coefficient
465- if coef_init is None :
466- coef_init = np .zeros (coef_shape )
467-
468- # initialize intercept
469- if intercept_init is None :
470- if isinstance (coef_shape , tuple ):
471- intercept_init = np .zeros (coef_shape [1 ])
472- else :
473- intercept_init = 0
474-
475- # format
476- coef_init = np .array (coef_init )
477- if fit_intercept :
478- if coef_init .ndim > 1 :
479- intercept_init = np .array (intercept_init )
480- else :
481- intercept_init = float (intercept_init )
482-
483- # maybe concatenate
484- if fit_intercept :
485- if coef_init .ndim == 2 :
486- init_val = np .vstack ([intercept_init , coef_init ])
487- else :
488- init_val = np .concatenate ([[intercept_init ], coef_init ])
489- else :
490- init_val = deepcopy (coef_init )
491-
492- return init_val
373+ # def process_init(X, y, loss_func, fit_intercept=True, coef_init=None,
374+ # intercept_init=None):
375+ # """
376+ # Processes the initializer.
377+
378+ # Parameters
379+ # ----------
380+
381+ # Outout
382+ # ------
383+ # init_val: array-like
384+ # The initial value. Shape is (n_features, ), (n_features + 1, )
385+ # (n_features, n_responses) or (n_features, n_responses + 1)
386+ # """
387+
388+ # if coef_init is None or (fit_intercept and intercept_init is None):
389+
390+ # # determine the coefficient shape
391+ # if safe_is_multi_response(loss_func):
392+ # coef_shape = (X.shape[1], y.shape[1])
393+ # else:
394+ # coef_shape = X.shape[1]
395+
396+ # # initialize coefficient
397+ # if coef_init is None:
398+ # coef_init = np.zeros(coef_shape)
399+
400+ # # initialize intercept
401+ # if intercept_init is None:
402+ # if isinstance(coef_shape, tuple):
403+ # intercept_init = np.zeros(coef_shape[1])
404+ # else:
405+ # intercept_init = 0
406+
407+ # # format
408+ # coef_init = np.array(coef_init)
409+ # if fit_intercept:
410+ # if coef_init.ndim > 1:
411+ # intercept_init = np.array(intercept_init)
412+ # else:
413+ # intercept_init = float(intercept_init)
414+
415+ # # maybe concatenate
416+ # if fit_intercept:
417+ # if coef_init.ndim == 2:
418+ # init_val = np.vstack([intercept_init, coef_init])
419+ # else:
420+ # init_val = np.concatenate([[intercept_init], coef_init])
421+ # else:
422+ # init_val = deepcopy(coef_init)
423+
424+ # return init_val
493425
494426
495427def process_param_path (lasso_pen_seq = None , ridge_pen_seq = None , check_decr = True ):
@@ -520,4 +452,3 @@ def process_param_path(lasso_pen_seq=None, ridge_pen_seq=None, check_decr=True):
520452 raise ValueError ("One of lasso_pen_seq, ridge_pen_seq should be provided " )
521453
522454 return param_path
523-
0 commit comments