Nori-Causal
Nori-Causal is a ~30M-parameter (30,342,598) model that reads a single observational table and returns, for every ordered pair of columns, the probability of a direct causal edge between them. It is zero-shot: one forward pass per table, with no per-dataset fitting. It is trained entirely on synthetic data (simulated structural causal models).
In synthefy-nori it powers the causal view of
NoriInterpreter: next to each feature's importance for predicting the target, it reports
P(feature β target) and P(target β feature), so an important feature that is more likely an
effect (a proxy) of the target than a cause is visible.
- Documentation: https://docs.synthefy.com/nori/
- Repository: https://github.com/Synthefy/synthefy-nori
- Library:
pip install "synthefy-nori[interpretability]>=X.Y.Z" - Checkpoint:
noricausal_31m.pt(this repo) - Parameters: ~30M (30,342,598)
- License: Apache-2.0
Architecture
Each cell is embedded with the Nori input layer (RBF numeric embedding, two values per token, a regression target encoder). Six blocks alternate attention across rows and across columns; the rows are then pooled (mean and standard deviation per column), fifteen blocks of attention run across the columns, and a bilinear edge head scores every ordered pair. One column can sit in the target slot, which the model reads as the variable of interest; with no target, every column is an ordinary node. Tables longer than 2,048 rows are evenly subsampled.
Up to 25 features, the table is read in one target-aware pass. Wider tables are read as random 30-column subsets with the target in the target slot of every subset that contains it, plus extra subsets until every feature has been read with the target at least 3 times; each pair's score is averaged over the subsets that contain it.
Usage
pip install "synthefy-nori[interpretability]>=X.Y.Z"
Through the interpreter (regression targets):
from sklearn.datasets import load_diabetes
from synthefy_nori.interpretability.glassbox import NoriInterpreter
data = load_diabetes()
interp = NoriInterpreter(causal=True).fit(data.data, data.target, feature_names=list(data.feature_names))
interp.causal_ranking_ # per feature: p_cause, p_effect, target_coverage, importance
interp.causal_adjacency_ # (d+1, d+1) edge probabilities; the target is the last node
On its own, with or without a target:
from synthefy_nori.causal import NoriCausal
causal = NoriCausal().fit(X, y) # downloads these weights from the Hub on first use; y is optional
causal.adjacency_ # [i, j] = P(node i -> node j)
It uses a GPU when one is available and falls back to CPU. To run from a local copy of this
checkpoint, pass NoriCausal(checkpoint="path/to/noricausal_31m.pt") (or
NoriInterpreter(causal_checkpoint=...)), or set SYNTHEFY_NORICAUSAL_31M_CHECKPOINT.
Intended use & limitations
- Intended for ranking candidate causes and effects of a target in a tabular dataset, and for screening features whose importance may come from being a consequence of the target.
- Probabilities, not a graph. The output is edge probabilities; no cutoff is applied by default. A cutoff calibrated on synthetic graphs over- or under-shoots on real tables, so a decided graph is not part of this release.
- Regression targets only in
NoriInterpreter: class labels are not numeric values, and the training targets were almost always continuous. - Observational data, no guarantees. Edge direction is inferred from distributional patterns learned on synthetic SCMs; hidden confounders, near-duplicate (collinear) columns and deterministic relationships can mislead it. Treat the scores as evidence to check, not as causal conclusions.
- Trained entirely on synthetic data; no benchmark data is used in training.
License
- Downloads last month
- 27