scm2cpp-lasso 0.13.2__tar.gz → 0.15.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/PKG-INFO +87 -4
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/README.md +86 -3
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/pyproject.toml +1 -1
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/__init__.py +679 -248
- scm2cpp_lasso-0.15.0/src/scm2cpp_lasso/_compat.py +284 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso.egg-info/PKG-INFO +87 -4
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso.egg-info/SOURCES.txt +1 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/setup.cfg +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/setup.py +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/__init__.py +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/_loader.py +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/_loader_accel_cublas.py +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/_loader_classifiers.py +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/_loader_derived_blas.py +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/_loader_derived_cublas.py +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/_loader_derived_loops.py +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/_loader_gpu_cuda.py +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/_loader_gramops_blas.py +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/_loader_gramops_loops.py +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/_loader_nmf_blas.py +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/_loader_nmf_cublas.py +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/_loader_nmf_loops.py +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/accel_cublas/accel.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/accel_cublas/accel.hpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/accel_cublas/accel_capi.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/classifiers.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/classifiers.hpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/classifiers_capi.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/derived_blas/lasso_design.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/derived_blas/lasso_design.hpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/derived_blas/lasso_design_capi.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/derived_cublas/lasso_design.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/derived_cublas/lasso_design.hpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/derived_cublas/lasso_design_capi.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/derived_loops/lasso_design.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/derived_loops/lasso_design.hpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/derived_loops/lasso_design_capi.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/gpu_cuda/gpu.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/gpu_cuda/gpu.hpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/gpu_cuda/gpu_capi.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/gramops_blas/gramops.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/gramops_blas/gramops.hpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/gramops_blas/gramops_capi.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/gramops_loops/gramops.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/gramops_loops/gramops.hpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/gramops_loops/gramops_capi.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/lasso_cov.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/lasso_cov.hpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/lasso_cov_capi.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/nmf_blas/nmf_cd.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/nmf_blas/nmf_cd.hpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/nmf_blas/nmf_cd_capi.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/nmf_cublas/nmf_cd.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/nmf_cublas/nmf_cd.hpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/nmf_cublas/nmf_cd_capi.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/nmf_loops/nmf_cd.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/nmf_loops/nmf_cd.hpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/nmf_loops/nmf_cd_capi.cpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/scm2cpp-blas.hpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/scm2cpp-cublas.hpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/scm2cpp-cuda.hpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_generated/scm2cpp.hpp +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/_libfind.py +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso/batch_capi.cu +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso.egg-info/dependency_links.txt +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso.egg-info/requires.txt +0 -0
- {scm2cpp_lasso-0.13.2 → scm2cpp_lasso-0.15.0}/src/scm2cpp_lasso.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scm2cpp-lasso
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.15.0
|
|
4
4
|
Summary: scikit-learn estimators (lasso, elastic net, ridge, ARD, SVM, SVR, logistic regression, GLMs, Huber, Theil-Sen, F-score selection, RFE, NMF, Gaussian mixture, FastICA, PCA, graphical lasso, RANSAC, SGD, OMP, LARS) translated from Scheme to C++ by scm2cpp; GPU batch solver and CUDA kernels included
|
|
5
5
|
Author: Hirotaka Niitsuma
|
|
6
6
|
License: MIT
|
|
@@ -102,6 +102,83 @@ at zero, and `lambda_grid()` walks down from it -- the construction
|
|
|
102
102
|
scikit-learn uses. If your columns differ wildly in scale, standardize
|
|
103
103
|
them first; this solver does not do it for you.
|
|
104
104
|
|
|
105
|
+
## scikit-learn compatibility
|
|
106
|
+
|
|
107
|
+
From 0.14.0 every estimator with a `fit(X, y)` (or `fit(X)`) derives
|
|
108
|
+
from scikit-learn's `BaseEstimator` and the matching mixin, so `clone`,
|
|
109
|
+
`get_params` / `set_params`, `GridSearchCV`, `Pipeline` and
|
|
110
|
+
`cross_val_score` take them, and all 44 pass
|
|
111
|
+
`sklearn.utils.estimator_checks.check_estimator` (`check-sklearn.py`
|
|
112
|
+
runs it; the expected failures are `DesignLasso`'s missing `n_iter_`,
|
|
113
|
+
since the derived kernel does not count its sweeps, and
|
|
114
|
+
`LBFGSLogisticRegressionCV`'s
|
|
115
|
+
`n_iter_` of 0 for a C whose warm start is already converged, which
|
|
116
|
+
scikit-learn's own `LogisticRegressionCV` reports on the same data).
|
|
117
|
+
scikit-learn stays optional: without it a numpy-only stand-in gives
|
|
118
|
+
`get_params`, `set_params`, `score` and the same input checks.
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from sklearn.model_selection import GridSearchCV
|
|
122
|
+
from scm2cpp_lasso import GramLasso
|
|
123
|
+
|
|
124
|
+
GridSearchCV(GramLasso(), {"alpha": [0.01, 0.1, 1.0]}, cv=5).fit(X, y)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`CovLasso`, `CovRidge`, `DerivedLasso`, `CovLogistic`,
|
|
128
|
+
`CovGroupLasso` and `CovMultiTaskLasso` keep their
|
|
129
|
+
data-in-the-constructor interface, which solves a whole path at once;
|
|
130
|
+
each has a `fit(X, y)` form with the intercept beside it (0.15.0):
|
|
131
|
+
|
|
132
|
+
| data in the constructor | `fit(X, y)` | scikit-learn |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| `CovLasso` | `GramLasso`, `GramElasticNet` | Lasso, ElasticNet |
|
|
135
|
+
| `CovRidge` | `GramRidge` | Ridge |
|
|
136
|
+
| `DerivedLasso` | `DesignLasso` | Lasso |
|
|
137
|
+
| `CovLogistic` | `GramLogisticRegression` | LogisticRegression(l1_ratio=1, solver="liblinear") |
|
|
138
|
+
| `CovGroupLasso` | `GramGroupLasso` (`groups=None`: the lasso) | (none) |
|
|
139
|
+
| `CovMultiTaskLasso` | `GramMultiTaskLasso`, `GramMultiTaskElasticNet` | MultiTaskLasso, MultiTaskElasticNet |
|
|
140
|
+
|
|
141
|
+
Against scikit-learn on 400 x 30 with an intercept: `DesignLasso` and
|
|
142
|
+
`GramGroupLasso(groups=None)` agree with `Lasso` to 1.5e-10, the
|
|
143
|
+
multi-task pair with `MultiTaskLasso` / `MultiTaskElasticNet` to
|
|
144
|
+
5e-14, and `GramLogisticRegression` reaches liblinear's objective to
|
|
145
|
+
eight digits with the same predictions.
|
|
146
|
+
|
|
147
|
+
Against the class each one wraps (one alpha, one core), which fits no
|
|
148
|
+
intercept and is unchanged: with `fit_intercept=False` the input checks
|
|
149
|
+
add 15 to 30 us a call (up to +50% on a 0.06 ms fit) and the finiteness
|
|
150
|
+
sum 8 to 14% on 20000 x 200 (none under `assume_finite`); with the
|
|
151
|
+
intercept, the column means add 20 to 30% on 20000 x 200 for the
|
|
152
|
+
Gram-matrix ones, which correct X'X
|
|
153
|
+
for the means rather than copy X to centre it, and about 55% for
|
|
154
|
+
`DesignLasso`, whose kernel needs the centred X itself.
|
|
155
|
+
`GramLogisticRegression` is slower than liblinear by its algorithm, 2.5x
|
|
156
|
+
at 20000 x 200 and more on small problems; `L1LogisticRegression` is
|
|
157
|
+
the fast one for that problem.
|
|
158
|
+
|
|
159
|
+
What it costs is the input checks: X must be a finite 2-D float64
|
|
160
|
+
array with the feature count of the fit. A plain C-ordered float64
|
|
161
|
+
ndarray is checked directly (one sum for finiteness); anything else (a
|
|
162
|
+
list, a DataFrame, another dtype) goes through scikit-learn's
|
|
163
|
+
`validate_data`, which raises the errors scikit-learn raises. The
|
|
164
|
+
checks alone, per call (`bench/sklearn-compat-overhead.py --checks`):
|
|
165
|
+
|
|
166
|
+
| X | fit | predict | predict, 50 rows | validate_data |
|
|
167
|
+
|---|---|---|---|---|
|
|
168
|
+
| 200 x 10 | 13 us | 7 us | 9 us | 173 us |
|
|
169
|
+
| 2000 x 50 | 47 us | 36 us | 9 us | 224 us |
|
|
170
|
+
| 20000 x 200 | 2.7 ms | 3.2 ms | 13 us | 3.2 ms |
|
|
171
|
+
|
|
172
|
+
The finiteness sum is the size-dependent part; under
|
|
173
|
+
`sklearn.set_config(assume_finite=True)` every row is about 3 us.
|
|
174
|
+
Whole calls, before and after (`bench/sklearn-compat-overhead.py`, 30
|
|
175
|
+
estimators, three shapes, the two packages alternating on one core of
|
|
176
|
+
a loaded machine): fits of a millisecond or more are unchanged within
|
|
177
|
+
the run-to-run spread (about 10%); fits of about 0.1 ms take 10 to
|
|
178
|
+
30% (10 to 40 us) longer; predict on 20000 x 200 takes about twice as
|
|
179
|
+
long (3 ms to 6 ms, the finiteness sum costing as much as the product
|
|
180
|
+
itself), and on small inputs 5 to 10 us longer.
|
|
181
|
+
|
|
105
182
|
## Estimators at a glance
|
|
106
183
|
|
|
107
184
|
Every estimator, the scikit-learn estimator it reproduces, the Scheme
|
|
@@ -110,12 +187,18 @@ it runs on, and whether Intel's sklearnex accelerates the scikit-learn
|
|
|
110
187
|
side. Builds: *loops* is the plain translation, *BLAS* / *cuBLAS* the
|
|
111
188
|
products handed to (cu)BLAS, *--cuda* the translator's GPU kernels
|
|
112
189
|
(`gpu_cuda/`), *GPU batch* the hand-written CUDA batch solver; the
|
|
113
|
-
package picks the fastest one present.
|
|
114
|
-
from scikit-learn's `BaseEstimator` yet, so `clone`, `GridSearchCV`
|
|
115
|
-
and `check_estimator` do not apply to them.
|
|
190
|
+
package picks the fastest one present.
|
|
116
191
|
|
|
117
192
|
| class | scikit-learn | Scheme kernel | builds | sklearnex accelerates |
|
|
118
193
|
|---|---|---|---|---|
|
|
194
|
+
| `GramLasso` | Lasso(precompute=True), fit(X, y) with the intercept | `lasso-cov.scm` | CPU | no |
|
|
195
|
+
| `GramElasticNet` | ElasticNet(precompute=True), fit(X, y) with the intercept | `lasso-cov.scm` | CPU | no |
|
|
196
|
+
| `GramRidge` | Ridge, fit(X, y) with the intercept, one or many targets | numpy (Cholesky) | CPU | Ridge |
|
|
197
|
+
| `GramMultiTaskLasso` | MultiTaskLasso, fit(X, Y) with the intercept | `lasso-cov.scm` | CPU | no |
|
|
198
|
+
| `GramMultiTaskElasticNet` | MultiTaskElasticNet, fit(X, Y) with the intercept | `lasso-cov.scm` | CPU | no |
|
|
199
|
+
| `GramGroupLasso` | (none; skglm GroupLasso), fit(X, y) with the intercept | `lasso-cov.scm` | CPU | - |
|
|
200
|
+
| `GramLogisticRegression` | LogisticRegression(l1_ratio=1, solver='liblinear'), by majorization | `lasso-cov.scm` | CPU | no |
|
|
201
|
+
| `DesignLasso` | Lasso, fit(X, y) with the intercept, from the design | `lasso-design.scm (--derive)` | loops / BLAS / cuBLAS | no |
|
|
119
202
|
| `CovLasso` | Lasso, ElasticNet, lasso_path(precompute=True) | `lasso-cov.scm` | CPU; GPU batch (fit_path_batch, bootstrap) | no |
|
|
120
203
|
| `CovLassoCV` | LassoCV, ElasticNetCV | `lasso-cov.scm` | CPU; GPU batch | no |
|
|
121
204
|
| `DerivedLasso` | lasso_path, from the design | `lasso-design.scm (--derive)` | loops / BLAS / cuBLAS | no |
|
|
@@ -84,6 +84,83 @@ at zero, and `lambda_grid()` walks down from it -- the construction
|
|
|
84
84
|
scikit-learn uses. If your columns differ wildly in scale, standardize
|
|
85
85
|
them first; this solver does not do it for you.
|
|
86
86
|
|
|
87
|
+
## scikit-learn compatibility
|
|
88
|
+
|
|
89
|
+
From 0.14.0 every estimator with a `fit(X, y)` (or `fit(X)`) derives
|
|
90
|
+
from scikit-learn's `BaseEstimator` and the matching mixin, so `clone`,
|
|
91
|
+
`get_params` / `set_params`, `GridSearchCV`, `Pipeline` and
|
|
92
|
+
`cross_val_score` take them, and all 44 pass
|
|
93
|
+
`sklearn.utils.estimator_checks.check_estimator` (`check-sklearn.py`
|
|
94
|
+
runs it; the expected failures are `DesignLasso`'s missing `n_iter_`,
|
|
95
|
+
since the derived kernel does not count its sweeps, and
|
|
96
|
+
`LBFGSLogisticRegressionCV`'s
|
|
97
|
+
`n_iter_` of 0 for a C whose warm start is already converged, which
|
|
98
|
+
scikit-learn's own `LogisticRegressionCV` reports on the same data).
|
|
99
|
+
scikit-learn stays optional: without it a numpy-only stand-in gives
|
|
100
|
+
`get_params`, `set_params`, `score` and the same input checks.
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from sklearn.model_selection import GridSearchCV
|
|
104
|
+
from scm2cpp_lasso import GramLasso
|
|
105
|
+
|
|
106
|
+
GridSearchCV(GramLasso(), {"alpha": [0.01, 0.1, 1.0]}, cv=5).fit(X, y)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`CovLasso`, `CovRidge`, `DerivedLasso`, `CovLogistic`,
|
|
110
|
+
`CovGroupLasso` and `CovMultiTaskLasso` keep their
|
|
111
|
+
data-in-the-constructor interface, which solves a whole path at once;
|
|
112
|
+
each has a `fit(X, y)` form with the intercept beside it (0.15.0):
|
|
113
|
+
|
|
114
|
+
| data in the constructor | `fit(X, y)` | scikit-learn |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| `CovLasso` | `GramLasso`, `GramElasticNet` | Lasso, ElasticNet |
|
|
117
|
+
| `CovRidge` | `GramRidge` | Ridge |
|
|
118
|
+
| `DerivedLasso` | `DesignLasso` | Lasso |
|
|
119
|
+
| `CovLogistic` | `GramLogisticRegression` | LogisticRegression(l1_ratio=1, solver="liblinear") |
|
|
120
|
+
| `CovGroupLasso` | `GramGroupLasso` (`groups=None`: the lasso) | (none) |
|
|
121
|
+
| `CovMultiTaskLasso` | `GramMultiTaskLasso`, `GramMultiTaskElasticNet` | MultiTaskLasso, MultiTaskElasticNet |
|
|
122
|
+
|
|
123
|
+
Against scikit-learn on 400 x 30 with an intercept: `DesignLasso` and
|
|
124
|
+
`GramGroupLasso(groups=None)` agree with `Lasso` to 1.5e-10, the
|
|
125
|
+
multi-task pair with `MultiTaskLasso` / `MultiTaskElasticNet` to
|
|
126
|
+
5e-14, and `GramLogisticRegression` reaches liblinear's objective to
|
|
127
|
+
eight digits with the same predictions.
|
|
128
|
+
|
|
129
|
+
Against the class each one wraps (one alpha, one core), which fits no
|
|
130
|
+
intercept and is unchanged: with `fit_intercept=False` the input checks
|
|
131
|
+
add 15 to 30 us a call (up to +50% on a 0.06 ms fit) and the finiteness
|
|
132
|
+
sum 8 to 14% on 20000 x 200 (none under `assume_finite`); with the
|
|
133
|
+
intercept, the column means add 20 to 30% on 20000 x 200 for the
|
|
134
|
+
Gram-matrix ones, which correct X'X
|
|
135
|
+
for the means rather than copy X to centre it, and about 55% for
|
|
136
|
+
`DesignLasso`, whose kernel needs the centred X itself.
|
|
137
|
+
`GramLogisticRegression` is slower than liblinear by its algorithm, 2.5x
|
|
138
|
+
at 20000 x 200 and more on small problems; `L1LogisticRegression` is
|
|
139
|
+
the fast one for that problem.
|
|
140
|
+
|
|
141
|
+
What it costs is the input checks: X must be a finite 2-D float64
|
|
142
|
+
array with the feature count of the fit. A plain C-ordered float64
|
|
143
|
+
ndarray is checked directly (one sum for finiteness); anything else (a
|
|
144
|
+
list, a DataFrame, another dtype) goes through scikit-learn's
|
|
145
|
+
`validate_data`, which raises the errors scikit-learn raises. The
|
|
146
|
+
checks alone, per call (`bench/sklearn-compat-overhead.py --checks`):
|
|
147
|
+
|
|
148
|
+
| X | fit | predict | predict, 50 rows | validate_data |
|
|
149
|
+
|---|---|---|---|---|
|
|
150
|
+
| 200 x 10 | 13 us | 7 us | 9 us | 173 us |
|
|
151
|
+
| 2000 x 50 | 47 us | 36 us | 9 us | 224 us |
|
|
152
|
+
| 20000 x 200 | 2.7 ms | 3.2 ms | 13 us | 3.2 ms |
|
|
153
|
+
|
|
154
|
+
The finiteness sum is the size-dependent part; under
|
|
155
|
+
`sklearn.set_config(assume_finite=True)` every row is about 3 us.
|
|
156
|
+
Whole calls, before and after (`bench/sklearn-compat-overhead.py`, 30
|
|
157
|
+
estimators, three shapes, the two packages alternating on one core of
|
|
158
|
+
a loaded machine): fits of a millisecond or more are unchanged within
|
|
159
|
+
the run-to-run spread (about 10%); fits of about 0.1 ms take 10 to
|
|
160
|
+
30% (10 to 40 us) longer; predict on 20000 x 200 takes about twice as
|
|
161
|
+
long (3 ms to 6 ms, the finiteness sum costing as much as the product
|
|
162
|
+
itself), and on small inputs 5 to 10 us longer.
|
|
163
|
+
|
|
87
164
|
## Estimators at a glance
|
|
88
165
|
|
|
89
166
|
Every estimator, the scikit-learn estimator it reproduces, the Scheme
|
|
@@ -92,12 +169,18 @@ it runs on, and whether Intel's sklearnex accelerates the scikit-learn
|
|
|
92
169
|
side. Builds: *loops* is the plain translation, *BLAS* / *cuBLAS* the
|
|
93
170
|
products handed to (cu)BLAS, *--cuda* the translator's GPU kernels
|
|
94
171
|
(`gpu_cuda/`), *GPU batch* the hand-written CUDA batch solver; the
|
|
95
|
-
package picks the fastest one present.
|
|
96
|
-
from scikit-learn's `BaseEstimator` yet, so `clone`, `GridSearchCV`
|
|
97
|
-
and `check_estimator` do not apply to them.
|
|
172
|
+
package picks the fastest one present.
|
|
98
173
|
|
|
99
174
|
| class | scikit-learn | Scheme kernel | builds | sklearnex accelerates |
|
|
100
175
|
|---|---|---|---|---|
|
|
176
|
+
| `GramLasso` | Lasso(precompute=True), fit(X, y) with the intercept | `lasso-cov.scm` | CPU | no |
|
|
177
|
+
| `GramElasticNet` | ElasticNet(precompute=True), fit(X, y) with the intercept | `lasso-cov.scm` | CPU | no |
|
|
178
|
+
| `GramRidge` | Ridge, fit(X, y) with the intercept, one or many targets | numpy (Cholesky) | CPU | Ridge |
|
|
179
|
+
| `GramMultiTaskLasso` | MultiTaskLasso, fit(X, Y) with the intercept | `lasso-cov.scm` | CPU | no |
|
|
180
|
+
| `GramMultiTaskElasticNet` | MultiTaskElasticNet, fit(X, Y) with the intercept | `lasso-cov.scm` | CPU | no |
|
|
181
|
+
| `GramGroupLasso` | (none; skglm GroupLasso), fit(X, y) with the intercept | `lasso-cov.scm` | CPU | - |
|
|
182
|
+
| `GramLogisticRegression` | LogisticRegression(l1_ratio=1, solver='liblinear'), by majorization | `lasso-cov.scm` | CPU | no |
|
|
183
|
+
| `DesignLasso` | Lasso, fit(X, y) with the intercept, from the design | `lasso-design.scm (--derive)` | loops / BLAS / cuBLAS | no |
|
|
101
184
|
| `CovLasso` | Lasso, ElasticNet, lasso_path(precompute=True) | `lasso-cov.scm` | CPU; GPU batch (fit_path_batch, bootstrap) | no |
|
|
102
185
|
| `CovLassoCV` | LassoCV, ElasticNetCV | `lasso-cov.scm` | CPU; GPU batch | no |
|
|
103
186
|
| `DerivedLasso` | lasso_path, from the design | `lasso-design.scm (--derive)` | loops / BLAS / cuBLAS | no |
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "scm2cpp-lasso"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.15.0"
|
|
8
8
|
description = "scikit-learn estimators (lasso, elastic net, ridge, ARD, SVM, SVR, logistic regression, GLMs, Huber, Theil-Sen, F-score selection, RFE, NMF, Gaussian mixture, FastICA, PCA, graphical lasso, RANSAC, SGD, OMP, LARS) translated from Scheme to C++ by scm2cpp; GPU batch solver and CUDA kernels included"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|