nguyennp86 commited on
Commit
33c5da7
Β·
verified Β·
1 Parent(s): 2bd4b9a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +28 -1080
app.py CHANGED
@@ -1,845 +1,15 @@
1
  """
2
- Speech Emotion Recognition - Gradio Application with 3 Tabs
3
- Tab 1: Feature Extraction (Upload audio files only)
4
- Tab 2: Model Training with GA Optimization (Parallel + Early Stopping + Logging)
5
- Tab 3: Emotion Prediction
6
  """
7
 
8
  import gradio as gr
9
- import numpy as np
10
- import pandas as pd
11
- import os
12
- import json
13
- import pickle
14
- from pathlib import Path
15
- import random
16
- import time
17
- from collections import deque
18
 
19
- # For parallel processing
20
- from joblib import Parallel, delayed
21
 
22
- # ============================================================================
23
- # TAB 1: FEATURE EXTRACTION (DUMMY DATA REMOVED)
24
- # ============================================================================
25
-
26
- def extract_dataset_features(uploaded_files, progress=gr.Progress()):
27
- """Extract features from uploaded dataset - NO DUMMY DATA"""
28
-
29
- if uploaded_files is None or len(uploaded_files) == 0:
30
- return """
31
- ## ⚠️ No Files Uploaded
32
-
33
- Please upload audio files (.wav format) to proceed.
34
-
35
- **Tips:**
36
- - Upload multiple files at once
37
- - RAVDESS format: `03-01-06-01-02-01-12.wav`
38
- - Or any `.wav` files with emotion labels in filename
39
- """, None, None
40
-
41
- try:
42
- from src.feature_extraction import extract_features
43
-
44
- progress(0, desc="Starting feature extraction...")
45
-
46
- data_list = []
47
- total_files = len(uploaded_files)
48
- failed_files = []
49
-
50
- for idx, audio_file in enumerate(uploaded_files):
51
- progress((idx + 1) / total_files, desc=f"Processing {idx + 1}/{total_files}: {Path(audio_file).name}")
52
-
53
- try:
54
- features, _, _ = extract_features(audio_file)
55
- filename = Path(audio_file).name
56
- emotion = extract_emotion_from_filename(filename)
57
-
58
- row = {
59
- 'file_path': audio_file,
60
- 'filename': filename,
61
- 'actor': extract_actor_from_filename(filename),
62
- 'emotion': emotion
63
- }
64
-
65
- for i, feat in enumerate(features):
66
- row[f'feature_{i}'] = feat
67
-
68
- data_list.append(row)
69
-
70
- except Exception as e:
71
- failed_files.append((audio_file, str(e)))
72
- print(f"Error processing {audio_file}: {e}")
73
- continue
74
-
75
- if len(data_list) == 0:
76
- return "❌ No features extracted. Check audio files and error logs.", None, None
77
-
78
- df = pd.DataFrame(data_list)
79
- df.to_csv('features_ravdess.csv', index=False)
80
-
81
- # Create summary
82
- summary = f"""
83
- ## βœ… Feature Extraction Complete!
84
-
85
- ### Statistics:
86
- - **Total Files Processed**: {len(df)}
87
- - **Features Extracted**: 162 per file
88
- - **Emotions Detected**: {', '.join(sorted(df['emotion'].unique()))}
89
- - **Output File**: `features_ravdess.csv`
90
-
91
- ### Emotion Distribution:
92
- {df['emotion'].value_counts().to_string()}
93
-
94
- ### Failed Files: {len(failed_files)}
95
- """
96
-
97
- if failed_files:
98
- summary += "\n\n**Failed files:**\n"
99
- for fname, error in failed_files[:10]: # Show first 10
100
- summary += f"- {Path(fname).name}: {error}\n"
101
-
102
- summary += "\nβœ… **Dataset ready for training!**"
103
-
104
- emotion_dist = df['emotion'].value_counts().to_dict()
105
-
106
- return summary, df.head(20), emotion_dist
107
-
108
- except Exception as e:
109
- return f"❌ Error: {str(e)}", None, None
110
-
111
-
112
- def extract_emotion_from_filename(filename):
113
- """Extract emotion from RAVDESS-style filename"""
114
- try:
115
- parts = filename.split('-')
116
- if len(parts) >= 3:
117
- emotion_code = int(parts[2])
118
- emotion_map = {
119
- 1: 'neutral', 2: 'calm', 3: 'happy', 4: 'sad',
120
- 5: 'angry', 6: 'fearful', 7: 'disgust', 8: 'surprised'
121
- }
122
- return emotion_map.get(emotion_code, 'unknown')
123
- except:
124
- pass
125
-
126
- # Fallback: Check filename for emotion keywords
127
- filename_lower = filename.lower()
128
- emotions = ['angry', 'calm', 'disgust', 'fearful', 'happy', 'neutral', 'sad', 'surprised']
129
- for emotion in emotions:
130
- if emotion in filename_lower:
131
- return emotion
132
-
133
- return 'unknown'
134
-
135
-
136
- def extract_actor_from_filename(filename):
137
- """Extract actor ID from filename"""
138
- try:
139
- parts = filename.split('-')
140
- if len(parts) >= 7:
141
- actor_id = int(parts[6].split('.')[0])
142
- return f'Actor_{actor_id:02d}'
143
- except:
144
- pass
145
- return 'Unknown'
146
-
147
-
148
- def preview_single_audio(audio_file):
149
- """Preview features from a single audio file"""
150
- if audio_file is None:
151
- return "Please upload an audio file", None, None
152
-
153
- try:
154
- from src.feature_extraction import extract_features
155
- from src.utils import create_waveform_plot, create_spectrogram_plot
156
-
157
- features, y, sr = extract_features(audio_file)
158
-
159
- summary = f"""
160
- ## πŸ” Single File Feature Preview
161
-
162
- - **Features Extracted**: 162
163
- - **Sample Rate**: {sr} Hz
164
- - **Duration**: {len(y)/sr:.2f} seconds
165
- - **Detected Emotion**: {extract_emotion_from_filename(Path(audio_file).name)}
166
- """
167
-
168
- waveform = create_waveform_plot(y, sr)
169
- spectrogram = create_spectrogram_plot(y, sr)
170
-
171
- return summary, waveform, spectrogram
172
-
173
- except Exception as e:
174
- return f"Error: {str(e)}", None, None
175
-
176
-
177
- # ============================================================================
178
- # TAB 2: GENETIC ALGORITHM CLASS (WITH PARALLEL + EARLY STOPPING + LOGGING)
179
- # ============================================================================
180
-
181
- class GeneticAlgorithm:
182
- """GA for optimizing features + hyperparameters + ensemble weights"""
183
-
184
- def __init__(self, X, y, n_features_to_select=80):
185
- self.X = X
186
- self.y = y
187
- self.n_features = X.shape[1]
188
- self.n_select = n_features_to_select
189
- self.n_classes = len(np.unique(y))
190
-
191
- self.population_size = 15
192
- self.n_generations = 20
193
- self.mutation_rate = 0.15
194
- self.crossover_rate = 0.8
195
- self.elite_size = 2
196
-
197
- # Early stopping
198
- self.early_stopping_patience = 5
199
- self.early_stopping_tolerance = 0.0001
200
-
201
- self.best_chromosome = None
202
- self.best_fitness = 0
203
- self.history = []
204
-
205
- # Logging
206
- self.log_messages = []
207
-
208
- def log(self, message):
209
- """Add log message"""
210
- timestamp = time.strftime("%H:%M:%S")
211
- log_entry = f"[{timestamp}] {message}"
212
- self.log_messages.append(log_entry)
213
- print(log_entry)
214
-
215
- def create_chromosome(self):
216
- """Create random chromosome"""
217
- chromosome = {
218
- 'feature_indices': np.sort(np.random.choice(
219
- self.n_features, self.n_select, replace=False
220
- )),
221
- 'xgb_n_estimators': random.choice([50, 100, 150]),
222
- 'xgb_max_depth': random.choice([3, 4, 5, 6]),
223
- 'xgb_learning_rate': random.choice([0.05, 0.1, 0.15]),
224
- 'lgbm_n_estimators': random.choice([50, 100, 150]),
225
- 'lgbm_num_leaves': random.choice([20, 31, 40]),
226
- 'lgbm_learning_rate': random.choice([0.05, 0.1, 0.15]),
227
- 'gb_n_estimators': random.choice([50, 100, 150]),
228
- 'gb_max_depth': random.choice([3, 4, 5]),
229
- 'gb_learning_rate': random.choice([0.05, 0.1, 0.15]),
230
- 'ada_n_estimators': random.choice([50, 100, 150]),
231
- 'ada_learning_rate': random.choice([0.5, 1.0, 1.5]),
232
- 'weights': self._random_weights(4)
233
- }
234
- return chromosome
235
-
236
- def _random_weights(self, n):
237
- """Generate n random weights that sum to 1"""
238
- w = np.random.dirichlet(np.ones(n))
239
- return w
240
-
241
- def fitness(self, chromosome, X_train, y_train, X_val, y_val):
242
- """Calculate fitness using validation accuracy"""
243
- try:
244
- from xgboost import XGBClassifier
245
- from lightgbm import LGBMClassifier
246
- from sklearn.ensemble import GradientBoostingClassifier, AdaBoostClassifier
247
- from sklearn.metrics import accuracy_score
248
-
249
- feature_indices = chromosome['feature_indices']
250
- X_train_selected = X_train[:, feature_indices]
251
- X_val_selected = X_val[:, feature_indices]
252
-
253
- models = []
254
-
255
- xgb = XGBClassifier(
256
- n_estimators=chromosome['xgb_n_estimators'],
257
- max_depth=chromosome['xgb_max_depth'],
258
- learning_rate=chromosome['xgb_learning_rate'],
259
- objective='multi:softprob',
260
- num_class=self.n_classes,
261
- random_state=42,
262
- n_jobs=-1,
263
- verbosity=0
264
- )
265
- xgb.fit(X_train_selected, y_train)
266
- models.append(xgb)
267
-
268
- lgbm = LGBMClassifier(
269
- n_estimators=chromosome['lgbm_n_estimators'],
270
- num_leaves=chromosome['lgbm_num_leaves'],
271
- learning_rate=chromosome['lgbm_learning_rate'],
272
- objective='multiclass',
273
- num_class=self.n_classes,
274
- random_state=42,
275
- n_jobs=-1,
276
- verbose=-1
277
- )
278
- lgbm.fit(X_train_selected, y_train)
279
- models.append(lgbm)
280
-
281
- gb = GradientBoostingClassifier(
282
- n_estimators=chromosome['gb_n_estimators'],
283
- max_depth=chromosome['gb_max_depth'],
284
- learning_rate=chromosome['gb_learning_rate'],
285
- random_state=42
286
- )
287
- gb.fit(X_train_selected, y_train)
288
- models.append(gb)
289
-
290
- ada = AdaBoostClassifier(
291
- n_estimators=chromosome['ada_n_estimators'],
292
- learning_rate=chromosome['ada_learning_rate'],
293
- algorithm='SAMME',
294
- random_state=42
295
- )
296
- ada.fit(X_train_selected, y_train)
297
- models.append(ada)
298
-
299
- predictions = [model.predict_proba(X_val_selected) for model in models]
300
- weights = chromosome['weights']
301
- ensemble_proba = np.average(predictions, axis=0, weights=weights)
302
- y_pred = np.argmax(ensemble_proba, axis=1)
303
-
304
- accuracy = accuracy_score(y_val, y_pred)
305
- return accuracy
306
-
307
- except Exception as e:
308
- print(f"Error in fitness: {e}")
309
- return 0.0
310
-
311
- def crossover(self, parent1, parent2):
312
- """Crossover operation"""
313
- if random.random() > self.crossover_rate:
314
- return parent1.copy(), parent2.copy()
315
-
316
- child1 = {}
317
- child2 = {}
318
-
319
- mask = np.random.rand(self.n_select) < 0.5
320
- child1_features = np.where(mask, parent1['feature_indices'], parent2['feature_indices'])
321
- child2_features = np.where(mask, parent2['feature_indices'], parent1['feature_indices'])
322
-
323
- child1_features = np.unique(child1_features)
324
- child2_features = np.unique(child2_features)
325
-
326
- while len(child1_features) < self.n_select:
327
- new_feat = random.randint(0, self.n_features - 1)
328
- if new_feat not in child1_features:
329
- child1_features = np.append(child1_features, new_feat)
330
-
331
- while len(child2_features) < self.n_select:
332
- new_feat = random.randint(0, self.n_features - 1)
333
- if new_feat not in child2_features:
334
- child2_features = np.append(child2_features, new_feat)
335
-
336
- child1['feature_indices'] = np.sort(child1_features[:self.n_select])
337
- child2['feature_indices'] = np.sort(child2_features[:self.n_select])
338
-
339
- for key in parent1.keys():
340
- if key != 'feature_indices':
341
- if random.random() < 0.5:
342
- child1[key] = parent1[key]
343
- child2[key] = parent2[key]
344
- else:
345
- child1[key] = parent2[key]
346
- child2[key] = parent1[key]
347
-
348
- return child1, child2
349
-
350
- def mutate(self, chromosome):
351
- """Mutation operation"""
352
- mutated = chromosome.copy()
353
-
354
- if random.random() < self.mutation_rate:
355
- n_replace = random.randint(1, 5)
356
- indices_to_replace = np.random.choice(self.n_select, n_replace, replace=False)
357
-
358
- for idx in indices_to_replace:
359
- new_feat = random.randint(0, self.n_features - 1)
360
- while new_feat in mutated['feature_indices']:
361
- new_feat = random.randint(0, self.n_features - 1)
362
- mutated['feature_indices'][idx] = new_feat
363
-
364
- mutated['feature_indices'] = np.sort(mutated['feature_indices'])
365
-
366
- if random.random() < self.mutation_rate:
367
- param_to_mutate = random.choice([
368
- 'xgb_n_estimators', 'xgb_max_depth', 'lgbm_n_estimators',
369
- 'gb_n_estimators', 'ada_n_estimators'
370
- ])
371
- temp = self.create_chromosome()
372
- mutated[param_to_mutate] = temp[param_to_mutate]
373
-
374
- if random.random() < self.mutation_rate:
375
- mutated['weights'] = self._random_weights(4)
376
-
377
- return mutated
378
-
379
- def evaluate_population_parallel(self, population, X_train, y_train, X_val, y_val, n_jobs=2):
380
- """
381
- Evaluate entire population in parallel
382
-
383
- Args:
384
- population: List of chromosomes
385
- n_jobs: Number of parallel jobs (default=2 for free tier)
386
-
387
- Returns:
388
- List of fitness scores
389
- """
390
- self.log(f" Evaluating {len(population)} individuals in parallel (n_jobs={n_jobs})...")
391
-
392
- fitness_scores = Parallel(n_jobs=n_jobs, verbose=0)(
393
- delayed(self.fitness)(chromosome, X_train, y_train, X_val, y_val)
394
- for chromosome in population
395
- )
396
-
397
- return fitness_scores
398
-
399
- def evolve(self, X_train, y_train, X_val, y_val, progress_callback=None, n_jobs=2):
400
- """
401
- Main GA evolution loop with parallel evaluation, early stopping, and logging
402
-
403
- Args:
404
- n_jobs: Number of parallel jobs (2 for free tier, 4+ for better hardware)
405
- """
406
-
407
- self.log("="*70)
408
- self.log("🧬 GENETIC ALGORITHM OPTIMIZATION")
409
- self.log("="*70)
410
- self.log(f"Population size: {self.population_size}")
411
- self.log(f"Generations: {self.n_generations}")
412
- self.log(f"Features to select: {self.n_select}/{self.n_features}")
413
- self.log(f"Early stopping patience: {self.early_stopping_patience}")
414
- self.log(f"Parallel jobs: {n_jobs}")
415
- self.log("="*70)
416
-
417
- population = [self.create_chromosome() for _ in range(self.population_size)]
418
-
419
- start_time = time.time()
420
- no_improve_count = 0
421
-
422
- for generation in range(self.n_generations):
423
- gen_start = time.time()
424
-
425
- self.log(f"\nπŸ“Š Generation {generation + 1}/{self.n_generations}")
426
-
427
- # Parallel fitness evaluation
428
- fitness_scores = self.evaluate_population_parallel(
429
- population, X_train, y_train, X_val, y_val, n_jobs=n_jobs
430
- )
431
-
432
- max_fitness = max(fitness_scores)
433
- avg_fitness = np.mean(fitness_scores)
434
- std_fitness = np.std(fitness_scores)
435
- max_idx = fitness_scores.index(max_fitness)
436
-
437
- # Track improvement
438
- improved = False
439
- if max_fitness > self.best_fitness + self.early_stopping_tolerance:
440
- self.best_fitness = max_fitness
441
- self.best_chromosome = population[max_idx].copy()
442
- no_improve_count = 0
443
- improved = True
444
- self.log(f" ✨ NEW BEST: {max_fitness:.4f} (+{max_fitness - (self.history[-1]['best_fitness'] if self.history else 0):.4f})")
445
- else:
446
- no_improve_count += 1
447
- self.log(f" β†’ Best: {max_fitness:.4f} (no improvement, count={no_improve_count})")
448
-
449
- # Log statistics
450
- self.log(f" Average: {avg_fitness:.4f} (Οƒ={std_fitness:.4f})")
451
- self.log(f" Range: [{min(fitness_scores):.4f}, {max(fitness_scores):.4f}]")
452
-
453
- gen_time = time.time() - gen_start
454
- elapsed = time.time() - start_time
455
- avg_gen_time = elapsed / (generation + 1)
456
- eta = avg_gen_time * (self.n_generations - generation - 1)
457
-
458
- self.log(f" Time: {gen_time:.1f}s | Elapsed: {elapsed/60:.1f}min | ETA: {eta/60:.1f}min")
459
-
460
- self.history.append({
461
- 'generation': generation + 1,
462
- 'best_fitness': max_fitness,
463
- 'avg_fitness': avg_fitness,
464
- 'std_fitness': std_fitness,
465
- 'time': gen_time,
466
- 'improved': improved
467
- })
468
-
469
- # Update progress callback
470
- if progress_callback:
471
- progress_callback(
472
- (generation + 1) / self.n_generations,
473
- desc=f"Gen {generation+1}/{self.n_generations} | Best: {max_fitness:.4f} | Avg: {avg_fitness:.4f} | ETA: {eta/60:.0f}min"
474
- )
475
-
476
- # Early stopping check
477
- if no_improve_count >= self.early_stopping_patience:
478
- self.log(f"\nπŸ›‘ EARLY STOPPING at generation {generation + 1}")
479
- self.log(f" No improvement for {self.early_stopping_patience} consecutive generations")
480
- self.log(f" Best fitness: {self.best_fitness:.4f}")
481
- break
482
-
483
- # Selection (Tournament + Elitism)
484
- selected = []
485
- for _ in range(self.population_size - self.elite_size):
486
- tournament = random.sample(list(zip(population, fitness_scores)), 3)
487
- winner = max(tournament, key=lambda x: x[1])[0]
488
- selected.append(winner)
489
-
490
- elite_indices = np.argsort(fitness_scores)[-self.elite_size:]
491
- elite = [population[i] for i in elite_indices]
492
-
493
- # Crossover & Mutation
494
- offspring = []
495
- for i in range(0, len(selected), 2):
496
- if i + 1 < len(selected):
497
- child1, child2 = self.crossover(selected[i], selected[i+1])
498
- offspring.append(self.mutate(child1))
499
- offspring.append(self.mutate(child2))
500
-
501
- population = elite + offspring[:self.population_size - self.elite_size]
502
-
503
- total_time = time.time() - start_time
504
-
505
- self.log("\n" + "="*70)
506
- self.log("βœ… GA OPTIMIZATION COMPLETE")
507
- self.log("="*70)
508
- self.log(f"Final best fitness: {self.best_fitness:.4f}")
509
- self.log(f"Total generations: {len(self.history)}/{self.n_generations}")
510
- self.log(f"Total time: {total_time/60:.1f} minutes")
511
- self.log(f"Average time per generation: {total_time/len(self.history):.1f}s")
512
- self.log("="*70)
513
-
514
- return self.best_chromosome
515
-
516
-
517
- # ============================================================================
518
- # TAB 2: TRAINING FUNCTION (WITH LOGGING)
519
- # ============================================================================
520
-
521
- def train_with_ga(use_ga, ga_generations, ga_population, n_jobs, progress=gr.Progress()):
522
- """Train models with or without GA optimization"""
523
-
524
- if not os.path.exists('features_ravdess.csv'):
525
- return """
526
- ## ❌ Error: Dataset Not Found
527
-
528
- Please go to **Tab 1: Feature Extraction** first!
529
-
530
- Upload your audio files and extract features before training.
531
- """, None, None, ""
532
-
533
- try:
534
- progress(0, desc="Loading dataset...")
535
-
536
- df = pd.read_csv('features_ravdess.csv')
537
-
538
- from sklearn.model_selection import train_test_split
539
- from sklearn.preprocessing import LabelEncoder, StandardScaler
540
- from xgboost import XGBClassifier
541
- from lightgbm import LGBMClassifier
542
- from sklearn.ensemble import GradientBoostingClassifier, AdaBoostClassifier
543
- from sklearn.metrics import accuracy_score
544
-
545
- feature_cols = [col for col in df.columns if col.startswith('feature_')]
546
- X = df[feature_cols].values
547
- y = df['emotion'].values
548
-
549
- label_encoder = LabelEncoder()
550
- y_encoded = label_encoder.fit_transform(y)
551
-
552
- X_train, X_test, y_train, y_test = train_test_split(
553
- X, y_encoded, test_size=0.2, random_state=42, stratify=y_encoded
554
- )
555
-
556
- progress(0.1, desc="Scaling features...")
557
- scaler = StandardScaler()
558
- X_train_scaled = scaler.fit_transform(X_train)
559
- X_test_scaled = scaler.transform(X_test)
560
-
561
- n_classes = len(label_encoder.classes_)
562
-
563
- training_log = ""
564
-
565
- if use_ga:
566
- progress(0.2, desc="Initializing GA...")
567
-
568
- X_train_ga, X_val_ga, y_train_ga, y_val_ga = train_test_split(
569
- X_train_scaled, y_train, test_size=0.2, random_state=42, stratify=y_train
570
- )
571
-
572
- ga = GeneticAlgorithm(X_train_ga, y_train_ga, n_features_to_select=80)
573
- ga.population_size = ga_population
574
- ga.n_generations = ga_generations
575
-
576
- best_config = ga.evolve(
577
- X_train_ga, y_train_ga, X_val_ga, y_val_ga,
578
- progress_callback=lambda p, desc: progress(0.2 + 0.6*p, desc=desc),
579
- n_jobs=n_jobs
580
- )
581
-
582
- # Collect logs
583
- training_log = "\n".join(ga.log_messages)
584
-
585
- progress(0.8, desc="Training final models with GA config...")
586
-
587
- selected_indices = best_config['feature_indices']
588
- X_train_selected = X_train_scaled[:, selected_indices]
589
- X_test_selected = X_test_scaled[:, selected_indices]
590
-
591
- xgb_model = XGBClassifier(
592
- n_estimators=best_config['xgb_n_estimators'],
593
- max_depth=best_config['xgb_max_depth'],
594
- learning_rate=best_config['xgb_learning_rate'],
595
- objective='multi:softprob',
596
- num_class=n_classes,
597
- random_state=42,
598
- n_jobs=-1,
599
- verbosity=0
600
- )
601
- xgb_model.fit(X_train_selected, y_train)
602
- xgb_acc = xgb_model.score(X_test_selected, y_test)
603
-
604
- lgbm_model = LGBMClassifier(
605
- n_estimators=best_config['lgbm_n_estimators'],
606
- num_leaves=best_config['lgbm_num_leaves'],
607
- learning_rate=best_config['lgbm_learning_rate'],
608
- objective='multiclass',
609
- num_class=n_classes,
610
- random_state=42,
611
- n_jobs=-1,
612
- verbose=-1
613
- )
614
- lgbm_model.fit(X_train_selected, y_train)
615
- lgbm_acc = lgbm_model.score(X_test_selected, y_test)
616
-
617
- gb_model = GradientBoostingClassifier(
618
- n_estimators=best_config['gb_n_estimators'],
619
- max_depth=best_config['gb_max_depth'],
620
- learning_rate=best_config['gb_learning_rate'],
621
- random_state=42
622
- )
623
- gb_model.fit(X_train_selected, y_train)
624
- gb_acc = gb_model.score(X_test_selected, y_test)
625
-
626
- ada_model = AdaBoostClassifier(
627
- n_estimators=best_config['ada_n_estimators'],
628
- learning_rate=best_config['ada_learning_rate'],
629
- algorithm='SAMME',
630
- random_state=42
631
- )
632
- ada_model.fit(X_train_selected, y_train)
633
- ada_acc = ada_model.score(X_test_selected, y_test)
634
-
635
- weights = best_config['weights']
636
-
637
- ga_summary = f"""
638
- ### 🧬 GA Optimization Results:
639
- - **Generations Completed**: {len(ga.history)}/{ga_generations}
640
- - **Population Size**: {ga_population}
641
- - **Best Fitness**: {ga.best_fitness:.4f}
642
- - **Parallel Jobs**: {n_jobs}
643
-
644
- ### 🎯 Best Configuration:
645
- - **XGBoost**: n_est={best_config['xgb_n_estimators']}, depth={best_config['xgb_max_depth']}, lr={best_config['xgb_learning_rate']}
646
- - **LightGBM**: n_est={best_config['lgbm_n_estimators']}, leaves={best_config['lgbm_num_leaves']}, lr={best_config['lgbm_learning_rate']}
647
- - **Gradient Boosting**: n_est={best_config['gb_n_estimators']}, depth={best_config['gb_max_depth']}, lr={best_config['gb_learning_rate']}
648
- - **AdaBoost**: n_est={best_config['ada_n_estimators']}, lr={best_config['ada_learning_rate']}
649
- """
650
-
651
- ga_history_df = pd.DataFrame(ga.history)
652
-
653
- else:
654
- progress(0.3, desc="Selecting features (variance)...")
655
-
656
- feature_variance = np.var(X_train_scaled, axis=0)
657
- selected_indices = np.argsort(feature_variance)[-80:]
658
-
659
- X_train_selected = X_train_scaled[:, selected_indices]
660
- X_test_selected = X_test_scaled[:, selected_indices]
661
-
662
- progress(0.4, desc="Training XGBoost...")
663
- xgb_model = XGBClassifier(
664
- n_estimators=150, max_depth=5, learning_rate=0.1,
665
- objective='multi:softprob', num_class=n_classes,
666
- random_state=42, n_jobs=-1, verbosity=0
667
- )
668
- xgb_model.fit(X_train_selected, y_train)
669
- xgb_acc = xgb_model.score(X_test_selected, y_test)
670
-
671
- progress(0.5, desc="Training LightGBM...")
672
- lgbm_model = LGBMClassifier(
673
- n_estimators=150, num_leaves=40, learning_rate=0.1,
674
- objective='multiclass', num_class=n_classes,
675
- random_state=42, n_jobs=-1, verbose=-1
676
- )
677
- lgbm_model.fit(X_train_selected, y_train)
678
- lgbm_acc = lgbm_model.score(X_test_selected, y_test)
679
-
680
- progress(0.65, desc="Training Gradient Boosting...")
681
- gb_model = GradientBoostingClassifier(
682
- n_estimators=100, max_depth=4, learning_rate=0.1, random_state=42
683
- )
684
- gb_model.fit(X_train_selected, y_train)
685
- gb_acc = gb_model.score(X_test_selected, y_test)
686
-
687
- progress(0.8, desc="Training AdaBoost...")
688
- ada_model = AdaBoostClassifier(
689
- n_estimators=100, learning_rate=1.0, algorithm='SAMME', random_state=42
690
- )
691
- ada_model.fit(X_train_selected, y_train)
692
- ada_acc = ada_model.score(X_test_selected, y_test)
693
-
694
- accuracies = [xgb_acc, lgbm_acc, gb_acc, ada_acc]
695
- weights = np.array(accuracies) / sum(accuracies)
696
-
697
- ga_summary = "\n### ⚑ Simple Training (No GA)\n"
698
- ga_history_df = None
699
- training_log = "Simple training mode - no GA logs"
700
-
701
- progress(0.9, desc="Creating ensemble...")
702
-
703
- predictions = [
704
- xgb_model.predict_proba(X_test_selected),
705
- lgbm_model.predict_proba(X_test_selected),
706
- gb_model.predict_proba(X_test_selected),
707
- ada_model.predict_proba(X_test_selected)
708
- ]
709
-
710
- ensemble_pred = np.average(predictions, axis=0, weights=weights)
711
- ensemble_labels = np.argmax(ensemble_pred, axis=1)
712
- ensemble_acc = accuracy_score(y_test, ensemble_labels)
713
-
714
- progress(0.95, desc="Saving models...")
715
-
716
- os.makedirs('weights', exist_ok=True)
717
-
718
- with open('weights/xgboost_model.pkl', 'wb') as f:
719
- pickle.dump(xgb_model, f)
720
- with open('weights/lightgbm_model.pkl', 'wb') as f:
721
- pickle.dump(lgbm_model, f)
722
- with open('weights/gradientboost_model.pkl', 'wb') as f:
723
- pickle.dump(gb_model, f)
724
- with open('weights/adaboost_model.pkl', 'wb') as f:
725
- pickle.dump(ada_model, f)
726
- with open('weights/scaler.pkl', 'wb') as f:
727
- pickle.dump(scaler, f)
728
- with open('weights/label_encoder.pkl', 'wb') as f:
729
- pickle.dump(label_encoder, f)
730
-
731
- config = {
732
- 'selected_features': selected_indices.tolist(),
733
- 'ensemble_weights': weights.tolist(),
734
- 'n_features': len(selected_indices),
735
- 'emotions': label_encoder.classes_.tolist(),
736
- 'model_accuracies': {
737
- 'xgboost': float(xgb_acc),
738
- 'lightgbm': float(lgbm_acc),
739
- 'gradientboosting': float(gb_acc),
740
- 'adaboost': float(ada_acc),
741
- 'ensemble': float(ensemble_acc)
742
- }
743
- }
744
-
745
- with open('weights/config.json', 'w') as f:
746
- json.dump(config, f, indent=2)
747
-
748
- progress(1.0, desc="Complete!")
749
-
750
- results_df = pd.DataFrame({
751
- 'Model': ['XGBoost', 'LightGBM', 'Gradient Boosting', 'AdaBoost', 'Ensemble'],
752
- 'Test Accuracy': [xgb_acc, lgbm_acc, gb_acc, ada_acc, ensemble_acc]
753
- })
754
-
755
- summary = f"""
756
- ## βœ… Training Complete!
757
-
758
- {ga_summary}
759
-
760
- ### πŸ“Š Model Performance:
761
- - **XGBoost**: {xgb_acc:.4f}
762
- - **LightGBM**: {lgbm_acc:.4f}
763
- - **Gradient Boosting**: {gb_acc:.4f}
764
- - **AdaBoost**: {ada_acc:.4f}
765
- - **Ensemble**: {ensemble_acc:.4f} ⭐
766
-
767
- ### βš–οΈ Ensemble Weights:
768
- {dict(zip(['XGBoost', 'LightGBM', 'GradientBoosting', 'AdaBoost'], [f'{w:.3f}' for w in weights]))}
769
-
770
- ### πŸ’Ύ Saved Files:
771
- - `weights/xgboost_model.pkl`
772
- - `weights/lightgbm_model.pkl`
773
- - `weights/gradientboost_model.pkl`
774
- - `weights/adaboost_model.pkl`
775
- - `weights/scaler.pkl`
776
- - `weights/label_encoder.pkl`
777
- - `weights/config.json`
778
-
779
- βœ… **Models ready for prediction!**
780
- """
781
-
782
- return summary, results_df, ga_history_df, training_log
783
-
784
- except Exception as e:
785
- import traceback
786
- error_trace = traceback.format_exc()
787
- return f"❌ Training failed: {str(e)}\n\n{error_trace}", None, None, ""
788
-
789
-
790
- # ============================================================================
791
- # TAB 3: PREDICTION (UNCHANGED)
792
- # ============================================================================
793
-
794
- def predict_emotion_tab(audio_file):
795
- """Predict emotion from audio"""
796
-
797
- if audio_file is None:
798
- return "Please upload an audio file", None, None, None
799
-
800
- if not os.path.exists('weights/config.json'):
801
- return "❌ Models not trained! Go to Tab 2 first.", None, None, None
802
-
803
- try:
804
- from src.ensemble_model import EnsembleEmotionRecognizer
805
- from src.feature_extraction import extract_features
806
- from src.utils import create_waveform_plot, create_spectrogram_plot, get_emotion_emoji
807
-
808
- model = EnsembleEmotionRecognizer(weights_dir='weights')
809
- features, y, sr = extract_features(audio_file)
810
- emotion, confidence, prob_dict = model.predict_with_confidence(features)
811
- emoji = get_emotion_emoji(emotion)
812
-
813
- result_text = f"""
814
- ## Prediction Result
815
-
816
- ### {emoji} **{emotion.upper()}**
817
-
818
- **Confidence: {confidence*100:.1f}%**
819
-
820
- ### Probability Distribution:
821
- """
822
- for emo, prob in sorted(prob_dict.items(), key=lambda x: x[1], reverse=True):
823
- bar = 'β–ˆ' * int(prob * 30) + 'β–‘' * (30 - int(prob * 30))
824
- result_text += f"\n**{emo.capitalize()}**: {bar} {prob*100:.2f}%"
825
-
826
- prob_chart = {k.capitalize(): v for k, v in prob_dict.items()}
827
- waveform_fig = create_waveform_plot(y, sr)
828
- spectrogram_fig = create_spectrogram_plot(y, sr)
829
-
830
- return result_text, prob_chart, waveform_fig, spectrogram_fig
831
-
832
- except Exception as e:
833
- import traceback
834
- error_trace = traceback.format_exc()
835
- return f"❌ Prediction failed: {str(e)}\n\n{error_trace}", None, None, None
836
-
837
-
838
- # ============================================================================
839
- # GRADIO INTERFACE
840
- # ============================================================================
841
-
842
- custom_css = """
843
  .gradio-container {
844
  font-family: 'Inter', 'Arial', sans-serif;
845
  max-width: 1400px;
@@ -866,8 +36,14 @@ custom_css = """
866
  }
