Frisch-Newton Interior Point Solver for Quantile Regression without Penalization - #32002
Frisch-Newton Interior Point Solver for Quantile Regression without Penalization#32002s3alfisc wants to merge 8 commits into
Conversation
❌ Linting issuesThis PR is introducing linting issues. Here's a summary of the issues. Note that you can avoid having linting issues by enabling You can see the details of the linting issues under the
|
ogrisel
left a comment
There was a problem hiding this comment.
Here is a shallow first pass of review. Please also update the existing tests to include the new solver (or extend them to deal with the fact that it only accept alpha=0).
Please also add a changelog entry as explained here:
https://github.com/scikit-learn/scikit-learn/blob/main/doc/whats_new/upcoming_changes/README.md
BTW, do you think this solver could be generalized to handle non-zero l2 or (l1 + l2) regularization?
| @@ -46,7 +46,11 @@ class QuantileRegressor(LinearModel, RegressorMixin, BaseEstimator): | |||
| solver : {'highs-ds', 'highs-ipm', 'highs', 'interior-point', \ | |||
| 'revised simplex'}, default='highs' | |||
There was a problem hiding this comment.
You need to also add the new method to the list of possible values above.
| ) | ||
|
|
||
| if self.solver == "frisch-newton" and alpha != 0: | ||
| raise ValueError("Frisch-Newton solver does not support alpha != 0.") |
There was a problem hiding this comment.
Please recall the actual value of alpha in the error message.
| if not success: | ||
| warnings.warn( | ||
| f"The 'frisch-newton' solver did not converge after {max_iter} iterations." | ||
| ) |
There was a problem hiding this comment.
This warning should be an instance of sklearn.exceptions.ConvergenceWarning.
| """ | ||
| if solver_options is None: | ||
| solver_options = {} | ||
| tol = solver_options.get("tol", 1e-7) |
There was a problem hiding this comment.
We might want to instead tol as an estimator parameter directly, as is customary for many scikit-learn estimators. We might also want to make this tolerance criterion independent of data scale if this is not already the case. Since the convergence is related to a duality gap, we could have a look at how it's done in ElasticNet and co.
Co-authored-by: Olivier Grisel <[email protected]>
|
Thanks for taking a first look at this @ogrisel, I'll work on this within the next days! |
|
|
||
| """ | ||
| Implements the Frisch-Newton Interior Point Solver for Quantile Regression | ||
| following Koenker and Ng (2005, https://link.springer.com/article/10.1007/s10255-005-0231-1). |
There was a problem hiding this comment.
As the Paper is behind a paywall, it would be nice to give a short derivation, like in
scikit-learn/sklearn/linear_model/_quantile.py
Lines 200 to 216 in 969df01
|
|
||
| params, _it = frisch_newton_solver( | ||
| A=X.T, | ||
| b=(1 - self.quantile) * X.T @ np.ones(n_indices), |
There was a problem hiding this comment.
| b=(1 - self.quantile) * X.T @ np.ones(n_indices), | |
| b=(1 - self.quantile) * X.sum(axis=0), |
| A_eq = sparse.hstack([X, -X, eye, -eye], format="csc") | ||
| else: | ||
| eye = np.eye(n_indices) | ||
| if self.solver == "frisch-newton": |
There was a problem hiding this comment.
| if self.solver == "frisch-newton": | |
| if self.solver == "frisch-newton": |
Could you add frisch-newton after the linprog? This way the comment around line 202/210 makes more sense and the FN-solver gets closer to its implementation.
|
|
||
| "Set starting values for the Frisch-Newton algorithm." |
There was a problem hiding this comment.
| "Set starting values for the Frisch-Newton algorithm." | |
| """Set starting values for the Frisch-Newton algorithm.""" |
| return min(_bound(x, dx, backoff), _bound(s, ds, backoff)) | ||
|
|
||
|
|
||
| def _cold_start(A: np.ndarray, c: np.ndarray, q: float): |
There was a problem hiding this comment.
| def _cold_start(A: np.ndarray, c: np.ndarray, q: float): | |
| def _cold_start(A, c, q): |
I guess it's fine without type annotations. A can be sparse, so ndarray is not the correct type.
|
|
||
|
|
||
| def frisch_newton_solver( | ||
| A: np.ndarray, |
There was a problem hiding this comment.
| A: np.ndarray, | |
| A: np.ndarray, |
Again annotations, at least A can be sparse.
|
|
||
| mu_curr = _duality_gap(x=x, z=z, s=s, w=w) | ||
|
|
||
| for _it in range(max_iter): |
There was a problem hiding this comment.
| for _it in range(max_iter): | |
| for it in range(max_iter): |
or just i.
|
⏰ This pull request might be automatically closed in two weeks from now. Thank you for your contribution to scikit-learn and for the effort you have put into this PR. This pull request does not yet meet the quality and clarity needed for an effective review. Project maintainers have limited time for code reviews, and our goal is to prioritize well-prepared contributions to keep scikit-learn maintainable. To increase the chance of a productive review, please refer to: How do I improve my issue or pull request? As the author, you are responsible for driving this PR, which entails doing necessary background research as well as presenting its context and your thought process. If you are a new contributor, or do not know how to fulfill these requirements, we recommend that you familiarise yourself with scikit-learn's development conventions via other contribution types (e.g., reviewing PRs) before submitting code. Scikit-learn maintainers cannot provide one-to-one guidance on this PR. However, if you ask focused, well-researched questions, a community member may be willing to help. 💬 If you substantially improve this PR within two weeks, a team member may remove the |
Reference Issues/PRs
See this issue. #31708
What does this implement/fix? Explain your changes.
Implements a fast Frisch-Newton Interior Point Solver for Vanilla Quantile Regression without penalization.