Skip to content

Commit a3ad1bc

Browse files
committed
opt module reorg
1 parent cbcc7af commit a3ad1bc

44 files changed

Lines changed: 956 additions & 1212 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ya_glm/backends/andersoncd/glm_solver.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ def solve_glm(X, y,
1313
loss_func='lin_reg',
1414
loss_kws={},
1515
fit_intercept=True,
16+
sample_weight=None,
1617

1718
lasso_pen=None,
1819
lasso_weights=None,
1920

21+
2022
# groups=None,
2123
# L1to2=False,
2224
# nuc=False,
@@ -31,6 +33,9 @@ def solve_glm(X, y,
3133
p0=10, verbose=0, tol=1e-4, prune=0,
3234
return_n_iter=False):
3335

36+
if sample_weight is not None:
37+
raise NotImplementedError("need to add")
38+
3439
X = check_array(X, 'csc', dtype=[np.float64, np.float32],
3540
order='F', copy=False, accept_large_sparse=False)
3641
y = check_array(y, 'csc', dtype=X.dtype.type, order='F', copy=False,

ya_glm/backends/cvxpy/glm_solver.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ def solve_glm(X, y,
1414
loss_func='lin_reg',
1515
loss_kws={},
1616
fit_intercept=True,
17+
sample_weight=None,
1718
lasso_pen=None,
1819
lasso_weights=None,
1920
groups=None,
@@ -28,6 +29,9 @@ def solve_glm(X, y,
2829
solver=None,
2930
cp_kws={}):
3031

32+
if sample_weight is not None:
33+
raise NotImplementedError("need to add")
34+
3135
start_time = time()
3236
######################
3337
# objective function #

ya_glm/backends/fista/WL1SolverGlm.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def solve(self, L1_weights, opt_init=None, opt_init_upv=None):
3232
loss_func=self.glm_loss,
3333

3434
loss_kws=self.loss_kws,
35+
sample_weight=self.sample_weight,
3536
fit_intercept=self.fit_intercept,
3637
lasso_pen=1,
3738
lasso_weights=L1_weights,

ya_glm/backends/fista/glm_solver.py

Lines changed: 77 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,17 @@
11
import numpy as np
2-
from copy import deepcopy
32
from 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
2010
from ya_glm.opt.utils import decat_coef_inter_vec, decat_coef_inter_mat
2111
from 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("""
@@ -38,6 +30,9 @@
3830
fit_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+
4136
lasso_pen: None, float
4237
(Optional) The L1 penalty parameter value.
4338
@@ -72,9 +67,6 @@
7267
intercept_init: None, float
7368
(Optional) Initialization for the intercept.
7469
75-
precomp_lip: None, float
76-
(Optional) Precomputed Lipschitz constant.
77-
7870
xtol: 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

495427
def 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-

ya_glm/backends/quantile_lp/cvxpy_quad_prog.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
get_coef_inter, get_quad_mat
77

88

9-
def solve(X, y, fit_intercept=True, quantile=0.5, sample_weights=None,
9+
def solve(X, y, fit_intercept=True, quantile=0.5, sample_weight=None,
1010
lasso_pen=1, ridge_pen=None,
1111
lasso_weights=None, ridge_weights=None, tikhonov=None,
1212
coef_init=None, intercept_init=None,
@@ -29,7 +29,7 @@ def solve(X, y, fit_intercept=True, quantile=0.5, sample_weights=None,
2929
setup_problem(X=X, y=y,
3030
fit_intercept=fit_intercept,
3131
quantile=quantile,
32-
sample_weights=sample_weights,
32+
sample_weight=sample_weight,
3333
lasso_pen=lasso_pen,
3434
ridge_pen=ridge_pen,
3535
lasso_weights=lasso_weights,
@@ -120,7 +120,7 @@ def solve_path(fit_intercept=True, cp_kws={}, zero_tol=1e-8,
120120
yield fit_out, params
121121

122122

123-
def setup_problem(X, y, fit_intercept=True, quantile=0.5, sample_weights=None,
123+
def setup_problem(X, y, fit_intercept=True, quantile=0.5, sample_weight=None,
124124
lasso_pen=1, ridge_pen=None,
125125
lasso_weights=None, ridge_weights=None, tikhonov=None,
126126
coef_init=None, intercept_init=None):
@@ -142,7 +142,7 @@ def setup_problem(X, y, fit_intercept=True, quantile=0.5, sample_weights=None,
142142
fit_intercept=fit_intercept,
143143
quantile=quantile,
144144
lasso_pen=lasso_pen,
145-
sample_weights=sample_weights,
145+
sample_weight=sample_weight,
146146
lasso_weights=lasso_weights)
147147

148148
lin_coef = cp.hstack(lin_coef)

0 commit comments

Comments
 (0)