optimization.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. """
  2. Performance optimization for Voice Recognition system.
  3. This module provides comprehensive performance optimization capabilities:
  4. - Real-time performance monitoring
  5. - Model optimization for deployment
  6. - Memory management
  7. - Latency optimization
  8. - Hardware-specific optimizations
  9. """
  10. import gc
  11. import logging
  12. import time
  13. import threading
  14. from typing import Dict, List, Optional, Any, Tuple, Union
  15. from dataclasses import dataclass, field
  16. from collections import deque
  17. import psutil
  18. import numpy as np
  19. import torch
  20. import torch.nn as nn
  21. from torch.profiler import profile, record_function, ProfilerActivity
  22. # Configure logging
  23. logger = logging.getLogger(__name__)
  24. @dataclass
  25. class VRPerformanceMetrics:
  26. """Performance metrics for voice recognition system."""
  27. # Timing metrics
  28. avg_inference_time_ms: float = 0.0
  29. max_inference_time_ms: float = 0.0
  30. min_inference_time_ms: float = float('inf')
  31. std_inference_time_ms: float = 0.0
  32. # Throughput metrics
  33. frames_per_second: float = 0.0
  34. audio_realtime_factor: float = 0.0 # Processing speed vs real-time
  35. # Memory metrics
  36. memory_usage_mb: float = 0.0
  37. peak_memory_mb: float = 0.0
  38. gpu_memory_mb: float = 0.0
  39. # Model metrics
  40. model_size_mb: float = 0.0
  41. flops_per_inference: Optional[int] = None
  42. # Quality metrics
  43. avg_confidence: float = 0.0
  44. successful_identifications: int = 0
  45. total_attempts: int = 0
  46. # System metrics
  47. cpu_usage_percent: float = 0.0
  48. temperature_celsius: Optional[float] = None
  49. def accuracy_rate(self) -> float:
  50. """Calculate identification accuracy rate."""
  51. if self.total_attempts == 0:
  52. return 0.0
  53. return self.successful_identifications / self.total_attempts
  54. def to_dict(self) -> Dict[str, Any]:
  55. """Convert metrics to dictionary."""
  56. return {
  57. 'timing': {
  58. 'avg_inference_time_ms': self.avg_inference_time_ms,
  59. 'max_inference_time_ms': self.max_inference_time_ms,
  60. 'min_inference_time_ms': self.min_inference_time_ms,
  61. 'std_inference_time_ms': self.std_inference_time_ms
  62. },
  63. 'throughput': {
  64. 'frames_per_second': self.frames_per_second,
  65. 'audio_realtime_factor': self.audio_realtime_factor
  66. },
  67. 'memory': {
  68. 'memory_usage_mb': self.memory_usage_mb,
  69. 'peak_memory_mb': self.peak_memory_mb,
  70. 'gpu_memory_mb': self.gpu_memory_mb
  71. },
  72. 'model': {
  73. 'model_size_mb': self.model_size_mb,
  74. 'flops_per_inference': self.flops_per_inference
  75. },
  76. 'quality': {
  77. 'avg_confidence': self.avg_confidence,
  78. 'accuracy_rate': self.accuracy_rate(),
  79. 'successful_identifications': self.successful_identifications,
  80. 'total_attempts': self.total_attempts
  81. },
  82. 'system': {
  83. 'cpu_usage_percent': self.cpu_usage_percent,
  84. 'temperature_celsius': self.temperature_celsius
  85. }
  86. }
  87. class VRPerformanceMonitor:
  88. """Real-time performance monitor for voice recognition system."""
  89. def __init__(self,
  90. window_size: int = 100,
  91. monitoring_interval: float = 1.0,
  92. enable_detailed_profiling: bool = False):
  93. """
  94. Initialize performance monitor.
  95. Args:
  96. window_size: Number of samples to keep for rolling statistics
  97. monitoring_interval: Monitoring update interval in seconds
  98. enable_detailed_profiling: Enable detailed PyTorch profiling
  99. """
  100. self.window_size = window_size
  101. self.monitoring_interval = monitoring_interval
  102. self.enable_detailed_profiling = enable_detailed_profiling
  103. # Performance data storage
  104. self.inference_times = deque(maxlen=window_size)
  105. self.confidence_scores = deque(maxlen=window_size)
  106. self.memory_usage = deque(maxlen=window_size)
  107. # Monitoring state
  108. self._is_monitoring = False
  109. self._monitor_thread: Optional[threading.Thread] = None
  110. self._start_time = time.time()
  111. self._frame_count = 0
  112. self._successful_count = 0
  113. # System monitoring
  114. self._process = psutil.Process()
  115. self._peak_memory = 0.0
  116. # Profiling
  117. self._profiler: Optional[torch.profiler.profile] = None
  118. self._profiling_active = False
  119. def start_monitoring(self):
  120. """Start performance monitoring."""
  121. if self._is_monitoring:
  122. logger.warning("Performance monitoring already active")
  123. return
  124. self._is_monitoring = True
  125. self._start_time = time.time()
  126. self._frame_count = 0
  127. self._successful_count = 0
  128. # Start monitoring thread
  129. self._monitor_thread = threading.Thread(
  130. target=self._monitoring_loop,
  131. daemon=True
  132. )
  133. self._monitor_thread.start()
  134. logger.info("Performance monitoring started")
  135. def stop_monitoring(self):
  136. """Stop performance monitoring."""
  137. self._is_monitoring = False
  138. if self._monitor_thread:
  139. self._monitor_thread.join(timeout=2.0)
  140. if self._profiling_active:
  141. self.stop_profiling()
  142. logger.info("Performance monitoring stopped")
  143. def record_inference(self,
  144. inference_time_ms: float,
  145. confidence: float,
  146. success: bool = True):
  147. """
  148. Record inference performance data.
  149. Args:
  150. inference_time_ms: Inference time in milliseconds
  151. confidence: Confidence score
  152. success: Whether inference was successful
  153. """
  154. self.inference_times.append(inference_time_ms)
  155. self.confidence_scores.append(confidence)
  156. self._frame_count += 1
  157. if success:
  158. self._successful_count += 1
  159. # Update peak memory
  160. current_memory = self._get_memory_usage()
  161. self._peak_memory = max(self._peak_memory, current_memory)
  162. def get_metrics(self) -> VRPerformanceMetrics:
  163. """Get current performance metrics."""
  164. metrics = VRPerformanceMetrics()
  165. # Timing metrics
  166. if self.inference_times:
  167. times = list(self.inference_times)
  168. metrics.avg_inference_time_ms = np.mean(times)
  169. metrics.max_inference_time_ms = np.max(times)
  170. metrics.min_inference_time_ms = np.min(times)
  171. metrics.std_inference_time_ms = np.std(times)
  172. # Throughput metrics
  173. elapsed_time = time.time() - self._start_time
  174. if elapsed_time > 0:
  175. metrics.frames_per_second = self._frame_count / elapsed_time
  176. # Estimate real-time factor (assuming 16kHz audio with 30ms frames)
  177. audio_duration_s = self._frame_count * 0.03 # 30ms frames
  178. metrics.audio_realtime_factor = audio_duration_s / elapsed_time
  179. # Memory metrics
  180. metrics.memory_usage_mb = self._get_memory_usage()
  181. metrics.peak_memory_mb = self._peak_memory
  182. metrics.gpu_memory_mb = self._get_gpu_memory_usage()
  183. # Quality metrics
  184. if self.confidence_scores:
  185. metrics.avg_confidence = np.mean(list(self.confidence_scores))
  186. metrics.successful_identifications = self._successful_count
  187. metrics.total_attempts = self._frame_count
  188. # System metrics
  189. metrics.cpu_usage_percent = self._process.cpu_percent()
  190. # Temperature (if available)
  191. try:
  192. temps = psutil.sensors_temperatures()
  193. if temps:
  194. cpu_temps = temps.get('coretemp', [])
  195. if cpu_temps:
  196. metrics.temperature_celsius = cpu_temps[0].current
  197. except:
  198. pass
  199. return metrics
  200. def start_profiling(self,
  201. activities: List[ProfilerActivity] = None,
  202. record_shapes: bool = True):
  203. """Start detailed PyTorch profiling."""
  204. if not self.enable_detailed_profiling:
  205. logger.warning("Detailed profiling not enabled")
  206. return
  207. if self._profiling_active:
  208. logger.warning("Profiling already active")
  209. return
  210. if activities is None:
  211. activities = [ProfilerActivity.CPU]
  212. if torch.cuda.is_available():
  213. activities.append(ProfilerActivity.CUDA)
  214. self._profiler = profile(
  215. activities=activities,
  216. record_shapes=record_shapes,
  217. with_stack=True
  218. )
  219. self._profiler.__enter__()
  220. self._profiling_active = True
  221. logger.info("PyTorch profiling started")
  222. def stop_profiling(self) -> Optional[str]:
  223. """Stop profiling and return results."""
  224. if not self._profiling_active or not self._profiler:
  225. return None
  226. self._profiler.__exit__(None, None, None)
  227. self._profiling_active = False
  228. # Get profiling results
  229. results = self._profiler.key_averages().table(sort_by="cpu_time_total", row_limit=20)
  230. logger.info("PyTorch profiling stopped")
  231. return results
  232. def _monitoring_loop(self):
  233. """Background monitoring loop."""
  234. while self._is_monitoring:
  235. try:
  236. # Update memory usage
  237. current_memory = self._get_memory_usage()
  238. self.memory_usage.append(current_memory)
  239. # Log periodic stats
  240. if len(self.inference_times) > 10:
  241. metrics = self.get_metrics()
  242. logger.debug(f"Performance: {metrics.avg_inference_time_ms:.1f}ms avg, "
  243. f"{metrics.frames_per_second:.1f} FPS, "
  244. f"{metrics.memory_usage_mb:.1f}MB memory")
  245. time.sleep(self.monitoring_interval)
  246. except Exception as e:
  247. logger.error(f"Error in monitoring loop: {e}")
  248. time.sleep(self.monitoring_interval)
  249. def _get_memory_usage(self) -> float:
  250. """Get current memory usage in MB."""
  251. try:
  252. memory_info = self._process.memory_info()
  253. return memory_info.rss / (1024 * 1024) # Convert to MB
  254. except:
  255. return 0.0
  256. def _get_gpu_memory_usage(self) -> float:
  257. """Get GPU memory usage in MB."""
  258. if not torch.cuda.is_available():
  259. return 0.0
  260. try:
  261. memory_allocated = torch.cuda.memory_allocated() / (1024 * 1024)
  262. return memory_allocated
  263. except:
  264. return 0.0
  265. class VRModelOptimizer:
  266. """Model optimization utilities for voice recognition."""
  267. @staticmethod
  268. def optimize_for_inference(model: nn.Module,
  269. enable_torch_compile: bool = True,
  270. use_half_precision: bool = False,
  271. enable_jit: bool = False) -> nn.Module:
  272. """
  273. Optimize model for inference.
  274. Args:
  275. model: PyTorch model to optimize
  276. enable_torch_compile: Use torch.compile (PyTorch 2.0+)
  277. use_half_precision: Convert to half precision
  278. enable_jit: Use TorchScript JIT compilation
  279. Returns:
  280. Optimized model
  281. """
  282. logger.info("Optimizing model for inference...")
  283. # Set to evaluation mode
  284. model.eval()
  285. # Disable gradients
  286. for param in model.parameters():
  287. param.requires_grad = False
  288. # Half precision
  289. if use_half_precision:
  290. model = model.half()
  291. logger.info("Converted model to half precision")
  292. # TorchScript JIT
  293. if enable_jit:
  294. try:
  295. # Create example input for tracing
  296. example_input = VRModelOptimizer._create_example_input(model)
  297. model = torch.jit.trace(model, example_input)
  298. logger.info("Applied TorchScript JIT compilation")
  299. except Exception as e:
  300. logger.warning(f"JIT compilation failed: {e}")
  301. # Torch compile (PyTorch 2.0+)
  302. if enable_torch_compile and hasattr(torch, 'compile'):
  303. try:
  304. model = torch.compile(model, mode='reduce-overhead')
  305. logger.info("Applied torch.compile optimization")
  306. except Exception as e:
  307. logger.warning(f"torch.compile failed: {e}")
  308. return model
  309. @staticmethod
  310. def quantize_model(model: nn.Module,
  311. quantization_backend: str = 'fbgemm',
  312. calibration_data: Optional[torch.Tensor] = None) -> nn.Module:
  313. """
  314. Quantize model for improved performance.
  315. Args:
  316. model: Model to quantize
  317. quantization_backend: Quantization backend ('fbgemm', 'qnnpack')
  318. calibration_data: Calibration data for quantization
  319. Returns:
  320. Quantized model
  321. """
  322. logger.info(f"Quantizing model with {quantization_backend} backend...")
  323. try:
  324. torch.backends.quantized.engine = quantization_backend
  325. # Prepare model for quantization
  326. model.eval()
  327. model_prepared = torch.quantization.prepare(model)
  328. # Calibrate if data provided
  329. if calibration_data is not None:
  330. with torch.no_grad():
  331. model_prepared(calibration_data)
  332. # Convert to quantized model
  333. quantized_model = torch.quantization.convert(model_prepared)
  334. logger.info("Model quantization completed successfully")
  335. return quantized_model
  336. except Exception as e:
  337. logger.error(f"Model quantization failed: {e}")
  338. return model
  339. @staticmethod
  340. def estimate_model_size(model: nn.Module) -> float:
  341. """
  342. Estimate model size in MB.
  343. Args:
  344. model: PyTorch model
  345. Returns:
  346. Model size in MB
  347. """
  348. param_size = 0
  349. buffer_size = 0
  350. for param in model.parameters():
  351. param_size += param.nelement() * param.element_size()
  352. for buffer in model.buffers():
  353. buffer_size += buffer.nelement() * buffer.element_size()
  354. total_size = param_size + buffer_size
  355. return total_size / (1024 * 1024) # Convert to MB
  356. @staticmethod
  357. def _create_example_input(model: nn.Module) -> torch.Tensor:
  358. """Create example input for model tracing."""
  359. # This is a simplified example - in practice, you'd need to
  360. # determine the correct input shape for your specific model
  361. return torch.randn(1, 40, 100) # Batch, features, time
  362. class VRMemoryManager:
  363. """Memory management utilities for voice recognition."""
  364. def __init__(self, max_memory_mb: int = 512):
  365. """
  366. Initialize memory manager.
  367. Args:
  368. max_memory_mb: Maximum memory usage limit in MB
  369. """
  370. self.max_memory_mb = max_memory_mb
  371. self._cleanup_threshold = max_memory_mb * 0.8 # Trigger cleanup at 80%
  372. self._last_cleanup = time.time()
  373. self._cleanup_interval = 30.0 # Cleanup interval in seconds
  374. def check_memory_usage(self) -> bool:
  375. """
  376. Check if memory usage is within limits.
  377. Returns:
  378. True if memory usage is acceptable
  379. """
  380. current_usage = self._get_current_memory_usage()
  381. if current_usage > self._cleanup_threshold:
  382. self._trigger_cleanup()
  383. return False
  384. return current_usage <= self.max_memory_mb
  385. def force_cleanup(self):
  386. """Force memory cleanup."""
  387. self._trigger_cleanup()
  388. def _get_current_memory_usage(self) -> float:
  389. """Get current process memory usage in MB."""
  390. try:
  391. process = psutil.Process()
  392. memory_info = process.memory_info()
  393. return memory_info.rss / (1024 * 1024)
  394. except:
  395. return 0.0
  396. def _trigger_cleanup(self):
  397. """Trigger memory cleanup operations."""
  398. current_time = time.time()
  399. # Rate limit cleanup operations
  400. if current_time - self._last_cleanup < self._cleanup_interval:
  401. return
  402. logger.info("Triggering memory cleanup...")
  403. # Python garbage collection
  404. gc.collect()
  405. # PyTorch cache cleanup
  406. if torch.cuda.is_available():
  407. torch.cuda.empty_cache()
  408. torch.cuda.synchronize()
  409. self._last_cleanup = current_time
  410. # Log memory usage after cleanup
  411. usage_after = self._get_current_memory_usage()
  412. logger.info(f"Memory usage after cleanup: {usage_after:.1f}MB")
  413. class VRLatencyOptimizer:
  414. """Latency optimization utilities for voice recognition."""
  415. def __init__(self, target_latency_ms: float = 100.0):
  416. """
  417. Initialize latency optimizer.
  418. Args:
  419. target_latency_ms: Target latency in milliseconds
  420. """
  421. self.target_latency_ms = target_latency_ms
  422. self._latency_history = deque(maxlen=50)
  423. self._optimization_suggestions = []
  424. def record_latency(self, latency_ms: float):
  425. """Record inference latency."""
  426. self._latency_history.append(latency_ms)
  427. # Check if optimization needed
  428. if len(self._latency_history) >= 10:
  429. avg_latency = np.mean(list(self._latency_history))
  430. if avg_latency > self.target_latency_ms * 1.2: # 20% tolerance
  431. self._generate_optimization_suggestions(avg_latency)
  432. def get_optimization_suggestions(self) -> List[str]:
  433. """Get optimization suggestions based on performance."""
  434. return self._optimization_suggestions.copy()
  435. def _generate_optimization_suggestions(self, avg_latency: float):
  436. """Generate optimization suggestions."""
  437. suggestions = []
  438. if avg_latency > self.target_latency_ms * 2:
  439. suggestions.append("Consider using model quantization")
  440. suggestions.append("Enable half precision inference")
  441. suggestions.append("Reduce audio processing length")
  442. elif avg_latency > self.target_latency_ms * 1.5:
  443. suggestions.append("Enable torch.compile optimization")
  444. suggestions.append("Consider using a smaller model architecture")
  445. else:
  446. suggestions.append("Fine-tune batch size for optimal performance")
  447. # Update suggestions if they've changed
  448. if suggestions != self._optimization_suggestions:
  449. self._optimization_suggestions = suggestions
  450. logger.info(f"Generated optimization suggestions: {suggestions}")
  451. def create_vr_performance_monitor(window_size: int = 100,
  452. monitoring_interval: float = 1.0,
  453. enable_profiling: bool = False) -> VRPerformanceMonitor:
  454. """
  455. Factory function to create a VRPerformanceMonitor.
  456. Args:
  457. window_size: Number of samples for rolling statistics
  458. monitoring_interval: Monitoring update interval
  459. enable_profiling: Enable detailed profiling
  460. Returns:
  461. Configured VRPerformanceMonitor instance
  462. """
  463. return VRPerformanceMonitor(
  464. window_size=window_size,
  465. monitoring_interval=monitoring_interval,
  466. enable_detailed_profiling=enable_profiling
  467. )
  468. def optimize_vr_for_deployment(model: nn.Module,
  469. target_device: str = "cpu",
  470. target_latency_ms: float = 100.0,
  471. enable_quantization: bool = False,
  472. calibration_data: Optional[torch.Tensor] = None) -> Dict[str, Any]:
  473. """
  474. Comprehensive optimization for voice recognition deployment.
  475. Args:
  476. model: Model to optimize
  477. target_device: Target deployment device
  478. target_latency_ms: Target latency requirement
  479. enable_quantization: Whether to apply quantization
  480. calibration_data: Data for quantization calibration
  481. Returns:
  482. Dictionary containing optimized model and metrics
  483. """
  484. logger.info("Starting comprehensive voice recognition optimization...")
  485. results = {
  486. 'original_model': model,
  487. 'optimized_model': None,
  488. 'optimization_applied': [],
  489. 'performance_improvement': {},
  490. 'model_size_reduction': 0.0
  491. }
  492. # Measure original performance
  493. original_size = VRModelOptimizer.estimate_model_size(model)
  494. # Apply optimizations
  495. optimized_model = model
  496. # Basic inference optimization
  497. optimized_model = VRModelOptimizer.optimize_for_inference(
  498. optimized_model,
  499. enable_torch_compile=True,
  500. use_half_precision=(target_device != "cpu"),
  501. enable_jit=False
  502. )
  503. results['optimization_applied'].append('inference_optimization')
  504. # Quantization if requested
  505. if enable_quantization:
  506. optimized_model = VRModelOptimizer.quantize_model(
  507. optimized_model,
  508. calibration_data=calibration_data
  509. )
  510. results['optimization_applied'].append('quantization')
  511. # Measure optimized performance
  512. optimized_size = VRModelOptimizer.estimate_model_size(optimized_model)
  513. results['model_size_reduction'] = (original_size - optimized_size) / original_size * 100
  514. results['optimized_model'] = optimized_model
  515. results['performance_improvement'] = {
  516. 'original_size_mb': original_size,
  517. 'optimized_size_mb': optimized_size,
  518. 'size_reduction_percent': results['model_size_reduction']
  519. }
  520. logger.info(f"Optimization completed. Model size reduced by {results['model_size_reduction']:.1f}%")
  521. return results