wangleiofficial commited on
Commit
ca6ba24
·
verified ·
1 Parent(s): a2cdbf7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +161 -162
app.py CHANGED
@@ -6,22 +6,20 @@ import gradio as gr
6
  from transformers import AutoTokenizer, AutoModel
7
 
8
  # ==========================
9
- # 🚧 0. 防止 Hugging Face 缓存溢出
10
  # ==========================
11
  os.environ["HF_HOME"] = "/tmp/hf_cache"
12
  os.environ["TRANSFORMERS_CACHE"] = "/tmp/hf_cache"
13
  os.environ["HF_HUB_DISABLE_SYMLINKS_WARNING"] = "1"
14
 
15
- # 每次启动时清理旧缓存
16
  for path in ["/tmp/hf_cache", os.path.expanduser("~/.cache/huggingface")]:
17
  shutil.rmtree(path, ignore_errors=True)
18
  os.makedirs(path, exist_ok=True)
19
 
20
  # ==========================
21
- # 1. Model Definition (模型架构定义)
22
  # ==========================
23
  class AttentionPooling(nn.Module):
24
- """Attention Pooling Layer"""
25
  def __init__(self, d_model):
26
  super().__init__()
27
  self.attention_net = nn.Linear(d_model, 1)
@@ -33,7 +31,6 @@ class AttentionPooling(nn.Module):
33
  return torch.bmm(attn_weights.unsqueeze(1), x).squeeze(1)
34
 
35
  class ProtDualBranchEnhancedClassifier(nn.Module):
36
- """Enhanced dual-branch model"""
37
  def __init__(self, d_model, projection_dim, num_classes, dropout, kernel_size):
38
  super().__init__()
39
  self.cls_projector = nn.Linear(d_model, projection_dim)
@@ -68,58 +65,51 @@ class ProtDualBranchEnhancedClassifier(nn.Module):
68
  return self.classifier_head(z_fused_gated)
69
 
70
  # ==========================
71
- # 2. Load Models and Files (加载模型与配置)
72
  # ==========================
73
  DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
74
  PLM_MODEL_NAME = "facebook/esm2_t30_150M_UR50D"
75
  CLASSIFIER_PATH = "best_model_esm2_t30_150M_UR50D.pth"
76
  LABEL_MAP_PATH = "label_map.json"
77
 
78
- # --- 加载标签映射 (这里定义了 NUM_CLASSES) ---
79
  if not os.path.exists(LABEL_MAP_PATH):
80
- raise FileNotFoundError(f"Error: Missing '{LABEL_MAP_PATH}'. Please upload it to your Space.")
81
  with open(LABEL_MAP_PATH, 'r') as f:
82
  label_to_idx = json.load(f)
83
  idx_to_label = {v: k for k, v in label_to_idx.items()}
84
 
85
- # ✅ 关键变量定义
86
  NUM_CLASSES = len(idx_to_label)
87
  D_MODEL = 640
88
 
89
- # --- 加载预训练蛋白模型 ---
90
- print("🔹 Loading Protein Language Model...")
91
  tokenizer = AutoTokenizer.from_pretrained(PLM_MODEL_NAME)
92
  plm_model = AutoModel.from_pretrained(PLM_MODEL_NAME).to(DEVICE)
93
  plm_model.eval()
94
- print("✅ PLM loaded successfully.")
95
 
96
- # --- 加载下游分类器 ---
97
- print("🔹 Loading downstream classifier...")
98
  classifier = ProtDualBranchEnhancedClassifier(
99
  d_model=D_MODEL, projection_dim=32, num_classes=NUM_CLASSES,
100
  dropout=0.3, kernel_size=3
101
  ).to(DEVICE)
102
 
103
  if not os.path.exists(CLASSIFIER_PATH):
104
- raise FileNotFoundError(f"Error: Could not find '{CLASSIFIER_PATH}'. Please upload your trained .pth file.")
105
 
106
  classifier.load_state_dict(torch.load(CLASSIFIER_PATH, map_location=DEVICE))
107
  classifier.eval()
108
- print("✅ Classifier loaded. Application is ready!")
109
 
110
  # ==========================
