본문으로 건너뛰기

070 BlendSearch 알고리즘 이해

키워드: BlendSearch, 탐색 알고리즘, 하이퍼파라미터 최적화

개요

BlendSearch는 FLAML의 핵심 하이퍼파라미터 탐색 알고리즘입니다. 글로벌 탐색과 로컬 탐색을 결합하여 효율적으로 최적 설정을 찾습니다. 이 글에서는 BlendSearch의 원리와 활용법을 알아봅니다.

실습 환경

  • Python 버전: 3.11 권장
  • 필요 패키지: flaml[automl]
pip install flaml[automl] pandas numpy matplotlib

BlendSearch란?

import numpy as np
import pandas as pd
from flaml import AutoML, tune

print("BlendSearch 핵심 개념:")
print("=" * 50)

concepts = {
'구성요소': ['글로벌 탐색', '로컬 탐색', '경제적 평가'],
'역할': [
'전체 탐색 공간 탐험 (Exploration)',
'유망 영역 집중 (Exploitation)',
'저비용 설정 우선 평가'
],
'기술': [
'Bayesian Optimization',
'CFO (Cost-Frugal Optimization)',
'Early stopping + 비용 인식'
]
}

print(pd.DataFrame(concepts).to_string(index=False))

글로벌 vs 로컬 탐색

import matplotlib.pyplot as plt

# 070 탐색 전략 시각화
np.random.seed(42)

fig, axes = plt.subplots(1, 3, figsize=(15, 5))

# 1. 글로벌 탐색 (Random/Bayesian)
ax = axes[0]
x_global = np.random.uniform(0, 10, 50)
y_global = np.random.uniform(0, 10, 50)
ax.scatter(x_global, y_global, alpha=0.6)
ax.set_title('Global Search\n(Exploration)')
ax.set_xlabel('Param 1')
ax.set_ylabel('Param 2')

# 2. 로컬 탐색 (CFO)
ax = axes[1]
center = (5, 5)
x_local = center[0] + np.random.randn(50) * 0.5
y_local = center[1] + np.random.randn(50) * 0.5
ax.scatter(x_local, y_local, alpha=0.6, color='orange')
ax.scatter(*center, color='red', s=100, marker='*', label='Best found')
ax.set_title('Local Search\n(Exploitation)')
ax.set_xlabel('Param 1')
ax.set_ylabel('Param 2')
ax.legend()

# 3. BlendSearch (결합)
ax = axes[2]
# 070 글로벌 포인트
x_blend_g = np.random.uniform(0, 10, 20)
y_blend_g = np.random.uniform(0, 10, 20)
# 070 로컬 포인트 (여러 유망 지역)
centers = [(3, 7), (7, 3)]
for c in centers:
x_l = c[0] + np.random.randn(15) * 0.5
y_l = c[1] + np.random.randn(15) * 0.5
ax.scatter(x_l, y_l, alpha=0.6, color='orange')
ax.scatter(x_blend_g, y_blend_g, alpha=0.4, color='blue', label='Global')
ax.scatter([c[0] for c in centers], [c[1] for c in centers],
color='red', s=100, marker='*', label='Local centers')
ax.set_title('BlendSearch\n(Global + Local)')
ax.set_xlabel('Param 1')
ax.set_ylabel('Param 2')
ax.legend()

plt.tight_layout()
plt.show()

print("\nBlendSearch 장점:")
print(" - 글로벌: 전체 공간 탐험으로 좋은 영역 발견")
print(" - 로컬: 발견된 영역에서 세밀한 최적화")
print(" - 결합: 두 장점을 모두 활용")

BlendSearch 작동 원리

print("BlendSearch 작동 순서:")
print("=" * 50)

steps = {
'단계': ['1. 초기화', '2. 글로벌 탐색', '3. 유망 영역 선정', '4. 로컬 탐색', '5. 반복'],
'설명': [
'저비용 초기 설정으로 시작',
'Bayesian Optimization으로 전체 공간 탐색',
'좋은 성능을 보인 영역 식별',
'CFO로 유망 영역 집중 탐색',
'시간 예산까지 글로벌/로컬 교대'
],
'비용': ['낮음', '중간', '-', '낮음~중간', '동적']
}

