Initial commit
This commit is contained in:
68
utils/classifier_model_store.py
Normal file
68
utils/classifier_model_store.py
Normal file
@@ -0,0 +1,68 @@
|
||||
"""
|
||||
모델 로드/저장/예측 담당
|
||||
"""
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import joblib
|
||||
import numpy as np
|
||||
from loguru import logger
|
||||
from sklearn.pipeline import Pipeline
|
||||
|
||||
import config
|
||||
|
||||
MODEL_FILENAME = "classifier.pkl"
|
||||
|
||||
|
||||
def _get_latest_path() -> Optional[Path]:
|
||||
candidates = sorted(config.CLASSIFIER_MODEL_BASE_PATH.rglob(MODEL_FILENAME))
|
||||
return candidates[-1] if candidates else None
|
||||
|
||||
|
||||
def _get_save_path() -> Path:
|
||||
from datetime import datetime
|
||||
date_dir = config.CLASSIFIER_MODEL_BASE_PATH / datetime.now().strftime("%Y%m%d")
|
||||
date_dir.mkdir(exist_ok=True)
|
||||
existing = sorted([d for d in date_dir.iterdir() if d.is_dir() and d.name.isdigit()])
|
||||
next_idx = int(existing[-1].name) + 1 if existing else 1
|
||||
save_dir = date_dir / f"{next_idx:04d}"
|
||||
save_dir.mkdir(exist_ok=True)
|
||||
return save_dir / MODEL_FILENAME
|
||||
|
||||
|
||||
_model: Optional[Pipeline] = None
|
||||
|
||||
# 서버 시작 시 최신 모델 자동 로드
|
||||
_latest = _get_latest_path()
|
||||
if _latest:
|
||||
try:
|
||||
_model = joblib.load(_latest)
|
||||
logger.info(f"classifier 모델 로드: {_latest} | classes={list(_model.classes_)}")
|
||||
except Exception as e:
|
||||
logger.warning(f"모델 로드 실패: {e}")
|
||||
|
||||
|
||||
def get_model() -> Optional[Pipeline]:
|
||||
return _model
|
||||
|
||||
|
||||
def set_model(pipeline: Pipeline) -> None:
|
||||
global _model
|
||||
path = _get_save_path()
|
||||
joblib.dump(pipeline, path)
|
||||
_model = pipeline
|
||||
logger.info(f"classifier 모델 저장: {path}")
|
||||
|
||||
|
||||
def predict(
|
||||
pipeline: Pipeline,
|
||||
text: str,
|
||||
threshold: float = 0.6,
|
||||
) -> tuple[str, float, dict[str, float]]:
|
||||
probs = pipeline.predict_proba([text])[0]
|
||||
classes = pipeline.classes_
|
||||
max_idx = int(np.argmax(probs))
|
||||
confidence = float(probs[max_idx])
|
||||
label = "OTHER" if confidence < threshold else classes[max_idx]
|
||||
all_probs = {c: round(float(p), 4) for c, p in zip(classes, probs)}
|
||||
return label, round(confidence, 4), all_probs
|
||||
Reference in New Issue
Block a user