ananyakarn commited on
Commit
817abb1
·
verified ·
1 Parent(s): b4653d9

incorporated the usage of csv files

Browse files
Files changed (1) hide show
  1. app.py +164 -104
app.py CHANGED
@@ -1,24 +1,14 @@
1
- import os
2
- import zipfile
3
- import requests
4
- import numpy as np
5
- import pandas as pd
6
- import librosa
7
- import torch
8
- import gradio as gr
9
-
10
- from transformers import AutoTokenizer, AutoModel
11
- from sklearn.ensemble import RandomForestClassifier
12
- from sklearn.preprocessing import StandardScaler
13
- from sklearn.metrics import accuracy_score, f1_score
14
- from sklearn.model_selection import train_test_split
15
-
16
  # =========================
17
- # 1. DOWNLOAD & EXTRACT DATASET
18
  # =========================
 
 
19
  url = "https://huggingface.co/datasets/ananyakarn/DAIC_WOZ_Data/resolve/main/DAIC_WOZ_Data.zip"
20
  zip_path = "data.zip"
21
 
 
 
 
22
  print("Downloading dataset...")
23
  r = requests.get(url, stream=True)
24
  with open(zip_path, "wb") as f:
@@ -27,28 +17,62 @@ with open(zip_path, "wb") as f:
27
 
28
  print("Extracting dataset...")
29
  with zipfile.ZipFile(zip_path, "r") as zip_ref:
30
- zip_ref.extractall(".")
31
 
32
  # =========================
33
- # 2. AUTO-DETECT DATASET FOLDER
34
  # =========================
35
  def find_dataset_root():
36
- for root, dirs, files in os.walk("."):
37
  for d in dirs:
38
  if "_P" in d or "_C" in d:
39
  return root
40
  return None
41
 
42
- extract_path = find_dataset_root()
43
 
44
- if extract_path is None:
45
- raise Exception("Dataset folders not found!")
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- print("Final dataset path:", extract_path)
48
- print("Sample folders:", os.listdir(extract_path)[:10])
 
 
49
 
50
  # =========================
51
- # 2. LOAD LIGHTWEIGHT BERT
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  # =========================
53
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
54
 
@@ -57,127 +81,163 @@ bert = AutoModel.from_pretrained("distilbert-base-uncased").to(device)
57
  bert.eval()
58
 
59
  # =========================
60
- # 3. FEATURE FUNCTIONS
61
  # =========================
 
 
 
 
 
 
 
 
62
  def get_text_embedding(text):
63
- inputs = tokenizer(text, return_tensors="pt", truncation=True, padding=True, max_length=128).to(device)
 
 
 
 
64
  with torch.no_grad():
65
  outputs = bert(**inputs)
66
  return outputs.last_hidden_state.mean(dim=1).squeeze().cpu().numpy()
67
 
68
- def get_audio_features(folder):
69
  try:
70
- file = [f for f in os.listdir(folder) if f.endswith("_AUDIO.wav")][0]
71
- y, sr = librosa.load(os.path.join(folder, file), sr=16000)
72
- mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=20)
73
  return np.mean(mfcc.T, axis=0)
74
  except:
75
- return np.zeros(20)
76
 
77
- def load_text(folder):
78
- try:
79
- file = [f for f in os.listdir(folder) if "TRANSCRIPT" in f][0]
80
- df = pd.read_csv(os.path.join(folder, file))
81
- return " ".join(df.iloc[:, -1].astype(str).tolist())
82
- except:
83
- return ""
84
 
85
- def get_label(folder):
86
- return 1 if "_P" in folder else 0
 
 
87
 
88
- # =========================
89
- # 4. BUILD BALANCED DATASET
90
- # =========================
91
- print("Building balanced dataset...")
92
 
93
- all_folders = [f for f in os.listdir(extract_path) if os.path.isdir(os.path.join(extract_path, f))]
 
 
 
94
 
