본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] 데코레이터 7가지를 활용한 ML 실험 로깅 표준화 및 실행 시간 추적 방법

by Papa Martino V 2026. 4. 22.
728x90

Decorator
Decorator

 

머신러닝(ML) 연구와 개발 과정에서 가장 흔히 발생하는 문제는 "어떤 파라미터로 실행했을 때 이 결과가 나왔는가?"에 대한 기록 누락입니다. 수많은 실험(Trial)을 반복하다 보면 코드는 지저분해지고, 수동으로 작성하는 로그는 신뢰도를 잃기 마련입니다. 본 포스팅에서는 파이썬의 강력한 기능인 데코레이터(Decorator)를 활용하여, 모델 학습 코드의 수정 없이 실행 시간, 하이퍼파라미터, 하드웨어 상태를 표준화된 방식으로 자동 기록하는 해결 방안을 심도 있게 다룹니다.


1. ML 실험에서 데코레이터 기반 로깅이 필요한 이유

전통적인 로깅 방식은 함수 내부 여기저기에 print()logger.info()를 흩뿌려 놓습니다. 이는 코드 가독성을 해칠 뿐만 아니라, 새로운 모델을 테스트할 때마다 로깅 코드를 다시 복사해야 하는 번거로움을 초래합니다. 데코레이터를 통한 관점 지향 프로그래밍(AOP) 스타일을 적용하면 다음과 같은 차별화된 이점을 얻을 수 있습니다.

비교 항목 수동 로깅 (Manual) 데코레이터 기반 로깅 (Standardized)
코드 오염도 함수 내 비즈니스 로직과 혼재 로직과 완전히 분리 (관심사 분리)
유지보수성 로깅 형식 변경 시 모든 함수 수정 데코레이터 정의부 1곳만 수정
실수 가능성 특정 파라미터 누락 가능성 높음 함수 시그니처 자동 파싱으로 누락 없음
시간 측정 정밀도 측정 위치에 따라 오차 발생 함수 호출 직전/직후의 일관된 측정

2. 실무 즉시 적용 가능한 ML 실험 자동화 Example (7가지)

단순한 시간 출력을 넘어, 실제 ML 파이프라인에서 성능을 극대화할 수 있는 7가지 데코레이터 패턴입니다.

Example 1: 고정밀 실행 시간 및 파라미터 자동 기록기

가장 기본이 되는 데코레이터로, 함수에 전달된 모든 인자(args, kwargs)와 실행 시간을 JSON 형태로 로깅합니다.

import time
import functools
import logging

logging.basicConfig(level=logging.INFO)

def log_experiment(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.perf_counter()
        # 함수 시그니처와 파라미터 로깅
        logging.info(f"실험 시작: {func.__name__} | 파라미터: {kwargs}")
        
        result = func(*args, **kwargs)
        
        end_time = time.perf_counter()
        duration = end_time - start_time
        logging.info(f"실험 완료: {func.__name__} | 소요 시간: {duration:.4f}초")
        return result
    return wrapper

@log_experiment
def train_model(learning_rate=0.01, epochs=10):
    time.sleep(1) # 모델 학습 시뮬레이션
    return "Model Trained"
    

Example 2: MLflow 연동을 위한 자동 트래킹 데코레이터

MLflow와 같은 MLOps 툴을 사용할 때, 실험 관리 코드를 깔끔하게 래핑하는 방법입니다.

def mlflow_track(experiment_name):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            import mlflow
            mlflow.set_experiment(experiment_name)
            with mlflow.start_run():
                mlflow.log_params(kwargs)
                result = func(*args, **kwargs)
                if isinstance(result, dict): # 메트릭 반환 시 자동 기록
                    mlflow.log_metrics(result)
                return result
        return wrapper
    return decorator
    

Example 3: 예외 상황 발생 시의 하이퍼파라미터 스냅샷 저장

학습 도중 에러가 나면 어떤 설정에서 죽었는지 찾는 것이 고역입니다. 이를 해결하기 위한 에러 전용 로거입니다.

def trace_on_failure(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            with open("crash_report.log", "a") as f:
                f.write(f"Error in {func.__name__} with params {kwargs}: {str(e)}\n")
            raise e
    return wrapper

Example 4: GPU 메모리 점유율 동시 측정 데코레이터

학습 전후의 GPU 메모리 변화를 추적하여 메모리 누수나 배치 사이즈 적절성을 판단합니다.

import torch

def monitor_gpu(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        if torch.cuda.is_available():
            torch.cuda.empty_cache()
            before = torch.cuda.memory_allocated()
            result = func(*args, **kwargs)
            after = torch.cuda.memory_allocated()
            print(f"GPU Memory Delta: {(after - before) / 1024**2:.2f} MB")
            return result
        return func(*args, **kwargs)
    return wrapper

Example 5: 데이터 버전(Hash) 체크섬 로깅

데이터셋의 무결성을 확인하기 위해 입력 데이터의 해시값을 자동으로 로깅하는 방법입니다.

import hashlib

def log_data_version(func):
    @functools.wraps(func)
    def wrapper(data, *args, **kwargs):
        data_hash = hashlib.md5(str(data).encode()).hexdigest()
        logging.info(f"Data Version (MD5): {data_hash}")
        return func(data, *args, **kwargs)
    return wrapper

Example 6: 반복 실험을 위한 결과 캐싱 및 중복 실행 방지

동일한 파라미터로 이미 실행된 실험이 있다면 결과만 즉시 반환하여 컴퓨팅 자원을 아낍니다.

_experiment_cache = {}

def cache_trial(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        key = (func.__name__, frozenset(kwargs.items()))
        if key in _experiment_cache:
            logging.info("기존 실험 결과 발견. 캐시된 데이터를 반환합니다.")
            return _experiment_cache[key]
        result = func(*args, **kwargs)
        _experiment_cache[key] = result
        return result
    return wrapper

Example 7: 다중 데코레이터 조합을 이용한 통합 프레임워크

지금까지 정의한 데코레이터들을 중첩하여 완벽한 실험 로깅 환경을 구축합니다.

@log_experiment
@monitor_gpu
@trace_on_failure
def final_training_pipeline(batch_size=32, model_type='resnet'):
    # 실제 복잡한 학습 로직
    pass

3. 전문가의 분석: 왜 표준화가 중요한가?

ML 실험의 재현성(Reproducibility)은 데이터 과학의 핵심입니다. 수천 번의 실험 중에서 단 하나의 우수한 모델을 찾아냈을 때, 그 모델의 탄생 배경(학습 시간, 당시의 데이터셋 버전, 파라미터 조합)을 소급하여 파악할 수 없다면 그 실험은 실패한 것과 다름없습니다. 데코레이터는 개발자가 로깅이라는 '부수적인' 작업에 신경 쓰지 않고 '모델 아키텍처'라는 본질에 집중할 수 있도록 돕는 훌륭한 추상화 도구입니다.


4. 참고 문헌 및 출처

  • Python Software Foundation. "Function definitions - Decorators." docs.python.org.
  • MLflow Documentation. "Automatic Logging in MLflow." mlflow.org.
  • "Fluent Python" by Luciano Ramalho - Chapter on Decorators and Closures.
  • PyTorch Documentation. "Cuda Memory Management." pytorch.org.
728x90