neural-trees 0.1.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.
- neural_trees-0.1.0/LICENSE +21 -0
- neural_trees-0.1.0/PKG-INFO +350 -0
- neural_trees-0.1.0/README.md +306 -0
- neural_trees-0.1.0/neural_trees/__init__.py +28 -0
- neural_trees-0.1.0/neural_trees/classical/__init__.py +7 -0
- neural_trees-0.1.0/neural_trees/classical/k_nearest_neighbors.py +116 -0
- neural_trees-0.1.0/neural_trees/classical/multilayer_perceptron.py +180 -0
- neural_trees-0.1.0/neural_trees/classical/naive_bayes.py +104 -0
- neural_trees-0.1.0/neural_trees/decision_trees/__init__.py +2 -0
- neural_trees-0.1.0/neural_trees/decision_trees/omnivariate_tree.py +193 -0
- neural_trees-0.1.0/neural_trees/decision_trees/soft_decision_tree.py +356 -0
- neural_trees-0.1.0/neural_trees/mixture_of_experts/__init__.py +1 -0
- neural_trees-0.1.0/neural_trees/mixture_of_experts/hierarchical_moe.py +279 -0
- neural_trees-0.1.0/neural_trees/statistical_tests/__init__.py +1 -0
- neural_trees-0.1.0/neural_trees/statistical_tests/classifier_comparison.py +295 -0
- neural_trees-0.1.0/neural_trees.egg-info/PKG-INFO +350 -0
- neural_trees-0.1.0/neural_trees.egg-info/SOURCES.txt +22 -0
- neural_trees-0.1.0/neural_trees.egg-info/dependency_links.txt +1 -0
- neural_trees-0.1.0/neural_trees.egg-info/requires.txt +11 -0
- neural_trees-0.1.0/neural_trees.egg-info/top_level.txt +1 -0
- neural_trees-0.1.0/setup.cfg +4 -0
- neural_trees-0.1.0/setup.py +55 -0
- neural_trees-0.1.0/tests/test_soft_decision_tree.py +74 -0
- neural_trees-0.1.0/tests/test_statistical_tests.py +82 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Cagri Temel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: neural-trees
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Implementations of algorithms from Prof. Dr. Ethem Alpaydın's research papers and ML textbook (MIT Press).
|
|
5
|
+
Home-page: https://github.com/cgrtml/neural-trees
|
|
6
|
+
Author: Cagri Temel
|
|
7
|
+
Author-email: cagritemel34@gmail.com
|
|
8
|
+
Keywords: machine learning,soft decision trees,mixture of experts,statistical tests,alpaydin,sklearn
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Intended Audience :: Education
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: >=3.8
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: numpy>=1.21
|
|
23
|
+
Requires-Dist: scipy>=1.7
|
|
24
|
+
Requires-Dist: scikit-learn>=1.0
|
|
25
|
+
Requires-Dist: torch>=1.10
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
29
|
+
Requires-Dist: matplotlib>=3.4; extra == "dev"
|
|
30
|
+
Requires-Dist: jupyter; extra == "dev"
|
|
31
|
+
Requires-Dist: plotly; extra == "dev"
|
|
32
|
+
Dynamic: author
|
|
33
|
+
Dynamic: author-email
|
|
34
|
+
Dynamic: classifier
|
|
35
|
+
Dynamic: description
|
|
36
|
+
Dynamic: description-content-type
|
|
37
|
+
Dynamic: home-page
|
|
38
|
+
Dynamic: keywords
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
Dynamic: provides-extra
|
|
41
|
+
Dynamic: requires-dist
|
|
42
|
+
Dynamic: requires-python
|
|
43
|
+
Dynamic: summary
|
|
44
|
+
|
|
45
|
+
# neural-trees
|
|
46
|
+
|
|
47
|
+
> PyTorch + sklearn implementations of the tree and mixture-of-experts algorithms
|
|
48
|
+
> from Alpaydın's research papers — the ones that never got a proper open-source home.
|
|
49
|
+
|
|
50
|
+
[](https://www.python.org/downloads/)
|
|
51
|
+
[](LICENSE)
|
|
52
|
+
[](https://github.com/cgrtml/neural-trees/actions)
|
|
53
|
+
[](https://scikit-learn.org)
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Why?
|
|
58
|
+
|
|
59
|
+
I was reading through Alpaydın's *Introduction to Machine Learning* and his papers
|
|
60
|
+
and kept hitting the same wall: interesting algorithms, no usable Python code anywhere.
|
|
61
|
+
The Soft Decision Tree paper (ICPR 2012) alone has hundreds of citations but the implementations
|
|
62
|
+
floating around are incomplete, undocumented, or years out of date.
|
|
63
|
+
|
|
64
|
+
So I wrote them myself — clean, tested, and fully compatible with the sklearn API.
|
|
65
|
+
|
|
66
|
+
Covered so far:
|
|
67
|
+
|
|
68
|
+
| Algorithm | Paper | Status |
|
|
69
|
+
|-----------|-------|--------|
|
|
70
|
+
| **Soft Decision Trees** | İrsoy, Yıldız, Alpaydın (ICPR 2012) | ✅ PyTorch + sklearn API |
|
|
71
|
+
| **Omnivariate Decision Trees** | Yıldız & Alpaydın (IEEE TNN 2001) | ✅ |
|
|
72
|
+
| **Hierarchical Mixture of Experts + Dropout** | İrsoy & Alpaydın (Neurocomputing 2021) | ✅ PyTorch |
|
|
73
|
+
| **GAL: Grow and Learn Networks** | Alpaydın (IJPRAI 1994) | ✅ |
|
|
74
|
+
| **Combined 5×2cv F Test** | Alpaydın (Neural Computation 1999) | ✅ Gold-standard classifier comparison |
|
|
75
|
+
| **McNemar's Test** | — | ✅ |
|
|
76
|
+
| **Naive Bayes (Gaussian/Bernoulli/Multinomial)** | Textbook Ch. 3 | ✅ |
|
|
77
|
+
| **Distance-Weighted KNN + CNN** | Alpaydın (AIR 1997) | ✅ |
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Installation
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pip install neural-trees
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Or install from source:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
git clone https://github.com/cgrtml/neural-trees.git
|
|
91
|
+
cd neural-trees
|
|
92
|
+
pip install -e ".[dev]"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Quick Start
|
|
98
|
+
|
|
99
|
+
### Soft Decision Trees
|
|
100
|
+
|
|
101
|
+
The flagship algorithm. Unlike hard decision trees, every sample reaches every leaf with some probability — making the tree **fully differentiable** and trainable end-to-end with backpropagation.
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from neural_trees import SoftDecisionTree
|
|
105
|
+
from sklearn.datasets import load_iris
|
|
106
|
+
from sklearn.model_selection import train_test_split
|
|
107
|
+
|
|
108
|
+
X, y = load_iris(return_X_y=True)
|
|
109
|
+
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
|
|
110
|
+
|
|
111
|
+
sdt = SoftDecisionTree(depth=4, max_epochs=40, penalty_coef=1e-3)
|
|
112
|
+
sdt.fit(X_train, y_train)
|
|
113
|
+
|
|
114
|
+
print(f"Accuracy: {sdt.score(X_test, y_test):.4f}")
|
|
115
|
+
|
|
116
|
+
# Inspect what each leaf learned
|
|
117
|
+
leaf_distributions = sdt.get_leaf_distributions() # shape: (n_leaves, n_classes)
|
|
118
|
+
|
|
119
|
+
# Inspect the split direction at each internal node
|
|
120
|
+
split_weights = sdt.get_split_weights() # list of weight vectors
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Key idea** (Irsoy, Yıldız, Alpaydın, 2012):
|
|
124
|
+
|
|
125
|
+
At each internal node *i*:
|
|
126
|
+
|
|
127
|
+
$$p_i(\mathbf{x}) = \sigma(\mathbf{w}_i^\top \mathbf{x} + b_i)$$
|
|
128
|
+
|
|
129
|
+
The probability of reaching leaf $\ell$ is the product of gate values along the path. Final prediction:
|
|
130
|
+
|
|
131
|
+
$$P(y \mid \mathbf{x}) = \sum_\ell \mu_\ell(\mathbf{x}) \cdot Q_\ell(y)$$
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
### Comparing Two Classifiers — The Gold Standard Test
|
|
136
|
+
|
|
137
|
+
Alpaydın's **Combined 5×2cv F Test** (Neural Computation, 1999) is the statistically correct way to compare two classifiers. It overcomes the inflated Type I error of the paired t-test.
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from neural_trees.statistical_tests import combined_5x2cv_f_test
|
|
141
|
+
from sklearn.svm import SVC
|
|
142
|
+
from sklearn.tree import DecisionTreeClassifier
|
|
143
|
+
from sklearn.datasets import load_breast_cancer
|
|
144
|
+
|
|
145
|
+
X, y = load_breast_cancer(return_X_y=True)
|
|
146
|
+
|
|
147
|
+
result = combined_5x2cv_f_test(
|
|
148
|
+
clf_A=DecisionTreeClassifier(),
|
|
149
|
+
clf_B=SVC(kernel="rbf"),
|
|
150
|
+
X=X, y=y,
|
|
151
|
+
alpha=0.05
|
|
152
|
+
)
|
|
153
|
+
print(result)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
StatisticalTestResult(
|
|
158
|
+
test = Alpaydın's Combined 5×2cv F Test
|
|
159
|
+
statistic = 12.4731
|
|
160
|
+
p-value = 0.0083
|
|
161
|
+
alpha = 0.05
|
|
162
|
+
decision = ✓ REJECT H0
|
|
163
|
+
note = Classifiers significantly differ
|
|
164
|
+
)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Why not just use a t-test?**
|
|
168
|
+
The paired t-test reuses training data across folds — the differences are correlated, inflating the false positive rate. Alpaydın's F test accounts for this by estimating variance within each 2-fold split, giving a much better calibrated test.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### Hierarchical Mixture of Experts with Dropout
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from neural_trees import HierarchicalMixtureOfExperts
|
|
176
|
+
from sklearn.datasets import load_digits
|
|
177
|
+
|
|
178
|
+
X, y = load_digits(return_X_y=True)
|
|
179
|
+
|
|
180
|
+
moe = HierarchicalMixtureOfExperts(
|
|
181
|
+
depth=2,
|
|
182
|
+
branching_factor=4, # 4^2 = 16 expert leaves
|
|
183
|
+
dropout_rate=0.3, # Dropout on gating networks (Irsoy & Alpaydın, 2021)
|
|
184
|
+
max_epochs=50,
|
|
185
|
+
verbose=True,
|
|
186
|
+
)
|
|
187
|
+
moe.fit(X, y)
|
|
188
|
+
print(f"Accuracy: {moe.score(X, y):.4f}")
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
### GAL — Grow and Learn Networks
|
|
194
|
+
|
|
195
|
+
No need to specify architecture. The network grows when it can't learn and prunes itself when neurons become redundant.
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from neural_trees.classical import GALNetwork
|
|
199
|
+
from sklearn.datasets import load_wine
|
|
200
|
+
|
|
201
|
+
X, y = load_wine(return_X_y=True)
|
|
202
|
+
|
|
203
|
+
gal = GALNetwork(
|
|
204
|
+
initial_hidden=2,
|
|
205
|
+
max_hidden=40,
|
|
206
|
+
grow_threshold=0.15,
|
|
207
|
+
prune_threshold=1e-4,
|
|
208
|
+
max_epochs=100,
|
|
209
|
+
verbose=True,
|
|
210
|
+
)
|
|
211
|
+
gal.fit(X, y)
|
|
212
|
+
print(f"Final hidden units: {gal.n_hidden_final_}")
|
|
213
|
+
print(f"Accuracy: {gal.score(X, y):.4f}")
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
### Omnivariate Decision Trees
|
|
219
|
+
|
|
220
|
+
At each node, automatically selects the best split type (univariate, linear LDA, or nonlinear MLP) using cross-validation.
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
from neural_trees import OmnivariateDecisionTree
|
|
224
|
+
from sklearn.datasets import load_wine
|
|
225
|
+
|
|
226
|
+
X, y = load_wine(return_X_y=True)
|
|
227
|
+
|
|
228
|
+
odt = OmnivariateDecisionTree(max_depth=4, cv_folds=3)
|
|
229
|
+
odt.fit(X, y)
|
|
230
|
+
|
|
231
|
+
# See how many nodes used each split type
|
|
232
|
+
print(odt.get_split_type_distribution())
|
|
233
|
+
# {'univariate': 3, 'linear': 4, 'nonlinear': 1}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## All sklearn-compatible
|
|
239
|
+
|
|
240
|
+
Every model follows the `fit` / `predict` / `predict_proba` / `score` interface:
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
from sklearn.pipeline import Pipeline
|
|
244
|
+
from sklearn.preprocessing import StandardScaler
|
|
245
|
+
|
|
246
|
+
pipe = Pipeline([
|
|
247
|
+
("scaler", StandardScaler()),
|
|
248
|
+
("sdt", SoftDecisionTree(depth=4, max_epochs=30)),
|
|
249
|
+
])
|
|
250
|
+
pipe.fit(X_train, y_train)
|
|
251
|
+
pipe.score(X_test, y_test)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
from sklearn.model_selection import GridSearchCV
|
|
256
|
+
|
|
257
|
+
param_grid = {"sdt__depth": [3, 4, 5], "sdt__penalty_coef": [1e-4, 1e-3, 1e-2]}
|
|
258
|
+
gs = GridSearchCV(pipe, param_grid, cv=5)
|
|
259
|
+
gs.fit(X_train, y_train)
|
|
260
|
+
print(gs.best_params_)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Notebooks
|
|
266
|
+
|
|
267
|
+
| Notebook | Description |
|
|
268
|
+
|----------|-------------|
|
|
269
|
+
| [`01_soft_decision_trees.ipynb`](notebooks/01_soft_decision_trees.ipynb) | Training, visualization, comparison with CART |
|
|
270
|
+
| [`02_classifier_comparison_tests.ipynb`](notebooks/02_classifier_comparison_tests.ipynb) | When to use which statistical test |
|
|
271
|
+
| [`03_hierarchical_moe.ipynb`](notebooks/03_hierarchical_moe.ipynb) | HMoE training and expert specialization |
|
|
272
|
+
| [`04_gal_network.ipynb`](notebooks/04_gal_network.ipynb) | Dynamic architecture growth/pruning |
|
|
273
|
+
| [`05_omnivariate_trees.ipynb`](notebooks/05_omnivariate_trees.ipynb) | Node-level split type analysis |
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## About the Author
|
|
278
|
+
|
|
279
|
+
**Prof. Dr. Ethem Alpaydın** is one of the world's leading machine learning researchers.
|
|
280
|
+
|
|
281
|
+
- Professor Emeritus at Boğaziçi University (Istanbul), now at Özyeğin University
|
|
282
|
+
- Author of *Introduction to Machine Learning* (MIT Press, 4 editions, 2004–2020) — used in hundreds of universities globally
|
|
283
|
+
- Author of *Machine Learning: The New AI* (MIT Press, 2016)
|
|
284
|
+
- PhD from EPFL (1990); research stays at UC Berkeley, MIT, and IDIAP
|
|
285
|
+
- **34,000+ citations** on Google Scholar
|
|
286
|
+
- IEEE Senior Member; Pattern Recognition journal editorial board
|
|
287
|
+
|
|
288
|
+
His 1999 paper on the Combined 5×2cv F Test is the standard reference for classifier comparison. His Soft Decision Trees paper (2012) remains one of the most elegant proposals for differentiable tree models — predating the modern neural tree literature.
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Citation
|
|
293
|
+
|
|
294
|
+
If you use this library in academic work, please cite the original papers:
|
|
295
|
+
|
|
296
|
+
```bibtex
|
|
297
|
+
@book{alpaydin2020introduction,
|
|
298
|
+
title = {Introduction to Machine Learning},
|
|
299
|
+
author = {Alpayd{\i}n, Ethem},
|
|
300
|
+
year = {2020},
|
|
301
|
+
edition = {4th},
|
|
302
|
+
publisher = {MIT Press}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
@article{irsoy2021dropout,
|
|
306
|
+
title = {Dropout Regularization in Hierarchical Mixture of Experts},
|
|
307
|
+
author = {\.{I}rsoy, O{\u{g}}uzhan and Alpayd{\i}n, Ethem},
|
|
308
|
+
journal = {Neurocomputing},
|
|
309
|
+
volume = {419},
|
|
310
|
+
pages = {148--156},
|
|
311
|
+
year = {2021}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
@inproceedings{irsoy2012soft,
|
|
315
|
+
title = {Soft Decision Trees},
|
|
316
|
+
author = {\.{I}rsoy, O{\u{g}}uzhan and Y{\i}ld{\i}z, Olcay Taner and Alpayd{\i}n, Ethem},
|
|
317
|
+
booktitle = {Proceedings of the 21st International Conference on Pattern Recognition (ICPR)},
|
|
318
|
+
year = {2012}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
@article{alpaydin1999combined,
|
|
322
|
+
title = {Combined 5x2cv {F} Test for Comparing Supervised Classification Learning Algorithms},
|
|
323
|
+
author = {Alpayd{\i}n, Ethem},
|
|
324
|
+
journal = {Neural Computation},
|
|
325
|
+
volume = {11},
|
|
326
|
+
number = {8},
|
|
327
|
+
pages = {1885--1892},
|
|
328
|
+
year = {1999}
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Roadmap
|
|
335
|
+
|
|
336
|
+
Things I'm planning to add:
|
|
337
|
+
|
|
338
|
+
- [ ] Multiple Kernel Learning (Gönen & Alpaydın, JMLR 2011)
|
|
339
|
+
- [ ] Localized Multiple Kernel Learning (ICML 2008)
|
|
340
|
+
- [ ] Convolutional Soft Decision Trees (ICANN 2018)
|
|
341
|
+
- [ ] Decision boundary visualization utilities
|
|
342
|
+
- [ ] Benchmark comparison on UCI datasets
|
|
343
|
+
|
|
344
|
+
If you find a bug or want to implement one of these, open an issue.
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## License
|
|
349
|
+
|
|
350
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# neural-trees
|
|
2
|
+
|
|
3
|
+
> PyTorch + sklearn implementations of the tree and mixture-of-experts algorithms
|
|
4
|
+
> from Alpaydın's research papers — the ones that never got a proper open-source home.
|
|
5
|
+
|
|
6
|
+
[](https://www.python.org/downloads/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](https://github.com/cgrtml/neural-trees/actions)
|
|
9
|
+
[](https://scikit-learn.org)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Why?
|
|
14
|
+
|
|
15
|
+
I was reading through Alpaydın's *Introduction to Machine Learning* and his papers
|
|
16
|
+
and kept hitting the same wall: interesting algorithms, no usable Python code anywhere.
|
|
17
|
+
The Soft Decision Tree paper (ICPR 2012) alone has hundreds of citations but the implementations
|
|
18
|
+
floating around are incomplete, undocumented, or years out of date.
|
|
19
|
+
|
|
20
|
+
So I wrote them myself — clean, tested, and fully compatible with the sklearn API.
|
|
21
|
+
|
|
22
|
+
Covered so far:
|
|
23
|
+
|
|
24
|
+
| Algorithm | Paper | Status |
|
|
25
|
+
|-----------|-------|--------|
|
|
26
|
+
| **Soft Decision Trees** | İrsoy, Yıldız, Alpaydın (ICPR 2012) | ✅ PyTorch + sklearn API |
|
|
27
|
+
| **Omnivariate Decision Trees** | Yıldız & Alpaydın (IEEE TNN 2001) | ✅ |
|
|
28
|
+
| **Hierarchical Mixture of Experts + Dropout** | İrsoy & Alpaydın (Neurocomputing 2021) | ✅ PyTorch |
|
|
29
|
+
| **GAL: Grow and Learn Networks** | Alpaydın (IJPRAI 1994) | ✅ |
|
|
30
|
+
| **Combined 5×2cv F Test** | Alpaydın (Neural Computation 1999) | ✅ Gold-standard classifier comparison |
|
|
31
|
+
| **McNemar's Test** | — | ✅ |
|
|
32
|
+
| **Naive Bayes (Gaussian/Bernoulli/Multinomial)** | Textbook Ch. 3 | ✅ |
|
|
33
|
+
| **Distance-Weighted KNN + CNN** | Alpaydın (AIR 1997) | ✅ |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install neural-trees
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Or install from source:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
git clone https://github.com/cgrtml/neural-trees.git
|
|
47
|
+
cd neural-trees
|
|
48
|
+
pip install -e ".[dev]"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
### Soft Decision Trees
|
|
56
|
+
|
|
57
|
+
The flagship algorithm. Unlike hard decision trees, every sample reaches every leaf with some probability — making the tree **fully differentiable** and trainable end-to-end with backpropagation.
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from neural_trees import SoftDecisionTree
|
|
61
|
+
from sklearn.datasets import load_iris
|
|
62
|
+
from sklearn.model_selection import train_test_split
|
|
63
|
+
|
|
64
|
+
X, y = load_iris(return_X_y=True)
|
|
65
|
+
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
|
|
66
|
+
|
|
67
|
+
sdt = SoftDecisionTree(depth=4, max_epochs=40, penalty_coef=1e-3)
|
|
68
|
+
sdt.fit(X_train, y_train)
|
|
69
|
+
|
|
70
|
+
print(f"Accuracy: {sdt.score(X_test, y_test):.4f}")
|
|
71
|
+
|
|
72
|
+
# Inspect what each leaf learned
|
|
73
|
+
leaf_distributions = sdt.get_leaf_distributions() # shape: (n_leaves, n_classes)
|
|
74
|
+
|
|
75
|
+
# Inspect the split direction at each internal node
|
|
76
|
+
split_weights = sdt.get_split_weights() # list of weight vectors
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Key idea** (Irsoy, Yıldız, Alpaydın, 2012):
|
|
80
|
+
|
|
81
|
+
At each internal node *i*:
|
|
82
|
+
|
|
83
|
+
$$p_i(\mathbf{x}) = \sigma(\mathbf{w}_i^\top \mathbf{x} + b_i)$$
|
|
84
|
+
|
|
85
|
+
The probability of reaching leaf $\ell$ is the product of gate values along the path. Final prediction:
|
|
86
|
+
|
|
87
|
+
$$P(y \mid \mathbf{x}) = \sum_\ell \mu_\ell(\mathbf{x}) \cdot Q_\ell(y)$$
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### Comparing Two Classifiers — The Gold Standard Test
|
|
92
|
+
|
|
93
|
+
Alpaydın's **Combined 5×2cv F Test** (Neural Computation, 1999) is the statistically correct way to compare two classifiers. It overcomes the inflated Type I error of the paired t-test.
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from neural_trees.statistical_tests import combined_5x2cv_f_test
|
|
97
|
+
from sklearn.svm import SVC
|
|
98
|
+
from sklearn.tree import DecisionTreeClassifier
|
|
99
|
+
from sklearn.datasets import load_breast_cancer
|
|
100
|
+
|
|
101
|
+
X, y = load_breast_cancer(return_X_y=True)
|
|
102
|
+
|
|
103
|
+
result = combined_5x2cv_f_test(
|
|
104
|
+
clf_A=DecisionTreeClassifier(),
|
|
105
|
+
clf_B=SVC(kernel="rbf"),
|
|
106
|
+
X=X, y=y,
|
|
107
|
+
alpha=0.05
|
|
108
|
+
)
|
|
109
|
+
print(result)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
StatisticalTestResult(
|
|
114
|
+
test = Alpaydın's Combined 5×2cv F Test
|
|
115
|
+
statistic = 12.4731
|
|
116
|
+
p-value = 0.0083
|
|
117
|
+
alpha = 0.05
|
|
118
|
+
decision = ✓ REJECT H0
|
|
119
|
+
note = Classifiers significantly differ
|
|
120
|
+
)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Why not just use a t-test?**
|
|
124
|
+
The paired t-test reuses training data across folds — the differences are correlated, inflating the false positive rate. Alpaydın's F test accounts for this by estimating variance within each 2-fold split, giving a much better calibrated test.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
### Hierarchical Mixture of Experts with Dropout
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from neural_trees import HierarchicalMixtureOfExperts
|
|
132
|
+
from sklearn.datasets import load_digits
|
|
133
|
+
|
|
134
|
+
X, y = load_digits(return_X_y=True)
|
|
135
|
+
|
|
136
|
+
moe = HierarchicalMixtureOfExperts(
|
|
137
|
+
depth=2,
|
|
138
|
+
branching_factor=4, # 4^2 = 16 expert leaves
|
|
139
|
+
dropout_rate=0.3, # Dropout on gating networks (Irsoy & Alpaydın, 2021)
|
|
140
|
+
max_epochs=50,
|
|
141
|
+
verbose=True,
|
|
142
|
+
)
|
|
143
|
+
moe.fit(X, y)
|
|
144
|
+
print(f"Accuracy: {moe.score(X, y):.4f}")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
### GAL — Grow and Learn Networks
|
|
150
|
+
|
|
151
|
+
No need to specify architecture. The network grows when it can't learn and prunes itself when neurons become redundant.
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from neural_trees.classical import GALNetwork
|
|
155
|
+
from sklearn.datasets import load_wine
|
|
156
|
+
|
|
157
|
+
X, y = load_wine(return_X_y=True)
|
|
158
|
+
|
|
159
|
+
gal = GALNetwork(
|
|
160
|
+
initial_hidden=2,
|
|
161
|
+
max_hidden=40,
|
|
162
|
+
grow_threshold=0.15,
|
|
163
|
+
prune_threshold=1e-4,
|
|
164
|
+
max_epochs=100,
|
|
165
|
+
verbose=True,
|
|
166
|
+
)
|
|
167
|
+
gal.fit(X, y)
|
|
168
|
+
print(f"Final hidden units: {gal.n_hidden_final_}")
|
|
169
|
+
print(f"Accuracy: {gal.score(X, y):.4f}")
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
### Omnivariate Decision Trees
|
|
175
|
+
|
|
176
|
+
At each node, automatically selects the best split type (univariate, linear LDA, or nonlinear MLP) using cross-validation.
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from neural_trees import OmnivariateDecisionTree
|
|
180
|
+
from sklearn.datasets import load_wine
|
|
181
|
+
|
|
182
|
+
X, y = load_wine(return_X_y=True)
|
|
183
|
+
|
|
184
|
+
odt = OmnivariateDecisionTree(max_depth=4, cv_folds=3)
|
|
185
|
+
odt.fit(X, y)
|
|
186
|
+
|
|
187
|
+
# See how many nodes used each split type
|
|
188
|
+
print(odt.get_split_type_distribution())
|
|
189
|
+
# {'univariate': 3, 'linear': 4, 'nonlinear': 1}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## All sklearn-compatible
|
|
195
|
+
|
|
196
|
+
Every model follows the `fit` / `predict` / `predict_proba` / `score` interface:
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
from sklearn.pipeline import Pipeline
|
|
200
|
+
from sklearn.preprocessing import StandardScaler
|
|
201
|
+
|
|
202
|
+
pipe = Pipeline([
|
|
203
|
+
("scaler", StandardScaler()),
|
|
204
|
+
("sdt", SoftDecisionTree(depth=4, max_epochs=30)),
|
|
205
|
+
])
|
|
206
|
+
pipe.fit(X_train, y_train)
|
|
207
|
+
pipe.score(X_test, y_test)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
from sklearn.model_selection import GridSearchCV
|
|
212
|
+
|
|
213
|
+
param_grid = {"sdt__depth": [3, 4, 5], "sdt__penalty_coef": [1e-4, 1e-3, 1e-2]}
|
|
214
|
+
gs = GridSearchCV(pipe, param_grid, cv=5)
|
|
215
|
+
gs.fit(X_train, y_train)
|
|
216
|
+
print(gs.best_params_)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Notebooks
|
|
222
|
+
|
|
223
|
+
| Notebook | Description |
|
|
224
|
+
|----------|-------------|
|
|
225
|
+
| [`01_soft_decision_trees.ipynb`](notebooks/01_soft_decision_trees.ipynb) | Training, visualization, comparison with CART |
|
|
226
|
+
| [`02_classifier_comparison_tests.ipynb`](notebooks/02_classifier_comparison_tests.ipynb) | When to use which statistical test |
|
|
227
|
+
| [`03_hierarchical_moe.ipynb`](notebooks/03_hierarchical_moe.ipynb) | HMoE training and expert specialization |
|
|
228
|
+
| [`04_gal_network.ipynb`](notebooks/04_gal_network.ipynb) | Dynamic architecture growth/pruning |
|
|
229
|
+
| [`05_omnivariate_trees.ipynb`](notebooks/05_omnivariate_trees.ipynb) | Node-level split type analysis |
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## About the Author
|
|
234
|
+
|
|
235
|
+
**Prof. Dr. Ethem Alpaydın** is one of the world's leading machine learning researchers.
|
|
236
|
+
|
|
237
|
+
- Professor Emeritus at Boğaziçi University (Istanbul), now at Özyeğin University
|
|
238
|
+
- Author of *Introduction to Machine Learning* (MIT Press, 4 editions, 2004–2020) — used in hundreds of universities globally
|
|
239
|
+
- Author of *Machine Learning: The New AI* (MIT Press, 2016)
|
|
240
|
+
- PhD from EPFL (1990); research stays at UC Berkeley, MIT, and IDIAP
|
|
241
|
+
- **34,000+ citations** on Google Scholar
|
|
242
|
+
- IEEE Senior Member; Pattern Recognition journal editorial board
|
|
243
|
+
|
|
244
|
+
His 1999 paper on the Combined 5×2cv F Test is the standard reference for classifier comparison. His Soft Decision Trees paper (2012) remains one of the most elegant proposals for differentiable tree models — predating the modern neural tree literature.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Citation
|
|
249
|
+
|
|
250
|
+
If you use this library in academic work, please cite the original papers:
|
|
251
|
+
|
|
252
|
+
```bibtex
|
|
253
|
+
@book{alpaydin2020introduction,
|
|
254
|
+
title = {Introduction to Machine Learning},
|
|
255
|
+
author = {Alpayd{\i}n, Ethem},
|
|
256
|
+
year = {2020},
|
|
257
|
+
edition = {4th},
|
|
258
|
+
publisher = {MIT Press}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
@article{irsoy2021dropout,
|
|
262
|
+
title = {Dropout Regularization in Hierarchical Mixture of Experts},
|
|
263
|
+
author = {\.{I}rsoy, O{\u{g}}uzhan and Alpayd{\i}n, Ethem},
|
|
264
|
+
journal = {Neurocomputing},
|
|
265
|
+
volume = {419},
|
|
266
|
+
pages = {148--156},
|
|
267
|
+
year = {2021}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
@inproceedings{irsoy2012soft,
|
|
271
|
+
title = {Soft Decision Trees},
|
|
272
|
+
author = {\.{I}rsoy, O{\u{g}}uzhan and Y{\i}ld{\i}z, Olcay Taner and Alpayd{\i}n, Ethem},
|
|
273
|
+
booktitle = {Proceedings of the 21st International Conference on Pattern Recognition (ICPR)},
|
|
274
|
+
year = {2012}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
@article{alpaydin1999combined,
|
|
278
|
+
title = {Combined 5x2cv {F} Test for Comparing Supervised Classification Learning Algorithms},
|
|
279
|
+
author = {Alpayd{\i}n, Ethem},
|
|
280
|
+
journal = {Neural Computation},
|
|
281
|
+
volume = {11},
|
|
282
|
+
number = {8},
|
|
283
|
+
pages = {1885--1892},
|
|
284
|
+
year = {1999}
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Roadmap
|
|
291
|
+
|
|
292
|
+
Things I'm planning to add:
|
|
293
|
+
|
|
294
|
+
- [ ] Multiple Kernel Learning (Gönen & Alpaydın, JMLR 2011)
|
|
295
|
+
- [ ] Localized Multiple Kernel Learning (ICML 2008)
|
|
296
|
+
- [ ] Convolutional Soft Decision Trees (ICANN 2018)
|
|
297
|
+
- [ ] Decision boundary visualization utilities
|
|
298
|
+
- [ ] Benchmark comparison on UCI datasets
|
|
299
|
+
|
|
300
|
+
If you find a bug or want to implement one of these, open an issue.
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## License
|
|
305
|
+
|
|
306
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
neural-trees: Implementations of algorithms from Prof. Dr. Ethem Alpaydın's
|
|
3
|
+
research papers and textbook "Introduction to Machine Learning" (MIT Press).
|
|
4
|
+
|
|
5
|
+
Reference:
|
|
6
|
+
Alpaydın, E. (2020). Introduction to Machine Learning (4th ed.). MIT Press.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
__version__ = "0.1.0"
|
|
10
|
+
__author__ = "Cagri Temel"
|
|
11
|
+
|
|
12
|
+
from neural_trees.decision_trees.soft_decision_tree import SoftDecisionTree
|
|
13
|
+
from neural_trees.decision_trees.omnivariate_tree import OmnivariateDecisionTree
|
|
14
|
+
from neural_trees.statistical_tests.classifier_comparison import (
|
|
15
|
+
combined_5x2cv_f_test,
|
|
16
|
+
mcnemar_test,
|
|
17
|
+
paired_t_test,
|
|
18
|
+
)
|
|
19
|
+
from neural_trees.mixture_of_experts.hierarchical_moe import HierarchicalMixtureOfExperts
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"SoftDecisionTree",
|
|
23
|
+
"OmnivariateDecisionTree",
|
|
24
|
+
"HierarchicalMixtureOfExperts",
|
|
25
|
+
"combined_5x2cv_f_test",
|
|
26
|
+
"mcnemar_test",
|
|
27
|
+
"paired_t_test",
|
|
28
|
+
]
|