Skip to content
diliadisPublic

About

A python-based framework for multi-target prediction

Resources

Contributing

Security policy

Stars

53 stars

Watchers

1 watching

Forks

Repository files navigation

DeepMTP

DeepMTP

A PyTorch framework for multi-target prediction with flexible two-branch neural networks.

CI PyPI version Supported Python versions Documentation MIT license

Documentation · Quick start · Input guide · Colab demos · Issues


DeepMTP learns from observations of the form (instance, target, score). It encodes instances and targets independently, fuses their representations, and predicts the score for each pair. The same interface covers:

  • multi-label classification (MLC);
  • multivariate regression (MTR);
  • multi-task learning (MTL);
  • dyadic prediction (DP); and
  • matrix completion (MC).

Use dense, sparse, tabular, sequence, foundation-model, molecular-graph, image, learned-ID, composite, or custom inputs on either branch. DeepMTP also provides reproducible checkpoints, PEFT/LoRA support, experiment tracking, and random-search or Hyperband optimization without making those integrations mandatory.

Upgrading from DeepMTP 0.0.22? Read the 0.0.22 → 0.0.23 migration guide and changelog before reusing an existing experiment.

Why DeepMTP?

Need DeepMTP support
One model across MTP problem families A shared two-branch abstraction for MLC, MTR, MTL, DP, and MC
Generalize to new entities Explicit validation settings for known or novel instances and targets
Mix representation types Built-in dense, sparse, tabular, sequence, foundation, graph, image, ID, and composite encoders
Choose how branches interact Dot product, concatenation plus MLP, or Kronecker-product fusion
Train larger models safely Gradient accumulation, gradient clipping, early stopping, and deterministic data handling
Track and restore experiments Versioned checkpoints, preprocessing provenance, TensorBoard, and Weights & Biases Artifacts
Adapt pretrained encoders efficiently Hugging Face Transformers with frozen, full fine-tuning, or PEFT/LoRA modes

Installation

DeepMTP supports Python 3.10–3.14. CPU execution is fully supported; a CUDA GPU is optional.

python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate
python -m pip install --upgrade pip
python -m pip install DeepMTP

Install only the integrations you use:

Extra Install command Adds
Datasets pip install "DeepMTP[datasets]" Downloadable benchmark datasets
Foundation pip install "DeepMTP[foundation]" Transformers and PEFT/LoRA
Graph pip install "DeepMTP[graph]" PyTorch Geometric encoders
HPO pip install "DeepMTP[hpo]" ConfigSpace, random search, and Hyperband
Image pip install "DeepMTP[image]" Torchvision models and transforms
Sparse pip install "DeepMTP[sparse]" SciPy sparse matrices
Streamlit pip install "DeepMTP[streamlit]" Streamlit progress adapters
Tracking pip install "DeepMTP[tracking]" TensorBoard and Weights & Biases
Visualization pip install "DeepMTP[visualization]" Rich terminal architecture diagrams and SVG export
Everything pip install "DeepMTP[all]" All runtime integrations

For GPU acceleration, install the appropriate PyTorch build using the official PyTorch selector first. See the installation guide for source and Conda workflows.

Quick start

This complete CPU example trains on a synthetic multi-label dataset, evaluates it, and returns pair-level predictions. It requires no downloads or optional dependencies.

import numpy as np

from DeepMTP import DeepMTP, DeepMTPConfig, data_process

rng = np.random.default_rng(42)
scores = (
    np.arange(24)[:, np.newaxis] + np.arange(4)[np.newaxis, :]
) % 2
data = {
    "train": {
        "y": scores,
        "X_instance": rng.normal(size=(24, 3)),
        "X_target": None,
    }
}
train, validation, test, data_info = data_process(
    data,
    validation_setting="B",
)

config = DeepMTPConfig(
    validation_setting=data_info["detected_validation_setting"],
    problem_mode=data_info["detected_problem_mode"],
    general_architecture_version="dot_product",
    compute_mode="cpu",
    num_workers=0,
    train_batchsize=16,
    val_batchsize=16,
    num_epochs=1,
    metrics=["accuracy"],
    metrics_average=["macro"],
    evaluate_val=True,
    use_early_stopping=False,
    save_model=False,
    results_path="results",
    experiment_name="quickstart",
    instance_branch_architecture="MLP",
    instance_branch_input_dim=data_info["instance_branch_input_dim"],
    instance_branch_nodes_per_layer=[8],
    target_branch_architecture="MLP",
    target_branch_input_dim=data_info["target_branch_input_dim"],
    target_branch_nodes_per_layer=[8],
    embedding_size=4,
)

model = DeepMTP(config)
validation_results = model.train(train, validation, test)
test_results, predictions = model.predict(test, return_predictions=True)

DeepMTP writes config.json, summary.txt, and any enabled logs or checkpoint below results/quickstart. The four core steps remain the same for every input type:

  1. Describe observed scores and optional side information.
  2. Call data_process with the intended validation setting and input kinds.
  3. Build a validated DeepMTPConfig.
  4. Train with DeepMTP.train and infer with DeepMTP.predict.

Mental model

Let x denote an instance, t a target, and y their observed relationship. DeepMTP learns two encoders:

instance x ──> instance encoder ──> pₓ ──┐
                                         ├──> fusion ──> score ŷ(x, t)
target t   ──> target encoder   ──> qₜ ──┘

The score can be binary, multiclass, or continuous. Dot-product fusion requires equal branch widths. MLP and Kronecker fusion can combine unequal widths.

DeepMTP independently encodes instances and targets, fuses their representations, and produces binary, multiclass, or regression predictions.

Inspect the same architecture directly from Python or a saved configuration:

from DeepMTP import print_architecture

print_architecture(config)
deepmtp architecture results/quickstart/config.json

Install DeepMTP[visualization] for responsive Rich panels, parameter statistics, and SVG export. Without it, DeepMTP falls back to a portable plain text representation. See the architecture visualization guide.

Generalization settings

The validation setting states which entity types may be unseen at evaluation time. Novel entities require side information; a learned ID lookup cannot represent an entity absent from training.

Setting Evaluation pairs Instance side information Target side information
A Known instances × known targets Optional Optional
B Novel instances × known targets Required Optional
C Known instances × novel targets Optional Required
D Novel instances × novel targets Required Required

Validation settings A through D distinguish whether evaluation contains known or novel instances, targets, or both.

See the configuration guide for split semantics and validation rules.

Choose your input representations

Each branch selects an encoder independently. A compound–protein model can, for example, combine a molecular graph branch with a protein foundation-model branch.

Architecture Input Good fit Dependency
MLP Dense numeric vectors Descriptors and precomputed embeddings Core
SPARSE COO/CSR or SciPy matrices Fingerprints, bag-of-words, indicators Core for PyTorch; sparse for SciPy
TABULAR Numeric and categorical columns Mixed entity metadata Core
SEQUENCE Token IDs and masks Proteins, SMILES, text Core; foundation for Hugging Face
GRAPH Homogeneous PyG graphs Molecular graphs graph
CONV Images Visual entity representations image
EMBEDDING Zero-based entity IDs Known entities without side features Core
COMPOSITE Named component mapping Multimodal or partially missing inputs Depends on components
CUSTOM User-defined batch type Domain-specific encoders Core

Learned entity IDs

Use EMBEDDING rather than a dense one-hot MLP when entities are known during training. Settings B, C, and D reject an ID branch on any novel entity axis.

from DeepMTP import DeepMTPConfig

number_of_instances = 24
number_of_targets = 4

config = DeepMTPConfig(
    validation_setting="A",
    problem_mode="classification",
    general_architecture_version="dot_product",
    metrics_average=["micro"],
    instance_branch_architecture="EMBEDDING",
    instance_branch_input_dim=number_of_instances,
    target_branch_architecture="EMBEDDING",
    target_branch_input_dim=number_of_targets,
    embedding_size=32,
)

Mixed tabular data

TABULAR keeps numeric and categorical preprocessing explicit and stores the fitted training-only state for compatible restoration.

import pandas as pd

from DeepMTP import DeepMTPConfig

instance_features = pd.DataFrame(
    {
        "id": [0, 1, 2],
        "age": [22.0, None, 41.0],
        "site": ["Ghent", "Paris", "Ghent"],
    }
)

tabular_schema = {
    "numeric_columns": ["age"],
    "categorical_columns": {
        "site": {
            "categories": ["Ghent", "Paris"],
            "embedding_dim": 2,
        }
    },
    "numeric_normalization": "standard",
    "numeric_missing": "mean",
    "categorical_unknown": "unknown",
    "feature_gating": True,
}

number_of_targets = 4

config = DeepMTPConfig(
    validation_setting="B",
    problem_mode="classification",
    general_architecture_version="dot_product",
    instance_branch_architecture="TABULAR",
    instance_branch_tabular_schema=tabular_schema,
    instance_branch_nodes_per_layer=[16],
    target_branch_architecture="EMBEDDING",
    target_branch_input_dim=number_of_targets,
    embedding_size=8,
)

Read the tabular guide for missing-value, normalization, and unknown-category policies.

Sparse high-dimensional data

SPARSE preserves sparsity through splitting, batching, and the first projection. Apply sparse-safe preprocessing upstream; legacy dense scaling is rejected rather than silently densifying the data.

config = DeepMTPConfig(
    validation_setting="B",
    problem_mode="classification",
    instance_branch_architecture="SPARSE",
    instance_branch_input_dim=fingerprint_width,
    instance_branch_nodes_per_layer=[128, 32],
    target_branch_architecture="MLP",
    target_branch_input_dim=number_of_targets,
    target_branch_nodes_per_layer=[32],
    embedding_size=32,
)

See the sparse-input guide for SciPy support, explicit IDs, and benchmark tooling.

Token sequences

SEQUENCE batches contain dynamically padded token IDs, attention masks, and original lengths. Choose gru, conv1d, or transformer; all ignore padded positions.

from DeepMTP import DeepMTPConfig

vocabulary_size = 32
number_of_targets = 4

config = DeepMTPConfig(
    validation_setting="B",
    problem_mode="classification",
    general_architecture_version="dot_product",
    instance_branch_architecture="SEQUENCE",
    instance_branch_input_dim=vocabulary_size,
    instance_branch_sequence_encoder="transformer",
    instance_branch_sequence_embedding_dim=16,
    instance_branch_sequence_transformer_num_heads=4,
    instance_branch_sequence_transformer_feedforward_dim=64,
    instance_branch_sequence_num_layers=1,
    instance_branch_sequence_padding_idx=0,
    target_branch_architecture="MLP",
    target_branch_input_dim=number_of_targets,
    target_branch_nodes_per_layer=[8],
    embedding_size=8,
)

Raw token rows must be unpadded and cannot contain the configured padding ID. The sequence guide documents collation and each encoder.

Foundation models and LoRA

The optional Hugging Face adapter uses AutoTokenizer and AutoModel for protein, chemical, biomedical, or general text encoders. Training modes are frozen, full, and lora.

from DeepMTP import FoundationLoRASpec, FoundationModelSpec

protein_model = FoundationModelSpec(
    model_name="facebook/esm2_t6_8M_UR50D",
    revision="<pinned-model-commit>",
    pooling="masked_mean",
    train_mode="lora",
    lora=FoundationLoRASpec(
        rank=8,
        alpha=16,
        dropout=0.1,
        target_modules=("query", "value"),
    ),
    max_length=1024,
)

Tokenize raw values with tokenize_foundation_sequences, then pass the normalized specification to a SEQUENCE branch with sequence_encoder="foundation". Hub revision capture, pooling, length policy, composite use, compact adapter checkpoints, and supported reference models are covered in the foundation-model guide.

LoRA trains compact adapters around a pinned foundation model, and adapter checkpoints restore those adapters and task weights with the recorded base identity.

Molecular graphs

GRAPH consumes homogeneous torch_geometric.data.Data objects and uses GIN or edge-aware GINE layers with mean, sum, or max graph pooling. Each graph needs floating node features, COO edge_index, and optional floating edge features. See the graph guide.

Composite and partially missing inputs

COMPOSITE combines named dense, sparse, graph, sequence, ID, or tabular components. Fusion can use concatenation, sample-specific gates, or modality self-attention. Optional components carry presence masks and learned missing representations; training-only modality dropout can improve robustness.

A composite branch independently encodes multiple modalities, substitutes learned representations for missing optional inputs, and combines components with presence-aware fusion.

