oldAhmed commited on
Commit
4cc7b45
·
verified ·
1 Parent(s): b5be0ac

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -242
app.py DELETED
@@ -1,242 +0,0 @@
1
- import os
2
- os.environ["HF_HOME"] = "/app/cache"
3
- os.environ["TORCH_HOME"] = "/app/cache"
4
-
5
- import torch
6
- import sys
7
- import torch.nn as nn
8
- from torchvision import transforms
9
- from torchvision.models import inception_v3, Inception_V3_Weights
10
- from transformers import BertTokenizerFast, BertModel
11
- from PIL import Image
12
- from fastapi import FastAPI, File, UploadFile, Form
13
- from fastapi.responses import JSONResponse
14
- import traceback
15
- from io import BytesIO
16
-
17
- # ----------------------------
18
- # Configurations
19
- # ----------------------------
20
- app = FastAPI(title="Multimodal Hate Speech Detection")
21
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
22
-
23
- CACHE_DIR = "/app/cache"
24
- MODEL_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "BERT_Inception_multimodal.pt")
25
-
26
- # Image preprocessing
27
- image_transform = transforms.Compose([
28
- transforms.Resize((299, 299)),
29
- transforms.ToTensor(),
30
- transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
31
- ])
32
-
33
- # ----------------------------
34
- # Model Definition
35
- # ----------------------------
36
- class TensorFusionMultimodalModel(nn.Module):
37
- def __init__(self, bert_model):
38
- super(TensorFusionMultimodalModel, self).__init__()
39
- self.bert = bert_model
40
- self.text_fc = nn.Linear(self.bert.config.hidden_size, 256)
41
-
42
- # Image branch (InceptionV3 backbone)
43
- weights = Inception_V3_Weights.IMAGENET1K_V1
44
- inception = inception_v3(weights=None, aux_logits=True) # Do NOT auto-download
45
- state_dict = weights.get_state_dict(progress=False) # Load from local cache
46
- inception.load_state_dict(state_dict)
47
-
48
- for param in inception.parameters():
49
- param.requires_grad = False
50
-
51
- self.cnn_backbone = nn.Sequential(*list(inception.children())[:-1]) # Remove FC layer
52
- self.image_fc = nn.Sequential(
53
- nn.Flatten(),
54
- nn.Linear(2048, 256),
55
- nn.BatchNorm1d(256),
56
- nn.ReLU(),
57
- nn.Dropout(0.5)
58
- )
59
-
60
- # Fusion layer
61
- self.fusion_dim = 256 * 256 + 256 + 256 + 1
62
- self.fusion_fc = nn.Sequential(
63
- nn.Linear(self.fusion_dim, 256),
64
- nn.ReLU(),
65
- nn.Dropout(0.4),
66
- nn.Linear(256, 1),
67
- nn.Sigmoid()
68
- )
69
-
70
- def tensor_fusion(self, text_feat, img_feat):
71
- batch_size = text_feat.size(0)
72
- outer = torch.bmm(text_feat.unsqueeze(2), img_feat.unsqueeze(1)).view(batch_size, -1)
73
- fusion = torch.cat([outer, text_feat, img_feat, torch.ones(batch_size, 1).to(text_feat.device)], dim=1)
74
- return fusion
75
-
76
- def forward(self, input_ids, attention_mask, images):
77
- text_output = self.bert(input_ids=input_ids, attention_mask=attention_mask)
78
- text_feat = self.text_fc(text_output.pooler_output)
79
-
80
- img_feat = self.cnn_backbone(images).flatten(1)
81
- img_feat = self.image_fc(img_feat)
82
-
83
- fused_feat = self.tensor_fusion(text_feat, img_feat)
84
- return self.fusion_fc(fused_feat)
85
-
86
- # ----------------------------
87
- # Load Model on Startup
88
- # ----------------------------
89
- @app.on_event("startup")
90
- def load_model():
91
- global model, tokenizer
92
-
93
- if not os.path.exists(MODEL_PATH):
94
- raise FileNotFoundError(f"[ERROR] Model file not found: {MODEL_PATH}")
95
-
96
- tokenizer = BertTokenizerFast.from_pretrained("bert-base-multilingual-cased", cache_dir=CACHE_DIR)
97
-
98
- # Allow pickle resolution
99
- sys.modules['__main__'] = sys.modules['app']
100
-
101
- # Load the full model
102
- loaded = torch.load(MODEL_PATH, map_location=device)
103
-
104
- # ✅ If loaded object still has `forward` via DataParallel, unwrap forcibly
105
- if hasattr(loaded, "module"): # Works even if type doesn't match
106
- loaded = loaded.module
107
-
108
- # ✅ Move all parameters and buffers to device
109
- loaded = loaded.to(device)
110
-
111
- # Set to eval
112
- loaded.eval()
113
-
114
- for p in loaded.parameters():
115
- p.requires_grad = False
116
-
117
- model = loaded
118
- print(f"[INFO] Model loaded successfully on {device}")
119
-
120
- # @app.on_event("startup")
121
- # def load_model():
122
- # global model, tokenizer
123
-
124
- # if not os.path.exists(MODEL_PATH):
125
- # raise FileNotFoundError(f"[ERROR] Model file not found: {MODEL_PATH}")
126
-
127
- # # Load tokenizer & BERT
128
- # tokenizer = BertTokenizerFast.from_pretrained("bert-base-multilingual-cased", cache_dir=CACHE_DIR)
129
- # bert_model = BertModel.from_pretrained("bert-base-multilingual-cased", cache_dir=CACHE_DIR)
130
-
131
- # # Trick for pickle to resolve class path
132
- # import sys
133
- # sys.modules['__main__'] = sys.modules['app']
134
-
135
- # # Load the full model
136
- # model = torch.load(MODEL_PATH, map_location=device)
137
- # model.to(device)
138
- # model.eval()
139
-
140
- # print("[INFO] Model and tokenizer loaded successfully!")
141
-
142
-
143
-
144
-
145
-
146
- # ----------------------------
147
- # API Endpoints
148
- # ----------------------------
149
- @app.get("/")
150
- async def root():
151
- return {"message": "API is running. Use POST /predict"}
152
-
153
-
154
- @app.post("/predict")
155
- async def predict(text: str = Form(...), file: UploadFile = File(...)):
156
- try:
157
- if 'model' not in globals() or model is None:
158
- return JSONResponse(content={"error": "Model not loaded"}, status_code=500)
159
- if 'tokenizer' not in globals() or tokenizer is None:
160
- return JSONResponse(content={"error": "Tokenizer not loaded"}, status_code=500)
161
-
162
- # Tokenize text
163
- encoding = tokenizer(
164
- text,
165
- add_special_tokens=True,
166
- max_length=125,
167
- padding='max_length',
168
- truncation=True,
169
- return_tensors='pt'
170
- )
171
- input_ids = encoding['input_ids'].to(device, non_blocking=True)
172
- attention_mask = encoding['attention_mask'].to(device, non_blocking=True)
173
-
174
- # Process image
175
- contents = await file.read()
176
- if not contents:
177
- return JSONResponse(content={"error": "Empty file received"}, status_code=400)
178
-
179
- try:
180
- img = Image.open(BytesIO(contents)).convert("RGB")
181
- except Exception:
182
- return JSONResponse(content={"error": "Invalid image file"}, status_code=400)
183
-
184
- img_tensor = image_transform(img).unsqueeze(0).to(device, non_blocking=True)
185
-
186
- # Predict
187
- with torch.inference_mode():
188
- # (Optional) autocast for GPU float16 speedup; safe to leave on CPU too
189
- # On CPU autocast('cpu') is available in newer torch; we keep it simple:
190
- output = model(input_ids, attention_mask, img_tensor)
191
-
192
- # output should be shape [1, 1]; get scalar
193
- prob = float(output.squeeze().item())
194
- label = "Hate" if prob >= 0.5 else "No Hate"
195
-
196
- return {"prediction": label, "confidence": round(prob, 4)}
197
-
198
- except Exception:
199
- print("Prediction Error:", traceback.format_exc())
200
- return JSONResponse(content={"error": "Internal server error"}, status_code=500)
201
-
202
- # @app.post("/predict")
203
- # async def predict(text: str = Form(...), file: UploadFile = File(...)):
204
- # try:
205
- # # Tokenize text
206
- # encoding = tokenizer(
207
- # text,
208
- # add_special_tokens=True,
209
- # max_length=125,
210
- # padding='max_length',
211
- # truncation=True,
212
- # return_tensors='pt'
213
- # )
214
- # input_ids = encoding['input_ids'].to(device)
215
- # attention_mask = encoding['attention_mask'].to(device)
216
-
217
- # # Process image
218
- # contents = await file.read()
219
- # img = Image.open(BytesIO(contents)).convert("RGB")
220
- # img_tensor = image_transform(img).unsqueeze(0).to(device)
221
-
222
- # # Predict
223
- # with torch.no_grad():
224
- # output = model(input_ids, attention_mask, img_tensor)
225
- # prob = output.item()
226
- # label = "Hate" if prob >= 0.5 else "No Hate"
227
-
228
- # return {"prediction": label, "confidence": round(prob, 4)}
229
-
230
-
231
-
232
- # except Exception as e:
233
- # print("Prediction Error:", traceback.format_exc())
234
- # return JSONResponse(content={"error": str(e)}, status_code=500)
235
-
236
-
237
- # ----------------------------
238
- # Run server (for local dev)
239
- # ----------------------------
240
- if __name__ == "__main__":
241
- import uvicorn
242
- uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=False)