111
- # 3. Prediction Function (预测函数)
112
  # ==========================
113
  def predict(sequence_input):
114
  if not sequence_input or sequence_input.isspace():
115
  raise gr.Error("Sequence cannot be empty.")
116
 
117
- # Clean FASTA header if present
118
  sequence = "".join(sequence_input.split('\n')[1:]) if sequence_input.startswith('>') else sequence_input
119
  sequence = re.sub(r'[^A-Z]', '', sequence.upper())
120
 
121
  if not sequence:
122
- raise gr.Error("Invalid sequence format. Please enter amino acids (A-Z).")
123
 
124
  with torch.no_grad():
125
  inputs = tokenizer(sequence, return_tensors="pt", truncation=True, max_length=1024).to(DEVICE)
@@ -136,185 +126,194 @@ def predict(sequence_input):
136
  return confidences
137
 
138
  # ==========================
139
- # 4. Academic Research Interface (UI 界面)
140
  # ==========================
141
 
142
- # 学术风格 CSS
143
- academic_css = """
144
- body { font-family: 'Roboto', 'Helvetica Neue', Arial, sans-serif; }
145
- .header-container {
146
- background: linear-gradient(to right, #1e3a8a, #3b82f6);
147
- color: white;
148
- padding: 2.5rem;
149
- border-radius: 10px;
150
- margin-bottom: 20px;
 
 
151
  text-align: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  }
153
- .header-title { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5rem; }
154
- .header-subtitle { font-size: 1.2rem; opacity: 0.9; font-weight: 300; }
155
- .badge-container { display: flex; justify-content: center; gap: 15px; margin-top: 15px; }
156
- .badge {
157
- background: rgba(255,255,255,0.2);
158
- padding: 5px 15px;
159
- border-radius: 20px;
160
- font-size: 0.9rem;
161
- border: 1px solid rgba(255,255,255,0.4);
162
  }
163
- .highlight-box {
164
- background: #f8fafc;
165
- border-left: 5px solid #3b82f6;
166
- padding: 15px;
167
- margin: 20px 0;
168
- color: #334155;
 
 
169
  }
170
- .performance-table { width: 100%; border-collapse: collapse; margin-top: 10px; }
171
- .performance-table th { background: #e2e8f0; padding: 8px; text-align: left; }
172
- .performance-table td { border-bottom: 1px solid #e2e8f0; padding: 8px; }
173
- .footer { text-align: center; color: #94a3b8; margin-top: 30px; font-size: 0.85rem; }
174
  """
175
 
176
- # 定义主题
177
- theme = gr.themes.Default(
178
  primary_hue="blue",
179
- secondary_hue="slate",
180
- neutral_hue="slate",
181
- font=[gr.themes.GoogleFont("Roboto"), "ui-sans-serif", "system-ui"]
182
  )
183
 
184
- with gr.Blocks(theme=theme, css=academic_css, title="LocPred-Prok Web Server") as app:
185
 
186
- # --- 1. 学术 Header ---
187
- with gr.Column(elem_classes="header-container"):
188
  gr.HTML("""
189
- <div class="header-title">LocPred-Prok</div>
190
- <div class="header-subtitle">
191
- Prokaryotic Protein Subcellular Localization Prediction with Dual-Branch Architecture
192
- </div>
193
- <div class="badge-container">
194
- <span class="badge">🧬 ESM-2 150M Backbone</span>
195
- <span class="badge">🏆 91.2% Accuracy</span>
196
- <span class="badge">🎯 MCC 0.889</span>
197
  </div>
198
  """)
199
 
200
- # --- 2. 核心功能区 (Tab结构) ---
201
  with gr.Tabs():
202
 
203
- # === Tab 1: Web Server (预测工具) ===
204
- with gr.TabItem("🚀 Prediction Server"):
205
  with gr.Row():
206
- # 左侧输入
207
- with gr.Column(scale=5):
208
- gr.Markdown("### 📥 Input Sequence (FASTA)")
209
  sequence_input = gr.Textbox(
210
- lines=8,
211
- placeholder=">Example_Protein\nMKFKLTAGCLAVAGVLLASSFGADAEIVVNAIYDQVARTEDGVYTQGQLTGRRIELLNKLGIEPEDSLASTVIHEFVARVGDDHGIETIIDEFYRQHPSASL...",
212
  show_label=False,
213
- elem_id="seq-input"
214
  )
 