95
- p_folders = [f for f in all_folders if "_P" in f]
96
- c_folders = [f for f in all_folders if "_C" in f]
97
 
98
- # 🔥 pick equal samples
99
- num_samples = min(10, len(p_folders), len(c_folders))
 
 
100
 
101
- p_folders = p_folders[:num_samples]
102
- c_folders = c_folders[:num_samples]
 
 
103
 
104
- folders = p_folders + c_folders
 
105
 
106
- print(f"Using {len(p_folders)} depressed and {len(c_folders)} control samples")
 
107
 
108
- # =========================
109
- # 5. FEATURE EXTRACTION
110
- # =========================
111
- X, y = [], []
112
 
113
- for folder in folders:
114
- path = os.path.join(extract_path, folder)
115
 
116
- text = load_text(path)
117
  text_feat = get_text_embedding(text)
118
- audio_feat = get_audio_features(path)
 
119
 
120
- combined = np.concatenate([text_feat, audio_feat])
 
121
 
122
- X.append(combined)
123
- y.append(get_label(folder))
124
 
125
- X = np.array(X)
126
- y = np.array(y)
127
 
128
  # =========================
129
- # 6. TRAIN-TEST SPLIT
130
  # =========================
131
- X_train, X_test, y_train, y_test = train_test_split(
132
- X, y, test_size=0.3, random_state=42, stratify=y
133
- )
134
 
135
  scaler = StandardScaler()
136
- X_train = scaler.fit_transform(X_train)
137
- X_test = scaler.transform(X_test)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
- # =========================
140
- # 7. TRAIN MODEL
141
- # =========================
142
- print("Training model...")
143
 
144
- model = RandomForestClassifier(n_estimators=50, random_state=42)
145
- model.fit(X_train, y_train)
146
 
147
- print("Model ready!")
 
 
 
 
 
 
 
148
 
149
  # =========================
150
- # 8. RUN EVALUATION
151
  # =========================
152
- def run_on_dataset():
153
 
154
- preds = model.predict(X_test)
 
155
 
156
- acc = accuracy_score(y_test, preds)
157
- f1 = f1_score(y_test, preds)
 
158
 
159
- results = []
160
- for i, pred in enumerate(preds):
161
- label = y_test[i]
162
- results.append(
163
- f"Sample {i+1} → Pred: {'Depressed' if pred else 'Control'} | True: {'Depressed' if label else 'Control'}"
164
- )
165
 
166
- output = "\n".join(results)
167
- output += f"\n\nAccuracy: {acc:.3f}"
168
- output += f"\nF1 Score: {f1:.3f}"
169
 
170
- return output
171
 
172
  # =========================
173
- # 9. GRADIO UI
174
  # =========================
175
- app = gr.Interface(
176
- fn=run_on_dataset,
177
- inputs=[],
178
- outputs="text",
179
- title="Multimodal Depression Detection",
180
- description="Balanced dataset training + evaluation (text + audio)"
181
- )
 
 
182
 
183
- app.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # =========================
2
+ # 1. DOWNLOAD DATASET
3
  # =========================
4
+ import os, requests, zipfile
5
+
6
  url = "https://huggingface.co/datasets/ananyakarn/DAIC_WOZ_Data/resolve/main/DAIC_WOZ_Data.zip"
7
  zip_path = "data.zip"
8
 
9
+ if not os.path.exists("data"):
10
+ os.makedirs("data", exist_ok=True)
11
+
12
  print("Downloading dataset...")
13
  r = requests.get(url, stream=True)
14
  with open(zip_path, "wb") as f:
 
17
 
18
  print("Extracting dataset...")
19
  with zipfile.ZipFile(zip_path, "r") as zip_ref:
20
+ zip_ref.extractall("data")
21
 
22
  # =========================
23
+ # 2. FIND DATASET ROOT
24
  # =========================
25
  def find_dataset_root():
26
+ for root, dirs, files in os.walk("data"):
27
  for d in dirs:
28
  if "_P" in d or "_C" in d:
