071 CFO(Cost-Frugal Optimization) 이해
키워드: CFO, 비용 효율적 최적화, 로컬 탐색
개요
CFO(Cost-Frugal Optimization)는 FLAML에서 개발한 비용 효율적인 하이퍼파라미터 최적화 알고리즘입니다. 학습 비용을 고려하면서 효과적으로 탐색하는 CFO의 원리를 알아봅니다.
실습 환경
- Python 버전: 3.11 권장
- 필요 패키지:
flaml[automl]
pip install flaml[automl] pandas numpy matplotlib
CFO란?
import numpy as np
import pandas as pd
from flaml import AutoML
print("CFO (Cost-Frugal Optimization) 핵심 개념:")
print("=" * 50)
concepts = {
'특징': ['비용 인식', '점진적 탐색', '랜덤화 로컬 탐색'],
'설명': [
'학습 비용이 낮은 설정 우선 평가',
'현재 최적에서 점진적으로 이동',
'확률적으로 탐색 방향 결정'
],
'장점': [
'제한된 시간에 더 많은 설정 평가',
'과도한 점프 없이 안정적 탐색',
'지역 최적에 빠지지 않음'
]
}
print(pd.DataFrame(concepts).to_string(index=False))
비용 인식 탐색
import matplotlib.pyplot as plt
# 071 비용 vs 성능 트레이드오프 시각화
np.random.seed(42)
# 071 가상의 설정들
configs = []
for n_est in range(50, 500, 50):
for depth in range(3, 15, 2):
cost = n_est * depth # 비용 추정
# 성능은 일정 수준까지 증가 후 포화
performance = 0.7 + 0.2 * (1 - np.exp(-n_est/200)) * (1 - np.exp(-depth/8))
performance += np.random.randn() * 0.02
configs.append({
'n_estimators': n_est,
'max_depth': depth,
'cost': cost,
'performance': min(performance, 0.95)
})
df_configs = pd.DataFrame(configs)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 071 비용 vs 성능
ax = axes[0]
scatter = ax.scatter(df_configs['cost'], df_configs['performance'],
c=df_configs['n_estimators'], cmap='viridis', alpha=0.7)
ax.set_xlabel('Training Cost')
ax.set_ylabel('Performance')
ax.set_title('Cost vs Performance Trade-off')
plt.colorbar(scatter, ax=ax, label='n_estimators')
# 071 비용 대비 성능 효율
ax = axes[1]
df_configs['efficiency'] = df_configs['performance'] / df_configs['cost'] * 1000
scatter2 = ax.scatter(df_configs['cost'], df_configs['efficiency'],
c=df_configs['max_depth'], cmap='plasma', alpha=0.7)
ax.set_xlabel('Training Cost')
ax.set_ylabel('Efficiency (Performance/Cost)')
ax.set_title('Cost Efficiency')
plt.colorbar(scatter2, ax=ax, label='max_depth')
plt.tight_layout()
plt.show()
print("\nCFO 전략:")
print(" 1. 저비용 설정에서 시작")
print(" 2. 성능 향상이 비용 증가보다 클 때만 이동")
print(" 3. 효율적인 설정 경로 탐색")
CFO 작동 원리
print("CFO 알고리즘 단계:")
print("=" * 50)
steps = {
'단계': ['1', '2', '3', '4', '5'],
'이름': [
'초기화',
'이웃 생성',
'비용 계산',
'평가 및 선택',
'반복'
],
'설명': [
'저비용 설정에서 시작 (low_cost_init_value)',
'현재 설정 주변의 후보 생성',
'각 후보의 예상 학습 비용 계산',
'비용 대비 성능 향상 고려하여 선택',
'종료 조건까지 2-4 반복'
]
}
print(pd.DataFrame(steps).to_string(index=False))
점진적 탐색 시각화
def simulate_cfo_search(start, n_steps=20):
"""CFO 탐색 시뮬레이션"""
np.random.seed(42)
path = [start]
current = list(start)
for _ in range(n_steps):
# 랜덤 방향으로 작은 이동
direction = np.random.choice([-1, 0, 1], size=2)
step_size = np.random.uniform(0.5, 1.5, size=2)
new_pos = [
max(0, min(10, current[0] + direction[0] * step_size[0])),
max(0, min(10, current[1] + direction[1] * step_size[1]))
]
# 비용 고려한 수락 확률
cost_increase = sum(new_pos) - sum(current)
if cost_increase <= 0 or np.random.random() < 0.3:
current = new_pos
path.append(tuple(current))
return path
# 071 CFO vs Random Search 비교
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# 071 CFO (점진적 탐색)
ax = axes[0]
cfo_path = simulate_cfo_search((1, 1), 30)
cfo_x, cfo_y = zip(*cfo_path)
ax.plot(cfo_x, cfo_y, 'b-', alpha=0.5, linewidth=1)
ax.scatter(cfo_x, cfo_y, c=range(len(cfo_path)), cmap='Blues', s=30)
ax.scatter(cfo_x[0], cfo_y[0], c='green', s=100, marker='o', label='Start')
ax.scatter(cfo_x[-1], cfo_y[-1], c='red', s=100, marker='*', label='End')
ax.set_xlabel('Parameter 1 (cost related)')
ax.set_ylabel('Parameter 2 (cost related)')
ax.set_title('CFO: Gradual Search')
ax.legend()
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
# 071 Random Search
ax = axes[1]
random_x = np.random.uniform(0, 10, 30)
random_y = np.random.uniform(0, 10, 30)
ax.scatter(random_x, random_y, c=range(30), cmap='Oranges', s=30)
ax.set_xlabel('Parameter 1')
ax.set_ylabel('Parameter 2')
ax.set_title('Random Search: Scattered')
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
plt.tight_layout()
plt.show()
print("CFO 특징:")
print(" - 시작점에서 점진적으로 이동")
print(" - 비용이 급격히 증가하는 방향 회피")
print(" - 효율적인 경로로 최적점 접근")
FLAML에서 CFO 활용
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
# 071 데이터 준비
X, y = make_classification(n_samples=3000, 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)
# 071 low_cost_init_value 설정으로 CFO 효과 극대화
custom_hp = {
'lgbm': {
'n_estimators': {
'domain': list(range(10, 500, 10)),
'init_value': 100,
'low_cost_init_value': 10, # 저비용 시작점
},
'num_leaves': {
'domain': list(range(4, 256, 4)),
'init_value': 31,
'low_cost_init_value': 4, # 저비용 시작점
},
'learning_rate': {
'domain': np.logspace(-3, 0, 50).tolist(),
'init_value': 0.1,
},
}
}
automl = AutoML()
automl.fit(
X_train, y_train,
task="classification",
time_budget=60,
estimator_list=['lgbm'],
custom_hp=custom_hp,
verbose=2
)
print(f"\n최적 설정: {automl.best_config}")
print(f"정확도: {1 - automl.best_loss:.4f}")
비용 함수 이해
def estimate_training_cost(config, n_samples):
"""학습 비용 추정 함수"""
# 트리 기반 모델 비용 추정
n_estimators = config.get('n_estimators', 100)
max_depth = config.get('max_depth', 6)
num_leaves = config.get('num_leaves', 31)
# 비용 = 트리 수 × 깊이(또는 리프) × 샘플 수
if max_depth:
complexity = min(2 ** max_depth, num_leaves)
else:
complexity = num_leaves
cost = n_estimators * complexity * np.log2(n_samples + 1)
return cost
# 071 비용 비교
configs_to_compare = [
{'n_estimators': 10, 'num_leaves': 4},
{'n_estimators': 50, 'num_leaves': 31},
{'n_estimators': 100, 'num_leaves': 64},
{'n_estimators': 300, 'num_leaves': 128},
]
print("설정별 비용 추정 (n_samples=3000):")
for config in configs_to_compare:
cost = estimate_training_cost(config, 3000)
print(f" {config}: {cost:,.0f}")
CFO 파라미터 상호작용
print("\nCFO에서 파라미터 상호작용:")
print("=" * 50)
interactions = {
'파라미터': ['n_estimators', 'max_depth', 'learning_rate', 'subsample'],
'비용 영향': ['선형 증가', '지수 증가', '없음', '선형 감소'],
'CFO 전략': [
'작은 값에서 시작, 점진적 증가',
'작은 값에서 시작, 신중히 증가',
'독립적으로 탐색',
'높은 값에서 시작 가능'
]
}
print(pd.DataFrame(interactions).to_string(index=False))
조기 종료와 CFO
print("\nCFO + 조기 종료 시너지:")
print("=" * 50)
synergy = {
'기법': ['CFO 저비용 시작', 'Early Stopping', '결합 효과'],
'설명': [
'n_estimators=10에서 시작',
'검증 성능 포화 시 학습 중단',
'필요한 만큼만 학습, 과적합 방지'
],
'장점': [
'빠른 초기 평가',
'불필요한 학습 방지',
'최대 효율로 시간 활용'
]
}
print(pd.DataFrame(synergy).to_string(index=False))
# 071 조기 종료 설정 예시
automl_early = AutoML()
automl_early.fit(
X_train, y_train,
task="classification",
time_budget=60,
estimator_list=['lgbm'],
early_stop=True, # 조기 종료 활성화
verbose=1
)
print(f"\n조기 종료 적용 결과: {automl_early.best_config}")
CFO vs 다른 로컬 탐색
comparison = {
'알고리즘': ['Hill Climbing', 'Simulated Annealing', 'CFO'],
'탐색 방식': ['가장 좋은 이웃', '확률적 수락', '비용 인식 이동'],
'비용 고려': ['없음', '없음', '있음'],
'지역 최적 탈출': ['어려움', '가능', '가능'],
'수렴 속도': ['빠름', '중간', '빠름']
}
print("\nCFO vs 다른 로컬 탐색 알고리즘:")
print(pd.DataFrame(comparison).to_string(index=False))
탐색 진행 모니터링
def monitor_cfo_progress(automl):
"""CFO 탐색 진행 모니터링"""
if not hasattr(automl, 'config_history') or not automl.config_history:
print("탐색 히스토리가 없습니다.")
return
history = automl.config_history
# 비용 관련 파라미터 추적
n_estimators_history = []
loss_history = []
for iter_id, info in history.items():
config = info.get('config', {})
if 'n_estimators' in config:
n_estimators_history.append(config['n_estimators'])
loss = info.get('val_loss')
if loss is not None:
loss_history.append(loss)
if n_estimators_history:
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# n_estimators 변화
ax = axes[0]
ax.plot(n_estimators_history, 'b-o', markersize=4)
ax.set_xlabel('Iteration')
ax.set_ylabel('n_estimators')
ax.set_title('CFO: n_estimators Evolution')
# Loss 변화
ax = axes[1]
ax.plot(loss_history, 'r-o', markersize=4)
ax.set_xlabel('Iteration')
ax.set_ylabel('Validation Loss')
ax.set_title('CFO: Loss Evolution')
plt.tight_layout()
plt.show()
print(f"\nn_estimators 범위: {min(n_estimators_history)} ~ {max(n_estimators_history)}")
print(f"최종 n_estimators: {n_estimators_history[-1]}")
monitor_cfo_progress(automl)
실전 권장사항
recommendations = {
'상황': [
'시간 제약 심함',
'대용량 데이터',
'파라미터 범위 넓음',
'비용 민감'
],
'권장 설정': [
'low_cost_init_value 필수',
'subsample < 1.0 시작',
'범위를 좁히거나 단계 크게',
'early_stop=True'
],
'예상 효과': [
'초기 평가 속도 10x 이상',
'메모리/시간 절약',
'탐색 효율 향상',
'불필요한 학습 방지'
]
}
print("\nCFO 실전 권장사항:")
print(pd.DataFrame(recommendations).to_string(index=False))
정리
- CFO: Cost-Frugal Optimization, 비용 효율적 탐색
- 저비용 시작: low_cost_init_value로 빠른 초기 평가
- 점진적 탐색: 현재 위치에서 조금씩 이동
- 비용 인식: 학습 비용 고려한 탐색 방향 결정
- 조기 종료 연계: 불필요한 학습 방지
- BlendSearch에서 로컬 탐색 담당
다음 글 예고
다음 글에서는 하이퍼파라미터 튜닝 심화에 대해 알아보겠습니다. FLAML의 다양한 튜닝 옵션과 고급 설정을 다룹니다.
FLAML AutoML 마스터 시리즈 #071