refactor: half way to process per session
All checks were successful
Build Worker Base and Application Images / check-base-changes (push) Successful in 7s
Build Worker Base and Application Images / build-base (push) Has been skipped
Build Worker Base and Application Images / build-docker (push) Successful in 2m52s
Build Worker Base and Application Images / deploy-stack (push) Successful in 9s

This commit is contained in:
ziesorx 2025-09-25 20:52:26 +07:00
parent 2e5316ca01
commit 34d1982e9e
12 changed files with 2771 additions and 92 deletions

View file

@ -34,11 +34,7 @@ class InferenceResult:
class YOLOWrapper:
"""Wrapper for YOLO models with caching and optimization"""
# Class-level model cache shared across all instances
_model_cache: Dict[str, Any] = {}
_cache_lock = Lock()
"""Wrapper for YOLO models with per-instance isolation (no shared cache)"""
def __init__(self, model_path: Path, model_id: str, device: Optional[str] = None):
"""
@ -65,61 +61,48 @@ class YOLOWrapper:
logger.info(f"Initialized YOLO wrapper for {model_id} on {self.device}")
def _load_model(self) -> None:
"""Load the YOLO model with caching"""
cache_key = str(self.model_path)
"""Load the YOLO model in isolation (no shared cache)"""
try:
from ultralytics import YOLO
with self._cache_lock:
# Check if model is already cached
if cache_key in self._model_cache:
logger.info(f"Loading model {self.model_id} from cache")
self.model = self._model_cache[cache_key]
self._extract_class_names()
return
logger.debug(f"Loading YOLO model {self.model_id} from {self.model_path} (ISOLATED)")
# Load model
try:
from ultralytics import YOLO
# Load model directly without any caching
self.model = YOLO(str(self.model_path))
logger.info(f"Loading YOLO model from {self.model_path}")
# Determine if this is a classification model based on filename or model structure
# Classification models typically have 'cls' in filename
is_classification = 'cls' in str(self.model_path).lower()
# Load model normally first
self.model = YOLO(str(self.model_path))
# For classification models, create a separate instance with task parameter
if is_classification:
try:
# Reload with classification task (like ML engineer's approach)
self.model = YOLO(str(self.model_path), task="classify")
logger.info(f"Loaded classification model {self.model_id} with task='classify' (ISOLATED)")
except Exception as e:
logger.warning(f"Failed to load with task='classify', using default: {e}")
# Fall back to regular loading
self.model = YOLO(str(self.model_path))
logger.info(f"Loaded model {self.model_id} with default task (ISOLATED)")
else:
logger.info(f"Loaded detection model {self.model_id} (ISOLATED)")
# Determine if this is a classification model based on filename or model structure
# Classification models typically have 'cls' in filename
is_classification = 'cls' in str(self.model_path).lower()
# Move model to device
if self.device == 'cuda' and torch.cuda.is_available():
self.model.to('cuda')
logger.info(f"Model {self.model_id} moved to GPU (ISOLATED)")
# For classification models, create a separate instance with task parameter
if is_classification:
try:
# Reload with classification task (like ML engineer's approach)
self.model = YOLO(str(self.model_path), task="classify")
logger.info(f"Loaded classification model {self.model_id} with task='classify'")
except Exception as e:
logger.warning(f"Failed to load with task='classify', using default: {e}")
# Fall back to regular loading
self.model = YOLO(str(self.model_path))
logger.info(f"Loaded model {self.model_id} with default task")
else:
logger.info(f"Loaded detection model {self.model_id}")
self._extract_class_names()
# Move model to device
if self.device == 'cuda' and torch.cuda.is_available():
self.model.to('cuda')
logger.info(f"Model {self.model_id} moved to GPU")
logger.debug(f"Successfully loaded model {self.model_id} in isolation - no shared cache!")
# Cache the model
self._model_cache[cache_key] = self.model
self._extract_class_names()
logger.info(f"Successfully loaded model {self.model_id}")
except ImportError:
logger.error("Ultralytics YOLO not installed. Install with: pip install ultralytics")
raise
except Exception as e:
logger.error(f"Failed to load YOLO model {self.model_id}: {str(e)}", exc_info=True)
raise
except ImportError:
logger.error("Ultralytics YOLO not installed. Install with: pip install ultralytics")
raise
except Exception as e:
logger.error(f"Failed to load YOLO model {self.model_id}: {str(e)}", exc_info=True)
raise
def _extract_class_names(self) -> None:
"""Extract class names from the model"""
@ -375,19 +358,15 @@ class YOLOWrapper:
return 'cls' in str(self.model_path).lower() or 'classify' in str(self.model_path).lower()
def clear_cache(self) -> None:
"""Clear the model cache"""
with self._cache_lock:
cache_key = str(self.model_path)
if cache_key in self._model_cache:
del self._model_cache[cache_key]
logger.info(f"Cleared cache for model {self.model_id}")
"""Clear model resources (no cache in isolated mode)"""
if self.model:
# Clear any model resources if needed
logger.info(f"Cleared resources for model {self.model_id} (no shared cache)")
@classmethod
def clear_all_cache(cls) -> None:
"""Clear all cached models"""
with cls._cache_lock:
cls._model_cache.clear()
logger.info("Cleared all model cache")
"""No-op in isolated mode (no shared cache to clear)"""
logger.info("No shared cache to clear in isolated mode")
def warmup(self, image_size: Tuple[int, int] = (640, 640)) -> None:
"""
@ -438,16 +417,17 @@ class ModelInferenceManager:
YOLOWrapper instance
"""
with self._lock:
# Check if already loaded
# Check if already loaded for this specific manager instance
if model_id in self.models:
logger.debug(f"Model {model_id} already loaded")
logger.debug(f"Model {model_id} already loaded in this manager instance")
return self.models[model_id]
# Load the model
# Load the model (each instance loads independently)
model_path = self.model_dir / model_file
if not model_path.exists():
raise FileNotFoundError(f"Model file not found: {model_path}")
logger.info(f"Loading model {model_id} in isolation for this manager instance")
wrapper = YOLOWrapper(model_path, model_id, device)
self.models[model_id] = wrapper