079 AutoML 결과 재현성 확보
키워드: 재현성, reproducibility, seed, 랜덤 시드
개요
머신러닝에서 재현성(Reproducibility)은 동일한 조건에서 동일한 결과를 얻을 수 있는 능력입니다. 연구, 디버깅, 프로덕션 배포에서 재현성은 매우 중요합니다. 이 글에서는 FLAML에서 재현 가능한 결과를 얻는 방법을 알아봅니다.
실습 환경
- Python 버전: 3.11 권장
- 필요 패키지:
flaml[automl]
pip install flaml[automl] pandas numpy
재현성이 중요한 이유
import numpy as np
import pandas as pd
from flaml import AutoML
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
reasons = {
'상황': ['연구/논문', '디버깅', '프로덕션', '협업'],
'필요성': [
'실험 결과 검증 가능',
'문제 원인 추적 가능',
'배포 전후 동일 성능',
'팀원 간 결과 공유'
],
'영향': [
'신뢰성 확보',
'빠른 문제 해결',
'안정적 서비스',
'효율적 협업'
]
}
print("재현성이 중요한 이유:")
print(pd.DataFrame(reasons).to_string(index=False))
랜덤 시드 설정
# 079 전역 랜덤 시드 설정
import random
import numpy as np
def set_global_seed(seed=42):
"""모든 랜덤 시드를 일관되게 설정"""
random.seed(seed)
np.random.seed(seed)
# PyTorch가 있는 경우
try:
import torch
torch.manual_seed(seed)
if torch.cuda.is_available():
torch.cuda.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
except ImportError:
pass
# TensorFlow가 있는 경우
try:
import tensorflow as tf
tf.random.set_seed(seed)
except ImportError:
pass
set_global_seed(42)
print("전역 랜덤 시드 설정 완료: 42")
# 079 데이터 생성 (시드 사용)
X, y = make_classification(
n_samples=2000,
n_features=20,
n_informative=10,
random_state=42 # 항상 시드 지정
)
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
random_state=42 # 항상 시드 지정
)
print(f"데이터 형태: {X_train.shape}")
FLAML 시드 설정
# 079 FLAML AutoML 시드 설정
automl = AutoML()
automl.fit(
X_train, y_train,
task="classification",
time_budget=30,
seed=42, # FLAML 시드
verbose=1
)
print(f"\n최적 모델: {automl.best_estimator}")
print(f"최적 설정: {automl.best_config}")
print(f"최적 손실: {automl.best_loss:.4f}")
재현성 검증
def verify_reproducibility(X_train, y_train, seed=42, n_runs=3):
"""재현성 검증"""
results = []
for run in range(n_runs):
# 전역 시드 재설정
set_global_seed(seed)
automl = AutoML()
automl.fit(
X_train, y_train,
task="classification",
time_budget=20,
seed=seed,
verbose=0
)
results.append({
'run': run + 1,
'best_estimator': automl.best_estimator,
'best_loss': automl.best_loss,
'n_trials': len(automl.config_history)
})
print(f"Run {run + 1}: {automl.best_estimator}, loss={automl.best_loss:.4f}")
# 재현성 확인
losses = [r['best_loss'] for r in results]
estimators = [r['best_estimator'] for r in results]
is_reproducible = len(set(estimators)) == 1 and np.std(losses) < 1e-6
print(f"\n재현성 검증: {'통과' if is_reproducible else '실패'}")
print(f" 모델 일관성: {len(set(estimators)) == 1}")
print(f" 손실 표준편차: {np.std(losses):.6f}")
return is_reproducible, results
# 079 재현성 검증 실행
is_reproducible, results = verify_reproducibility(X_train, y_train)
환경 정보 저장
import sys
import platform
import json
from datetime import datetime
def save_environment_info(filepath="environment_info.json"):
"""환경 정보 저장"""
env_info = {
'timestamp': datetime.now().isoformat(),
'python_version': sys.version,
'platform': platform.platform(),
'processor': platform.processor(),
'packages': {}
}
# 주요 패키지 버전
packages = ['flaml', 'numpy', 'pandas', 'scikit-learn', 'lightgbm', 'xgboost']
for pkg in packages:
try:
module = __import__(pkg.replace('-', '_'))
env_info['packages'][pkg] = getattr(module, '__version__', 'unknown')
except ImportError:
env_info['packages'][pkg] = 'not installed'
# 파일 저장
with open(filepath, 'w') as f:
json.dump(env_info, f, indent=2)
print(f"환경 정보 저장: {filepath}")
return env_info
# 079 환경 정보 저장
env_info = save_environment_info()
print("\n환경 정보:")
for key, value in env_info['packages'].items():
print(f" {key}: {value}")
모델 및 설정 저장
import pickle
import joblib
def save_reproducible_model(automl, filepath_prefix="model"):
"""재현 가능한 모델 저장"""
# 1. 모델 저장
model_path = f"{filepath_prefix}_model.pkl"
joblib.dump(automl, model_path)
# 2. 설정 저장
config_path = f"{filepath_prefix}_config.json"
config = {
'best_estimator': automl.best_estimator,
'best_config': automl.best_config,
'best_loss': automl.best_loss,
'seed': 42
}
with open(config_path, 'w') as f:
json.dump(config, f, indent=2)
# 3. 학습 이력 저장
history_path = f"{filepath_prefix}_history.json"
# config_history는 직렬화를 위해 변환
history = {}
for k, v in automl.config_history.items():
history[str(k)] = {key: str(val) if not isinstance(val, (int, float, str, bool, type(None))) else val
for key, val in v.items()}
with open(history_path, 'w') as f:
json.dump(history, f, indent=2)
print(f"모델 저장: {model_path}")
print(f"설정 저장: {config_path}")
print(f"이력 저장: {history_path}")
return model_path, config_path, history_path
# 079 모델 저장
paths = save_reproducible_model(automl, "flaml_reproducible")
모델 재로드 및 검증
def load_and_verify_model(model_path, config_path, X_test, y_test):
"""모델 로드 및 검증"""
# 모델 로드
loaded_model = joblib.load(model_path)
# 설정 로드
with open(config_path, 'r') as f:
saved_config = json.load(f)
# 예측 수행
y_pred = loaded_model.predict(X_test)
from sklearn.metrics import accuracy_score
accuracy = accuracy_score(y_test, y_pred)
print(f"로드된 모델: {saved_config['best_estimator']}")
print(f"저장된 설정: {saved_config['best_config']}")
print(f"테스트 정확도: {accuracy:.4f}")
return loaded_model, accuracy
# 079 모델 로드 및 검증
loaded_model, accuracy = load_and_verify_model(
"flaml_reproducible_model.pkl",
"flaml_reproducible_config.json",
X_test, y_test
)
데이터 버전 관리
import hashlib
def get_data_hash(X, y):
"""데이터 해시 생성"""
data = np.concatenate([X.flatten(), y.flatten()])
return hashlib.md5(data.tobytes()).hexdigest()
def save_data_with_version(X, y, filepath_prefix="data"):
"""버전 정보와 함께 데이터 저장"""
data_hash = get_data_hash(X, y)
# 데이터 저장
np.savez(
f"{filepath_prefix}.npz",
X=X,
y=y
)
# 메타데이터 저장
metadata = {
'shape_X': X.shape,
'shape_y': y.shape,
'hash': data_hash,
'timestamp': datetime.now().isoformat()
}
with open(f"{filepath_prefix}_metadata.json", 'w') as f:
json.dump(metadata, f, indent=2)
print(f"데이터 저장: {filepath_prefix}.npz")
print(f"데이터 해시: {data_hash}")
return data_hash
# 079 데이터 버전 저장
data_hash = save_data_with_version(X_train, y_train, "train_data")
완전한 재현성 체크리스트
checklist = {
'항목': [
'전역 시드',
'FLAML 시드',
'데이터 분할 시드',
'환경 정보',
'패키지 버전',
'데이터 해시',
'모델 저장',
'설정 저장'
],
'방법': [
'set_global_seed(42)',
'automl.fit(seed=42)',
'train_test_split(random_state=42)',
'platform, sys 정보 저장',
'pip freeze > requirements.txt',
'hashlib.md5()',
'joblib.dump()',
'json.dump(config)'
],
'필수': ['Yes', 'Yes', 'Yes', 'Recommended', 'Recommended', 'Recommended', 'Yes', 'Yes']
}
print("\n재현성 체크리스트:")
print(pd.DataFrame(checklist).to_string(index=False))
실험 관리 클래스
class ExperimentManager:
"""재현 가능한 실험 관리"""
def __init__(self, experiment_name, seed=42):
self.experiment_name = experiment_name
self.seed = seed
self.timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
self.results = {}
def setup(self):
"""실험 환경 설정"""
set_global_seed(self.seed)
self.results['seed'] = self.seed
self.results['timestamp'] = self.timestamp
self.results['environment'] = {
'python': sys.version,
'platform': platform.platform()
}
print(f"실험 '{self.experiment_name}' 설정 완료 (seed={self.seed})")
def run_automl(self, X_train, y_train, time_budget=30):
"""AutoML 실행"""
automl = AutoML()
automl.fit(
X_train, y_train,
task="classification",
time_budget=time_budget,
seed=self.seed,
verbose=1
)
self.results['model'] = {
'best_estimator': automl.best_estimator,
'best_loss': automl.best_loss,
'n_trials': len(automl.config_history)
}
return automl
def save(self, filepath=None):
"""실험 결과 저장"""
if filepath is None:
filepath = f"{self.experiment_name}_{self.timestamp}.json"
# 직렬화 가능한 형태로 변환
serializable = {}
for k, v in self.results.items():
if isinstance(v, dict):
serializable[k] = {str(kk): str(vv) for kk, vv in v.items()}
else:
serializable[k] = str(v)
with open(filepath, 'w') as f:
json.dump(serializable, f, indent=2)
print(f"실험 결과 저장: {filepath}")
return filepath
# 079 실험 관리자 사용
experiment = ExperimentManager("flaml_classification", seed=42)
experiment.setup()
automl_exp = experiment.run_automl(X_train, y_train, time_budget=20)
experiment.save()
재현성 문제 해결
troubleshooting = {
'문제': [
'다른 결과',
'패키지 버전 차이',
'OS 차이',
'부동소수점 오차'
],
'원인': [
'시드 미설정',
'API 변경',
'시스템 라이브러리 차이',
'CPU/GPU 연산 차이'
],
'해결책': [
'모든 시드 명시적 설정',
'requirements.txt로 버전 고정',
'Docker 컨테이너 사용',
'허용 오차(tolerance) 설정'
]
}
print("\n재현성 문제 해결:")
print(pd.DataFrame(troubleshooting).to_string(index=False))
Docker를 활용한 완벽한 재현성
dockerfile_content = """
# 079 Dockerfile for reproducible FLAML experiments
FROM python:3.11-slim
WORKDIR /app
# 079 패키지 설치
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 079 코드 복사
COPY . .
# 079 환경 변수
ENV PYTHONHASHSEED=42
CMD ["python", "train.py"]
"""
print("Docker를 사용한 재현성 확보:")
print(dockerfile_content)
print("\n실행 방법:")
print(" docker build -t flaml-experiment .")
print(" docker run flaml-experiment")
정리
- 전역 시드: random, numpy, torch, tensorflow 모두 설정
- FLAML 시드: fit(seed=42)로 명시적 지정
- 데이터 시드: train_test_split(random_state=42)
- 환경 저장: 패키지 버전, 시스템 정보 기록
- 모델 저장: joblib으로 전체 상태 저장
- Docker: 완벽한 재현성을 위한 컨테이너화
다음 글 예고
다음 글에서는 고급 기능 총정리로 Part 5를 마무리합니다. 지금까지 배운 고급 기능들을 종합하고 활용 가이드를 제공합니다.
FLAML AutoML 마스터 시리즈 #079