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
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:
parent
2e5316ca01
commit
34d1982e9e
12 changed files with 2771 additions and 92 deletions
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue