Downloads · 30 days
24
12% of all-time downloads
GleghornLab/DSM_150_ppi_lora
DSM_150_ppi_lora is a machine learning model from GleghornLab. Use it for the machine learning task on the model card, and read the license before you ship it in a product. It is set up for transformers. The card lists the license as other.
DSM-150-ppi generates a candidate interacting sequence conditioned on a target sequence. It is GleghornLab/DSM150 fine-tuned with LoRA on protein-protein interaction pairs.
Downloads · 30 days
24
12% of all-time downloads
All-time downloads
206
Public
Parameters
151M
1.2 GB on disk
Likes
0
Public
Click a slice to open those files.
.safetensors606 MB · 100%
From the Hugging Face model README
DSM-150-ppi generates a candidate interacting sequence conditioned on a target sequence. It is GleghornLab/DSM_150 fine-tuned with LoRA on protein-protein interaction pairs.
This is the version the preprint reports, and it is not recommended for real use. LoRA fine-tuning leaves the outputs unreliable; use Synthyra/DSM_ppi_full instead.
| Class | ESM_Diff_Binders, model type esm_diff |
| Hidden size | 640, 30 layers, 20 attention heads, feed-forward width 2,560 |
| LoRA | rank 8, alpha 32, dropout 0.01, on the attention layers |
| Weights | model.safetensors, 606 MB, float32 |
import torch
from models.modeling_dsm import DSM # from github.com/Gleghorn-Lab/DSM
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = DSM.from_pretrained("GleghornLab/DSM_150_ppi_lora").to(device).eval()
tokenizer = model.tokenizer
You are using a model of type esm_diff to instantiate a model of type dsm.
That warning is expected.
from models.modeling_dsm import DSM_ppi
model = DSM_ppi.from_pretrained("GleghornLab/DSM_150_ppi_lora").to(device).eval()
tokenizer = model.tokenizer
mask_token = tokenizer.mask_token
target_seq = "MGTPLWALLGGPWRGTATYEDGTKVTLDYRYTRVSPDRLRADVTYTTPDGTTLEATVDLWKDANGVIRYHATYPDGTSADGTLTQLDADTLLATGTYDDGTKYTVTLTRVAPGSGWHHHHHH"
interactor_template = ''.join([mask_token] * 256)
input_tokens = tokenizer.encode(
target_seq + '<eos>' + interactor_template, add_special_tokens=True, return_tensors='pt'
).to(device)
output = model.mask_diffusion_generate(
tokenizer=tokenizer, input_tokens=input_tokens, step_divisor=100, temperature=1.0, remasking="random"
)
target, binder = model.decode_dual_input(output, seperator='<eos>')
The PPI set was compiled from STRING v12 for nine model organisms, keeping entries with a combined score of at least 900, clustering the unique sequences with CD-HIT at 90% identity, and keeping only pairs where both sequences are cluster representatives: 646,000 training pairs, 5,850 for validation, and a leakage-free test set of 1,320.
One epoch of that set, with the format [CLS]--SeqA--[EOS]--[MASKED SeqB]--[EOS] and the same
objective as pretraining, so only SeqB is masked. The order of SeqA and SeqB was swapped at random
during training and fixed during testing. LoRA rank 8, alpha 32, dropout 0.01 on the attention
layers.
Conditioning helps: on the PPI test set at a 15% mask rate, DSM-150-ppi reconstructs 33.0% of masked positions against 31.4% for DSM-150 and 31.9% for the control (GleghornLab/DSM_150_ppi_control), which saw the same fine-tuning without the target sequence.
Sequence reconstruction on the PPI test set at a 15% mask rate, from the preprint's supplementary table. DSM-ppi models see SeqA and SeqB; every other model sees SeqB only.
| Model | Cross entropy | MCC | F1 | Accuracy |
|---|---|---|---|---|
| ESM2-150M | 2.202 | 0.276 | 0.318 | 32.2% |
| ESM2-650M | 2.407 | 0.260 | 0.307 | 30.7% |
| ESM2-3B | 2.234 | 0.286 | 0.334 | 33.1% |
| DSM-150 | 2.225 | 0.268 | 0.311 | 31.4% |
| DSM-650 | 2.046 | 0.325 | 0.365 | 36.7% |
| DSM-150-ppi-control | 2.199 | 0.274 | 0.315 | 31.9% |
| DSM-150-ppi | 2.165 | 0.285 | 0.326 | 33.0% |
| DSM-650-ppi | 1.989 | 0.342 | 0.382 | 38.3% |
LICENSE file in this repository@misc{hallee2025diffusionsequencemodelsenhanced,
title={Diffusion Sequence Models for Enhanced Protein Representation and Generation},
author={Logan Hallee and Nikolaos Rafailidis and David B. Bichara and Jason P. Gleghorn},
year={2025},
eprint={2506.08293},
archivePrefix={arXiv},
primaryClass={q-bio.BM},
url={https://arxiv.org/abs/2506.08293},
}
What follows is the project README, shared with github.com/Gleghorn-Lab/DSM.
DSM (Diffusion Sequence Model) is a novel Protein Language Model (pLM) developed in collaboration between the Gleghorn Lab and Synthyra. It was trained with masked diffusion to enable both high-quality representation learning and generative protein design. This repository contains the code for training, evaluating, and applying DSM and its variants.
DSM is capable of generating diverse, biomimetic sequences that align with expected amino acid compositions, secondary structures, and predicted functions. Furthermore, DSM's learned representations match or exceed those of comparably sized pLMs on various downstream tasks. DSM is detailed extensively in our preprint (which is currently in review). Beyond the base and PPI variants, we are currently training versions to jointly diffuse over sequence and foldseek tokens, as well as Annotation Vocabulary tokens. Since the preprint release, Synthyra has trained Synthyra/DSM_ppi_full which neglects the LoRA PPI training in favor for full finetuning. Additionally, the sequences SeqA and SeqB are jointly masked instead of just SeqB in the original version. We plan on adding the many new results to the second version of the preprint and eventual journal article.
Relevant Huggingface hosted models and datasets
Base DSM Models:
DSM-ppi Models: (LoRA versions - results reported in paper but not recommended for real use)
(Fully finetuned - recommended for real use)
Datasets:
Utility Models:
This section outlines how to use a trained DSM model for common generation tasks. The core generation logic is provided by the GenerateMixin class, used by DSM models.
First, ensure you have a trained model (either one you trained or a pre-trained one from Hugging Face Hub) and the necessary environment set up.
import torch
from models.modeling_dsm import DSM # Or DSM_ppi for binder generation
# Load a pre-trained model
model_name_or_path = "GleghornLab/DSM_650" # Replace with your model of choice
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = DSM.from_pretrained(model_name_or_path).to(device).eval()
tokenizer = model.tokenizer
You are using a model of type esm_diff to instantiate a model of type dsm. This is not supported for all configurations of models and can yield errors.
This warning is normal - all good!
To generate a novel sequence of a specific length. DSM uses a progressive denoising approach.
### Unconditional generation
length = 100
mask_token = tokenizer.mask_token
# optionally, enforce starting with methionine
input_tokens = tokenizer.encode('M' + ''.join([mask_token] * (length - 1)), add_special_tokens=True, return_tensors='pt').to(device)
output = model.mask_diffusion_generate(
tokenizer=tokenizer,
input_tokens=input_tokens,
step_divisor=100, # lower is slower but better
temperature=1.0, # sampling temperature
remasking="random", # strategy for remasking tokens not kept
preview=False, # set this to True to watch the mask tokens get rilled in real time
slow=False, # adds a small delay to the real time filling (because it is usually very fast and watching carefully is hard!)
return_trajectory=False # set this to True to return the trajectory of the generation (what you watch in the preview)
) # Note: output will be a tuple if return_trajectory is True
generated_sequences = model.decode_output(output)
print(f"Generated sequence: {generated_sequences[0]}")
Generated sequence: MFRVDALQVAQQETLAIGRSTAYDKQESPSMAQRQVLTQLAAYGGENDLRQICIPAERRNFLSIANGASYQFVEEDNEANGGYWSPHKAGLPESACKRFI
To fill in masked regions of a template sequence:
# Mask Filling / Inpainting
template_sequence = "MA<mask><mask><mask>KEG<mask><mask>STL"
input_tokens = tokenizer.encode(template_sequence, add_special_tokens=True, return_tensors='pt').to(device)
output = model.mask_diffusion_generate(
tokenizer=tokenizer,
input_tokens=input_tokens,
step_divisor=100, # lower is slower but better
temperature=1.0, # sampling temperature
remasking="random", # strategy for remasking tokens not kept
preview=False, # set this to True to watch the mask tokens get rilled in real time
slow=False, # adds a small delay to the real time filling (because it is usually very fast and watching carefully is hard!)
return_trajectory=False # set this to True to return the trajectory of the generation (what you watch in the preview)
) # Note: output will be a tuple if return_trajectory is True
generated_sequences = model.decode_output(output)
print(f"Generated sequence: {generated_sequences[0]}")
Generated sequence: MAVKFKEGGISTL
# from models.modeling_dsm import DSM_ppi
# model_binder = DSM_ppi.from_pretrained("GleghornLab/DSM_650_ppi_lora").to(device).eval()
# The lora version from the paper leads to unreliable outputs
# Synthyra has generously trained a version through full fine tuning
model = DSM.from_pretrained("Synthyra/DSM_ppi_full").to(device).eval()
# BBF-14
target_seq = "MGTPLWALLGGPWRGTATYEDGTKVTLDYRYTRVSPDRLRADVTYTTPDGTTLEATVDLWKDANGVIRYHATYPDGTSADGTLTQLDADTLLATGTYDDGTKYTVTLTRVAPGSGWHHHHHH"
# For binder generation, the 'interactor' (SeqB) part is what gets generated/filled.
# Start with a fully masked interactor of desired length.
interactor_template_len = 256
interactor_template = ''.join([mask_token] * interactor_template_len)
combined_input_str = target_seq + '<eos>' + interactor_template
input_tokens = tokenizer.encode(combined_input_str, add_special_tokens=True, return_tensors='pt').to(device)
output = model.mask_diffusion_generate(
tokenizer=tokenizer,
input_tokens=input_tokens,
step_divisor=100, # lower is slower but better
temperature=1.0, # sampling temperature
remasking="random", # strategy for remasking tokens not kept
preview=False, # set this to True to watch the mask tokens get rilled in real time
slow=False, # adds a small delay to the real time filling (because it is usually very fast and watching carefully is hard!)
return_trajectory=False # set this to True to return the trajectory of the generation (what you watch in the preview)
) # Note: output will be a tuple if return_trajectory is True
target, binder = model.decode_dual_input(output, seperator='<eos>')
# Parse out the generated interactor part based on EOS tokens.
# Example: generated_full_seq_str.split(model_binder.tokenizer.eos_token)[1]
print(f"Generated binder {binder[0]}")
Generated binder HRHHHRRPTHARETEWLARMRLGIAEHQRIAVPRSDLEPDQMRERAADNQRLVKEYDQVIDHQTEGSTERLFEVLRVWEQVNTEQAHHEASAALEFGRVGYPDDEGGRAFYTQANAHKKDLVEYIGGIDEDAKWDPRIAWLMPEGGQPVKATVIGVSEERINGLKVLDDHWGRERRLWLINLFTALQAYDDPTRPTQVTLTPATDQLTNDVQYLLLSTRYTPPGVTTAVKIRKLDGRTLKVLTTEAPYVVRGATLS
Folded with Chai1:
Synthyra/DSM_ppi_full was actually trained to fill masks from any part of SeqA and SeqB. That means you can fully hallucinate plausibly interacting protein pairs.
seq_a_length = 128
seq_b_length = 128
seq_a_template = ''.join([mask_token] * seq_a_length)
seq_b_template = ''.join([mask_token] * seq_b_length)
combined_input_str = seq_a_template + '<eos>' + seq_b_template
input_tokens = tokenizer.encode(combined_input_str, add_special_tokens=True, return_tensors='pt').to(device)
output = model.mask_diffusion_generate(
tokenizer=tokenizer,
input_tokens=input_tokens,
step_divisor=10, # lower is slower but better
temperature=1.0, # sampling temperature
remasking="random", # strategy for remasking tokens not kept
preview=False, # set this to True to watch the mask tokens get rilled in real time
slow=False, # adds a small delay to the real time filling (because it is usually very fast and watching carefully is hard!)
return_trajectory=False # set this to True to return the trajectory of the generation (what you watch in the preview)
) # Note: output will be a tuple if return_trajectory is True
seqa, seqb = model.decode_dual_input(output, seperator='<eos>')
# Parse out the generated interactor part based on EOS tokens.
# Example: generated_full_seq_str.split(model_binder.tokenizer.eos_token)[1]
print(f"SeqA: {seqa[0][5:]}") # remove cls token
print(f"SeqB: {seqb[0]}")
SeqA: MVNLAKMRQRTEQNLREVSSFVKILFHTVLKFPMKINIGIHVHINMQAAQNAAADQNMQATNVIDLHNFKMGKDIGVDNKASATAHIYDEAHHTFLQLGAIKLLHAIPMIAGPVRCRLPIGFGHRFRG
SeqB: HYKNPMHSLLDSNVLHKDVVEVRLPIKIGMELDVMASAMREFLMPGTQQGDLRVIAEKRPVNKLHTYRRDLVKLLLAGAKLGTEAKSVELDLYRTELGGLVVYIININIATWDIIFAKVKICRGNDKP
Folded with Chai1:
There are various demos with many more to come. For example, in demo_dsm_ppi_full.py (run by python -m demos.demo_dsm_ppi_full) we perform a test on DSM-ppi.
We take 1000 protein pairs from BIOGRID (real protein-protein interactions) and 1000 from Negatome (non interacting protein pairs) and mask the second sequence (SeqB) by 50%.
This acts as a sanity check, as we expect the accuracy on reconstructing real positive PPIs to be higher than the accuracy on non-interacting proteins.
Indeed, this is the case:
==================================================
RESULTS COMPARISON
==================================================
Positive examples:
Mean accuracy: 0.495 ± 0.322
Processed: 1000 examples
Negative examples:
Mean accuracy: 0.227 ± 0.231
Processed: 1000 examples
Difference (Positive - Negative): 0.267
T-test: t=21.331, p=0.000
Difference is statistically significant (p < 0.05)
Clone the repository:
git clone https://github.com/Gleghorn-Lab/DSM.git
cd DSM
Initialize the submodules:
git submodule update --init --remote --recursive
Set up the Python virtual environment:
The setup_bioenv.sh script creates a virtual environment named bioenv in your home directory (~/bioenv), installs PyTorch with CUDA 12.6 support, and then installs all other dependencies from requirements.txt.
Make the script executable:
chmod +x setup_bioenv.sh
Run the script:
./setup_bioenv.sh
If you are not on a linux machine, you can install the requirements directly
python -m pip install -r requirements.txt
Activate the environment: Each time you want to work on this project, activate the virtual environment:
source ~/bioenv/bin/activate
To deactivate the environment:
deactivate
All together
git clone https://github.com/Gleghorn-Lab/DSM.git
cd DSM
git submodule update --init --remote --recursive
chmod +x setup_bioenv.sh
./setup_bioenv.sh
source ~/bioenv/bin/activate
Log in once, and the scripts find your credentials themselves:
hf auth login # Hugging Face; or set HF_TOKEN
wandb login # Weights & Biases; or set WANDB_API_KEY
Training pushes the trained model to the Hugging Face Hub and logs to Weights & Biases, so it needs both. Reading a private model or dataset needs the Hugging Face login.
The primary script for training models is training/train_dsm.py. This script further pretrains an ESM2 checkpoint using the DSM objective (masked diffusion based on LLaDA) on a large protein sequence dataset like OMG-prot50.
train_dsm.py1/(t + epsilon) where t is the corruption level, penalizing errors more at low mask rates.tau=30) and tied output projection weights to the token embeddings.data.dataset_classes.SequenceDatasetFromList for validation/test sets and data.dataset_classes.IterableDatasetFromHF for streaming training.data.data_collators.SequenceCollator is used for batching.TrainingArguments.IterableTrainer (from training.iterable_trainer.py) handles iterable datasets.Usage Example:
python -m training.train_dsm \
--model_path facebook/esm2_t33_650M_UR50D \
--save_path GleghornLab/DSM_650 \
--lr 1e-4 \
--batch_size 8 \
--grad_accum 16 \
--max_steps 100000 \
--save_every 1000 \
--fp16 \
--wandb_project "DSM_Training"
Key Command-Line Arguments for train_dsm.py:
--model_path: Path to the base ESM2 model to start from.--save_path: Path to save the trained DSM model on Hugging Face Hub.--lr: Learning rate.--batch_size: Batch size per device.--grad_accum: Gradient accumulation steps.--max_steps: Maximum training steps.--wandb_project: Wandb project name (default: DSM).--max_length: Maximum sequence length.--save_every: Save model and evaluate every N steps.--fp16: Enable mixed-precision training.--bugfix: Use small batch size and max length for debugging.The training/ directory may also contain scripts like train_dsm_bind.py.
[CLS]--SeqA--[EOS]--[MASKED~SeqB]--[EOS].And training/iterable_trainer.py provides the get_iterable_trainer function used by train_dsm.py to enable training with iterable datasets.
The repository includes a comprehensive suite for evaluating model performance, focusing on:
Sequence Reconstruction (Mask Filling):
evaluation/mask_filling.py is central to this.Unconditional Generation Quality:
evaluation/unconditional_generation_tuning.py (to find optimal generation parameters like temperature and step divisor s), evaluation/unconditional_generation.py, evaluation/ss_pred.py (using production_ss4_model or production_ss9_model), evaluation/annotate_comparisons.py, evaluation/compare_distributions.py, evaluation/plot_distribution_comparisons.py.run_eval_pipeline.py script automates this workflow.Representation Quality (Model Probing):
Conditional Generation (Binder Design for DSM-ppi):
The evaluation/ directory also contains a readme.md which provides further details on some evaluation workflows. Key metrics used include:
Functional annotations (evaluation/annotate_comparisons.py) and binding-affinity predictions (evaluation/conditional_binder.py, evaluation/unconditional_binder.py) come from the Synthyra API, and the scripts read its key from the SYNTHYRA_API_KEY environment variable. The pipeline below needs it for its annotation step. The binder scripts' --test flag substitutes random scores and needs no key.
Running the Full Unconditional Evaluation Pipeline:
python run_eval_pipeline.py --data_dir ./evaluation_results
Refer to run_eval_pipeline.py --help for more options, such as --skip_tuning.
The script evaluation/mask_filling.py is used to evaluate models on their ability to predict masked tokens in a sequence across various masking rates.
Functionality:
evaluation/plot_mask_fill_results.py.Usage Example:
python -m evaluation.mask_filling \
--batch_size 4 \
--mask_rates 0.15 0.30 0.50 \
--data_splits valid test \
--results_dir ./results/mask_fill_custom
To generate a comparison plot from existing results:
python -m evaluation.mask_filling --generate_comparison_plot --results_dir ./results/mask_fill_custom --plot_output ./results/mask_fill_custom/comparison.png
The evaluation/ directory contains additional scripts for more specific analyses. These are typically run independently:
evaluation/all_targets_uncond.py and evaluation/all_targets_cond.py: Likely for evaluating generation towards specific targets, unconditionally and conditionally.evaluation/conditional_binder.py and evaluation/unconditional_binder.py: Suggest evaluation focused on generating protein binders.evaluation/unconditional_by_length.py: May evaluate unconditional generation focusing on sequence length distributions.evaluation/utils.py: Utility functions for evaluation scripts.Users should refer to individual scripts (e.g., using python -m evaluation.<script_name> --help) for their specific usage and arguments.
The evaluation/ directory also contains a readme.md which provides further details on the unconditional generation evaluation workflow.
DSM demonstrates strong performance in both protein sequence generation and representation learning, establishing masked diffusion as a powerful paradigm.
Biomimetic Sequence Generation: Unconditionally generated DSM sequences closely mimic natural protein distributions in terms of amino acid k-mers, predicted secondary structures (JS divergence < 0.01 for AA k-mers), and predicted functional annotations (AV terms, JS divergence ~0.1). This suggests DSM captures underlying biological principles.
Superior Sequence Reconstruction: DSM models significantly outperform MLM-based ESM2 models in reconstructing sequences from highly corrupted inputs (up to 90% masking).
High-Quality Embeddings: DSM embeddings match or exceed the quality of those from comparably sized pLMs (ESM2, DPLM) and even larger autoregressive models (ProtCLM 1B) on various downstream tasks evaluated by linear probing. DSM-650 generally provides the best representations among tested models of similar size.
Effective Binder Design (DSM-ppi):
Efficiency: DSM can generate realistic protein sequences from a single forward pass during reconstruction tasks at high mask rates, offering potential efficiency advantages over iterative AR or some discrete diffusion models.
These results highlight DSM's capability to unify high-quality protein representation learning and biologically coherent generative modeling within a single framework.
We validated various DSM generated binders using biolayer interferometry through Adaptyv Bio - sending in 20 designs for EGFR and PD-L1. Sequences generated via unconditional generation were sorted hierarchically by predicted binding affinity (Synteract2), then ESMfold pLDDT, ESM2 PLL, and finally Chai1 iPTM.
Of the 13 expressed, 12 designs bound to EGFR with 11 of them binding strongly. Noteably, the top design, dsm_egfr_10, presented a mean KD in the picomolar range (861 pM), which is a ~30% increase in binding affinity vs. the winner (and our starting template) of the Adaptyv EGFR competition at 1.21 nM, and ~90% over the original starting scFV Cetuximab at 664 nM.
dsm_egfr_10
QVQLQQSGPGLVQPSQSLSITCTVSGFSLTNYGVHWVRQSPGKGLEWLGVIWSGGNTDYNTPFTSRLSISRDTSKSQVFFKMNSLQTDDTAVYYCARALTYYDYEFAYWGQGTLVTVSAGGGGSGGGGSGGGGSDILLTQSPVILSVSPGERVSFSCRASQSIGSNIHWYQQRTNGSPKLLIRYASESISGIPSRFSGSGSGTDFTLSINSVDPEDIADYYCQQNNNWPTTFGAGTKLEIK
This sequence would have won the EGFR competition by a wide margin! Of course, we piggybacked on the winning entry as our template. We have been using DSM-PPI-full to attempt to replicate competition winning binders from the Cetuximab starting point instead. Stay tuned!
<img width="1865" height="548" alt="image" src="https://github.com/user-attachments/assets/ce486326-f4ba-4604-af47-259f7bbe496f" />All 20 PD-L1 designs had high expression rates, with 15/20 binding. 1 weak, 10 medium, and 3 strong. The strongest presented with an average KD of 8.06 nM (pKD) , which is markedly less than the original template at 0.8 pM. We attribute the consistent binding but worse performance overall to the higher error between Synteract2 ppKD and true pKD of the template, implying it is not modeled well by our affinity system.
<img width="1592" height="516" alt="image" src="https://github.com/user-attachments/assets/6d2cde0e-75a4-4f29-999f-a8c601286845" /> <img src="https://github.com/Gleghorn-Lab/DSM/blob/main/wetlab_result_analysis/pdl1/kinetics/dsm_pdl1_7_1.png" width="400">@misc{hallee2025diffusionsequencemodelsenhanced,
title={Diffusion Sequence Models for Enhanced Protein Representation and Generation},
author={Logan Hallee and Nikolaos Rafailidis and David B. Bichara and Jason P. Gleghorn},
year={2025},
eprint={2506.08293},
archivePrefix={arXiv},
primaryClass={q-bio.BM},
url={https://arxiv.org/abs/2506.08293},
}