├── .gitignore ├── config.py ├── README.md ├── extract.py ├── preprocess_embeddings.py ├── affinity_evaluation.py ├── finetune.py ├── create_dpo_data.py ├── utils.py ├── folding_evaluation.ipynb └── generation_evaluation.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | data 2 | nohup.out 3 | .ipynb_checkpoints 4 | weights 5 | __pycache__ 6 | wandb 7 | outputs -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import ml_collections 2 | 3 | def create_config(): 4 | config = ml_collections.ConfigDict() 5 | config.learning_rate = 1e-3 6 | config.batch_size = 64 7 | config.loss_type = "hinge" 8 | config.alpha = 1.0 9 | config.beta = 0.1 10 | 11 | config.train_data = "data/dpo/holdout_500k/dpo_train_data.csv" 12 | config.eval_data = "data/dpo/holdout_500k/dpo_val_data.csv" 13 | 14 | return config -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Finetuning ESM3 with Contrastive Preference Optimization for Antigen-Specific Antibody Design 2 | 3 | Final project for CS 582, ML for Bioinformatics. Our data is derived from [this study](https://zenodo.org/records/10831512). Using contrastive preference optimization, a variant of direct preference optimization, we can finetune ESM3 to more effectively redesign the CDR3 region of Trastuzumab bound to HER2. We find that sequences generated by the finetuned model greatly surpass those of existing protein language foundation models in terms of plausibility and edit distances from ground truth high affinity sequences. Furthermore, after folding and docking the generated antibody structures, we find that our finetuned model generates unique sequences with binding affinities comparable to those of the high affinity sequences from the training dataset. 4 | -------------------------------------------------------------------------------- /extract.py: -------------------------------------------------------------------------------- 1 | # import pandas as pd 2 | 3 | # file_path = "data/dpo_data.csv" 4 | 5 | # # Load the dataset 6 | # df_full = pd.read_csv(file_path) 7 | 8 | # # Sort by similarity in descending order 9 | # df_sorted_full = df_full.sort_values(by="similarity", ascending=False) 10 | 11 | # # Extract the top 100k rows 12 | # df_top_100k = df_sorted_full.head(int(5e5)) 13 | 14 | # # Save the top 100k rows to a new CSV file 15 | # output_file = "data/dpo/top_5e5.csv" 16 | # df_top_100k.to_csv(output_file, index=False) 17 | 18 | import pandas as pd 19 | 20 | file_path = "data/dpo/top_5e5.csv" 21 | 22 | # Load the dataset 23 | df_full = pd.read_csv(file_path) 24 | 25 | # Randomly sample rows to remove 26 | df_removed_val = df_full.sample(n=10000, random_state=42) 27 | 28 | df_remaining = df_full.drop(df_removed_val.index) 29 | 30 | df_removed_test = df_remaining.sample(n=1000, random_state=42) 31 | 32 | # Create a new DataFrame excluding the sampled rows 33 | df_train = df_remaining.drop(df_removed_test.index) 34 | 35 | # Save both DataFrames to separate CSV files 36 | df_removed_val.to_csv("data/dpo/holdout_500k/dpo_val_data.csv", index=False) 37 | df_removed_test.to_csv("data/dpo/holdout_500k/dpo_test_data.csv", index=False) 38 | df_train.to_csv("data/dpo/holdout_500k/dpo_train_data.csv", index=False) 39 | -------------------------------------------------------------------------------- /preprocess_embeddings.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import torch 3 | import os 4 | 5 | from tqdm import tqdm 6 | from huggingface_hub import HfApi 7 | from huggingface_hub import login 8 | from esm.models.esm3 import ESM3 9 | from esm.sdk.api import ESM3InferenceClient, ESMProtein, GenerationConfig, LogitsConfig 10 | 11 | hf_token = os.getenv('HF_TOKEN') 12 | if hf_token: 13 | api = HfApi() 14 | 15 | model: ESM3InferenceClient = ESM3.from_pretrained("esm3-open").to("cuda") 16 | seq_df = pd.read_csv("./data/all.csv") 17 | positive_seqs = seq_df[seq_df.label == 1].seq 18 | negative_seqs = seq_df[seq_df.label == 0].seq 19 | 20 | chunk_size = 10000 21 | 22 | for i in tqdm(range(0, len(positive_seqs), chunk_size)): 23 | pos_seq_emb_map = {} 24 | chunk_seqs = positive_seqs[i:i + chunk_size] 25 | 26 | for seq in chunk_seqs: 27 | full_seq = f"EVQLVESGGGLVQPGGSLRLSCAASGFNIKDTYIHWVRQAPGKGLEWVARIYPTNGYTRYADSVKGRFTISADTSKNTAYLQMNSLRAEDTAVYYC{seq}WGQGTLVTVSS" 28 | protein = ESMProtein(sequence=full_seq) 29 | protein_tensor = model.encode(protein) 30 | logits = model.logits(protein_tensor, LogitsConfig(sequence=True, return_embeddings=True)) 31 | pos_seq_emb_map[seq] = logits.embeddings[:,96:106] 32 | 33 | torch.save(pos_seq_emb_map, f"./data/positive_embeddings/seq_emb_checkpoint_{i // chunk_size}.pth") 34 | 35 | for i in tqdm(range(0, len(negative_seqs), chunk_size)): 36 | neg_seq_emb_map = {} 37 | chunk_seqs = negative_seqs[i:i + chunk_size] 38 | 39 | for seq in chunk_seqs: 40 | full_seq = f"EVQLVESGGGLVQPGGSLRLSCAASGFNIKDTYIHWVRQAPGKGLEWVARIYPTNGYTRYADSVKGRFTISADTSKNTAYLQMNSLRAEDTAVYYC{seq}WGQGTLVTVSS" 41 | protein = ESMProtein(sequence=full_seq) 42 | protein_tensor = model.encode(protein) 43 | logits = model.logits(protein_tensor, LogitsConfig(sequence=True, return_embeddings=True)) 44 | neg_seq_emb_map[seq] = logits.embeddings[:,96:106] 45 | 46 | torch.save(neg_seq_emb_map, f"./data/negative_embeddings/seq_emb_checkpoint_{i // chunk_size}.pth") -------------------------------------------------------------------------------- /affinity_evaluation.py: -------------------------------------------------------------------------------- 1 | import pdbfixer 2 | import openmm 3 | import torch 4 | import os 5 | import biotite.structure as struc 6 | 7 | from tqdm import tqdm 8 | from biotite.structure import AtomArray, Atom 9 | from biotite.structure.io import save_structure 10 | from biotite.structure.io.pdb import PDBFile 11 | 12 | ENERGY = openmm.unit.kilocalorie_per_mole 13 | LENGTH = openmm.unit.angstroms 14 | torch.set_num_threads(8) 15 | 16 | def openmm_relax(pdb_file, stiffness=10., tolerance=2.39, use_gpu=False): 17 | fixer = pdbfixer.PDBFixer(pdb_file) 18 | fixer.findMissingResidues() 19 | fixer.findMissingAtoms() 20 | fixer.addMissingAtoms() 21 | fixer.addMissingHydrogens() 22 | 23 | force_field = openmm.app.ForceField("amber14/protein.ff14SB.xml") 24 | modeller = openmm.app.Modeller(fixer.topology, fixer.positions) 25 | modeller.addHydrogens(force_field) 26 | system = force_field.createSystem(modeller.topology) 27 | 28 | if stiffness > 0: 29 | stiffness = stiffness * ENERGY / (LENGTH**2) 30 | force = openmm.CustomExternalForce("0.5 * k * ((x-x0)^2 + (y-y0)^2 + (z-z0)^2)") 31 | force.addGlobalParameter("k", stiffness) 32 | for p in ["x0", "y0", "z0"]: 33 | force.addPerParticleParameter(p) 34 | for residue in modeller.topology.residues(): 35 | for atom in residue.atoms(): 36 | if atom.name in ["N", "CA", "C", "CB"]: 37 | force.addParticle( 38 | atom.index, modeller.positions[atom.index] 39 | ) 40 | system.addForce(force) 41 | 42 | tolerance = tolerance 43 | integrator = openmm.LangevinIntegrator(0, 0.01, 1.0) 44 | platform = openmm.Platform.getPlatformByName("CUDA" if use_gpu else "CPU") 45 | 46 | simulation = openmm.app.Simulation(modeller.topology, system, integrator, platform) 47 | simulation.context.setPositions(modeller.positions) 48 | simulation.minimizeEnergy(tolerance) 49 | state = simulation.context.getState(getEnergy=True) 50 | energy = state.getKineticEnergy() + state.getPotentialEnergy() 51 | 52 | with open(pdb_file, "w") as f: 53 | openmm.app.PDBFile.writeFile( 54 | simulation.topology, 55 | simulation.context.getState(getPositions=True).getPositions(), 56 | f, 57 | keepIds=True 58 | ) 59 | return energy 60 | 61 | for pdb in tqdm(os.listdir("outputs/complexes/ground_truth")): 62 | openmm_relax(os.path.join("outputs/complexes/ground_truth", pdb)) -------------------------------------------------------------------------------- /finetune.py: -------------------------------------------------------------------------------- 1 | import os 2 | import torch 3 | 4 | from config import create_config 5 | from datasets import load_dataset 6 | from huggingface_hub import login 7 | from esm.models.esm3 import ESM3 8 | from trl import CPOConfig 9 | from trl.trainer.utils import DPODataCollatorWithPadding 10 | from utils import ESMCPOTrainer, ESMDataCollator 11 | from esm.tokenization.sequence_tokenizer import EsmSequenceTokenizer 12 | from peft import LoraConfig, PeftConfig 13 | from datetime import datetime 14 | 15 | # DDP is not working for some reason (cuda internal error) 16 | os.environ["CUDA_VISIBLE_DEVICES"] = "1" 17 | 18 | os.environ["WANDB_PROJECT"] = "antibody-dpo" 19 | 20 | timestamp = datetime.now().strftime("%Y%m%d-%H%M%S") 21 | 22 | # login() 23 | model = ESM3.from_pretrained("esm3-open") 24 | 25 | config = create_config() 26 | 27 | dataset = load_dataset("csv", data_files={"train": config.train_data, "eval": config.eval_data}) 28 | # split_datasets = dataset["data"].train_test_split(test_size=0.1) 29 | # train_dataset = split_datasets["train"] 30 | # test_dataset = split_datasets["test"] 31 | 32 | # Freeze all params except sequence track 33 | for name, param in model.named_parameters(): 34 | if name in [ 35 | "encoder.sequence_embed.weight", 36 | "output_heads.sequence_head.0.weight", 37 | "output_heads.sequence_head.0.bias", 38 | "output_heads.sequence_head.2.weight", 39 | "output_heads.sequence_head.2.bias", 40 | "output_heads.sequence_head.3.weight", 41 | "output_heads.sequence_head.3.bias" 42 | ]: 43 | param.requires_grad = True 44 | else: 45 | param.requires_grad = False 46 | 47 | config = CPOConfig( 48 | learning_rate=config.learning_rate, 49 | per_device_train_batch_size=config.batch_size, 50 | loss_type=config.loss_type, 51 | cpo_alpha=config.alpha, 52 | beta=config.beta, 53 | save_strategy="steps", 54 | save_steps=0.1, 55 | save_safetensors=False, 56 | output_dir=f"weights/{timestamp}", 57 | remove_unused_columns=False, 58 | generate_during_eval=True, 59 | eval_strategy="steps", 60 | eval_steps=0.1, 61 | run_name=timestamp 62 | ) 63 | 64 | trainer = ESMCPOTrainer( 65 | model=model, 66 | args=config, 67 | train_dataset=dataset["train"], 68 | eval_dataset=dataset["eval"], 69 | data_collator=ESMDataCollator(), 70 | processing_class=EsmSequenceTokenizer() 71 | ) 72 | 73 | trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad) 74 | print(f"Number of trainable parameters: {trainable_params}") 75 | 76 | trainer.train() -------------------------------------------------------------------------------- /create_dpo_data.py: -------------------------------------------------------------------------------- 1 | import os 2 | import torch 3 | import torch.nn.functional as F 4 | from tqdm import tqdm 5 | import csv 6 | 7 | positive_emb_path = "data/positive_embeddings" 8 | negative_emb_path = "data/negative_embeddings" 9 | out_file = "data/dpo_data.csv" 10 | threshold = 0.995 11 | similarity_matches = [] 12 | 13 | def load_embeddings_in_batches(emb_path, batch_size=2): 14 | emb_files = [f for f in os.listdir(emb_path) if f.endswith(".pth")] 15 | total_files = len(emb_files) 16 | 17 | for i in range(0, total_files, batch_size): 18 | batch_files = emb_files[i:i + batch_size] 19 | batch_dict = {} 20 | for emb_file in batch_files: 21 | full_path = os.path.join(emb_path, emb_file) 22 | data = torch.load(full_path, map_location="cpu") 23 | batch_dict.update(data) 24 | yield batch_dict 25 | 26 | negative_batches_list = list(load_embeddings_in_batches(negative_emb_path, batch_size=2)) 27 | positive_batches = load_embeddings_in_batches(positive_emb_path, batch_size=2) 28 | 29 | for pos_batch_idx, pos_emb_dict in enumerate(tqdm(positive_batches, desc="Processing Positive Batches")): 30 | if not pos_emb_dict: 31 | continue 32 | 33 | pos_seqs, pos_embs = zip(*pos_emb_dict.items()) 34 | pos_embs_tensor = torch.stack([torch.mean(emb.squeeze(0), dim=-1) for emb in pos_embs]) 35 | pos_embs_tensor = pos_embs_tensor.to("cuda:0") 36 | pos_norm = F.normalize(pos_embs_tensor, p=2, dim=1) 37 | 38 | for neg_batch_idx, neg_emb_dict in enumerate(tqdm(negative_batches_list, desc=f"Processing Negative Batches for Pos Batch {pos_batch_idx+1}", leave=False)): 39 | if not neg_emb_dict: 40 | continue 41 | 42 | neg_seqs, neg_embs = zip(*neg_emb_dict.items()) 43 | neg_embs_tensor = torch.stack([torch.mean(emb.squeeze(0), dim=-1) for emb in neg_embs]) 44 | neg_embs_tensor = neg_embs_tensor.to("cuda:0") 45 | neg_norm = F.normalize(neg_embs_tensor, p=2, dim=1) 46 | 47 | cos_sim_matrix = torch.mm(pos_norm, neg_norm.transpose(0, 1)) 48 | pos_indices, neg_indices = torch.where(cos_sim_matrix > threshold) 49 | 50 | for pos_idx, neg_idx in zip(pos_indices.tolist(), neg_indices.tolist()): 51 | pos_seq = pos_seqs[pos_idx] 52 | neg_seq = neg_seqs[neg_idx] 53 | similarity = cos_sim_matrix[pos_idx, neg_idx].item() 54 | 55 | similarity_matches.append({ 56 | "positive_seq": pos_seq, 57 | "negative_seq": neg_seq, 58 | "similarity": similarity 59 | }) 60 | 61 | del neg_embs_tensor, neg_norm, cos_sim_matrix, neg_indices, pos_indices 62 | torch.cuda.empty_cache() 63 | 64 | del pos_embs_tensor, pos_norm, pos_seqs, pos_embs 65 | torch.cuda.empty_cache() 66 | 67 | with open(out_file, "w", newline="") as csvfile: 68 | fieldnames = ["positive_seq", "negative_seq", "similarity"] 69 | writer = csv.DictWriter(csvfile, fieldnames=fieldnames) 70 | 71 | writer.writeheader() 72 | for match in similarity_matches: 73 | writer.writerow(match) 74 | 75 | print(f"Similarity matches saved to {out_file}") 76 | print(f"Total matches found: {len(similarity_matches)}") -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | import wandb 5 | import transformers 6 | 7 | from collections import defaultdict 8 | from packaging import version 9 | from esm.tokenization.sequence_tokenizer import EsmSequenceTokenizer 10 | from torch.utils.data import Dataset 11 | from trl import CPOTrainer, CPOConfig 12 | from contextlib import nullcontext 13 | from transformers import Trainer 14 | from trl.data_utils import maybe_extract_prompt, maybe_apply_chat_template 15 | from trl.trainer.utils import pad_to_length 16 | from typing import Any, Callable, Literal, Optional, Union, Dict 17 | from accelerate import PartialState 18 | from peft import PeftModel, get_peft_model, prepare_model_for_kbit_training 19 | from esm.sdk.api import ESMProtein, GenerationConfig 20 | 21 | class ESMDataCollator: 22 | def __call__(self, features): 23 | batch = {} 24 | for batch_dict in features: 25 | for k in batch_dict: 26 | if k not in batch: 27 | batch[k] = [] 28 | batch[k].append(batch_dict[k]) 29 | 30 | for k in batch: 31 | if k.endswith(("_input_ids", "_labels")): 32 | batch[k] = torch.tensor(batch[k]) 33 | 34 | return batch 35 | 36 | class ESMCPOTrainer(Trainer): 37 | def __init__( 38 | self, 39 | model=None, 40 | args=None, 41 | data_collator=None, 42 | train_dataset=None, 43 | eval_dataset=None, 44 | processing_class=None, 45 | model_init=None, 46 | callbacks=None, 47 | optimizers=(None, None), 48 | preprocess_logits_for_metrics=None, 49 | peft_config=None, 50 | compute_metrics=None, 51 | ): 52 | if peft_config: 53 | model = get_peft_model(model, peft_config) 54 | 55 | self.max_length = args.max_length 56 | self.generate_during_eval = args.generate_during_eval 57 | self.label_pad_token_id = args.label_pad_token_id 58 | self.padding_value = args.padding_value if args.padding_value is not None else processing_class.pad_token_id 59 | self.truncation_mode = args.truncation_mode 60 | self.max_completion_length = args.max_completion_length 61 | self.processing_class = processing_class 62 | 63 | self.beta = args.beta 64 | self.label_smoothing = args.label_smoothing 65 | self.loss_type = args.loss_type 66 | self.cpo_alpha = args.cpo_alpha 67 | 68 | self._stored_metrics = defaultdict(lambda: defaultdict(list)) 69 | 70 | with PartialState().local_main_process_first(): 71 | train_dataset = train_dataset.map( 72 | self.tokenize_row, 73 | num_proc=args.dataset_num_proc, 74 | load_from_cache_file=True 75 | ) 76 | eval_dataset = eval_dataset.map( 77 | self.tokenize_row, 78 | num_proc=args.dataset_num_proc, 79 | load_from_cache_file=True 80 | ) 81 | 82 | super().__init__( 83 | model=model, 84 | args=args, 85 | data_collator=data_collator, 86 | train_dataset=train_dataset, 87 | eval_dataset=eval_dataset, 88 | processing_class=processing_class, 89 | model_init=model_init, 90 | compute_metrics=compute_metrics, 91 | callbacks=callbacks, 92 | optimizers=optimizers, 93 | preprocess_logits_for_metrics=preprocess_logits_for_metrics, 94 | ) 95 | 96 | def tokenize_row(self, feature: Dict[str, Any]) -> Dict[str, Any]: 97 | batch = {} 98 | chosen = f"EVQLVESGGGLVQPGGSLRLSCAASGFNIKDTYIHWVRQAPGKGLEWVARIYPTNGYTRYADSVKGRFTISADTSKNTAYLQMNSLRAEDTAVYYC{feature['chosen']}WGQGTLVTVSS" 99 | rejected = f"EVQLVESGGGLVQPGGSLRLSCAASGFNIKDTYIHWVRQAPGKGLEWVARIYPTNGYTRYADSVKGRFTISADTSKNTAYLQMNSLRAEDTAVYYC{feature['rejected']}WGQGTLVTVSS" 100 | 101 | chosen_tokens = self.processing_class( 102 | chosen, truncation=False 103 | ) 104 | 105 | rejected_tokens = self.processing_class( 106 | rejected, truncation=False 107 | ) 108 | 109 | batch["chosen_input_ids"] = chosen_tokens["input_ids"] 110 | batch["rejected_input_ids"] = rejected_tokens["input_ids"] 111 | batch["chosen_labels"] = chosen_tokens["input_ids"] 112 | batch["rejected_labels"] = rejected_tokens["input_ids"] 113 | 114 | return batch 115 | 116 | def concatenated_inputs( 117 | self, 118 | batch: dict[str, Union[list, torch.LongTensor]], 119 | is_encoder_decoder: bool = False, 120 | label_pad_token_id: int = -100, 121 | padding_value: int = 1, 122 | device: Optional[torch.device] = None, 123 | ): 124 | concatenated_batch = {} 125 | max_length = max(batch["chosen_input_ids"].shape[1], batch["rejected_input_ids"].shape[1]) 126 | 127 | for k in batch: 128 | if k.startswith("chosen") and isinstance(batch[k], torch.Tensor): 129 | if "labels" in k: 130 | pad_value = label_pad_token_id 131 | elif k.endswith("_input_ids"): 132 | pad_value = padding_value 133 | elif k.endswith("_attention_mask"): 134 | pad_value = 0 135 | concatenated_key = k.replace("chosen", "concatenated") 136 | concatenated_batch[concatenated_key] = pad_to_length(batch[k], max_length, pad_value=pad_value) 137 | for k in batch: 138 | if k.startswith("rejected") and isinstance(batch[k], torch.Tensor): 139 | if "labels" in k or is_encoder_decoder: 140 | pad_value = label_pad_token_id 141 | elif k.endswith("_input_ids"): 142 | pad_value = padding_value 143 | elif k.endswith("_attention_mask"): 144 | pad_value = 0 145 | concatenated_key = k.replace("rejected", "concatenated") 146 | concatenated_batch[concatenated_key] = torch.cat( 147 | ( 148 | concatenated_batch[concatenated_key], 149 | pad_to_length(batch[k], max_length, pad_value=pad_value), 150 | ), 151 | dim=0, 152 | ).to(device=device) 153 | 154 | return concatenated_batch 155 | 156 | def cpo_loss( 157 | self, 158 | policy_chosen_logps: torch.FloatTensor, 159 | policy_rejected_logps: torch.FloatTensor, 160 | ) -> tuple[torch.FloatTensor, torch.FloatTensor, torch.FloatTensor]: 161 | """Compute the CPO loss for a batch of policy and reference model log probabilities. 162 | 163 | Args: 164 | policy_chosen_logps: Log probabilities of the policy model for the chosen responses. Shape: (batch_size,) 165 | policy_rejected_logps: Log probabilities of the policy model for the rejected responses. Shape: (batch_size,) 166 | 167 | Returns: 168 | A tuple of three tensors: (losses, chosen_rewards, rejected_rewards). 169 | The losses tensor contains the CPO loss for each example in the batch. 170 | The chosen_rewards and rejected_rewards tensors contain the rewards for the chosen and rejected responses, respectively. 171 | """ 172 | logits = (policy_chosen_logps - policy_rejected_logps).to(self.accelerator.device) 173 | 174 | # The beta is a temperature parameter for the CPO loss, typically something in the range of 0.1 to 0.5. 175 | # We ignore the reference model as beta -> 0. The label_smoothing parameter encodes our uncertainty about the labels and 176 | # calculates a conservative CPO loss. 177 | 178 | if self.loss_type == "simpo": 179 | gamma_logratios = self.simpo_gamma / self.beta 180 | logits = logits - gamma_logratios 181 | # This reduces to Equation 3 from the CPO paper when label_smoothing -> 0. 182 | losses = ( 183 | -F.logsigmoid(self.beta * logits) * (1 - self.label_smoothing) 184 | - F.logsigmoid(-self.beta * logits) * self.label_smoothing 185 | ) 186 | elif self.loss_type == "sigmoid": 187 | # This reduces to Equation 3 from the CPO paper when label_smoothing -> 0. 188 | losses = ( 189 | -F.logsigmoid(self.beta * logits) * (1 - self.label_smoothing) 190 | - F.logsigmoid(-self.beta * logits) * self.label_smoothing 191 | ) 192 | elif self.loss_type == "hinge": 193 | losses = torch.relu(1 - self.beta * logits) 194 | elif self.loss_type == "ipo": 195 | # eqn (17) of the paper where beta is the regularization parameter for the IPO loss, denoted by tau in the paper. 196 | losses = (logits - 1 / (2 * self.beta)) ** 2 197 | else: 198 | raise ValueError( 199 | f"Unknown loss type: {self.loss_type}. Should be one of ['sigmoid', 'hinge', 'ipo', 'simpo']" 200 | ) 201 | 202 | chosen_rewards = self.beta * (policy_chosen_logps.to(self.accelerator.device)).detach() 203 | rejected_rewards = self.beta * (policy_rejected_logps.to(self.accelerator.device)).detach() 204 | 205 | return losses, chosen_rewards, rejected_rewards 206 | 207 | @staticmethod 208 | def get_batch_logps( 209 | logits: torch.FloatTensor, 210 | labels: torch.LongTensor, 211 | average_log_prob: bool = False, 212 | label_pad_token_id: int = -100, 213 | is_encoder_decoder: bool = False, 214 | ) -> torch.FloatTensor: 215 | """Compute the log probabilities of the given labels under the given logits. 216 | 217 | Args: 218 | logits: Logits of the model (unnormalized). Shape: (batch_size, sequence_length, vocab_size) 219 | labels: Labels for which to compute the log probabilities. Label tokens with a value of label_pad_token_id are ignored. Shape: (batch_size, sequence_length) 220 | average_log_prob: If True, return the average log probability per (non-masked) token. Otherwise, return the sum of the log probabilities of the (non-masked) tokens. 221 | label_pad_token_id: The label pad token id. 222 | is_encoder_decoder: Whether the model is an encoder-decoder model. 223 | 224 | Returns: 225 | A tensor of shape (batch_size,) containing the average/sum log probabilities of the given labels under the given logits. 226 | """ 227 | if logits.shape[:-1] != labels.shape: 228 | raise ValueError("Logits (batch and sequence length dim) and labels must have the same shape.") 229 | 230 | loss_mask = labels != label_pad_token_id 231 | 232 | per_token_logps = torch.gather(logits.log_softmax(-1), dim=2, index=labels.unsqueeze(2)).squeeze(2) 233 | 234 | if average_log_prob: 235 | return (per_token_logps * loss_mask).sum(-1) / loss_mask.sum(-1) 236 | else: 237 | return (per_token_logps * loss_mask).sum(-1) 238 | 239 | def concatenated_forward( 240 | self, model: nn.Module, batch: dict[str, Union[list, torch.LongTensor]] 241 | ) -> tuple[torch.FloatTensor, torch.FloatTensor, torch.FloatTensor, torch.FloatTensor]: 242 | """Run the given model on the given batch of inputs, concatenating the chosen and rejected inputs together. 243 | 244 | We do this to avoid doing two forward passes, because it's faster for FSDP. 245 | """ 246 | concatenated_batch = self.concatenated_inputs( 247 | batch, 248 | device=self.accelerator.device, 249 | ) 250 | len_chosen = batch["chosen_labels"].shape[0] 251 | 252 | if type(model).__name__ == "EsmForMaskedLM": 253 | outputs = model(concatenated_batch["concatenated_input_ids"]) 254 | all_logits = outputs.logits[:,97:107] 255 | else: 256 | with torch.amp.autocast("cuda", dtype=torch.bfloat16): 257 | outputs = model( 258 | sequence_tokens=concatenated_batch["concatenated_input_ids"] 259 | ) 260 | all_logits = outputs.sequence_logits[:,97:107] 261 | 262 | def cross_entropy_loss(logits, labels): 263 | # Flatten the tokens 264 | loss_fct = nn.CrossEntropyLoss() 265 | logits = logits.contiguous().view(-1, logits.shape[-1]) 266 | labels = labels.contiguous().view(-1) 267 | # Enable model parallelism 268 | labels = labels.to(logits.device) 269 | loss = loss_fct(logits, labels) 270 | return loss 271 | 272 | labels = concatenated_batch["concatenated_labels"][:,97:107].clone() 273 | 274 | if self.cpo_alpha == 0: 275 | nll_loss = torch.tensor(0.0).to(self.accelerator.device) 276 | else: 277 | nll_loss = cross_entropy_loss(all_logits, labels) 278 | 279 | all_logps = self.get_batch_logps( 280 | all_logits, 281 | labels, 282 | average_log_prob=self.loss_type in ["ipo", "simpo"], 283 | label_pad_token_id=self.label_pad_token_id 284 | ) 285 | 286 | chosen_logps = all_logps[:len_chosen] 287 | rejected_logps = all_logps[len_chosen:] 288 | 289 | chosen_logits = all_logits[:len_chosen] 290 | rejected_logits = all_logits[len_chosen:] 291 | 292 | return (chosen_logps, rejected_logps, chosen_logits, rejected_logits, nll_loss) 293 | 294 | def get_batch_loss_metrics( 295 | self, 296 | model, 297 | batch: dict[str, Union[list, torch.LongTensor]], 298 | train_eval: Literal["train", "eval"] = "train", 299 | ): 300 | """Compute the CPO loss and other metrics for the given batch of inputs for train or test.""" 301 | metrics = {} 302 | 303 | forward_output = self.concatenated_forward(model, batch) 304 | ( 305 | policy_chosen_logps, 306 | policy_rejected_logps, 307 | policy_chosen_logits, 308 | policy_rejected_logits, 309 | policy_nll_loss, 310 | ) = forward_output[:5] 311 | 312 | losses, chosen_rewards, rejected_rewards = self.cpo_loss( 313 | policy_chosen_logps, 314 | policy_rejected_logps, 315 | ) 316 | 317 | loss = losses.mean() + self.cpo_alpha * policy_nll_loss 318 | reward_accuracies = (chosen_rewards > rejected_rewards).float() 319 | 320 | prefix = "eval_" if train_eval == "eval" else "" 321 | metrics[f"{prefix}rewards/chosen"] = chosen_rewards.mean().cpu() 322 | metrics[f"{prefix}rewards/rejected"] = rejected_rewards.mean().cpu() 323 | metrics[f"{prefix}rewards/accuracies"] = reward_accuracies.mean().cpu() 324 | metrics[f"{prefix}rewards/margins"] = (chosen_rewards - rejected_rewards).mean().cpu() 325 | metrics[f"{prefix}logps/rejected"] = policy_rejected_logps.detach().mean().cpu() 326 | metrics[f"{prefix}logps/chosen"] = policy_chosen_logps.detach().mean().cpu() 327 | metrics[f"{prefix}logits/rejected"] = policy_rejected_logits.detach().mean().cpu() 328 | metrics[f"{prefix}logits/chosen"] = policy_chosen_logits.detach().mean().cpu() 329 | metrics[f"{prefix}nll_loss"] = policy_nll_loss.detach().mean().cpu() 330 | 331 | return loss, metrics 332 | 333 | def compute_loss( 334 | self, 335 | model, 336 | inputs, 337 | return_outputs=False, 338 | num_items_in_batch=None, 339 | ) -> Union[torch.Tensor, tuple[torch.Tensor, dict[str, torch.Tensor]]]: 340 | loss, metrics = self.get_batch_loss_metrics(model, inputs, train_eval="train") 341 | 342 | # force log the metrics 343 | self.store_metrics(metrics, train_eval="train") 344 | 345 | if return_outputs: 346 | return (loss, metrics) 347 | return loss 348 | 349 | def generate_from_model(self, model) -> str: 350 | # if type(model).__name__ == "EsmForMaskedLM": 351 | # prompt = f"EVQLVESGGGLVQPGGSLRLSCAASGFNIKDTYIHWVRQAPGKGLEWVARIYPTNGYTRYADSVKGRFTISADTSKNTAYLQMNSLRAEDTAVYYC{"".join(["" for _ in range(10)])}WGQGTLVTVSS" 352 | # inputs = self.processing_class(prompt, return_tensors="pt") 353 | # with torch.no_grad(): 354 | # logits = model(**inputs).logits 355 | 356 | # else: 357 | prompt = "EVQLVESGGGLVQPGGSLRLSCAASGFNIKDTYIHWVRQAPGKGLEWVARIYPTNGYTRYADSVKGRFTISADTSKNTAYLQMNSLRAEDTAVYYC__________WGQGTLVTVSS" 358 | protein = ESMProtein(sequence=prompt) 359 | protein = model.generate(protein, GenerationConfig(track="sequence", num_steps=4, temperature=0.1)) 360 | 361 | return protein.sequence[96:106] 362 | 363 | def prediction_step( 364 | self, 365 | model, 366 | inputs, 367 | prediction_loss_only, 368 | ignore_keys=None, 369 | ): 370 | if ignore_keys is None: 371 | if hasattr(model, "config"): 372 | ignore_keys = getattr(model.config, "keys_to_ignore_at_inference", []) 373 | else: 374 | ignore_keys = [] 375 | 376 | with torch.no_grad(): 377 | loss, metrics = self.get_batch_loss_metrics(model, inputs, train_eval="eval") 378 | 379 | # force log the metrics 380 | self.store_metrics(metrics, train_eval="eval") 381 | 382 | if prediction_loss_only: 383 | return (loss.detach(), None, None) 384 | 385 | # logits for the chosen and rejected samples from model 386 | logits_dict = { 387 | "eval_logits/chosen": metrics["eval_logits/chosen"], 388 | "eval_logits/rejected": metrics["eval_logits/rejected"], 389 | } 390 | logits = tuple(v.unsqueeze(dim=0) for k, v in logits_dict.items() if k not in ignore_keys) 391 | logits = torch.stack(logits).mean(axis=1).to(self.accelerator.device) 392 | labels = torch.zeros(logits.shape[0], device=self.accelerator.device) 393 | 394 | return (loss.detach(), logits, labels) 395 | 396 | def store_metrics(self, metrics: dict[str, float], train_eval: Literal["train", "eval"] = "train") -> None: 397 | for key, value in metrics.items(): 398 | self._stored_metrics[train_eval][key].append(value) 399 | 400 | def evaluation_loop( 401 | self, 402 | dataloader, 403 | description, 404 | prediction_loss_only=None, 405 | ignore_keys=None, 406 | metric_key_prefix="eval", 407 | ): 408 | """ 409 | Overriding built-in evaluation loop to store metrics for each batch. 410 | Prediction/evaluation loop, shared by `Trainer.evaluate()` and `Trainer.predict()`. 411 | 412 | Works both with or without labels. 413 | """ 414 | 415 | # Sample and save to game log if requested (for one batch to save time) 416 | if self.generate_during_eval: 417 | # Generate random indices within the range of the total number of samples 418 | 419 | generated_sequence = self.generate_from_model(self.model) 420 | 421 | self.log( 422 | { 423 | "seq_log": wandb.Table( 424 | columns=["Sequences"], 425 | rows=[[generated_sequence]] 426 | ) 427 | } 428 | ) 429 | self.state.log_history.pop() 430 | 431 | # Base evaluation 432 | initial_output = super().evaluation_loop( 433 | dataloader, description, prediction_loss_only, ignore_keys, metric_key_prefix 434 | ) 435 | 436 | return initial_output 437 | 438 | def log(self, logs: dict[str, float], start_time: Optional[float] = None) -> None: 439 | """ 440 | Log `logs` on the various objects watching training, including stored metrics. 441 | 442 | Args: 443 | logs (`dict[str, float]`): 444 | The values to log. 445 | start_time (`float` or `None`, *optional*, defaults to `None`): 446 | Start time of the training. 447 | """ 448 | # logs either has 'loss' or 'eval_loss' 449 | train_eval = "train" if "loss" in logs else "eval" 450 | # Add averaged stored metrics to logs 451 | for key, metrics in self._stored_metrics[train_eval].items(): 452 | logs[key] = torch.tensor(metrics).mean().item() 453 | del self._stored_metrics[train_eval] 454 | 455 | if version.parse(transformers.__version__) >= version.parse("4.47.0.dev0"): 456 | return super().log(logs, start_time) 457 | else: # transformers<=4.46 458 | return super().log(logs) -------------------------------------------------------------------------------- /folding_evaluation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 45, 6 | "id": "b4c4c5a5-42e9-42a8-a292-74c57f035fba", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import torch\n", 11 | "import pandas as pd\n", 12 | "import numpy as np\n", 13 | "import seaborn as sns\n", 14 | "import tmtools\n", 15 | "import os\n", 16 | "\n", 17 | "from tmtools.io import get_structure, get_residue_data\n", 18 | "from tmtools import tm_align\n", 19 | "from iglm import IgLM\n", 20 | "from safetensors.torch import load_file\n", 21 | "from esm.models.esm3 import ESM3\n", 22 | "from transformers import AutoModel\n", 23 | "from esm.sdk.api import ESMProtein, GenerationConfig\n", 24 | "from matplotlib import pyplot as plt\n", 25 | "from nltk.metrics import edit_distance\n", 26 | "from tqdm import tqdm\n", 27 | "from antiberty import AntiBERTyRunner" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 26, 33 | "id": "d4cca4fe-02d9-4922-99d4-1b7aea4a8d2a", 34 | "metadata": {}, 35 | "outputs": [ 36 | { 37 | "name": "stdout", 38 | "output_type": "stream", 39 | "text": [ 40 | "\n", 41 | "\n" 42 | ] 43 | } 44 | ], 45 | "source": [] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 2, 50 | "id": "671831e8-e634-42bf-a1d3-493c249eb3e7", 51 | "metadata": {}, 52 | "outputs": [ 53 | { 54 | "name": "stderr", 55 | "output_type": "stream", 56 | "text": [ 57 | "Fetching 22 files: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 22/22 [00:00<00:00, 61434.55it/s]\n", 58 | "/home/av47/miniconda3/envs/esm/lib/python3.12/site-packages/esm/pretrained.py:68: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n", 59 | " state_dict = torch.load(\n" 60 | ] 61 | } 62 | ], 63 | "source": [ 64 | "base_model = ESM3.from_pretrained(\"esm3-open\", device=torch.device(\"cuda:0\"))\n", 65 | "finetuned_model = ESM3.from_pretrained(\"esm3-open\", device=torch.device(\"cuda:0\"))" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": 3, 71 | "id": "0415b61c-1ed8-44c2-9042-24e18a81e4e4", 72 | "metadata": {}, 73 | "outputs": [], 74 | "source": [ 75 | "def load_weights(path, model):\n", 76 | " state_dict = torch.load(path, map_location=\"cuda:0\")\n", 77 | " new_dict = {}\n", 78 | " \n", 79 | " for k, v in state_dict.items():\n", 80 | " if k in model.state_dict():\n", 81 | " new_dict[k] = v\n", 82 | " model.load_state_dict(new_dict)" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": 4, 88 | "id": "7a1ec6fb-e2a2-433c-bf3d-b4bb48835cbb", 89 | "metadata": {}, 90 | "outputs": [ 91 | { 92 | "name": "stderr", 93 | "output_type": "stream", 94 | "text": [ 95 | "/tmp/ipykernel_3914019/3178905372.py:2: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n", 96 | " state_dict = torch.load(path, map_location=\"cuda:0\")\n" 97 | ] 98 | } 99 | ], 100 | "source": [ 101 | "load_weights(\"weights/20241201-144617/checkpoint-46362/pytorch_model.bin\", finetuned_model)" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 12, 107 | "id": "4817dbe7-7cb9-4cdb-a0e3-995c34822c0d", 108 | "metadata": {}, 109 | "outputs": [], 110 | "source": [ 111 | "def fold_sequence(sequence, model, save_dir):\n", 112 | " prompt = f\"EVQLVESGGGLVQPGGSLRLSCAASGFNIKDTYIHWVRQAPGKGLEWVARIYPTNGYTRYADSVKGRFTISADTSKNTAYLQMNSLRAEDTAVYYC{sequence}WGQGTLVTVSS\"\n", 113 | " protein = ESMProtein(sequence=prompt)\n", 114 | " protein = model.generate(protein, GenerationConfig(track=\"structure\", num_steps=8))\n", 115 | " protein.to_pdb(f\"{save_dir}/{sequence}.pdb\")" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 47, 121 | "id": "2f099951-b1ae-4c8b-8c13-39c82fface1b", 122 | "metadata": { 123 | "scrolled": true 124 | }, 125 | "outputs": [ 126 | { 127 | "name": "stderr", 128 | "output_type": "stream", 129 | "text": [ 130 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 13.89it/s]\n", 131 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 14.70it/s]\n", 132 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 14.74it/s]\n", 133 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 14.74it/s]\n", 134 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 18.87it/s]\n", 135 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.58it/s]\n", 136 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.71it/s]\n", 137 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.83it/s]\n", 138 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.78it/s]\n", 139 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.68it/s]\n", 140 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.72it/s]\n", 141 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.81it/s]\n", 142 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.78it/s]\n", 143 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.80it/s]\n", 144 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.78it/s]\n", 145 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.70it/s]\n", 146 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.71it/s]\n", 147 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.71it/s]\n", 148 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.73it/s]\n", 149 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.71it/s]\n", 150 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.75it/s]\n", 151 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.76it/s]\n", 152 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.71it/s]\n", 153 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.77it/s]\n", 154 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.72it/s]\n", 155 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.72it/s]\n", 156 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.82it/s]\n", 157 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.75it/s]\n", 158 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.79it/s]\n", 159 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.80it/s]\n", 160 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.78it/s]\n", 161 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.75it/s]\n", 162 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.76it/s]\n", 163 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.77it/s]\n", 164 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.77it/s]\n", 165 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.77it/s]\n", 166 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.74it/s]\n", 167 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.34it/s]\n", 168 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.68it/s]\n", 169 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.67it/s]\n", 170 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.62it/s]\n", 171 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.82it/s]\n", 172 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.74it/s]\n", 173 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.68it/s]\n", 174 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.74it/s]\n", 175 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.79it/s]\n", 176 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.77it/s]\n", 177 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.79it/s]\n", 178 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.68it/s]\n", 179 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.76it/s]\n", 180 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.84it/s]\n", 181 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.66it/s]\n", 182 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.79it/s]\n", 183 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.73it/s]\n", 184 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.74it/s]\n", 185 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.80it/s]\n", 186 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.73it/s]\n", 187 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.74it/s]\n", 188 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.71it/s]\n", 189 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 23.76it/s]\n" 190 | ] 191 | } 192 | ], 193 | "source": [ 194 | "for file in os.listdir(\"outputs/ABodyBuilder2_pdb\"):\n", 195 | " fold_sequence(file[:-4], base_model, \"outputs/esm_pdb/base\")\n", 196 | " fold_sequence(file[:-4], finetuned_model, \"outputs/esm_pdb/finetuned\")" 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": 62, 202 | "id": "8ad61097-0275-4029-8aec-abd9ce6daa32", 203 | "metadata": {}, 204 | "outputs": [], 205 | "source": [ 206 | "base_rmsd = []\n", 207 | "finetuned_rmsd = []\n", 208 | "\n", 209 | "for file in os.listdir(\"outputs/ABodyBuilder2_pdb\"):\n", 210 | " template_struct = get_structure(f\"outputs/ABodyBuilder2_pdb/{file}\")\n", 211 | " template_chain = next(template_struct.get_chains())\n", 212 | " template_coords, template_seq = get_residue_data(template_chain)\n", 213 | "\n", 214 | " base_struct = get_structure(f\"outputs/esm_pdb/base/{file}\")\n", 215 | " base_chain = next(base_struct.get_chains())\n", 216 | " base_coords, base_seq = get_residue_data(base_chain)\n", 217 | "\n", 218 | " finetuned_struct = get_structure(f\"outputs/esm_pdb/finetuned/{file}\")\n", 219 | " finetuned_chain = next(finetuned_struct.get_chains())\n", 220 | " finetuned_coords, finetuned_seq = get_residue_data(finetuned_chain)\n", 221 | " \n", 222 | " base_res = tm_align(template_coords[96:106], base_coords[96:106], template_seq[96:106], base_seq[96:106])\n", 223 | " finetuned_res = tm_align(template_coords[96:106], finetuned_coords[96:106], template_seq[96:106], finetuned_seq[96:106])\n", 224 | " \n", 225 | " base_rmsd.append(base_res.rmsd)\n", 226 | " finetuned_rmsd.append(finetuned_res.rmsd)" 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": 68, 232 | "id": "e276482a-55d1-48e6-81c7-5bb4fff7f327", 233 | "metadata": {}, 234 | "outputs": [ 235 | { 236 | "data": { 237 | "image/png": "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sQmX/K4fqgpl/Q5W5P1b8jMqRERUtTw8PBge6KUhi7t+iIiIiErCoEJERESyxaBCREREssWgQkRERLLFoEJERESyxaBCREREssWgQkRERLLFz1EhIqphXuYPfOvSpQtatGiB7777zmDblJubN2/C29sb58+fR4sWLaQuR3IMKkRENYhKpYK/f0NkZ+dUyXiWlhaIjf2rQmFlxIgRWLNmTbH2+Ph4bNu2DaampoYsESNGjMCDBw9K/eJAOerSpQsOHz5crP3DDz/E8uXLAQCHDx/GnDlzEBMTg9zcXNStWxcdOnTAypUrYWZmhujoaHTt2hX29vZQq9U6nx57+vRptG3bFgC0X3MTFxeHjz76CFevXkV6ejrc3Nzw3nvvISwszODH5GkMKkRENYhGo0F2dg7WffEO/D3qVOpYsapUDJ23CRqNpsJnVXr27ImIiAidtjp16sDY2NiQJb7UxowZg3/96186bUVfbnj16lX07NkTH3/8MX744QdYWFggPj4eW7duRUFBgc46NjY22L59O4YMGaJtW7VqFTw8PKBSqbRtpqamGD58OAICAmBvb48LFy5gzJgxKCwsxLx58yrt52RQISKqgfw96iCgfl2pyyiVUqmEi4tLsfZnL/14eXnhgw8+wPXr17F582Y4ODhgxowZ+OCDD7Tr3L59G5999hn27t0LIyMjdOrUCd9//z28vLwwe/Zs7dkbhUIBADh06BAAoGvXrkhLS4O9vT0AICYmBi1btkRiYiK8vLwQGRmJiRMnIioqChMnTsTt27fx2muvISIiQueLC3/66ScsWrRIu94nn3yCcePGaZ//888/8eGHHyI2NhZNmjTB9OnTy7WPLC0tS9xHALB37164uLhg4cKF2jYfHx/07NmzWN/g4GCsXr1aG1RycnKwceNGfPLJJ/jyyy+1/V555RW88sor2mVPT09ER0fj6NGj5apXX5xMS0REL7VFixahdevWOH/+PMaNG4exY8ciLi4OAJCfn4/AwEDY2Njg6NGjOH78OKytrdGzZ088evQIkydPxjvvvIOePXtCrVZDrVajQ4cO5R47Ozsb33zzDX7++WccOXIEKpUKkydP1j6/fv16zJo1C1999RViY2Mxb948zJw5UxuOMjMz8dZbb6FRo0Y4e/YsZs+erbO+vlxcXKBWq3HkyJHn9h02bBiOHj2qPXuydetW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3Lx5EydOnMCAAQNgbGyMIUOGSFkWERERyYSkc1SSkpIwZMgQ3Lt3D3Xq1MFrr72GU6dOoU6dl2MSGhEREVUuSYPKxo0bpRyeiIiIZE5Wc1SIiIiInsagQkRERLLFoEJERESyxaBCREREssWgQkRERLLFoEJERESyxaBCREREssWgQkRERLLFoEJERESyxaBCREREssWgQkRERLLFoEJERESyxaBCREREssWgQkRERLLFoEJERESyxaBCREREssWgQkRERLLFoEJERESyxaBCREREssWgQkRERLLFoEJERESyxaBCREREssWgQkRERLLFoEJERESyxaBCREREssWgQkRERLLFoEJERESypVdQuXHjhqHrICIiIipGr6Di6+uLrl27Yt26dcjNzTV0TUREREQA9Awq586dQ7NmzRASEgIXFxd8+OGH+PPPPw1dGxEREdVwegWVFi1a4Pvvv8edO3ewevVqqNVqvPbaa2jSpAkWL16M1NRUQ9dJRERENdALTaY1MTHBwIEDsXnzZixYsADXr1/H5MmT4e7ujuHDh0OtVhuqTiIiIqqBXiionDlzBuPGjYOrqysWL16MyZMnIyEhAfv27cOdO3fQr18/Q9VJRERENZCJPistXrwYERERiIuLQ+/evbF27Vr07t0bRkZPco+3tzciIyPh5eVlyFqJiIiohtErqCxbtgwjR47EiBEj4OrqWmIfJycnrFq16oWKIyIioppNr6ASHx//3D5mZmYIDg7WZ/NEREREAPScoxIREYHNmzcXa9+8eTPWrFnzwkURERERAXoGlfDwcDg6OhZrd3Jywrx58164KCIiIiJAz6CiUqng7e1drN3T0xMqleqFiyIiIiIC9AwqTk5OuHjxYrH2CxcuoHbt2noVMn/+fCgUCkycOFGv9YmIiKj60SuoDBkyBJ988gkOHTqEgoICFBQU4ODBg/j000/x7rvvVnh7p0+fxooVK9CsWTN9yiEiIqJqSq+g8uWXX6Jdu3Z48803YWFhAQsLC/To0QNvvPFGheeoZGZmIigoCCtXroSDg4M+5RAREVE1pdftyWZmZoiKisKXX36JCxcuwMLCAk2bNoWnp2eFtzV+/Hj06dMH3bp1w9y5c8vsm5eXh7y8PO1yRkZGhcerTlQqFTQajU6bWq3GgwcPqmT8xMREAMBdVcILbcfKzgEOTm6GKImI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", 238 | "text/plain": [ 239 | "
" 240 | ] 241 | }, 242 | "metadata": {}, 243 | "output_type": "display_data" 244 | } 245 | ], 246 | "source": [ 247 | "sns.histplot(base_rmsd, bins=10, alpha=0.5, label=\"Base ESM3\")\n", 248 | "sns.histplot(finetuned_rmsd, bins=10, alpha=0.5, label=\"Finetuned ESM3\")\n", 249 | "plt.xlabel(\"Self-consistency RMSD (scRMSD)\")\n", 250 | "plt.ylabel(\"Frequency\")\n", 251 | "plt.legend()\n", 252 | "plt.show()" 253 | ] 254 | } 255 | ], 256 | "metadata": { 257 | "kernelspec": { 258 | "display_name": "esm", 259 | "language": "python", 260 | "name": "esm" 261 | }, 262 | "language_info": { 263 | "codemirror_mode": { 264 | "name": "ipython", 265 | "version": 3 266 | }, 267 | "file_extension": ".py", 268 | "mimetype": "text/x-python", 269 | "name": "python", 270 | "nbconvert_exporter": "python", 271 | "pygments_lexer": "ipython3", 272 | "version": "3.12.7" 273 | } 274 | }, 275 | "nbformat": 4, 276 | "nbformat_minor": 5 277 | } 278 | -------------------------------------------------------------------------------- /generation_evaluation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "e657ac60-ded3-4bb7-904e-49c9a03dd175", 7 | "metadata": {}, 8 | "outputs": [ 9 | { 10 | "ename": "ModuleNotFoundError", 11 | "evalue": "No module named 'torch'", 12 | "output_type": "error", 13 | "traceback": [ 14 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 15 | "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", 16 | "Cell \u001b[0;32mIn[1], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mtorch\u001b[39;00m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mpandas\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mpd\u001b[39;00m\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mnumpy\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mnp\u001b[39;00m\n", 17 | "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'torch'" 18 | ] 19 | } 20 | ], 21 | "source": [ 22 | "import torch\n", 23 | "import pandas as pd\n", 24 | "import numpy as np\n", 25 | "import seaborn as sns\n", 26 | "\n", 27 | "from iglm import IgLM\n", 28 | "from safetensors.torch import load_file\n", 29 | "from esm.models.esm3 import ESM3\n", 30 | "from transformers import AutoModel\n", 31 | "from esm.sdk.api import ESMProtein, GenerationConfig\n", 32 | "from matplotlib import pyplot as plt\n", 33 | "from nltk.metrics import edit_distance\n", 34 | "from tqdm import tqdm\n", 35 | "from antiberty import AntiBERTyRunner" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 2, 41 | "id": "eb7679aa-8a14-4c36-bcdd-82d4a2b5490d", 42 | "metadata": {}, 43 | "outputs": [ 44 | { 45 | "name": "stderr", 46 | "output_type": "stream", 47 | "text": [ 48 | "Fetching 22 files: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 22/22 [00:00<00:00, 58438.69it/s]\n", 49 | "/home/av47/miniconda3/envs/esm/lib/python3.12/site-packages/esm/pretrained.py:68: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n", 50 | " state_dict = torch.load(\n" 51 | ] 52 | } 53 | ], 54 | "source": [ 55 | "base_model = ESM3.from_pretrained(\"esm3-open\", device=torch.device(\"cuda:1\"))\n", 56 | "finetuned_model = ESM3.from_pretrained(\"esm3-open\", device=torch.device(\"cuda:1\"))" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 4, 62 | "id": "7f90b680-6032-4f53-9a22-697af76dbcb0", 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "def load_weights(path, model):\n", 67 | " state_dict = torch.load(path, map_location=\"cuda:1\")\n", 68 | " new_dict = {}\n", 69 | " \n", 70 | " for k, v in state_dict.items():\n", 71 | " if k in model.state_dict():\n", 72 | " new_dict[k] = v\n", 73 | " model.load_state_dict(new_dict)" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": 5, 79 | "id": "492f7d49-d3f8-446a-b1a2-a64a039a16d3", 80 | "metadata": {}, 81 | "outputs": [ 82 | { 83 | "name": "stderr", 84 | "output_type": "stream", 85 | "text": [ 86 | "/tmp/ipykernel_3909169/2961478943.py:2: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n", 87 | " state_dict = torch.load(path, map_location=\"cuda:1\")\n" 88 | ] 89 | } 90 | ], 91 | "source": [ 92 | "load_weights(\"weights/20241201-144617/checkpoint-46362/pytorch_model.bin\", finetuned_model)" 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": 15, 98 | "id": "83a634ec-a53d-4a4f-89f6-bee958e99d27", 99 | "metadata": {}, 100 | "outputs": [], 101 | "source": [ 102 | "def generate_sequences(model, N, batch_size, num_steps, temperature):\n", 103 | " prompt = \"EVQLVESGGGLVQPGGSLRLSCAASGFNIKDTYIHWVRQAPGKGLEWVARIYPTNGYTRYADSVKGRFTISADTSKNTAYLQMNSLRAEDTAVYYC__________WGQGTLVTVSS\"\n", 104 | " protein = ESMProtein(sequence=prompt)\n", 105 | " protein_list = [ESMProtein(sequence=prompt)] * batch_size\n", 106 | " config_list = [GenerationConfig(track=\"sequence\", num_steps=num_steps, temperature=temperature)] * batch_size\n", 107 | " generated_sequences = []\n", 108 | " for _ in range(N//batch_size):\n", 109 | " generated_seqs = model.batch_generate(protein_list, config_list)\n", 110 | " generated_sequences.extend([seq.sequence[96:106] for seq in generated_seqs])\n", 111 | " return generated_sequences" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 22, 117 | "id": "90af064e-c44f-4adf-b69b-bb384d5cdd75", 118 | "metadata": { 119 | "scrolled": true 120 | }, 121 | "outputs": [ 122 | { 123 | "name": "stderr", 124 | "output_type": "stream", 125 | "text": [ 126 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.75it/s]\n", 127 | "/home/av47/miniconda3/envs/esm/lib/python3.12/site-packages/esm/pretrained.py:49: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n", 128 | " state_dict = torch.load(\n", 129 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.93it/s]\n", 130 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.93it/s]\n", 131 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.92it/s]\n", 132 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.92it/s]\n", 133 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.92it/s]\n", 134 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.92it/s]\n", 135 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.92it/s]\n", 136 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.91it/s]\n", 137 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.91it/s]\n", 138 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.91it/s]\n", 139 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.91it/s]\n", 140 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.91it/s]\n", 141 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.91it/s]\n", 142 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.90it/s]\n", 143 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.91it/s]\n", 144 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.90it/s]\n", 145 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.90it/s]\n", 146 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.90it/s]\n", 147 | "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 8/8 [00:04<00:00, 1.90it/s]\n" 148 | ] 149 | } 150 | ], 151 | "source": [ 152 | "base_seqs = generate_sequences(base_model, 1000, 100, 8, 1.0)\n", 153 | "finetuned_seqs = generate_sequences(finetuned_model, 1000, 100, 8, 1.0)" 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": 30, 159 | "id": "c326a2db-da28-4ba0-9238-429df11b8221", 160 | "metadata": {}, 161 | "outputs": [ 162 | { 163 | "name": "stderr", 164 | "output_type": "stream", 165 | "text": [ 166 | "100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:38<00:00, 26.15it/s]\n" 167 | ] 168 | } 169 | ], 170 | "source": [ 171 | "iglm = IgLM()\n", 172 | "\n", 173 | "parent_sequence = \"EVQLVESGGGLVQPGGSLRLSCAASGFNIKDTYIHWVRQAPGKGLEWVARIYPTNGYTRYADSVKGRFTISADTSKNTAYLQMNSLRAEDTAVYYCXXXXXXXXXXWGQGTLVTVSS\"\n", 174 | "chain_token = \"[HEAVY]\"\n", 175 | "species_token = \"[HUMAN]\"\n", 176 | "infill_range = (96, 106)\n", 177 | "num_seqs = 1000\n", 178 | "\n", 179 | "generated_seqs = iglm.infill(\n", 180 | " parent_sequence,\n", 181 | " chain_token,\n", 182 | " species_token,\n", 183 | " infill_range=infill_range,\n", 184 | " num_to_generate=num_seqs,\n", 185 | ")\n", 186 | "\n", 187 | "iglm_seqs = [seq[96:106] for seq in generated_seqs]" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": 31, 193 | "id": "4c8e2d49-b813-42da-8c8a-ff743634eb26", 194 | "metadata": {}, 195 | "outputs": [], 196 | "source": [ 197 | "with open(\"outputs/esm3_base_seqs.txt\", \"w\") as f:\n", 198 | " for seq in base_seqs:\n", 199 | " f.write(f\"{seq}\\n\")\n", 200 | "\n", 201 | "with open(\"outputs/esm3_finetuned_seqs.txt\", \"w\") as f:\n", 202 | " for seq in finetuned_seqs:\n", 203 | " f.write(f\"{seq}\\n\")\n", 204 | "\n", 205 | "with open(\"outputs/iglm_seqs.txt\", \"w\") as f:\n", 206 | " for seq in iglm_seqs:\n", 207 | " f.write(f\"{seq}\\n\")" 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": 24, 213 | "id": "2117c5a0-e8b7-4377-8e82-584e7f1a6fc3", 214 | "metadata": {}, 215 | "outputs": [], 216 | "source": [ 217 | "df = pd.read_csv(\"data/all.csv\")\n", 218 | "positive_seqs = df[df['label'] == 1]['seq'].tolist()\n", 219 | "negative_seqs = df[df['label'] == 0]['seq'].tolist()" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": 25, 225 | "id": "b4d4ac12-23f9-470a-bf0b-cbfd116fbe5d", 226 | "metadata": { 227 | "scrolled": true 228 | }, 229 | "outputs": [ 230 | { 231 | "name": "stderr", 232 | "output_type": "stream", 233 | "text": [ 234 | " 0%|▍ | 3/1000 [00:27<2:31:49, 9.14s/it]\n" 235 | ] 236 | }, 237 | { 238 | "ename": "KeyboardInterrupt", 239 | "evalue": "", 240 | "output_type": "error", 241 | "traceback": [ 242 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 243 | "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", 244 | "Cell \u001b[0;32mIn[25], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m finetuned_positive_edit_distances \u001b[38;5;241m=\u001b[39m []\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m gen_seq \u001b[38;5;129;01min\u001b[39;00m tqdm(base_seqs):\n\u001b[0;32m----> 5\u001b[0m pos_distances \u001b[38;5;241m=\u001b[39m [\u001b[43medit_distance\u001b[49m\u001b[43m(\u001b[49m\u001b[43mgen_seq\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpos_seq\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m pos_seq \u001b[38;5;129;01min\u001b[39;00m positive_seqs]\n\u001b[1;32m 6\u001b[0m base_positive_edit_distances\u001b[38;5;241m.\u001b[39mappend(np\u001b[38;5;241m.\u001b[39mmean(pos_distances))\n\u001b[1;32m 8\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m gen_seq \u001b[38;5;129;01min\u001b[39;00m tqdm(finetuned_seqs):\n", 245 | "File \u001b[0;32m~/miniconda3/envs/esm/lib/python3.12/site-packages/nltk/metrics/distance.py:111\u001b[0m, in \u001b[0;36medit_distance\u001b[0;34m(s1, s2, substitution_cost, transpositions)\u001b[0m\n\u001b[1;32m 109\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m s1[i \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m1\u001b[39m] \u001b[38;5;241m==\u001b[39m s2[j \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m1\u001b[39m]:\n\u001b[1;32m 110\u001b[0m last_right_buf \u001b[38;5;241m=\u001b[39m j\n\u001b[0;32m--> 111\u001b[0m \u001b[43m_edit_dist_step\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 112\u001b[0m \u001b[43m \u001b[49m\u001b[43mlev\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 113\u001b[0m \u001b[43m \u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 114\u001b[0m \u001b[43m \u001b[49m\u001b[43mj\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 115\u001b[0m \u001b[43m \u001b[49m\u001b[43ms1\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 116\u001b[0m \u001b[43m \u001b[49m\u001b[43ms2\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 117\u001b[0m \u001b[43m \u001b[49m\u001b[43mlast_left\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 118\u001b[0m \u001b[43m \u001b[49m\u001b[43mlast_right\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 119\u001b[0m \u001b[43m \u001b[49m\u001b[43msubstitution_cost\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43msubstitution_cost\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 120\u001b[0m \u001b[43m \u001b[49m\u001b[43mtranspositions\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtranspositions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 121\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 122\u001b[0m last_left_t[s1[i \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m1\u001b[39m]] \u001b[38;5;241m=\u001b[39m i\n\u001b[1;32m 123\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m lev[len1][len2]\n", 246 | "File \u001b[0;32m~/miniconda3/envs/esm/lib/python3.12/site-packages/nltk/metrics/distance.py:41\u001b[0m, in \u001b[0;36m_edit_dist_step\u001b[0;34m(lev, i, j, s1, s2, last_left, last_right, substitution_cost, transpositions)\u001b[0m\n\u001b[1;32m 37\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_last_left_t_init\u001b[39m(sigma):\n\u001b[1;32m 38\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m {c: \u001b[38;5;241m0\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m c \u001b[38;5;129;01min\u001b[39;00m sigma}\n\u001b[0;32m---> 41\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_edit_dist_step\u001b[39m(\n\u001b[1;32m 42\u001b[0m lev, i, j, s1, s2, last_left, last_right, substitution_cost\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m1\u001b[39m, transpositions\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[1;32m 43\u001b[0m ):\n\u001b[1;32m 44\u001b[0m c1 \u001b[38;5;241m=\u001b[39m s1[i \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m1\u001b[39m]\n\u001b[1;32m 45\u001b[0m c2 \u001b[38;5;241m=\u001b[39m s2[j \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m1\u001b[39m]\n", 247 | "\u001b[0;31mKeyboardInterrupt\u001b[0m: " 248 | ] 249 | } 250 | ], 251 | "source": [ 252 | "base_positive_edit_distances = []\n", 253 | "finetuned_positive_edit_distances = []\n", 254 | "\n", 255 | "for gen_seq in tqdm(base_seqs):\n", 256 | " pos_distances = [edit_distance(gen_seq, pos_seq) for pos_seq in positive_seqs]\n", 257 | " base_positive_edit_distances.append(np.mean(pos_distances))\n", 258 | " \n", 259 | "for gen_seq in tqdm(finetuned_seqs):\n", 260 | " pos_distances = [edit_distance(gen_seq, pos_seq) for pos_seq in positive_seqs]\n", 261 | " finetuned_positive_edit_distances.append(np.mean(pos_distances))" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": 51, 267 | "id": "fdca6816-8095-4c23-a962-07eb9e68e166", 268 | "metadata": {}, 269 | "outputs": [ 270 | { 271 | "data": { 272 | "text/plain": [ 273 | "" 274 | ] 275 | }, 276 | "execution_count": 51, 277 | "metadata": {}, 278 | "output_type": "execute_result" 279 | }, 280 | { 281 | "data": { 282 | "image/png": "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Gj65XI7Wga2RrcrWWD5q8bZq12FjZYHrUTMZeIiIDIReOyhXZcWKFTA1NcXw4cORm5uLkJAQfPHFF9J6MzMz7Ny5E5MnT0ZwcDDs7OwQFhaGDz74QI+tpoZMqVQiW5WNsI/CIPOXQalUYvv27RjWvS3cHW2r3D7xXjo2fHYKyrQshh0iIgNhUGHn8OHDGs+tra2xevVqrF69usJt/Pz88Ntvv9Vxy+hRI/OXwTfQFxYKC1g4W8DLzwlerg76bhYREWlB7+PsEBEREdUlhh0iIiIyagw7REREZNQYdoiIiMioMewQERGRUWPYISIiIqPGsENERERGjWGHiIiIjBrDDhERERk1hh0iIiIyagw7REREZNQYdoiIiMioMewQERGRUWPYISIiIqPGsENERERGjWGHiIiIjBrDDhERERk1hh0iIiIyagw7REREZNQYdoiIiMioMewQERGRUWPYISIiIqPGsENERERGjWGHiIiIjBrDDhERERk1hh0iIiIyagw7REREZNQYdoiIiMioMewQERGRUWPYISIiIqPGsENERERGjWGHiIiIjBrDDhERERk1hh0iIiIyagw7REREZNQYdoiIiMioMewQERGRUWPYISIiIqPGsENERERGjWGHiIiIjBrDDhERERk1hh0iIiIyagw7REREZNQYdoiIiMioMewQERGRUWPYISIiIqPGsENERERGjWGHiIiIjBrDDhERERk1hh0iIiIyalqFnVu3bum6HURERER1QquwExAQgF69euG7775DTk6OrttEREREpDNahZ3z58+jffv2mDVrFmQyGV5//XX8+eefum4bERERUa1pFXY6duyIzz77DAkJCVi/fj0UCgWefvppPPbYY1i+fDmSk5N13U4iIiIirdSqg7K5uTmGDRuGH374AUuWLMHNmzcxe/Zs+Pj4YMyYMVAoFLpqJxEREZFWahV2zp49izfeeANeXl5Yvnw5Zs+ejbi4OOzbtw8JCQkYMmSIrtpJREREpBVzbTZavnw5IiMjERMTg4EDB2Ljxo0YOHAgTE2LspO/vz+ioqLQtGlTXbaViIiIqMa0Cjtr1qzBuHHjMHbsWHh5eZVbxsPDA+vWratV44iIiIhqS6vLWLGxsZg7d26FQQcALC0tERYWVmk9a9asQfv27eHo6AhHR0cEBwdj9+7d0vqcnBxMmTIFbm5usLe3x/Dhw5GUlKRRh1wux6BBg2BrawsPDw/MmTMH+fn52hwWERERGSGtwk5kZCR++OGHMst/+OEHbNiwodr1NGnSBB9//DHOnTuHs2fP4tlnn8WQIUNw9epVAMDMmTPx66+/4ocffsCRI0eQkJCAYcOGSdsXFBRg0KBByMvLw4kTJ7BhwwZERUVh/vz52hwWERERGSGtwk5ERATc3d3LLPfw8MDixYurXc/gwYMxcOBAtGjRAi1btsRHH30Ee3t7nDp1CmlpaVi3bh2WL1+OZ599Fp06dUJkZCROnDiBU6dOAQD27t2La9eu4bvvvkPHjh0xYMAALFq0CKtXr0ZeXl6F+83NzUV6errGg4iIiIyTVmFHLpfD39+/zHI/Pz/I5XKtGlJQUIAtW7YgKysLwcHBOHfuHNRqNfr06SOVad26NXx9fXHy5EkAwMmTJ9GuXTt4enpKZUJCQpCeni6dHSpPREQEnJycpIePj49WbSYiIiLDp1XY8fDwwOXLl8ssv3TpEtzc3GpU15UrV2Bvbw8rKytMmjQJP//8M9q0aYPExERYWlrC2dlZo7ynpycSExMBAImJiRpBp3h98bqKzJ07F2lpadLj7t27NWozERERNRxa3Y01cuRIvPnmm3BwcMAzzzwDADhy5AimT5+Ol19+uUZ1tWrVChcvXkRaWhp+/PFHhIWF4ciRI9o0q9qsrKxgZWVVp/sgIiIiw6BV2Fm0aBFu376N3r17w9y8qIrCwkKMGTOmRn12gKK7tgICAgAAnTp1wpkzZ/DZZ59hxIgRyMvLQ2pqqsbZnaSkJMhkMgCATCYrMydX8d1axWWIiIjo0abVZSxLS0ts3boV169fx6ZNm7B9+3bExcVh/fr1sLS0rFWDCgsLkZubi06dOsHCwgIHDhyQ1sXExEAulyM4OBgAEBwcjCtXruD+/ftSmX379sHR0RFt2rSpVTuIiIjIOGh1ZqdYy5Yt0bJlS623nzt3LgYMGABfX19kZGRg8+bNOHz4MH7//Xc4OTlh/PjxmDVrFlxdXeHo6Ihp06YhODgYXbt2BQD069cPbdq0wejRo7F06VIkJiZi3rx5mDJlCi9TEREREQAtw05BQQGioqJw4MAB3L9/H4WFhRrrDx48WK167t+/L00Y6uTkhPbt2+P3339H3759AQArVqyAqakphg8fjtzcXISEhOCLL76QtjczM8POnTsxefJkBAcHw87ODmFhYfjggw+0OSwiIiIyQlqFnenTpyMqKgqDBg3CY489BhMTE612XtV0EtbW1li9ejVWr15dYRk/Pz/89ttvWu2fiIiIjJ9WYWfLli3Ytm0bBg4cqOv2EBEREemU1h2Ui++gIiIiIjJkWoWdt956C5999hmEELpuDxEREZFOaXUZ648//sChQ4ewe/dutG3bFhYWFhrrt2/frpPGEREREdWWVmHH2dkZQ4cO1XVbiIiIiHROq7ATGRmp63YQERER1Qmt+uwAQH5+Pvbv348vv/wSGRkZAICEhARkZmbqrHFEREREtaXVmZ07d+6gf//+kMvlyM3NRd++feHg4IAlS5YgNzcXa9eu1XU7iYiIiLSi1Zmd6dOno3PnzkhJSYGNjY20fOjQoRpzWRERERHpm1Zndo4dO4YTJ06UmfSzadOm+Pvvv3XSMCIiIiJd0OrMTmFhIQoKCsosv3fvHhwcHGrdKCIiIiJd0Srs9OvXDytXrpSem5iYIDMzEwsWLOAUEkRERGRQtLqM9emnnyIkJARt2rRBTk4OXnnlFcTGxsLd3R3ff/+9rttIREREpDWtwk6TJk1w6dIlbNmyBZcvX0ZmZibGjx+P0NBQjQ7LRERERPqmVdgBAHNzc4waNUqXbSEiIiLSOa3CzsaNGytdP2bMGK0aQ0RERKRrWoWd6dOnazxXq9VQqVSwtLSEra0tww4REREZDK3uxkpJSdF4ZGZmIiYmBk8//TQ7KBMREZFB0XpurNJatGiBjz/+uMxZHyIiIiJ90lnYAYo6LSckJOiySiIiIqJa0arPzv/+9z+N50IIKBQKfP7553jqqad00jAiIiIiXdAq7Dz//PMaz01MTNCoUSM8++yz+PTTT3XRLiIiIiKd0CrsFBYW6rodRERERHVCp312iIiIiAyNVmd2Zs2aVe2yy5cv12YXRERERDqhVdi5cOECLly4ALVajVatWgEAbty4ATMzMwQFBUnlTExMdNNKIiIiIi1pFXYGDx4MBwcHbNiwAS4uLgCKBhp89dVX0b17d7z11ls6bSQRERGRtrTqs/Ppp58iIiJCCjoA4OLigg8//JB3YxEREZFB0SrspKenIzk5uczy5ORkZGRk1LpRRERERLqiVdgZOnQoXn31VWzfvh337t3DvXv38NNPP2H8+PEYNmyYrttIREREpDWt+uysXbsWs2fPxiuvvAK1Wl1Ukbk5xo8fj08++USnDSQiIiKqDa3Cjq2tLb744gt88skniIuLAwA0b94cdnZ2Om0cERERUW3ValBBhUIBhUKBFi1awM7ODkIIXbWLiIiISCe0CjsPHjxA79690bJlSwwcOBAKhQIAMH78eN52TkRERAZFq7Azc+ZMWFhYQC6Xw9bWVlo+YsQI7NmzR2eNIyIiIqotrfrs7N27F7///juaNGmisbxFixa4c+eOThpGREREpAtandnJysrSOKNT7OHDh7Cysqp1o4iIiIh0Rauw0717d2zcuFF6bmJigsLCQixduhS9evXSWeOIiIiIakury1hLly5F7969cfbsWeTl5eHtt9/G1atX8fDhQxw/flzXbSQiIiLSmlZndh577DHcuHEDTz/9NIYMGYKsrCwMGzYMFy5cQPPmzXXdRiIiIiKt1fjMjlqtRv/+/bF27Vq89957ddEmIiIiIp2p8ZkdCwsLXL58uS7aQkRERKRzWl3GGjVqFNatW6frthARERHpnFYdlPPz87F+/Xrs378fnTp1KjMn1vLly3XSOCIiIqLaqlHYuXXrFpo2bYq//voLQUFBAIAbN25olDExMdFd64iIiIhqqUZhp0WLFlAoFDh06BCAoukhVq1aBU9PzzppHBEREVFt1ajPTulZzXfv3o2srCydNoiIiIhIl7TqoFysdPghIiIiMjQ1CjsmJiZl+uSwjw4REREZshr12RFCYOzYsdJknzk5OZg0aVKZu7G2b9+uuxYSERER1UKNwk5YWJjG81GjRum0MURERES6VqOwExkZWVftICIiIqoTteqgTERERGToGHaIiIjIqDHsEBERkVFj2CEiIiKjxrBDRERERk2vYSciIgJPPPEEHBwc4OHhgeeffx4xMTEaZXJycjBlyhS4ubnB3t4ew4cPR1JSkkYZuVyOQYMGwdbWFh4eHpgzZw7y8/Pr81CIiIjIQOk17Bw5cgRTpkzBqVOnsG/fPqjVavTr109jvq2ZM2fi119/xQ8//IAjR44gISEBw4YNk9YXFBRg0KBByMvLw4kTJ7BhwwZERUVh/vz5+jgkIiIiMjA1GmdH1/bs2aPxPCoqCh4eHjh37hyeeeYZpKWlYd26ddi8eTOeffZZAEVj/QQGBuLUqVPo2rUr9u7di2vXrmH//v3w9PREx44d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", 283 | "text/plain": [ 284 | "
" 285 | ] 286 | }, 287 | "metadata": {}, 288 | "output_type": "display_data" 289 | } 290 | ], 291 | "source": [ 292 | "# sns.figure(figsize=(12, 6))\n", 293 | "\n", 294 | "sns.histplot(np.load(\"outputs/base_seqs_edit_distance.npy\"), bins=20, alpha=0.5, label=\"Base ESM3\")\n", 295 | "sns.histplot(np.load(\"outputs/iglm_seqs_edit_distance.npy\"), bins=20, alpha=0.5, label=\"IgLM\")\n", 296 | "sns.histplot(np.load(\"outputs/finetuned_seqs_edit_distance.npy\"), bins=20, alpha=0.5, label=\"Finetuned ESM3\")\n", 297 | "\n", 298 | "plt.xlabel(\"Average Edit Distance from Positive Binders\")\n", 299 | "plt.ylabel(\"Frequency\")\n", 300 | "plt.legend()\n", 301 | "# sns.show()" 302 | ] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": 44, 307 | "id": "a979103a-fa01-4956-8e7a-2372170febbe", 308 | "metadata": {}, 309 | "outputs": [ 310 | { 311 | "name": "stderr", 312 | "output_type": "stream", 313 | "text": [ 314 | "100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 100/100 [25:35<00:00, 15.35s/it]\n", 315 | "100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 100/100 [25:42<00:00, 15.43s/it]\n" 316 | ] 317 | } 318 | ], 319 | "source": [ 320 | "base_negative_edit_distances = []\n", 321 | "finetuned_negative_edit_distances = []\n", 322 | "\n", 323 | "for gen_seq in tqdm(base_seqs):\n", 324 | " neg_distances = [edit_distance(gen_seq, neg_seq) for neg_seq in negative_seqs]\n", 325 | " base_negative_edit_distances.append(np.mean(neg_distances))\n", 326 | " \n", 327 | "for gen_seq in tqdm(finetuned_seqs):\n", 328 | " neg_distances = [edit_distance(gen_seq, neg_seq) for neg_seq in negative_seqs]\n", 329 | " finetuned_negative_edit_distances.append(np.mean(neg_distances))" 330 | ] 331 | }, 332 | { 333 | "cell_type": "code", 334 | "execution_count": 46, 335 | "id": "c1f5227b-bd5d-4fae-8ba0-e0cd237e4b4d", 336 | "metadata": {}, 337 | "outputs": [ 338 | { 339 | "data": { 340 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAA+QAAAINCAYAAAC3YbXvAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/GU6VOAAAACXBIWXMAAA9hAAAPYQGoP6dpAABTN0lEQVR4nO3de3zP9f//8ft75/PGYpsMy5miUBqVaOWcEGIVUTpIIh18Sshh8RFSSofZ6EMOfUqnD6UVISRFB0IO4WubPn3YDJvZnr8/unj/erOxvW17ztyul8vrcun9Ojxfj/f7+X7rfd/z9Xq+HcYYIwAAAAAAUKY8bBcAAAAAAMCliEAOAAAAAIAFBHIAAAAAACwgkAMAAAAAYAGBHAAAAAAACwjkAAAAAABYQCAHAAAAAMACAjkAAAAAABZ42S6gtOXn5+vgwYMKDg6Ww+GwXQ4AAAAAoIIzxujo0aOqVq2aPDwKHwev8IH84MGDio6Otl0GAAAAAOASs3//flWvXr3Q7RU+kAcHB0v664UICQmxXA0AAAAAoKLLzMxUdHS0M48WpsIH8tOXqYeEhBDIAQAAAABl5ny3TTOpGwAAAAAAFhDIAQAAAACwgEAOAAAAAIAFFf4ecgAAAAAVlzFGp06dUl5enu1ScAnx9PSUl5fXBf+0NoEcAAAAwEXp5MmTSk1N1fHjx22XgktQQECAoqKi5OPj43YbBHIAAAAAF538/Hzt2bNHnp6eqlatmnx8fC54tBIoCmOMTp48qT/++EN79uxR3bp15eHh3t3gBHIAAAAAF52TJ08qPz9f0dHRCggIsF0OLjH+/v7y9vbW77//rpMnT8rPz8+tdpjUDQAAAMBFy92RSeBClcR7j3cvAAAAAAAWEMgBAAAAALCAe8gBAAAAVBiDkjeW6fkSB1xbpudDxcIIOQAAAACUkQEDBsjhcDiX8PBwdejQQT/++KPVupKTk13qOr38fbKyP/74Qw8//LBq1KghX19fRUZGqn379lq7dq1zn1q1asnhcGjhwoVnnaNx48ZyOBxKTk52rnvwwQdVu3Zt+fv7q0qVKurWrZt+/fXXUn2u5QmBHAAAAADKUIcOHZSamqrU1FSlpKTIy8tLXbp0sV2WQkJCnHWdXn7//Xfn9p49e+qHH37Q3LlztWPHDn300Ue6+eab9eeff7q0Ex0draSkJJd169evV1pamgIDA13WN2/eXElJSdq2bZs+++wzGWN02223KS8vr/SeaDlCIAcAAACAMnR6dDkyMlJXX321nnnmGe3fv19//PGHc5+nn35a9erVU0BAgK644gqNHj1aubm5zu1btmxR27ZtFRwcrJCQEDVv3lzfffedc/uaNWt04403yt/fX9HR0Xrsscd07Nixc9blcDicdZ1eIiIiJElHjhzR6tWrNXnyZLVt21Y1a9bUddddp1GjRun22293aSc+Pl6rVq3S/v37nevmzJmj+Ph4eXm53jU9ePBg3XTTTapVq5aaNWumCRMmaP/+/dq7d2+xX9eLEYEcAAAAACzJysrSv/71L9WpU0fh4eHO9cHBwUpOTtbWrVv18ssv66233tL06dOd2+Pj41W9enVt3LhRmzZt0jPPPCNvb29J0q5du9ShQwf17NlTP/74oxYtWqQ1a9bo0UcfdbvOoKAgBQUFaenSpcrJyTnnvhEREWrfvr3mzp0rSTp+/LgWLVqkgQMHnvO4Y8eOKSkpSTExMYqOjna71osJgRwAAAAAytAnn3ziDLjBwcH66KOPtGjRIpfftX7uuefUqlUr1apVS127dtXIkSO1ePFi5/Z9+/YpLi5ODRo0UN26ddWrVy81bdpUkpSQkKD4+Hg9/vjjqlu3rlq1aqWZM2dq3rx5ys7OLrSujIwMZ12nl44dO0qSvLy8lJycrLlz5yosLEytW7fWP/7xj0LvfR84cKCSk5NljNF7772n2rVr6+qrry5w39dee815vmXLlmnFihXy8fEp7st6USKQAwAAAEAZatu2rTZv3qzNmzfr22+/Vfv27dWxY0eX+7UXLVqk1q1bKzIyUkFBQXruuee0b98+5/YRI0bo/vvvV1xcnF588UXt2rXLuW3Lli1KTk52Cdbt27dXfn6+9uzZU2hdwcHBzrpOL2+//bZze8+ePXXw4EF99NFH6tChg1auXKlmzZq5TNJ2WufOnZWVlaWvv/5ac+bMOefoeHx8vH744QetWrVK9erVU+/evc/5h4OKhEAOAAAAAGUoMDBQderUUZ06dXTttdfq7bff1rFjx/TWW29JktatW6f4+Hh16tRJn3zyiX744Qc9++yzOnnypLONsWPH6pdfflHnzp315ZdfqlGjRvrggw8k/XUZ/IMPPugSrLds2aKdO3eqdu3ahdbl4eHhrOv0cvnll7vs4+fnp1tvvVWjR4/WN998owEDBmjMmDFnteXl5aV77rlHY8aM0YYNGxQfH1/oeUNDQ1W3bl3ddNNNeu+99/Trr786n0tFx++QAwAAAIBFDodDHh4eOnHihCTpm2++Uc2aNfXss8869/n76Plp9erVU7169TR8+HD17dtXSUlJ6t69u5o1a6atW7eqTp06pV57o0aNtHTp0gK3DRw4UFOnTlWfPn1UqVKlIrVnjJEx5rz3qVcUBHIA57egj+0KiqffItsVAAAAFConJ0dpaWmSpMOHD+vVV19VVlaWunbtKkmqW7eu9u3bp4ULF+raa6/Vp59+6jJifOLECT355JO68847FRMTowMHDmjjxo3q2bOnpL9maL/++uv16KOP6v7771dgYKC2bt2qFStW6NVXXy20LmOMs66/q1q1qg4fPqxevXpp4MCBatKkiYKDg/Xdd99pypQp6tatW4HtNWzYUP/9738VEBBQ4Pbdu3dr0aJFuu2221SlShUdOHBAL774ovz9/dWpU6eivZgXOQI5AAAAgAojccC1tks4r+XLlysqKkrSX/dtN2jQQEuWLNHNN98sSbr99ts1fPhwPfroo8rJyVHnzp01evRojR07VpLk6empP//8U/fee6/S09N12WWXqUePHho3bpwkqUmTJlq1apWeffZZ3XjjjTLGqHbt2urT59yDLJmZmc66/i41NVWVKlVSy5YtNX36dO3atUu5ubmKjo7WAw88oH/84x+Ftvn3mePP5Ofnp9WrV2vGjBk6fPiwIiIidNNNN+mbb75R1apVz1lrReEwxhjbRZSmzMxMhYaGKiMjQyEhIbbLAS5OjJADAIByJjs7W3v27FFMTIz8/Pxsl4NL0Lneg0XNoUzqBgAAAACABQRyAAAAAAAsIJADAAAAAGABgRwAAAAAAAsI5AAAAAAAWEAgBwAAAADAAgI5AAAAAAAWEMgBAAAAALCAQA4AAAAA5cDNN9+sxx9/3HYZpWrv3r1yOBzavHmz7VLKBS/bBQAAAABAiVnQp2zP129RsXYfMGCA5s6de9b6nTt36v3335e3t3dJVeY835EjR7R06dISbbc03XzzzVq1atVZ6x988EHNnj1bkrRq1SqNGzdOmzdvVnZ2ti6//HK1atVKb731lnx8fLRy5Uq1bdtWYWFhSk1NlZ+fn7OdjRs36rrrrpMkGWMkSdu3b9dDDz2krVu3KiMjQ9WqVVO/fv00ZsyYEu+TvyOQAwAAAEAZ6tChg5KSklzWValSRZ6enpYqKn8eeOABvfDCCy7rAgICJElbt25Vhw4dNHToUM2cOVP+/v7auXOn/v3vfysvL8/lmODgYH3wwQfq27evc11iYqJq1Kihffv2Odd5e3vr3nvvVbNmzRQWFqYtW7bogQceUH5+viZNmlRqz5NL1gEAAACgDPn6+ioyMtJl8fT0POuS9Vq1amnSpEkaOHCggoODVaNGDb355psube3fv1+9e/dWWFiYKleurG7dumnv3r2SpLFjx2ru3Ln68MMP5XA45HA4tHLlSq1cuVIOh0NHjhxxtrN582Y5HA7nscnJyQoLC9Nnn32mhg0bKigoSB06dFBqaqrL+d9++201bNhQfn5+atCggV577TWX7d9++62uueYa+fn5qUWLFvrhhx+K9BoFBASc9RqFhIRIkj7//HNFRkZqypQpuvLKK1W7dm116NBBb731lvz9/V3a6d+/v+bMmeN8fOLECS1cuFD9+/d32e+KK67Qfffdp6ZNm6pmzZq6/fbbFR8fr9WrVxepXncRyAEAAACgnHrppZecQfaRRx7Rww8/rO3bt0uScnNz1b59ewUHB2v16tVau3atMzifPHlSI0eOVO/evZ1BOjU1Va1atSryuY8fP66pU6fqnXfe0ddff619+/Zp5MiRzu3z58/X888/r4kTJ2rbtm2aNGmSRo8e7bwkPysrS126dFGjRo20adMmjR071uV4d0VGRio1NVVff/31efe95557tHr1audo+L///W/VqlVLzZo1O+dxv/32m5YvX642bdpccL3nQiAHAAAAgDL0ySefKCgoyLn06tWr0H07deqkRx55RHXq1NHTTz+tyy67TF999ZUkadGiRcrPz9fbb7+tq666Sg0bNlRSUpL27dunlStXKigoSP7+/i4j8j4+PkWuMzc3V7Nnz1aLFi3UrFkzPfroo0pJSXFuHzNmjF566SX16NFDMTEx6tGjh4YPH6433nhDkrRgwQLl5+crMTFRjRs3VpcuXfTkk08W6dyvvfaay2sUFBSk+fPnS5J69eqlvn37qk2bNoqKilL37t316quvKjMz86x2qlatqo4dOyo5OVmSNGfOHA0cOLDQ87Zq1Up+fn6qW7eubrzxxrMumy9pBHIAAAAA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XX5+vlJSUhQbG2uxMgAAAAAALky5HiGXpBEjRqh///5q0aKFrrvuOs2YMUPHjh3TfffdZ7s0AAAAAADcVu4DeZ8+ffTHH3/o+eefV1pamq6++motX778rIneUHH5+vpqzJgxZ92KgIsb/Vox0a8VE/1aMdGvFRP9WjHRrxWXw5xvHnYAAAAAAFDiyvU95AAAAAAAVFQEcgAAAAAALCCQAwAAAABgAYEcAAAAAAALCOSwKi8vT6NHj1ZMTIz8/f1Vu3ZtjR8/Xueaa/D999/XrbfeqipVqigkJESxsbH67LPPyrBqnI87/fp3a9eulZeXl66++urSLRTF4m6/5uTk6Nlnn1XNmjXl6+urWrVqac6cOWVUNc7H3X6dP3++mjZtqoCAAEVFRWngwIH6888/y6hqFMXRo0f1+OOPq2bNmvL391erVq20cePGcx6zcuVKNWvWTL6+vqpTp46Sk5PLplgUWXH7le9NFwd3Pq+n8b3pImcAiyZOnGjCw8PNJ598Yvbs2WOWLFligoKCzMsvv1zoMcOGDTOTJ0823377rdmxY4cZNWqU8fb2Nt9//30ZVo5zcadfTzt8+LC54oorzG233WaaNm1a+sWiyNzt19tvv920bNnSrFixwuzZs8d88803Zs2aNWVUNc7HnX5ds2aN8fDwMC+//LLZvXu3Wb16tWncuLHp3r17GVaO8+ndu7dp1KiRWbVqldm5c6cZM2aMCQkJMQcOHChw/927d5uAgAAzYsQIs3XrVvPKK68YT09Ps3z58jKuHOdS3H7le9PFobj9ehrfmy5+/OwZrOrSpYsiIiKUmJjoXNezZ0/5+/vrX//6V5Hbady4sfr06aPnn3++NMpEMV1Iv951112qW7euPD09tXTpUm3evLmUq0VRudOvy5cv11133aXdu3ercuXKZVUqisGdfp06dapef/117dq1y7nulVde0eTJk3XgwIFSrxnnd+LECQUHB+vDDz9U586dneubN2+ujh07asKECWcd8/TTT+vTTz/Vzz//7Fx311136ciRI1q+fHmZ1I1zc6dfC8L3pvLlQvqV700XPy5Zh1WtWrVSSkqKduzYIUnasmWL1qxZo44dOxa5jfz8fB09epQv++WIu/2alJSk3bt3a8yYMWVRJorJnX796KOP1KJFC02ZMkWXX3656tWrp5EjR+rEiRNlVTbOw51+jY2N1f79+/Wf//xHxhilp6frvffeU6dOncqqbJzHqVOnlJeXJz8/P5f1/v7+WrNmTYHHrFu3TnFxcS7r2rdvr3Xr1pVanSged/r1THxvKn/c7Ve+N1UMXrYLwKXtmWeeUWZmpho0aCBPT0/l5eVp4sSJio+PL3IbU6dOVVZWlnr37l2KlaI43OnXnTt36plnntHq1avl5cU/TeWRO/26e/durVmzRn5+fvrggw/03//+V4888oj+/PNPJSUllWH1KIw7/dq6dWvNnz9fffr0UXZ2tk6dOqWuXbtq1qxZZVg5ziU4OFixsbEaP368GjZsqIiICL377rtat26d6tSpU+AxaWlpioiIcFkXERGhzMxMnThxQv7+/mVROs7BnX49E9+byh93+pXvTRUHI+SwavHixZo/f74WLFig77//XnPnztXUqVM1d+7cIh2/YMECjRs3TosXL1bVqlVLuVoUVXH7NS8vT/369dO4ceNUr169Mq4WReXO5zU/P18Oh0Pz58/Xddddp06dOmnatGmaO3cuo+TlhDv9unXrVg0bNkzPP/+8Nm3apOXLl2vv3r166KGHyrBynM8777wjY4wuv/xy+fr6aubMmerbt688PPj6dzG7kH7le1P5VZx+5XtTBWPzBnagevXq5tVXX3VZN378eFO/fv3zHvvuu+8af39/88knn5RWeXBTcfv18OHDRpLx9PR0Lg6Hw7kuJSWlLMrGebjzeb333ntN7dq1XdZt3brVSDI7duwolTpRPO706913323uvPNOl3WrV682kszBgwdLpU64Lysry9kvvXv3Np06dSpwvxtvvNEMGzbMZd2cOXNMSEhIaZcINxS1X0/je9PFoSj9yvemioXrG2DV8ePHz/rLn6enp/Lz88953LvvvquBAwdq4cKFLpNfoHwobr+GhITop59+cln32muv6csvv9R7772nmJiYUqsVRefO57V169ZasmSJsrKyFBQUJEnasWOHPDw8VL169VKtF0XjTr8eP378rEskPT09JanIP2+IshMYGKjAwEAdPnxYn332maZMmVLgfrGxsfrPf/7jsm7FihWKjY0tizJRTEXtV4nvTReTovQr35sqGNt/EcClrX///ubyyy93/tzO+++/by677DLz1FNPOfd55plnzD333ON8PH/+fOPl5WVmzZplUlNTncuRI0dsPAUUwJ1+PdOYMWP4+Y5yxp1+PXr0qKlevbq58847zS+//GJWrVpl6tata+6//34bTwEFcKdfk5KSjJeXl3nttdfMrl27zJo1a0yLFi3MddddZ+MpoBDLly83y5YtM7t37zaff/65adq0qWnZsqU5efKkMebsfj39s2dPPvmk2bZtm5k1axY/e1YOFbdf+d50cShuv56J700XLwI5rMrMzDTDhg0zNWrUMH5+fuaKK64wzz77rMnJyXHu079/f9OmTRvn4zZt2hhJZy39+/cv+yeAArnTr2fifyzlj7v9um3bNhMXF2f8/f1N9erVzYgRI8zx48fLuHoUxt1+nTlzpmnUqJHx9/c3UVFRJj4+/ry/l4uytWjRInPFFVcYHx8fExkZaYYMGeISwgrq16+++spcffXVxsfHx1xxxRUmKSmpbIvGeRW3X/nedHFw5/P6d3xvunjxO+QAAAAAAFjANJsAAAAAAFhAIAcAAAAAwAICOQAAAAAAFhDIAQAAAACwgEAOAAAAAIAFBHIAAAAAACwgkAMAAAAAYAGBHABQYSUnJyssLMz5eOzYsbr66qtL9ZwOh0NLly4t1XPYMnbsWEVERFTo51gaatWqpRkzZtguQytXrpTD4dCRI0cuqJ0BAwbojjvuKJGaAOBSRyAHgIvUunXr5Onpqc6dO9supUw4HI4Cl4ULFxa5jZEjRyolJcX5uKjBYsCAAc7zeXt7KyIiQrfeeqvmzJmj/Px8l31TU1PVsWPHIj+niyXYbtu2TePGjdMbb7xRrOdYmk73y4svvuiyfunSpXI4HGVez5l/ADpt48aNGjx4cKme++abb3b5XERERKhXr176/fffnfu0atVKqampCg0NLdVaAABFRyAHgItUYmKihg4dqq+//loHDx4s1XMZY3Tq1KlSPUdRJCUlKTU11WUpzkhdUFCQwsPD3Tp3hw4dlJqaqr1792rZsmVq27athg0bpi5duri8NpGRkfL19XXrHOXZrl27JEndunUr9DmePHmyrMuSn5+fJk+erMOHD5f5uYuqSpUqCggIKPXzPPDAA0pNTdXBgwf14Ycfav/+/br77rud2318fBQZGWnljxV/V17+PQGA8oBADgAXoaysLC1atEgPP/ywOnfurOTkZOe2fv36qU+fPi775+bm6rLLLtO8efMkSfn5+UpISFBMTIz8/f3VtGlTvffee879T1/aumzZMjVv3ly+vr5as2aNdu3apW7duikiIkJBQUG69tpr9cUXX7icKzU1VZ07d5a/v79iYmK0YMGCsy7ZPXLkiO6//35VqVJFISEhateunbZs2XLe5x0WFqbIyEiXxc/Pz7k9OTlZNWrUUEBAgLp3764///zT5fi/X7I+duxYzZ07Vx9++KFzVHHlypWFntvX11eRkZG6/PLL1axZM/3jH//Qhx9+qGXLlrm8/n8f9T558qQeffRRRUVFyc/PTzVr1lRCQoKkvy5jlqTu3bvL4XA4HxflNa5Vq5YmTZqkgQMHKjg4WDVq1NCbb77pss+BAwfUt29fVa5cWYGBgWrRooU2bNjg3P7hhx+qWbNm8vPz0xVXXKFx48YVGpLGjh2rrl27SpI8PDycge70FQYTJ05UtWrVVL9+fUnSTz/9pHbt2snf31/h4eEaPHiwsrKynO2dPm7SpEmKiIhQWFiYXnjhBZ06dUpPPvmkKleurOrVqyspKanQ/jgtLi5OkZGRzte1MGvWrNGNN94of39/RUdH67HHHtOxY8ec24vyvp02bZquuuoqBQYGKjo6Wo888ojzea1cuVL33XefMjIynO+nsWPHSnK9ZL0kPp+FCQgIUGRkpKKionT99dfr0Ucf1ffff+/cfuYl66dH9D/77DM1bNhQQUFBzj88nZaXl6cRI0YoLCxM4eHheuqpp2SMcTmvu/+ebNmyRW3btlVwcLBCQkLUvHlzfffdd+d9ngBQoRgAwEUnMTHRtGjRwhhjzMcff2xq165t8vPzjTHGfPLJJ8bf398cPXrUuf/HH39s/P39TWZmpjHGmAkTJpgGDRqY5cuXm127dpmkpCTj6+trVq5caYwx5quvvjKSTJMmTcznn39ufvvtN/Pnn3+azZs3m9mzZ5uffvrJ7Nixwzz33HPGz8/P/P77785zxcXFmauvvtqsX7/ebNq0ybRp08b4+/ub6dOnu+zTtWtXs3HjRrNjxw7zxBNPmPDwcPPnn38W+pwlmQ8++KDQ7evXrzceHh5m8uTJZvv27ebll182YWFhJjQ01LnPmDFjTNOmTY0xxhw9etT07t3bdOjQwaSmpprU1FSTk5NTYNv9+/c33bp1K3Bb06ZNTceOHQus85///KeJjo42X3/9tdm7d69ZvXq1WbBggTHGmEOHDhlJJikpyaSmpppDhw4ZY0yRXuOaNWuaypUrm1mzZpmdO3eahIQE4+HhYX799Vfnc7viiivMjTfeaFavXm127txpFi1aZL755htjjDFff/21CQkJMcnJyWbXrl3m888/N7Vq1TJjx44t8DkePXrUJCUlGUnO1+r06xIUFGTuuece8/PPP5uff/7ZZGVlmaioKNOjRw/z008/mZSUFBMTE2P69+/v8noGBwebIUOGmF9//dUkJiYaSaZ9+/Zm4sSJZseOHWb8+PHG29vb7N+/v8Ca/t4v77//vvHz83Pu+8EHH5i/f8X57bffTGBgoJk+fbrZsWOHWbt2rbnmmmvMgAEDnPsU5X07ffp08+WXX5o9e/aYlJQUU79+ffPwww8bY4zJyckxM2bMMCEhIc7X6PRnsGbNms52SuLzWZA2bdqYYcOGOR//+eefpmvXrqZt27bOdac/14cPHzbGGJOUlGS8vb1NXFyc2bhxo9m0aZNp2LCh6devn/OYyZMnm0qVKpl///vfZuvWrWbQoEEmODjY5fPg7r8njRs3NnfffbfZtm2b2bFjh1m8eLHZvHlzoc8RACoiAjkAXIRatWplZsyYYYwxJjc311x22WXmq6++cnk8b9485/59+/Y1ffr0McYYk52dbQICApzh7LRBgwaZvn37GmP+/xfopUuXnreWxo0bm1deecUYY8y2bduMJLNx40bn9p07dxpJzkCyevVqExISYrKzs13aqV27tnnjjTcKPY8k4+fnZwIDA12W00G1b9++plOnTi7H9OnTp9BAbsy5g/bfnWu/Pn36mIYNG7rUeTqQDx061LRr1875x5KCntO5/shw2t9fY2P+Cnh3332383F+fr6pWrWqef31140xxrzxxhsmODi40D9w3HLLLWbSpEku69555x0TFRVVaA1nhlxj/npdIiIiXP6Q8eabb5pKlSqZrKws57pPP/3UeHh4mLS0NOdxNWvWNHl5ec596tevb2688Ubn41OnTpnAwEDz7rvvFlrT3/vl+uuvNwMHDiyw1kGDBpnBgwe7HLt69Wrj4eFhTpw4UaT3bUGWLFliwsPDnY+TkpJc3m+n/T2Ql8TnsyBt2rQx3t7eJjAw0AQEBBhJpl69embPnj3OfQoK5JLMb7/95txn1qxZJiIiwvk4KirKTJkyxfk4NzfXVK9e3fm6X8i/J8HBwSY5ObnQ5wQAlwKvMhyMBwCUgO3bt+vbb7/VBx98IEny8vJSnz59lJiYqJtvvlleXl7q3bu35s+fr3vuuUfHjh3Thx9+6Jz87LffftPx48d16623urR78uRJXXPNNS7rWrRo4fI4KytLY8eO1aeffqrU1FSdOnVKJ06c0L59+5y1eXl5qVmzZs5j6tSpo0qVKjkfb9myRVlZWWfdy33ixAnnfcqFmT59uuLi4lzWVatWTdJfk451797dZVtsbKyWL19+zjYvlDGm0HtyBwwYoFtvvVX169dXhw4d1KVLF912223nbO98r/FpTZo0cf63w+FQZGSkDh06JEnavHmzrrnmGlWuXLnAc2zZskVr167VxIkTnevy8vKUnZ2t48ePF+t+56uuuko+Pj7Ox9u2bVPTpk0VGBjoXNe6dWvl5+dr+/btioiIkCQ1btxYHh7//865iIgIXXnllc7Hnp6eCg8Pdz6n85k8ebLatWunkSNHFvh8f/zxR82fP9+5zhij/Px87dmzRzt27Djv+1aSvvjiCyUkJOjXX39VZmamTp06VezXrCQ/n2eKj4/Xs88+K0lKT0/XpEmTdNttt2nTpk0KDg4u8JiAgADVrl3b+TgqKsr5mmdkZCg1NVUtW7Z0qb9FixbOy9Yv5N+TESNG6P7779c777yjuLg49erVy6UWALgUEMgB4CKTmJioU6dOOYOo9Fe48PX11auvvqrQ0FDFx8erTZs2OnTokFasWCF/f3916NBBkpz3vH766ae6/PLLXdo+c6Kuv4cq6a9ZylesWKGpU6eqTp068vf315133lmsybyysrIUFRVV4P3aBc1Q/XeRkZGqU6dOkc9VFrZt26aYmJgCtzVr1kx79uzRsmXL9MUXX6h3796Ki4s75/3ARX2Nvb29XR47HA7njO/+/v7nrDkrK0vjxo1Tjx49ztr293vyi+LM90hRFVT/uZ7T+dx0001q3769Ro0apQEDBrhsy8rK0oMPPqjHHnvsrONq1KihHTt2nLf9vXv3qkuXLnr44Yc1ceJEVa5cWWvWrNGgQYN08uTJYv0Ro6Q+n2cKDQ11fj7q1KmjxMRERUVFadGiRbr//vsLPKag19yccY/4uVzIvydjx45Vv3799Omnn2rZsmUaM2aMFi5ceNYf1gCgIiOQA8BF5NSpU5o3b55eeumls0Za77jjDr377rt66KGH1KpVK0VHR2vRokVatmyZevXq5fzi3ahRI/n6+mrfvn1q06ZNsc6/du1aDRgwwPmFOSsrS3v37nVur1+/vk6dOqUffvhBzZs3l/TXCNrfZ8Bu1qyZ0tLS5OXl5ZzIrCQ0bNjQZdIySVq/fv05j/Hx8VFeXp7b5/zyyy/1008/afjw4YXuExISoj59+qhPnz6688471aFDB/3vf/9T5cqV5e3tfdb5z/caF0WTJk309ttvO89zpmbNmmn79u2l8seNhg0bKjk5WceOHXMGsLVr18rDw8M56VtpefHFF3X11VefdZ5mzZpp69athT7forxvN23apPz8fL300kvOkf3Fixe7tFPU91NpfT7P5OnpKemvq0/cERoaqqioKG3YsEE33XSTpL/+Ddq0aZPzaoILrbdevXqqV6+ehg8frr59+yopKYlADuCSQiAHgIvIJ598osOHD2vQoEFn/ZZwz549lZiYqIceekjSX7M5z549Wzt27NBXX33l3C84OFgjR47U8OHDlZ+frxtuuEEZGRlau3atQkJC1L9//0LPX7duXb3//vvq2rWrHA6HRo8e7TKC2aBBA8XFxWnw4MF6/fXX5e3trSeeeEL+/v7Oy7rj4uIUGxurO+64Q1OmTFG9evV08OBBffrpp+revftZl7X+3ZEjR5SWluayLjg4WIGBgXrsscfUunVrTZ06Vd26ddNnn3123svVa9Wqpc8++0zbt29XeHi4QkNDzxoxPC0nJ0dpaWnKy8tTenq6li9froSEBHXp0kX33ntvgcdMmzZNUVFRuuaaa+Th4aElS5YoMjLSeSVArVq1lJKSotatW8vX11eVKlU672tcFH379tWkSZN0xx13KCEhQVFRUfrhhx9UrVo1xcbG6vnnn1eXLl1Uo0YN3XnnnfLw8NCWLVv0888/a8KECcU615ni4+M1ZswY9e/fX2PHjtUff/yhoUOH6p577nFerl5arrrqKsXHx2vmzJku659++mnnrOP333+/AgMDtXXrVq1YsUKvvvpqkd63derUUW5url555RV17dpVa9eu1ezZs13OU6tWLWVlZSklJUVNmzZVQEBAoSPnpfH5PH78uPPzkZ6ervHjx8vPz++8t0mcy7Bhw/Tiiy+qbt26atCggaZNm+acpf1C6j1x4oSefPJJ3XnnnYqJidGBAwe0ceNG9ezZ0+1aAeCiZPUOdgBAsXTp0uWsictO27Bhg5FktmzZYowxZuvWrUaSqVmz5lmTiuXn55sZM2aY+vXrG29vb1OlShXTvn17s2rVKmPM2ZM/nbZnzx7Ttm1b4+/vb6Kjo82rr7561uzOBw8eNB07djS+vr6mZs2aZsGCBaZq1apm9uzZzn0yMzPN0KFDTbVq1Yy3t7eJjo428fHxZt++fYU+d0kFLgkJCc59EhMTTfXq1Y2/v7/p2rWrmTp16jkndTt06JC59dZbTVBQkJHknBjvTP3793eez8vLy1SpUsXExcWZOXPmuExMdrrO0xO1vfnmm+bqq682gYGBJiQkxNxyyy3m+++/d+770UcfmTp16hgvLy9Ts2bNIr/Gf58k7LSmTZuaMWPGOB/v3bvX9OzZ04SEhJiAgADTokULs2HDBuf25cuXm1atWhl/f38TEhJirrvuOvPmm28W+voXNqlbQZPd/fjjj6Zt27bGz8/PVK5c2TzwwAMus4oXdNyZz7Gw53m+8+/Zs8f4+PicVeu3337r7OvAwEDTpEkTM3HiROf2orxvp02bZqKiooy/v79p3769mTdv3lmfk4ceesiEh4cbSc7+KOh5XMjnsyBt2rRx+VxUqlTJtGnTxnz55ZfOfQqa1O3MSejO7Ofc3FwzbNgwExISYsLCwsyIESPMvffe6/K6u/PvSU5OjrnrrrtMdHS08fHxMdWqVTOPPvqoOXHiRKHPEQAqIocxxbhRCACAYjpw4ICio6P1xRdf6JZbbrFdDlAkvG8BAGWBQA4AKFFffvmlsrKydNVVVyk1NVVPPfWU/u///k87duwo9HJwwDbetwAAG7iHHABQonJzc/WPf/xDu3fvVnBwsFq1aqX58+cTalCu8b4FANjACDkAAAAAABZ42C4AAAAAAIBLEYEcAAAAAAALCOQAAAAAAFhAIAcAAAAAwAICOQAAAAAAFhDIAQAAAACwgEAOAAAAAIAFBHIAAAAAACwgkAMAAAAAYMH/AzfRgbx1RUu0AAAAAElFTkSuQmCC", 341 | "text/plain": [ 342 | "
" 343 | ] 344 | }, 345 | "metadata": {}, 346 | "output_type": "display_data" 347 | } 348 | ], 349 | "source": [ 350 | "plt.figure(figsize=(12, 6))\n", 351 | "\n", 352 | "plt.hist(base_negative_edit_distances, bins=8, alpha=0.7, label=\"Base ESM3\")\n", 353 | "plt.hist(finetuned_negative_edit_distances, bins=8, alpha=0.7, label=\"Finetuned ESM3\")\n", 354 | "plt.xlabel(\"Average Edit Distance from Negative Binders\")\n", 355 | "plt.ylabel(\"Frequency\")\n", 356 | "plt.legend()\n", 357 | "plt.show()" 358 | ] 359 | }, 360 | { 361 | "cell_type": "code", 362 | "execution_count": 24, 363 | "id": "6ffa7414-f60f-4848-bd21-1fc0cb6b7958", 364 | "metadata": {}, 365 | "outputs": [], 366 | "source": [ 367 | "antiberty = AntiBERTyRunner()\n", 368 | "\n", 369 | "def get_log_likelihood(sequences):\n", 370 | " pll = antiberty.pseudo_log_likelihood(\n", 371 | " [f\"EVQLVESGGGLVQPGGSLRLSCAASGFNIKDTYIHWVRQAPGKGLEWVARIYPTNGYTRYADSVKGRFTISADTSKNTAYLQMNSLRAEDTAVYYC{seq}WGQGTLVTVSS\" for seq in sequences], \n", 372 | " batch_size=16\n", 373 | " )\n", 374 | " # probabilities = torch.exp(pll)\n", 375 | " return pll" 376 | ] 377 | }, 378 | { 379 | "cell_type": "code", 380 | "execution_count": 25, 381 | "id": "2dcca7cd-6f66-476f-a49d-5af3ea7d3743", 382 | "metadata": { 383 | "scrolled": true 384 | }, 385 | "outputs": [ 386 | { 387 | "name": "stderr", 388 | "output_type": "stream", 389 | "text": [ 390 | "100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [02:06<00:00, 7.91it/s]\n", 391 | "100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [02:47<00:00, 5.99it/s]\n", 392 | "100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [02:49<00:00, 5.91it/s]\n" 393 | ] 394 | } 395 | ], 396 | "source": [ 397 | "with open(\"outputs/esm3_base_seqs.txt\", \"r\") as f:\n", 398 | " base_seqs = [line.strip() for line in f.readlines()]\n", 399 | "\n", 400 | "with open(\"outputs/esm3_finetuned_seqs.txt\", \"r\") as f:\n", 401 | " finetuned_seqs = [line.strip() for line in f.readlines()]\n", 402 | "\n", 403 | "with open(\"outputs/iglm_seqs.txt\", \"r\") as f:\n", 404 | " iglm_seqs = [line.strip() for line in f.readlines()]\n", 405 | "\n", 406 | "base_pll = get_log_likelihood(base_seqs)\n", 407 | "finetuned_pll = get_log_likelihood(finetuned_seqs)\n", 408 | "iglm_pll = get_log_likelihood(iglm_seqs)" 409 | ] 410 | }, 411 | { 412 | "cell_type": "code", 413 | "execution_count": 52, 414 | "id": "efafeea4-57e8-4239-96fa-c8760c87ac5e", 415 | "metadata": {}, 416 | "outputs": [ 417 | { 418 | "data": { 419 | "image/png": "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d5B/6qmnNNtVKhX8/PyQlpaGw4cPa26zQUREVJcxAOlBoVBAqczD9/95FeEBXiZ/vzhpKkYu3g6FQlGro0D+/v6Ijo7WCkC7du2Co6Mj0tLSaq0uIiIiXTEAGSA8wAvtn2hU22VUKTs7G2+88QZ2794NZ2dnvP/++/j5558rHfEpz5gxY7B69WqsWrUK9vb2AIANGzZgzJgxWLRokYmqJyIiMr5anQS9ZMkSdOrUCU5OTvD29saLL76I+Ph4rTY9evSASCTSerzxxhtabaRSKQYOHAixWAxvb2/MnDkThYWFNflR6rRp06bh+PHj+OWXX3DgwAHExsbi/PnzOvfToUMHBAUF4aeffgJQctz//PNPjBo1ytglExERmVStBqCjR49i6tSpOHXqFA4cOAC1Wo2+ffsiNzdXq92kSZMgk8k0j2XLlmleKyoqwsCBA1FQUIATJ05g48aNiImJwbx582r649RJ2dnZ2LhxI1asWIHevXujZcuWiI6ORlFRkV79jR8/Hhs2bAAAxMTEYMCAAfDyMv1pQCIiImOq1VNg+/fv13oeExMDb29vnDt3Dt27d9dsF4vFkEgk5fbx+++/49q1azh48CB8fHzQtm1bLFq0CLNmzcKCBQtgY2NTZh+VSgWVSqV5npWVZaRPVPfcunULarUaTz75pGabi4sLmjZtqld/I0eOxAcffIBbt24hJiYGq1evNlapRERENaZOrQOUmZkJAHB3d9favnnzZnh6eqJly5aYPXs2lEql5rWTJ0+iVatW8PHx0Wzr168fsrKycPXq1XLfZ8mSJXBxcdE8/P39TfBpGiYPDw8899xzmDBhAvLz89G/f//aLomIiEhndSYAFRcX491338XTTz+Nli1bara/9tpr+P7773H48GHMnj0b3333HUaOHKl5XS6Xa4UfAJrncrm83PeaPXs2MjMzNY+7d++a4BPVDU2aNIG1tTXOnDmj2ZaZmYkbN27o3ef48eNx5MgRjB49GpaWlsYok4iIqEbVmavApk6diitXruDYsWNa219//XXNz61atYKvry969+6NxMREhISE6PVetra2sLW1Naje+sLJyQljxozBzJkz4e7uDm9vb8yfPx8WFhaatX1Kpaam4uLFi1rbfH19ywTMyMhIpKamwtnZ2dTlExERmUSdCEBRUVHYs2cP/vzzTzRu3LjStp07dwYA3Lx5EyEhIZBIJPjrr7+02iQnJwNAhfOGjCVOmmrS/o31PitXrsQbb7yB5557TnMZ/N27d2FnZ6fVbsuWLdiyZYvWtkWLFmHOnDla20QiETw9PQ2qiYiIqDbVagASBAFvvfUWdu3ahSNHjiA4OLjKfUpHKHx9fQEAERER+OSTT5CSkgJvb28AwIEDB+Ds7IzmzZubpG5PT0+IxfYYuXi7Sfovj1hsr1PoiImJ0fzs5OSEzZs3a57n5ubio48+0hpdO3LkSKX9CYJQ4Wuurq6Vvk5ERFTX1GoAmjp1KrZs2YKff/4ZTk5Omjk7Li4usLe3R2JiIrZs2YIBAwbAw8MDly9fxnvvvYfu3bujdevWAIC+ffuiefPmGDVqFJYtWwa5XI45c+Zg6tSpJjvNFRAQgLi46/XmXmAXLlzA9evX8eSTTyIzMxMLFy4EALzwwgvGLJGIiKjeqNUAtHbtWgAoc/+o6OhojB07FjY2Njh48CBWrVqF3Nxc+Pv7Y8iQIVqnZCwtLbFnzx5MmTIFERERcHBwwJgxYzS/5E0lICCgXt2cdMWKFYiPj4eNjQ06dOiA2NhYnsYiIiKzVeunwCrj7++Po0ePVtlPYGAgfv31V2OV1eC0a9cO586dq+0yiIiI6ow6cxk8ERERUU1hACIiIiKzwwBEREREZocBiIiIiMwOAxARERGZHQYgIiIiMjt14lYY9ZFUKq03CyE+rkePHmjbti1WrVpllP7qotu3byM4OBgXLlxA27Zta7scIiKqYxiA9CCVStEsvBnylHk19p72Yntcj7te7RA0duxYbNy4scz2hIQE7Ny5E9bW1katb+zYscjIyMDu3buN2q8p9ejRo9x1piZPnox169YBAI4ePYqPPvoIFy9eRH5+Pho1aoQuXbrgm2++gY2NDY4cOYKePXvC1dUVMplM6/5qZ86cwZNPPgng3zWv4uPj8cYbb+DatWvIzMyEn58fXnvtNcyfP9/ofyZERFQxBiA9KBQK5CnzMOaTMZAEm/aGqwAgT5Jj44cboVAodBoFioyMRHR0tNY2Ly8vWFpaGrvEemvSpEllVg0Xi8UAgGvXriEyMhJvvfUWVq9eDXt7eyQkJOCnn35CUVGR1j5OTk7YtWsXhg8frtm2fv16BAQEQCqVarZZW1tj9OjRaN++PVxdXXHp0iVMmjQJxcXFWLx4sQk/KRERPYoByACSYAkCwuvu7TBsbW0hkZQNaI+fAgsKCsLrr7+OmzdvYseOHXBzc8OcOXO0bpZ69+5dTJ8+Hb///jssLCzQrVs3fPHFFwgKCsKCBQs0o00ikQgAcPjwYQBAz549kZ6eDldXVwAlN7Nt164dkpKSEBQUhJiYGLz77rvYtm0b3n33Xdy9exddu3ZFdHS05oa3APDtt9/is88+0+z39ttv480339S8/tdff2Hy5MmIi4tDy5Yt8eGHH1brGInF4nKPEQD8/vvvkEgkWLZsmWZbSEgIIiMjy7QdM2YMNmzYoAlAeXl52Lp1K95++20sWrRI065JkyZo0qSJ5nlgYCCOHDmC2NjYatVLRETGwUnQBAD47LPP0LFjR1y4cAFvvvkmpkyZgvj4eACAWq1Gv3794OTkhNjYWBw/fhyOjo6IjIxEQUEBZsyYgVdffRWRkZGQyWSQyWTo0qVLtd9bqVRixYoV+O677/Dnn39CKpVixowZmtc3b96MefPm4ZNPPkFcXBwWL16MuXPnakJXTk4OnnvuOTRv3hznzp3DggULtPbXl0QigUwmw59//lll21GjRiE2NlYz2vPTTz8hKCgI7du3r3S/mzdvYv/+/XjmmWcMrpeIiKqPAagB27NnDxwdHTWPV155pcK2AwYMwJtvvonQ0FDMmjULnp6emlGcbdu2obi4GN9++y1atWqF8PBwREdHQyqV4siRI3B0dIS9vb1mxEkikcDGxqbadarVaqxbtw4dO3ZE+/btERUVhUOHDmlenz9/Pj777DMMHjwYwcHBGDx4MN577z18/fXXAIAtW7aguLgY69evR4sWLfDcc89h5syZ1XrvNWvWaB0jR0dHbN68GQDwyiuvYPjw4XjmmWfg6+uLl156CV9++SWysrLK9OPt7Y3+/fsjJiYGALBhwwaMHz++wvft0qUL7OzsEBYWhm7dupn85r1ERKSNAagB69mzJy5evKh5rF69usK2rVu31vwsEokgkUiQkpICALh06RJu3rwJJycnTUhwd3dHfn4+EhMTDa5TLBYjJCRE89zX11fz3rm5uUhMTMSECRO0QsrHH3+see+4uDi0bt1aawJyREREtd57xIgRWsfo4sWLeP755wEAlpaWiI6Oxr1797Bs2TI0atQIixcvRosWLSCTycr0NX78eMTExODWrVs4efIkRowYUeH7btu2DefPn8eWLVuwd+9erFixolr1EhGRcXAOUAPm4OCA0NDQarV9/AokkUiE4uJiACWnmDp06KAZGXmUl5dXhX1aWJTk69IroICS0Z7qvHfpPjk5OQCAb775Bp07d9ZqZ4zJ3C4uLlUeo0aNGmHUqFEYNWoUFi1ahCeeeALr1q3DRx99pNWuf//+eP311zFhwgQMGjQIHh4eFfbp7+8PAGjevDmKiorw+uuvY/r06ZygTkRUQxiAqErt27fHtm3b4O3tDWdn53Lb2NjYlLkyqjQcyWQyuLm5ASiZBK0LHx8f+Pn54datWxWOqISHh+O7775Dfn6+ZhTo1KlTOr1Pdbm5ucHX1xe5ubllXrOyssLo0aOxbNky7Nu3r9p9FhcXQ61Wo7i4mAGIiKiG8BQYVWnEiBHw9PTECy+8gNjYWCQlJeHIkSN4++23ce/ePQAlV5JdvnwZ8fHxUCgUUKvVCA0Nhb+/PxYsWICEhATs3bsXn332mc7v/9FHH2HJkiVYvXo1bty4gb///hvR0dFYuXIlAOC1116DSCTCpEmTcO3aNfz666/VPqWkVCohl8u1Hunp6QCAr7/+GlOmTMHvv/+OxMREXL16FbNmzcLVq1cxaNCgcvtbtGgRUlNT0a9fv3Jf37x5M7Zv3464uDjcunUL27dvx+zZszF06FCuA0REVIM4AmQAeZK8Qb1PRcRiMf7880/MmjULgwcPRnZ2Nho1aoTevXtrRoQmTZqEI0eOoGPHjsjJycHhw4fRo0cP/PDDD5gyZQpat26NTp064eOPP650MnZ5Jk6cCLFYjOXLl2PmzJlwcHBAq1at8O677wIAHB0d8b///Q9vvPEG2rVrh+bNm2Pp0qUYMmRIlX1/8803+Oabb7S29evXD/v378eTTz6JY8eO4Y033sCDBw/g6OiIFi1aYPfu3RVetWVjYwNPT88K38/KygpLly7FjRs3IAgCAgMDERUVhffee6/6B4SIiAwmEh6doGGmsrKy4OLigszMzDKnePLz85GUlITg4GDN6ZX6sBI0NRzl/R0kqmnnz59Hhw4dMGvLLIPWP5PGSbH0taU4d+5clctEEFWlst/fVeEIkB4CAgJwPe56vb0XGBERkbljANJTQEAAAwkREVE9xUnQREREZHYYgIiIiMjsMABVE+eKU23h3z0iIuNjAKpC6cJ0BQUFtVwJmSulUgmg7IrZRESkP06CroKVlRXEYjFSU1NhbW2tub0DkakJggClUomUlBS4urpylWgiIiNiAKqCSCSCr68vkpKScOfOndouh8yQq6srJBJJbZdBRNSgMABVg42NDcLCwngajGqctbU1R36IiEyAAaiaLCwsuAovERFRA8EAREREZk8qlRptdX+u3F8/MAAREZFZM/b9HXnvxvqBAYiIiMyaQqFAnjIPYz4ZA0mwYRccyJPk2PjhRigUCgagOo4BiIiICIAkWGLQne6pfuGiNkRERGR2GICIiIjI7DAAERERkdlhACIiIiKzwwBEREREZocBiIiIiMwOAxARERGZHQYgIiIiMjsMQERERGR2GICIiIjI7DAAERERkdlhACIiIiKzwwBEREREZocBiIiIiMwOAxARERGZHQYgIiIiMjsMQERERGR2GICIiIjI7DAAERERkdlhACIiIiKzwwBEREREZocBiIiIiMxOrQagJUuWoFOnTnBycoK3tzdefPFFxMfHa7XJz8/H1KlT4eHhAUdHRwwZMgTJyclabaRSKQYOHAixWAxvb2/MnDkThYWFNflRiIiIqB6p1QB09OhRTJ06FadOncKBAwegVqvRt29f5Obmatq89957+N///ocdO3bg6NGjePDgAQYPHqx5vaioCAMHDkRBQQFOnDiBjRs3IiYmBvPmzauNj0RERET1gFVtvvn+/fu1nsfExMDb2xvnzp1D9+7dkZmZifXr12PLli3o1asXACA6Ohrh4eE4deoUnnrqKfz++++4du0aDh48CB8fH7Rt2xaLFi3CrFmzsGDBAtjY2NTGRyMiIqI6rE7NAcrMzAQAuLu7AwDOnTsHtVqNPn36aNo0a9YMAQEBOHnyJADg5MmTaNWqFXx8fDRt+vXrh6ysLFy9erXc91GpVMjKytJ6EBERkfmoMwGouLgY7777Lp5++mm0bNkSACCXy2FjYwNXV1ettj4+PpDL5Zo2j4af0tdLXyvPkiVL4OLionn4+/sb+dMQERFRXVZnAtDUqVNx5coVbN261eTvNXv2bGRmZmoed+/eNfl7EhERUd1Rq3OASkVFRWHPnj34888/0bhxY812iUSCgoICZGRkaI0CJScnQyKRaNr89ddfWv2VXiVW2uZxtra2sLW1NfKnICIiovqiVkeABEFAVFQUdu3ahT/++APBwcFar3fo0AHW1tY4dOiQZlt8fDykUikiIiIAABEREfj777+RkpKiaXPgwAE4OzujefPmNfNBiIiIqF6p1RGgqVOnYsuWLfj555/h5OSkmbPj4uICe3t7uLi4YMKECZg2bRrc3d3h7OyMt956CxEREXjqqacAAH379kXz5s0xatQoLFu2DHK5HHPmzMHUqVM5ykNERETlqtUAtHbtWgBAjx49tLZHR0dj7NixAIDPP/8cFhYWGDJkCFQqFfr164c1a9Zo2lpaWmLPnj2YMmUKIiIi4ODggDFjxmDhwoU19TGIiIionqnVACQIQpVt7Ozs8NVXX+Grr76qsE1gYCB+/fVXY5ZGREREDViduQqMiIiIqKYwABEREZHZYQAiIiIis8MARERERGaHAYiIiIjMDgMQERERmR0GICIiIjI7DEBERERkdhiAiIiIyOwwABEREZHZYQAiIiIis8MARERERGaHAYiIiIjMDgMQERERmR0GICIiIjI7DEBERERkdhiAiIiIyOwwABEREZHZYQAiIiIis8MARERERGaHAYiIiIjMDgMQERERmR0GICIiIjI7DEBERERkdhiAiIiIyOwwABEREZHZYQAiIiIis8MARERERGaHAYiIiIjMDgMQERERmR0GICIiIjI7DEBERERkdhiAiIiIyOwwABEREZHZYQAiIiIis8MARERERGaHAYiIiIjMDgMQERERmR0GICIiIjI7egWgW7duGbsOIiIiohqjVwAKDQ1Fz5498f333yM/P9/YNRERERGZlF4B6Pz582jdujWmTZsGiUSCyZMn46+//jJ2bUREREQmoVcAatu2Lb744gs8ePAAGzZsgEwmQ9euXdGyZUusXLkSqampxq6TiIiIyGgMmgRtZWWFwYMHY8eOHVi6dClu3ryJGTNmwN/fH6NHj4ZMJjNWnURERERGY1AAOnv2LN588034+vpi5cqVmDFjBhITE3HgwAE8ePAAL7zwgrHqJCIiIjIaK312WrlyJaKjoxEfH48BAwZg06ZNGDBgACwsSvJUcHAwYmJiEBQUZMxaiYiIiIxCrwC0du1ajB8/HmPHjoWvr2+5bby9vbF+/XqDiiMiIiIyBb0CUEJCQpVtbGxsMGbMGH26JyIiIjIpveYARUdHY8eOHWW279ixAxs3bjS4KCIiIiJT0isALVmyBJ6enmW2e3t7Y/HixQYXRURERGRKegUg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", 420 | "text/plain": [ 421 | "
" 422 | ] 423 | }, 424 | "metadata": {}, 425 | "output_type": "display_data" 426 | } 427 | ], 428 | "source": [ 429 | "sns.histplot(base_pll.cpu(), bins=20, alpha=0.5, label=\"Base ESM3\")\n", 430 | "sns.histplot(iglm_pll.cpu(), bins=20, alpha=0.5, label=\"IgLM\")\n", 431 | "sns.histplot(finetuned_pll.cpu(), bins=20, alpha=0.5, label=\"Finetuned ESM3\")\n", 432 | "plt.xlabel(\"Plausibility (AntiBERTy PLL)\")\n", 433 | "plt.ylabel(\"Frequency\")\n", 434 | "plt.legend()\n", 435 | "plt.show()" 436 | ] 437 | }, 438 | { 439 | "cell_type": "code", 440 | "execution_count": 41, 441 | "id": "14bfabe8-7d36-43e6-a914-d7f9e57327ce", 442 | "metadata": {}, 443 | "outputs": [], 444 | "source": [ 445 | "k=400\n", 446 | "_, base_indices = torch.topk(base_pll, k)\n", 447 | "_, finetuned_indices = torch.topk(finetuned_pll, k)\n", 448 | "_, iglm_indices = torch.topk(iglm_pll, k)\n", 449 | "\n", 450 | "top_base_seqs = [base_seqs[i] for i in base_indices]\n", 451 | "top_finetuned_seqs = [finetuned_seqs[i] for i in finetuned_indices]\n", 452 | "top_iglm_seqs = [iglm_seqs[i] for i in iglm_indices]" 453 | ] 454 | }, 455 | { 456 | "cell_type": "code", 457 | "execution_count": 49, 458 | "id": "69d71b45-0657-44c8-ba70-8281dfb1171d", 459 | "metadata": {}, 460 | "outputs": [], 461 | "source": [ 462 | "seq_list = [f\"EVQLVESGGGLVQPGGSLRLSCAASGFNIKDTYIHWVRQAPGKGLEWVARIYPTNGYTRYADSVKGRFTISADTSKNTAYLQMNSLRAEDTAVYYC{seq}WGQGTLVTVSS\" for seq in list(set(top_finetuned_seqs))[:32]]\n", 463 | "\n", 464 | "with open(\"outputs/top_finetuned.txt\", \"w\") as f:\n", 465 | " for seq in seq_list:\n", 466 | " f.write(f\"{seq}, {seq[96:106]}\\n\")" 467 | ] 468 | } 469 | ], 470 | "metadata": { 471 | "kernelspec": { 472 | "display_name": "esm", 473 | "language": "python", 474 | "name": "esm" 475 | }, 476 | "language_info": { 477 | "codemirror_mode": { 478 | "name": "ipython", 479 | "version": 3 480 | }, 481 | "file_extension": ".py", 482 | "mimetype": "text/x-python", 483 | "name": "python", 484 | "nbconvert_exporter": "python", 485 | "pygments_lexer": "ipython3", 486 | "version": "3.13.0" 487 | } 488 | }, 489 | "nbformat": 4, 490 | "nbformat_minor": 5 491 | } 492 | --------------------------------------------------------------------------------