print(pd.DataFrame(steps).to_string(index=False))

FLAML에서 BlendSearch 사용

from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

# 070 데이터 준비
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)

# 070 기본 FLAML (BlendSearch 자동 사용)
automl = AutoML()
automl.fit(
X_train, y_train,
task="classification",
time_budget=60,
verbose=2 # 상세 로그
)

print(f"\n최적 모델: {automl.best_estimator}")
print(f"최적 설정: {automl.best_config}")
print(f"탐색 반복 수: {len(automl.config_history)}")

탐색 히스토리 분석

def analyze_search_history(automl):
"""BlendSearch 탐색 히스토리 분석"""
history = automl.config_history

if not history:
print("탐색 히스토리가 비어있습니다.")
return

# 히스토리 데이터 추출
iterations = []
losses = []
estimators = []

for iter_id, config_info in history.items():
iterations.append(iter_id)
losses.append(config_info.get('val_loss', None))
estimators.append(config_info.get('learner', 'unknown'))

# 시각화
fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# 손실 추이
valid_losses = [l for l in losses if l is not None]
if valid_losses:
ax = axes[0]
ax.plot(range(len(valid_losses)), valid_losses, 'b-', alpha=0.7)
ax.scatter(range(len(valid_losses)), valid_losses, c='blue', s=20)

# 최소값 표시
min_idx = np.argmin(valid_losses)
ax.scatter(min_idx, valid_losses[min_idx], c='red', s=100, marker='*',
zorder=5, label=f'Best: {valid_losses[min_idx]:.4f}')
ax.set_xlabel('Iteration')
ax.set_ylabel('Validation Loss')
ax.set_title('Search Progress')
ax.legend()

# 모델별 탐색 횟수
ax = axes[1]
from collections import Counter
est_counts = Counter(estimators)
ax.bar(est_counts.keys(), est_counts.values())
ax.set_xlabel('Estimator')
ax.set_ylabel('Count')
ax.set_title('Estimator Exploration Count')
plt.xticks(rotation=45)

plt.tight_layout()
plt.show()

print(f"\n탐색 통계:")
print(f" 총 반복: {len(iterations)}")
print(f" 최적 손실: {min(valid_losses):.4f}")
print(f" 탐색된 모델: {dict(est_counts)}")

analyze_search_history(automl)

tune.run으로 BlendSearch 직접 사용

from flaml import tune
from flaml.tune.searcher import BlendSearch
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

def evaluate_config(config):
"""설정 평가 함수"""
model = RandomForestClassifier(
n_estimators=config['n_estimators'],
max_depth=config['max_depth'],
min_samples_split=config['min_samples_split'],
random_state=42
)

scores = cross_val_score(model, X_train, y_train, cv=3, scoring='accuracy')
return {'accuracy': scores.mean()}

# 070 탐색 공간
search_space = {
'n_estimators': tune.randint(50, 300),
'max_depth': tune.randint(3, 20),
'min_samples_split': tune.randint(2, 20),
}

# 070 BlendSearch로 최적화
analysis = tune.run(
evaluate_config,
config=search_space,
metric='accuracy',
mode='max',
num_samples=50,
time_budget_s=60,
verbose=1,
)

print(f"\n최적 설정: {analysis.best_config}")
print(f"최적 정확도: {analysis.best_result['accuracy']:.4f}")

BlendSearch 파라미터 조정

# 070 BlendSearch 세부 설정
from flaml.tune.searcher.blendsearch import BlendSearch

# 070 커스텀 BlendSearch 설정
searcher = BlendSearch(
metric='accuracy',
mode='max',
space=search_space,
low_cost_partial_config={
'n_estimators': 50, # 저비용 초기값
'max_depth': 3,
},
points_to_evaluate=[
{'n_estimators': 100, 'max_depth': 6, 'min_samples_split': 2}, # 알려진 좋은 설정
],
)

analysis_custom = tune.run(
evaluate_config,
config=search_space,
search_alg=searcher,
num_samples=30,
time_budget_s=60,
)

print(f"커스텀 BlendSearch 결과: {analysis_custom.best_config}")

