nonzeroexit commited on
Commit
f0f9b27
·
verified ·
1 Parent(s): 01617d1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +53 -70
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
- # Load model and scaler
 
 
9
  model = joblib.load("RF.joblib")
10
  scaler = joblib.load("norm (4).joblib")
11
 
12
- # Feature list (KEEP THIS CONSISTENT)
 
 
 
 
 
 
 
 
 
 
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
- # Normalize ALL features
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) and features.startswith("Error:"):
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
- # === Tokenize & embed using mean pooling ===
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 = model(**tokens)
123
- embedding = outputs.last_hidden_state.mean(dim=1).squeeze().cpu().numpy().reshape(1, -1) # Shape: (1, 1024)
124
 
125
- # === MIC models and scalers for each bacterium ===
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" # Ensure this PCA file was also saved using joblib
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
- # === Load model and predict ===
165
- mic_model = joblib.load(cfg["model"])
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) and features.startswith("Error:"):
184
- return "Error", 0.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
- import gradio as gr
 
 
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 (e.g., FLPVLAGGL) to predict AMP class and MIC values across bacteria."
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)