29
  return root
30
  return None
31
 
32
+ DATA_PATH = find_dataset_root()
33
 
34
+ if DATA_PATH is None:
35
+ raise Exception("Dataset not found!")
36
+
37
+ print("Dataset path:", DATA_PATH)
38
+
39
+ # =========================
40
+ # 3. IMPORT LIBRARIES
41
+ # =========================
42
+ import numpy as np
43
+ import pandas as pd
44
+ import librosa
45
+ import torch
46
+ import torch.nn as nn
47
 
48
+ from tqdm import tqdm
49
+ from transformers import AutoTokenizer, AutoModel
50
+ from sklearn.metrics import accuracy_score, f1_score
51
+ from sklearn.preprocessing import StandardScaler
52
 
53
  # =========================
54
+ # 4. LOAD LABELS (AVEC CSV)
55
+ # =========================
56
+ def load_labels():
57
+ df_train = pd.read_csv("train_split_Depression_AVEC2017.csv")
58
+ df_dev = pd.read_csv("dev_split_Depression_AVEC2017.csv")
59
+ df_test = pd.read_csv("test_split_Depression_AVEC2017.csv")
60
+
61
+ df = pd.concat([df_train, df_dev, df_test])
62
+
63
+ labels = {}
64
+ for _, row in df.iterrows():
65
+ pid = str(int(row["Participant_ID"]))
66
+ phq = row["PHQ8_Score"]
67
+ label = 1 if phq >= 10 else 0
68
+ labels[pid] = label
69
+
70
+ return labels
71
+
72
+ labels_dict = load_labels()
73
+
74
+ # =========================
75
+ # 5. LOAD TEXT MODEL
76
  # =========================
77
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
78
 
 
81
  bert.eval()
82
 
83
  # =========================
84
+ # 6. FEATURE FUNCTIONS
85
  # =========================
86
+ def load_text(folder_path):
87
+ try:
88
+ file = [f for f in os.listdir(folder_path) if "TRANSCRIPT" in f][0]
89
+ df = pd.read_csv(os.path.join(folder_path, file))
90
+ return " ".join(df.iloc[:, -1].astype(str).tolist())
91
+ except:
92
+ return ""
93
+
94
  def get_text_embedding(text):
95
+ if text == "":
96
+ return np.zeros(768)
97
+
98
+ inputs = tokenizer(text, return_tensors="pt",
99
+ truncation=True, padding=True, max_length=256).to(device)
100
  with torch.no_grad():
101
  outputs = bert(**inputs)
102
  return outputs.last_hidden_state.mean(dim=1).squeeze().cpu().numpy()
103
 
104
+ def get_audio_features(folder_path):
105
  try:
106
+ file = [f for f in os.listdir(folder_path) if f.endswith("_AUDIO.wav")][0]
107
+ y, sr = librosa.load(os.path.join(folder_path, file), sr=16000)
108
+ mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=40)
109
  return np.mean(mfcc.T, axis=0)
110
  except:
111
+ return np.zeros(40)
112
 
113
+ def get_visual_features(folder_path):
114
+ features = []
 
 
 
 
 
115
 
116
+ for key in ["AUs", "pose", "gaze"]:
117
+ try:
118
+ file = [f for f in os.listdir(folder_path) if key in f][0]
119
+ df = pd.read_csv(os.path.join(folder_path, file))
120
 
121
+ cols = [c for c in df.columns if any(k in c.lower() for k in ["au", "pose", "gaze"])]
122
+ df = df[cols]
 
 
123
 
124
+ feat = np.concatenate([df.mean().values, df.std().values])
125
+ features.append(feat)
126
+ except:
127
+ features.append(np.zeros(20))
128
 
129
+ return np.concatenate(features)
 
130
 
131
+ # =========================
132
+ # 7. BUILD DATASET (ONLY VALID LABELS)
133
+ # =========================
134
+ print("\nBuilding dataset...")
135
 
