#!/usr/bin/env python3 """ Comprehensive Setup Script for Optimized Twi Speech Recognition Engine ===================================================================== This script provides complete setup including: 1. Environment setup and dependency installation 2. Whisper fine-tuning on your Twi audio data 3. Intent classifier training 4. Model validation and testing 5. Server configuration Usage: python setup_complete.py # Full setup with fine-tuning python setup_complete.py --skip-whisper # Skip Whisper fine-tuning python setup_complete.py --quick # Quick setup for development python setup_complete.py --evaluate-only # Only evaluate existing models Author: AI Assistant Date: 2025-11-05 """ import os import sys import subprocess import logging import argparse import json import time import shutil from pathlib import Path from typing import Dict, List, Optional, Any, Tuple # Configure logging logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) logger = logging.getLogger(__name__) class ComprehensiveSetup: """Complete setup manager for the optimized Twi speech engine.""" def __init__(self, args): self.args = args self.base_dir = Path(__file__).parent self.project_root = self.base_dir.parent self.setup_status = { "dependencies": False, "directories": False, "whisper_model": False, "intent_classifier": False, "validation": False, "server_config": False, } # Paths self.data_dir = self.project_root / "data" / "raw" self.prompts_file = self.project_root / "twi_prompts.csv" self.models_dir = self.base_dir / "models" self.whisper_model_dir = self.models_dir / "whisper_twi" self.intent_model_dir = self.models_dir / "intent_classifier" def print_header(self): """Print setup header.""" print("\n" + "=" * 80) print(" COMPREHENSIVE SETUP: OPTIMIZED TWI SPEECH RECOGNITION") print("=" * 80) print(f"🎯 Goal: Fine-tune Whisper + Train Intent Classifier") print(f"πŸ“ Data: {self.data_dir}") print(f"πŸ“‹ Prompts: {self.prompts_file}") print(f"πŸ’Ύ Models: {self.models_dir}") print("=" * 80) def check_prerequisites(self) -> bool: """Check if all prerequisites are available.""" logger.info("πŸ” Checking prerequisites...") issues = [] # Check Python version if sys.version_info < (3, 8): issues.append(f"Python 3.8+ required, found {sys.version}") # Check data availability if not self.data_dir.exists(): issues.append(f"Audio data directory not found: {self.data_dir}") else: # Count audio files audio_files = list(self.data_dir.rglob("*.wav")) + list( self.data_dir.rglob("*.mp3") ) if len(audio_files) == 0: issues.append(f"No audio files found in {self.data_dir}") else: logger.info(f"βœ… Found {len(audio_files)} audio files") # Check prompts file if not self.prompts_file.exists(): issues.append(f"Prompts file not found: {self.prompts_file}") else: import pandas as pd try: df = pd.read_csv(self.prompts_file) logger.info(f"βœ… Found {len(df)} prompts in CSV") except Exception as e: issues.append(f"Invalid prompts file: {e}") # Check disk space (need ~10GB for models) try: import shutil total, used, free = shutil.disk_usage(self.base_dir) free_gb = free / (1024**3) if free_gb < 10: issues.append( f"Insufficient disk space: {free_gb:.1f}GB available, 10GB+ recommended" ) else: logger.info(f"βœ… Sufficient disk space: {free_gb:.1f}GB available") except: logger.warning("Could not check disk space") if issues: logger.error("❌ Prerequisites check failed:") for issue in issues: logger.error(f" - {issue}") return False logger.info("βœ… All prerequisites satisfied") return True def install_dependencies(self) -> bool: """Install required dependencies.""" logger.info("πŸ“¦ Installing dependencies...") requirements_file = self.base_dir / "requirements.txt" if not requirements_file.exists(): logger.error(f"Requirements file not found: {requirements_file}") return False try: # Install core requirements cmd = [sys.executable, "-m", "pip", "install", "-r", str(requirements_file)] result = subprocess.run(cmd, capture_output=True, text=True, timeout=1800) if result.returncode != 0: logger.error(f"Failed to install requirements: {result.stderr}") return False logger.info("βœ… Core dependencies installed") # Install additional dependencies for fine-tuning additional_deps = [ "datasets", "jiwer", "evaluate", "accelerate", "librosa>=0.10.0", "soundfile>=0.12.0", ] for dep in additional_deps: try: cmd = [sys.executable, "-m", "pip", "install", dep] result = subprocess.run( cmd, capture_output=True, text=True, timeout=300 ) if result.returncode == 0: logger.info(f"βœ… Installed {dep}") else: logger.warning(f"⚠️ Failed to install {dep}: {result.stderr}") except Exception as e: logger.warning(f"⚠️ Error installing {dep}: {e}") self.setup_status["dependencies"] = True return True except subprocess.TimeoutExpired: logger.error("❌ Installation timed out") return False except Exception as e: logger.error(f"❌ Installation failed: {e}") return False def setup_directories(self) -> bool: """Create necessary directories.""" logger.info("πŸ“ Setting up directories...") directories = [ self.models_dir, self.whisper_model_dir, self.intent_model_dir, self.base_dir / "data", self.base_dir / "data" / "cache", self.base_dir / "logs", ] try: for directory in directories: directory.mkdir(parents=True, exist_ok=True) logger.info(f"βœ… Created: {directory}") self.setup_status["directories"] = True return True except Exception as e: logger.error(f"❌ Failed to create directories: {e}") return False def fine_tune_whisper(self) -> bool: """Fine-tune Whisper model on Twi data.""" if self.args.skip_whisper: logger.info("⏭️ Skipping Whisper fine-tuning") return True logger.info("🎀 Starting Whisper fine-tuning on Twi data...") # Check if model already exists if self.whisper_model_dir.exists() and list( self.whisper_model_dir.glob("*.json") ): if not self.args.force: logger.info( f"βœ… Whisper model already exists at {self.whisper_model_dir}" ) self.setup_status["whisper_model"] = True return True else: logger.info("πŸ”„ Overwriting existing Whisper model") shutil.rmtree(self.whisper_model_dir) self.whisper_model_dir.mkdir(parents=True) try: # Import training script sys.path.insert(0, str(self.base_dir)) # Determine model size based on args model_size = "tiny" if self.args.quick else "small" epochs = 3 if self.args.quick else 10 batch_size = 4 if self.args.quick else 8 logger.info( f"Training config: model={model_size}, epochs={epochs}, batch_size={batch_size}" ) # Run training cmd = [ sys.executable, str(self.base_dir / "train_whisper_twi.py"), "--model_size", model_size, "--data_dir", str(self.data_dir), "--prompts_file", str(self.prompts_file), "--output_dir", str(self.whisper_model_dir), "--epochs", str(epochs), "--batch_size", str(batch_size), "--eval_steps", "100" if self.args.quick else "500", ] logger.info(f"Running: {' '.join(cmd)}") result = subprocess.run(cmd, timeout=7200) # 2 hour timeout if result.returncode == 0: logger.info("βœ… Whisper fine-tuning completed successfully") self.setup_status["whisper_model"] = True return True else: logger.error("❌ Whisper fine-tuning failed") return False except subprocess.TimeoutExpired: logger.error("❌ Whisper fine-tuning timed out") return False except ImportError as e: logger.error(f"❌ Failed to import training modules: {e}") return False except Exception as e: logger.error(f"❌ Whisper fine-tuning failed: {e}") return False def train_intent_classifier(self) -> bool: """Train intent classification model.""" logger.info("🎯 Training intent classifier...") # Check if model already exists if self.intent_model_dir.exists() and list( self.intent_model_dir.glob("*.json") ): if not self.args.force: logger.info( f"βœ… Intent classifier already exists at {self.intent_model_dir}" ) self.setup_status["intent_classifier"] = True return True else: logger.info("πŸ”„ Overwriting existing intent classifier") shutil.rmtree(self.intent_model_dir) self.intent_model_dir.mkdir(parents=True) try: # Run intent classifier training cmd = [ sys.executable, str(self.base_dir / "train_intent_classifier.py"), "--data", str(self.prompts_file), "--output", str(self.intent_model_dir), "--augment", ] if not self.args.quick: cmd.extend(["--epochs", "10"]) else: cmd.extend(["--epochs", "3"]) logger.info(f"Running: {' '.join(cmd)}") result = subprocess.run(cmd, timeout=1800) # 30 minute timeout if result.returncode == 0: logger.info("βœ… Intent classifier training completed") self.setup_status["intent_classifier"] = True return True else: logger.error("❌ Intent classifier training failed") return False except subprocess.TimeoutExpired: logger.error("❌ Intent classifier training timed out") return False except Exception as e: logger.error(f"❌ Intent classifier training failed: {e}") return False def validate_models(self) -> bool: """Validate trained models.""" logger.info("πŸ§ͺ Validating models...") validation_results = {} # Test Whisper model if self.whisper_model_dir.exists(): try: # Try loading the model from transformers import ( WhisperForConditionalGeneration, WhisperProcessor, ) model = WhisperForConditionalGeneration.from_pretrained( self.whisper_model_dir ) processor = WhisperProcessor.from_pretrained(self.whisper_model_dir) logger.info("βœ… Whisper model loads successfully") validation_results["whisper"] = "success" # Test with a sample audio file if available audio_files = list(self.data_dir.rglob("*.wav")) if audio_files: test_audio = audio_files[0] logger.info(f"Testing with: {test_audio}") import librosa import torch audio, sr = librosa.load(test_audio, sr=16000) inputs = processor(audio, sampling_rate=16000, return_tensors="pt") with torch.no_grad(): predicted_ids = model.generate( inputs.input_features, max_length=50 ) transcription = processor.batch_decode( predicted_ids, skip_special_tokens=True )[0] logger.info(f"βœ… Sample transcription: '{transcription}'") validation_results["whisper_test"] = transcription except Exception as e: logger.error(f"❌ Whisper model validation failed: {e}") validation_results["whisper"] = f"failed: {e}" # Test intent classifier if self.intent_model_dir.exists(): try: from transformers import pipeline classifier = pipeline( "text-classification", model=str(self.intent_model_dir) ) # Test with sample texts test_texts = ["KΙ” fie", "KΙ” cart mu", "HwehwΙ› nneΙ›ma"] for text in test_texts: result = classifier(text) logger.info( f"βœ… '{text}' -> {result[0]['label']} ({result[0]['score']:.3f})" ) validation_results["intent_classifier"] = "success" except Exception as e: logger.error(f"❌ Intent classifier validation failed: {e}") validation_results["intent_classifier"] = f"failed: {e}" # Test complete pipeline try: sys.path.insert(0, str(self.base_dir / "src")) from speech_recognizer import create_speech_recognizer recognizer = create_speech_recognizer() health = recognizer.health_check() if health["status"] == "healthy": logger.info("βœ… Complete pipeline validation successful") validation_results["pipeline"] = "healthy" else: logger.warning(f"⚠️ Pipeline health check: {health['status']}") validation_results["pipeline"] = health["status"] except Exception as e: logger.error(f"❌ Pipeline validation failed: {e}") validation_results["pipeline"] = f"failed: {e}" # Save validation results results_file = self.base_dir / "validation_results.json" with open(results_file, "w") as f: json.dump(validation_results, f, indent=2) self.setup_status["validation"] = True return True def configure_server(self) -> bool: """Configure server settings.""" logger.info("βš™οΈ Configuring server...") try: # Update configuration to use trained models config_file = self.base_dir / "config" / "config.py" # Read current config with open(config_file, "r") as f: config_content = f.read() # Update whisper configuration if self.whisper_model_dir.exists(): logger.info("Configuring to use fine-tuned Whisper model") config_content = config_content.replace( '"model_size": "large-v3"', '"model_size": "custom"' ) config_content = config_content.replace( '"use_fine_tuned": True', '"use_fine_tuned": True' ) # Save updated config with open(config_file, "w") as f: f.write(config_content) logger.info("βœ… Server configuration updated") self.setup_status["server_config"] = True return True except Exception as e: logger.error(f"❌ Server configuration failed: {e}") return False def print_summary(self): """Print setup summary.""" print("\n" + "=" * 80) print(" SETUP SUMMARY") print("=" * 80) for component, status in self.setup_status.items(): emoji = "βœ…" if status else "❌" print( f"{emoji} {component.replace('_', ' ').title()}: {'Success' if status else 'Failed'}" ) print("\n" + "-" * 80) success_count = sum(1 for status in self.setup_status.values() if status) total_count = len(self.setup_status) if success_count == total_count: print("πŸŽ‰ SETUP COMPLETED SUCCESSFULLY!") print("\nπŸ“‹ Next Steps:") print("1. Start the server: python main.py server") print( "2. Test with audio: curl -X POST -F 'file=@audio.wav' http://localhost:8000/test-intent" ) print("3. Check API docs: http://localhost:8000/docs") print("\n🎯 Models Trained:") if self.whisper_model_dir.exists(): print(f" πŸ“± Fine-tuned Whisper: {self.whisper_model_dir}") if self.intent_model_dir.exists(): print(f" 🎯 Intent Classifier: {self.intent_model_dir}") else: print(f"⚠️ SETUP PARTIALLY COMPLETED ({success_count}/{total_count})") print("\n❌ Failed Components:") for component, status in self.setup_status.items(): if not status: print(f" - {component.replace('_', ' ').title()}") print("\nπŸ”§ Troubleshooting:") print("1. Check logs above for specific error messages") print("2. Ensure sufficient disk space (10GB+)") print("3. Verify audio data in data/raw directory") print("4. Try running with --quick for faster setup") print("=" * 80) def run_complete_setup(self) -> bool: """Run the complete setup process.""" start_time = time.time() self.print_header() if not self.check_prerequisites(): return False steps = [ ("Installing Dependencies", self.install_dependencies), ("Setting up Directories", self.setup_directories), ("Fine-tuning Whisper", self.fine_tune_whisper), ("Training Intent Classifier", self.train_intent_classifier), ("Validating Models", self.validate_models), ("Configuring Server", self.configure_server), ] for step_name, step_func in steps: if self.args.evaluate_only and step_name not in ["Validating Models"]: continue logger.info(f"\nπŸš€ {step_name}...") success = step_func() if not success: logger.error(f"❌ {step_name} failed!") if not self.args.continue_on_error: break elapsed_time = time.time() - start_time logger.info(f"\n⏱️ Total setup time: {elapsed_time / 60:.1f} minutes") self.print_summary() return all(self.setup_status.values()) def main(): """Main setup function.""" parser = argparse.ArgumentParser( description="Comprehensive setup for Optimized Twi Speech Engine", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: python setup_complete.py # Full setup with fine-tuning python setup_complete.py --quick # Quick development setup python setup_complete.py --skip-whisper # Skip Whisper fine-tuning python setup_complete.py --evaluate-only # Only validate existing models """, ) parser.add_argument( "--skip-whisper", action="store_true", help="Skip Whisper fine-tuning (use pre-trained model)", ) parser.add_argument( "--quick", action="store_true", help="Quick setup with smaller models and fewer epochs", ) parser.add_argument( "--force", action="store_true", help="Overwrite existing models" ) parser.add_argument( "--evaluate-only", action="store_true", help="Only evaluate existing models, skip training", ) parser.add_argument( "--continue-on-error", action="store_true", help="Continue setup even if some steps fail", ) args = parser.parse_args() try: setup = ComprehensiveSetup(args) success = setup.run_complete_setup() sys.exit(0 if success else 1) except KeyboardInterrupt: print("\n⚠️ Setup interrupted by user") sys.exit(1) except Exception as e: logger.error(f"❌ Setup failed with unexpected error: {e}") import traceback traceback.print_exc() sys.exit(1) if __name__ == "__main__": main()