config = DeepMTPConfig(
    validation_setting="B",
    problem_mode="classification",
    general_architecture_version="mlp",
    instance_branch_architecture="COMPOSITE",
    instance_branch_composite_fusion="attention",
    instance_branch_composite_attention_dim=32,
    instance_branch_composite_attention_num_heads=4,
    instance_branch_composite_components={
        "descriptors": {
            "architecture": "MLP",
            "input_dim": descriptor_width,
            "output_dim": 16,
        },
        "tokens": {
            "architecture": "SEQUENCE",
            "input_dim": vocabulary_size,
            "output_dim": 16,
            "embedding_dim": 16,
            "optional": True,
        },
    },
    instance_branch_composite_modality_dropout=0.2,
    target_branch_architecture="MLP",
    target_branch_input_dim=number_of_targets,
    target_branch_nodes_per_layer=[32],
    comb_mlp_nodes_per_layer=[32],
)

The composite-input guide explains component schemas, missingness, and fusion behavior.

Tasks, losses, and metrics

Binary classification is the backward-compatible classification default and uses logits with binary cross entropy. Regression defaults to mean squared error and also supports MAE and Huber losses.

Multiclass mode predicts exactly one of at least three classes. Labels must be zero-based integer IDs, and every declared class must occur in training data.

import numpy as np

from DeepMTP import DeepMTPConfig, data_process

scores = (
    np.arange(18)[:, np.newaxis] + np.arange(3)[np.newaxis, :]
) % 3
train, validation, test, data_info = data_process(
    {"train": {"y": scores}},
    validation_setting="A",
    classification_mode="multiclass",
)

config = DeepMTPConfig(
    validation_setting=data_info["detected_validation_setting"],
    problem_mode="classification",
    classification_mode=data_info["detected_classification_mode"],
    num_classes=data_info["detected_num_classes"],
    metrics=["accuracy", "f1_score", "auroc"],
    metrics_average=["micro"],
    multiclass_average="macro",
    compute_mode="cpu",
    instance_branch_architecture="EMBEDDING",
    instance_branch_input_dim=data_info["instance_branch_input_dim"],
    target_branch_architecture="EMBEDDING",
    target_branch_input_dim=data_info["target_branch_input_dim"],
    embedding_size=8,
)
Problem Default loss Metrics
Binary classification Binary cross entropy with logits Hamming loss, AUROC, AUPR, accuracy, recall, precision, F1
Multiclass classification Cross entropy Accuracy, recall, precision, F1, AUROC, AUPR
Regression Mean squared error RMSE, MSE, MAE, R²

metrics_average groups observations globally, by target, or by instance. multiclass_average combines classes within applicable multiclass metrics. See the multiclass guide for prediction columns and averaging rules.

Training and reproducibility

Larger effective batches and stable gradients

config = DeepMTPConfig(
    # architecture and task settings...
    gradient_accumulation_steps=4,
    gradient_clip_norm=1.0,
)

Accumulation combines observation-weighted microbatches before an optimizer step, including an uneven final group. Clipping applies a global norm limit at optimizer-step boundaries. Their inert defaults (1 and None) preserve historical behavior.

Checkpoints

Format File Contains Use it for
full (default) model.pt Model, optimizer, configuration, provenance Restoration, optimizer warm starts, and fully fine-tuned backbones
foundation_adapter foundation_adapter.zip PEFT adapters, DeepMTP task weights, identities, hashes Compact LoRA inference or warm starts
restored = DeepMTP(
    {
        "results_path": "./results",
        "experiment_name": "restored",
    },
    checkpoint_dir="./results/training-run/model.pt",
)

Compact adapter checkpoints exclude base-model and optimizer weights. Fully fine-tuned foundation models therefore require full. Both formats record preprocessing and environment metadata; load checkpoints only from trusted sources. Read the checkpoint guide for replay and compatibility details.

TensorBoard and Weights & Biases

Set use_tensorboard_logger=True for local TensorBoard events. For W&B, supply the project and entity; richer uploads remain explicitly opt-in.

config = DeepMTPConfig(
    # architecture and task settings...
    wandb_project_entity="my-team",
    wandb_project_name="deepmtp",
    wandb_run_name="protein-lora",
    wandb_group="ablations",
    wandb_tags=["protein", "lora"],
    wandb_watch="gradients",
    wandb_log_model_artifact=True,
    wandb_model_artifact_aliases=["latest", "best"],
    wandb_log_predictions=True,
)

DeepMTP records epoch-aware metrics, summaries, failures, parameter counts, bounded prediction tables, model Artifacts, input lineage, and optional W&B Registry links. Restore either checkpoint format directly:

