adamantix commited on
Commit
59f4cee
·
verified ·
1 Parent(s): 329c09c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +121 -118
app.py CHANGED
@@ -1,119 +1,122 @@
1
- import io
2
- import pickle
3
-
4
- import numpy as np
5
- import torch
6
- from fastapi import FastAPI, UploadFile, File, Form
7
- from fastapi.middleware.cors import CORSMiddleware
8
- from PIL import Image
9
- from transformers import AutoTokenizer, AutoModel
10
- import open_clip
11
-
12
- device = "cuda" if torch.cuda.is_available() else "cpu"
13
-
14
- TEXT_MODEL_NAME = "indobenchmark/indobert-large-p1"
15
- tokenizer = AutoTokenizer.from_pretrained(TEXT_MODEL_NAME)
16
- text_model = AutoModel.from_pretrained(TEXT_MODEL_NAME).to(device)
17
- text_model.eval()
18
-
19
- clip_model, _, clip_preprocess = open_clip.create_model_and_transforms(
20
- "EVA01-g-14-plus",
21
- pretrained="merged2b_s11b_b114k"
22
- )
23
- clip_model.to(device)
24
- clip_model.eval()
25
-
26
- with open("xgb_full.pkl", "rb") as f:
27
- xgb_model = pickle.load(f)
28
-
29
- def preprocess_text(text: str) -> str:
30
- # nanti ditambahin preprocessingnya
31
- return text.strip()
32
-
33
- app = FastAPI(
34
- title="Multimodal Water Pollution Risk API",
35
- description=(
36
- "Input: text + image + geospatial + time\n"
37
- "Model: IndoBERT + EVA-CLIP (HF Hub) + XGBoost (xgb.pkl)\n"
38
- ),
39
- version="1.0.0",
40
- )
41
-
42
- app.add_middleware(
43
- CORSMiddleware,
44
- allow_origins=["*"],
45
- allow_methods=["*"],
46
- allow_headers=["*"],
47
- )
48
-
49
- @app.get("/")
50
- def root():
51
- return {
52
- "status": "OK",
53
- "message": "Multimodal Water Pollution Risk API is running.",
54
- "info": "Use POST /predict with text, image, and features.",
55
- }
56
-
57
- @app.post("/predict")
58
- async def predict(
59
- text: str = Form(...),
60
- longitude: float = Form(...),
61
- latitude: float = Form(...),
62
- location_cluster: int = Form(...),
63
- hour: int = Form(...),
64
- dayofweek: int = Form(...),
65
- month: int = Form(...),
66
- image: UploadFile = File(...),
67
- ):
68
- # 1. Preprocess text
69
- cleaned_text = preprocess_text(text)
70
-
71
- # 2. Encode text -> IndoBERT CLS embedding (shape: [1, 1024])
72
- text_inputs = tokenizer(
73
- cleaned_text,
74
- return_tensors="pt",
75
- padding="max_length",
76
- truncation=True,
77
- max_length=128,
78
- )
79
- text_inputs = {k: v.to(device) for k, v in text_inputs.items()}
80
- with torch.no_grad():
81
- text_emb = text_model(**text_inputs).last_hidden_state[:, 0, :]
82
- text_emb = text_emb.cpu().numpy()
83
-
84
- # 3. Encode image -> EVA-CLIP image embedding (shape: [1, 1024] / sesuai model)
85
- img_bytes = await image.read()
86
- pil_img = Image.open(io.BytesIO(img_bytes)).convert("RGB")
87
- img_tensor = clip_preprocess(pil_img).unsqueeze(0).to(device)
88
-
89
- with torch.no_grad():
90
- img_emb = clip_model.encode_image(img_tensor)
91
- img_emb = img_emb.cpu().numpy()
92
-
93
- # 4. Additional numeric features (same order as training)
94
- add_feats = np.array(
95
- [[longitude, latitude, location_cluster, hour, dayofweek, month]],
96
- dtype=np.float32,
97
- )
98
-
99
- # 5. Concatenate: [image_emb, text_emb, add_feats]
100
- # pastikan bentuk-nya [1, dim_image + dim_text + 6]
101
- fused = np.concatenate([img_emb, text_emb, add_feats], axis=1)
102
-
103
- # 6. XGBoost prediction
104
- proba = xgb_model.predict_proba(fused)[0] # shape: [2]
105
- pred_idx = int(np.argmax(proba))
106
- label = "KRITIS" if pred_idx == 1 else "WASPADA"
107
-
108
- return {
109
- "prediction": label,
110
- "probabilities": {
111
- "WASPADA": float(proba[0]),
112
- "KRITIS": float(proba[1]),
113
- },
114
- }
115
-
116
- if __name__ == "__main__":
117
- import uvicorn
118
-
 
 
 
