GraphKAN: Graph Kolmogorov-Arnold Network for Small Molecule-Protein Interaction Predictions
Companion code for the ICML 2024 Workshop (ML4LMS) paper:
Tashin Ahmed & Md Habibur Rahman Sifat. GraphKAN: Graph Kolmogorov Arnold Network for Small Molecule-Protein Interaction Predictions. OpenReview Β· PDF
GraphKAN replaces the MLP message functions of a GNN with Fourier-series
Kolmogorov-Arnold Network (KAN) layers, and is used here to predict whether a
small molecule binds to one of three protein targets (sEH, BRD4, HSA),
following the Leash-BIO Predict New Medicines
data format.
This release is a bug-fix and packaging rewrite that makes the original proof-of-concept actually runnable. The model architecture and mathematics of the paper are unchanged. Highlights:
- Packaging with
uvβ a singlepyproject.toml, an installableferroinpackage, and aferroinCLI. - Fixed every crash in the original scripts (see Issues fixed).
- Proper package layout under
src/ferroin/with consistent imports βfrom src.data_creation import .../ flat-import mixing is gone. - CLI +
Configdataclass so every hyperparameter is overridable without editing code, plus a--smoke-testmode for a 1-minute sanity run. - Reproducible training: global seeding, an optional validation split, and best-checkpoint tracking.
- Robust data paths resolved relative to the project root (overridable via environment variables / CLI).
- Tests that run offline (no dataset required):
pytest.
ferroin/
βββ pyproject.toml # uv / pip project definition
βββ .gitignore
βββ README.md
βββ LICENSE
βββ ferroin.png
βββ data/ # YOU place train.parquet / test.parquet here
β βββ train.parquet
β βββ test.parquet
βββ src/ferroin/
β βββ __init__.py
β βββ cli.py # `ferroin` command-line entry point
β βββ main.py # run_pipeline(): load -> featurize -> train -> predict
β βββ preprocessing.py # DuckDB fetch of balanced train + full test
β βββ data_fetcher.py # SQL helpers (balanced_data_creation, protein_data)
β βββ data_creation.py # SMILES -> PyG Data graphs
β βββ featurizing.py # Test-set featurization + slicing
β βββ featurizer.py # atom_features, bond_features, feature dims
β βββ gnn_model.py # NaiveFourierKANLayer + CustomGNNLayer + GNNModel
β βββ train.py # training loop w/ validation + best checkpoint
β βββ predict.py # sigmoid inference, robust id handling
β βββ parameters.py # Config dataclass, defaults, set_seed()
βββ tests/
βββ test_smoke.py # offline tests (no data needed)
βββ test_dataset_shapes.py
- Python 3.10 β 3.12 (the
rdkitwheels currently have no 3.13 builds). uv0.4+ (recommended) or plain pip.- Works on Linux, Windows, and macOS (Apple Silicon). On Intel macOS the
newest
torch/rdkitno longer ship x86_64 wheels β pintorch==2.2.2andrdkit==2024.3.5, or use conda for RDKit.
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows (PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"From the repository root:
# 1. Create a virtual environment and install the project (+ dev + wandb extras)
uv sync --extra dev --extra wandbThis creates .venv/, installs torch, torch-geometric, rdkit, duckdb,
pandas, numpy, tqdm, plus pytest/ruff/pylint and optional wandb.
GPU / CUDA users: uv installs the default PyPI torch. For a CUDA build, add the PyTorch index, e.g. for CUDA 12.1:
uv sync --extra dev --extra-index-url https://download.pytorch.org/whl/cu121
Place the Leash-BIO parquet files in data/ (or point to them with --train-path
/ --test-path or the FERROIN_TRAIN_PATH / FERROIN_TEST_PATH env vars):
data/
βββ train.parquet # columns: id, molecule_smiles, protein_name, binds, ...
βββ test.parquet # columns: id, molecule_smiles, protein_name, ...
Download from the Kaggle competition page.
uv run pytest -q # offline smoke tests
uv run ferroin --help # CLI helpA tiny config (1 protein, 200 samples/class, 1 epoch) to confirm the full pipeline runs end-to-end:
uv run ferroin --smoke-testuv run ferroin \
--train-path data/train.parquet \
--test-path data/test.parquet \
--proteins sEH BRD4 HSA \
--samples-per-category 30000 \
--epochs 5 --layers 6 --hidden-dim 64 --grid-size 300 \
--lr 1e-3 --dropout 0.3 --val-frac 0.1 --seed 42This will, for each protein:
- sample a class-balanced training subset (DuckDB),
- featurize molecules into PyG graphs,
- train a GraphKAN model (keeping the best checkpoint by validation loss),
- save the checkpoint to
trained_models/<protein>_model_<epochs>.pth, - predict binding probabilities on the test set.
Predictions for all proteins are concatenated and written to
predictions/final_predictions.csv (columns: id, binds).
from ferroin.parameters import Config
from ferroin.main import run_pipeline
cfg = Config(epochs=10, layers=6, hidden_dim=64, use_wandb=True)
final_df = run_pipeline(cfg) # pandas.DataFrame with columns ['id', 'binds']All hyperparameters live in ferroin/parameters.py as a Config dataclass
with sensible defaults, and can be overridden via:
| Source | Example |
|---|---|
| CLI flag | --epochs 10 --layers 6 |
| Environment variable | FERROIN_TRAIN_PATH=/data/train.parquet |
| Programmatic | Config(epochs=10) or cfg.epochs = 10 |
Key defaults: epochs=5, layers=6, hidden_dim=64, grid_size=300,
lr=1e-3, dropout=0.3, samples_per_category=30000, val_frac=0.1,
seed=42.
uv run wandb login # one time
uv run ferroin --wandb --wandb-project GraphKAN_Protein_Binding ...The core idea (unchanged from the paper): in each message-passing layer the
incoming message concat(x_j, edge_attr) is transformed by a
Fourier-series KAN layer instead of an MLP:
NaiveFourierKANLayerlearns(2, out, in, grid)coefficients for thecos/sinbasis of frequenciesk = 1..grid, combined via aneinsum.CustomGNNLayer(MessagePassing,aggr='max') wraps the KAN layer.GNNModelstackslayersof them withBatchNorm1d+ReLU+Dropout, thenglobal_max_pooland a linear readout producing a binding logit.
See src/ferroin/gnn_model.py.
uv run pytest -q # offline (no dataset required)
uv run ruff check src teststests/test_smoke.py builds a few tiny molecules, runs a forward+backward pass
through the GraphKAN model, and checks the predict helper β a fast way to
confirm the environment and code are healthy.
The original proof-of-concept scripts could not run as-is. Every one of these was fixed:
featurizing.pycalledfeat_data_in_batches(...)with a missingids_listargument (TypeError).featurizing.pyreassignedtest_data_slicedinsidemain(), shadowing the module-level dict thatmain.pyimported (always empty).featurizing.pyused placeholder paths ('path_to_brd4_test.csv').data_creation.pyreferenced undefined globalsseh_df/brd4_df/hsa_dfinmain()(NameError).main.pyconsumed empty module-level dicts (featurized_data_train,test_data_sliced) β the pipeline never actually produced data.predict.pycalled.cpu()onmolecule_id, which is a Python int collated into a list (crash).main.pycalled.item()on ids assuming tensors (crash on ints).gnn_model.pyhardcoded the edge feature dimension asin_channels + 6β now parameterized viaedge_dim.test_dataset_shapes.pycompared balanced subsets against full-dataset shapes, so the check always "failed" β now correct (2 * samples_per_category).- Imports mixed
from src.xand flatfrom xwith no__init__.pyβ replaced by a single installableferroinpackage. - No seeding β added global
set_seed()for reproducibility. - No validation / checkpointing β training now keeps the best model by validation loss.
- Incomplete
requirements.txtβ replaced by a completepyproject.tomlmanaged byuv. - Removed dead/unused imports (
wandb,DataLoader) and redundant CSV writes.
If you use this code, please cite the paper:
@inproceedings{ahmed2024graphkan,
title = {GraphKAN: Graph Kolmogorov Arnold Network for Small Molecule-Protein
Interaction Predictions},
author = {Ahmed, Tashin and Sifat, Md Habibur Rahman},
booktitle = {ICML 2024 Workshop on Machine Learning for Low-resource Sensors},
year = {2024},
url = {https://openreview.net/forum?id=d5uz4wrYeg}
}MIT License β Copyright (c) 2024 Tashin Ahmed. See LICENSE.