867
  """
868
 
869
- with gr.Blocks(css=custom_css, theme=gr.themes.Soft(), title="Speech Emotion Recognition") as demo:
 
 
 
 
 
870
 
 
871
  gr.HTML("""
872
  <div class="header">
873
  <h1>🎀 Speech Emotion Recognition</h1>
@@ -875,274 +51,46 @@ with gr.Blocks(css=custom_css, theme=gr.themes.Soft(), title="Speech Emotion Rec
875
  </div>
876
  """)
877
 
878
- # ========================================================================
879
- # TAB 1: FEATURE EXTRACTION (SIMPLIFIED - NO DUMMY DATA)
880
- # ========================================================================
881
-
882
- with gr.Tab("1️⃣ Feature Extraction"):
883
- gr.Markdown("""
884
- ## πŸ“ Extract Features from Dataset
885
-
886
- Upload your audio files (.wav format) to create the feature dataset.
887
-
888
- **Supported formats:**
889
- - RAVDESS format: `03-01-06-01-02-01-12.wav`
890
- - Custom format with emotion in filename
891
- """)
892
-
893
- with gr.Row():
894
- with gr.Column(scale=1):
895
- uploaded_files = gr.File(
896
- label="πŸ“€ Upload Audio Files (.wav)",
897
- file_count="multiple",
898
- type="filepath",
899
- file_types=[".wav"]
900
- )
901
-
902
- extract_dataset_btn = gr.Button(
903
- "πŸ”Š Extract Dataset Features",
904
- variant="primary",
905
- size="lg"
906
- )
907
-
908
- gr.Markdown("""
909
- ---
910
- ### πŸ” Preview Single Audio
911
-
912
- Test feature extraction on a single file before processing the entire dataset.
913
- """)
914
-
915
- preview_audio = gr.Audio(
916
- sources=["upload"],
917
- type="filepath",
918
- label="Upload Single File"
919
- )
920
- preview_btn = gr.Button("Preview Features")
921
-
922
- with gr.Column(scale=2):
923
- extract_output = gr.Markdown()
924
- feature_preview_df = gr.Dataframe(label="Dataset Preview (First 20 rows)")
925
- emotion_distribution = gr.Label(label="Emotion Distribution")
926
-
927
- with gr.Row():
928
- preview_waveform = gr.Plot(label="Waveform")
929
- preview_spectrogram = gr.Plot(label="Spectrogram")
930
-
931
- extract_dataset_btn.click(
932
- fn=extract_dataset_features,
933
- inputs=[uploaded_files],
934
- outputs=[extract_output, feature_preview_df, emotion_distribution]
935
- )
936
-
937
- preview_btn.click(
938
- fn=preview_single_audio,
939
- inputs=[preview_audio],
940
- outputs=[extract_output, preview_waveform, preview_spectrogram]
941
- )
942
-
943
- # ========================================================================
944
- # TAB 2: TRAINING WITH GA (WITH PARALLEL + LOGGING)
945
- # ========================================================================
946
-
947
- with gr.Tab("2️⃣ Model Training"):
948
- gr.Markdown("""
949
- ## 🧬 Train Models with Genetic Algorithm
950
-
951
- Optimize feature selection, hyperparameters, and ensemble weights using GA with parallel processing.
952
- """)
953
-
954
- with gr.Row():
955
- with gr.Column(scale=1):
956
- use_ga = gr.Checkbox(
957
- label="Use Genetic Algorithm Optimization",
958
- value=True,
959
- info="GA optimizes features + hyperparameters + ensemble weights"
960
- )
961
-
962
- ga_generations = gr.Slider(
963
- minimum=5,
964
- maximum=50,
965
- value=20,
966
- step=5,
967
- label="GA Generations",
968
- info="More generations = better optimization but slower",
969
- visible=True
970
- )
971
-
972
- ga_population = gr.Slider(
973
- minimum=5,
974
- maximum=30,
975
- value=15,
976
- step=5,
977
- label="GA Population Size",
978
- info="Larger population = more exploration but slower",
979
- visible=True
980
- )
981
-
982
- n_jobs = gr.Slider(
983
- minimum=1,
984
- maximum=8,
985
- value=2,
986
- step=1,
987
- label="Parallel Jobs",
988
- info="Number of CPU cores to use (2 for free tier, 4+ for better hardware)",
989
- visible=True
990
- )
991
-
992
- def toggle_ga_params(use_ga_val):
993
- return (
994
- gr.update(visible=use_ga_val),
995
- gr.update(visible=use_ga_val),
996
- gr.update(visible=use_ga_val)
997
- )
998
-
999
- use_ga.change(
1000
- fn=toggle_ga_params,
1001
- inputs=[use_ga],
1002
- outputs=[ga_generations, ga_population, n_jobs]
1003
- )
1004
-
1005
- train_btn = gr.Button(
1006
- "πŸš€ Start Training",
1007
- variant="primary",
1008
- size="lg"
1009
- )
1010
-
1011
- gr.Markdown("""
1012
- ### 🧬 GA Features:
1013
- - **βœ… Parallel Evaluation**: 2-4x speedup
1014
- - **βœ… Early Stopping**: Auto-stop when converged
1015
- - **βœ… Real-time Logging**: See progress details
1016
- - **βœ… Feature Selection**: 80 best from 162
1017
-
1018
- ### ⏱️ Estimated Time:
1019
- - **With GA (Parallel)**: 30-45 minutes
1020
- - **Without GA**: 5-10 minutes
1021
- """)
1022
-
1023
- with gr.Column(scale=2):
1024
- training_output = gr.Markdown()
1025
- results_table = gr.Dataframe(label="Model Performance")
1026
- ga_history_table = gr.Dataframe(label="GA Evolution History", visible=True)
1027
-
1028
- # Training log output
1029
- with gr.Accordion("πŸ“œ Detailed Training Log", open=False):
1030
- training_log_output = gr.Textbox(
1031
- label="Training Log",
1032
- lines=20,
1033
- max_lines=30,
1034
- interactive=False,
1035
- show_copy_button=True
1036
- )
1037
-
1038
- train_btn.click(
1039
- fn=train_with_ga,
1040
- inputs=[use_ga, ga_generations, ga_population, n_jobs],
1041
- outputs=[training_output, results_table, ga_history_table, training_log_output]
1042
- )
1043
-
1044
- # ========================================================================
1045
- # TAB 3: PREDICTION (UNCHANGED)
1046
- # ========================================================================
1047
-
1048
- with gr.Tab("3️⃣ Emotion Prediction"):
1049
- gr.Markdown("""
1050
- ## 🎯 Predict Emotion from Audio
1051
-
1052
- Upload audio to detect emotion using trained ensemble models.
1053
- """)
1054
-
1055
- with gr.Row():
1056
- with gr.Column(scale=1):
1057
- audio_input_predict = gr.Audio(
1058
- sources=["upload", "microphone"],
1059
- type="filepath",
1060
- label="Upload or Record Audio"
1061
- )
1062
- predict_btn = gr.Button(
1063
- "🎯 Predict Emotion",
1064
- variant="primary",
1065
- size="lg"
1066
- )
1067
-
1068
- gr.Markdown("""
1069
- ### 🎭 Supported Emotions:
1070
-
1071
- - 😠 **Angry**
1072
- - 😌 **Calm**
1073
- - 🀒 **Disgust**
1074
- - 😨 **Fearful**
1075
- - 😊 **Happy**
1076
- - 😐 **Neutral**
1077
- - 😒 **Sad**
1078
- - 😲 **Surprised**
1079
- """)
1080
-
1081
- with gr.Column(scale=2):
1082
- prediction_output = gr.Markdown()
1083
- prob_chart = gr.Label(
1084
- label="Emotion Probabilities",
1085
- num_top_classes=8
1086
- )
1087
-
1088
- with gr.Row():
1089
- waveform_predict = gr.Plot(label="Waveform")
1090
- spectrogram_predict = gr.Plot(label="Spectrogram")
1091
-
1092
- predict_btn.click(
1093
- fn=predict_emotion_tab,
1094
- inputs=[audio_input_predict],
1095
- outputs=[prediction_output, prob_chart, waveform_predict, spectrogram_predict]
1096
- )
1097
-
1098
- # ========================================================================
1099
- # FOOTER
1100
- # ========================================================================
1101
 
 
1102
  gr.Markdown("""
1103
  ---
1104
 
1105
  ## πŸ“š About This System
1106
 
1107
  ### Pipeline:
1108
- 1. **Feature Extraction** β†’ Extract 162 audio features β†’ Save to CSV
1109
  2. **Model Training** β†’ GA optimizes features + hyperparameters + ensemble weights
1110
  3. **Prediction** β†’ Use trained ensemble to predict emotions
1111
 
1112
- ### ⚑ Optimizations (Ζ―u tiΓͺn 1):
1113
- - **βœ… No Dummy Data**: Upload real audio files only
1114
  - **βœ… Parallel Processing**: 2-4x speedup with joblib
1115
- - **βœ… Early Stopping**: Stop when GA converges (save ~40% time)
1116
  - **βœ… Real-time Logging**: Detailed progress feedback
 
1117
 
1118
  ### Models:
1119
- - **XGBoost**: Extreme Gradient Boosting
1120
- - **LightGBM**: Light Gradient Boosting Machine
1121
- - **Gradient Boosting**: Sequential Ensemble Learning
1122
- - **AdaBoost**: Adaptive Boosting
1123
  - **Ensemble**: Weighted Soft Voting
1124
 
1125
- ### Features:
1126
- - Zero Crossing Rate (1)
1127
- - Chroma STFT (12)
1128
- - MFCC (20)
1129
- - RMS Energy (1)
1130
- - Mel Spectrogram (128)
1131
- - **Total**: 162 β†’ **GA selects**: 80
1132
-
1133
  ### Performance:
1134
  - **With GA**: ~87-90% accuracy
1135
  - **Without GA**: ~82-85% accuracy
1136
- - **Dataset**: RAVDESS (1,440 samples)
1137
 
1138
  ---
1139
 
1140
- Built with ❀️ using Gradio | Optimized with Parallel Processing & Early Stopping
1141
  """)
1142
 
 
1143
  if __name__ == "__main__":
1144
  demo.launch(
1145
- server_name="0.0.0.0",
1146
- server_port=7860,
1147
  show_error=True
1148
  )
 
1
  """
2
+ Speech Emotion Recognition - Main Application
3
+ Entry point for Gradio interface
 
 
4
  """
5
 
6
  import gradio as gr
7
+ import config
8
+ from src.ui import create_tab1, create_tab2, create_tab3
 
 
 
 
 
 
 
9
 
 
 
10
 
11
+ # Custom CSS
12
+ CUSTOM_CSS = """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  .gradio-container {
14
  font-family: 'Inter', 'Arial', sans-serif;
15
  max-width: 1400px;
 
36
  }
37
  """
38
 
39
+ # Create Gradio interface
40
+ with gr.Blocks(
41
+ css=CUSTOM_CSS,
42
+ theme=gr.themes.Soft(),
43
+ title="Speech Emotion Recognition"
44
+ ) as demo:
45
 
46
+ # Header
47
  gr.HTML("""
48
  <div class="header">
49
  <h1>🎀 Speech Emotion Recognition</h1>
 
51
  </div>
52
  """)
53
 
54
+ # Create tabs
55
+ create_tab1()
56
+ create_tab2()
57
+ create_tab3()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
+ # Footer
60
  gr.Markdown("""
61
  ---
62
 
63
  ## πŸ“š About This System
64
 
65
  ### Pipeline:
66
+ 1. **Feature Extraction** β†’ Extract 162 audio features from dataset
67
  2. **Model Training** β†’ GA optimizes features + hyperparameters + ensemble weights
68
  3. **Prediction** β†’ Use trained ensemble to predict emotions
69
 
70
+ ### ⚑ Key Features:
71
+ - **βœ… Modular Architecture**: Clean, maintainable code structure
72
  - **βœ… Parallel Processing**: 2-4x speedup with joblib
73
+ - **βœ… Early Stopping**: Auto-stop when converged (~40% time savings)
74
  - **βœ… Real-time Logging**: Detailed progress feedback
75
+ - **βœ… Configuration Management**: All settings in one place
76
 
77
  ### Models:
78
+ - XGBoost, LightGBM, Gradient Boosting, AdaBoost
 
 
 
79
  - **Ensemble**: Weighted Soft Voting
80
 
 
 
 
 
 
 
 
 
81
  ### Performance:
82
  - **With GA**: ~87-90% accuracy
83
  - **Without GA**: ~82-85% accuracy
 
84
 
85
  ---
86
 
87
+ Built with ❀️ using Gradio | Optimized Architecture
88
  """)
89
 
90
+
91
  if __name__ == "__main__":
92
  demo.launch(
93
+ server_name=config.UI_CONFIG['server_name'],
94
+ server_port=config.UI_CONFIG['server_port'],
95
  show_error=True
96
  )