PypCoder commited on
Commit
f639baf
·
verified ·
1 Parent(s): 4d5ad5b

Modularized app.py

Browse files
Files changed (1) hide show
  1. app.py +76 -63
app.py CHANGED
@@ -1,73 +1,86 @@
 
 
 
 
 
1
  import gradio as gr
2
- import spaces
3
- import torch
4
- import torch.nn as nn
5
- from huggingface_hub import hf_hub_download
6
- from transformers import EsmModel, EsmTokenizer
7
 
8
- HF_REPO_ID = "PypCoder/SERAPH"
9
- WEIGHTS_FILE = "SERAPH.pth"
10
- ESM_MODEL_ID = "facebook/esm2_t6_8M_UR50D"
11
- IDX_TO_LABEL = {0: 'H', 1: 'E', 2: 'C'}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
- class SERAPH(nn.Module):
14
- def __init__(self, esm_model, conv_channels=256, kernel_size=7,
15
- lstm_hidden=256, num_classes=3, dropout=0.3, freeze_esm=True):
16
- super().__init__()
17
- self.esm = esm_model
18
- if freeze_esm:
19
- for param in self.esm.encoder.layer[:-2].parameters():
20
- param.requires_grad = False
21
- esm_embed_dim = self.esm.config.hidden_size
22
- self.conv = nn.Conv1d(esm_embed_dim, conv_channels, kernel_size=kernel_size, padding=kernel_size//2)
23
- self.bn = nn.BatchNorm1d(conv_channels)
24
- self.dropout = nn.Dropout(dropout)
25
- self.bilstm = nn.LSTM(conv_channels, lstm_hidden, num_layers=2, batch_first=True, bidirectional=True)
26
- self.fc = nn.Linear(lstm_hidden * 2, num_classes)
27
 
28
- def forward(self, input_ids, attention_mask=None):
29
- x = self.esm(input_ids=input_ids, attention_mask=attention_mask).last_hidden_state
30
- x = x.transpose(1, 2)
31
- x = torch.relu(self.bn(self.conv(x)))
32
- x = self.dropout(x)
33
- x = x.transpose(1, 2)
34
- x, _ = self.bilstm(x)
35
- x = self.dropout(x)
36
- return self.fc(x)
37
 
38
- print("Loading ESM2 backbone...")
39
- esm = EsmModel.from_pretrained(ESM_MODEL_ID)
40
- tokenizer = EsmTokenizer.from_pretrained(ESM_MODEL_ID)
41
 
42
- print("Downloading SERAPH weights...")
43
- weights_path = hf_hub_download(repo_id=HF_REPO_ID, filename=WEIGHTS_FILE)
44
- checkpoint = torch.load(weights_path, map_location="cpu")
 
 
 
 
 
 
 
45
 
46
- model = SERAPH(esm_model=esm)
47
- model.load_state_dict(checkpoint["model_state_dict"])
48
- model.eval()
49
- print("SERAPH ready.")
 
 
50
 
51
- @spaces.GPU
52
- def predict(sequence: str) -> dict:
53
- sequence = sequence.upper().strip()
54
- tokens = tokenizer(sequence, return_tensors="pt", truncation=True, max_length=512)
55
- with torch.no_grad():
56
- output = model(input_ids=tokens["input_ids"], attention_mask=tokens["attention_mask"])
57
- preds = output.argmax(dim=-1)[0]
58
- labels = [IDX_TO_LABEL[p.item()] for p in preds[1:-1]]
59
- return {
60
- "sequence": sequence,
61
- "prediction": "".join(labels),
62
- "length": len(sequence),
63
- "composition": {"H": labels.count("H"), "E": labels.count("E"), "C": labels.count("C")},
64
- }
65
 
66
- demo = gr.Interface(
67
- fn=predict,
68
- inputs=gr.Textbox(label="Amino Acid Sequence"),
69
- outputs=gr.JSON(label="SERAPH Prediction"),
70
- title="SERAPH — Protein Secondary Structure Prediction",
71
- )
72
 
73
- demo.launch()
 
 
1
+ """
2
+ SERAPH Playground — Gradio app entry point.
3
+
4
+ Run with: python app.py
5
+ """
6
  import gradio as gr
 
 
 
 
 
7
 
8
+ from config import PORTFOLIO_URL
9
+ from dataset import PROTEIN_DATASET
10
+ from html_builder import build_result_html
11
+ from inference import predict_structure, on_preset_change
12
+ from styles import CUSTOM_CSS
13
+
14
+ with gr.Blocks(css=CUSTOM_CSS, title="SERAPH — Protein Playground") as demo:
15
+
16
+ # Top Branding Navigation Bar (back button + title + status)
17
+ gr.HTML(f"""
18
+ <div style='display: flex; justify-content: space-between; align-items: center; padding: 12px 0; margin-bottom: 20px; border-bottom: 1px solid rgba(255,255,255,0.08);'>
19
+ <a href="{PORTFOLIO_URL}" target="_top" class="back-to-portfolio">
20
+ &larr; Back to Portfolio
21
+ </a>
22
+ <div style='font-family: "Space Grotesk", sans-serif; font-weight: 700; font-size: 1.1rem; color: #f5f5f7;'>
23
+ SERAPH <span style='font-size: 0.75rem; font-family: "JetBrains Mono"; color: #8e8e93; font-weight: 400; margin-left: 8px;'>v1.0 • ESM-2 + BiLSTM</span>
24
+ </div>
25
+ <div style='font-family: "JetBrains Mono", monospace; font-size: 0.72rem; padding: 4px 12px; background: rgba(255, 255, 255, 0.04); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 99px; color: #8e8e93;'>
26
+ 🟢 Model Live
27
+ </div>
28
+ </div>
29
+ """)
30
+
31
+ gr.HTML("""
32
+ <h1 class="hero-title">SERAPH Playground</h1>
33
+ <p class="hero-subtitle">Predict 3-state protein secondary structure (Alpha Helix, Beta Sheet, Coil) using fine-tuned ESM-2 protein language embeddings.</p>
34
+ """)
35
 
36
+ with gr.Row():
37
+ preset_dropdown = gr.Dropdown(
38
+ choices=["-- Select a Preloaded Protein Preset (50 Available) --"] + [p["name"] for p in PROTEIN_DATASET],
39
+ value="01. Human Myoglobin (Oxygen Storage)",
40
+ label="SELECT FROM 50 PRELOADED PROTEIN STRUCTURES",
41
+ interactive=True
42
+ )
 
 
 
 
 
 
 
43
 
44
+ with gr.Row():
45
+ sequence_input = gr.Textbox(
46
+ value=PROTEIN_DATASET[0]["sequence"],
47
+ label="AMINO ACID SEQUENCE (FASTA / IUPAC)",
48
+ lines=3,
49
+ placeholder="Type or paste amino acid sequence..."
50
+ )
 
 
51
 
52
+ with gr.Row():
53
+ predict_btn = gr.Button("Run SERAPH Prediction ⚡", elem_classes=["btn-magnetic"])
 
54
 
55
+ output_html = gr.HTML(
56
+ value=build_result_html(
57
+ PROTEIN_DATASET[0]["sequence"],
58
+ "C" * len(PROTEIN_DATASET[0]["sequence"]), # placeholder before click
59
+ PROTEIN_DATASET[0]["true_ss"],
60
+ None,
61
+ PROTEIN_DATASET[0]["description"],
62
+ PROTEIN_DATASET[0]["fun_fact"]
63
+ )
64
+ )
65
 
66
+ # Event Handlers
67
+ preset_dropdown.change(
68
+ fn=on_preset_change,
69
+ inputs=[preset_dropdown],
70
+ outputs=[sequence_input]
71
+ )
72
 
73
+ predict_btn.click(
74
+ fn=predict_structure,
75
+ inputs=[sequence_input, preset_dropdown],
76
+ outputs=[output_html, gr.State(), gr.State()]
77
+ )
 
 
 
 
 
 
 
 
 
78
 
79
+ gr.HTML("""
80
+ <div style='border-top: 1px solid rgba(255,255,255,0.08); margin-top: 40px; padding-top: 20px; text-align: center; font-size: 0.8rem; color: #55555a; font-family: "Plus Jakarta Sans";'>
81
+ Built with PyTorch &amp; Hugging Face Spaces
82
+ </div>
83
+ """)
 
84
 
85
+ if __name__ == "__main__":
86
+ demo.launch()