CoolFace
Apppublic

ChatterjeeLab/zero_shot_mutation_prediction

sourceHugging Faceupdated 2y agoView on Hugging Face
1likes
app_1_heatmap.py198 linesDownload Raw Back to root
1import gradio as gr2import pandas as pd3import torch4from transformers import AutoTokenizer, AutoModelForMaskedLM5import torch.nn.functional as F6import logging7import numpy as np8import matplotlib.pyplot as plt9import seaborn as sns10from io import BytesIO11from PIL import Image12from contextlib import contextmanager13import warnings14import sys15import os16import zipfile17 18logging.getLogger("transformers.modeling_utils").setLevel(logging.ERROR)19device = torch.device("cuda" if torch.cuda.is_available() else "cpu")20print(f"Using device: {device}")21 22# Load the tokenizer and model23model_name = "ChatterjeeLab/FusOn-pLM"24tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)25model = AutoModelForMaskedLM.from_pretrained(model_name, trust_remote_code=True)26model.to(device)27model.eval()28 29@contextmanager30def suppress_output():31    with open(os.devnull, 'w') as devnull:32        old_stdout = sys.stdout33        sys.stdout = devnull34        try:35            yield36        finally:37            sys.stdout = old_stdout38 39def process_sequence(sequence, domain_bounds, n):40    AAs_tokens = ['L', 'A', 'G', 'V', 'S', 'E', 'R', 'T', 'I', 'D', 'P', 'K', 'Q', 'N', 'F', 'Y', 'M', 'H', 'W', 'C']41    # checking sequence inputs42    if not sequence.strip():43      raise gr.Error("Error: The sequence input is empty. Please enter a valid protein sequence.") 44      return None, None, None45    if any(char not in AAs_tokens for char in sequence):46      raise gr.Error("Error: The sequence input contains non-amino acid characters. Please enter a valid protein sequence.") 47      return None, None, None48 49    # checking domain bounds inputs50    try:51      start = int(domain_bounds['start'][0])52      end = int(domain_bounds['end'][0])53    except ValueError:54      raise gr.Error("Error: Start and end indices must be integers.")55      return None, None, None56    if start >= end:57      raise gr.Error("Start index must be smaller than end index.")58      return None, None, None59    if start == 0 and end != 0:60      raise gr.Error("Indexing starts at 1. Please enter valid domain bounds.")61      return None, None, None62    if start <= 0 or end <= 0:63      raise gr.Error("Domain bounds must be positive integers. Please enter valid domain bounds.")64      return None, None, None65    if start > len(sequence) or end > len(sequence):66      raise gr.Error("Domain bounds exceed sequence length.")67      return None, None, None68 69    # checking n inputs70    if n == None:71      raise gr.Error("Choose Top N Tokens from the dropdown menu.")72      return None, None, None73 74    start_index = int(domain_bounds['start'][0]) - 175    end_index = int(domain_bounds['end'][0])76 77    top_n_mutations = {}78    all_logits = []79 80    for i in range(len(sequence)):81          if start_index <= i <= (end_index - 1):82              masked_seq = sequence[:i] + '<mask>' + sequence[i+1:]83              inputs = tokenizer(masked_seq, return_tensors="pt", padding=True, truncation=True, max_length=2000)84              inputs = {k: v.to(device) for k, v in inputs.items()}85              with torch.no_grad():86                  logits = model(**inputs).logits87              mask_token_index = torch.where(inputs["input_ids"] == tokenizer.mask_token_id)[1]88              mask_token_logits = logits[0, mask_token_index, :]89 90              # Define amino acid tokens91              all_tokens_logits = mask_token_logits.squeeze(0)92              top_tokens_indices = torch.argsort(all_tokens_logits, dim=0, descending=True)93              top_tokens_logits = all_tokens_logits[top_tokens_indices]94              mutation = []95              # make sure we don't include non-AA tokens96              for token_index in top_tokens_indices:97                  decoded_token = tokenizer.decode([token_index.item()])98                  if decoded_token in AAs_tokens:99                      mutation.append(decoded_token)100                      if len(mutation) == n:101                          break102              top_n_mutations[(sequence[i], i)] = mutation103 104              # collecting logits for the heatmap105              logits_array = mask_token_logits.cpu().numpy()106              # filter out non-amino acid tokens107              filtered_indices = list(range(4, 23 + 1))108              filtered_logits = logits_array[:, filtered_indices]109              all_logits.append(filtered_logits)110 111 112    token_indices = torch.arange(logits.size(-1))113    tokens = [tokenizer.decode([idx]) for idx in token_indices]114    filtered_tokens = [tokens[i] for i in filtered_indices]115 116    all_logits_array = np.vstack(all_logits)117    normalized_logits_array = F.softmax(torch.tensor(all_logits_array), dim=-1).numpy()118    transposed_logits_array = normalized_logits_array.T119 120    # Plotting the heatmap121    domain_len = end - start122    if 500 > domain_len > 100:123      step_size = 49124    elif 500 <= domain_len:125      step_size = 99126    elif domain_len < 10:127      step_size = 1128    else:129      step_size = 9130    x_tick_positions = np.arange(start_index, end_index, step_size)131    x_tick_labels = [str(pos + 1) for pos in x_tick_positions]132 133    plt.figure(figsize=(15, 8))134    plt.rcParams.update({'font.size': 18})135 136    sns.heatmap(transposed_logits_array, cmap='plasma', xticklabels=x_tick_labels, yticklabels=filtered_tokens)137    plt.title('Token Probability Heatmap')138    plt.ylabel('Token')139    plt.xlabel('Residue Index')140    plt.yticks(rotation=0)141    plt.xticks(x_tick_positions - start_index + 0.5, x_tick_labels, rotation=0)142 143    # Save the figure to a BytesIO object144    buf = BytesIO()145    plt.savefig(buf, format='png', dpi = 300)146    buf.seek(0)147    plt.close()148 149    # Convert BytesIO object to an image150    img = Image.open(buf)151 152    original_residues = []153    mutations = []154    positions = []155 156    for key, value in top_n_mutations.items():157        original_residue, position = key158        original_residues.append(original_residue)159        mutations.append(value)160        positions.append(position + 1)161 162    df = pd.DataFrame({163        'Original Residue': original_residues,164        'Predicted Residues': mutations,165        'Position': positions166    })167    df.to_csv("predicted_tokens.csv", index=False)168    img.save("heatmap.png", dpi=(300, 300))169    zip_path = "outputs.zip"170    with zipfile.ZipFile(zip_path, 'w') as zipf:171        zipf.write("predicted_tokens.csv")172        zipf.write("heatmap.png")173 174 175    return df, img, zip_path176 177demo = gr.Interface(178    fn=process_sequence,179    inputs=[180        gr.Textbox(label="Sequence", placeholder="Enter the protein sequence here"),181        gr.Dataframe(182            headers=["start", "end"],183            datatype=["number", "number"],184            row_count=(1, "fixed"),185            col_count=(2, "fixed"),186            label="Domain Bounds"187        ),188        gr.Dropdown([i for i in range(1, 21)], label="Top N Tokens"),189    ],190     outputs=[191        gr.Dataframe(label="Predicted Tokens (in order of decreasing likelihood)"),192        gr.Image(type="pil", label="Heatmap"),193        gr.File(label="Download Outputs"),194    ],195)196if __name__ == "__main__":197    with suppress_output():198      demo.launch()