neural-trees
Soft decision trees, mixture of experts, and statistical model comparison tests for Python. A scikit-learn compatible library implementing classic machine learning algorithms from research papers, with a PyTorch backend.
Decision boundary learning with Soft Decision Trees on a toy dataset.
Features
- scikit-learn compatible API (
fit,predict,score, works inPipeline) - PyTorch backend with GPU support
- Soft Decision Trees, Hierarchical Mixture of Experts, Multivariate and Omnivariate Trees, GAL
- Combined 5x2cv F test, McNemar's test, paired t-test for classifier comparison
- Tested on standard benchmarks (Iris, Wine, Breast Cancer)
Installation
pip install neural-trees
Install from source
git clone https://github.com/cgrtml/neural-trees.git
cd neural-trees
pip install -e .
Quick Start
Train a Soft Decision Tree on the Iris dataset:
from neural_trees import SoftDecisionTree
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
model = SoftDecisionTree(depth=4, max_epochs=40)
model.fit(X_train, y_train)
print(model.score(X_test, y_test)) # ~0.97
Use it inside a scikit-learn pipeline:
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
pipe = Pipeline([
("scaler", StandardScaler()),
("model", SoftDecisionTree(depth=4, max_epochs=40)),
])
pipe.fit(X_train, y_train)
pipe.score(X_test, y_test)
Interactive playground
app.py is a Streamlit dashboard for comparing the models side by
side on standard and synthetic datasets, with live decision boundaries and
hyperparameter controls:
pip install streamlit plotly
streamlit run app.py
Benchmark
5-fold stratified cross-validation accuracy with StandardScaler preprocessing,
averaged over 5 seeds. Every number comes from
benchmarks/run_benchmarks.py, so the table can
be re-run and checked:
python benchmarks/run_benchmarks.py --seeds 5
| Model | Iris | Wine | Breast Cancer |
|---|---|---|---|
| Soft Decision Tree (depth=4) | 0.900 | 0.979 | 0.976 |
| Multivariate Tree (depth=3) | 0.973 | 0.989 | 0.952 |
| CART (sklearn) | 0.943 | 0.917 | 0.920 |
| Random Forest | 0.945 | 0.980 | 0.960 |
| SVM (RBF) | 0.959 | 0.984 | 0.978 |
On Wine and Breast Cancer the soft tree closes most of the gap between CART and
kernel or ensemble methods while staying differentiable. On Iris it does not:
150 samples over 3 classes is too little data for a depth-4 tree with 15 gates
trained for 40 epochs, and a single oblique split does better. growth="incremental"
lets the tree choose its own depth against a validation split rather than being
given one. That is the
honest shape of the trade-off, and it is why the comparison scripts in
examples/ use a hypothesis test rather than a single accuracy
number.
Algorithms
These implementations start from the published algorithms below and depart from them where this library makes its own design choices. Where an implementation deviates deliberately, the module docstring says so. Treat the references as the lineage of an idea, not as a claim of exact reproduction.
| Algorithm | Reference |
|---|---|
| Soft Decision Trees | İrsoy, Yıldız, Alpaydın (ICPR 2012) |
| Hard export of a soft tree | to_hard_tree(), this library |
| Multivariate Decision Trees | Alpaydın & Çetin (1995), Yıldız & Alpaydın (IEEE TNN 2001) |
| Omnivariate Decision Trees | Yıldız & Alpaydın (IEEE TNN 2001) |
| Hierarchical Mixture of Experts with subtree dropout | İrsoy & Alpaydın (Neurocomputing 2021) |
| GAL: Grow and Learn Networks | Alpaydın (IJPRAI 1994) |
| Combined 5x2cv F Test | Alpaydın (Neural Computation 1999) |
| McNemar's Test, Paired t-test | Standard references |
| Naive Bayes, Weighted KNN | Textbook chapters 3 to 8 |
Use Cases
Research. Reproduce or extend results from the original papers with a clean, tested codebase.
Statistical model comparison. Compare classifiers with proper hypothesis tests instead of ad hoc accuracy diffs:
from neural_trees import combined_5x2cv_f_test
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_breast_cancer
X, y = load_breast_cancer(return_X_y=True)
result = combined_5x2cv_f_test(
DecisionTreeClassifier(),
SVC(kernel="rbf"),
X, y,
)
print(result)
Education. A working reference for soft splits and mixtures of experts beyond textbook diagrams.
Why Soft Decision Trees
Standard decision trees use hard splits, which makes them non-differentiable and unstable to small input changes. Soft Decision Trees replace each split with a sigmoid gate, which means:
- The tree is fully differentiable and trains with gradient descent
- Predictions are smooth, not piecewise constant
- Performance often lands between CART and ensemble methods
- The tree stays interpretable, you can still read off split decisions
Examples
Runnable scripts in examples/:
| Script | What it shows |
|---|---|
01_iris_classification.py |
Minimal train/test loop on Iris |
02_pipeline_with_scaler.py |
StandardScaler + SoftDecisionTree in a Pipeline, 5-fold CV |
03_classifier_comparison.py |
Combined 5x2cv F test against CART |
04_decision_boundary.py |
Decision boundary plot on make_moons |
python examples/01_iris_classification.py
Notebooks
01_soft_decision_trees.ipynb: training, decision boundary visualization, comparison with CART02_classifier_comparison_tests.ipynb: when to use which statistical test03_multivariate_decision_trees.ipynb: oblique splits against CART and soft trees
Citation
If you use this library in academic work, please cite the original papers:
@inproceedings{irsoy2012soft,
title = {Soft Decision Trees},
author = {\.{I}rsoy, O{\u{g}}uzhan and Y{\i}ld{\i}z, Olcay Taner and Alpayd{\i}n, Ethem},
booktitle = {ICPR},
year = {2012}
}
@article{alpaydin1999combined,
title = {Combined 5x2cv {F} Test for Comparing Supervised Classification Learning Algorithms},
author = {Alpayd{\i}n, Ethem},
journal = {Neural Computation},
volume = {11},
number = {8},
pages = {1885--1892},
year = {1999}
}
To cite this implementation:
@software{temel_neural_trees,
author = {Temel, Cagri},
title = {neural-trees: scikit-learn compatible Soft Decision Trees and Mixture of Experts},
year = {2026},
url = {https://github.com/cgrtml/neural-trees}
}
Limitations
neural-trees is not the right tool for every problem:
- Very high-dimensional data. Every internal node holds a dense weight
vector, so parameter count grows as
2^depth x n_features. Beyond a few thousand features, reduce dimensionality first or use a linear model. - Streaming or online learning. Training is batch only; there is no
partial_fit. Refit from scratch when new data arrives. - Sub-millisecond inference. The PyTorch backend adds per-call overhead.
SoftDecisionTree.to_hard_tree()exports the learned gates as a plain numpy model that predicts about 5x faster and prints its rules, at the cost of reading each gate as a hard decision rather than a soft one. - Very large sample counts. Training is full-batch gradient descent over epochs, not an optimized tree-growing routine like CART. Millions of rows will be slow on CPU.
- Categorical features. There is no built-in encoding; sigmoid gates
expect continuous, scaled inputs. Encode and scale in a
Pipeline.
Changelog
See CHANGELOG.md. Versions 0.2.0 and 0.3.0 fixed four models that did not work in 0.1.x, so upgrade if you are on an earlier release. Every classifier passes scikit-learn's estimator checks as of 0.4.0.
Contributing
Contributions are welcome. New to open source? See CONTRIBUTING.md for a beginner-friendly walkthrough.
Every open issue states what would close it, so you can judge the size before you start.
First contribution, no deep ML background needed, tagged
good first issue:
write a test file for one of the pure-numpy estimators, wire ruff into CI,
move packaging to pyproject.toml, add a coverage threshold, or run the
notebooks in CI so their committed outputs cannot go stale.
If you know scikit-learn and PyTorch: sample_weight and class_weight
support, or vectorizing the mixture-of-experts gating tree the way
SoftDecisionTree already is (that one has a worked reference implementation
in the repo to copy).
If you want a research problem: incremental tree growing from İrsoy, Yıldız and Alpaydın (ICPR 2012), which the fixed-depth implementation here does not do, or distilling a trained soft tree into a hard one for readable rules and fast inference.
For larger changes, open an issue first to discuss the approach. If this project is useful to you, a star helps others find it.
Contributors
Thanks to everyone who has improved this library.
| Contributor | Contribution |
|---|---|
| @snoopuppy582 | Symmetric McNemar disagreement test (#20), development requirements (#23), depth validation (#22), reproducibility test (#24) |
| @aribaskagan | Fixed the coverage target in CI, which had been measuring a module that no longer exists (#25) |
| @yunaremaia | Migrated packaging to pyproject.toml and wired ruff into CI (#46) |
The list began with the GitHub Sprint segment of the WSU Data and Analytics Breakout (May 15, 2026) and stays open to anyone. Full history: the contributors graph.
License
MIT. See LICENSE.
Metadata
Release files for neural-trees 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| neural_trees-0.5.0.tar.gz | 62.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| neural_trees-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 110.3 kB
Release files / neural_trees-0.5.0.tar.gz
| Download URL | neural_trees-0.5.0.tar.gz |
|---|---|
| Size | 62.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
babab866bb636d5e433cb9578e19e35048bdfdac28f4d8fbbfd89f03fd2da3bc
|
|
BLAKE2b-256 checksum How to use checksums |
cef1b111811f70d1ddf060c8e10606e4ee782eefb85ad5c6336ee13e6a380603
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.7
|
Release files / neural_trees-0.5.0-py3-none-any.whl
| Download URL | neural_trees-0.5.0-py3-none-any.whl |
|---|---|
| Size | 48.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
aa9f5fc4cb2d87afe2e484020e1218a8adf4e04eb2cc91caecece83619443fe7
|
|
BLAKE2b-256 checksum How to use checksums |
031aae15389e15a922938167dd27d92e32ef7adc9b89b704d09ab3d01b60fb1e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.7
|