model = DeepMTP.from_wandb_artifact(
    "my-team/deepmtp/deepmtp-model:best",
    {
        "results_path": "./results",
        "experiment_name": "restored",
    },
)

See the W&B guide for offline mode, privacy-conscious defaults, lineage, and Registry setup.

Hyperparameter optimization

DeepMTP includes random search and Hyperband through the optional hpo extra. Hyperband allocates increasing epoch budgets to promising configurations and discards weaker candidates early. This small offline example uses one epoch as an API check; real studies should expand both the search space and budget.

Show the complete Hyperband example
import ConfigSpace as CS
import numpy as np

from DeepMTP import DeepMTP, data_process
from DeepMTP.hpo import BaseWorker, HyperBand

rng = np.random.default_rng(42)
scores = (
    np.arange(16)[:, np.newaxis] + np.arange(3)[np.newaxis, :]
) % 2
data = {
    "train": {
        "y": scores,
        "X_instance": rng.normal(size=(16, 3)),
        "X_target": None,
    }
}
train, validation, test, data_info = data_process(
    data,
    validation_setting="B",
)

config_space = CS.ConfigurationSpace(seed=42)
config_space.add(
    [
        CS.Float(
            "learning_rate",
            (1e-4, 1e-2),
            default=1e-3,
            log=True,
        ),
        CS.Integer("embedding_size", (2, 4), default=3),
    ]
)

base_config = {
    "hpo_results_path": "hpo_results",
    "validation_setting": data_info["detected_validation_setting"],
    "problem_mode": data_info["detected_problem_mode"],
    "general_architecture_version": "dot_product",
    "compute_mode": "cpu",
    "num_workers": 0,
    "train_batchsize": 16,
    "val_batchsize": 16,
    "metrics": [],
    "metrics_average": ["macro"],
    "evaluate_train": False,
    "evaluate_val": False,
    "use_early_stopping": False,
    "save_model": True,
    "verbose": False,
    "instance_branch_architecture": "MLP",
    "instance_branch_input_dim": data_info["instance_branch_input_dim"],
    "target_branch_architecture": "MLP",
    "target_branch_input_dim": data_info["target_branch_input_dim"],
}

worker = BaseWorker(
    train,
    validation,
    test,
    data_info,
    base_config,
    metric_to_optimize="loss",
)
optimizer = HyperBand(
    base_worker=worker,
    configspace=config_space,
    eta=2,
    max_budget=1,
    direction="min",
)
best_experiment = optimizer.run_optimizer()

best_model = DeepMTP(
    best_experiment.info["config"],
    checkpoint_dir=best_experiment.info["model_dir"],
)
best_model_results = best_model.predict(test, verbose=True)

The HPO guide documents search-space routing, budgets, and restoration.

Demos

The version-controlled notebooks use small datasets and are executed by the test suite. Their first cell installs the matching source when opened in a fresh Colab runtime; local execution uses the active environment.

Workflow Open in Colab
Dataset preparation Open In Colab
Multi-label classification Open In Colab
Multivariate regression Open In Colab
Multi-task learning Open In Colab
Matrix completion Open In Colab
Dyadic prediction Open In Colab
Hyperband Open In Colab
Random search Open In Colab

The links are checked weekly. Verify their mapping and live endpoints manually with:

python scripts/check_colab_links.py

Documentation and project links

Development

git clone https://github.com/diliadis/DeepMTP.git
cd DeepMTP
python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e . --group dev
pre-commit run --all-files
python -m pytest

Conda users can start with conda env create -f environment.yml. Please read CONTRIBUTING.md before opening a pull request.

Citation

If DeepMTP supports your research, please cite the SoftwareX paper:

@article{iliadis2023deepmtp,
  title = {DeepMTP: A Python-based deep learning framework for multi-target prediction},
  author = {Iliadis, Dimitrios and De Baets, Bernard and Waegeman, Willem},
  journal = {SoftwareX},
  volume = {23},
  pages = {101516},
  year = {2023},
  publisher = {Elsevier},
  doi = {10.1016/j.softx.2023.101516}
}

Related work:

License

DeepMTP is released under the MIT License.

About

A python-based framework for multi-target prediction

Resources

Contributing

Security policy

Stars

53 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages