deskit 0.2.0__tar.gz → 0.4.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.
- {deskit-0.2.0/src/deskit.egg-info → deskit-0.4.0}/PKG-INFO +37 -29
- {deskit-0.2.0 → deskit-0.4.0}/README.md +36 -28
- {deskit-0.2.0 → deskit-0.4.0}/pyproject.toml +1 -1
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit/__init__.py +4 -4
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit/des/__init__.py +2 -2
- deskit-0.2.0/src/deskit/des/knndwsi.py → deskit-0.4.0/src/deskit/des/dewsi.py +4 -4
- deskit-0.4.0/src/deskit/des/dewst.py +200 -0
- deskit-0.2.0/src/deskit/des/knndws.py → deskit-0.4.0/src/deskit/des/dewsu.py +3 -3
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit/router.py +9 -9
- {deskit-0.2.0 → deskit-0.4.0/src/deskit.egg-info}/PKG-INFO +37 -29
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit.egg-info/SOURCES.txt +3 -2
- {deskit-0.2.0 → deskit-0.4.0}/LICENSE +0 -0
- {deskit-0.2.0 → deskit-0.4.0}/setup.cfg +0 -0
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit/_config.py +0 -0
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit/analysis.py +0 -0
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit/base/__init__.py +0 -0
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit/base/base.py +0 -0
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit/base/knnbase.py +0 -0
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit/des/knorae.py +0 -0
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit/des/knoraiu.py +0 -0
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit/des/knorau.py +0 -0
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit/des/ola.py +0 -0
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit/metrics.py +0 -0
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit/neighbors.py +0 -0
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit/utils.py +0 -0
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit.egg-info/dependency_links.txt +0 -0
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit.egg-info/requires.txt +0 -0
- {deskit-0.2.0 → deskit-0.4.0}/src/deskit.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deskit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A Python library for Dynamic Ensemble Selection
|
|
5
5
|
Author: Tikhon Vodyanov
|
|
6
6
|
License-Expression: MIT
|
|
@@ -31,7 +31,7 @@ Dynamic: license-file
|
|
|
31
31
|
|
|
32
32
|
# deskit
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
deskit is a flexible, lightweight, and easy-to-use ensembling library that implements
|
|
35
35
|
Dynamic Ensemble Selection (DES) algorithms for ensembling multiple ML models
|
|
36
36
|
on a given dataset.
|
|
37
37
|
|
|
@@ -43,6 +43,8 @@ requiring any wrappers, including custom models, popular ML libraries, and APIs.
|
|
|
43
43
|
deskit includes several DES algorithms, and it works with both classification
|
|
44
44
|
and regression.
|
|
45
45
|
|
|
46
|
+
See the full documentation [here](https://TikaaVo.github.io/deskit/).
|
|
47
|
+
|
|
46
48
|
# Dynamic Ensemble Selection
|
|
47
49
|
|
|
48
50
|
Ensemble learning in machine learning refers to when multiple models trained on a
|
|
@@ -148,14 +150,15 @@ weights = router.predict(X_test[i])
|
|
|
148
150
|
|
|
149
151
|
## Algorithms
|
|
150
152
|
|
|
151
|
-
| Method
|
|
152
|
-
|
|
153
|
-
| `
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
| `
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
153
|
+
| Method | Best for | Notes |
|
|
154
|
+
|------------|----------------|------------------------------------------------------------------------------------------------------|
|
|
155
|
+
| `DEWS-U` | Regression | Softmax over neighborhood-averaged scores. Temperature controls sharpness. |
|
|
156
|
+
| `DEWS-I` | Regression | Like DEWS-U but scores are inverse-distance weighted. |
|
|
157
|
+
| `DEWS-T` | Both | Like DEWS-U but fits a weighted trend line over neighbor scores and extrapolates to the test point. |
|
|
158
|
+
| `KNORA-U` | Classification | Vote-count weighting. Each model earns one vote per neighbor it correctly classifies. |
|
|
159
|
+
| `KNORA-E` | Classification | Intersection-based. Only models correct on all neighbors survive; falls back to smaller neighborhoods. |
|
|
160
|
+
| `KNORA-IU` | Classification | Like KNORA-U but votes are inverse-distance weighted. |
|
|
161
|
+
| `OLA` | Both | Hard selection: only the single best model in the neighborhood contributes. |
|
|
159
162
|
|
|
160
163
|
---
|
|
161
164
|
|
|
@@ -202,13 +205,18 @@ def pinball(y_true, y_pred, alpha=0.9):
|
|
|
202
205
|
e = y_true - y_pred
|
|
203
206
|
return alpha * e if e >= 0 else (alpha - 1) * e
|
|
204
207
|
|
|
205
|
-
router =
|
|
208
|
+
router = DEWSU(task="regression", metric=pinball, mode="min", k=20)
|
|
206
209
|
```
|
|
207
210
|
|
|
208
211
|
Built-in metric strings: `accuracy`, `mae`, `mse`, `rmse`, `log_loss`, `prob_correct`.
|
|
209
212
|
|
|
210
213
|
---
|
|
211
214
|
|
|
215
|
+
## Data types
|
|
216
|
+
|
|
217
|
+
deskit can be used with non-tabular data types like images, time series, and more. However, when used, the
|
|
218
|
+
passed features either need to be run through a feature extractor beforehand, such as a CNN backbone for images.
|
|
219
|
+
|
|
212
220
|
## Benchmark results
|
|
213
221
|
|
|
214
222
|
100-seed benchmark (seeds 0–99) on standard sklearn and OpenML datasets. "Best Single" is the best
|
|
@@ -224,39 +232,39 @@ Pool: KNN, Decision Tree, SVR, Ridge, Bayesian Ridge.
|
|
|
224
232
|
|
|
225
233
|
This pool was selected for having variability in architectures while avoiding a single dominant model.
|
|
226
234
|
|
|
227
|
-
deskit algorithms tested: OLA,
|
|
235
|
+
deskit algorithms tested: OLA, DEWS-U, DEWS-I, DEWS-T, KNORA-U, KNORA-E, KNORA-IU.
|
|
228
236
|
|
|
229
237
|
### Regression (MAE, lower is better)
|
|
230
238
|
|
|
231
|
-
% shown as delta vs Best Single.
|
|
239
|
+
% shown as delta vs Best Single. 20-seed mean.
|
|
232
240
|
|
|
233
|
-
| Dataset | Best Single | Simple Avg | deskit best
|
|
234
|
-
|
|
235
|
-
| California Housing (sklearn) | 0.
|
|
236
|
-
| Bike Sharing (OpenML) | 51.
|
|
237
|
-
| Abalone (OpenML) | **1.
|
|
238
|
-
| Diabetes (sklearn) | **44.
|
|
239
|
-
| Concrete Strength (OpenML) | 5.
|
|
241
|
+
| Dataset | Best Single | Simple Avg | deskit best |
|
|
242
|
+
|------------------------------|-------------|------------|---------------------------|
|
|
243
|
+
| California Housing (sklearn) | 0.3956 | +7.99% | **−2.54%** (DEWS-I) |
|
|
244
|
+
| Bike Sharing (OpenML) | 51.678 | +47.77% | **−6.86%** (DEWS-I) |
|
|
245
|
+
| Abalone (OpenML) | **1.4981** | +1.14% | +1.47% (KNORA-U/KNORA-IU) |
|
|
246
|
+
| Diabetes (sklearn) | **44.504** | +3.18% | +1.09% (DEWS-I/DEWS-T) |
|
|
247
|
+
| Concrete Strength (OpenML) | 5.2686 | +23.66% | **−1.20%** (DEWS-I) |
|
|
240
248
|
|
|
241
249
|
deskit beats best single and simple averaging on 3/5 regression datasets. This shows how DES can provide a
|
|
242
250
|
strong boost if used on the right dataset, but it might be counterproductive if used blindly.
|
|
243
251
|
|
|
244
252
|
KNORA variants are designed for classification, which explains the poor performance
|
|
245
253
|
on regression datasets; However, some exception can occur in certain datasets, either where
|
|
246
|
-
feature space
|
|
254
|
+
feature space has hard clusters (like in Concrete Strength) or when the target is discrete
|
|
247
255
|
and classification-like (like in Abalone).
|
|
248
256
|
|
|
249
257
|
### Classification (Accuracy, higher is better)
|
|
250
258
|
|
|
251
|
-
% shown as delta vs Best Single.
|
|
259
|
+
% shown as delta vs Best Single. 20-seed mean.
|
|
252
260
|
|
|
253
|
-
| Dataset | Best Single | Simple Avg | deskit best
|
|
254
|
-
|
|
255
|
-
| HAR (OpenML) | 98.24% | −0.
|
|
256
|
-
| Yeast (OpenML) |
|
|
257
|
-
| Image Segment (OpenML) | 93.
|
|
258
|
-
| Waveform (OpenML) | **
|
|
259
|
-
| Vowel (OpenML) |
|
|
261
|
+
| Dataset | Best Single | Simple Avg | deskit best |
|
|
262
|
+
|------------------------|-------------|------------|--------------------------|
|
|
263
|
+
| HAR (OpenML) | 98.24% | −0.33% | **+0.16%** (DEWS-T) |
|
|
264
|
+
| Yeast (OpenML) | 58.87% | +0.77% | **+1.66%** (KNORA-IU) |
|
|
265
|
+
| Image Segment (OpenML) | 93.70% | +1.40% | **+2.25%** (DEWS-T) |
|
|
266
|
+
| Waveform (OpenML) | **85.91%** | −0.98% | −0.39% (DEWS-T) |
|
|
267
|
+
| Vowel (OpenML) | 89.95% | −2.05% | **+0.93%** (KNORA-IU) |
|
|
260
268
|
|
|
261
269
|
deskit beats or matches best single and simple averaging on 4/5 classification datasets. As seen on regression, DES
|
|
262
270
|
can improve or hurt performance, so it must be used wisely, but if used correctly it can show promising results.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# deskit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
deskit is a flexible, lightweight, and easy-to-use ensembling library that implements
|
|
4
4
|
Dynamic Ensemble Selection (DES) algorithms for ensembling multiple ML models
|
|
5
5
|
on a given dataset.
|
|
6
6
|
|
|
@@ -12,6 +12,8 @@ requiring any wrappers, including custom models, popular ML libraries, and APIs.
|
|
|
12
12
|
deskit includes several DES algorithms, and it works with both classification
|
|
13
13
|
and regression.
|
|
14
14
|
|
|
15
|
+
See the full documentation [here](https://TikaaVo.github.io/deskit/).
|
|
16
|
+
|
|
15
17
|
# Dynamic Ensemble Selection
|
|
16
18
|
|
|
17
19
|
Ensemble learning in machine learning refers to when multiple models trained on a
|
|
@@ -117,14 +119,15 @@ weights = router.predict(X_test[i])
|
|
|
117
119
|
|
|
118
120
|
## Algorithms
|
|
119
121
|
|
|
120
|
-
| Method
|
|
121
|
-
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
122
|
+
| Method | Best for | Notes |
|
|
123
|
+
|------------|----------------|------------------------------------------------------------------------------------------------------|
|
|
124
|
+
| `DEWS-U` | Regression | Softmax over neighborhood-averaged scores. Temperature controls sharpness. |
|
|
125
|
+
| `DEWS-I` | Regression | Like DEWS-U but scores are inverse-distance weighted. |
|
|
126
|
+
| `DEWS-T` | Both | Like DEWS-U but fits a weighted trend line over neighbor scores and extrapolates to the test point. |
|
|
127
|
+
| `KNORA-U` | Classification | Vote-count weighting. Each model earns one vote per neighbor it correctly classifies. |
|
|
128
|
+
| `KNORA-E` | Classification | Intersection-based. Only models correct on all neighbors survive; falls back to smaller neighborhoods. |
|
|
129
|
+
| `KNORA-IU` | Classification | Like KNORA-U but votes are inverse-distance weighted. |
|
|
130
|
+
| `OLA` | Both | Hard selection: only the single best model in the neighborhood contributes. |
|
|
128
131
|
|
|
129
132
|
---
|
|
130
133
|
|
|
@@ -171,13 +174,18 @@ def pinball(y_true, y_pred, alpha=0.9):
|
|
|
171
174
|
e = y_true - y_pred
|
|
172
175
|
return alpha * e if e >= 0 else (alpha - 1) * e
|
|
173
176
|
|
|
174
|
-
router =
|
|
177
|
+
router = DEWSU(task="regression", metric=pinball, mode="min", k=20)
|
|
175
178
|
```
|
|
176
179
|
|
|
177
180
|
Built-in metric strings: `accuracy`, `mae`, `mse`, `rmse`, `log_loss`, `prob_correct`.
|
|
178
181
|
|
|
179
182
|
---
|
|
180
183
|
|
|
184
|
+
## Data types
|
|
185
|
+
|
|
186
|
+
deskit can be used with non-tabular data types like images, time series, and more. However, when used, the
|
|
187
|
+
passed features either need to be run through a feature extractor beforehand, such as a CNN backbone for images.
|
|
188
|
+
|
|
181
189
|
## Benchmark results
|
|
182
190
|
|
|
183
191
|
100-seed benchmark (seeds 0–99) on standard sklearn and OpenML datasets. "Best Single" is the best
|
|
@@ -193,39 +201,39 @@ Pool: KNN, Decision Tree, SVR, Ridge, Bayesian Ridge.
|
|
|
193
201
|
|
|
194
202
|
This pool was selected for having variability in architectures while avoiding a single dominant model.
|
|
195
203
|
|
|
196
|
-
deskit algorithms tested: OLA,
|
|
204
|
+
deskit algorithms tested: OLA, DEWS-U, DEWS-I, DEWS-T, KNORA-U, KNORA-E, KNORA-IU.
|
|
197
205
|
|
|
198
206
|
### Regression (MAE, lower is better)
|
|
199
207
|
|
|
200
|
-
% shown as delta vs Best Single.
|
|
208
|
+
% shown as delta vs Best Single. 20-seed mean.
|
|
201
209
|
|
|
202
|
-
| Dataset | Best Single | Simple Avg | deskit best
|
|
203
|
-
|
|
204
|
-
| California Housing (sklearn) | 0.
|
|
205
|
-
| Bike Sharing (OpenML) | 51.
|
|
206
|
-
| Abalone (OpenML) | **1.
|
|
207
|
-
| Diabetes (sklearn) | **44.
|
|
208
|
-
| Concrete Strength (OpenML) | 5.
|
|
210
|
+
| Dataset | Best Single | Simple Avg | deskit best |
|
|
211
|
+
|------------------------------|-------------|------------|---------------------------|
|
|
212
|
+
| California Housing (sklearn) | 0.3956 | +7.99% | **−2.54%** (DEWS-I) |
|
|
213
|
+
| Bike Sharing (OpenML) | 51.678 | +47.77% | **−6.86%** (DEWS-I) |
|
|
214
|
+
| Abalone (OpenML) | **1.4981** | +1.14% | +1.47% (KNORA-U/KNORA-IU) |
|
|
215
|
+
| Diabetes (sklearn) | **44.504** | +3.18% | +1.09% (DEWS-I/DEWS-T) |
|
|
216
|
+
| Concrete Strength (OpenML) | 5.2686 | +23.66% | **−1.20%** (DEWS-I) |
|
|
209
217
|
|
|
210
218
|
deskit beats best single and simple averaging on 3/5 regression datasets. This shows how DES can provide a
|
|
211
219
|
strong boost if used on the right dataset, but it might be counterproductive if used blindly.
|
|
212
220
|
|
|
213
221
|
KNORA variants are designed for classification, which explains the poor performance
|
|
214
222
|
on regression datasets; However, some exception can occur in certain datasets, either where
|
|
215
|
-
feature space
|
|
223
|
+
feature space has hard clusters (like in Concrete Strength) or when the target is discrete
|
|
216
224
|
and classification-like (like in Abalone).
|
|
217
225
|
|
|
218
226
|
### Classification (Accuracy, higher is better)
|
|
219
227
|
|
|
220
|
-
% shown as delta vs Best Single.
|
|
228
|
+
% shown as delta vs Best Single. 20-seed mean.
|
|
221
229
|
|
|
222
|
-
| Dataset | Best Single | Simple Avg | deskit best
|
|
223
|
-
|
|
224
|
-
| HAR (OpenML) | 98.24% | −0.
|
|
225
|
-
| Yeast (OpenML) |
|
|
226
|
-
| Image Segment (OpenML) | 93.
|
|
227
|
-
| Waveform (OpenML) | **
|
|
228
|
-
| Vowel (OpenML) |
|
|
230
|
+
| Dataset | Best Single | Simple Avg | deskit best |
|
|
231
|
+
|------------------------|-------------|------------|--------------------------|
|
|
232
|
+
| HAR (OpenML) | 98.24% | −0.33% | **+0.16%** (DEWS-T) |
|
|
233
|
+
| Yeast (OpenML) | 58.87% | +0.77% | **+1.66%** (KNORA-IU) |
|
|
234
|
+
| Image Segment (OpenML) | 93.70% | +1.40% | **+2.25%** (DEWS-T) |
|
|
235
|
+
| Waveform (OpenML) | **85.91%** | −0.98% | −0.39% (DEWS-T) |
|
|
236
|
+
| Vowel (OpenML) | 89.95% | −2.05% | **+0.93%** (KNORA-IU) |
|
|
229
237
|
|
|
230
238
|
deskit beats or matches best single and simple averaging on 4/5 classification datasets. As seen on regression, DES
|
|
231
239
|
can improve or hurt performance, so it must be used wisely, but if used correctly it can show promising results.
|
|
@@ -5,13 +5,13 @@ Metrics
|
|
|
5
5
|
-------
|
|
6
6
|
Pass a metric name string:
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
DEWSU(task='classification', metric='log_loss', mode='min')
|
|
9
9
|
|
|
10
10
|
Or import a metric function directly:
|
|
11
11
|
|
|
12
12
|
from deskit.metrics import log_loss, mae
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
DEWSU(task='classification', metric=log_loss, mode='min')
|
|
15
15
|
|
|
16
16
|
Available built-in metrics:
|
|
17
17
|
Scalar predictions (pass predict() output):
|
|
@@ -21,7 +21,7 @@ Available built-in metrics:
|
|
|
21
21
|
'log_loss', 'prob_correct'
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
from deskit.des.
|
|
24
|
+
from deskit.des.dewsu import DEWSU
|
|
25
25
|
from deskit.des.ola import OLA
|
|
26
26
|
from deskit.des.knorau import KNORAU
|
|
27
27
|
from deskit.des.knorae import KNORAE
|
|
@@ -31,7 +31,7 @@ from deskit._config import SPEED_PRESETS, list_presets
|
|
|
31
31
|
from deskit.analysis import analyze
|
|
32
32
|
|
|
33
33
|
__all__ = [
|
|
34
|
-
'
|
|
34
|
+
'DEWSU',
|
|
35
35
|
'OLA',
|
|
36
36
|
'KNORAU',
|
|
37
37
|
'KNORAE',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from deskit.des.
|
|
1
|
+
from deskit.des.dewsu import DEWSU
|
|
2
2
|
from deskit.des.ola import OLA
|
|
3
3
|
from deskit.des.knorau import KNORAU
|
|
4
4
|
from deskit.des.knorae import KNORAE
|
|
5
5
|
from deskit.des.knoraiu import KNORAIU
|
|
6
6
|
|
|
7
|
-
__all__ = ['
|
|
7
|
+
__all__ = ['DEWSU', 'OLA', 'KNORAU', 'KNORAE', 'KNORAIU']
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
DEWS-IU: K-Nearest Neighbors with Distance-Weighted Softmax — Inverse-weighted Union.
|
|
3
3
|
"""
|
|
4
4
|
from deskit.base.knnbase import KNNBase
|
|
5
5
|
from deskit._config import make_finder, resolve_metric, prep_fit_inputs
|
|
@@ -7,11 +7,11 @@ from deskit.utils import to_numpy
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
class
|
|
10
|
+
class DEWSI(KNNBase):
|
|
11
11
|
"""
|
|
12
|
-
|
|
12
|
+
DEWS-IU: K-Nearest Neighbors with Distance-Weighted Softmax — Inverse-weighted Union.
|
|
13
13
|
|
|
14
|
-
Extends
|
|
14
|
+
Extends DEWS-U by replacing the simple average of neighbor scores with an
|
|
15
15
|
inverse-distance-weighted average, so closer neighbors have a stronger
|
|
16
16
|
influence on the softmax routing — analogous to how KNORA-IU extends KNORA-U.
|
|
17
17
|
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DEWS-T: Distance-weighted Ensemble with Softmax — Trend.
|
|
3
|
+
"""
|
|
4
|
+
from deskit.base.knnbase import KNNBase
|
|
5
|
+
from deskit._config import make_finder, resolve_metric, prep_fit_inputs
|
|
6
|
+
from deskit.utils import to_numpy
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
_SIGNED_METRICS = {'mae', 'mse'}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _signed_residual(y_true, y_pred):
|
|
14
|
+
return float(y_true) - float(y_pred)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DEWST(KNNBase):
|
|
18
|
+
"""
|
|
19
|
+
DEWS-T: Distance-weighted Ensemble with Softmax — Trend.
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
task : str
|
|
23
|
+
'classification' or 'regression'.
|
|
24
|
+
metric : str or callable
|
|
25
|
+
Scoring function. 'mae' or 'mse' activate signed-residual mode;
|
|
26
|
+
all other metrics are trended directly.
|
|
27
|
+
mode : str
|
|
28
|
+
'max' if higher scores are better, 'min' if lower.
|
|
29
|
+
k : int
|
|
30
|
+
Neighbourhood size. Default: 10.
|
|
31
|
+
threshold : float
|
|
32
|
+
Competence gate. After per-neighbourhood normalisation (best=1.0,
|
|
33
|
+
worst=0.0), models below this fraction are excluded from softmax.
|
|
34
|
+
0.0 disables the gate; 1.0 reduces to OLA behaviour. Default: 0.5.
|
|
35
|
+
temperature : float, optional
|
|
36
|
+
Softmax sharpness. Lower = sharper routing toward the local best model.
|
|
37
|
+
Defaults to 0.1 for min-metrics, 1.0 otherwise.
|
|
38
|
+
r2_threshold : float
|
|
39
|
+
Minimum weighted R² for the trend line to be trusted. Below this value
|
|
40
|
+
the sample falls back to DEWS-I scoring for that model. Default: 0.2.
|
|
41
|
+
preset : str
|
|
42
|
+
Neighbour search preset. Default: 'balanced'. See list_presets().
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, task, metric='mae', mode='min', k=10,
|
|
46
|
+
threshold=0.5, temperature=None, r2_threshold=0.2,
|
|
47
|
+
preset='balanced', **kwargs):
|
|
48
|
+
metric_name, metric_fn = resolve_metric(metric)
|
|
49
|
+
finder = make_finder(preset, k, **kwargs)
|
|
50
|
+
|
|
51
|
+
self._use_signed = metric_name in _SIGNED_METRICS
|
|
52
|
+
self._metric_name = metric_name
|
|
53
|
+
self._convert = {'mae': np.abs, 'mse': np.square}.get(metric_name)
|
|
54
|
+
|
|
55
|
+
# For signed metrics, use signed residuals
|
|
56
|
+
super().__init__(
|
|
57
|
+
metric=_signed_residual if self._use_signed else metric_fn,
|
|
58
|
+
mode='max' if self._use_signed else mode,
|
|
59
|
+
neighbor_finder=finder
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
self._real_mode = mode
|
|
63
|
+
self.task = task
|
|
64
|
+
self.threshold = threshold
|
|
65
|
+
self._temperature = temperature
|
|
66
|
+
self.r2_threshold = r2_threshold
|
|
67
|
+
|
|
68
|
+
def fit(self, features, y, preds_dict):
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
features : array-like, shape (n_val, n_features)
|
|
74
|
+
Validation features. Must not overlap with train or test data.
|
|
75
|
+
y : array-like, shape (n_val,)
|
|
76
|
+
Validation ground-truth labels or values.
|
|
77
|
+
preds_dict : dict[str, array-like]
|
|
78
|
+
Validation predictions keyed by model name.
|
|
79
|
+
"""
|
|
80
|
+
features, y, preds_dict = prep_fit_inputs(
|
|
81
|
+
features, y, preds_dict, self._metric_name
|
|
82
|
+
)
|
|
83
|
+
super().fit(features, y, preds_dict)
|
|
84
|
+
|
|
85
|
+
def predict(self, x, temperature=None, threshold=None):
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
x : array-like, shape (n_features,) or (n_samples, n_features)
|
|
91
|
+
temperature : float, optional
|
|
92
|
+
Overrides the instance temperature for this call.
|
|
93
|
+
threshold : float, optional
|
|
94
|
+
Overrides the instance threshold for this call.
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
dict or list of dict
|
|
99
|
+
Single sample: {model_name: weight}. Batch: list of such dicts.
|
|
100
|
+
"""
|
|
101
|
+
t = temperature if temperature is not None else (
|
|
102
|
+
self._temperature if self._temperature is not None else
|
|
103
|
+
(0.1 if self._real_mode == 'min' else 1.0))
|
|
104
|
+
th = threshold if threshold is not None else self.threshold
|
|
105
|
+
|
|
106
|
+
x = np.atleast_2d(to_numpy(x))
|
|
107
|
+
batch_size = x.shape[0]
|
|
108
|
+
|
|
109
|
+
distances, indices = self.model.kneighbors(x) # (batch, k)
|
|
110
|
+
k = distances.shape[1]
|
|
111
|
+
|
|
112
|
+
# Inverse-distance weights
|
|
113
|
+
inv_dist = 1.0 / np.maximum(distances, 1e-8) # (batch, k)
|
|
114
|
+
inv_dist_w = inv_dist / inv_dist.sum(axis=1, keepdims=True)
|
|
115
|
+
|
|
116
|
+
# Scores at each neighbour: (batch, k, n_models).
|
|
117
|
+
neighbor_scores = self.matrix[indices]
|
|
118
|
+
|
|
119
|
+
# Weighted least squares trend
|
|
120
|
+
d_max = distances.max(axis=1, keepdims=True)
|
|
121
|
+
d_norm = distances / np.where(d_max > 0, d_max, 1.0) # (batch, k)
|
|
122
|
+
|
|
123
|
+
# X^{T}WX: shape (batch, 2, 2)
|
|
124
|
+
W = inv_dist_w # (batch, k)
|
|
125
|
+
a = W.sum(axis=1) # (batch,)
|
|
126
|
+
b = (W * d_norm).sum(axis=1)
|
|
127
|
+
d_v = (W * d_norm ** 2).sum(axis=1)
|
|
128
|
+
det = a * d_v - b ** 2 # (batch,)
|
|
129
|
+
bad_det = np.abs(det) <= 1e-12
|
|
130
|
+
det_safe = np.where(bad_det, 1.0, det)
|
|
131
|
+
|
|
132
|
+
# XᵀWy for all models: shape (batch, 2, n_models).
|
|
133
|
+
Wy = neighbor_scores * inv_dist_w[:, :, np.newaxis] # (batch, k, n_models)
|
|
134
|
+
Wdy = Wy * d_norm[:, :, np.newaxis]
|
|
135
|
+
XtWy_0 = Wy.sum(axis=1) # (batch, n_models)
|
|
136
|
+
XtWy_1 = Wdy.sum(axis=1) # (batch, n_models)
|
|
137
|
+
|
|
138
|
+
# Closed-form 2×2 inverse applied.
|
|
139
|
+
# intercept B0
|
|
140
|
+
# slope B1
|
|
141
|
+
intercept = (d_v[:, np.newaxis] * XtWy_0 -
|
|
142
|
+
b[:, np.newaxis] * XtWy_1) / det_safe[:, np.newaxis]
|
|
143
|
+
slope = (a[:, np.newaxis] * XtWy_1 -
|
|
144
|
+
b[:, np.newaxis] * XtWy_0) / det_safe[:, np.newaxis]
|
|
145
|
+
|
|
146
|
+
# Weighted R^2
|
|
147
|
+
y_hat = (intercept[:, np.newaxis, :] +
|
|
148
|
+
slope[:, np.newaxis, :] *
|
|
149
|
+
d_norm[:, :, np.newaxis]) # (batch, k, n_models)
|
|
150
|
+
y_wmean = XtWy_0 # weighted mean
|
|
151
|
+
ss_res = (inv_dist_w[:, :, np.newaxis] *
|
|
152
|
+
(neighbor_scores - y_hat) ** 2).sum(axis=1)
|
|
153
|
+
ss_tot = (inv_dist_w[:, :, np.newaxis] *
|
|
154
|
+
(neighbor_scores - y_wmean[:, np.newaxis, :]) ** 2).sum(axis=1)
|
|
155
|
+
r2 = np.where(ss_tot > 1e-12, 1.0 - ss_res / ss_tot, 0.0)
|
|
156
|
+
# Bad determinant = fallback.
|
|
157
|
+
r2 = np.where(bad_det[:, np.newaxis], 0.0, r2) # (batch, n_models)
|
|
158
|
+
|
|
159
|
+
# DEWS-I fallback
|
|
160
|
+
if self._use_signed:
|
|
161
|
+
# Convert signed residuals back to metric
|
|
162
|
+
fallback_raw = self._convert(neighbor_scores)
|
|
163
|
+
dewsi_scores = -(fallback_raw * inv_dist_w[:, :, np.newaxis]).sum(axis=1)
|
|
164
|
+
else:
|
|
165
|
+
dewsi_scores = XtWy_0
|
|
166
|
+
|
|
167
|
+
# Convert trend intercept to routing scord
|
|
168
|
+
if self._use_signed:
|
|
169
|
+
trend_scores = -self._convert(intercept) # negate for min-routing
|
|
170
|
+
else:
|
|
171
|
+
trend_scores = intercept
|
|
172
|
+
|
|
173
|
+
# Blend: trust trend where R² ≥ threshold, fall back otherwise.
|
|
174
|
+
use_trend = r2 >= self.r2_threshold
|
|
175
|
+
avg_scores = np.where(use_trend, trend_scores, dewsi_scores)
|
|
176
|
+
|
|
177
|
+
# Standard DEWS softmax
|
|
178
|
+
local_min = avg_scores.min(axis=1, keepdims=True)
|
|
179
|
+
local_max = avg_scores.max(axis=1, keepdims=True)
|
|
180
|
+
local_range = local_max - local_min
|
|
181
|
+
norm_scores = (avg_scores - local_min) / np.where(local_range > 0, local_range, 1.0)
|
|
182
|
+
|
|
183
|
+
if th > 0:
|
|
184
|
+
gate = norm_scores >= th
|
|
185
|
+
any_pass = gate.any(axis=1, keepdims=True)
|
|
186
|
+
gate = np.where(any_pass, gate, norm_scores == 1.0)
|
|
187
|
+
norm_scores = norm_scores * gate
|
|
188
|
+
|
|
189
|
+
max_scores = norm_scores.max(axis=1, keepdims=True)
|
|
190
|
+
exp_scores = np.exp((norm_scores - max_scores) / t)
|
|
191
|
+
if th > 0:
|
|
192
|
+
exp_scores = exp_scores * gate
|
|
193
|
+
total = exp_scores.sum(axis=1, keepdims=True)
|
|
194
|
+
weights = np.where(total > 0,
|
|
195
|
+
exp_scores / np.where(total > 0, total, 1.0),
|
|
196
|
+
np.full_like(exp_scores, 1.0 / len(self.models)))
|
|
197
|
+
|
|
198
|
+
if batch_size == 1:
|
|
199
|
+
return dict(zip(self.models, weights[0]))
|
|
200
|
+
return [dict(zip(self.models, w)) for w in weights]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
DEWS-U: K-Nearest Neighbors with Distance-Weighted Softmax.
|
|
3
3
|
"""
|
|
4
4
|
from deskit.base.knnbase import KNNBase
|
|
5
5
|
from deskit._config import make_finder, resolve_metric, prep_fit_inputs
|
|
@@ -7,9 +7,9 @@ from deskit.utils import to_numpy
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
class
|
|
10
|
+
class DEWSU(KNNBase):
|
|
11
11
|
"""
|
|
12
|
-
|
|
12
|
+
DEWS-U: K-Nearest Neighbors with Distance-Weighted Softmax.
|
|
13
13
|
|
|
14
14
|
Parameters
|
|
15
15
|
----------
|
|
@@ -3,7 +3,7 @@ DynamicRouter — string-based factory for programmatic algorithm selection.
|
|
|
3
3
|
|
|
4
4
|
Use DynamicRouter when you need to choose an algorithm via a string at runtime.
|
|
5
5
|
"""
|
|
6
|
-
from deskit.des.
|
|
6
|
+
from deskit.des.dewsu import DEWSU
|
|
7
7
|
from deskit.des.ola import OLA
|
|
8
8
|
from deskit.des.knorau import KNORAU
|
|
9
9
|
from deskit.des.knorae import KNORAE
|
|
@@ -12,7 +12,7 @@ from deskit._config import SPEED_PRESETS, list_presets
|
|
|
12
12
|
from deskit.utils import to_numpy, add_batch_dim
|
|
13
13
|
|
|
14
14
|
_METHOD_CLASSES = {
|
|
15
|
-
'
|
|
15
|
+
'DEWS-U': DEWSU,
|
|
16
16
|
'ola': OLA,
|
|
17
17
|
'knora-u': KNORAU,
|
|
18
18
|
'knora-e': KNORAE,
|
|
@@ -29,7 +29,7 @@ class DynamicRouter:
|
|
|
29
29
|
task : str
|
|
30
30
|
'classification' or 'regression'.
|
|
31
31
|
method : str
|
|
32
|
-
'
|
|
32
|
+
'DEWS-U', 'ola', 'knora-u', or 'knora-e'.
|
|
33
33
|
metric : str or callable
|
|
34
34
|
Per-sample scoring function. Built-in names: 'accuracy', 'mae', 'mse',
|
|
35
35
|
'rmse', 'log_loss', 'prob_correct'. Or any callable (y_true, y_pred) -> float.
|
|
@@ -40,7 +40,7 @@ class DynamicRouter:
|
|
|
40
40
|
threshold : float
|
|
41
41
|
Competence gate applied after per-neighborhood normalization.
|
|
42
42
|
temperature : float, optional
|
|
43
|
-
Softmax sharpness for
|
|
43
|
+
Softmax sharpness for DEWS-U. Ignored by other algorithms.
|
|
44
44
|
preset : str
|
|
45
45
|
Speed/accuracy preset. Call list_presets() for options.
|
|
46
46
|
feature_extractor : callable, optional
|
|
@@ -51,7 +51,7 @@ class DynamicRouter:
|
|
|
51
51
|
Forwarded to the neighbor finder constructor.
|
|
52
52
|
"""
|
|
53
53
|
|
|
54
|
-
def __init__(self, task, method='
|
|
54
|
+
def __init__(self, task, method='DEWS-U', metric='accuracy', mode='max',
|
|
55
55
|
k=10, threshold=0.5, temperature=None, preset='balanced',
|
|
56
56
|
feature_extractor=None, finder=None, **kwargs):
|
|
57
57
|
|
|
@@ -71,8 +71,8 @@ class DynamicRouter:
|
|
|
71
71
|
# Pass finder through as a kwarg when using preset='custom'.
|
|
72
72
|
extra = {'finder': finder} if finder is not None else {}
|
|
73
73
|
|
|
74
|
-
#
|
|
75
|
-
if method == '
|
|
74
|
+
# DEWSU accepts temperature; the others don't.
|
|
75
|
+
if method == 'DEWS-U':
|
|
76
76
|
self._des = cls(
|
|
77
77
|
task=task, metric=metric, mode=mode, k=k,
|
|
78
78
|
threshold=threshold, temperature=temperature,
|
|
@@ -108,7 +108,7 @@ class DynamicRouter:
|
|
|
108
108
|
----------
|
|
109
109
|
x : array-like, shape (n_features,) or (n_samples, n_features)
|
|
110
110
|
temperature : float, optional
|
|
111
|
-
|
|
111
|
+
DEWS-U only. Overrides the instance temperature for this call.
|
|
112
112
|
threshold : float, optional
|
|
113
113
|
Overrides the instance threshold for this call.
|
|
114
114
|
|
|
@@ -125,7 +125,7 @@ class DynamicRouter:
|
|
|
125
125
|
# Class methods
|
|
126
126
|
|
|
127
127
|
@classmethod
|
|
128
|
-
def from_data_size(cls, n_samples, n_features, task, method='
|
|
128
|
+
def from_data_size(cls, n_samples, n_features, task, method='DEWS-U',
|
|
129
129
|
metric='accuracy', mode='max', k=10, threshold=0.5,
|
|
130
130
|
n_queries=None, **extra_kwargs):
|
|
131
131
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deskit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A Python library for Dynamic Ensemble Selection
|
|
5
5
|
Author: Tikhon Vodyanov
|
|
6
6
|
License-Expression: MIT
|
|
@@ -31,7 +31,7 @@ Dynamic: license-file
|
|
|
31
31
|
|
|
32
32
|
# deskit
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
deskit is a flexible, lightweight, and easy-to-use ensembling library that implements
|
|
35
35
|
Dynamic Ensemble Selection (DES) algorithms for ensembling multiple ML models
|
|
36
36
|
on a given dataset.
|
|
37
37
|
|
|
@@ -43,6 +43,8 @@ requiring any wrappers, including custom models, popular ML libraries, and APIs.
|
|
|
43
43
|
deskit includes several DES algorithms, and it works with both classification
|
|
44
44
|
and regression.
|
|
45
45
|
|
|
46
|
+
See the full documentation [here](https://TikaaVo.github.io/deskit/).
|
|
47
|
+
|
|
46
48
|
# Dynamic Ensemble Selection
|
|
47
49
|
|
|
48
50
|
Ensemble learning in machine learning refers to when multiple models trained on a
|
|
@@ -148,14 +150,15 @@ weights = router.predict(X_test[i])
|
|
|
148
150
|
|
|
149
151
|
## Algorithms
|
|
150
152
|
|
|
151
|
-
| Method
|
|
152
|
-
|
|
153
|
-
| `
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
| `
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
153
|
+
| Method | Best for | Notes |
|
|
154
|
+
|------------|----------------|------------------------------------------------------------------------------------------------------|
|
|
155
|
+
| `DEWS-U` | Regression | Softmax over neighborhood-averaged scores. Temperature controls sharpness. |
|
|
156
|
+
| `DEWS-I` | Regression | Like DEWS-U but scores are inverse-distance weighted. |
|
|
157
|
+
| `DEWS-T` | Both | Like DEWS-U but fits a weighted trend line over neighbor scores and extrapolates to the test point. |
|
|
158
|
+
| `KNORA-U` | Classification | Vote-count weighting. Each model earns one vote per neighbor it correctly classifies. |
|
|
159
|
+
| `KNORA-E` | Classification | Intersection-based. Only models correct on all neighbors survive; falls back to smaller neighborhoods. |
|
|
160
|
+
| `KNORA-IU` | Classification | Like KNORA-U but votes are inverse-distance weighted. |
|
|
161
|
+
| `OLA` | Both | Hard selection: only the single best model in the neighborhood contributes. |
|
|
159
162
|
|
|
160
163
|
---
|
|
161
164
|
|
|
@@ -202,13 +205,18 @@ def pinball(y_true, y_pred, alpha=0.9):
|
|
|
202
205
|
e = y_true - y_pred
|
|
203
206
|
return alpha * e if e >= 0 else (alpha - 1) * e
|
|
204
207
|
|
|
205
|
-
router =
|
|
208
|
+
router = DEWSU(task="regression", metric=pinball, mode="min", k=20)
|
|
206
209
|
```
|
|
207
210
|
|
|
208
211
|
Built-in metric strings: `accuracy`, `mae`, `mse`, `rmse`, `log_loss`, `prob_correct`.
|
|
209
212
|
|
|
210
213
|
---
|
|
211
214
|
|
|
215
|
+
## Data types
|
|
216
|
+
|
|
217
|
+
deskit can be used with non-tabular data types like images, time series, and more. However, when used, the
|
|
218
|
+
passed features either need to be run through a feature extractor beforehand, such as a CNN backbone for images.
|
|
219
|
+
|
|
212
220
|
## Benchmark results
|
|
213
221
|
|
|
214
222
|
100-seed benchmark (seeds 0–99) on standard sklearn and OpenML datasets. "Best Single" is the best
|
|
@@ -224,39 +232,39 @@ Pool: KNN, Decision Tree, SVR, Ridge, Bayesian Ridge.
|
|
|
224
232
|
|
|
225
233
|
This pool was selected for having variability in architectures while avoiding a single dominant model.
|
|
226
234
|
|
|
227
|
-
deskit algorithms tested: OLA,
|
|
235
|
+
deskit algorithms tested: OLA, DEWS-U, DEWS-I, DEWS-T, KNORA-U, KNORA-E, KNORA-IU.
|
|
228
236
|
|
|
229
237
|
### Regression (MAE, lower is better)
|
|
230
238
|
|
|
231
|
-
% shown as delta vs Best Single.
|
|
239
|
+
% shown as delta vs Best Single. 20-seed mean.
|
|
232
240
|
|
|
233
|
-
| Dataset | Best Single | Simple Avg | deskit best
|
|
234
|
-
|
|
235
|
-
| California Housing (sklearn) | 0.
|
|
236
|
-
| Bike Sharing (OpenML) | 51.
|
|
237
|
-
| Abalone (OpenML) | **1.
|
|
238
|
-
| Diabetes (sklearn) | **44.
|
|
239
|
-
| Concrete Strength (OpenML) | 5.
|
|
241
|
+
| Dataset | Best Single | Simple Avg | deskit best |
|
|
242
|
+
|------------------------------|-------------|------------|---------------------------|
|
|
243
|
+
| California Housing (sklearn) | 0.3956 | +7.99% | **−2.54%** (DEWS-I) |
|
|
244
|
+
| Bike Sharing (OpenML) | 51.678 | +47.77% | **−6.86%** (DEWS-I) |
|
|
245
|
+
| Abalone (OpenML) | **1.4981** | +1.14% | +1.47% (KNORA-U/KNORA-IU) |
|
|
246
|
+
| Diabetes (sklearn) | **44.504** | +3.18% | +1.09% (DEWS-I/DEWS-T) |
|
|
247
|
+
| Concrete Strength (OpenML) | 5.2686 | +23.66% | **−1.20%** (DEWS-I) |
|
|
240
248
|
|
|
241
249
|
deskit beats best single and simple averaging on 3/5 regression datasets. This shows how DES can provide a
|
|
242
250
|
strong boost if used on the right dataset, but it might be counterproductive if used blindly.
|
|
243
251
|
|
|
244
252
|
KNORA variants are designed for classification, which explains the poor performance
|
|
245
253
|
on regression datasets; However, some exception can occur in certain datasets, either where
|
|
246
|
-
feature space
|
|
254
|
+
feature space has hard clusters (like in Concrete Strength) or when the target is discrete
|
|
247
255
|
and classification-like (like in Abalone).
|
|
248
256
|
|
|
249
257
|
### Classification (Accuracy, higher is better)
|
|
250
258
|
|
|
251
|
-
% shown as delta vs Best Single.
|
|
259
|
+
% shown as delta vs Best Single. 20-seed mean.
|
|
252
260
|
|
|
253
|
-
| Dataset | Best Single | Simple Avg | deskit best
|
|
254
|
-
|
|
255
|
-
| HAR (OpenML) | 98.24% | −0.
|
|
256
|
-
| Yeast (OpenML) |
|
|
257
|
-
| Image Segment (OpenML) | 93.
|
|
258
|
-
| Waveform (OpenML) | **
|
|
259
|
-
| Vowel (OpenML) |
|
|
261
|
+
| Dataset | Best Single | Simple Avg | deskit best |
|
|
262
|
+
|------------------------|-------------|------------|--------------------------|
|
|
263
|
+
| HAR (OpenML) | 98.24% | −0.33% | **+0.16%** (DEWS-T) |
|
|
264
|
+
| Yeast (OpenML) | 58.87% | +0.77% | **+1.66%** (KNORA-IU) |
|
|
265
|
+
| Image Segment (OpenML) | 93.70% | +1.40% | **+2.25%** (DEWS-T) |
|
|
266
|
+
| Waveform (OpenML) | **85.91%** | −0.98% | −0.39% (DEWS-T) |
|
|
267
|
+
| Vowel (OpenML) | 89.95% | −2.05% | **+0.93%** (KNORA-IU) |
|
|
260
268
|
|
|
261
269
|
deskit beats or matches best single and simple averaging on 4/5 classification datasets. As seen on regression, DES
|
|
262
270
|
can improve or hurt performance, so it must be used wisely, but if used correctly it can show promising results.
|
|
@@ -17,8 +17,9 @@ src/deskit/base/__init__.py
|
|
|
17
17
|
src/deskit/base/base.py
|
|
18
18
|
src/deskit/base/knnbase.py
|
|
19
19
|
src/deskit/des/__init__.py
|
|
20
|
-
src/deskit/des/
|
|
21
|
-
src/deskit/des/
|
|
20
|
+
src/deskit/des/dewsi.py
|
|
21
|
+
src/deskit/des/dewst.py
|
|
22
|
+
src/deskit/des/dewsu.py
|
|
22
23
|
src/deskit/des/knorae.py
|
|
23
24
|
src/deskit/des/knoraiu.py
|
|
24
25
|
src/deskit/des/knorau.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|