diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml
index f3bec35..51e9c2c 100644
--- a/.github/workflows/workflow.yml
+++ b/.github/workflows/workflow.yml
@@ -62,7 +62,9 @@ jobs:
uses: ./.github/actions/setup-uv-env
with:
python-version: ${{ matrix.python-version }}
- install-args: "--extra rna --extra report --extra tabpfn --extra tabicl --extra clustering --group test_duration"
+ install-args: >-
+ --extra rna --extra report --extra tabpfn --extra tabicl
+ --extra node --extra clustering --group test_duration
- name: Cache HuggingFace and Torch models ποΈ
uses: actions/cache@v4
@@ -116,7 +118,9 @@ jobs:
uses: ./.github/actions/setup-uv-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- install-args: "--extra rna --extra report --extra tabpfn --extra tabicl --extra clustering --group test_duration"
+ install-args: >-
+ --extra rna --extra report --extra tabpfn --extra tabicl
+ --extra node --extra clustering --group test_duration
- name: Cache HuggingFace and Torch models ποΈ
uses: actions/cache@v4
diff --git a/examples/notebooks/04_feature_engineering/04_chemeleon_fingerprints.ipynb b/examples/notebooks/04_feature_engineering/04_chemeleon_fingerprints.ipynb
new file mode 100644
index 0000000..d1e4e12
--- /dev/null
+++ b/examples/notebooks/04_feature_engineering/04_chemeleon_fingerprints.ipynb
@@ -0,0 +1,97 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "7ab185ba",
+ "metadata": {},
+ "source": [
+ "# CheMeleon GNN Fingerprints\n",
+ "\n",
+ "This notebook demonstrates how to generate CheMeleon molecular embeddings from SMILES using Mother."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "2457164a",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "from rdkit import Chem\n",
+ "\n",
+ "from mother.feature_generation.fp_gnn_gen import CheMeleonFingerprintFactory"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "17278c7c",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Embedding matrix shape: (3, 2048)\n",
+ "Dtype: float32\n",
+ "First row (first 8 values): [0. 0. 0. 0. 0. 0. 0. 0.]\n"
+ ]
+ }
+ ],
+ "source": [
+ "smiles = [\"CCO\", \"c1ccccc1\", \"CC(=O)O\"]\n",
+ "mols = [Chem.MolFromSmiles(s) for s in smiles]\n",
+ "mols_array = np.array(mols, dtype=object)\n",
+ "\n",
+ "checkpoint_path = \"path/to/chemeleon/checkpoint.pt\" # Replace with your local chemprop checkpoint path.\n",
+ "factory = CheMeleonFingerprintFactory(\n",
+ " output_dim=2048,\n",
+ " batch_size=128,\n",
+ " checkpoint_path=checkpoint_path,\n",
+ " device=\"cpu\",\n",
+ ")\n",
+ "transformer = factory.get_fingerprint_generator()\n",
+ "embeddings = transformer.fit_transform(mols_array)\n",
+ "\n",
+ "print(\"Embedding matrix shape:\", embeddings.shape)\n",
+ "print(\"Dtype:\", embeddings.dtype)\n",
+ "print(\"First row (first 8 values):\", np.round(embeddings[0][:8], 4))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "20d687a9",
+ "metadata": {},
+ "source": [
+ "## Notes\n",
+ "\n",
+ "- Install dependencies before using CheMeleon embeddings (e.g. `pip install 'mother-ml[chemprop]'` or `pip install chemprop`).\n",
+ "- If `checkpoint_path=None`, the transformer calls `get_default_chemeleon_checkpoint()` which auto-downloads `chemeleon_mp.pt` from Zenodo on first use and caches it in `~/.cache/mother/`.\n",
+ "- Input to the transformer should be RDKit molecule objects, matching other fingerprint generators.\n",
+ "- Invalid molecules are returned as rows with `NaN` values.\n"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "mother-ml (3.13.14)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.13.14"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/examples/notebooks/05_advanced/05_NODE.ipynb b/examples/notebooks/05_advanced/05_NODE.ipynb
new file mode 100644
index 0000000..452f711
--- /dev/null
+++ b/examples/notebooks/05_advanced/05_NODE.ipynb
@@ -0,0 +1,4621 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "adb55670",
+ "metadata": {
+ "id": "cell-1",
+ "language": "markdown"
+ },
+ "source": [
+ "# NODE: Neural Oblivious Decision Ensembles\n",
+ "\n",
+ "> A guided, chapter-based tour of NODE: from soft decision trees and prediction heads to uncertainty, explanations, and tuning.\n",
+ "\n",
+ "This notebook introduces the public `NODERegressor` and `NODEClassifier` APIs through intuition-first explanations and runnable examples. It covers regression, classification, probabilistic flow heads, uncertainty, embeddings, multitask targets, SHAP explanations, and MotherTuner integration.\n",
+ "\n",
+ "The uncertainty workflow is collected in one place: ordinary MC dropout, flow sampling, combined flow-plus-dropout decomposition, BALD mutual information, and sampled BALSA-EMD disagreement.\n",
+ "\n",
+ "### Architecture at a glance\n",
+ "\n",
+ "```text\n",
+ "X -> feature/categorical embedding -> dense ODST layers -> prediction head -> y or p(y|X)\n",
+ " +-- subset\n",
+ " +-- linear\n",
+ " +-- MLP\n",
+ " +-- flow (regression)\n",
+ "```\n",
+ "\n",
+ "NODE is a differentiable ensemble of **oblivious decision trees**. Each ODST layer makes soft routing decisions; dense connections pass the original features and earlier tree outputs to later layers. A head converts the final tree representation into point predictions or a predictive distribution.\n",
+ "\n",
+ "### Contents\n",
+ "\n",
+ "| Chapter | Focus |\n",
+ "|---|---|\n",
+ "| 01 | Foundations: sparse activations, oblivious trees, dense connections, and hyperparameters |\n",
+ "| 02 | Setup and quick-start regression/classification workflows |\n",
+ "| 03 | Prediction heads and architecture comparisons |\n",
+ "| 04 | Probabilistic regression with normalizing flows |\n",
+ "| 05 | Uncertainty estimation: dropout, flow, BALD, and BALSA-EMD |\n",
+ "| 06 | Learned embeddings and UMAP |\n",
+ "| 07 | Multi-target regression |\n",
+ "| 08 | Class weights and imbalanced data |\n",
+ "| 09 | Multi-label classification |\n",
+ "| 10 | SHAP explanations |\n",
+ "| 11 | Advanced inputs, skorch, devices, and estimator compatibility |\n",
+ "| 12 | Dropout controls and uncertainty implementation details |\n",
+ "| 13 | MotherTuner hyperparameter optimization |\n",
+ "| 14 | Summary and API map |"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7292992b",
+ "metadata": {
+ "language": "markdown"
+ },
+ "source": [
+ "
\n",
+ "\n",
+ "**Plain-language guide: what is NODE?**\n",
+ "\n",
+ "NODE is a collection of many small decision trees trained together by a neural-network optimizer. A traditional tree makes a hard yes/no decision such as \"is mass greater than 200?\" NODE makes that decision softly, for example \"this row is 70% on the left and 30% on the right.\" It combines many such soft decisions, which lets it learn useful rules while staying trainable with the same gradient-based methods used by neural networks.\n",
+ "\n",
+ "The word **ensemble** simply means βa team of models.β **Oblivious** means that every split at the same tree level uses the same rule. These design choices make the model compact and efficient; they do not require you to understand advanced calculus to use it.\n",
+ "\n",
+ "
Understand the sparse activations, oblivious trees, dense connections, and hyperparameters that make NODE work.
\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "91c27e4c",
+ "metadata": {
+ "id": "cell-2",
+ "language": "markdown"
+ },
+ "source": [
+ "---\n",
+ "## 1 Architecture Deep Dive\n",
+ "\n",
+ "### Sparse activations\n",
+ "\n",
+ "NODE uses sparse activation functions to turn feature selection and binning into differentiable operations:\n",
+ "\n",
+ "| Function | Used for | Intuition |\n",
+ "|---|---|---|\n",
+ "| `entmax15` | `choice_function` | Selects a sparse mixture of input features. |\n",
+ "| `sparsemax` | `choice_function` | Alternative sparse feature selector. |\n",
+ "| `entmoid15` | `bin_function` | Produces soft, sparse sigmoid-like split gates. |\n",
+ "| `sparsemoid` | `bin_function` | Alternative split-gating function. |\n",
+ "\n",
+ "The default pair, `entmax15` and `entmoid15`, lets each tree focus on a small subset of features while keeping the forward pass differentiable.\n",
+ "\n",
+ "### Oblivious decision trees\n",
+ "\n",
+ "An oblivious tree uses the same split feature and threshold at every node of a given depth. For depth $d$, it has $2^d$ leaves. Routing is soft rather than hard, so the tree can be trained with gradient descent.\n",
+ "\n",
+ "For sample $x$, tree output can be written as\n",
+ "\n",
+ "$$\n",
+ "h(x) = \\sum_{\\ell=1}^{2^d} \\pi_\\ell(x)\\,w_\\ell,\n",
+ "$$\n",
+ "\n",
+ "where $w_\\ell$ is the learned response at leaf $\\ell$ and $\\pi_\\ell(x)$ is the differentiable probability that $x$ reaches that leaf. The routing probabilities satisfy $\\pi_\\ell(x) \\ge 0$ and $\\sum_\\ell \\pi_\\ell(x)=1$.\n",
+ "\n",
+ "### Dense connections\n",
+ "\n",
+ "A `DenseODSTBlock` stacks tree layers with dense connections:\n",
+ "\n",
+ "```text\n",
+ "Layer 1 input: [X] β hβ\n",
+ "Layer 2 input: [X, hβ] β hβ\n",
+ "Layer 3 input: [X, hβ, hβ] β hβ\n",
+ "```\n",
+ "\n",
+ "This gives later layers access to both the original features and previously learned tree representations."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a2da25ff",
+ "metadata": {},
+ "source": [
+ "
\n",
+ "\n",
+ "## NODE from input to prediction: the complete picture\n",
+ "\n",
+ "NODE is easiest to understand as a **team of soft rule-makers**. Each tree looks at the input, softly chooses which features matter, softly decides which side of each split a row belongs to, and combines the resulting leaf responses. The word *soft* means that a row can partly belong to both sides while the model is learning.\n",
+ "\n",
+ "```text\n",
+ "one data row x\n",
+ " β\n",
+ " βΌ\n",
+ "feature-selection logits for each tree and depth\n",
+ " β\n",
+ " β entmax15 or sparsemax\n",
+ " β \"which input features should this split look at?\"\n",
+ " βΌ\n",
+ "weighted feature value v for each tree/depth\n",
+ " β\n",
+ " β compare v with a learned threshold\n",
+ " β scale by a learned temperature\n",
+ " βΌ\n",
+ "left/right split probabilities\n",
+ " β\n",
+ " β entmoid15 or sparsemoid\n",
+ " β \"how much left and how much right?\"\n",
+ " βΌ\n",
+ "leaf probabilities for the whole tree\n",
+ " β\n",
+ " β multiply the branch probabilities across depths\n",
+ " βΌ\n",
+ "weighted average of learned leaf responses\n",
+ " β\n",
+ " βΌ\n",
+ "one tree output\n",
+ " β\n",
+ " βββ repeat for many trees and NODE layers βββΊ prediction head βββΊ prediction\n",
+ "```\n",
+ "\n",
+ "For a row $x$, tree $t$, and depth $d$, the implementation first creates a weighted feature value:\n",
+ "\n",
+ "$$\n",
+ "v_{t,d}(x) = \\sum_{j=1}^{F} a_{j,t,d}\\,x_j,\n",
+ "$$\n",
+ "\n",
+ "where $x_j$ is input feature $j$ and $a_{j,t,d}$ is the learned feature-selection weight. The weights are produced by `entmax15` or `sparsemax` and satisfy:\n",
+ "\n",
+ "$$\n",
+ "a_{j,t,d} \\ge 0, \\qquad \\sum_{j=1}^{F} a_{j,t,d}=1.\n",
+ "$$\n",
+ "\n",
+ "The tree then compares that weighted value with a learned threshold $\\tau_{t,d}$:\n",
+ "\n",
+ "$$\n",
+ "q_{t,d}(x) = \\bigl(v_{t,d}(x)-\\tau_{t,d}\\bigr)\\exp(-\\log T_{t,d}).\n",
+ "$$\n",
+ "\n",
+ "The positive and negative versions $[-q_{t,d}, q_{t,d}]$ are passed to `entmoid15` or `sparsemoid`, producing two branch probabilities. If depth $d$ sends a row left with probability $b_{t,d,0}$ and right with probability $b_{t,d,1}$, the probability of reaching leaf $\\ell$ is the product of the branch probabilities selected by that leaf:\n",
+ "\n",
+ "$$\n",
+ "\\pi_{t,\\ell}(x) = \\prod_{d=1}^{D} b_{t,d,\\,\\mathrm{branch}(\\ell,d)}.\n",
+ "$$\n",
+ "\n",
+ "Finally, the tree output is:\n",
+ "\n",
+ "$$\n",
+ " h_t(x)=\\sum_{\\ell=1}^{2^D}\\pi_{t,\\ell}(x)w_{t,\\ell},\n",
+ "$$\n",
+ "\n",
+ "where $w_{t,\\ell}$ is the learned response stored at leaf $\\ell$. This is the formula behind the whole tree: every possible leaf contributes according to how much probability the row has of reaching it.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "### The four sparse activations, explained safely\n",
+ "\n",
+ "There are two different jobs here. Do not mix them up:\n",
+ "\n",
+ "```text\n",
+ "FEATURE SELECTION TREE SPLIT\n",
+ "entmax15 / sparsemax entmoid15 / sparsemoid\n",
+ "\"which features matter?\" \"how much left versus right?\"\n",
+ "output: many feature weights output: two branch probabilities\n",
+ "sum across features = 1 left + right = 1\n",
+ "```\n",
+ "\n",
+ "### 1. `sparsemax`: a sparse probability selector\n",
+ "\n",
+ "Imagine giving each input feature a score. `sparsemax` converts the scores into non-negative weights that sum to one, but it is willing to assign **exactly zero** to unimportant features.\n",
+ "\n",
+ "```text\n",
+ "feature scores: [ 3.0, 2.0, 0.2, -1.0 ]\n",
+ " β\n",
+ " sparsemax\n",
+ " βΌ\n",
+ "feature weights: [ 0.75, 0.25, 0.00, 0.00 ]\n",
+ " sum = 1.00\n",
+ "```\n",
+ "\n",
+ "Its mathematical definition is the Euclidean projection onto the probability simplex:\n",
+ "\n",
+ "$$\n",
+ "\\operatorname{sparsemax}(z)\n",
+ "= \\arg\\min_{p}\\frac{1}{2}\\|p-z\\|_2^2\n",
+ "\\quad\\text{subject to}\\quad p_j\\ge0,\\;\\sum_jp_j=1.\n",
+ "$$\n",
+ "\n",
+ "In plain language: find the closest non-negative set of weights that adds up to one. Because weights below the learned threshold are clipped to zero, sparsemax creates hard zeros while remaining piecewise differentiable.\n",
+ "\n",
+ "### 2. `entmax15`: a smoother sparse selector\n",
+ "\n",
+ "`entmax15` has the same job as sparsemax but uses a gentler sparsity rule. It usually keeps a small group of useful features and can give them smoother, more graded weights.\n",
+ "\n",
+ "```text\n",
+ "feature scores: [ 3.0, 2.0, 0.2, -1.0 ]\n",
+ " β\n",
+ " entmax15\n",
+ " βΌ\n",
+ "feature weights: [ 0.62, 0.30, 0.08, 0.00 ]\n",
+ " sum = 1.00\n",
+ "```\n",
+ "\n",
+ "The exact values depend on the scores; the diagram is only an intuition. The implementation solves:\n",
+ "\n",
+ "$$\n",
+ "\\operatorname{entmax}_{1.5}(z)\n",
+ "= \\arg\\max_{p\\in\\Delta}\n",
+ "\\left\\{\\langle z,p\\rangle + H_{1.5}(p)\\right\\},\n",
+ "$$\n",
+ "\n",
+ "where $\\Delta=\\{p:p_j\\ge0,\\sum_jp_j=1\\}$ and $H_{1.5}$ is a Tsallis entropy regularizer. In the code, the resulting supported values are computed as a squared thresholded quantity:\n",
+ "\n",
+ "$$\n",
+ "p_j = \\left[\\frac{z_j}{2}-\\tau\\right]_+^2,\n",
+ "$$\n",
+ "\n",
+ "with the threshold $\\tau$ chosen so that the outputs sum to one. The important practical result is: entmax15 is sparse like sparsemax, but its transition into and out of the active set is smoother.\n",
+ "\n",
+ "### Sparsemax versus entmax15\n",
+ "\n",
+ "| Question | `sparsemax` | `entmax15` |\n",
+ "|---|---|---|\n",
+ "| Job | Select features | Select features |\n",
+ "| Outputs | Non-negative weights summing to 1 | Non-negative weights summing to 1 |\n",
+ "| Exact zeros | Common | Common |\n",
+ "| Shape | Piecewise linear projection | Smoother sparse transformation |\n",
+ "| Practical intuition | A sharper shortlist | A shortlist with softer importance differences |\n",
+ "\n",
+ "Neither function selects one feature globally for the whole model. It selects weights separately for each tree and each depth, so different parts of NODE can focus on different feature combinations.\n",
+ "\n",
+ "### 3. `sparsemoid`: a simple soft left/right gate\n",
+ "\n",
+ "`sparsemoid` is used for the binary decision at a tree split. The code is:\n",
+ "\n",
+ "$$\n",
+ "\\operatorname{sparsemoid}(u)=\\operatorname{clip}\\left(\\frac{u+1}{2},0,1\\right).\n",
+ "$$\n",
+ "\n",
+ "It is a straight-line ramp clipped to the interval $[0,1]$:\n",
+ "\n",
+ "```text\n",
+ "input u -1 0 +1\n",
+ " β β β\n",
+ "sparsemoid 0.0 0.5 1.0\n",
+ " β β β\n",
+ "meaning left half right\n",
+ "```\n",
+ "\n",
+ "NODE passes the symmetric pair $[-q,q]$ into this function. Therefore the two outputs are complementary:\n",
+ "\n",
+ "$$\n",
+ "\\operatorname{sparsemoid}(-q)+\\operatorname{sparsemoid}(q)=1\n",
+ "$$\n",
+ "\n",
+ "whenever the values are inside the unclipped region, and the clipping still keeps the pair at the two valid extremes when $q$ is large.\n",
+ "\n",
+ "### 4. `entmoid15`: a smoother soft left/right gate\n",
+ "\n",
+ "`entmoid15` has the same job as sparsemoid but uses the 1.5-entmax-style nonlinear curve. It gives a smooth, nonlinear transition from βmostly leftβ to βmostly right.β\n",
+ "\n",
+ "```text\n",
+ " right probability\n",
+ " 1.0 | ______\n",
+ " | /\n",
+ " | /\n",
+ " 0.5 |---------β--------- q = 0\n",
+ " | /\n",
+ " | /\n",
+ " 0.0 |______/ left probability = 1 - right\n",
+ " negative q positive q\n",
+ "```\n",
+ "\n",
+ "For the symmetric pair used by NODE:\n",
+ "\n",
+ "$$\n",
+ "\\bigl[b_{left},b_{right}\\bigr]\n",
+ "=\\bigl[\\operatorname{entmoid15}(-q),\n",
+ "\\operatorname{entmoid15}(q)\\bigr],\n",
+ "$$\n",
+ "\n",
+ "and the implementation is designed so:\n",
+ "\n",
+ "$$\n",
+ "b_{left}\\ge0,\\qquad b_{right}\\ge0,\\qquad b_{left}+b_{right}=1.\n",
+ "$$\n",
+ "\n",
+ "The practical difference from sparsemoid is the shape of the transition. `sparsemoid` is a clipped linear ramp; `entmoid15` is a smooth nonlinear gate. Both give the tree a differentiable left/right decision instead of a hard threshold.\n",
+ "\n",
+ "### Putting the four functions together\n",
+ "\n",
+ "```text\n",
+ " learned feature scores\n",
+ " β\n",
+ " βββββββββββββ΄ββββββββββββ\n",
+ " βΌ βΌ\n",
+ " sparsemax entmax15\n",
+ " sharp sparse smooth sparse\n",
+ " feature weights feature weights\n",
+ " βββββββββββββ¬ββββββββββββ\n",
+ " βΌ\n",
+ " weighted feature value\n",
+ " β\n",
+ " threshold + temperature\n",
+ " β\n",
+ " βββββββββββββ΄ββββββββββββ\n",
+ " βΌ βΌ\n",
+ " sparsemoid entmoid15\n",
+ " linear soft gate nonlinear soft gate\n",
+ " βββββββββββββ¬ββββββββββββ\n",
+ " βΌ\n",
+ " left/right probabilities\n",
+ " β\n",
+ " leaf probability products\n",
+ " β\n",
+ " weighted leaf response\n",
+ "```\n",
+ "\n",
+ "The standard NODE choice is `choice_function=\"entmax15\"` with `bin_function=\"entmoid15\"`. To compare alternatives, change one role at a time:\n",
+ "\n",
+ "```python\n",
+ "NODERegressor(choice_function=\"entmax15\", bin_function=\"entmoid15\") # default\n",
+ "NODERegressor(choice_function=\"sparsemax\", bin_function=\"entmoid15\") # sharper selection\n",
+ "NODERegressor(choice_function=\"entmax15\", bin_function=\"sparsemoid\") # linear split gates\n",
+ "NODERegressor(choice_function=\"sparsemax\", bin_function=\"sparsemoid\") # both alternatives\n",
+ "```\n",
+ "\n",
+ "These functions do not make the model a hard decision tree during training. They provide a smooth route for gradients. After training, many feature weights or branch probabilities may be close to zero or one, making the learned behavior easier to interpret as soft rules.\n",
+ "\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b70c38fc",
+ "metadata": {
+ "language": "markdown"
+ },
+ "source": [
+ "
\n",
+ "\n",
+ "**Plain-language guide: why βsoftβ and βdifferentiableβ trees?**\n",
+ "\n",
+ "A normal decision tree sends a row down exactly one path. That hard choice is excellent for making predictions, but it is awkward to improve with gradient descent: a tiny change in a threshold can suddenly move a row to another branch. NODE replaces each hard choice with a smooth score between 0 and 1. During training, the score can gradually change, so the optimizer can tell the model which direction would improve the prediction.\n",
+ "\n",
+ "You can think of this like a dimmer switch instead of a light switch. The final model can still behave like a set of rules, but training has a smooth signal to follow. **Differentiable** here means βsmall changes in the inputs or parameters produce a usable signal for improving the model,β not that you need to calculate derivatives by hand.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "## Hyperparameter field guide\n",
+ "\n",
+ "A **hyperparameter** is a setting chosen before training starts. The model learns its weights from the data, but it does not automatically decide how many trees to build, how long to train, or how much regularisation to use. The sections below explain the settings exposed by `NODERegressor` and `NODEClassifier`.\n",
+ "\n",
+ "A useful rule is: start with the defaults, change one group of settings at a time, and compare models on validation data. More complexity is not automatically better. A larger model can fit richer patterns, but it can also take longer, use more memory, and overfit.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "### 1. Model size: how much can NODE learn?\n",
+ "\n",
+ "| Parameter | Plain-language meaning | Practical effect |\n",
+ "|---|---|---|\n",
+ "| `num_trees` | Number of small trees in the team | More trees give the model more patterns to combine, but increase training time and memory. Try 64β512 for experiments; increase when the data is complex and validation error is still high. |\n",
+ "| `depth` | Number of decisions in each tree | A depth of 4 allows $2^4 = 16$ possible leaf regions. Larger values capture more detailed rules but can overfit. Try 3β6. |\n",
+ "| `num_layers` | Number of tree blocks stacked one after another | More layers allow later blocks to build on earlier representations. Start at 1; try 2 only when a shallow model is underfitting. |\n",
+ "| `additional_tree_output_dim` | Number of values each tree contributes | Larger values make the learned representation wider. This increases capacity and downstream head size. Usually leave at the default. |\n",
+ "| `max_layers_retained` | How many earlier layers a later layer can see | `None` means all earlier layers are available. Limiting this can reduce memory for deep models, but may remove useful context. |\n",
+ "\n",
+ "**How to recognise the problem:** if both training and validation scores are poor, try more trees, a little more depth, or another layer. If training is much better than validation, reduce model size or increase regularisation instead.\n",
+ "\n",
+ "### 2. Prediction head: how are tree outputs turned into predictions?\n",
+ "\n",
+ "| Parameter | Plain-language meaning | When to use it |\n",
+ "|---|---|---|\n",
+ "| `head_type=\"subset\"` | Averages a learned subset of tree outputs | Good default for ordinary regression and classification. Fast and relatively compact. |\n",
+ "| `head_type=\"linear\"` | Uses one linear layer on the tree representation | Useful as a simple, interpretable baseline. |\n",
+ "| `head_type=\"mlp\"` | Adds one or more neural-network layers after the trees | Useful when the relationship between tree outputs and the target is complex. Tune `mlp_hidden_dims`, `mlp_activation`, and `mlp_dropout` together. |\n",
+ "| `head_type=\"flow\"` | Predicts a full probability distribution rather than one value | Use for probabilistic regression, prediction intervals, and sampling. It is available for regression, not ordinary classification. |\n",
+ "| `mlp_hidden_dims` | Width of the MLP head layers, for example `[128, 64, 32]` | Larger layers increase capacity and memory. Shorter or narrower lists are safer for small datasets. Used only with `head_type=\"mlp\"`. |\n",
+ "| `mlp_activation` | Shape of the MLP's non-linearity: `ReLU`, `GELU`, or `LeakyReLU` | `ReLU` is a sensible default; try `GELU` if the MLP head needs a smoother activation. |\n",
+ "\n",
+ "For a flow head, these parameters describe the distribution model:\n",
+ "\n",
+ "| Parameter | Plain-language meaning | Practical effect |\n",
+ "|---|---|---|\n",
+ "| `flow_type` | The kind of distribution-building recipe | Start with `NICE` for speed or `NSF` for a strong quality/speed trade-off. `GMM` is useful when outcomes have several distinct modes. |\n",
+ "| `flow_transforms` | Number of reversible transformations | More transformations make the distribution more flexible but slower. |\n",
+ "| `flow_bins` | Number of pieces used by a spline flow | More bins let `NSF` draw a more detailed curve. Increase only when the target distribution is complicated. |\n",
+ "| `flow_degree` | Flexibility of the polynomial transformation | Used by `BPF`; higher values can model more shape detail at extra cost. |\n",
+ "| `flow_signal` | Hidden signal size inside autoregressive flows | Used by `NAF` and `UNAF`; larger values increase expressiveness and computation. |\n",
+ "| `flow_components` | Number of mixture components | Used by `GMM`; more components can represent more peaks, but can overfit small datasets. |\n",
+ "\n",
+ "
\n",
+ "\n",
+ "**What should I choose?** Use `subset` when you mainly need a point prediction. Use `mlp` when the basic head is not expressive enough. Use `flow` when the spread and shape of possible outcomes matter, such as risk estimates or prediction intervals. A flow head does not magically make predictions accurate; it learns the uncertainty patterns present in the training data.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "### 3. Dropout and regularisation: preventing overconfidence and overfitting\n",
+ "\n",
+ "A dropout value is a probability between 0 and 1. At training time, that fraction of selected signals is temporarily hidden. This makes the model rely less on any single feature, tree, or hidden unit. Dropout is also used by NODE's Monte-Carlo uncertainty estimates: repeated predictions with different masks show how much plausible models disagree.\n",
+ "\n",
+ "| Parameter | What is randomly hidden? | Starting guidance |\n",
+ "|---|---|---|\n",
+ "| `input_dropout` | Input features entering the tree blocks | Small values such as 0.05β0.2. Especially useful for MC-dropout uncertainty. |\n",
+ "| `input_dropout_only_input` | Whether input dropout happens only at the first input | `False` applies it throughout the dense tree blocks; `True` is a milder, more targeted choice. |\n",
+ "| `tree_dropout` | Whole tree outputs before the prediction head | Small values such as 0.02β0.1. Helps avoid relying on one tree. |\n",
+ "| `tree_dropout_only_head` | Whether tree dropout is applied only immediately before the head | `True` is the conservative default; set `False` only when deeper regularisation is needed. |\n",
+ "| `mlp_dropout` | Hidden units inside an MLP head | Usually 0.0β0.3. Relevant only when `head_type=\"mlp\"`. |\n",
+ "| `embedding_dropout` | Dimensions of learned categorical embeddings | Useful when there are many categorical columns or categories. Leave at 0 for purely numeric data. |\n",
+ "\n",
+ "Do not interpret a larger dropout rate as automatically βmore accurate uncertainty.β It creates more variation by design. Check calibration and validation performance as well as the size of the uncertainty bands.\n",
+ "\n",
+ "### 4. Training settings: how the model learns\n",
+ "\n",
+ "| Parameter | Plain-language meaning | Practical effect |\n",
+ "|---|---|---|\n",
+ "| `max_epochs` | Maximum number of full passes through the training data | More epochs give the optimizer more chances to improve. Increase if validation loss is still falling; reduce for quick experiments. |\n",
+ "| `lr` | Learning rate: how large each weight update is | Too high can make training unstable; too low can make it painfully slow. Try `1e-3` to `1e-2`, starting from the default. |\n",
+ "| `batch_size` | Number of rows processed before one update | Larger batches use more memory and give steadier updates. Smaller batches can help generalisation but are noisier. Try 32, 64, 128, or 256. |\n",
+ "| `batch_size_tuning_upper_bound` | Largest batch size considered by automatic tuning | Set this to `None` to keep `batch_size` fixed. If set, tuning tests doubled batch sizes up to this bound. |\n",
+ "| `optimizer` | Rule used to update the weights | `torch.optim.Adam` is the practical default. Change it only when you have a reason to test another optimizer. |\n",
+ "| `criterion` | Error measure used for ordinary supervised training | `nn.MSELoss` is the default regression loss. Flow heads use their own negative log-likelihood internally. Classification uses the appropriate classification loss. |\n",
+ "| `device` | Where computation runs: `\"cpu\"` or `\"cuda\"` | Use `\"cuda\"` when a compatible GPU is available; use `\"cpu\"` for small examples or reproducibility across machines. |\n",
+ "| `iterator_train__shuffle` | Whether training rows are shuffled each epoch | Keep `True` unless row order has a deliberate meaning. |\n",
+ "| `train_split` | Validation data made available during training | A validation split enables monitoring and automatic early stopping. Use it when tuning or when you want protection against unnecessary training. |\n",
+ "| `callbacks` | Extra skorch training tools | Add callbacks for early stopping, learning-rate schedules, logging, or checkpointing. NODE already adds shape/loss callbacks and early stopping when a validation split is active. |\n",
+ "\n",
+ "**Learning-rate intuition:** `lr` is like the step size while walking downhill toward lower error. Huge steps may jump over the valley; tiny steps eventually work but take a long time. `max_epochs` controls how long you keep walking.\n",
+ "\n",
+ "### 5. Feature handling and task settings\n",
+ "\n",
+ "| Parameter | Meaning |\n",
+ "|---|---|\n",
+ "| `cat_features` | Names of DataFrame columns that should be treated as categorical. NODE does not infer this automatically. Declare string or category columns here. |\n",
+ "| `target_type` | `\"single_target\"` for one output or `\"multi_target\"` for several outputs. |\n",
+ "| `task_weights` | Relative importance of targets in multi-target regression. Increase a target's weight when errors on it matter more. |\n",
+ "| `model_type` | Internal task label. Keep `\"regression\"` for `NODERegressor` and `\"classification\"` for `NODEClassifier`. |\n",
+ "\n",
+ "### 6. Advanced architecture settings: usually leave these alone\n",
+ "\n",
+ "| Parameter | Meaning |\n",
+ "|---|---|\n",
+ "| `choice_function` | Sparse feature selector, usually `entmax15` or `sparsemax` | Controls how strongly each tree focuses on a subset of features. |\n",
+ "| `bin_function` | Soft split gate, usually `entmoid15` or `sparsemoid` | Controls how a feature value is converted into soft left/right routing. |\n",
+ "| `initialize_response` | Initial distribution of tree responses: `normal` or `uniform` | Changes the starting point, not the final model family. |\n",
+ "| `initialize_selection_logits` | Initial distribution of feature-selection scores: `uniform` or `normal` | Mostly useful for controlled experiments. |\n",
+ "| `threshold_init_beta` | Shape parameter for initial split thresholds | Affects where thresholds start before learning. |\n",
+ "| `threshold_init_cutoff` | Range cutoff for initial thresholds | Affects the initial threshold range. |\n",
+ "| `batch_norm_continuous_input` | Whether continuous inputs are batch-normalised | Can help when numeric feature scales differ substantially; otherwise preprocessing is usually clearer. |\n",
+ "\n",
+ "### A practical tuning recipe\n",
+ "\n",
+ "1. Start with `num_trees=128`, `depth=4`, `num_layers=1`, the default head, `lr=0.005`, and a modest `max_epochs`.\n",
+ "2. Use a validation split and choose a metric that matches the task.\n",
+ "3. Tune model size first: compare `num_trees` and `depth` while keeping everything else fixed.\n",
+ "4. Then compare heads: `subset` for a baseline, `mlp` for extra point-prediction flexibility, and `flow` for probabilistic regression.\n",
+ "5. Tune `lr` and `batch_size` next. Only after that tune dropout and flow-specific settings.\n",
+ "6. Stop increasing complexity when validation performance stops improving. For uncertainty work, also check whether the intervals are calibrated, not just whether they are wide.\n",
+ "\n",
+ "The most important search is usually a small search over `num_trees`, `depth`, `lr`, `batch_size`, and `head_type`. The advanced initialisation parameters should not be included in a general-purpose search unless you are investigating the NODE architecture itself."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a96b83e4",
+ "metadata": {
+ "id": "cell-3",
+ "language": "markdown"
+ },
+ "source": [
+ "## Setup"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "id": "301ac586",
+ "metadata": {
+ "id": "cell-4",
+ "language": "markdown"
+ },
+ "outputs": [],
+ "source": [
+ "import warnings\n",
+ "\n",
+ "warnings.filterwarnings(\"ignore\")\n",
+ "\n",
+ "import inspect\n",
+ "\n",
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "from sklearn.datasets import make_regression, make_classification\n",
+ "from sklearn.model_selection import train_test_split\n",
+ "from sklearn.preprocessing import StandardScaler\n",
+ "from sklearn.metrics import r2_score, root_mean_squared_error, accuracy_score\n",
+ "\n",
+ "# Unified NODE APIs\n",
+ "from mother.ml.models.m_node import NODERegressor, NODEClassifier\n",
+ "\n",
+ "\n",
+ "RANDOM_STATE = 42\n",
+ "np.random.seed(RANDOM_STATE)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6d1ea9cd",
+ "metadata": {},
+ "source": [
+ "
\n",
+ "
Chapter 02
\n",
+ "
Setup and Quick Start
\n",
+ "
Load the public NODE APIs, create the example data, and train the first regression and classification models.
\n",
+ "\n",
+ "**Plain-language guide: what is a normalizing flow?**\n",
+ "\n",
+ "A normalizing flow is a flexible way to describe uncertainty. Imagine starting with a simple bell-shaped pile of sand and repeatedly stretching, squeezing, or bending it until it matches the range of outcomes seen in the data. Because every transformation is reversible, the model can both generate plausible outcomes and calculate how likely a particular outcome is.\n",
+ "\n",
+ "For this notebook, the practical difference is simple: a standard regression head returns one best guess, while a flow head returns a whole distribution of plausible guesses. You can sample from that distribution to ask not only βwhat is the prediction?β but also βhow much could it vary?β\n",
+ "\n",
+ "
\n",
+ "\n",
+ "### Flow types and tuning them without the jargon\n",
+ "\n",
+ "A flow head is the part of NODE that turns a tree representation into a **whole distribution of possible target values**. The different flow types are different recipes for bending a simple distribution into the shape required by the data. They all answer the same practical question: βgiven this molecule or row, which outcomes are plausible, and how likely are they?β\n",
+ "\n",
+ "| Flow type | Easy description | Useful when | Main tuning parameters |\n",
+ "|---|---|---|---|\n",
+ "| `NICE` | Makes simple additive shifts between groups of values | You want a fast, stable baseline | `flow_transforms` |\n",
+ "| `NSF` | Uses flexible learned splines, like adjustable curves | Strong general-purpose choice for smooth or irregular distributions | `flow_transforms`, `flow_bins` |\n",
+ "| `RealNVP` | Uses reversible affine stretch-and-shift operations | You want a flexible, widely used coupling-flow design | `flow_transforms` |\n",
+ "| `NAF` | Uses an expressive autoregressive transformation, where dimensions are handled in sequence | Very complex conditional distributions | `flow_transforms`, `flow_signal` |\n",
+ "| `UNAF` | An unconstrained version of an autoregressive flow | You need additional flexibility and can afford more tuning | `flow_transforms`, `flow_signal` |\n",
+ "| `BPF` | Uses monotonic Bernstein polynomials to reshape the distribution | You want a smooth monotonic transformation | `flow_degree` |\n",
+ "| `GMM` | Represents the output as several Gaussian βhumpsβ | Outcomes have multiple distinct peaks or regimes | `flow_components` |\n",
+ "\n",
+ "```text\n",
+ " simple base distribution\n",
+ " β\n",
+ " βΌ\n",
+ " flow transformation recipe\n",
+ " ββββββββββ¬βββββββββ¬βββββββββ¬βββββββββ\n",
+ " β NICE β NSF β NAF β GMM β ...\n",
+ " β shifts β splinesβ autoregβ humps β\n",
+ " ββββββββββ΄βββββββββ΄βββββββββ΄βββββββββ\n",
+ " β\n",
+ " βΌ\n",
+ " conditional distribution p(y | X)\n",
+ "```\n",
+ "\n",
+ "#### What each tuning parameter controls\n",
+ "\n",
+ "| Parameter | Plain-language meaning | What happens when it increases |\n",
+ "|---|---|---|\n",
+ "| `flow_transforms` | Number of reversible transformation blocks | More blocks can model more complicated shapes, but increase computation and can overfit. |\n",
+ "| `flow_bins` | Number of pieces in an `NSF` spline | More pieces make the curve more detailed. Too many can make training slower or unstable on small datasets. |\n",
+ "| `flow_degree` | Polynomial degree in `BPF` | Higher degree allows a more detailed monotonic curve, at extra cost. |\n",
+ "| `flow_signal` | Hidden width used inside `NAF`/`UNAF` | More width gives the autoregressive network more expressive power and uses more memory. |\n",
+ "| `flow_components` | Number of Gaussian components in `GMM` | More components can represent more peaks, but can invent unnecessary peaks on small datasets. |\n",
+ "\n",
+ "A sensible search order is:\n",
+ "\n",
+ "```text\n",
+ "1. Start with flow_type=\"NICE\" or flow_type=\"NSF\"\n",
+ "2. Tune flow_transforms (overall flow depth)\n",
+ "3. If NSF: tune flow_bins (curve detail)\n",
+ "4. If NAF/UNAF: tune flow_signal (hidden flexibility)\n",
+ "5. If BPF: tune flow_degree (polynomial detail)\n",
+ "6. If GMM: tune flow_components (number of peaks)\n",
+ "```\n",
+ "\n",
+ "Start with `NSF` when you want a strong general-purpose flow, `NICE` when speed and stability matter most, and `GMM` when a histogram suggests several separate outcome groups. Do not tune parameters belonging to another flow type: for example, `flow_bins` has no useful effect for `GMM`.\n",
+ "\n",
+ "#### Batching for wide one-layer NODE models\n",
+ "\n",
+ "A model with `num_layers=1` and many trees is **wide and shallow**:\n",
+ "\n",
+ "```text\n",
+ " wide, shallow NODE\n",
+ " X βββΊ [many trees in one ODST layer] βββΊ head βββΊ prediction\n",
+ " T1 T2 T3 ... T2048\n",
+ "```\n",
+ "\n",
+ "A model with several layers is **deeper and sequential**:\n",
+ "\n",
+ "```text\n",
+ " deeper NODE\n",
+ " X βββΊ layer 1 βββΊ layer 2 βββΊ layer 3 βββΊ head\n",
+ " h1 h2 h3\n",
+ "```\n",
+ "\n",
+ "The one-layer design is useful when you want a large ensemble of relatively independent tree decisions without repeatedly concatenating intermediate layers. It can be fast and expressive, but the representation for each row can still be wide. `batch_size` controls how many rows are carried through that wide representation at once.\n",
+ "\n",
+ "```text\n",
+ "Full training data: [row 1, row 2, row 3, ... row N]\n",
+ " β\n",
+ " split rows into minibatches\n",
+ " β\n",
+ " βββββββββββββββββββββββΌββββββββββββββββββββββ\n",
+ " βΌ βΌ βΌ\n",
+ " [rows 1..B] [rows B+1..2B] [...]\n",
+ " β β β\n",
+ " many trees Γ B rows many trees Γ B rows many trees Γ B rows\n",
+ " β β β\n",
+ " loss + gradients loss + gradients loss + gradients\n",
+ " ββββββββββββββββ optimizer updates ββββββββββ\n",
+ "```\n",
+ "\n",
+ "A common misconception: batching does **not** mean that one minibatch sees only some of the trees. Every row in a minibatch passes through the full one-layer tree ensemble. Batching only limits how many rows and their intermediate activations must be resident in memory at the same time.\n",
+ "\n",
+ "A rough memory intuition is:\n",
+ "\n",
+ "$$\n",
+ "\\text{activation memory} \\propto\n",
+ "\\text{batch size} \\times \\text{number of trees}\n",
+ "\\times \\text{tree output width}.\n",
+ "$$\n",
+ "\n",
+ "So for a wide model, reducing `batch_size` is often the first memory adjustment. Reducing `num_trees` also reduces memory, but changes the model capacity. Increasing `batch_size` can improve hardware throughput when memory allows, but does not make the model wider or more expressive by itself.\n",
+ "\n",
+ "| Situation | What to try |\n",
+ "|---|---|\n",
+ "| Out-of-memory error with many trees | Reduce `batch_size` first, for example 128 β 64 β 32. |\n",
+ "| Training is stable but GPU/CPU is underused | Increase `batch_size` gradually until throughput improves or memory becomes tight. |\n",
+ "| One-layer model underfits | Increase `num_trees` or `additional_tree_output_dim`, then retune `batch_size` if needed. |\n",
+ "| Deep model uses too much memory from skip connections | Try `max_layers_retained=1` or another small value; this changes connectivity, not just row batching. |\n",
+ "| Need automatic batch-size search | Set `batch_size_tuning_upper_bound`; tuning tests the starting batch size and doubled values up to the bound. |\n",
+ "\n",
+ "For a one-layer wide model, `max_layers_retained` has no practical effect because there are no earlier NODE layers to retain. It becomes relevant only when `num_layers > 1`, where it limits how many previous layer outputs are included in later layers.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "### Target normalization for flow heads: what, why, and how\n",
+ "\n",
+ "The flow head learns a probability distribution for the target, not just a single best value. It trains by assigning high probability to the observed target values. That makes the numerical scale of the target especially important.\n",
+ "\n",
+ "Suppose the original target is $y$ and we transform it with a training-set mean $\\mu$ and standard deviation $\\sigma$:\n",
+ "\n",
+ "$$\n",
+ "z = \\frac{y - \\mu}{\\sigma}.\n",
+ "$$\n",
+ "\n",
+ "After this transformation, most training targets are roughly centred around 0 and have a spread of roughly 1. The flow therefore learns a distribution in the stable $z$-space rather than having to learn values such as 0.0003, 2500, and 1,000,000 on the same numerical scale.\n",
+ "\n",
+ "#### Why flows benefit especially from scaling\n",
+ "\n",
+ "A flow head uses a log-likelihood loss. In simplified form, it tries to make $\\log p(z \\mid x)$ large for the observed target. If target values have a very large scale, the flow must learn very large distribution widths and shifts. This can cause:\n",
+ "\n",
+ "- large or badly balanced gradients;\n",
+ "- poor initial flow parameters and slow convergence;\n",
+ "- numerical instability in spline, affine, or mixture transformations;\n",
+ "- a distribution that fits the centre but gives badly calibrated tails;\n",
+ "- difficulty comparing uncertainty values across targets with different units.\n",
+ "\n",
+ "Scaling does not remove the scientific meaning of the target. It only gives the optimizer a convenient coordinate system.\n",
+ "\n",
+ "```text\n",
+ "original target space normalized training space\n",
+ " y = 12, 15, 18, 21, 24 z = -1.26, -0.63, 0.00, 0.63, 1.26\n",
+ " β β\n",
+ " βββββββ transform using train ΞΌ, Ο ββββ\n",
+ "\n",
+ "flow learns p(z | X), then predictions are transformed back to y-space\n",
+ "```\n",
+ "\n",
+ "#### The density-unit detail\n",
+ "\n",
+ "A density changes when the unit of measurement changes. If $y = \\mu + \\sigma z$, then:\n",
+ "\n",
+ "$$\n",
+ "p_y(y \\mid x) = \\frac{1}{\\sigma}p_z\\left(\\frac{y-\\mu}{\\sigma}\\mid x\\right),\n",
+ "$$\n",
+ "\n",
+ "so:\n",
+ "\n",
+ "$$\n",
+ "\\log p_y(y \\mid x) = \\log p_z(z \\mid x) - \\log \\sigma.\n",
+ "$$\n",
+ "\n",
+ "The $-\\log\\sigma$ term is the change-of-units correction. During training, it is constant with respect to the model parameters, so training in standardized space is valid. It becomes important when reporting a calibrated likelihood in the original units. Entropy also changes with units: $H(Y)=H(Z)+\\log|\\sigma|$. Therefore, entropy values in standardized target space and original target space should not be compared as if they had the same units.\n",
+ "\n",
+ "#### Correct workflow\n",
+ "\n",
+ "Fit every data-dependent transform on the training targets only. Do not calculate the mean or standard deviation using validation or test targets.\n",
+ "\n",
+ "```python\n",
+ "from sklearn.preprocessing import StandardScaler\n",
+ "\n",
+ "# Fit only on the training target.\n",
+ "y_scaler = StandardScaler()\n",
+ "y_train_z = y_scaler.fit_transform(y_train.reshape(-1, 1)).ravel().astype(\"float32\")\n",
+ "y_test_z = y_scaler.transform(y_test.reshape(-1, 1)).ravel().astype(\"float32\")\n",
+ "\n",
+ "flow_model = NODERegressor(\n",
+ " head_type=\"flow\",\n",
+ " flow_type=\"NSF\",\n",
+ " max_epochs=100,\n",
+ " lr=0.005,\n",
+ " device=\"cpu\",\n",
+ ")\n",
+ "flow_model.fit(X_train, y_train_z)\n",
+ "\n",
+ "# Point predictions are still in standardized units, so invert them.\n",
+ "pred_z = flow_model.predict(X_test)\n",
+ "pred_y = y_scaler.inverse_transform(pred_z.reshape(-1, 1)).ravel()\n",
+ "\n",
+ "# Flow samples and quantiles must also be inverse-transformed.\n",
+ "samples_z = flow_model.predict(\n",
+ " X_test[:5],\n",
+ " num_samples=500,\n",
+ " return_sample_distribution=True,\n",
+ ")\n",
+ "samples_y = y_scaler.inverse_transform(\n",
+ " samples_z.reshape(-1, 1)\n",
+ ").reshape(samples_z.shape)\n",
+ "```\n",
+ "\n",
+ "The same rule applies to `predict_quantiles()` and uncertainty intervals: calculate them in the space used for training, then transform the interval endpoints or samples back to the original target units before presenting them. Do not inverse-transform standard deviations by applying the scaler to a single value; for a positive target scale, multiply a standard-deviation-like quantity by $\\sigma$. Entropy requires the separate $+\\log|\\sigma|$ correction above.\n",
+ "\n",
+ "For positive, right-skewed targets, try a two-step transform: $y' = \\log(1+y)$, then standardization. Reverse the two steps in the opposite order after prediction. Use this only when it matches the scientific meaning of the target; it is not a default choice.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "
\n",
+ "\n",
+ "### Input scaling: NODE compared with a standard MLP\n",
+ "\n",
+ "NODE and an ordinary MLP both benefit from sensible feature scales, but for different reasons.\n",
+ "\n",
+ "| Model | How input scale affects it | Practical consequence |\n",
+ "|---|---|---|\n",
+ "| Standard MLP | Inputs are multiplied by learned weights and passed through activations. A feature measured in millions can dominate early updates, saturate activations, and create badly conditioned gradients. | Standardize continuous features unless there is a strong reason not to. This is usually important for stable, efficient training. |\n",
+ "| NODE | Soft trees compare features with learned thresholds and temperatures. A feature's numeric range affects where thresholds are initialized and how sharp the routing becomes. Sparse selection is also influenced by relative scales. | NODE is often more tolerant than an MLP, but continuous features should still usually be standardized or put on comparable ranges. |\n",
+ "| NODE with categorical columns | Categorical columns are label-encoded and passed through learned embeddings. Their integer codes are categories, not measurements. | Do not standardize categorical codes as continuous numbers; declare them with `cat_features`. |\n",
+ "\n",
+ "```text\n",
+ "standard MLP NODE\n",
+ "X -> scale -> linear -> activation X -> scale -> learned thresholds/soft gates\n",
+ " β β\n",
+ "large feature values can cause scale changes threshold locations\n",
+ "large updates or saturation and routing sharpness\n",
+ " β β\n",
+ " βββββ both benefit from comparable continuous feature scales βββββ\n",
+ "```\n",
+ "\n",
+ "#### What goes wrong when continuous inputs are badly scaled?\n",
+ "\n",
+ "Imagine one descriptor ranges from 0 to 1 and another from 0 to 1,000,000. In an MLP, the large-valued feature can produce much larger first-layer pre-activations before the network has learned compensating weights. In NODE, the large-valued feature can receive thresholds and temperature behavior on a very different numerical scale; its routing may initially be too broad, too sharp, or poorly placed relative to the other features. The model can sometimes learn around this, but it wastes optimization steps doing so.\n",
+ "\n",
+ "Scaling is therefore a conditioning aid, not a guarantee of better accuracy. It does not make a feature more important and does not remove nonlinear relationships.\n",
+ "\n",
+ "#### Recommended mixed-feature workflow\n",
+ "\n",
+ "1. Split train and test data before fitting any scaler.\n",
+ "2. Keep binary Morgan bits as 0/1 numeric features; scaling them is optional, but do it consistently if mixing them with other continuous features.\n",
+ "3. Standardize continuous physicochemical descriptors using training rows only.\n",
+ "4. Keep dense CheMeleon embeddings on a consistent numeric scale; standardization is often useful when concatenating them with descriptors.\n",
+ "5. Declare genuine categorical columns through `cat_features`; do not treat their label codes as ordered measurements.\n",
+ "6. Apply exactly the same fitted feature transform to validation and test rows.\n",
+ "7. Compare scaled and unscaled baselines using the same split and random seeds rather than assuming either model is always superior.\n",
+ "\n",
+ "```python\n",
+ "from sklearn.compose import ColumnTransformer\n",
+ "from sklearn.preprocessing import StandardScaler\n",
+ "\n",
+ "continuous_columns = [\"molecular_weight\", \"logP\", \"tpsa\"]\n",
+ "feature_scaler = ColumnTransformer(\n",
+ " [(\"continuous\", StandardScaler(), continuous_columns)],\n",
+ " remainder=\"passthrough\",\n",
+ ")\n",
+ "\n",
+ "X_train_scaled = feature_scaler.fit_transform(X_train)\n",
+ "X_test_scaled = feature_scaler.transform(X_test)\n",
+ "\n",
+ "# If categorical columns are retained, pass them through a representation\n",
+ "# that NODE declares as categorical rather than treating their codes as numeric.\n",
+ "model = NODERegressor(num_trees=256, depth=4, device=\"cpu\")\n",
+ "model.fit(X_train_scaled, y_train)\n",
+ "```\n",
+ "\n",
+ "The short version: normalize targets for stable flow likelihood training, and normalize continuous inputs to improve conditioning for both NODE and MLP. NODE is usually less scale-sensitive than an MLP because its main operation is thresholded routing, but it is not scale-invariant. Always fit transforms on training data only and reverse target transforms before reporting scientific results.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "**Important distinction for mixed tables:** the `ColumnTransformer` example above is a numeric preprocessing example. Its output is a numeric matrix, so it must not be combined with `cat_features` names from the original DataFrame. If you need NODE's learned categorical embeddings, keep the data as a DataFrame, pass the original categorical column names through `cat_features`, and scale only the continuous columns while preserving the categorical columns.\n",
+ "\n",
+ "In all cases, never standardize category labels as if they were measurements. A label code such as `0`, `1`, or `2` is an identifier, not a statement that category 2 is twice category 1.\n",
+ "\n",
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# Pick a few test points to visualise\n",
+ "idx = [0, 5, 10]\n",
+ "X_subset = X_test[idx]\n",
+ "\n",
+ "# Point predictions (mode estimate)\n",
+ "preds_subset = flow_model.predict(X_subset)\n",
+ "\n",
+ "# Sample distribution: (n_samples, num_draws, output_dim)\n",
+ "samples = flow_model.predict(\n",
+ " X_subset,\n",
+ " num_samples=500,\n",
+ " return_sample_distribution=True,\n",
+ ")\n",
+ "\n",
+ "samples = np.asarray(samples)\n",
+ "if samples.ndim == 3 and samples.shape[-1] == 1:\n",
+ " samples = samples[..., 0] # -> (n_samples, num_draws)\n",
+ "\n",
+ "fig, axes = plt.subplots(1, len(idx), figsize=(5 * len(idx), 4), sharey=True)\n",
+ "for i, ax in enumerate(axes):\n",
+ " ax.hist(samples[i], bins=40, density=True, alpha=0.7, color=\"steelblue\")\n",
+ " ax.axvline(y_test[idx[i]], color=\"red\", ls=\"--\", lw=2, label=\"true\")\n",
+ " ax.axvline(preds_subset[i], color=\"orange\", ls=\"-\", lw=2, label=\"predicted mode\")\n",
+ " ax.set_title(f\"Test sample {idx[i]}\")\n",
+ " ax.legend()\n",
+ "fig.suptitle(\"Flow head β predictive distributions\", fontsize=14)\n",
+ "plt.tight_layout()\n",
+ "plt.show()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7d898485",
+ "metadata": {
+ "id": "cell-20",
+ "language": "markdown"
+ },
+ "source": [
+ "---\n",
+ "## 6 Uncertainty estimation\n",
+ "\n",
+ "NODE exposes three related uncertainty workflows:\n",
+ "\n",
+ "| Method | Supported heads | What is sampled | Main signal | API |\n",
+ "|---|---|---|---|---|\n",
+ "| MC dropout | All heads, when at least one dropout rate is positive | Stochastic trunk/head masks | Epistemic uncertainty | `predict_uncertainty()` |\n",
+ "| Flow sampling | Flow heads | Samples from the learned $p(y\\mid x)$ | Aleatoric uncertainty | `predict_uncertainty()` |\n",
+ "| Combined decomposition | Flow heads with dropout | Dropout experts and flow draws | Data and knowledge components | `predict_with_combined_uncertainty()` |\n",
+ "\n",
+ "### MC dropout\n",
+ "\n",
+ "A fitted NODE model is evaluated repeatedly with different dropout masks. The model weights stay fixed; only the selected dropout paths change. For $T$ stochastic predictions $f_{\\theta_t}(x)$,\n",
+ "\n",
+ "$$\n",
+ "\\bar{y}(x) = \\frac{1}{T}\\sum_{t=1}^{T} f_{\\theta_t}(x),\n",
+ "\\qquad\n",
+ "\\sigma_{\\mathrm{MC}}(x) = \\sqrt{\\frac{1}{T-1}\\sum_{t=1}^{T}\\left(f_{\\theta_t}(x)-\\bar{y}(x)\\right)^2}.\n",
+ "$$\n",
+ "\n",
+ "The mean is the MC prediction and the standard deviation is an epistemic, or knowledge, signal. It measures sensitivity to plausible subnetworks, not irreducible measurement noise.\n",
+ "\n",
+ "### Flow uncertainty\n",
+ "\n",
+ "A flow head represents a conditional density $p(y\\mid x)$ rather than only a point estimate. Sampling from that density exposes variation that belongs to the target distribution itself. This is the aleatoric, or data, component.\n",
+ "\n",
+ "The `predict_uncertainty()` result uses the common columns `mean_predictions`, `knowledge_uncertainty`, `data_uncertainty`, and `total_uncertainty`. For a flow-plus-dropout model, use `predict_with_combined_uncertainty()` when you need the explicit decomposition and the `knowledge_method` choice."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b9c10d56",
+ "metadata": {
+ "id": "cell-21",
+ "language": "markdown"
+ },
+ "source": [
+ "### 6.1 MC Dropout uncertainty (any head)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "id": "213e37f0",
+ "metadata": {
+ "id": "cell-22",
+ "language": "markdown"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Re-initializing module because the following parameters were re-set: module__head_type, module__input_dim, module__output_dim.\n",
+ "Re-initializing criterion.\n",
+ "Re-initializing optimizer.\n",
+ " epoch train_loss dur\n",
+ "------- ------------ ------\n",
+ " 1 \u001b[36m4.6743\u001b[0m 0.2266\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " 2 \u001b[36m2.1302\u001b[0m 0.0537\n",
+ " 3 \u001b[36m1.6379\u001b[0m 0.0498\n",
+ " 4 \u001b[36m1.1329\u001b[0m 0.0804\n",
+ " 5 \u001b[36m0.7710\u001b[0m 0.0489\n",
+ " 6 0.7731 0.0515\n",
+ " 7 \u001b[36m0.6371\u001b[0m 0.0493\n",
+ " 8 \u001b[36m0.5107\u001b[0m 0.0775\n",
+ " 9 \u001b[36m0.3966\u001b[0m 0.1115\n",
+ " 10 \u001b[36m0.3701\u001b[0m 0.0663\n",
+ " 11 \u001b[36m0.3163\u001b[0m 0.1488\n",
+ " 12 \u001b[36m0.3148\u001b[0m 0.1562\n",
+ " 13 0.3187 0.1396\n",
+ " 14 \u001b[36m0.2342\u001b[0m 0.1523\n",
+ " 15 \u001b[36m0.2262\u001b[0m 0.1535\n",
+ " 16 \u001b[36m0.1911\u001b[0m 0.1350\n",
+ " 17 0.2064 0.1527\n",
+ " 18 0.2129 0.1565\n",
+ " 19 0.2283 0.1592\n",
+ " 20 0.2057 0.1371\n",
+ " pred mean_predictions knowledge_uncertainty data_uncertainty \\\n",
+ "0 0.008035 0.008035 0.355814 None \n",
+ "1 -0.779116 -0.779116 0.282763 None \n",
+ "2 0.181898 0.181898 0.290713 None \n",
+ "3 0.799892 0.799892 0.296573 None \n",
+ "4 -0.105128 -0.105128 0.366607 None \n",
+ "\n",
+ " total_uncertainty \n",
+ "0 0.355814 \n",
+ "1 0.282763 \n",
+ "2 0.290713 \n",
+ "3 0.296573 \n",
+ "4 0.366607 \n",
+ "\n",
+ "Columns: ['pred', 'mean_predictions', 'knowledge_uncertainty', 'data_uncertainty', 'total_uncertainty']\n"
+ ]
+ }
+ ],
+ "source": [
+ "X_train, X_test, y_train, y_test, y_scaler = get_regression_data()\n",
+ "\n",
+ "model_mlp = NODERegressor(\n",
+ " num_trees=256,\n",
+ " depth=4,\n",
+ " head_type=\"mlp\",\n",
+ " input_dropout=0.1, # required for MC Dropout\n",
+ " max_epochs=20,\n",
+ " lr=0.01,\n",
+ " device=\"cpu\",\n",
+ ")\n",
+ "model_mlp.fit(X_train, y_train)\n",
+ "\n",
+ "df_unc = model_mlp.predict_uncertainty(X_test, num_samples=30)\n",
+ "print(df_unc.head())\n",
+ "print(f\"\\nColumns: {list(df_unc.columns)}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "id": "8495eaa2",
+ "metadata": {
+ "id": "cell-23",
+ "language": "markdown"
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAA94AAAHqCAYAAADyGZa5AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjExLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvctoD+AAAAAlwSFlzAAAPYQAAD2EBqD+naQAA+GxJREFUeJzs3XeYZGWV+PHvvZVzdQ6TmCGHQUBEFAwoiPxEMWBAwAgqC4OKo4txTSvgIrAEFZXFAIsBXXNcA0pwAUlDcAIz0zMdq6u7ctWtG39/VHczPZ2quqvjnM/z9DPT1bduvdXdM1XnnvOeoziO4yCEEEIIIYQQQoh5oS72AoQQQgghhBBCiJVMAm8hhBBCCCGEEGIeSeAthBBCCCGEEELMIwm8hRBCCCGEEEKIeSSBtxBCCCGEEEIIMY8k8BZCCCGEEEIIIeaRBN5CCCGEEEIIIcQ8ksBbCCGEEEIIIYSYRxJ4CyGEEEIIIYQQ80gCbyGEEPPiscceQ1EUvv/97y/2UsaYpomiKHzqU59a7KUIsai++MUvoigKmqYt9lKEEOKAIIG3EEIsY+l0GkVRUBSFd7/73ZMe87WvfW3smP/93/+dcN+LLrqoqvOPfsRiMV7ykpdw99131/35LHdz+XmMyuVyXHPNNZx88snE43ECgQCHHXYYb3rTm/j1r3+NbdtVPb6iKLhcLhoaGjj22GO56KKL+Otf/1q357qUfOpTn0JRFEzTXOylLIil8HyXwhqEEGI5kcBbCCFWgFAoxN13302hUJjwtW9/+9uEQqE5nf+9730vjuNg2zaPPPIIjY2NvPnNb+aOO+6Y03lXqtn+PJ599llOOOEEbr31VjZt2sT27dtJp9P8+Mc/pq2tjbPPPpsHH3xwxscf/XmZpsnu3bv5+te/jsvl4uUvfznveMc7sCxrzs9RLG+f+tSncBwHv9+/2EsRQogDggTeQgixArzuda9D07QJWeinn36aBx98kHPPPbcuj6MoCgcffDB33HEHPp+P66+/vi7nXWlm8/MwTZNzzjmHYrHIAw88wPnnn09LSws+n4+NGzfy1a9+lf/5n//B5/NVvY7RCoUXv/jF3Hrrrdx6661873vf4/Of//ycn6MQQgghqieBtxBCrACtra2cddZZfPvb3x53++23387atWt5xSteUdfHi0QidHZ2snfv3qqOv/POOznssMPw+/2ccMIJ/OUvf5lwTLlc5nOf+xxHHHEEPp+P5uZmLrzwQnp6esaO0TRtXCm11+vlkEMO4ZOf/OSEvao9PT285S1vIRaL0dDQwMUXXzxpBno+zObncffdd/PUU0/xyU9+kra2tknPe84553D88cfPel0XX3wxRx11FNdffz26rgPj9+LffvvtHH744bjd7rGf0b333ssZZ5xBNBolGAxy4okn8t///d/jzrt582bcbje5XI53vetdxONx4vE4559/PgMDAxPWUc053/a2t3HQQQdNuO+3vvUtFEVhx44dAFxwwQX8+7//OwAej2fsd+PJJ5+c8vtQ7bn3fW6lUolLLrmExsZGotEob33rWxkeHp5wjm3btnHBBRfQ2dlJIBDgec97Ht/4xjfGbRHYtm0bb3/722lra8Pr9XLYYYfx5S9/edwxo4+bz+d53/veR3NzM2vWrJnx+Y4+h9GPSCTCKaecwk9/+tNx65xsj3e1z3W6Ndx2220oisK999474Xvzve99D0VR+NOf/jTlz0YIIVYqCbyFEGKFePe7380999zD7t27gUoG9Y477uAd73gHqlrf/+5zuRy9vb2sWbNmxmN/+tOf8sQTT3DPPfewY8cOmpubef3rX086nR47xjRNzjrrLL7+9a9z1VVXMTg4yL333ktPTw+nnnrq2LF+vx/HccY+BgcH+cpXvsI3vvENNm/ePG59L3vZy3jiiSf4/e9/T1dXF2eccQabNm2q6/dhOrX+PH77298CcNZZZ83rul71qleRy+V45JFHxt3+wx/+kCeffJI//elP3H///USjUf74xz9y2mmn0djYyOOPP87u3bs5++yzOf/887nuuusmnPvSSy/lnHPOYc+ePfzqV7/igQce4JWvfCWlUmnsmFrPOZM77riDT37ykwAYhjH2u3HMMcfUfK7pfPjDH+bMM89k9+7d/OxnP+MPf/gDl19++bhjHnvsMU488US6u7v5+c9/TjKZ5I477uCRRx7hiSeeAGDLli284AUvYHh4mP/93/9leHiYa6+9lmuuuWbC+aDyPX31q1/N9u3b+exnPzvj873ooovGbrMsi2eeeYaXvvSlvOlNb5o0GJ7Nc51uDW9/+9tpaGjgq1/96oTzfvWrX2X9+vWcdtppVa1DCCFWEgm8hRBihTj77LNpamriO9/5DgC/+c1v6O/v513velfdHsNxHHbu3Mk73/lOyuXypIHC/gYHB7nmmmvo6Ohg9erV3HDDDWQyGX74wx+OHfPd736XP//5z9x+++284Q1vIBqNcsQRR/CDH/yARCLBzTffPOm5Y7EY55xzDps3b+Zb3/rW2N7lb37zmzz77LPccccdvPCFLyQajfKWt7yF5z3vefX5RlSh1p9HV1cXQFUXM+Zi9erVAPT29o67vb+/n6985SusWrWKk046iRNOOIGPfexjrFq1ijvuuIP169fT2trKZz/7Wc455xw+85nPkMlkxu5vWRYvfvGLx35+p5xyCrfffjtPPfXUuMx/LedcKizL4oUvfCGvf/3riUajnHbaaXzgAx/g+9//Prlcbuy4TZs2EQ6H+dWvfsWJJ55IKBRi48aNfP3rX+e4444D4IMf/CCxWIyf/OQnbNy4kXA4zOte9zq+9KUv8bWvfY2dO3dOeNw3vvGNNDQ08N73vremdauqyurVq7nqqqs46qijuO222+r2XKcSCAR417vexY9//GMSicTY7Y899hh///vfec973oOiKDU9DyGEWAkk8BZCiBXC4/Hw9re/ne9+97s4jsPtt9/OS1/6Ug4++OA5n3u0fFRVVZ73vOcxMDDA97//fd75znfOeN/XvOY14z4/4ogjcLvd4wKMX/ziF0SjUV71qleNO7alpYVjjz2We+65Z+y2X/7yl7ziFa+goaEBVVVRFIUrr7yScrlMd3c3UMmqdnR0cOKJJ4473+tf//pan/qszefPYy4cxwGYEPy87nWvG/d5KpXikUce4bWvfS0ej2fc184991wKhQJ///vfpz3Hy172MuLx+Fhp8WzOuVTs/3t8zDHHYFnW2AWTTCbDfffdxznnnDNl87xcLsc999zDa17zGoLB4LivnX766di2zd/+9rdxt+//PZ2Jpml87nOf4+ijjyYQCIwrA9+3hH46Mz3XmVxyySUYhjEu0P/qV7+Kqqp1vRAohBDLiQTeQgixgrzrXe9i586d/OQnP+GXv/xl3d7kjnbJdhyHXC7Hfffdx1vf+taq7tvR0THuc1VVCYVC40rN+/v7yWazeL1e3G43LpdrLKj++9//ztDQEFDJGr/uda9j48aNPPzww2iahuM4YxlxwzAAGBoamnSf9FR7p/fX3t4+YYyaoijE4/Gq7j+qlp/HunXrAKreNz9boxcnOjs7x92+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tt4M6YLOFP2ilyUJvFcYn8/H0UcfPe0xTzzxBK985St529vexk033TTh689//vN58MEHyWazY7f97ne/IxQKTTmmTAghhBBCCCHmwrJtdvRlSBfKMx+8zEip+Qqwb3M1gO9///tTNlfbunUrp59+OieeeCIf+MAHxh131FFHoaoqb3/72/mP//gP3vSmN/HJT36SRCLB5Zdfzsc+9jHi8fiCPS8hhBBCCCHEgaNrME/PcIHmSIChvLbYy6krCbyrZJjWzAct0mPs21xtMvs2V3v88cdpbW2lu7t7XAM2gAceeIBIJILb7eaPf/wjX/ziF/noRz9KKBTiM5/5DJdccsms1ieEEEIIIYQQ00lkSuwayBINePG4V15htgTeM3C7FEJ+DwXNmPcZ2wAhvwe3S5n5wH3s21xtJm95y1t4y1veMuNxra2t3HjjjTWtQwghhBBCCCFqldcMdvRlUJRK7LUSSeA9A6/bxXEHNWFaC7Oz3+1S8LpdC/JYQgghhBBCCLGYDMtme1+GrGbQHgss9nLmjQTeVfC6XXjlOyWEEEIIIYQQdeM4DrsGcvSnirTGAuPGGa80K694XgghhBBCCCHEktefLrF7MEtD2IfbtbJD05X97IQQQgghhBBCLDmZos72vgw+t4vAAVBevOwC72KxyDPPPEMul1vspQghhBBCCCGEqFHZsNjel0bTTeIh32IvZ0Esm8B7+/btnHvuubS3t/PGN76RtrY2zjvvPAqFwmIvTQghhBBCCCFEFWzHYedAlsGsRnN05TZT29+yCby3bNnCeeedRzqd5plnnmHbtm088MADfPSjH13spQkhhBBCCCGEqEL3UJ49yTxNYT8udeU2U9vfsimmf+Mb3zju89WrV3Puuefym9/8ZpFWJIQQQgghhBCiWql8mWf7swS9bnyeA2uE8rLJeE/mscce46CDDlrsZQghhBBCCCGEmIbjOOwdymNYNtGgd7GXs+CWbeD9X//1X/z5z3/myiuvnPKYcrlMNpsd9yHm7q677uIlL3nJ2Oe33XYbp59++pJYixBCCCGEEGLpyRR1BjMlYsEDo5na/pZl4P2LX/yCSy65hJtvvnnaoOuqq64iFouNfaxZs2YBV7lyFQoF+vr6xj7P5XL09/dXff9bb72VM888c17WIoQQQgghhFh6+lJFTNvBf4CVmI9adoH3r371K9785jdz7bXXcskll0x77Mc//nEymczYx969exdolQeWiy66iD/+8Y9VH5/L5RgYGJjHFQkhhBBCCCGWirxmMJAuEg0ceCXmo5ZV4P2b3/yGN73pTXz5y19m06ZNMx7v8/mIRqPjPhZKX6bE/c8m6cuU5vVxtm7dyurVq/nlL3/J2WefzWGHHcZrXvMannnmmQnH/PrXv+aMM85g/fr1/O53vwPgkUce4Q1veAOHHnoop5xyCjfccAOWZY17jB/+8IecdNJJbNy4kfe+970kk8lxX//BD37A+eefP+62Rx55hHPPPZdDDz2U0047jV/96lcA/OhHP+KLX/wiTz/9NKtXr2b16tXccccddVuLEEIIIYQQYu4GsyVS+XJdzjWQLqIZFkHfsuntXXfL5pn/5S9/4Q1veAPnnXceL37xi3n44YcBcLvdHHfccfPymI7jUDKsmQ/cz4//0c2//fwpbAdUBT73uqN50/NXV33/gMeFolTXWt8wDHp6erjooou49dZbWb9+Pddccw2vfOUr2b59O6FQaOyYyy67jJtvvpljjjmG1tZWHnnkEU477TS+8IUvcNVVV9Hb28ull15KIpHgS1/6ElD5vl9wwQVce+21nH766fz0pz/lU5/61LimdvuXmj/44IO87GUv4wMf+ACf+tSnyGQy/Md//AcvetGLeM1rXsOmTZv4yU9+Mhb8NzQ01G0tQgghhBBCiLlxHIeuwRy2A8cd1ITXPfvycE036R0uEvZ76rjC5UdxHMdZ7EVU4+abb+bb3/72hNsjkQh//vOfqzpHNpslFouRyWQmZL81TWPXrl2sX78ev98PQFE3Oeozv5vz2mv19OfPJOit7prIk08+ycaNG/nGN77BxRdfDFSayq1bt45PfepTXHbZZWPH/PjHPx43lu21r30thx56KNddd93Ybb///e954xvfSC6XQ1EUXv3qV9PW1sZ3vvOdcfd75pln2LFjBwA33HAD3/rWt3jyyScBOOuss3C73fziF78Yt1bHcVAUhWuvvZY77riDxx57rO5r2d9kP1chhBBCCCHE1AqawYPbE5R0k6PXNrKuJTLrc+0ZzPFUd4qOeLDq5CJAf7rIiQe30BRZuu/hp4sv97dsMt6XXXYZl1122WIvY8k69dRTx/7u8/k46aSTeOKJJ8Ydc8IJJ4z7/P777+eBBx7gJz/5CY7j4DgO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Cj0UIiIiIiIiGgAD7xJlCNHduzvogs1SMkv1iYiIiIiIxhwG3iUqFFPhtVtQxWw3ERERERFRUWPgXYIMQyCuplAfdMG6h+JrREREREREVFgMvEtQV0yFz2lFpc9e6KEQERERERHRHjDwLjG6IZDQurPdFhOz3URERERERMWOgXeJ6Ywm4XfZUOFltpuIiIiIiKgUMPAuISndgJbS0RB0wazw0hEREREREZUCRm8lpDOqIuCxodxjK/RQiIiIiIiIKEsMvEtESjegGwbqAy6YmO0mIiIiIiIqGYzgSkRHNImgx4Ygs91EREREREQlhYF3CdBSBgxDoC7ggiLzkhEREREREZUSRnEloCOaRLnXzmw3ERERERFRCWLgXeRUTQcA1AdckCWpwKMhIiIiIiKiXDHwLnIdsSQqfXaUua2FHgoRERERERENAgPvIpZM6ZAlCXUBJ7PdREREREREJYqBdxHrjCZR5bPD72S2m4iIiIiIqFQx8C5idqsJdQEXJGa7iYiIiIiISpap0AOg/imyhAqvA16HpdBDISIiIiIioiFgxrsIyZIEl92M2jIns91EREREREQljhnvImS3mLB3fRlsZqXQQyEiIiIiIqIhYsa7SDHoJiIiIiIiGh0YeBMRERERERHlEQNvIiIiIiIiojxi4E1ERERERESURwy8iYiIiIiIiPKIgTcRERERERFRHjHwJiIiIiIiIsojBt5EREREREREecTAm4iIiIiIiCiPGHgTERERERER5REDbyIiIiIiIqI8YuBNRERERERElEcMvImIiIiIiIjyiIE3ERERERERUR4x8CYiIiIiIiLKI1OhBzCShBAAgFAoVOCREBERERERUSnriSt74szdGVOBdzgcBgDU19cXeCREREREREQ0GoTDYXi93t3uI4lswvNRwjAMbN++HW63G5IkFXo4AwqFQqivr8eWLVvg8XgKPRwaAK9TaeB1Kg28TsWP16g08DqVBl6n0sDrVBoKeZ2EEAiHw6ipqYEs734V95jKeMuyjLq6ukIPI2sej4c/5CWA16k08DqVBl6n4sdrVBp4nUoDr1Np4HUqDYW6TnvKdPdgcTUiIiIiIiKiPGLgTURERERERJRHDLyLkNVqxcKFC2G1Wgs9FNoNXqfSwOtUGnidih+vUWngdSoNvE6lgdepNJTKdRpTxdWIiIiIiIiIRhoz3kRERERERER5xMCbiIiIiIiIKI8YeBMRERERERHl0Zjq410KUqkUPv74Y5hMJuy1116QJKnQQyIAW7ZswaZNm3DAAQfA6XT2u8/GjRvR2tqK6dOnw+VyjfAIKZFIYN26dSgrK0NdXd2APzuffvop4vE49t57b1gslhEeJSWTSaxbtw5OpxMTJkyALPe9/6vrOj7++GNIkoS99967331oZHz44YcIhUI47LDD+lyHRCKBTz75BC6XC1OnTi3QCMemrq4urFmzps/2WbNmwW63Z2wLhUJYv349Kioq0NDQMFJDpF189tlnUFUVM2bM6Pd3WmtrK7788ks0NDSgsrKyACMcu9566y1omtZne0VFRZ/fbVu3bkVTUxMmT54Mn883QiOkHqlUCl9++SUikQjGjx8Pv9/f735ffPEFOjs7MWPGDDgcjhEe5W4IKhorV64UtbW1or6+XlRUVIjp06eLdevWFXpYY9ry5cvFiSeeKILBoAAgPvjggz77hMNh8fWvf104nU4xbdo04XQ6xf333z/iYx2r2tvbxdlnny18Pp/Yb7/9RDAYFAceeKD4+OOPM/bbvHmzmDlzpggEAmLChAmivLxcvPLKKwUa9diTTCbFZZddJoLBoDjggANEVVWVmDhxonjttdcy9nv//ffFuHHjRG1traiqqhKTJk0Sa9asKdCox7bXX39dmM1mAUCEw+GMx55//nlRVlYmJk2aJHw+nzj44INFU1NTgUY69ixZskQAEIcffnjGv02bNmXsd+eddwq73S6mT58uHA6H+Pa3vy1isViBRj02vfPOO2KvvfYSVVVV4sADDxT77bdfn79Pl19+ubBarWKvvfYSVqtVnHPOOcIwjAKNeOyZP39+xs/RnDlzBABx4YUXpvdJJpPi+9//vrDb7WLGjBnCZrOJP/7xjwUc9djz6quvioaGBlFfXy8OOOAAYbfbxc9//nOh63p6n/b2dnHUUUcJt9stpkyZIjwej3j88ccLOOpMDLyLRCwWEzU1NeKcc84RQgiRSqXEN7/5TXHggQcWeGRj21133SWeffZZsWrVqgED77PPPltMmTJFtLW1CSGEuP/++4WiKOKTTz4Z4dGOTR9//LG46667RDKZFEIIkUgkxEknnSRmzJiRsd/RRx8tjj766PR+l19+ufD7/aKjo2Okhzwmtbe3izvuuEMkEgkhhBC6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Xn75Zfz0pz8F0P33at26dVi/fn36ec8++yzKy8sxc+bMgox7rNn158kwDPzrX//CPvvsk9523HHH4d///nfGvs8++ywOOeSQjGwe5c/Xv/51bN++PWOt9tatW+FwONJTyI877ji88MILGYmX5557jn+bCuDee+/FzJkzcdBBB2VsN5vNGQEe0J1Vfemll3idRkh5eTkA9KlPsXXr1vRjkydPxoQJEzKu09atW/Hee+8Vz3UqbG036u3MM88UDQ0N4tFHHxX33HOP8Hq94pprrin0sMa0bdu2iWXLlomHHnpIABD333+/WLZsmdi2bVt6n+eff14oiiKuu+468b//+7/i8MMPF/vuu2+6bRXl1+bNm0VNTY2YPXu2WLp0qVi2bFn6XzQaTe93yy23CIfDIe68807xxBNPiClTpoiTTjqpgCMfWx555BFx0kkniQcffFAsWbJE/Nd//Zeorq4W3/72tzP2++UvfymqqqrEQw89JO6//34RCATERRddVJhBU79VzQ3DEMccc4zYd999xVNPPSVuvfVWYbFY2P5oBJ111lniggsuEP/7v/8rnnnmGTF//nzhcrnEu+++m94nEomIKVOmiGOPPVb8/e9/F1deeaUwmUzi3//+dwFHPra0t7eLcePGibPOOku8+OKL4s9//rPw+/1i4cKF6X02b94sAoGAOP3008Vzzz0nfvrTnwq32y0+/fTTwg18DOro6BB2u13ccccd/T7+xhtvCIvFIi6++GLx7LPPim984xuioaGBLUlH0Pz588W4cePEAw88IP75z3+Kn/zkJ8JsNov33nsvvc/jjz8uTCaTuPHGG8UzzzwjZs+eLQ466CCRSqUKOPKvSEKwZGKx0DQNd9xxB1566SWYTCaceuqpOOuss/pk72jkPPXUU/iv//qvPtv/8z//E9/97nfTX//rX//C3Xffjba2NsyaNQtXXHFFn0JelB8rV67EZZdd1u9jDz/8MCZMmJD++rHHHsPjjz+OeDyOefPm4cILL8yozEz59eqrr+KRRx7Bli1bUF1djRNPPBGnnnpqetoYAOi6jrvuugsvvPACJEnCSSedhF/84hcZ+9DIee2113DVVVdhyZIlsNvt6e3RaBS33HILli1bBpfLhTPPPDOjUB7ll6ZpuO+++7BkyRKoqoq9994b559/fkZnFKB7ydpNN92E1atXo6KiAueccw6OOOKIAo16bGpsbMQf/vAHrFmzBhUVFTjllFPwne98J2Ofzz//HH/4wx/wxRdfYNy4cbj44ovTxddoZLz44ou44YYb8Nxzz8Hn8/W7z1tvvYXbb78djY2N2HvvvXH55ZejtrZ2ZAc6hiWTSfz1r3/F0qVLEQ6HMWnSJJx77rl9flb++c9/4m9/+xs6OzsxZ84cXHbZZfB6vQUadSYG3kRERERERER5xBQCERERERERUR4x8CYiIiIiIiLKIwbeRERERERERHnEwJuIiIiIiIgojxh4ExEREREREeURA28iIiIiIiKiPGLgTURERERERJRHDLyJiIgGsG7dOrz00kvDcqy3334bn3zyybAcayR88cUX+Mc//jHg4x999BH+9a9/jeCI8mPp0qX48MMP01//61//wkcffTSkYw7HMfrzwgsvoKWlZdiPS0RE+cfAm4iICmrNmjV4/vnn8fbbbyOZTBZ6OBmef/55XH755UM+TmtrK0488USYzeZhGNXA1q5di5dffnlYjrVkyRJcdNFFAz7+9NNPY+HChcNyrkK68cYb8d///d/prxcuXIinn3466+e/8sorfW6o5HqMbL355pu4+OKLh/24RESUfwy8iYioIJqbm3HIIYfga1/7Gu6++25ceOGFmD59Ou67775CD23Y3XTTTTjuuOMwZcqUvJ7n2Wefxa9//eu8nmO0+9rXvoZ99tkn6/2vuuoqPPPMM0M6RrYuuugiPPXUU/j444+H/dhERJRfpkIPgIiIxqbLLrsM8XgcGzduhN1uB9CdGX7llVfS+2zatAlvvPEGAMDpdGKvvfbCpEmTMo7z0UcfYceOHZg7dy5WrVqVDujLy8uRSCSwYsUKpFIpHHroofB4PH2ed/jhh6efd/jhhyMQCOxx7J9//jk++ugjVFZW4sADD4TVah1w30Qigb/97W948sknM7Y3NTVh1apVcDgcmD17NhwOR8bj7733HjZt2oT6+nocdNBBA77mlStXorW1FbNmzcKHH36Ijo4OPP744wCAgw8+GBMnTsxqzLquY/ny5UgkEth///33+B70fn39vX9NTU1YunQpTj31VJhMX33c2L59O5YtW4bvfOc7UBSl39e1u2vS32v/7ne/m34Nb7/9NpqbmzFlyhTstddefcbb2dmJZcuWIRAI4IADDujz+BFHHIHKysqMbYZh4N1330VTUxP2339/NDQ0AACWLVuGtrY2rFmzJv2en3LKKf0eIx6PY+XKlQiHwzjggAMwbty4jMdfeukljB8/HuXl5fjggw9gMplwyCGHZFwnv9+Pb37zm7jrrrtwxx13DHBFiIioGDHwJiKigvjoo49w6KGHpoNuAAgGgzjttNPSX2/ZsgV///vfAQDhcBjLli3Dj3/8Y9x+++3pfZ5++mk8+OCDMJlMmDhxIpqbm/HFF1/g9ttvx+9//3tMmDAB27dvR1dXF9566y1UV1enn/fwww/DZrOhoqICoVAIn3/+OZ577jkcddRR/Y45lUphwYIF+Mc//oE5c+Zg69atiEajePbZZzFjxox+n7N8+XJEo1HMnTs3ve2BBx7Aeeedh0MOOQRCCGzduhUPPfQQDj74YMRiMZx00kn48MMPMXv2bLz//vuYPn06nn/+ebjd7oyxO51OlJeXo6qqCsFgEB9//DE6OjrS71llZSUaGhr2OOZwOIzjjjsOmzZtwgEHHIDVq1dj+vTpe7yGjY2NmDVrVr/vn8PhwE9/+lPY7XacdNJJ6ef88Y9/xMqVK/H973+/z/GyuSb9vfbvfve72LhxI771rW9B13VMnjwZ7733HubMmYMnnngiPcX/rbfewje+8Q3U19cjEAhg27ZtkCQp40bDwoULMzLWGzZswMknn4z29nbMnDkTn376Kc455xxcfPHFeOONN9DR0YG1a9dCkiQAwPz58/sc44MPPsD8+fPh9/tRU1ODFStW4LLLLsO1116bPu/ll1+OsrIybNq0CTNmzMDHH38Mh8OBN998M33dAeCYY47BH//4RwbeRESlRhARERXAueeeK3w+n7j//vvFjh07snrOpk2bhNfrFa+99lp628KFC4UkSWLp0qVCCCEMwxCHHnqoUBRFvPnmm0IIIXRdF/vtt5+4+uqrM54HQNx///0ZY5o6dapIpVJCCCH++Mc/ipkzZ6Yfv+GGG8T+++8vQqFQetsFF1wgDj/88AHHfPPNN4upU6dmbBs/frz461//mv66sbFRrFy5UgghxLXXXisaGhrS70lLS4sYP368uPLKK/uM/fnnn8847o033ihmzZqVsS2bMV999dVi8uTJor29XQghxJYtW0QgEBDTpk0b8HVl8/6dddZZ4tvf/nb6cVVVRXl5ubj77rsHfcyBXvucOXPEJZdckv46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rFixAi+//DJeffVVBINB/PnPf8ZJJ52Uzub94Q9/wLRp0/DWW2/B6XTi4YcfxptvvpkxRbenZVNvM2fO7DM1eNasWX3WLe+zzz649dZb8fTTT6OlpQX3338/vve976Ufnz59Ok444YT017W1tVi9ejUeeughvPfee3C73Tj11FNx6qmn7va1XnXVVTjxxBNx1VVXweVypY/x1ltvweFw4MEHH8Q3vvENAN1rf1etWoV77rkHy5Ytw5QpU3DzzTdnjL2/1wwAhxxyCJ588kksWbIE7777LiorKzFv3rw9jnn69Ol45513cPfdd+ODDz7A9773PVxwwQV46aWXBnxN++yzDy644ALMnz9/wPevx8knn4ynn34aP/7xj3f7PvUcd3fXZKDXfvLJJ+PDDz/EI488guXLl6OhoQF33XUXDjzwwPQ+v/rVr1BXV4fnn38esVgMDzzwAJYtW5ZRYK13GzAA+N73vofp06fjkUcewcqVK3HQQQdhwYIF6cevvvpqlJeX4/XXX0c8Hsf8+fP7HOOnP/0pJk2ahKeeegpvv/02zj77bPzsZz/LWI9+wgkn9Gnhdthhh2XUDXjqqadQXV2N+fPn7/F9JCKi4iKJ4VooR0REVEKuvfZavPLKK1i+fPmInO/mm2/G7Nmzceyxx47I+YrJj370I7S3t+P//u//drvfSF+TUnP11VfjxBNPTFfrJyKi0sGMNxER0Qi4/PLLCz2EEffGG29g+fLleOKJJ9JtvWjwrr/++kIPgYiIBomBNxERjUkDTVmm4fPOO+/g448/xuOPP47DDjtsj/vzmhAR0WjFqeZEREREREREecR2YkRERERERER5xMCbiIiIiIiIKI8YeBMRERERERHlEQNvIiIiIiIiojxi4E1ERERERESURwy8iYiIiIiIiPKIgTcRERERERFRHjHwJiIiIiIiIsojBt5EREREREREefT/AbJaiIrqK9+sAAAAAElFTkSuQmCC",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# Plot predictions with uncertainty bands\n",
+ "sort_idx = np.argsort(df_unc[\"mean_predictions\"].values)\n",
+ "preds_sorted = df_unc[\"mean_predictions\"].values[sort_idx]\n",
+ "unc_sorted = df_unc[\"total_uncertainty\"].values[sort_idx]\n",
+ "true_sorted = y_test[sort_idx]\n",
+ "\n",
+ "fig, ax = plt.subplots(figsize=(10, 5))\n",
+ "ax.fill_between(\n",
+ " range(len(preds_sorted)),\n",
+ " preds_sorted - 2 * unc_sorted,\n",
+ " preds_sorted + 2 * unc_sorted,\n",
+ " alpha=0.25,\n",
+ " color=\"steelblue\",\n",
+ " label=\"Β±2Ο\",\n",
+ ")\n",
+ "ax.plot(preds_sorted, \"o-\", ms=3, label=\"predicted\")\n",
+ "ax.plot(true_sorted, \"x\", ms=4, color=\"red\", label=\"true\")\n",
+ "ax.set_xlabel(\"Sample (sorted by prediction)\")\n",
+ "ax.set_ylabel(\"Target\")\n",
+ "ax.set_title(\"MLP head β MC Dropout uncertainty\")\n",
+ "ax.legend()\n",
+ "plt.tight_layout()\n",
+ "plt.show()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "794ddda2",
+ "metadata": {
+ "id": "cell-24",
+ "language": "markdown"
+ },
+ "source": [
+ "#### How dropout controls uncertainty β the three knobs\n",
+ "\n",
+ "NODE has **three independent dropout knobs**, each acting at a different stage and each\n",
+ "feeding Monte-Carlo (MC) dropout uncertainty. Turning any of them up injects more\n",
+ "stochasticity, so **higher dropout β more spread across MC passes β larger `total_uncertainty`**.\n",
+ "\n",
+ "| Knob | Acts on | Regularises against |\n",
+ "|------|---------|---------------------|\n",
+ "| `input_dropout` | raw / embedded input features (inside every `DenseODSTBlock`) | over-reliance on any single feature |\n",
+ "| `tree_dropout` | **whole tree outputs** before the head (Bernoulli mask, inverted-dropout scaling) | over-reliance on any single tree |\n",
+ "| `mlp_dropout` | hidden units inside the MLP head (`nn.Dropout`) | head co-adaptation |\n",
+ "\n",
+ "All three are gated on the module's `training` flag. For MC dropout, NODE keeps the model in\n",
+ "`eval()` (so **BatchNorm uses its running statistics and is never updated**) and switches on\n",
+ "**only** the dropout mechanisms β see Β§13.5. If *all* dropouts are 0, `predict_uncertainty`\n",
+ "falls back to a deterministic `predict()` (zero variance).\n",
+ "\n",
+ "The cell below fits one model, then isolates each knob at inference and sweeps its rate to show\n",
+ "the monotonic variance β relationship.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 44,
+ "id": "5127bf8a",
+ "metadata": {
+ "id": "cell-25",
+ "language": "markdown"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Re-initializing module because the following parameters were re-set: module__head_type, module__input_dim, module__output_dim.\n",
+ "Re-initializing criterion.\n",
+ "Re-initializing optimizer.\n",
+ " epoch train_loss dur\n",
+ "------- ------------ ------\n",
+ " 1 \u001b[36m1.2379\u001b[0m 0.0672\n",
+ " 2 \u001b[36m1.0366\u001b[0m 0.0222\n",
+ " 3 \u001b[36m0.6278\u001b[0m 0.0221\n",
+ " 4 \u001b[36m0.4741\u001b[0m 0.0221\n",
+ " 5 \u001b[36m0.3456\u001b[0m 0.0217\n",
+ " 6 \u001b[36m0.2720\u001b[0m 0.0211\n",
+ " 7 \u001b[36m0.2472\u001b[0m 0.0210\n",
+ " 8 \u001b[36m0.2249\u001b[0m 0.0213\n",
+ " 9 \u001b[36m0.1847\u001b[0m 0.0215\n",
+ " 10 0.2388 0.0220\n",
+ " 11 \u001b[36m0.1763\u001b[0m 0.0242\n",
+ " 12 \u001b[36m0.1508\u001b[0m 0.0224\n",
+ " 13 0.1763 0.0224\n",
+ " 14 \u001b[36m0.1477\u001b[0m 0.0255\n",
+ " 15 0.1507 0.0242\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " 16 0.1627 0.0261\n",
+ " 17 0.1735 0.0226\n",
+ " 18 0.1608 0.0261\n",
+ " 19 \u001b[36m0.1407\u001b[0m 0.0276\n",
+ " 20 \u001b[36m0.1389\u001b[0m 0.0211\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "predict_uncertainty (MC-dropout): input_dropout, tree_dropout, and relevant head dropout are all 0. MC-dropout repeats are deterministic, so variance-based epistemic uncertainty collapses to zero. Set at least one dropout > 0 during training, or pass input_dropout= and/or tree_dropout= to predict_uncertainty() for a temporary inference-time override.\n",
+ "predict_uncertainty (MC-dropout): input_dropout, tree_dropout, and relevant head dropout are all 0. MC-dropout repeats are deterministic, so variance-based epistemic uncertainty collapses to zero. Set at least one dropout > 0 during training, or pass input_dropout= and/or tree_dropout= to predict_uncertainty() for a temporary inference-time override.\n",
+ "predict_uncertainty (MC-dropout): input_dropout, tree_dropout, and relevant head dropout are all 0. MC-dropout repeats are deterministic, so variance-based epistemic uncertainty collapses to zero. Set at least one dropout > 0 during training, or pass input_dropout= and/or tree_dropout= to predict_uncertainty() for a temporary inference-time override.\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "dropout p 0.0 0.1 0.2 0.3 0.5\n",
+ "input_dropout 0.000 0.271 0.405 0.525 0.804\n",
+ "tree_dropout 0.000 0.057 0.087 0.114 0.172\n",
+ "mlp_dropout 0.000 0.157 0.255 0.367 0.724\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "β Each dropout knob is deterministic at p=0 and increases variance monotonically.\n"
+ ]
+ }
+ ],
+ "source": [
+ "import torch.nn as nn\n",
+ "from mother.ml.models.node_utils import DenseODSTBlock\n",
+ "\n",
+ "X_train, X_test, y_train, y_test, y_scaler = get_regression_data()\n",
+ "\n",
+ "# Fit one model with all three dropout paths active\n",
+ "reg_dp = NODERegressor(\n",
+ " head_type=\"mlp\",\n",
+ " num_trees=64,\n",
+ " depth=4,\n",
+ " input_dropout=0.1,\n",
+ " tree_dropout=0.1,\n",
+ " mlp_dropout=0.1,\n",
+ " max_epochs=20,\n",
+ " lr=0.01,\n",
+ " device=\"cpu\",\n",
+ ")\n",
+ "reg_dp.fit(X_train, y_train)\n",
+ "\n",
+ "\n",
+ "def set_dropouts(est, *, input_dp, tree_dp, mlp_dp):\n",
+ " \"\"\"Override dropout rates on a fitted NODE model (to isolate one knob at inference).\"\"\"\n",
+ " m = est.module_\n",
+ " est.input_dropout = input_dp\n",
+ " m.input_dropout = input_dp\n",
+ " for sub in m.modules():\n",
+ " if isinstance(sub, DenseODSTBlock):\n",
+ " sub.input_dropout = input_dp\n",
+ " m.tree_dropout = tree_dp\n",
+ " m.mlp_dropout = mlp_dp\n",
+ " for sub in m.modules():\n",
+ " if isinstance(sub, nn.Dropout):\n",
+ " sub.p = mlp_dp\n",
+ "\n",
+ "\n",
+ "def mean_mc_std(est, X, num_samples=100):\n",
+ " \"\"\"Mean MC-dropout std across the test set.\"\"\"\n",
+ " return float(np.mean(est._predict_uncertainty_mc_dropout(X, num_samples=num_samples, use_std=True)))\n",
+ "\n",
+ "\n",
+ "sweep = [0.0, 0.1, 0.2, 0.3, 0.5]\n",
+ "mechanisms = {\n",
+ " \"input_dropout\": lambda p: dict(input_dp=p, tree_dp=0.0, mlp_dp=0.0),\n",
+ " \"tree_dropout\": lambda p: dict(input_dp=0.0, tree_dp=p, mlp_dp=0.0),\n",
+ " \"mlp_dropout\": lambda p: dict(input_dp=0.0, tree_dp=0.0, mlp_dp=p),\n",
+ "}\n",
+ "\n",
+ "curves = {}\n",
+ "for name, cfg in mechanisms.items():\n",
+ " stds = []\n",
+ " for p in sweep:\n",
+ " set_dropouts(reg_dp, **cfg(p))\n",
+ " stds.append(mean_mc_std(reg_dp, X_test))\n",
+ " curves[name] = stds\n",
+ "\n",
+ "# Report + plot\n",
+ "print(\"dropout p \".ljust(16) + \"\".join(f\"{p:>8}\" for p in sweep))\n",
+ "for name, stds in curves.items():\n",
+ " print(name.ljust(16) + \"\".join(f\"{s:>8.3f}\" for s in stds))\n",
+ "\n",
+ "fig, ax = plt.subplots(figsize=(8, 5))\n",
+ "for name, stds in curves.items():\n",
+ " ax.plot(sweep, stds, \"o-\", label=name)\n",
+ "ax.set_xlabel(\"dropout probability\")\n",
+ "ax.set_ylabel(\"mean MC-dropout std\")\n",
+ "ax.set_title(\"Higher dropout β more predictive variance\")\n",
+ "ax.legend()\n",
+ "plt.tight_layout()\n",
+ "plt.show()\n",
+ "\n",
+ "# Sanity checks: zero variance at p=0, and variance grows with p\n",
+ "for name, stds in curves.items():\n",
+ " assert stds[0] < 1e-6, f\"{name}: expected ~0 variance at p=0\"\n",
+ " assert stds[-1] > stds[1], f\"{name}: variance should grow with dropout\"\n",
+ "print(\"\\nβ Each dropout knob is deterministic at p=0 and increases variance monotonically.\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 62,
+ "id": "25b796d1",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "predict_uncertainty (MC-dropout): input_dropout, tree_dropout, and relevant head dropout are all 0. MC-dropout repeats are deterministic, so variance-based epistemic uncertainty collapses to zero. Set at least one dropout > 0 during training, or pass input_dropout= and/or tree_dropout= to predict_uncertainty() for a temporary inference-time override.\n",
+ "predict_uncertainty (MC-dropout): input_dropout, tree_dropout, and relevant head dropout are all 0. MC-dropout repeats are deterministic, so variance-based epistemic uncertainty collapses to zero. Set at least one dropout > 0 during training, or pass input_dropout= and/or tree_dropout= to predict_uncertainty() for a temporary inference-time override.\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "predict_uncertainty (MC-dropout): input_dropout, tree_dropout, and relevant head dropout are all 0. MC-dropout repeats are deterministic, so variance-based epistemic uncertainty collapses to zero. Set at least one dropout > 0 during training, or pass input_dropout= and/or tree_dropout= to predict_uncertainty() for a temporary inference-time override.\n",
+ "predict_uncertainty (MC-dropout): input_dropout, tree_dropout, and relevant head dropout are all 0. MC-dropout repeats are deterministic, so variance-based epistemic uncertainty collapses to zero. Set at least one dropout > 0 during training, or pass input_dropout= and/or tree_dropout= to predict_uncertainty() for a temporary inference-time override.\n",
+ "predict_uncertainty (MC-dropout): input_dropout, tree_dropout, and relevant head dropout are all 0. MC-dropout repeats are deterministic, so variance-based epistemic uncertainty collapses to zero. Set at least one dropout > 0 during training, or pass input_dropout= and/or tree_dropout= to predict_uncertainty() for a temporary inference-time override.\n",
+ "predict_uncertainty (MC-dropout): input_dropout, tree_dropout, and relevant head dropout are all 0. MC-dropout repeats are deterministic, so variance-based epistemic uncertainty collapses to zero. Set at least one dropout > 0 during training, or pass input_dropout= and/or tree_dropout= to predict_uncertainty() for a temporary inference-time override.\n",
+ "predict_uncertainty (MC-dropout): input_dropout, tree_dropout, and relevant head dropout are all 0. MC-dropout repeats are deterministic, so variance-based epistemic uncertainty collapses to zero. Set at least one dropout > 0 during training, or pass input_dropout= and/or tree_dropout= to predict_uncertainty() for a temporary inference-time override.\n",
+ "predict_uncertainty (MC-dropout): input_dropout, tree_dropout, and relevant head dropout are all 0. MC-dropout repeats are deterministic, so variance-based epistemic uncertainty collapses to zero. Set at least one dropout > 0 during training, or pass input_dropout= and/or tree_dropout= to predict_uncertainty() for a temporary inference-time override.\n",
+ "predict_uncertainty (MC-dropout): input_dropout, tree_dropout, and relevant head dropout are all 0. MC-dropout repeats are deterministic, so variance-based epistemic uncertainty collapses to zero. Set at least one dropout > 0 during training, or pass input_dropout= and/or tree_dropout= to predict_uncertainty() for a temporary inference-time override.\n",
+ "predict_uncertainty (MC-dropout): input_dropout, tree_dropout, and relevant head dropout are all 0. MC-dropout repeats are deterministic, so variance-based epistemic uncertainty collapses to zero. Set at least one dropout > 0 during training, or pass input_dropout= and/or tree_dropout= to predict_uncertainty() for a temporary inference-time override.\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Interpretation: these bars compare stochastic effect sizes, not model quality. Tree dropout is not expected to be largest because many trees provide redundant signals. The internal tree-dropout mode can affect deeper layers, while head-only tree dropout cannot.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Compare every dropout mechanism across shallow and deeper NODE models.\n",
+ "# This is intentionally small: it checks behavior, not final model quality.\n",
+ "import pandas as pd\n",
+ "\n",
+ "\n",
+ "def set_all_dropout_rates(\n",
+ " estimator,\n",
+ " *,\n",
+ " input_rate=0.0,\n",
+ " tree_rate=0.0,\n",
+ " mlp_rate=0.0,\n",
+ " input_only=None,\n",
+ "):\n",
+ " \"\"\"Set estimator, top module, dense blocks, and MLP dropout consistently.\"\"\"\n",
+ " module = estimator.module_\n",
+ " estimator.input_dropout = input_rate\n",
+ " module.input_dropout = input_rate\n",
+ " module.tree_dropout = tree_rate\n",
+ " module.mlp_dropout = mlp_rate\n",
+ "\n",
+ " if input_only is not None:\n",
+ " estimator.input_dropout_only_input = input_only\n",
+ " module.input_dropout_only_input = input_only\n",
+ "\n",
+ " for submodule in module.modules():\n",
+ " if isinstance(submodule, DenseODSTBlock):\n",
+ " submodule.input_dropout = input_rate\n",
+ " submodule.tree_dropout = tree_rate\n",
+ " if input_only is not None:\n",
+ " submodule.input_dropout_only_input = input_only\n",
+ " elif isinstance(submodule, nn.Dropout):\n",
+ " submodule.p = mlp_rate\n",
+ "\n",
+ "\n",
+ "def measure_dropout_case(num_layers, mechanism, *, tree_dropout_only_head=True):\n",
+ " torch.manual_seed(RANDOM_STATE)\n",
+ " model = NODERegressor(\n",
+ " num_trees=16,\n",
+ " depth=3,\n",
+ " num_layers=num_layers,\n",
+ " max_layers_retained=1 if num_layers > 1 else None,\n",
+ " head_type=\"mlp\",\n",
+ " input_dropout=0.1,\n",
+ " tree_dropout=0.1,\n",
+ " tree_dropout_only_head=tree_dropout_only_head,\n",
+ " mlp_dropout=0.1,\n",
+ " max_epochs=3,\n",
+ " lr=0.01,\n",
+ " batch_size=64,\n",
+ " device=\"cpu\",\n",
+ " verbose=0,\n",
+ " )\n",
+ " model.fit(X_train[:160], y_train[:160])\n",
+ "\n",
+ " # First verify the deterministic reference, then activate exactly one path.\n",
+ " set_all_dropout_rates(model)\n",
+ " zero_std = float(np.mean(model._predict_uncertainty_mc_dropout(X_test[:40], num_samples=12)))\n",
+ "\n",
+ " rates = {\"input_rate\": 0.0, \"tree_rate\": 0.0, \"mlp_rate\": 0.0}\n",
+ " input_only = None\n",
+ " if mechanism == \"input_dropout_only_input\":\n",
+ " rates[\"input_rate\"] = 0.3\n",
+ " input_only = True\n",
+ " elif mechanism == \"input_dropout_dense\":\n",
+ " rates[\"input_rate\"] = 0.3\n",
+ " input_only = False\n",
+ " elif mechanism.startswith(\"tree_dropout\"):\n",
+ " rates[\"tree_rate\"] = 0.3\n",
+ " elif mechanism == \"mlp_dropout\":\n",
+ " rates[\"mlp_rate\"] = 0.3\n",
+ " set_all_dropout_rates(model, input_only=input_only, **rates)\n",
+ " active_std = float(np.mean(model._predict_uncertainty_mc_dropout(X_test[:40], num_samples=12)))\n",
+ "\n",
+ " return zero_std, active_std\n",
+ "\n",
+ "\n",
+ "comparison_rows = []\n",
+ "mechanism_options = [\n",
+ " (\"input_dropout_only_input\", True),\n",
+ " (\"input_dropout_dense\", True),\n",
+ " (\"tree_dropout_head\", True),\n",
+ " (\"tree_dropout_internal\", False),\n",
+ " (\"mlp_dropout\", True),\n",
+ "]\n",
+ "\n",
+ "for num_layers in [1, 3]:\n",
+ " for mechanism, tree_dropout_only_head in mechanism_options:\n",
+ " zero_std, active_std = measure_dropout_case(\n",
+ " num_layers,\n",
+ " mechanism,\n",
+ " tree_dropout_only_head=tree_dropout_only_head,\n",
+ " )\n",
+ " comparison_rows.append(\n",
+ " {\n",
+ " \"num_layers\": num_layers,\n",
+ " \"mechanism\": mechanism,\n",
+ " \"zero_dropout_std\": zero_std,\n",
+ " \"active_dropout_std\": active_std,\n",
+ " }\n",
+ " )\n",
+ "\n",
+ "comparison = pd.DataFrame(comparison_rows)\n",
+ "display(comparison.round(4))\n",
+ "\n",
+ "# Expected invariants: no masks means deterministic MC passes; active masks create variation.\n",
+ "assert comparison[\"zero_dropout_std\"].max() < 1e-6\n",
+ "assert (comparison[\"active_dropout_std\"] > 1e-6).all()\n",
+ "\n",
+ "fig, axes = plt.subplots(1, 2, figsize=(14, 5), sharey=True)\n",
+ "for axis, num_layers in zip(axes, [1, 3]):\n",
+ " subset = comparison[comparison[\"num_layers\"] == num_layers]\n",
+ " axis.bar(\n",
+ " subset[\"mechanism\"],\n",
+ " subset[\"active_dropout_std\"],\n",
+ " color=[\"#2563eb\", \"#60a5fa\", \"#f97316\", \"#c2410c\", \"#16a34a\"],\n",
+ " )\n",
+ " axis.set_title(f\"{num_layers} NODE layer{'s' if num_layers > 1 else ''}\")\n",
+ " axis.set_ylabel(\"mean MC-dropout standard deviation\")\n",
+ " axis.tick_params(axis=\"x\", rotation=35)\n",
+ " axis.grid(axis=\"y\", alpha=0.25)\n",
+ "\n",
+ "fig.suptitle(\"Dropout comparison across NODE depth\")\n",
+ "plt.tight_layout()\n",
+ "plt.show()\n",
+ "\n",
+ "print(\n",
+ " \"Interpretation: these bars compare stochastic effect sizes, not model quality. \"\n",
+ " \"Tree dropout is not expected to be largest because many trees provide redundant signals. \"\n",
+ " \"The internal tree-dropout mode can affect deeper layers, while head-only tree dropout cannot.\"\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8d5647e5",
+ "metadata": {
+ "id": "cell-26",
+ "language": "markdown"
+ },
+ "source": [
+ "### 6.2 Flow-based uncertainty"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "id": "37616b30",
+ "metadata": {
+ "id": "cell-27",
+ "language": "markdown"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " pred mean_predictions knowledge_uncertainty data_uncertainty \\\n",
+ "0 -0.146884 -0.146884 0.441380 0.251797 \n",
+ "1 -1.055259 -1.055259 0.204571 0.330905 \n",
+ "2 0.296962 0.296962 0.364480 0.197141 \n",
+ "3 0.956501 0.956501 0.451994 0.054674 \n",
+ "4 -0.116013 -0.116013 0.388588 0.235590 \n",
+ "\n",
+ " total_uncertainty \n",
+ "0 0.693177 \n",
+ "1 0.535477 \n",
+ "2 0.561620 \n",
+ "3 0.506669 \n",
+ "4 0.624178 \n",
+ "\n",
+ "Data uncertainty available: True\n",
+ "Knowledge uncertainty available: True\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Re-use flow_model from Section 5 (already trained with input_dropout=0.1)\n",
+ "df_flow_unc = flow_model.predict_uncertainty(\n",
+ " X_test,\n",
+ " num_samples=50,\n",
+ " return_quantiles=True,\n",
+ " quantiles=[0.025, 0.5, 0.975],\n",
+ ")\n",
+ "\n",
+ "# predict_uncertainty with return_quantiles returns a tuple: (DataFrame, quantile_array)\n",
+ "if isinstance(df_flow_unc, tuple):\n",
+ " df_flow, quantiles_arr = df_flow_unc\n",
+ "else:\n",
+ " df_flow = df_flow_unc\n",
+ " quantiles_arr = None\n",
+ "\n",
+ "print(df_flow.head())\n",
+ "print(f\"\\nData uncertainty available: {df_flow['data_uncertainty'].notna().any()}\")\n",
+ "print(f\"Knowledge uncertainty available: {df_flow['knowledge_uncertainty'].notna().any()}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ad90ba01",
+ "metadata": {
+ "id": "cell-28",
+ "language": "markdown"
+ },
+ "source": [
+ "### 6.3 Combined uncertainty decomposition (flow + dropout)\n",
+ "\n",
+ "When using a **flow head** with **dropout > 0**, `predict_with_combined_uncertainty()`\n",
+ "decomposes total uncertainty into:\n",
+ "\n",
+ "- **Data (aleatoric)** β irreducible noise in the data\n",
+ "- **Knowledge (epistemic)** β model uncertainty, reducible with more data\n",
+ "\n",
+ "Each dropout pass yields a whole density $p_t(y\\mid x)$ for the *same* $x$ (dropout\n",
+ "perturbs the weights, so each pass is one plausible model). There are **two** ways to\n",
+ "summarise the $T$ densities, and the *gap* between them is the epistemic term:\n",
+ "\n",
+ "- **average the entropies** (`data`) β mean entropy across experts, reflecting the\n",
+ " shared intrinsic noise;\n",
+ "- **entropy of the average** (`total`) β pool the densities into a mixture\n",
+ " $\\bar p=\\tfrac1T\\sum_t p_t$, then measure *its* width (wide if the passes are wide\n",
+ " **or** disagree about where $y$ sits).\n",
+ "\n",
+ "```\n",
+ " ββ MC pass 1 (mask ΞΈβ) ββΊ pβ(y|x) ββΊ H[pβ]\n",
+ " β\n",
+ " X βββββββββββββββΌβ MC pass 2 (mask ΞΈβ) ββΊ pβ(y|x) ββΊ H[pβ]\n",
+ " β\n",
+ " ββ MC pass T (mask ΞΈβ) ββΊ pβ(y|x) ββΊ H[pβ]\n",
+ " β\n",
+ " data = (1/T) Ξ£β H[pβ] (expected entropy) βββ€\n",
+ " total = H[ (1/T) Ξ£β pβ ] (mixture entropy) βββ€\n",
+ " knowledge = total β data (mutual information) ββ\n",
+ "```\n",
+ "\n",
+ "If the passes **agree** (identical narrow bells) the mixture equals each pass, so\n",
+ "`total β data` and `knowledge β 0`. If they **disagree** (peaks at different\n",
+ "locations) the mixture is broad/multimodal, so `knowledge` is large β it captures\n",
+ "disagreement in *location and shape*, not just scalar spread.\n",
+ "\n",
+ "$$\n",
+ "\\text{data} = \\tfrac1T\\textstyle\\sum_t H[p_t], \\qquad\n",
+ "\\text{total} = H\\!\\big[\\tfrac1T\\textstyle\\sum_t p_t\\big], \\qquad\n",
+ "\\text{knowledge} = \\text{total} - \\text{data} \\; (\\ge 0).\n",
+ "$$\n",
+ "\n",
+ "> **Two kinds of \"samples\".** The decomposition is computed **per input $x$** and\n",
+ "> works on a single point; the internal draws $y_s\\sim p_t(\\cdot\\mid x)$ only\n",
+ "> *estimate* the entropy integrals for that one $x$ β they are not extra data points.\n",
+ ">\n",
+ "> **Sign caveat.** `data` and `total` are *differential* entropies (nats) and may be\n",
+ "> **negative** for peaked flows β a *negative* value simply means a very sharp,\n",
+ "> confident density (low aleatoric uncertainty), because a probability *density* can\n",
+ "> exceed 1. Only the mutual-information `knowledge` term is guaranteed $\\ge 0$.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "789101f5",
+ "metadata": {
+ "language": "markdown"
+ },
+ "source": [
+ "
\n",
+ "\n",
+ "**Plain-language guide: entropy, nats, and βdifferentialβ entropy**\n",
+ "\n",
+ "Entropy is a measure of spread or surprise. A prediction concentrated in a narrow range has low uncertainty; a prediction spread across many possible values has high uncertainty. **Nats** are simply the measurement unit used when the calculation uses the natural logarithm, just as centimetres are a unit for distance.\n",
+ "\n",
+ "For a continuous value such as a chemical property, we use **differential entropy** instead of the discrete entropy used for class labels. It describes the shape and width of a probability density, not a count of equally likely options. Its number can be negative when a density is very sharply concentrated. That is not an error and does not mean βnegative uncertaintyβ; it means the density is narrower than the reference scale. The useful comparison is between models or inputs on the same scale.\n",
+ "\n",
+ "In the decomposition below, **data uncertainty** means noise that remains even with a perfect model, while **knowledge uncertainty** means that plausible models disagree. More data can often reduce the second kind.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "#### Dropout placement and dense-layer memory\n",
+ "\n",
+ "Dropout is not one single switch in NODE. The three rates hide information at three different stages, while the two `*_only_*` options decide how far a mask travels. During ordinary training, masks change from batch to batch. During MC-dropout uncertainty estimation, the model deliberately repeats predictions with different masks so the spread reveals how sensitive the prediction is.\n",
+ "\n",
+ "```text\n",
+ " INPUT FEATURES\n",
+ " β\n",
+ " input_dropout β feature-wise mask\n",
+ " only_input? βΌ\n",
+ " [original X or masked X]\n",
+ " β\n",
+ " βββββββββββββ΄ββββββββββββ\n",
+ " β β\n",
+ " ODST layer 1 ODST layer 2 ...\n",
+ " many soft trees many soft trees\n",
+ " β β\n",
+ " tree mask? tree mask?\n",
+ " if only_head=False if only_head=False\n",
+ " βββββββββββββ¬ββββββββββββ\n",
+ " β\n",
+ " tree_dropout (if only_head=True)\n",
+ " masks complete trees here\n",
+ " β\n",
+ " TREE REPRESENTATION\n",
+ " β\n",
+ " MLP head: Linear β activation\n",
+ " β\n",
+ " mlp_dropout mask\n",
+ " β\n",
+ " PREDICTION\n",
+ "```\n",
+ "\n",
+ "
\n",
+ "\n",
+ "##### Input dropout: `input_dropout` and `input_dropout_only_input`\n",
+ "\n",
+ "| Setting | What it means | When it is useful |\n",
+ "|---|---|---|\n",
+ "| `input_dropout=0.0` | No input features are hidden | Deterministic training at this stage; useful as a baseline. |\n",
+ "| `input_dropout=p` | Each selected input feature is randomly hidden with probability `p` and rescaled when kept | Reduces reliance on a small set of features and can create MC-dropout variation. Small values such as 0.05β0.15 are a sensible starting range. |\n",
+ "| `input_dropout_only_input=True` | Mask only the original input features entering the first representation | Targeted feature regularisation. This is the safer choice when you want to perturb raw inputs without repeatedly masking learned tree outputs. |\n",
+ "| `input_dropout_only_input=False` | Mask original features **and** feature channels coming from earlier ODST layers | Stronger regularisation in a multi-layer dense block. It makes later layers work even when some earlier representations are unavailable. |\n",
+ "\n",
+ "```text\n",
+ "input_dropout_only_input=True\n",
+ "\n",
+ " X ββ[mask X]βββΊ Layer 1 βββΊ Layer 2 βββΊ Layer 3 βββΊ head\n",
+ " β² β² β²\n",
+ " no new no new no new\n",
+ " input input input\n",
+ " mask mask mask\n",
+ "\n",
+ "input_dropout_only_input=False\n",
+ "\n",
+ " X ββ[mask]βββΊ Layer 1 ββ[mask X + h1]βββΊ Layer 2 ββ[mask X + h1 + h2]βββΊ head\n",
+ " h1 h2\n",
+ "```\n",
+ "\n",
+ "The second setting is not βmore input dropoutβ in a simple numeric sense: it changes *which representation channels* can disappear. For one-layer NODE the distinction is usually small; for multiple layers it changes the training signal substantially.\n",
+ "\n",
+ "##### Tree dropout: `tree_dropout` and `tree_dropout_only_head`\n",
+ "\n",
+ "Tree dropout removes complete trees, not individual values inside a tree. A dropped tree contributes no output for that training or MC pass, while the remaining trees are rescaled so the average signal stays comparable.\n",
+ "\n",
+ "| Setting | What it means | Trade-off |\n",
+ "|---|---|---|\n",
+ "| `tree_dropout=0.0` | Keep every tree | No tree-level regularisation or tree-level MC variation. |\n",
+ "| `tree_dropout=p` | Drop each whole tree with probability `p` | Prevents the head from depending too strongly on a few trees. Values around 0.02β0.1 are conservative; high values can remove too much capacity. |\n",
+ "| `tree_dropout_only_head=True` | Apply the tree mask once to the final representation immediately before the prediction head | Local, conservative regularisation. Earlier ODST layers see their complete outputs. This is the default. |\n",
+ "| `tree_dropout_only_head=False` | Apply tree dropout to each ODST layer output as it is produced | Stronger regularisation. Later layers must learn from incomplete earlier tree representations, which can help deep models but may be noisy for small datasets. |\n",
+ "\n",
+ "```text\n",
+ " tree_dropout_only_head=True\n",
+ "\n",
+ " Layer 1: [T1 T2 T3 T4] βββΊ Layer 2: [T5 T6 T7 T8]\n",
+ " β\n",
+ " final mask: [ 1 0 1 1 0 1 1 1 ]\n",
+ " β\n",
+ " MLP / subset / flow head\n",
+ "\n",
+ " tree_dropout_only_head=False\n",
+ "\n",
+ " Layer 1: [T1 T2 T3 T4] ββmaskβββΊ [T1 0 T3 T4]\n",
+ " β\n",
+ " Layer 2 receives incomplete representation and builds new trees\n",
+ " β\n",
+ " Layer 2 output βββββββββββββββmaskβββΊ final tree representation\n",
+ " β\n",
+ " βΌ\n",
+ " prediction head\n",
+ "```\n",
+ "\n",
+ "Use `tree_dropout_only_head=True` when you want mild regularisation or stable debugging. Consider `False` when there are several NODE layers and validation performance suggests the intermediate representations are over-specialised.\n",
+ "\n",
+ "##### How input, tree, and MLP dropout combine\n",
+ "\n",
+ "The masks act at different locations, so they are complementary rather than three copies of the same operation:\n",
+ "\n",
+ "```text\n",
+ " one training / MC pass\n",
+ "\n",
+ " X βββΊ [input feature mask] βββΊ NODE tree blocks βββΊ [whole-tree mask]\n",
+ " input_dropout tree_dropout\n",
+ " β\n",
+ " βΌ\n",
+ " MLP head: Linear β activation\n",
+ " β\n",
+ " [hidden-unit mask]\n",
+ " mlp_dropout\n",
+ " β\n",
+ " βΌ\n",
+ " prediction\n",
+ "\n",
+ " Across repeated MC passes:\n",
+ " X mask changes tree mask changes MLP mask changes\n",
+ " \\ | /\n",
+ " βββββββββββββ different plausible subnetworks βββββββββββββ\n",
+ " β\n",
+ " βΌ\n",
+ " spread of predictions = knowledge signal\n",
+ "```\n",
+ "\n",
+ "For `head_type=\"mlp\"`, `mlp_dropout` is the only one of the three that acts *inside* the MLP head. `input_dropout` and `tree_dropout` perturb what the MLP receives; `mlp_dropout` perturbs how the MLP transforms that received representation. With `head_type=\"subset\"`, `linear`, or `flow`, `mlp_dropout` is not used, but input and tree dropout still can be used.\n",
+ "\n",
+ "A practical progression is:\n",
+ "\n",
+ "```text\n",
+ "1. No dropout : understand baseline fit\n",
+ "2. input_dropout : test feature-level robustness\n",
+ "3. tree_dropout : test ensemble-level robustness\n",
+ "4. mlp_dropout (MLP head) : regularise the prediction head\n",
+ "5. combine small rates : only if validation and calibration support it\n",
+ "```\n",
+ "\n",
+ "Avoid turning all rates up at once. If every mask is strong, the model may spend most of training reconstructing information that was removed rather than learning the task.\n",
+ "\n",
+ "##### Previous-layer retention: `max_layers_retained`\n",
+ "\n",
+ "NODE layers use dense connections: a later layer can receive the original input and outputs from earlier layers. `max_layers_retained` limits how many of the most recent earlier layers are kept in that connection.\n",
+ "\n",
+ "```text\n",
+ "max_layers_retained=None (all previous layers)\n",
+ "\n",
+ " Layer 1: X ββββββββββββββββΊ h1\n",
+ " Layer 2: X + h1 ββββββββββββΊ h2\n",
+ " Layer 3: X + h1 + h2 βββββββΊ h3\n",
+ " Layer 4: X + h1 + h2 + h3 ββΊ h4\n",
+ "\n",
+ "max_layers_retained=1 (only the immediately previous layer)\n",
+ "\n",
+ " Layer 1: X ββββββββββββββββΊ h1\n",
+ " Layer 2: X + h1 ββββββββββββΊ h2\n",
+ " Layer 3: X + h2 ββββββββββββΊ h3\n",
+ " Layer 4: X + h3 ββββββββββββΊ h4\n",
+ "\n",
+ "max_layers_retained=2 (the two most recent layers)\n",
+ "\n",
+ " Layer 1: X ββββββββββββββββΊ h1\n",
+ " Layer 2: X + h1 ββββββββββββΊ h2\n",
+ " Layer 3: X + h1 + h2 βββββββΊ h3\n",
+ " Layer 4: X + h2 + h3 βββββββΊ h4\n",
+ "```\n",
+ "\n",
+ "`None` gives the richest skip connections but grows the amount of information each layer receives. `1` keeps memory and computation more controlled and behaves more like a short chain. Values between 1 and `num_layers - 1` provide a compromise. This parameter matters only when `num_layers > 1`; with one layer there are no previous layers to retain.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "**Rule of thumb:** begin with `input_dropout_only_input=True`, `tree_dropout_only_head=True`, and `max_layers_retained=None` for a small or shallow model. For deeper models, try `max_layers_retained=1` if memory grows or later layers become too expensive. Change the dropout placement switches only when you have a validation-based reason to use stronger regularisation.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "**Morgan fingerprints: local structural clues**\n",
+ "\n",
+ "A Morgan fingerprint, also called an ECFP-style fingerprint, describes which small circular neighborhoods occur in a molecule. Each bit answers a question such as βdoes this kind of atom environment appear somewhere?β The result is usually a long vector of zeros and ones.\n",
+ "\n",
+ "Morgan fingerprints are good at recognising local substructures and chemical similarity. They are less direct at expressing smooth numeric trends, and two molecules can have similar chemistry while differing in many individual bits. NODE is a good fit because its soft tree splits can select useful groups of bits and combine them into rules such as βthis motif is present together with that motif.β\n",
+ "\n",
+ "
\n",
+ "\n",
+ "
\n",
+ "\n",
+ "**Physicochemical descriptors: continuous property clues**\n",
+ "\n",
+ "Descriptors are numeric summaries of a molecule, such as molecular weight, logP, topological polar surface area, hydrogen-bond counts, ring counts, or flexibility. They provide a compact, human-readable view of properties that often change gradually across molecules.\n",
+ "\n",
+ "NODE handles these continuous columns naturally. Its learned thresholds can discover ranges and interactions, for example βhigher lipophilicity matters when polar surface area is low.β This is useful because the relationship between a descriptor and an outcome is often not a straight line: a property may help up to a point, then become harmful.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "
\n",
+ "\n",
+ "**CheMeleon fingerprints: learned global representations**\n",
+ "\n",
+ "CheMeleon fingerprints are learned molecular representations produced by a pretrained chemical model. Rather than manually counting motifs or calculating descriptors, the pretrained model has learned patterns from large-scale molecular data. The resulting vector can capture broader context and relationships that may be difficult to encode with a fixed fingerprint.\n",
+ "\n",
+ "These vectors are usually dense and continuous, with many dimensions carrying related information. NODE can act as a compact nonlinear prediction layer on top of them: the trees select useful regions of the representation, and the head maps those regions to the target property. The pretrained fingerprint supplies chemical knowledge; NODE adapts that knowledge to the specific supervised task.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "#### Why combine them?\n",
+ "\n",
+ "These representations answer different questions:\n",
+ "\n",
+ "| Representation | Main information | Typical limitation |\n",
+ "|---|---|---|\n",
+ "| Morgan fingerprint | Which local structural motifs are present? | Sparse, high-dimensional, and sensitive to the chosen radius and bit size |\n",
+ "| Physicochemical descriptors | What measurable global properties does the molecule have? | Hand-designed and unable to represent every structural pattern |\n",
+ "| CheMeleon fingerprint | What broader chemical patterns has a pretrained model learned? | Dense, less directly interpretable, and dependent on the pretrained model |\n",
+ "\n",
+ "NODE is well suited to the combination because its input pipeline can accept a mixed feature table, while its differentiable tree blocks can learn nonlinear interactions between sparse binary bits and dense numeric vectors. For example, the model can learn that a CheMeleon signal matters only when a particular Morgan motif is present, or that the effect of a structural motif changes across a range of logP values.\n",
+ "\n",
+ "This is a **complementarity hypothesis**, not a guarantee that adding every representation will improve results. More features can add noise, redundancy, memory use, and overfitting. Compare at least these baselines on the same train/validation split:\n",
+ "\n",
+ "1. Morgan fingerprints only.\n",
+ "2. Physicochemical descriptors only.\n",
+ "3. CheMeleon fingerprints only.\n",
+ "4. A concatenation of the representations.\n",
+ "\n",
+ "Keep preprocessing and evaluation identical. If the combined representation wins consistently on held-out data, that is evidence that the representations contribute useful information beyond one another. Use `cat_features` only for genuinely categorical columns; fingerprints and numeric descriptors should normally be passed as numeric features.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "**Practical warning about leakage**\n",
+ "\n",
+ "Compute fingerprints and descriptors using only information available at prediction time. If a descriptor or pretrained embedding was generated using the target, future assay results, or molecules from the validation/test split in a way that reveals their labels, the score will be overly optimistic. Fit any data-dependent scaling or feature selection on the training split only.\n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 60,
+ "id": "3441a8c9",
+ "metadata": {
+ "id": "cell-53",
+ "language": "markdown"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Install shap for explanations: pip install shap\n"
+ ]
+ }
+ ],
+ "source": [
+ "try:\n",
+ " import shap\n",
+ "\n",
+ " # Build a proper SHAP Explanation object from the GradientExplainer results\n",
+ " # (shap_values and X_test[:20] were computed in Β§12.1)\n",
+ " with torch.no_grad():\n",
+ " base_val = float(node_shap.module_(torch.tensor(X_train[:50], dtype=torch.float32)).cpu().numpy().mean())\n",
+ "\n",
+ " feature_names = [f\"Feature {i}\" for i in range(X_test.shape[1])]\n",
+ " explanation = shap.Explanation(\n",
+ " values=shap_values, # (20, 10)\n",
+ " base_values=np.full(20, base_val),\n",
+ " data=X_test[:20],\n",
+ " feature_names=feature_names,\n",
+ " )\n",
+ "\n",
+ " # --- 1. Beeswarm plot (density-aware dot plot) ---\n",
+ " fig, ax = plt.subplots(figsize=(10, 6))\n",
+ " shap.plots.beeswarm(explanation, show=False)\n",
+ " plt.title(\"Beeswarm β per-sample feature contributions\")\n",
+ " plt.tight_layout()\n",
+ " plt.show()\n",
+ "\n",
+ " # --- 2. Heatmap (sample Γ feature matrix) ---\n",
+ " fig, ax = plt.subplots(figsize=(12, 6))\n",
+ " shap.plots.heatmap(explanation, show=False)\n",
+ " plt.title(\"SHAP Heatmap β samples Γ features\")\n",
+ " plt.tight_layout()\n",
+ " plt.show()\n",
+ "\n",
+ " # --- 3. Dependence plot for the most important feature ---\n",
+ " top_feature = int(np.abs(shap_values).mean(axis=0).argmax())\n",
+ " fig, ax = plt.subplots(figsize=(8, 5))\n",
+ " shap.dependence_plot(\n",
+ " top_feature,\n",
+ " shap_values,\n",
+ " X_test[:20],\n",
+ " feature_names=feature_names,\n",
+ " ax=ax,\n",
+ " show=False,\n",
+ " )\n",
+ " ax.set_title(f\"Dependence β {feature_names[top_feature]} (strongest interaction colour)\")\n",
+ " plt.tight_layout()\n",
+ " plt.show()\n",
+ "\n",
+ " print(\"β All SHAP visualizations rendered successfully.\")\n",
+ "\n",
+ "except ImportError:\n",
+ " print(\"Install shap for explanations: pip install shap\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a02677d9",
+ "metadata": {
+ "id": "cell-54",
+ "language": "markdown"
+ },
+ "source": [
+ "---\n",
+ "## 12 Advanced Topics\n",
+ "\n",
+ "### 12.1 DataFrame & Categorical Support\n",
+ "\n",
+ "NODE natively accepts **pandas DataFrames**. Categorical columns must be **declared explicitly**\n",
+ "via the `cat_features` argument (mirroring CatBoost's `cat_features`) β there is **no automatic\n",
+ "dtype-based detection**. Any non-numeric column that is *not* listed in `cat_features` raises a\n",
+ "`ValueError`, so the contract is always explicit:\n",
+ "\n",
+ "```python\n",
+ "import pandas as pd\n",
+ "\n",
+ "df = pd.DataFrame({\n",
+ " \"mass\": [150.0, 220.0, 310.0],\n",
+ " \"logP\": [2.1, 3.5, 1.2],\n",
+ " \"scaffold\": [\"benzene\", \"pyridine\", \"benzene\"],\n",
+ "})\n",
+ "\n",
+ "reg = NODERegressor(cat_features=[\"scaffold\"]) # declare categoricals explicitly\n",
+ "reg.fit(df, y) # scaffold label-encoded + embedded\n",
+ "preds = reg.predict(df)\n",
+ "```\n",
+ "\n",
+ "The `InputOutputShapeSetter` callback then handles:\n",
+ "- Input dimension from `X.shape[1]`\n",
+ "- Applying the declared `cat_features` as categorical columns\n",
+ "- Label encoding and embedding-dimension calculation\n",
+ "\n",
+ "### 12.2 Embeddings β Downstream Models\n",
+ "\n",
+ "Use NODE's tree layers as a **feature extractor**, then feed the learned embeddings into any downstream model:\n",
+ "\n",
+ "```python\n",
+ "# 1. Train NODE\n",
+ "node = NODERegressor(num_trees=2048, max_epochs=100, device=\"cpu\")\n",
+ "node.fit(X_train, y_train)\n",
+ "\n",
+ "# 2. Extract embeddings\n",
+ "emb_train = node.get_embeddings(X_train)\n",
+ "emb_test = node.get_embeddings(X_test)\n",
+ "\n",
+ "# 3. Feed into a flow head for probabilistic output\n",
+ "flow = NODERegressor(head_type=\"flow\", flow_type=\"NSF\", max_epochs=200)\n",
+ "flow.fit(emb_train, y_train)\n",
+ "samples = flow.predict_flow_head(emb_test, num_samples=500, return_sample_distribution=True)\n",
+ "```\n",
+ "\n",
+ "This two-stage approach lets you decouple representation learning from the prediction head,\n",
+ "or reuse expensive embeddings with multiple models.\n",
+ "\n",
+ "### 12.3 Skorch Integration (Validation + Early Stopping Setup)\n",
+ "\n",
+ "NODE estimators are built on [skorch](https://github.com/skorch-dev/skorch),\n",
+ "so all skorch features work out of the box β custom callbacks, learning-rate\n",
+ "schedulers, validation splits, etc.\n",
+ "\n",
+ "**Important default behavior:** `train_split=None` by default, which means:\n",
+ "- no validation subset is created,\n",
+ "- no `valid_loss` is computed,\n",
+ "- no early stopping callback is auto-attached.\n",
+ "\n",
+ "To enable early stopping, set a validation split explicitly.\n",
+ "\n",
+ "```python\n",
+ "from skorch.dataset import ValidSplit\n",
+ "\n",
+ "reg = NODERegressor(\n",
+ " train_split=ValidSplit(cv=0.15, random_state=42),\n",
+ ")\n",
+ "```\n",
+ "\n",
+ "With a validation split active, NODE automatically adds:\n",
+ "- `EarlyStopping(patience=20, monitor=\"valid_loss\")`\n",
+ "\n",
+ "You can still override this with your own callback settings.\n",
+ "\n",
+ "```python\n",
+ "from skorch.dataset import ValidSplit\n",
+ "from skorch.callbacks import EarlyStopping, LRScheduler\n",
+ "\n",
+ "reg = NODERegressor(\n",
+ " train_split=ValidSplit(cv=0.15, random_state=42),\n",
+ " callbacks=[\n",
+ " EarlyStopping(patience=10, monitor=\"valid_loss\"),\n",
+ " LRScheduler(policy=\"CosineAnnealingLR\", T_max=50),\n",
+ " ],\n",
+ ")\n",
+ "```\n",
+ "\n",
+ "#### Activating adaptive learning rate\n",
+ "\n",
+ "By default, NODE uses a **fixed** learning rate (`lr=...`) unless you add a scheduler callback.\n",
+ "For adaptive LR based on validation progress, use `LRScheduler(policy=\"ReduceLROnPlateau\")`\n",
+ "with `monitor=\"valid_loss\"` and an explicit validation split.\n",
+ "\n",
+ "```python\n",
+ "from skorch.dataset import ValidSplit\n",
+ "from skorch.callbacks import EarlyStopping, LRScheduler\n",
+ "\n",
+ "reg = NODERegressor(\n",
+ " lr=5e-3,\n",
+ " max_epochs=40,\n",
+ " train_split=ValidSplit(cv=0.15, random_state=42),\n",
+ " callbacks=[\n",
+ " LRScheduler(\n",
+ " policy=\"ReduceLROnPlateau\",\n",
+ " monitor=\"valid_loss\",\n",
+ " factor=0.5,\n",
+ " patience=5,\n",
+ " min_lr=1e-5,\n",
+ " ),\n",
+ " EarlyStopping(patience=12, monitor=\"valid_loss\"),\n",
+ " ],\n",
+ ")\n",
+ "```\n",
+ "\n",
+ "The same callback pattern works for classification as well.\n",
+ "\n",
+ "```python\n",
+ "from skorch.dataset import ValidSplit\n",
+ "from skorch.callbacks import LRScheduler\n",
+ "\n",
+ "clf = NODEClassifier(\n",
+ " train_split=ValidSplit(cv=0.2, random_state=42),\n",
+ " callbacks=[\n",
+ " LRScheduler(\n",
+ " policy=\"ReduceLROnPlateau\",\n",
+ " monitor=\"valid_loss\",\n",
+ " factor=0.5,\n",
+ " patience=4,\n",
+ " min_lr=1e-5,\n",
+ " )\n",
+ " ],\n",
+ ")\n",
+ "```\n",
+ "\n",
+ "### 12.4 Device Management\n",
+ "\n",
+ "```python\n",
+ "# Auto-detect GPU (uses CUDA if available, else CPU)\n",
+ "reg = NODERegressor()\n",
+ "\n",
+ "# Force CPU (useful for CI / small models)\n",
+ "reg = NODERegressor(device=\"cpu\")\n",
+ "\n",
+ "# Specify a particular GPU\n",
+ "reg = NODERegressor(device=\"cuda:0\")\n",
+ "```\n",
+ "\n",
+ "### 12.5 MC Dropout β Implementation Details\n",
+ "\n",
+ "NODE's MC-dropout is deliberately *surgical* about which modules become stochastic at inference:\n",
+ "\n",
+ "1. **The model stays in `eval()` mode.** This is the crucial subtlety β it means every\n",
+ " `BatchNorm1d` layer keeps using its **frozen running statistics** and its running stats are\n",
+ " **never updated** by the extra forward passes. Only the dropout paths inject randomness, so the\n",
+ " MC variance reflects genuine model uncertainty rather than shifting normalisation.\n",
+ "2. **Dropout is re-enabled by directly flipping `.training = True`** (not by calling `.train()`) on\n",
+ " exactly three module types: the top NODE module (for `tree_dropout`), each `DenseODSTBlock` (for\n",
+ " `input_dropout`), and every `nn.Dropout` in the MLP head (for `mlp_dropout`). Everything else β\n",
+ " including BatchNorm β stays in eval mode.\n",
+ "3. **Automatic deterministic fallback.** If *all* dropout rates are 0, there is nothing to sample,\n",
+ " so `predict_uncertainty()` returns the deterministic prediction with zero variance instead of\n",
+ " wastefully running `num_samples` identical passes.\n",
+ "\n",
+ "The net effect: reliable epistemic estimates from a single trained model, **without retraining** and\n",
+ "**without corrupting the learned BatchNorm statistics**.\n",
+ "\n",
+ "### 12.6 Interface Compatibility with Other Mother Estimators\n",
+ "\n",
+ "NODE's `predict_uncertainty()` uses the same signature as TabPFN, RandomForest,\n",
+ "and CatBoost on the `ranker_update` branch:\n",
+ "\n",
+ "```python\n",
+ "# All Mother estimators share this interface:\n",
+ "results = model.predict_uncertainty(X)\n",
+ "# β DataFrame with: mean_predictions, knowledge_uncertainty,\n",
+ "# data_uncertainty, total_uncertainty\n",
+ "\n",
+ "results, q = model.predict_uncertainty(\n",
+ " X, return_quantiles=True, quantiles=[0.025, 0.5, 0.975],\n",
+ ")\n",
+ "# β (DataFrame, ndarray of shape (n_samples, n_quantiles))\n",
+ "\n",
+ "unc = model.predict_uncertainty(X, uncertainty_for_opt=True)\n",
+ "# β pd.Series of total_uncertainty (for optimisation loops)\n",
+ "```\n",
+ "\n",
+ "NODE additionally provides `predict_quantiles()` as a convenience shorthand and\n",
+ "`predict_with_combined_uncertainty()` for flow-head decomposition."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6b289ce4",
+ "metadata": {
+ "id": "cell-55",
+ "language": "markdown"
+ },
+ "source": [
+ "---\n",
+ "## 13 Dropout controls for regularisation and uncertainty\n",
+ "\n",
+ "NODE has three independent dropout probabilities. Each is a Bernoulli mask applied at a different stage:\n",
+ "\n",
+ "| Parameter | Masks | Typical role |\n",
+ "|---|---|---|\n",
+ "| `input_dropout` | Individual feature channels entering an ODST layer | Feature-level regularisation and MC uncertainty |\n",
+ "| `tree_dropout` | Whole tree channels, with one shared draw per tree | Tree-level regularisation and MC uncertainty |\n",
+ "| `mlp_dropout` | Units in the MLP prediction head | Head-level regularisation and MC uncertainty |\n",
+ "\n",
+ "At probability $p$, inverted dropout keeps an active value with probability $1-p$ and scales it by $1/(1-p)$, preserving its expectation:\n",
+ "\n",
+ "$$\n",
+ "\\tilde{h} = \\frac{m}{1-p}h, \\qquad m\\sim\\mathrm{Bernoulli}(1-p).\n",
+ "$$\n",
+ "\n",
+ "The two boolean placement controls refine where the first two masks act:\n",
+ "\n",
+ "- `input_dropout_only_input=True` limits feature dropout to original input features; `False` also masks dense between-layer inputs.\n",
+ "- `tree_dropout_only_head=True` masks the final tree representation once; `False` applies tree dropout within every ODST layer.\n",
+ "\n",
+ "For `num_layers > 1`, these choices determine whether intermediate representations are regularised. With all dropout probabilities set to zero, MC uncertainty is deterministic and has zero variance.\n",
+ "\n",
+ "### BALD decomposition\n",
+ "\n",
+ "When combining a flow head with dropout, each dropout pass is treated as an expert density $p_t(y\\mid x)$. Let the pooled density be\n",
+ "\n",
+ "$$\n",
+ "\\bar{p}(y\\mid x)=\\frac{1}{T}\\sum_{t=1}^{T}p_t(y\\mid x).\n",
+ "$$\n",
+ "\n",
+ "The implementation reports the expected expert entropy as data uncertainty and the pooled entropy as total uncertainty:\n",
+ "\n",
+ "$$\n",
+ "U_{\\mathrm{data}}=\\frac{1}{T}\\sum_{t=1}^{T}H[p_t],\n",
+ "\\qquad\n",
+ "U_{\\mathrm{total}}=H[\\bar{p}].\n",
+ "$$\n",
+ "\n",
+ "Their difference is the BALD mutual-information term:\n",
+ "\n",
+ "$$\n",
+ "U_{\\mathrm{knowledge}}=U_{\\mathrm{total}}-U_{\\mathrm{data}}\n",
+ "=H[\\bar{p}]-\\frac{1}{T}\\sum_{t=1}^{T}H[p_t]\\ge 0.\n",
+ "$$\n",
+ "\n",
+ "For continuous distributions, differential entropy can be negative. That is valid for a sharply concentrated density; the non-negative quantity is the BALD difference, not necessarily each entropy individually.\n",
+ "\n",
+ "### BALSA-EMD\n",
+ "\n",
+ "`predict_with_combined_uncertainty(..., knowledge_method=\"balsa_emd\")` replaces the BALD entropy gap with a sampled Earth Mover's Distance disagreement score between consecutive dropout experts. Because this score is not an entropy term, the implementation does not claim the additive identity $U_{\\mathrm{total}}=U_{\\mathrm{data}}+U_{\\mathrm{knowledge}}$ for `balsa_emd`. Use `knowledge_method=\"bald\"` when you need the additive entropy decomposition."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9aae1333",
+ "metadata": {},
+ "source": [
+ "
\n",
+ "\n",
+ "### Before the equations: what do these words mean?\n",
+ "\n",
+ "**Uncertainty** means that the model can see more than one plausible outcome. For example, a molecule might reasonably have a predicted activity somewhere between 2 and 4 rather than exactly 3.\n",
+ "\n",
+ "**Entropy** is a way of summarising how spread out those possibilities are. A narrow group of possibilities means less uncertainty; a wide group means more uncertainty. Entropy is not the prediction itself and it is not an accuracy score.\n",
+ "\n",
+ "**Nats** are only the unit used for entropy. They are like metres for distance or degrees for temperature. The word does not add another kind of uncertainty. Two entropy values can be compared when they use the same target scale and the same logarithm convention.\n",
+ "\n",
+ "**Differential entropy** is the version of entropy used for a continuous numeric outcome, such as solubility or a molecular property. Classification has a short list of separate choices, so ordinary entropy counts uncertainty across those choices. Regression has infinitely many possible numeric values, so differential entropy describes the width and shape of a continuous probability curve instead.\n",
+ "\n",
+ "A continuous probability curve is a **density**, not a list of probabilities assigned to individual exact numbers. A density can be greater than 1 because it describes probability per unit of measurement. That is why differential entropy can be negative. A negative differential entropy does **not** mean negative uncertainty or a bug; it usually means the predicted curve is very narrow on the current measurement scale.\n",
+ "\n",
+ "**Numerical precision** means that computers store numbers with a limited number of digits. A calculation that is mathematically zero might appear as `-0.0000001`, and a value that should be non-negative might appear as `-0.000001` because of rounding. βNon-negative up to numerical precisionβ means: treat tiny values around zero as zero, but investigate a clearly negative value.\n",
+ "\n",
+ "```text\n",
+ "wide predictive curve narrow predictive curve\n",
+ " ___ _____\n",
+ " / \\ / \\\n",
+ " ___/ \\___ __/ \\__\n",
+ "more possible outcomes fewer, more concentrated outcomes\n",
+ "higher spread/entropy lower spread; differential entropy may be negative\n",
+ "```\n",
+ "\n",
+ "For this notebook, the safe interpretation is:\n",
+ "\n",
+ "- `data_uncertainty`: uncertainty that belongs to the outcome itself, even if the model were perfect;\n",
+ "- `knowledge_uncertainty`: disagreement between plausible dropout versions of the model;\n",
+ "- `total_uncertainty`: the combined spread of the pooled predictions;\n",
+ "- a tiny negative value close to zero: usually floating-point rounding, not a meaningful negative uncertainty.\n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7fb27b941602401d91542211134fc71a",
+ "metadata": {
+ "id": "cell-56",
+ "language": "markdown"
+ },
+ "source": [
+ "---\n",
+ "## 14 Hyperparameter Tuning with MotherTuner\n",
+ "\n",
+ "NODE plugs directly into Mother's `MotherTuner` for automated hyperparameter optimisation via\n",
+ "Optuna. NODE ships its own `hyperparameter_space` (architecture, dropout, learning-rate and\n",
+ "head-specific ranges), so you do **not** write a suggestion function by hand β the space is derived\n",
+ "from the estimator inside the pipeline.\n",
+ "\n",
+ "Dropout policy used by default search space:\n",
+ "- keep conservative defaults for startup/enqueued trials (`input_dropout=0.05`, `tree_dropout=0.02`),\n",
+ "- keep `input_dropout` focused in a lower range,\n",
+ "- allow a wider `tree_dropout` tuning range up to `0.35` when stronger regularisation is helpful.\n",
+ "\n",
+ "The API follows the standard Mother pattern (identical to the `test_fast_mother_tuner` unit test):\n",
+ "\n",
+ "1. Wrap the estimator in a `PipelineWithHyperparameterRooting` so its search space and defaults are\n",
+ " discoverable.\n",
+ "2. Construct `MotherTuner(scorer=..., tuning_direction=..., n_trials_optuna=..., n_startup_trials=...)`\n",
+ " β note the **scorer and Optuna settings live on the tuner**, while the data and CV splitter are\n",
+ " passed to `optimize()`.\n",
+ "3. Call `tuner.optimize(estimator=pipeline, X=X_df, y=y_series, cross_validation=cv,\n",
+ " default_parameters=pipeline.default_parameters())`. It returns a **fitted, tuned pipeline**.\n",
+ "4. Inspect results via `tuner.study` (`.best_trial.params`, `.best_trial.value`).\n",
+ "\n",
+ "```python\n",
+ "import pandas as pd\n",
+ "from sklearn.model_selection import KFold\n",
+ "from mother.optimization import MotherTuner\n",
+ "from mother.ml import PipelineWithHyperparameterRooting\n",
+ "\n",
+ "pipeline = PipelineWithHyperparameterRooting(\n",
+ " [(\"regressor\", NODERegressor(max_epochs=50, device=\"cpu\"))]\n",
+ ")\n",
+ "\n",
+ "tuner = MotherTuner(\n",
+ " scorer=\"neg_mean_squared_error\", # sklearn scorer name or callable\n",
+ " tuning_direction=\"maximize\", # maximise the (negative) MSE\n",
+ " n_trials_optuna=30,\n",
+ " n_startup_trials=5, # first trial(s) evaluate the defaults\n",
+ ")\n",
+ "\n",
+ "best_pipeline = tuner.optimize(\n",
+ " estimator=pipeline,\n",
+ " X=X_df, # pandas DataFrame\n",
+ " y=y_series, # pandas Series\n",
+ " cross_validation=KFold(n_splits=3, shuffle=True, random_state=42),\n",
+ " default_parameters=pipeline.default_parameters(),\n",
+ ")\n",
+ "\n",
+ "print(f\"Best value : {tuner.study.best_trial.value:.4f}\")\n",
+ "print(f\"Best params: {tuner.study.best_trial.params}\")\n",
+ "\n",
+ "# `best_pipeline` is already refit β use it directly\n",
+ "preds = best_pipeline.predict(X_df)\n",
+ "```\n",
+ "\n",
+ "The cell below runs a **tiny** version (2 trials, 32 trees, 2-fold CV) so it finishes quickly.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4cda7e8e",
+ "metadata": {
+ "language": "markdown"
+ },
+ "source": [
+ "
\n",
+ "\n",
+ "### BALD and BALSA-EMD in plain language\n",
+ "\n",
+ "Both methods ask the same high-level question:\n",
+ "\n",
+ "> **If we make several plausible versions of the model, do they agree about this molecule or data row?**\n",
+ "\n",
+ "The versions are created by Monte-Carlo dropout. The weights are not retrained for every pass; instead, a different dropout mask temporarily hides different features, trees, or MLP units. Each pass is treated as one plausible **expert**.\n",
+ "\n",
+ "```text\n",
+ " same input x\n",
+ " β\n",
+ " ββββββββββββΌβββββββββββ\n",
+ " β β β\n",
+ " dropout 1 dropout 2 dropout T\n",
+ " β β β\n",
+ " expert p1 expert p2 expert pT\n",
+ " β β β\n",
+ " ββββββββββββ΄βββββββββββ\n",
+ " β\n",
+ " compare the predicted distributions\n",
+ " β\n",
+ " small disagreement β low knowledge uncertainty\n",
+ " large disagreement β high knowledge uncertainty\n",
+ "```\n",
+ "\n",
+ "**Important:** this is not the same as ordinary measurement noise. A flow may say that every expert predicts a broad range of outcomes, which is data uncertainty. Knowledge uncertainty appears when the experts disagree about the range, location, or shape of that distribution.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "#### BALD: compare entropy before and after pooling\n",
+ "\n",
+ "**BALD** stands for Bayesian Active Learning by Disagreement. For a flow head, each dropout pass produces a probability distribution $p_t(y \\mid x)$ rather than a single number. BALD compares:\n",
+ "\n",
+ "- **Each expert separately:** how uncertain is one model about the outcome?\n",
+ "- **All experts pooled together:** how uncertain are we after mixing their predictions?\n",
+ "\n",
+ "```text\n",
+ " expert 1: narrow distribution around 2.0 βββΊ entropy H[p1]\n",
+ " expert 2: narrow distribution around 2.0 βββΊ entropy H[p2]\n",
+ " expert 3: narrow distribution around 2.0 βββΊ entropy H[p3]\n",
+ " β\n",
+ " average the entropies ββββ\n",
+ " data uncertainty\n",
+ "\n",
+ " pooled mixture: still narrow around 2.0 ββββββββΊ entropy H[p_bar]\n",
+ "\n",
+ " total uncertainty - data uncertainty\n",
+ " H[p_bar] - average(H[p_t]) β 0\n",
+ "```\n",
+ "\n",
+ "When the experts disagree, the pooled mixture becomes wider or even multimodal:\n",
+ "\n",
+ "```text\n",
+ " expert 1: /\n",
+ " / \\ peak near 1\n",
+ " expert 2: /\\ peak near 4\n",
+ " expert 3: /\\ peak near 2\n",
+ " β\n",
+ " βΌ\n",
+ " pooled mixture: /\\ /\\ several possible locations\n",
+ " / \\_____/ \\\n",
+ "\n",
+ " each expert's own spread = data uncertainty\n",
+ " extra spread from disagreement = BALD knowledge uncertainty\n",
+ "```\n",
+ "\n",
+ "The calculation used by NODE is:\n",
+ "\n",
+ "$$\n",
+ "U_{\\mathrm{data}} = \\frac{1}{T}\\sum_{t=1}^{T} H[p_t],\n",
+ "\\qquad\n",
+ "U_{\\mathrm{total}} = H\\left[\\frac{1}{T}\\sum_{t=1}^{T}p_t\\right],\n",
+ "$$\n",
+ "\n",
+ "$$\n",
+ "U_{\\mathrm{knowledge}}^{\\mathrm{BALD}}\n",
+ "= U_{\\mathrm{total}} - U_{\\mathrm{data}}.\n",
+ "$$\n",
+ "\n",
+ "For classification, $H$ is ordinary Shannon entropy over class probabilities. For continuous flow outputs, $H$ is **differential entropy**, so individual data and total values can be negative. The BALD difference is the meaningful disagreement term and is non-negative up to numerical error.\n",
+ "\n",
+ "Use BALD when you want an information-theoretic, additive decomposition:\n",
+ "\n",
+ "```python\n",
+ "result = model.predict_with_combined_uncertainty(\n",
+ " X,\n",
+ " knowledge_method=\"bald\",\n",
+ ")\n",
+ "\n",
+ "# For the flow implementation:\n",
+ "# total_uncertainty β data_uncertainty + knowledge_uncertainty\n",
+ "```\n",
+ "\n",
+ "#### BALSA-EMD: compare samples directly\n",
+ "\n",
+ "**BALSA** is a family of Bayesian active-learning scores based on distribution disagreement. **EMD** means Earth Moverβs Distance, also called the Wasserstein-1 distance. The intuition is physical:\n",
+ "\n",
+ "> Imagine one expertβs predicted probability mass as a pile of sand and another expertβs prediction as a second pile. EMD measures the average amount of distance the sand must be moved to turn one pile into the other.\n",
+ "\n",
+ "```text\n",
+ " expert A samples: 1.0 1.8 2.0 2.2 3.0\n",
+ " β move the samples\n",
+ " βΌ\n",
+ " expert B samples: 3.0 3.8 4.0 4.2 5.0\n",
+ "\n",
+ " Every outcome shifted right by about 2\n",
+ " β EMD is about 2\n",
+ " β the experts strongly disagree\n",
+ "```\n",
+ "\n",
+ "If the samples overlap, little movement is needed:\n",
+ "\n",
+ "```text\n",
+ " expert A: 1.8 2.0 2.2 2.4\n",
+ " expert B: 1.9 2.1 2.2 2.5\n",
+ "\n",
+ " The piles almost overlap\n",
+ " β small EMD\n",
+ " β small knowledge disagreement\n",
+ "```\n",
+ "\n",
+ "NODEβs sampled BALSA-EMD workflow is:\n",
+ "\n",
+ "```text\n",
+ " for each input x:\n",
+ " dropout pass 1 β draw S outcomes from p1(y|x)\n",
+ " dropout pass 2 β draw S outcomes from p2(y|x)\n",
+ " compare the two sample sets with EMD\n",
+ "\n",
+ " dropout pass 2 β draw S outcomes from p2(y|x)\n",
+ " dropout pass 3 β draw S outcomes from p3(y|x)\n",
+ " compare the next pair with EMD\n",
+ "\n",
+ " ...\n",
+ " aggregate the consecutive-pair distances\n",
+ "```\n",
+ "\n",
+ "For one-dimensional regression, NODE sorts the samples from each pair and averages the absolute distance between corresponding sorted samples:\n",
+ "\n",
+ "$$\n",
+ "\\operatorname{EMD}(p_t,p_{t+1})\n",
+ "\\approx \\frac{1}{S}\\sum_{s=1}^{S}\n",
+ "\\left|y^{(s)}_{t,\\mathrm{sorted}} - y^{(s)}_{t+1,\\mathrm{sorted}}\\right|.\n",
+ "$$\n",
+ "\n",
+ "For multi-target outputs, the implementation projects samples onto several random directions, computes one-dimensional EMD on each projection, and averages the results. This is called a sliced Wasserstein approximation.\n",
+ "\n",
+ "```text\n",
+ "1-D target: sort samples β match by rank β average |difference|\n",
+ " [low ... high] [low ... high]\n",
+ "\n",
+ "multi-target: project onto direction 1 ββΊ 1-D EMD\n",
+ " project onto direction 2 ββΊ 1-D EMD\n",
+ " project onto direction 3 ββΊ 1-D EMD\n",
+ " β\n",
+ " βββΊ average projected distances\n",
+ "```\n",
+ "\n",
+ "The implementation compares consecutive pairs $(p_1,p_2), (p_2,p_3), \\ldots$ and normally sums their distances. With `reduction=\"mean\"`, it averages over the $T-1$ pairs instead.\n",
+ "\n",
+ "BALD is the standard entropy-based decomposition used above. BALSA-EMD is an alternative that ranks samples by how much the experts *disagree* (a distribution distance) rather than by entropy; the two use different units and are not interchangeable.\n",
+ "\n",
+ "#### BALD versus BALSA-EMD\n",
+ "\n",
+ "| Question | BALD | BALSA-EMD |\n",
+ "|---|---|---|\n",
+ "| What is compared? | Entropy of each density versus entropy of the pooled density | Direct distance between samples from expert densities |\n",
+ "| Main intuition | How much wider is the pooled prediction because experts disagree? | How far must one expertβs predicted outcomes move to match anotherβs? |\n",
+ "| Units | Entropy units, usually nats | Target-distance units, after any target scaling |\n",
+ "| Additive decomposition? | Yes: total = data + knowledge | No: EMD is not an entropy term |\n",
+ "| Sensitive to | Overall entropy and mixture broadening | Location, spread, and shape differences visible in samples |\n",
+ "| Best use | Reporting a principled uncertainty decomposition | Ranking active-learning candidates by distribution disagreement |\n",
+ "\n",
+ "```text\n",
+ "BALD: densities ββΊ entropies ββΊ subtract ββΊ knowledge score\n",
+ "BALSA-EMD: samples ββΊ sort/project ββΊ distances ββΊ knowledge score\n",
+ "```\n",
+ "\n",
+ "Choose `knowledge_method=\"bald\"` when you need `data_uncertainty`, `total_uncertainty`, and an additive entropy identity. Choose `knowledge_method=\"balsa_emd\"` when the main goal is to rank inputs where plausible experts predict meaningfully different outcomes. BALSA-EMD scores should not be added to the entropy columns, because they are measured in different units.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "**Practical settings:** BALSA-EMD needs enough MC passes and flow samples to estimate distances reliably. Increasing `num_mc_samples` gives more expert pairs; increasing `num_flow_samples` makes each empirical distribution smoother. Start with small values while developing, then increase them for final uncertainty ranking. Keep dropout modest, because very large dropout can create artificial disagreement rather than useful model uncertainty.\n",
+ "\n",
+ "