215
  with gr.Row():
216
  clear_btn = gr.ClearButton(components=[sequence_input], value="Clear")
217
- submit_btn = gr.Button("Run Prediction", variant="primary", scale=2)
218
-
219
- gr.Markdown("#### Example Sequences")
220
- gr.Examples(
221
- examples=[
222
- [">Gram-negative Outer Membrane Protein\nMSKLVKTLTISEISKAQNNGGKPAWCWYTLAMCGAGYDSGTCDYMYSHCFGIKHHSSGSSSYHC"],
223
- [">Gram-positive Cell Wall Protein\nMKFKLTAGCLAVAGVLLASSFGADAEIVVNAIYDQVARTEDGVYTQGQLTGRRIELLNKLGIEPEDSLASTVIHEFVARVGDDHGIETIIDEFYRQHPSASL"],
224
- ],
225
- inputs=sequence_input,
226
- label=None
227
- )
228
 
229
- # 右侧输出
230
- with gr.Column(scale=4):
231
- gr.Markdown("### 📊 Prediction Results")
232
-
233
- # 这里使用了 NUM_CLASSES,现在它已经在前面定义过了
234
- output_label = gr.Label(num_top_classes=NUM_CLASSES, label="Probabilities")
235
 
236
- # 解释性文字
237
- gr.Markdown(
238
- """
239
- <div style="font-size: 0.9rem; color: #64748b; margin-top: 10px;">
240
- <b>Note:</b> This model is optimized for challenging classes including
241
- <i>Gram-positive cell wall</i> and <i>Gram-negative outer membrane</i> proteins.
242
  </div>
243
- """
244
- )
245
 
246
- # === Tab 2: About & Abstract (论文展示) ===
247
- with gr.TabItem("📖 About & Abstract"):
248
- gr.Markdown("### Abstract")
249
- gr.Markdown(
250
- """
251
- The precise localization of proteins within prokaryotic cells is fundamental to understanding their function.
252
- **LocPred-Prok** is a novel deep learning framework that employs a purpose-built **dual-branch architecture**,
253
- synergistically integrating global and local sequence features extracted from **ESM-2 (150M)** embeddings.
254
- """
255
- )
256
-
257
- # 高亮核心发现
258
- gr.HTML("""
259
- <div class="highlight-box">
260
- <b>💡 Key Findings:</b><br>
261
- 1. <b>Bigger Better:</b> Peak performance is achieved by the mid-sized ESM-2-150M, not the largest models.<br>
262
- 2. <b>Hard Classes Solved:</b> Exceptional performance on Gram-positive cell wall (MCC=0.84) and Gram-negative outer membrane (MCC=0.91).
263
- </div>
264
- """)
265
-
266
- gr.Markdown("### 📈 Performance Metrics (Homology-Partitioned Benchmark)")
267
- gr.HTML("""
268
- <table class="performance-table">
269
- <tr>
270
- <th>Metric</th>
271
- <th>LocPred-Prok Score</th>
272
- <th>Improvement</th>
273
- </tr>
274
- <tr>
275
- <td><b>Accuracy</b></td>
276
- <td><b>91.2%</b></td>
277
- <td>State-of-the-Art</td>
278
- </tr>
279
- <tr>
280
- <td><b>MCC (Overall)</b></td>
281
- <td><b>0.889</b></td>
282
- <td>Significant Leap</td>
283
- </tr>
284
- <tr>
285
- <td>MCC (Outer Membrane)</td>
286
- <td>0.91</td>
287
- <td>High Precision</td>
288
- </tr>
289
- </table>
290
- """)
291
-
292
- # === Tab 3: Citation (引用) ===
293
- with gr.TabItem("📝 Citation"):
294
- gr.Markdown("If you use LocPred-Prok in your research, please cite our paper:")
295
- gr.Code(
296
- """
297
- @article{LocPredProk2025,
298
- title={LocPred-Prok: Prokaryotic protein subcellular localization prediction with a dual-branch architecture and protein language model},
299
- author={Your Name and Co-authors},
300
- journal={Submission Journal},
301
  year={2025}
302
- }
303
- """,
304
- language="bibtex",
305
- label="BibTeX"
306
- )
307
 
