Update README.md
Browse files
README.md
CHANGED
|
@@ -1,3 +1,163 @@
|
|
| 1 |
---
|
| 2 |
license: apache-2.0
|
| 3 |
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
license: apache-2.0
|
| 3 |
---
|
| 4 |
+
<p align="center">
|
| 5 |
+
<a href="https://arxiv.org/abs/2607.11508"><img src="https://img.shields.io/badge/arXiv-2607.11508-b31b1b.svg" alt="arXiv"></a>
|
| 6 |
+
<a href="https://huggingface.co/DMIRLAB/CDFM"><img src="https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-DMIRLAB%2FCDFM-ffbd45" alt="HuggingFace"></a>
|
| 7 |
+
<a href="https://github.com/DMIRLAB-Group/CDFM"><img src="https://img.shields.io/badge/GitHub-DMIRLAB--Group%2FCDFM-181717?logo=github" alt="GitHub"></a>
|
| 8 |
+
<img src="https://img.shields.io/badge/License-Apache_2.0-blue.svg" alt="License">
|
| 9 |
+
<img src="https://img.shields.io/badge/python-3.10+-blue" alt="Python">
|
| 10 |
+
</p>
|
| 11 |
+
|
| 12 |
+
# CDFM: Towards a General-Purpose Causal Discovery Foundation Model
|
| 13 |
+
|
| 14 |
+
Causal Discovery Foundation Model (CDFM) is a pretrained foundation model for zero-shot causal discovery. Given purely observational data `X (N, D)`, it predicts the causal graph `G (D, D)` in a single forward pass.
|
| 15 |
+
|
| 16 |
+
CDFM reframes causal discovery as a unified, general-purpose framework for zero-shot structural inference. By pretraining on a massive, highly diverse space of synthetic structural causal models, CDFM successfully internalizes complex statistical asymmetries.
|
| 17 |
+
|
| 18 |
+
<p align="center">
|
| 19 |
+
<img src="https://raw.githubusercontent.com/DMIRLAB-Group/CDFM/refs/heads/main/docs/figures/benchmark_overview.png" width="85%" style="display: block; margin: auto;" alt="CDFM benchmark overview">
|
| 20 |
+
</p>
|
| 21 |
+
|
| 22 |
+
- **State-of-the-art accuracy.** Outperforms all baselines across 15 mechanism families and on real-world benchmarks.
|
| 23 |
+
- **Zero-shot.** One pretrained checkpoint works for any N, any D.
|
| 24 |
+
- **Easy to use.** pip-installable, single `model.predict(data)` call.
|
| 25 |
+
|
| 26 |
+
---
|
| 27 |
+
|
| 28 |
+
## Installation
|
| 29 |
+
|
| 30 |
+
```bash
|
| 31 |
+
pip install cdfm-base
|
| 32 |
+
```
|
| 33 |
+
|
| 34 |
+
Requirements: `torch>=2.0`, `numpy>=1.20`, `safetensors`, `networkx`, `huggingface_hub`.
|
| 35 |
+
|
| 36 |
+
---
|
| 37 |
+
|
| 38 |
+
## Usage
|
| 39 |
+
|
| 40 |
+
### 1. Causal Discovery
|
| 41 |
+
|
| 42 |
+
The simplest way to use CDFM is to load the model and pass your observational data directly. By default, CDFM automatically calibrates the threshold for edge prediction.
|
| 43 |
+
|
| 44 |
+
```python
|
| 45 |
+
from cdfm import CDFM
|
| 46 |
+
from cdfm.utils import evaluate_graph, edge_auroc
|
| 47 |
+
import numpy as np
|
| 48 |
+
|
| 49 |
+
# Load from HuggingFace Hub
|
| 50 |
+
model = CDFM.from_pretrained("DMIRLAB/CDFM")
|
| 51 |
+
|
| 52 |
+
# Load a simple 4-variable nonlinear example (RFF mechanisms)
|
| 53 |
+
data = np.loadtxt("tests/data/simple/data.csv", delimiter=",")
|
| 54 |
+
gt = np.loadtxt("tests/data/simple/adjacency.csv", delimiter=",").astype(np.int32)
|
| 55 |
+
|
| 56 |
+
# 1. Standard Prediction (Auto-calibrated threshold)
|
| 57 |
+
result = model.predict(data)
|
| 58 |
+
# 2. Manual Threshold Control
|
| 59 |
+
result_manual = model.predict(data, threshold=0.5)
|
| 60 |
+
|
| 61 |
+
print(result.adjacency) # (D, D) binary causal graph
|
| 62 |
+
metrics = evaluate_graph(result.adjacency, gt)
|
| 63 |
+
auc = edge_auroc(result.logits, gt)
|
| 64 |
+
|
| 65 |
+
print(f"F1={metrics['f1']:.4f} SHD={metrics['shd']} AUC={auc:.4f}")
|
| 66 |
+
# → F1=1.0000 SHD=0 AUC=1.0000
|
| 67 |
+
```
|
| 68 |
+
|
| 69 |
+
### 2. Missing value imputation
|
| 70 |
+
|
| 71 |
+
CDFM has a built-in imputation head trained with quantile loss. Call `model.imputation(data)` to fill missing values automatically:
|
| 72 |
+
|
| 73 |
+
```python
|
| 74 |
+
from cdfm import CDFM
|
| 75 |
+
import numpy as np
|
| 76 |
+
|
| 77 |
+
model = CDFM.from_pretrained("DMIRLAB/CDFM")
|
| 78 |
+
|
| 79 |
+
# Load data and create missing values (seed for reproducibility)
|
| 80 |
+
rng = np.random.default_rng(42)
|
| 81 |
+
data = np.loadtxt("tests/data/simple/data.csv", delimiter=",")
|
| 82 |
+
data_with_nan = data.copy()
|
| 83 |
+
data_with_nan[rng.random(data.shape) < 0.2] = np.nan
|
| 84 |
+
|
| 85 |
+
# CDFM imputation — auto-detects NaN
|
| 86 |
+
imputed = model.imputation(data_with_nan)
|
| 87 |
+
|
| 88 |
+
# Compare with mean imputation
|
| 89 |
+
mean_imp = data_with_nan.copy()
|
| 90 |
+
for j in range(data.shape[1]):
|
| 91 |
+
col = data_with_nan[~np.isnan(data_with_nan[:, j]), j]
|
| 92 |
+
mean_imp[np.isnan(mean_imp[:, j]), j] = col.mean()
|
| 93 |
+
|
| 94 |
+
missing = np.isnan(data_with_nan)
|
| 95 |
+
mae_cdfm = np.abs(imputed[missing] - data[missing]).mean()
|
| 96 |
+
mae_mean = np.abs(mean_imp[missing] - data[missing]).mean()
|
| 97 |
+
print(f"CDFM MAE: {mae_cdfm:.4f} | Mean MAE: {mae_mean:.4f}")
|
| 98 |
+
# → CDFM MAE: 0.3719 | Mean MAE: 0.7817
|
| 99 |
+
```
|
| 100 |
+
---
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
## API Reference
|
| 104 |
+
|
| 105 |
+
### `CDFM` Class
|
| 106 |
+
|
| 107 |
+
```python
|
| 108 |
+
class CDFM:
|
| 109 |
+
@classmethod
|
| 110 |
+
def from_pretrained(
|
| 111 |
+
cls,
|
| 112 |
+
pretrained_model_name_or_path: str = "DMIRLAB/CDFM", # HF Hub or local path
|
| 113 |
+
device: str = "auto", # auto / cpu / cuda:N
|
| 114 |
+
threshold: float | None = None, # None = auto-calibrate
|
| 115 |
+
) -> "CDFM"
|
| 116 |
+
|
| 117 |
+
def predict(
|
| 118 |
+
self,
|
| 119 |
+
data: np.ndarray, # (N, D) float32
|
| 120 |
+
threshold: float | None = None, # Probability threshold
|
| 121 |
+
standardize: bool = True, # Apply z-score standardization
|
| 122 |
+
missing_mask: np.ndarray | None = None,
|
| 123 |
+
) -> CDFMResult
|
| 124 |
+
```
|
| 125 |
+
|
| 126 |
+
### `CDFMResult` Object
|
| 127 |
+
|
| 128 |
+
```python
|
| 129 |
+
@dataclass
|
| 130 |
+
class CDFMResult:
|
| 131 |
+
logits: np.ndarray # (D, D) raw edge scores
|
| 132 |
+
probabilities: np.ndarray # (D, D) sigmoid(logits)
|
| 133 |
+
adjacency: np.ndarray | None # (D, D) binary graph
|
| 134 |
+
threshold: float | None # Threshold value used
|
| 135 |
+
runtime_sec: float # Wall-clock time
|
| 136 |
+
```
|
| 137 |
+
|
| 138 |
+
---
|
| 139 |
+
|
| 140 |
+
## Links
|
| 141 |
+
|
| 142 |
+
- [Paper (arXiv)](https://arxiv.org/abs/2607.11508)
|
| 143 |
+
- [HuggingFace Model](https://huggingface.co/DMIRLAB/CDFM)
|
| 144 |
+
- [GitHub Repository](https://github.com/DMIRLAB-Group/CDFM)
|
| 145 |
+
|
| 146 |
+
## License
|
| 147 |
+
|
| 148 |
+
This project is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0).
|
| 149 |
+
|
| 150 |
+
## Citation
|
| 151 |
+
|
| 152 |
+
If you use CDFM in your research, please cite:
|
| 153 |
+
|
| 154 |
+
```bibtex
|
| 155 |
+
@article{qiao2026cdfm,
|
| 156 |
+
title = {{CDFM}: Towards a General-Purpose Causal Discovery Foundation Model},
|
| 157 |
+
author = {Jie Qiao and Ruichu Cai and Zijian Li and Weilin Chen and
|
| 158 |
+
Pengfei Hua and Boyan Xu and Zhengming Chen and Zhifeng Hao and
|
| 159 |
+
Peng Cui},
|
| 160 |
+
journal = {arXiv preprint arXiv:2607.11508},
|
| 161 |
+
year = {2026},
|
| 162 |
+
}
|
| 163 |
+
```
|