Update app.py
#3
by Dareen-Housam - opened
app.py
CHANGED
|
@@ -4,9 +4,6 @@ import torch.nn as nn
|
|
| 4 |
import sys
|
| 5 |
import os
|
| 6 |
|
| 7 |
-
from fastapi import FastAPI
|
| 8 |
-
from fastapi.middleware.cors import CORSMiddleware
|
| 9 |
-
|
| 10 |
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
| 11 |
|
| 12 |
from models.audio_model import FrozenExtractor, LinearProjection, AGRU, PersonalityRegressor
|
|
@@ -36,17 +33,14 @@ REG_HEADS = 8
|
|
| 36 |
REG_DROPOUT = 0.1
|
| 37 |
PROJ_DROPOUT = 0.1
|
| 38 |
|
| 39 |
-
|
| 40 |
# ββ Checkpoint loader helper βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 41 |
def _unwrap_ckpt(ckpt):
|
| 42 |
-
"""Extract state dict from common checkpoint wrapper formats."""
|
| 43 |
if not isinstance(ckpt, dict):
|
| 44 |
return ckpt
|
| 45 |
for key in ("model_state", "model_state_dict", "state_dict", "model"):
|
| 46 |
if key in ckpt:
|
| 47 |
return ckpt[key]
|
| 48 |
-
return ckpt
|
| 49 |
-
|
| 50 |
|
| 51 |
def _load_ckpt(model, path):
|
| 52 |
ckpt = torch.load(path, map_location=DEVICE, weights_only=False)
|
|
@@ -54,7 +48,6 @@ def _load_ckpt(model, path):
|
|
| 54 |
model.load_state_dict(state)
|
| 55 |
return model
|
| 56 |
|
| 57 |
-
|
| 58 |
# ββ Visual model βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 59 |
print("β³ Loading visual model...")
|
| 60 |
visual_model = VisualPersonalityModel(VISUAL_CFG).to(DEVICE)
|
|
@@ -62,7 +55,6 @@ visual_model = _load_ckpt(visual_model, "checkpoints/best_model.pt")
|
|
| 62 |
visual_model.eval()
|
| 63 |
print("β Visual model ready")
|
| 64 |
|
| 65 |
-
|
| 66 |
# ββ Fusion model βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 67 |
print("β³ Loading fusion model...")
|
| 68 |
fusion_model = FusionMLP(
|
|
@@ -72,10 +64,8 @@ fusion_model = _load_ckpt(fusion_model, "checkpoints/best_fusion_model.pt")
|
|
| 72 |
fusion_model.eval()
|
| 73 |
print("β Fusion model ready")
|
| 74 |
|
| 75 |
-
|
| 76 |
# ββ Audio model ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 77 |
-
print("β³ Loading audio model
|
| 78 |
-
|
| 79 |
frozen_ext = FrozenExtractor("facebook/wav2vec2-base", n_shallow=N_SHALLOW).to(DEVICE)
|
| 80 |
frozen_ext.eval()
|
| 81 |
|
|
@@ -95,46 +85,35 @@ regressor = PersonalityRegressor(
|
|
| 95 |
dropout=REG_DROPOUT,
|
| 96 |
).to(DEVICE)
|
| 97 |
|
| 98 |
-
|
| 99 |
-
audio_ckpt = torch.load(
|
| 100 |
-
"checkpoints/best_audio_model.pt", map_location=DEVICE, weights_only=False
|
| 101 |
-
)
|
| 102 |
audio_state = _unwrap_ckpt(audio_ckpt)
|
| 103 |
|
| 104 |
-
print(f"Audio checkpoint keys: {list(audio_state.keys())[:8]}")
|
| 105 |
-
|
| 106 |
if "deep_transformer" in audio_state:
|
| 107 |
-
# Saved as separate component dicts
|
| 108 |
deep_transformer.load_state_dict(audio_state["deep_transformer"])
|
| 109 |
proj.load_state_dict(audio_state["proj"])
|
| 110 |
agru.load_state_dict(audio_state["agru"])
|
| 111 |
regressor.load_state_dict(audio_state["regressor"])
|
| 112 |
else:
|
| 113 |
-
# Single flat state dict β split by prefix
|
| 114 |
dt_state = {k[len("deep_transformer."):]: v for k, v in audio_state.items() if k.startswith("deep_transformer.")}
|
| 115 |
-
proj_state = {k[len("proj."):]: v
|
| 116 |
-
agru_state = {k[len("agru."):]: v
|
| 117 |
-
reg_state = {k[len("regressor."):]: v
|
| 118 |
-
|
| 119 |
if dt_state:
|
| 120 |
deep_transformer.load_state_dict(dt_state)
|
| 121 |
proj.load_state_dict(proj_state)
|
| 122 |
agru.load_state_dict(agru_state)
|
| 123 |
regressor.load_state_dict(reg_state)
|
| 124 |
-
else:
|
| 125 |
-
print(f"β οΈ Could not match audio keys. All keys: {list(audio_state.keys())}")
|
| 126 |
|
| 127 |
for m in [deep_transformer, proj, agru, regressor]:
|
| 128 |
m.eval()
|
| 129 |
print("β Audio model ready")
|
| 130 |
|
| 131 |
-
|
| 132 |
# ββ Face detector ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 133 |
print("β³ Loading face detector...")
|
| 134 |
face_detector = _load_face_detector()
|
| 135 |
print("β Face detector ready")
|
| 136 |
|
| 137 |
-
|
| 138 |
# ββ Inference ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 139 |
def predict(video_path):
|
| 140 |
if video_path is None:
|
|
@@ -144,22 +123,15 @@ def predict(video_path):
|
|
| 144 |
with open(video_path, "rb") as f:
|
| 145 |
video_bytes = f.read()
|
| 146 |
|
| 147 |
-
visual_preds, visual_emb = get_visual_embedding(
|
| 148 |
-
video_bytes, visual_model, DEVICE, face_detector
|
| 149 |
-
)
|
| 150 |
-
|
| 151 |
audio_preds, audio_emb = get_audio_embedding(
|
| 152 |
-
video_bytes, frozen_ext, deep_transformer,
|
| 153 |
-
proj, agru, regressor, DEVICE
|
| 154 |
-
)
|
| 155 |
-
|
| 156 |
-
fusion_preds = get_fusion_prediction(
|
| 157 |
-
audio_emb, visual_emb, fusion_model, DEVICE
|
| 158 |
)
|
|
|
|
| 159 |
|
| 160 |
results = format_predictions(fusion_preds, visual_preds, audio_preds)
|
| 161 |
-
|
| 162 |
fusion = results["fusion"]
|
|
|
|
| 163 |
md = "## π§ Fusion Results (Main)\n\n"
|
| 164 |
for trait, score in fusion.items():
|
| 165 |
bar = "β" * int(score / 5) + "β" * (20 - int(score / 5))
|
|
@@ -171,14 +143,10 @@ def predict(video_path):
|
|
| 171 |
import traceback
|
| 172 |
return None, f"β Error: {str(e)}\n\n```{traceback.format_exc()}```"
|
| 173 |
|
| 174 |
-
|
| 175 |
# ββ Gradio UI ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 176 |
-
with gr.Blocks(title="Personality Prediction API"
|
| 177 |
|
| 178 |
-
gr.Markdown(""
|
| 179 |
-
# π§ Personality Prediction
|
| 180 |
-
Upload a video to predict **OCEAN** personality traits using audio + visual fusion.
|
| 181 |
-
""")
|
| 182 |
|
| 183 |
with gr.Row():
|
| 184 |
with gr.Column(scale=1):
|
|
@@ -188,26 +156,14 @@ with gr.Blocks(title="Personality Prediction API", theme=gr.themes.Soft()) as de
|
|
| 188 |
with gr.Column(scale=1):
|
| 189 |
summary_out = gr.Markdown(label="Results Summary")
|
| 190 |
|
| 191 |
-
with gr.Accordion("π Full Results
|
| 192 |
json_out = gr.JSON(label="Raw Scores (%)")
|
| 193 |
|
| 194 |
predict_btn.click(
|
| 195 |
fn=predict,
|
| 196 |
inputs=video_input,
|
| 197 |
outputs=[json_out, summary_out],
|
| 198 |
-
api_name="predict"
|
| 199 |
)
|
| 200 |
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
app = FastAPI()
|
| 204 |
-
|
| 205 |
-
app.add_middleware(
|
| 206 |
-
CORSMiddleware,
|
| 207 |
-
allow_origins=["*"],
|
| 208 |
-
allow_credentials=True,
|
| 209 |
-
allow_methods=["*"],
|
| 210 |
-
allow_headers=["*"],
|
| 211 |
-
)
|
| 212 |
-
|
| 213 |
-
app = gr.mount_gradio_app(app, demo, path="/")
|
|
|
|
| 4 |
import sys
|
| 5 |
import os
|
| 6 |
|
|
|
|
|
|
|
|
|
|
| 7 |
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
| 8 |
|
| 9 |
from models.audio_model import FrozenExtractor, LinearProjection, AGRU, PersonalityRegressor
|
|
|
|
| 33 |
REG_DROPOUT = 0.1
|
| 34 |
PROJ_DROPOUT = 0.1
|
| 35 |
|
|
|
|
| 36 |
# ββ Checkpoint loader helper βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 37 |
def _unwrap_ckpt(ckpt):
|
|
|
|
| 38 |
if not isinstance(ckpt, dict):
|
| 39 |
return ckpt
|
| 40 |
for key in ("model_state", "model_state_dict", "state_dict", "model"):
|
| 41 |
if key in ckpt:
|
| 42 |
return ckpt[key]
|
| 43 |
+
return ckpt
|
|
|
|
| 44 |
|
| 45 |
def _load_ckpt(model, path):
|
| 46 |
ckpt = torch.load(path, map_location=DEVICE, weights_only=False)
|
|
|
|
| 48 |
model.load_state_dict(state)
|
| 49 |
return model
|
| 50 |
|
|
|
|
| 51 |
# ββ Visual model βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 52 |
print("β³ Loading visual model...")
|
| 53 |
visual_model = VisualPersonalityModel(VISUAL_CFG).to(DEVICE)
|
|
|
|
| 55 |
visual_model.eval()
|
| 56 |
print("β Visual model ready")
|
| 57 |
|
|
|
|
| 58 |
# ββ Fusion model βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 59 |
print("β³ Loading fusion model...")
|
| 60 |
fusion_model = FusionMLP(
|
|
|
|
| 64 |
fusion_model.eval()
|
| 65 |
print("β Fusion model ready")
|
| 66 |
|
|
|
|
| 67 |
# ββ Audio model ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 68 |
+
print("β³ Loading audio model...")
|
|
|
|
| 69 |
frozen_ext = FrozenExtractor("facebook/wav2vec2-base", n_shallow=N_SHALLOW).to(DEVICE)
|
| 70 |
frozen_ext.eval()
|
| 71 |
|
|
|
|
| 85 |
dropout=REG_DROPOUT,
|
| 86 |
).to(DEVICE)
|
| 87 |
|
| 88 |
+
audio_ckpt = torch.load("checkpoints/best_audio_model.pt", map_location=DEVICE, weights_only=False)
|
|
|
|
|
|
|
|
|
|
| 89 |
audio_state = _unwrap_ckpt(audio_ckpt)
|
| 90 |
|
|
|
|
|
|
|
| 91 |
if "deep_transformer" in audio_state:
|
|
|
|
| 92 |
deep_transformer.load_state_dict(audio_state["deep_transformer"])
|
| 93 |
proj.load_state_dict(audio_state["proj"])
|
| 94 |
agru.load_state_dict(audio_state["agru"])
|
| 95 |
regressor.load_state_dict(audio_state["regressor"])
|
| 96 |
else:
|
|
|
|
| 97 |
dt_state = {k[len("deep_transformer."):]: v for k, v in audio_state.items() if k.startswith("deep_transformer.")}
|
| 98 |
+
proj_state = {k[len("proj."):]: v for k, v in audio_state.items() if k.startswith("proj.")}
|
| 99 |
+
agru_state = {k[len("agru."):]: v for k, v in audio_state.items() if k.startswith("agru.")}
|
| 100 |
+
reg_state = {k[len("regressor."):]: v for k, v in audio_state.items() if k.startswith("regressor.")}
|
| 101 |
+
|
| 102 |
if dt_state:
|
| 103 |
deep_transformer.load_state_dict(dt_state)
|
| 104 |
proj.load_state_dict(proj_state)
|
| 105 |
agru.load_state_dict(agru_state)
|
| 106 |
regressor.load_state_dict(reg_state)
|
|
|
|
|
|
|
| 107 |
|
| 108 |
for m in [deep_transformer, proj, agru, regressor]:
|
| 109 |
m.eval()
|
| 110 |
print("β Audio model ready")
|
| 111 |
|
|
|
|
| 112 |
# ββ Face detector ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 113 |
print("β³ Loading face detector...")
|
| 114 |
face_detector = _load_face_detector()
|
| 115 |
print("β Face detector ready")
|
| 116 |
|
|
|
|
| 117 |
# ββ Inference ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 118 |
def predict(video_path):
|
| 119 |
if video_path is None:
|
|
|
|
| 123 |
with open(video_path, "rb") as f:
|
| 124 |
video_bytes = f.read()
|
| 125 |
|
| 126 |
+
visual_preds, visual_emb = get_visual_embedding(video_bytes, visual_model, DEVICE, face_detector)
|
|
|
|
|
|
|
|
|
|
| 127 |
audio_preds, audio_emb = get_audio_embedding(
|
| 128 |
+
video_bytes, frozen_ext, deep_transformer, proj, agru, regressor, DEVICE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
)
|
| 130 |
+
fusion_preds = get_fusion_prediction(audio_emb, visual_emb, fusion_model, DEVICE)
|
| 131 |
|
| 132 |
results = format_predictions(fusion_preds, visual_preds, audio_preds)
|
|
|
|
| 133 |
fusion = results["fusion"]
|
| 134 |
+
|
| 135 |
md = "## π§ Fusion Results (Main)\n\n"
|
| 136 |
for trait, score in fusion.items():
|
| 137 |
bar = "β" * int(score / 5) + "β" * (20 - int(score / 5))
|
|
|
|
| 143 |
import traceback
|
| 144 |
return None, f"β Error: {str(e)}\n\n```{traceback.format_exc()}```"
|
| 145 |
|
|
|
|
| 146 |
# ββ Gradio UI ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 147 |
+
with gr.Blocks(title="Personality Prediction API") as demo:
|
| 148 |
|
| 149 |
+
gr.Markdown("# π§ Personality Prediction\nUpload a video to predict **OCEAN** personality traits.")
|
|
|
|
|
|
|
|
|
|
| 150 |
|
| 151 |
with gr.Row():
|
| 152 |
with gr.Column(scale=1):
|
|
|
|
| 156 |
with gr.Column(scale=1):
|
| 157 |
summary_out = gr.Markdown(label="Results Summary")
|
| 158 |
|
| 159 |
+
with gr.Accordion("π Full Results", open=False):
|
| 160 |
json_out = gr.JSON(label="Raw Scores (%)")
|
| 161 |
|
| 162 |
predict_btn.click(
|
| 163 |
fn=predict,
|
| 164 |
inputs=video_input,
|
| 165 |
outputs=[json_out, summary_out],
|
| 166 |
+
api_name="predict" # This creates the API endpoint
|
| 167 |
)
|
| 168 |
|
| 169 |
+
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|