글로벌/로컬 비율 조정

print("BlendSearch 비율 조정:")
print("=" * 50)

ratios = {
'설정': ['기본', '탐험 중심', '활용 중심'],
'글로벌:로컬': ['자동 조정', '높은 글로벌', '높은 로컬'],
'적합한 상황': [
'일반적인 경우',
'탐색 공간이 크거나 불확실할 때',
'이미 좋은 영역을 알고 있을 때'
],
'방법': [
'FLAML 기본 사용',
'num_samples 증가',
'points_to_evaluate 활용'
]
}

print(pd.DataFrame(ratios).to_string(index=False))

비용 인식 탐색

print("\n비용 인식 탐색 (Cost-Aware Search):")
print("=" * 50)

# 070 비용 함수 예시
def get_training_cost(config):
"""학습 비용 추정"""
n_est = config.get('n_estimators', 100)
depth = config.get('max_depth', 10)
# 트리 수와 깊이에 비례하는 비용
return n_est * (2 ** min(depth, 10))

# 070 비용 순 정렬 예시
configs = [
{'n_estimators': 50, 'max_depth': 3},
{'n_estimators': 100, 'max_depth': 6},
{'n_estimators': 200, 'max_depth': 10},
{'n_estimators': 300, 'max_depth': 15},
]

costs = [(c, get_training_cost(c)) for c in configs]
costs.sort(key=lambda x: x[1])

print("설정별 추정 비용:")
for config, cost in costs:
print(f" {config}: {cost:,}")

print("\nBlendSearch는 저비용 설정을 먼저 평가하여 빠르게 기준선 확보")

BlendSearch vs 다른 알고리즘

comparison = {
'알고리즘': ['Random Search', 'Grid Search', 'Bayesian Opt', 'BlendSearch'],
'탐색 전략': ['무작위', '격자 기반', '확률 모델', '글로벌+로컬'],
'효율성': ['낮음', '매우 낮음', '높음', '매우 높음'],
'비용 인식': ['없음', '없음', '부분적', '있음'],
'조기 종료': ['없음', '없음', '가능', '내장']
}

print("\nBlendSearch vs 다른 알고리즘:")
print(pd.DataFrame(comparison).to_string(index=False))

실전 팁

tips = {
'상황': [
'시간 제약 심함',
'큰 탐색 공간',
'도메인 지식 있음',
'재현성 필요'
],
'권장 설정': [
'low_cost_init_value 설정',
'time_budget 충분히 확보',
'points_to_evaluate 활용',
'seed 고정'
],
'효과': [
'빠른 기준선 확보',
'충분한 탐색',
'좋은 영역에서 시작',
'결과 재현 가능'
]
}

print("\nBlendSearch 실전 팁:")
print(pd.DataFrame(tips).to_string(index=False))

디버깅과 모니터링

# 070 verbose로 탐색 과정 확인
automl_debug = AutoML()
automl_debug.fit(
X_train, y_train,
task="classification",
time_budget=30,
verbose=3, # 최대 상세도
)

# 070 탐색 로그 분석
print("\n탐색 로그 분석:")
if hasattr(automl_debug, 'config_history'):
for iter_id, info in list(automl_debug.config_history.items())[:5]:
print(f"\nIteration {iter_id}:")
print(f" Learner: {info.get('learner', 'N/A')}")
print(f" Val Loss: {info.get('val_loss', 'N/A')}")

정리

  • BlendSearch: 글로벌 탐색 + 로컬 탐색 결합
  • 글로벌 탐색: Bayesian Optimization으로 전체 공간 탐험
  • 로컬 탐색: CFO로 유망 영역 집중 탐색
  • 비용 인식: 저비용 설정 우선 평가
  • 자동 균형: 탐험과 활용 자동 조절
  • 효율성: 제한된 시간에 좋은 설정 빠르게 발견

다음 글 예고

다음 글에서는 CFO(Cost-Frugal Optimization) 이해에 대해 알아보겠습니다. BlendSearch의 로컬 탐색에 사용되는 CFO 알고리즘을 상세히 다룹니다.


FLAML AutoML 마스터 시리즈 #070