136
+ all_folders = [
137
+ f for f in os.listdir(DATA_PATH)
138
+ if os.path.isdir(os.path.join(DATA_PATH, f))
139
+ ]
140
 
141
+ data = []
142
+ used_folders = []
143
 
144
+ for folder in tqdm(all_folders):
145
+ pid = folder.split("_")[0]
146
 
147
+ if pid not in labels_dict:
148
+ continue
 
 
149
 
150
+ folder_path = os.path.join(DATA_PATH, folder)
 
151
 
152
+ text = load_text(folder_path)
153
  text_feat = get_text_embedding(text)
154
+ audio_feat = get_audio_features(folder_path)
155
+ visual_feat = get_visual_features(folder_path)
156
 
157
+ combined = np.concatenate([text_feat, audio_feat, visual_feat])
158
+ label = labels_dict[pid]
159
 
160
+ data.append((combined, label))
161
+ used_folders.append(folder)
162
 
163
+ print("Total valid samples:", len(data))
 
164
 
165
  # =========================
166
+ # 8. PREPARE DATA
167
  # =========================
168
+ X = np.array([d[0] for d in data])
169
+ y = np.array([d[1] for d in data])
 
170
 
171
  scaler = StandardScaler()
172
+ X = scaler.fit_transform(X)
173
+
174
+ # Split using AVEC logic (simple split for now)
175
+ split = int(0.7 * len(X))
176
+ X_train, X_test = X[:split], X[split:]
177
+ y_train, y_test = y[:split], y[split:]
178
+
179
+ # =========================
180
+ # 9. MODEL
181
+ # =========================
182
+ class MultiModalNN(nn.Module):
183
+ def __init__(self, input_dim):
184
+ super().__init__()
185
+ self.net = nn.Sequential(
186
+ nn.Linear(input_dim, 256),
187
+ nn.ReLU(),
188
+ nn.Dropout(0.3),
189
+ nn.Linear(256, 64),
190
+ nn.ReLU(),
191
+ nn.Linear(64, 1)
192
+ )
193
 
194
+ def forward(self, x):
195
+ return self.net(x)
 
 
196
 
197
+ model = MultiModalNN(X.shape[1]).to(device)
 
198
 
199
+ optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)
200
+ criterion = nn.BCEWithLogitsLoss()
201
+
202
+ X_train_t = torch.tensor(X_train, dtype=torch.float32).to(device)
203
+ y_train_t = torch.tensor(y_train, dtype=torch.float32).to(device)
204
+
205
+ X_test_t = torch.tensor(X_test, dtype=torch.float32).to(device)
206
+ y_test_t = torch.tensor(y_test, dtype=torch.float32).to(device)
207
 
208
  # =========================
209
+ # 10. TRAIN
210
  # =========================
211
+ print("\nTraining model...")
212
 
213
+ for epoch in range(10):
214
+ model.train()
215
 
216
+ optimizer.zero_grad()
217
+ outputs = model(X_train_t).squeeze()
218
+ loss = criterion(outputs, y_train_t)
219
 
220
+ loss.backward()
221
+ optimizer.step()
 
 
 
 
222
 
223
+ print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")
 
 
224
 
225
+ print("Model ready!")
226
 
227
  # =========================
228
+ # 11. EVALUATION
229
  # =========================
230
+ model.eval()
231
+
232
+ with torch.no_grad():
233
+ outputs = model(X_test_t).squeeze()
234
+ preds = (torch.sigmoid(outputs) > 0.5).int().cpu().numpy()
235
+
236
+ print("\n========== RESULTS ==========")
237
+ print("Accuracy:", accuracy_score(y_test, preds))
238
+ print("F1 Score:", f1_score(y_test, preds))
239
 
240
+ print("\nSample Predictions:")
241
+ for i in range(min(10, len(preds))):
242
+ label = "Depressed" if preds[i] == 1 else "Control"
243
+ print(f"{used_folders[split+i]} → {label}")