Downloads · 30 days
29
10% of all-time downloads
GleghornLab/production_ss9_model
production_ss9_model is a token classification model from GleghornLab. Use it when you need labels on individual words, such as names. It is set up for transformers. The card lists the license as other.
A nine-class secondary structure predictor, used in the DSM study to compare the secondary structure of generated sequences with natural ones at finer resolution than the four-class model. It is Synthyra/ESMplusplusla…
Downloads · 30 days
29
10% of all-time downloads
All-time downloads
278
Public
Parameters
596M
2.4 GB on disk
Likes
0
Public
Click a slice to open those files.
.safetensors2.4 GB · 100%
From the Hugging Face model README
A nine-class secondary structure predictor, used in the DSM study to compare the secondary structure of generated sequences with natural ones at finer resolution than the four-class model. It is Synthyra/ESMplusplus_large, the ESMC-600 architecture, with a token classification head.
Classes follow DSSP conventions plus a label for disordered residues: 0 = B (beta bridge),
1 = C (coil or loop), 2 = D (disordered), 3 = E (beta strand), 4 = G (3-10 helix),
5 = H (alpha helix), 6 = I (pi helix), 7 = S (bend), 8 = T (turn). The config carries them as
LABEL_0 to LABEL_8.
| Class | ESMplusplusForTokenClassification, model type ESMplusplus |
| Hidden size | 1,152, 36 layers, 18 attention heads |
| Vocabulary | 64 tokens, the ESMC alphabet |
| Labels | 9 |
| Weights | model.safetensors, 2.4 GB, float32 |
import torch
from transformers import AutoModelForTokenClassification, AutoTokenizer
model = AutoModelForTokenClassification.from_pretrained(
"GleghornLab/production_ss9_model", trust_remote_code=True
).eval()
tokenizer = AutoTokenizer.from_pretrained("GleghornLab/production_ss9_model", trust_remote_code=True)
tokens = tokenizer("MKTAYIAKQRQISFVKSHFSRQ", return_tensors="pt")
with torch.no_grad():
classes = model(**tokens).logits.argmax(-1)[0] # (l,)
print("".join("BCDEGHIST"[index] for index in classes[1:-1].tolist()))
Trained on the Proteinea secondary structure training set, validated on CB513 and TS115, and tested on CASP12, CASP13, and CASP14. Base models were first screened with a single transformer block probe over full-residue embeddings, hidden size 512 with rotary embeddings; ESMC-600 was chosen for the production model, balancing performance against throughput, and fine-tuned with LoRA on its attention layers instead of an external probe.
The preprint reports this model's test-set performance as ROC curves with 95% confidence intervals from DeLong's test, in supplementary figure S4b. The screening that chose its base model is in supplementary figure S3.
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},
}