308
  # --- Footer ---
309
  gr.HTML("""
310
- <div class="footer">
311
- Developed by iSysLab | <a href="https://github.com/isyslab-hust" target="_blank">GitHub</a> | Based on ESM-2 & PyTorch
312
  </div>
313
  """)
314
 
315
- # 绑定事件
316
  submit_btn.click(fn=predict, inputs=sequence_input, outputs=output_label)
317
  clear_btn.click(lambda: None, outputs=[output_label])
318
 
319
- # 启动
320
  app.launch()
 
6
  from transformers import AutoTokenizer, AutoModel
7
 
8
  # ==========================
9
+ # 🚧 0. 基础设置与缓存清理 (保持不变)
10
  # ==========================
11
  os.environ["HF_HOME"] = "/tmp/hf_cache"
12
  os.environ["TRANSFORMERS_CACHE"] = "/tmp/hf_cache"
13
  os.environ["HF_HUB_DISABLE_SYMLINKS_WARNING"] = "1"
14
 
 
15
  for path in ["/tmp/hf_cache", os.path.expanduser("~/.cache/huggingface")]:
16
  shutil.rmtree(path, ignore_errors=True)
17
  os.makedirs(path, exist_ok=True)
18
 
19
  # ==========================
20
+ # 1. Model Definition (保持不变)
21
  # ==========================
22
  class AttentionPooling(nn.Module):
 
23
  def __init__(self, d_model):
24
  super().__init__()
25
  self.attention_net = nn.Linear(d_model, 1)
 
31
  return torch.bmm(attn_weights.unsqueeze(1), x).squeeze(1)
32
 
33
  class ProtDualBranchEnhancedClassifier(nn.Module):
 
34
  def __init__(self, d_model, projection_dim, num_classes, dropout, kernel_size):
35
  super().__init__()
36
  self.cls_projector = nn.Linear(d_model, projection_dim)
 
65
  return self.classifier_head(z_fused_gated)
66
 
67
  # ==========================
68
+ # 2. Load Models (保持不变)
69
  # ==========================
70
  DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
71
  PLM_MODEL_NAME = "facebook/esm2_t30_150M_UR50D"
72
  CLASSIFIER_PATH = "best_model_esm2_t30_150M_UR50D.pth"
73
  LABEL_MAP_PATH = "label_map.json"
74
 
 
75
  if not os.path.exists(LABEL_MAP_PATH):
76
+ raise FileNotFoundError(f"Error: Missing '{LABEL_MAP_PATH}'.")
77
  with open(LABEL_MAP_PATH, 'r') as f:
78
  label_to_idx = json.load(f)
79
  idx_to_label = {v: k for k, v in label_to_idx.items()}
80
 
 
81
  NUM_CLASSES = len(idx_to_label)
82
  D_MODEL = 640
83
 
84
+ print("🔹 Loading models...")
 
85
  tokenizer = AutoTokenizer.from_pretrained(PLM_MODEL_NAME)
86
  plm_model = AutoModel.from_pretrained(PLM_MODEL_NAME).to(DEVICE)
87
  plm_model.eval()
 
88
 
 
 
89
  classifier = ProtDualBranchEnhancedClassifier(
90
  d_model=D_MODEL, projection_dim=32, num_classes=NUM_CLASSES,
91
  dropout=0.3, kernel_size=3
92
  ).to(DEVICE)
93
 
94
  if not os.path.exists(CLASSIFIER_PATH):
95
+ raise FileNotFoundError(f"Error: Could not find '{CLASSIFIER_PATH}'.")
96
 
97
  classifier.load_state_dict(torch.load(CLASSIFIER_PATH, map_location=DEVICE))
98
  classifier.eval()
99
+ print("✅ Ready.")
100
 
101
  # ==========================
102
+ # 3. Predict Logic (保持不变)
103
  # ==========================
104
  def predict(sequence_input):
105
  if not sequence_input or sequence_input.isspace():
106
  raise gr.Error("Sequence cannot be empty.")
107
 
 
108
  sequence = "".join(sequence_input.split('\n')[1:]) if sequence_input.startswith('>') else sequence_input
109
  sequence = re.sub(r'[^A-Z]', '', sequence.upper())
110
 
111
  if not sequence:
112
+ raise gr.Error("Invalid sequence.")
113
 
114
  with torch.no_grad():
115
  inputs = tokenizer(sequence, return_tensors="pt", truncation=True, max_length=1024).to(DEVICE)
 
126
  return confidences
127
 
128
  # ==========================
129
+ # 4. Ultra-Modern UI Design
130
  # ==========================
131
 
132
+ # 极简现代风 CSS
133
+ modern_css = """
134
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;800&display=swap');
135
+
136
+ body {
137
+ font-family: 'Inter', sans-serif !important;
138
+ background-color: #f8fafc;
139
+ }
140
+
141
+ /* 1. 顶部 Hero Section */
142
+ .hero-container {
143
  text-align: center;
144
+ padding: 3rem 1rem;
145
+ margin-bottom: 1rem;
146
+ }
147
+ .hero-title {
148
+ font-size: 3rem;
149
+ font-weight: 800;
150
+ margin-bottom: 0.5rem;
151
+ background: -webkit-linear-gradient(45deg, #0f172a, #334155);
152
+ -webkit-background-clip: text;
153
+ -webkit-text-fill-color: transparent;
154
+ letter-spacing: -1px;
155
+ }
156
+ .hero-subtitle {
157
+ font-size: 1.25rem;
158
+ color: #64748b;
159
+ font-weight: 300;
160
+ max-width: 600px;
161
+ margin: 0 auto;
162
+ }
163
+
164
+ /* 2. 卡片风格 */
165
+ .modern-card {
166
+ background: white;
167
+ border-radius: 16px;
168
+ padding: 24px;
169
+ border: 1px solid #e2e8f0;
170
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
171
+ transition: all 0.3s ease;
172
+ }
173
+ .modern-card:hover {
174
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
175
+ }
176
+
177
+ /* 3. 输入框优化 - 模仿代码编辑器 */
178
+ textarea {
179
+ font-family: 'SF Mono', 'Menlo', 'Monaco', 'Courier New', monospace !important;
180
+ font-size: 14px !important;
181
+ background-color: #f8fafc !important;
182
+ border: 1px solid #e2e8f0 !important;
183
+ border-radius: 8px !important;
184
+ }
185
+
186
+ /* 4. 按钮优化 */
187
+ button.primary {
188
+ background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%) !important;
189
+ border: none !important;
190
+ font-weight: 600 !important;
191
+ letter-spacing: 0.5px !important;
192
+ transition: transform 0.1s ease-in-out !important;
193
+ }
194
+ button.primary:hover {
195
+ transform: translateY(-2px);
196
+ box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
197
+ }
198
+
199
+ /* 5. 标签页优化 */
200
+ .tabs {
201
+ border: none !important;
202
+ background: transparent !important;
203
+ }
204
+ .tab-nav {
205
+ border-bottom: 1px solid #e2e8f0;
206
+ margin-bottom: 20px;
207
+ }
208
+ .tab-nav button {
209
+ font-weight: 600;
210
+ color: #64748b;
211
  }
212
+ .tab-nav button.selected {
213
+ color: #2563eb;
214
+ border-bottom: 2px solid #2563eb;
 
 
 
 
 
 
215
  }
216
+
217
+ /* 6. Footer */
218
+ .footer-text {
219
+ text-align: center;
220
+ color: #94a3b8;
221
+ font-size: 0.8rem;
222
+ margin-top: 40px;
223
+ padding-bottom: 20px;
224
  }
 
 
 
 
225
  """
226
 
227
+ # 使用极简主题作为底子
228
+ theme = gr.themes.Soft(
229
  primary_hue="blue",
230
+ radius_size="lg",
231
+ font=[gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"]
 
232
  )
233
 
234
+ with gr.Blocks(theme=theme, css=modern_css, title="LocPred-Prok") as app:
235
 
236
+ # --- Hero Section ---
237
+ with gr.Column(elem_classes="hero-container"):
238
  gr.HTML("""
239
+ <div class="hero-title">LocPred-Prok</div>
240
+ <div class="hero-subtitle">
241
+ Next-generation prokaryotic subcellular localization using dual-branch protein language models.
 
 
 
 
 
242
  </div>
243
  """)
244
 
245
+ # --- Main Content ---
246
  with gr.Tabs():
247
 
248
+ # === TAB 1: Predict ===
249
+ with gr.TabItem("Predict", id="tab-predict"):
250
  with gr.Row():
251
+ # Input Column
252
+ with gr.Column(scale=3, elem_classes="modern-card"):
253
+ gr.Markdown("### Sequence Input")
254
  sequence_input = gr.Textbox(
255
+ lines=12,
256
+ placeholder="> Paste FASTA sequence here...",
257
  show_label=False,
258
+ container=False
259
  )
260
+
261
  with gr.Row():
262
  clear_btn = gr.ClearButton(components=[sequence_input], value="Clear")
263
+ submit_btn = gr.Button("Analyze Sequence", variant="primary", scale=2)
 
 
 
 
 
 
 
 
 
 
264
 
265
+ # Output Column
266
+ with gr.Column(scale=2, elem_classes="modern-card"):
267
+ gr.Markdown("### Analysis Result")
268
+ # 隐藏 Label 自身的文字标签,保持界面干净
269
+ output_label = gr.Label(num_top_classes=NUM_CLASSES, show_label=False)
 
270
 
271
+ gr.HTML("""
272
+ <div style="margin-top: 20px; padding: 10px; background: #eff6ff; border-radius: 8px; font-size: 0.85rem; color: #1e40af;">
273
+ ℹ️ <b>Model Insight:</b> Prediction is based on the fusion of global semantic features (ESM-2) and local structural refinements.
 
 
 
274
  </div>
275
+ """)
 
276
 
277
+ # === TAB 2: Methodology ===
278
+ with gr.TabItem("Methodology", id="tab-about"):
279
+ with gr.Column(elem_classes="modern-card"):
280
+ gr.Markdown("### The Architecture")
281
+ gr.Markdown(
282
+ """
283
+ **LocPred-Prok** moves beyond the "bigger is better" paradigm. Instead of relying solely on massive parameter counts, we engineered a specialized **Dual-Branch Architecture**:
284
+
285
+ * **Global Branch:** Leverages the `ESM-2 (150M)` foundation model to capture deep semantic dependencies.
286
+ * **Local Branch:** Utilizes convolutional refinement and attention pooling to detect subtle signal motifs often missed by global pooling.
287
+
288
+ This synergy allows for precise identification of challenging localization sites, particularly in **Cell Wall** and **Outer Membrane** regions.
289
+ """
290
+ )
291
+
292
+ # === TAB 3: Cite ===
293
+ with gr.TabItem("Cite", id="tab-cite"):
294
+ with gr.Column(elem_classes="modern-card"):
295
+ gr.Markdown("### BibTeX Reference")
296
+ gr.Code(
297
+ value="""@article{LocPredProk2025,
298
+ title={LocPred-Prok: Prokaryotic protein subcellular localization prediction with a dual-branch architecture},
299
+ author={Your Name et al.},
300
+ journal={Bioinformatics},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  year={2025}
302
+ }""",
303
+ label=None,
304
+ language=None, # 防止之前的报错
305
+ interactive=False
306
+ )
307
 
308
  # --- Footer ---
309
  gr.HTML("""
310
+ <div class="footer-text">
311
+ © 2025 iSysLab HUST &nbsp;|&nbsp; Powered by PyTorch & ESM-2
312
  </div>
313
  """)
314
 
315
+ # Logic
316
  submit_btn.click(fn=predict, inputs=sequence_input, outputs=output_label)
317
  clear_btn.click(lambda: None, outputs=[output_label])
318
 
 
319
  app.launch()