119
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
1
+ import io
2
+ import pickle
3
+ import numpy as np
4
+ import torch
5
+ from fastapi import FastAPI, UploadFile, File, Form
6
+ from fastapi.middleware.cors import CORSMiddleware
7
+ from PIL import Image
8
+ from transformers import AutoTokenizer, AutoModel
9
+ import open_clip
10
+ import re
11
+
12
+ device = "cuda" if torch.cuda.is_available() else "cpu"
13
+
14
+ TEXT_MODEL_NAME = "indobenchmark/indobert-large-p1"
15
+ tokenizer = AutoTokenizer.from_pretrained(TEXT_MODEL_NAME)
16
+ text_model = AutoModel.from_pretrained(TEXT_MODEL_NAME).to(device)
17
+ text_model.eval()
18
+
19
+ clip_model, _, clip_preprocess = open_clip.create_model_and_transforms(
20
+ "EVA01-g-14-plus",
21
+ pretrained="merged2b_s11b_b114k"
22
+ )
23
+ clip_model.to(device)
24
+ clip_model.eval()
25
+
26
+ with open("xgb_full.pkl", "rb") as f:
27
+ xgb_model = pickle.load(f)
28
+
29
+ def preprocess_text(text: str) -> str:
30
+ text = str(text).lower()
31
+ text = re.sub(r'http\S+|www\.\S+', '', text)
32
+ text = re.sub(r'@\w+|#\w+', '', text)
33
+ text = re.sub(r'[^a-z\s]', ' ', text)
34
+ text = re.sub(r'\s+', ' ', text).strip()
35
+ return " ".join(text.split())
36
+
37
+ app = FastAPI(
38
+ title="Multimodal Water Pollution Risk API",
39
+ description=(
40
+ "Input: text + image + geospatial + time\n"
41
+ "Model: IndoBERT + EVA-CLIP (HF Hub) + XGBoost (xgb.pkl)\n"
42
+ ),
43
+ version="1.0.0",
44
+ )
45
+
46
+ app.add_middleware(
47
+ CORSMiddleware,
48
+ allow_origins=["*"],
49
+ allow_methods=["*"],
50
+ allow_headers=["*"],
51
+ )
52
+
53
+ @app.get("/")
54
+ def root():
55
+ return {
56
+ "status": "OK",
57
+ "message": "Multimodal Water Pollution Risk API is running.",
58
+ "info": "Use POST /predict with text, image, and features.",
59
+ }
60
+
61
+ @app.post("/predict")
62
+ async def predict(
63
+ text: str = Form(...),
64
+ longitude: float = Form(...),
65
+ latitude: float = Form(...),
66
+ location_cluster: int = Form(...),
67
+ hour: int = Form(...),
68
+ dayofweek: int = Form(...),
69
+ month: int = Form(...),
70
+ image: UploadFile = File(...),
71
+ ):
72
+ # 1. preprocess text
73
+ cleaned_text = preprocess_text(text)
74
+
75
+ # 2. encode text (ambil CLS token-nya)
76
+ text_inputs = tokenizer(
77
+ cleaned_text,
78
+ return_tensors="pt",
79
+ padding="max_length",
80
+ truncation=True,
81
+ max_length=128,
82
+ )
83
+ text_inputs = {k: v.to(device) for k, v in text_inputs.items()}
84
+ with torch.no_grad():
85
+ text_emb = text_model(**text_inputs).last_hidden_state[:, 0, :] # take the CLS token only
86
+ text_emb = text_emb.cpu().numpy()
87
+
88
+ # 3. encode image (EVA-CLIP image embedding)
89
+ img_bytes = await image.read()
90
+ pil_img = Image.open(io.BytesIO(img_bytes)).convert("RGB")
91
+ img_tensor = clip_preprocess(pil_img).unsqueeze(0).to(device)
92
+
93
+ with torch.no_grad():
94
+ img_emb = clip_model.encode_image(img_tensor)
95
+ img_emb = img_emb.cpu().numpy()
96
+
97
+ # 4. additional numeric features (longitude, latitude, location_cluster, hour, dayofweek, month)
98
+ add_feats = np.array(
99
+ [[longitude, latitude, location_cluster, hour, dayofweek, month]],
100
+ dtype=np.float32,
101
+ )
102
+
103
+ # 5. concatenate (early fusion): [image_emb, text_emb, add_feats]
104
+ fused = np.concatenate([img_emb, text_emb, add_feats], axis=1)
105
+
106
+ # 6. predict
107
+ proba = xgb_model.predict_proba(fused)[0]
108
+ pred_idx = int(np.argmax(proba))
109
+ label = "KRITIS" if pred_idx == 1 else "WASPADA"
110
+
111
+ return {
112
+ "prediction": label,
113
+ "probabilities": {
114
+ "WASPADA": float(proba[0]),
115
+ "KRITIS": float(proba[1]),
116
+ },
117
+ }
118
+
119
+ if __name__ == "__main__":
120
+ import uvicorn
121
+
122
  uvicorn.run(app, host="0.0.0.0", port=7860)