Files changed (1) hide show
  1. app.py +16 -60
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 # already a flat state dict
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 (downloading wav2vec2 if not cached)...")
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
- # Load audio checkpoint β€” handle all common formats
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 for k, v in audio_state.items() if k.startswith("proj.")}
116
- agru_state = {k[len("agru."):]: v for k, v in audio_state.items() if k.startswith("agru.")}
117
- reg_state = {k[len("regressor."):]: v for k, v in audio_state.items() if k.startswith("regressor.")}
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", theme=gr.themes.Soft()) as demo:
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 (Audio / Visual / Fusion)", open=False):
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
- gr.Markdown("---\n**Traits:** Extraversion Β· Agreeableness Β· Conscientiousness Β· Neuroticism Β· Openness")
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()