Views
No views yet
DSM model for common generation tasks. The core generation logic is provided by the GenerateMixin class, used by DSM models.1import torch
2from models.modeling_dsm import DSM # Or DSM_ppi for binder generation
3
4# Load a pre-trained model
5model_name_or_path = "GleghornLab/DSM_650" # Replace with your model of choice
6device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
7model = DSM.from_pretrained(model_name_or_path).to(device).eval()
8tokenizer = model.tokenizerYou 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.1### Unconditional generation
2length = 100
3mask_token = tokenizer.mask_token
4# optionally, enforce starting with methionine
5input_tokens = tokenizer.encode('M' + ''.join([mask_token] * (length - 1)), add_special_tokens=True, return_tensors='pt').to(device)
6output = model.mask_diffusion_generate(
7 tokenizer=tokenizer,
8 input_tokens=input_tokens,
9 step_divisor=100, # lower is slower but better
10 temperature=1.0, # sampling temperature
11 remasking="random", # strategy for remasking tokens not kept
12 preview=False, # set this to True to watch the mask tokens get rilled in real time
13 slow=False, # adds a small delay to the real time filling (because it is usually very fast and watching carefully is hard!)
14 return_trajectory=False # set this to True to return the trajectory of the generation (what you watch in the preview)
15) # Note: output will be a tuple if return_trajectory is True
16
17generated_sequences = model.decode_output(output)
18print(f"Generated sequence: {generated_sequences[0]}")Generated sequence: MFRVDALQVAQQETLAIGRSTAYDKQESPSMAQRQVLTQLAAYGGENDLRQICIPAERRNFLSIANGASYQFVEEDNEANGGYWSPHKAGLPESACKRFI1# Mask Filling / Inpainting
2template_sequence = "MA<mask><mask><mask>KEG<mask><mask>STL"
3input_tokens = tokenizer.encode(template_sequence, add_special_tokens=True, return_tensors='pt').to(device)
4
5output = model.mask_diffusion_generate(
6 tokenizer=tokenizer,
7 input_tokens=input_tokens,
8 step_divisor=100, # lower is slower but better
9 temperature=1.0, # sampling temperature
10 remasking="random", # strategy for remasking tokens not kept
11 preview=False, # set this to True to watch the mask tokens get rilled in real time
12 slow=False, # adds a small delay to the real time filling (because it is usually very fast and watching carefully is hard!)
13 return_trajectory=False # set this to True to return the trajectory of the generation (what you watch in the preview)
14) # Note: output will be a tuple if return_trajectory is True
15
16generated_sequences = model.decode_output(output)
17print(f"Generated sequence: {generated_sequences[0]}")Generated sequence: MAVKFKEGGISTL1# from models.modeling_dsm import DSM_ppi
2# model_binder = DSM_ppi.from_pretrained("GleghornLab/DSM_650_ppi_lora").to(device).eval()
3# The lora version from the paper leads to unreliable outputs
4# Synthyra has generously trained a version through full fine tuning
5
6model = DSM.from_pretrained("Synthyra/DSM_ppi_full").to(device).eval()
7
8# BBF-14
9target_seq = "MGTPLWALLGGPWRGTATYEDGTKVTLDYRYTRVSPDRLRADVTYTTPDGTTLEATVDLWKDANGVIRYHATYPDGTSADGTLTQLDADTLLATGTYDDGTKYTVTLTRVAPGSGWHHHHHH"
10# For binder generation, the 'interactor' (SeqB) part is what gets generated/filled.
11# Start with a fully masked interactor of desired length.
12interactor_template_len = 256
13interactor_template = ''.join([mask_token] * interactor_template_len)
14
15combined_input_str = target_seq + '<eos>' + interactor_template
16
17input_tokens = tokenizer.encode(combined_input_str, add_special_tokens=True, return_tensors='pt').to(device)
18
19output = model.mask_diffusion_generate(
20 tokenizer=tokenizer,
21 input_tokens=input_tokens,
22 step_divisor=100, # lower is slower but better
23 temperature=1.0, # sampling temperature
24 remasking="random", # strategy for remasking tokens not kept
25 preview=False, # set this to True to watch the mask tokens get rilled in real time
26 slow=False, # adds a small delay to the real time filling (because it is usually very fast and watching carefully is hard!)
27 return_trajectory=False # set this to True to return the trajectory of the generation (what you watch in the preview)
28) # Note: output will be a tuple if return_trajectory is True
29
30target, binder = model.decode_dual_input(output, seperator='<eos>')
31# Parse out the generated interactor part based on EOS tokens.
32# Example: generated_full_seq_str.split(model_binder.tokenizer.eos_token)[1]
33print(f"Generated binder {binder[0]}")Generated binder HRHHHRRPTHARETEWLARMRLGIAEHQRIAVPRSDLEPDQMRERAADNQRLVKEYDQVIDHQTEGSTERLFEVLRVWEQVNTEQAHHEASAALEFGRVGYPDDEGGRAFYTQANAHKKDLVEYIGGIDEDAKWDPRIAWLMPEGGQPVKATVIGVSEERINGLKVLDDHWGRERRLWLINLFTALQAYDDPTRPTQVTLTPATDQLTNDVQYLLLSTRYTPPGVTTAVKIRKLDGRTLKVLTTEAPYVVRGATLSSynthyra/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.1seq_a_length = 128
2seq_b_length = 128
3
4seq_a_template = ''.join([mask_token] * seq_a_length)
5seq_b_template = ''.join([mask_token] * seq_b_length)
6
7combined_input_str = seq_a_template + '<eos>' + seq_b_template
8
9input_tokens = tokenizer.encode(combined_input_str, add_special_tokens=True, return_tensors='pt').to(device)
10
11output = model.mask_diffusion_generate(
12 tokenizer=tokenizer,
13 input_tokens=input_tokens,
14 step_divisor=10, # lower is slower but better
15 temperature=1.0, # sampling temperature
16 remasking="random", # strategy for remasking tokens not kept
17 preview=False, # set this to True to watch the mask tokens get rilled in real time
18 slow=False, # adds a small delay to the real time filling (because it is usually very fast and watching carefully is hard!)
19 return_trajectory=False # set this to True to return the trajectory of the generation (what you watch in the preview)
20) # Note: output will be a tuple if return_trajectory is True
21
22seqa, seqb = model.decode_dual_input(output, seperator='<eos>')
23# Parse out the generated interactor part based on EOS tokens.
24# Example: generated_full_seq_str.split(model_binder.tokenizer.eos_token)[1]
25print(f"SeqA: {seqa[0][5:]}") # remove cls token
26print(f"SeqB: {seqb[0]}")1SeqA: MVNLAKMRQRTEQNLREVSSFVKILFHTVLKFPMKINIGIHVHINMQAAQNAAADQNMQATNVIDLHNFKMGKDIGVDNKASATAHIYDEAHHTFLQLGAIKLLHAIPMIAGPVRCRLPIGFGHRFRG
2SeqB: HYKNPMHSLLDSNVLHKDVVEVRLPIKIGMELDVMASAMREFLMPGTQQGDLRVIAEKRPVNKLHTYRRDLVKLLLAGAKLGTEAKSVELDLYRTELGGLVVYIININIATWDIIFAKVKICRGNDKPdemo_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:1==================================================
2RESULTS COMPARISON
3==================================================
4Positive examples:
5 Mean accuracy: 0.495 ± 0.322
6 Processed: 1000 examples
7
8Negative examples:
9 Mean accuracy: 0.227 ± 0.231
10 Processed: 1000 examples
11
12Difference (Positive - Negative): 0.267
13T-test: t=21.331, p=0.000
14Difference is statistically significant (p < 0.05)1git clone https://github.com/Gleghorn-Lab/DSM.git
2cd DSMgit submodule update --init --remote --recursivesetup_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.chmod +x setup_bioenv.sh./setup_bioenv.shpython -m pip install -r requirements.txtsource ~/bioenv/bin/activatedeactivate1git clone https://github.com/Gleghorn-Lab/DSM.git
2cd DSM
3git submodule update --init --remote --recursive
4chmod +x setup_bioenv.sh
5./setup_bioenv.sh
6source ~/bioenv/bin/activatetraining/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.1python -m training.train_dsm \
2 --model_path facebook/esm2_t33_650M_UR50D \
3 --save_path GleghornLab/DSM_650 \
4 --lr 1e-4 \
5 --batch_size 8 \
6 --grad_accum 16 \
7 --max_steps 100000 \
8 --save_every 1000 \
9 --fp16 \
10 --wandb_project "DSM_Training" \
11 --token <your_hf_token_if_needed_for_private_repo_or_saving>train_dsm.py:--token: Hugging Face token.--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.training/ directory may also contain scripts like train_dsm_bind.py.[CLS]--SeqA--[EOS]--[MASKED~SeqB]--[EOS].training/iterable_trainer.py provides the get_iterable_trainer function used by train_dsm.py to enable training with iterable datasets.evaluation/mask_filling.py is central to this.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.evaluation/ directory also contains a readme.md which provides further details on some evaluation workflows. Key metrics used include:python run_eval_pipeline.py --token YOUR_HF_TOKEN --data_dir ./evaluation_resultsrun_eval_pipeline.py --help for more options, such as --skip_tuning.evaluation/mask_filling.py is used to evaluate models on their ability to predict masked tokens in a sequence across various masking rates.evaluation/plot_mask_fill_results.py.1python -m evaluation.mask_filling \
2 --token YOUR_HF_TOKEN \
3 --batch_size 4 \
4 --mask_rates 0.15 0.30 0.50 \
5 --data_splits valid test \
6 --results_dir ./results/mask_fill_custompython -m evaluation.mask_filling --generate_comparison_plot --results_dir ./results/mask_fill_custom --plot_output ./results/mask_fill_custom/comparison.pngevaluation/ 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.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_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.QVQLQQSGPGLVQPSQSLSITCTVSGFSLTNYGVHWVRQSPGKGLEWLGVIWSGGNTDYNTPFTSRLSISRDTSKSQVFFKMNSLQTDDTAVYYCARALTYYDYEFAYWGQGTLVTVSAGGGGSGGGGSGGGGSDILLTQSPVILSVSPGERVSFSCRASQSIGSNIHWYQQRTNGSPKLLIRYASESISGIPSRFSGSGSGTDFTLSINSVDPEDIADYYCQQNNNWPTTFGAGTKLEIK|
|
![]() |

@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},
}