Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -4,12 +4,27 @@ import numpy as np
|
|
4 |
import pandas as pd
|
5 |
from propy import AAComposition, Autocorrelation, CTD, PseudoAAC
|
6 |
from sklearn.preprocessing import MinMaxScaler
|
|
|
|
|
|
|
7 |
|
8 |
-
#
|
|
|
|
|
9 |
model = joblib.load("RF.joblib")
|
10 |
scaler = joblib.load("norm (4).joblib")
|
11 |
|
12 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
selected_features = [
|
14 |
"_SolventAccessibilityC3", "_SecondaryStrC1", "_SecondaryStrC3", "_ChargeC1", "_PolarityC1",
|
15 |
"_NormalizedVDWVC1", "_HydrophobicityC3", "_SecondaryStrT23", "_PolarizabilityD1001",
|
@@ -45,16 +60,17 @@ selected_features = [
|
|
45 |
"APAAC24"
|
46 |
]
|
47 |
|
|
|
|
|
|
|
48 |
def extract_features(sequence):
|
49 |
-
|
50 |
all_features_dict = {}
|
|
|
|
|
|
|
51 |
|
52 |
-
# Calculate all dipeptide features
|
53 |
dipeptide_features = AAComposition.CalculateAADipeptideComposition(sequence)
|
54 |
-
|
55 |
-
# Add only the first 420 features to the dictionary
|
56 |
-
first_420_keys = list(dipeptide_features.keys())[:420] # Get the first 420 keys
|
57 |
-
filtered_dipeptide_features = {key: dipeptide_features[key] for key in first_420_keys}
|
58 |
ctd_features = CTD.CalculateCTD(sequence)
|
59 |
auto_features = Autocorrelation.CalculateAutoTotal(sequence)
|
60 |
pseudo_features = PseudoAAC.GetAPseudoAAC(sequence, lamda=9)
|
@@ -64,65 +80,45 @@ def extract_features(sequence):
|
|
64 |
all_features_dict.update(auto_features)
|
65 |
all_features_dict.update(pseudo_features)
|
66 |
|
67 |
-
# Convert all features to DataFrame
|
68 |
feature_df_all = pd.DataFrame([all_features_dict])
|
|
|
|
|
|
|
69 |
|
70 |
-
|
71 |
-
normalized_feature_array = scaler.transform(feature_df_all.values) # Normalize the numpy array
|
72 |
-
normalized_feature_df = pd.DataFrame(normalized_feature_array, columns=feature_df_all.columns) # Convert back to DataFrame with original column names
|
73 |
-
|
74 |
-
# Select features AFTER normalization
|
75 |
-
feature_df_selected = normalized_feature_df[selected_features].copy()
|
76 |
-
feature_df_selected = feature_df_selected.fillna(0) # Fill missing if any after selection (though unlikely now)
|
77 |
-
feature_array = feature_df_selected.values
|
78 |
-
|
79 |
-
|
80 |
-
return feature_array
|
81 |
-
|
82 |
|
|
|
|
|
|
|
83 |
def predict(sequence):
|
84 |
-
"""Predicts whether the input sequence is an AMP."""
|
85 |
features = extract_features(sequence)
|
86 |
-
if isinstance(features, str)
|
87 |
return features
|
88 |
-
|
89 |
prediction = model.predict(features)[0]
|
90 |
probabilities = model.predict_proba(features)[0]
|
91 |
-
|
92 |
if prediction == 0:
|
93 |
return f"{probabilities[0] * 100:.2f}% chance of being an Antimicrobial Peptide (AMP)"
|
94 |
else:
|
95 |
return f"{probabilities[1] * 100:.2f}% chance of being Non-AMP"
|
96 |
|
97 |
-
|
|
|
|
|
98 |
def predictmic(sequence):
|
99 |
-
import torch
|
100 |
-
from transformers import BertTokenizer, BertModel
|
101 |
-
import numpy as np
|
102 |
-
import joblib # ✅ Use joblib instead of pickle
|
103 |
-
from math import expm1
|
104 |
-
|
105 |
-
# === Load ProtBert model ===
|
106 |
-
tokenizer = BertTokenizer.from_pretrained("Rostlab/prot_bert", do_lower_case=False)
|
107 |
-
model = BertModel.from_pretrained("Rostlab/prot_bert")
|
108 |
-
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
109 |
-
model = model.to(device).eval()
|
110 |
-
|
111 |
-
# === Preprocess input sequence ===
|
112 |
sequence = ''.join([aa for aa in sequence.upper() if aa in "ACDEFGHIKLMNPQRSTVWY"])
|
113 |
if len(sequence) < 10:
|
114 |
return {"Error": "Sequence too short or invalid. Must contain at least 10 valid amino acids."}
|
115 |
|
116 |
-
#
|
117 |
seq_spaced = ' '.join(list(sequence))
|
118 |
tokens = tokenizer(seq_spaced, return_tensors="pt", padding='max_length', truncation=True, max_length=512)
|
119 |
tokens = {k: v.to(device) for k, v in tokens.items()}
|
120 |
|
121 |
with torch.no_grad():
|
122 |
-
outputs =
|
123 |
-
embedding = outputs.last_hidden_state.mean(dim=1).squeeze().cpu().numpy().reshape(1, -1)
|
124 |
|
125 |
-
#
|
126 |
bacteria_config = {
|
127 |
"E.coli": {
|
128 |
"model": "coli_xgboost_model.pkl",
|
@@ -142,59 +138,46 @@ def predictmic(sequence):
|
|
142 |
"K.Pneumonia": {
|
143 |
"model": "pne_mlp_model.pkl",
|
144 |
"scaler": "pne_scaler.pkl",
|
145 |
-
"pca": "pne_pca"
|
146 |
}
|
147 |
}
|
148 |
|
149 |
mic_results = {}
|
150 |
-
|
151 |
for bacterium, cfg in bacteria_config.items():
|
152 |
try:
|
153 |
-
# === Load scaler and transform ===
|
154 |
scaler = joblib.load(cfg["scaler"])
|
155 |
scaled = scaler.transform(embedding)
|
156 |
-
|
157 |
-
# === Apply PCA if exists ===
|
158 |
-
if cfg["pca"] is not None:
|
159 |
pca = joblib.load(cfg["pca"])
|
160 |
transformed = pca.transform(scaled)
|
161 |
else:
|
162 |
transformed = scaled
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
mic_log = mic_model.predict(transformed)[0]
|
167 |
-
mic = round(expm1(mic_log), 3) # Inverse of log1p used in training
|
168 |
-
|
169 |
mic_results[bacterium] = mic
|
170 |
-
|
171 |
except Exception as e:
|
172 |
mic_results[bacterium] = f"Error: {str(e)}"
|
173 |
|
174 |
return mic_results
|
175 |
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
def full_prediction(sequence):
|
181 |
-
# AMP prediction
|
182 |
features = extract_features(sequence)
|
183 |
-
if isinstance(features, str)
|
184 |
-
return "Error", 0
|
185 |
-
|
186 |
prediction = model.predict(features)[0]
|
187 |
probabilities = model.predict_proba(features)[0]
|
188 |
-
|
189 |
amp_result = "Antimicrobial Peptide (AMP)" if prediction == 0 else "Non-AMP"
|
190 |
confidence = round(probabilities[0 if prediction == 0 else 1] * 100, 2)
|
191 |
-
|
192 |
-
# MIC prediction
|
193 |
mic_values = predictmic(sequence)
|
194 |
-
|
195 |
return amp_result, f"{confidence}%", mic_values
|
196 |
|
197 |
-
|
|
|
|
|
198 |
iface = gr.Interface(
|
199 |
fn=full_prediction,
|
200 |
inputs=gr.Textbox(label="Enter Protein Sequence"),
|
@@ -204,7 +187,7 @@ iface = gr.Interface(
|
|
204 |
gr.JSON(label="Predicted MIC (µg/mL) for Each Bacterium")
|
205 |
],
|
206 |
title="AMP & MIC Predictor",
|
207 |
-
description="Enter an amino acid sequence (
|
208 |
)
|
209 |
|
210 |
iface.launch(share=True)
|
|
|
4 |
import pandas as pd
|
5 |
from propy import AAComposition, Autocorrelation, CTD, PseudoAAC
|
6 |
from sklearn.preprocessing import MinMaxScaler
|
7 |
+
import torch
|
8 |
+
from transformers import BertTokenizer, BertModel
|
9 |
+
from math import expm1
|
10 |
|
11 |
+
# =====================
|
12 |
+
# Load AMP Classifier
|
13 |
+
# =====================
|
14 |
model = joblib.load("RF.joblib")
|
15 |
scaler = joblib.load("norm (4).joblib")
|
16 |
|
17 |
+
# =====================
|
18 |
+
# Load ProtBert Globally
|
19 |
+
# =====================
|
20 |
+
tokenizer = BertTokenizer.from_pretrained("Rostlab/prot_bert", do_lower_case=False)
|
21 |
+
protbert_model = BertModel.from_pretrained("Rostlab/prot_bert")
|
22 |
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
23 |
+
protbert_model = protbert_model.to(device).eval()
|
24 |
+
|
25 |
+
# =====================
|
26 |
+
# Feature List (ProPy)
|
27 |
+
# =====================
|
28 |
selected_features = [
|
29 |
"_SolventAccessibilityC3", "_SecondaryStrC1", "_SecondaryStrC3", "_ChargeC1", "_PolarityC1",
|
30 |
"_NormalizedVDWVC1", "_HydrophobicityC3", "_SecondaryStrT23", "_PolarizabilityD1001",
|
|
|
60 |
"APAAC24"
|
61 |
]
|
62 |
|
63 |
+
# =====================
|
64 |
+
# AMP Feature Extractor
|
65 |
+
# =====================
|
66 |
def extract_features(sequence):
|
|
|
67 |
all_features_dict = {}
|
68 |
+
sequence = ''.join([aa for aa in sequence.upper() if aa in "ACDEFGHIKLMNPQRSTVWY"])
|
69 |
+
if len(sequence) < 10:
|
70 |
+
return "Error: Sequence too short."
|
71 |
|
|
|
72 |
dipeptide_features = AAComposition.CalculateAADipeptideComposition(sequence)
|
73 |
+
filtered_dipeptide_features = {k: dipeptide_features[k] for k in list(dipeptide_features.keys())[:420]}
|
|
|
|
|
|
|
74 |
ctd_features = CTD.CalculateCTD(sequence)
|
75 |
auto_features = Autocorrelation.CalculateAutoTotal(sequence)
|
76 |
pseudo_features = PseudoAAC.GetAPseudoAAC(sequence, lamda=9)
|
|
|
80 |
all_features_dict.update(auto_features)
|
81 |
all_features_dict.update(pseudo_features)
|
82 |
|
|
|
83 |
feature_df_all = pd.DataFrame([all_features_dict])
|
84 |
+
normalized_array = scaler.transform(feature_df_all.values)
|
85 |
+
normalized_df = pd.DataFrame(normalized_array, columns=feature_df_all.columns)
|
86 |
+
selected_df = normalized_df[selected_features].fillna(0)
|
87 |
|
88 |
+
return selected_df.values
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
|
90 |
+
# =====================
|
91 |
+
# AMP Classifier
|
92 |
+
# =====================
|
93 |
def predict(sequence):
|
|
|
94 |
features = extract_features(sequence)
|
95 |
+
if isinstance(features, str):
|
96 |
return features
|
|
|
97 |
prediction = model.predict(features)[0]
|
98 |
probabilities = model.predict_proba(features)[0]
|
|
|
99 |
if prediction == 0:
|
100 |
return f"{probabilities[0] * 100:.2f}% chance of being an Antimicrobial Peptide (AMP)"
|
101 |
else:
|
102 |
return f"{probabilities[1] * 100:.2f}% chance of being Non-AMP"
|
103 |
|
104 |
+
# =====================
|
105 |
+
# MIC Predictor (ProtBert-based)
|
106 |
+
# =====================
|
107 |
def predictmic(sequence):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
sequence = ''.join([aa for aa in sequence.upper() if aa in "ACDEFGHIKLMNPQRSTVWY"])
|
109 |
if len(sequence) < 10:
|
110 |
return {"Error": "Sequence too short or invalid. Must contain at least 10 valid amino acids."}
|
111 |
|
112 |
+
# Tokenize
|
113 |
seq_spaced = ' '.join(list(sequence))
|
114 |
tokens = tokenizer(seq_spaced, return_tensors="pt", padding='max_length', truncation=True, max_length=512)
|
115 |
tokens = {k: v.to(device) for k, v in tokens.items()}
|
116 |
|
117 |
with torch.no_grad():
|
118 |
+
outputs = protbert_model(**tokens)
|
119 |
+
embedding = outputs.last_hidden_state.mean(dim=1).squeeze().cpu().numpy().reshape(1, -1)
|
120 |
|
121 |
+
# MIC model config
|
122 |
bacteria_config = {
|
123 |
"E.coli": {
|
124 |
"model": "coli_xgboost_model.pkl",
|
|
|
138 |
"K.Pneumonia": {
|
139 |
"model": "pne_mlp_model.pkl",
|
140 |
"scaler": "pne_scaler.pkl",
|
141 |
+
"pca": "pne_pca"
|
142 |
}
|
143 |
}
|
144 |
|
145 |
mic_results = {}
|
|
|
146 |
for bacterium, cfg in bacteria_config.items():
|
147 |
try:
|
|
|
148 |
scaler = joblib.load(cfg["scaler"])
|
149 |
scaled = scaler.transform(embedding)
|
150 |
+
if cfg["pca"]:
|
|
|
|
|
151 |
pca = joblib.load(cfg["pca"])
|
152 |
transformed = pca.transform(scaled)
|
153 |
else:
|
154 |
transformed = scaled
|
155 |
+
model = joblib.load(cfg["model"])
|
156 |
+
mic_log = model.predict(transformed)[0]
|
157 |
+
mic = round(expm1(mic_log), 3)
|
|
|
|
|
|
|
158 |
mic_results[bacterium] = mic
|
|
|
159 |
except Exception as e:
|
160 |
mic_results[bacterium] = f"Error: {str(e)}"
|
161 |
|
162 |
return mic_results
|
163 |
|
164 |
+
# =====================
|
165 |
+
# Combined Prediction Function
|
166 |
+
# =====================
|
|
|
167 |
def full_prediction(sequence):
|
|
|
168 |
features = extract_features(sequence)
|
169 |
+
if isinstance(features, str):
|
170 |
+
return "Error", "0%", {}
|
|
|
171 |
prediction = model.predict(features)[0]
|
172 |
probabilities = model.predict_proba(features)[0]
|
|
|
173 |
amp_result = "Antimicrobial Peptide (AMP)" if prediction == 0 else "Non-AMP"
|
174 |
confidence = round(probabilities[0 if prediction == 0 else 1] * 100, 2)
|
|
|
|
|
175 |
mic_values = predictmic(sequence)
|
|
|
176 |
return amp_result, f"{confidence}%", mic_values
|
177 |
|
178 |
+
# =====================
|
179 |
+
# Gradio Interface
|
180 |
+
# =====================
|
181 |
iface = gr.Interface(
|
182 |
fn=full_prediction,
|
183 |
inputs=gr.Textbox(label="Enter Protein Sequence"),
|
|
|
187 |
gr.JSON(label="Predicted MIC (µg/mL) for Each Bacterium")
|
188 |
],
|
189 |
title="AMP & MIC Predictor",
|
190 |
+
description="Enter an amino acid sequence (≥10 valid letters) to predict AMP class and MIC values."
|
191 |
)
|
192 |
|
193 |
iface.launch(share=True)
|