Motivation
- 기존의 하이퍼파라미터 튜닝 방식은 주로 수동적이고 시간이 많이 소요되었습니다.
- 그리드 서치(Grid Search)나 랜덤 서치(Random Search)는 단순하지만, 고차원의 하이퍼파라미터 공간에서는 비효율적입니다.
- HyperOpt는 베이지안 최적화(Bayesian Optimization)와 같은 고급 기법을 통해 하이퍼파라미터 최적화를 보다 효율적으로 수행할 수 있게 합니다.
Pros & Cons
Pros
- 효율적인 탐색: HyperOpt는 베이지안 최적화와 같은 고급 기법을 사용하여 하이퍼파라미터 공간을 효율적으로 탐색합니다.
- 다양한 검색 알고리즘: TPE(Tree-structured Parzen Estimator), 랜덤 서치, 그리드 서치 등 다양한 검색 알고리즘을 지원합니다
- 유연성: 하이퍼파라미터 공간을 유연하게 정의할 수 있으며, 사용자 정의 목표 함수도 쉽게 사용할 수 있습니다.
Cons
- 설정 복잡성: 초기 설정이 다소 복잡할 수 있으며, 사용자 정의 목표 함수를 작성해야 합니다.
- 성능 의존성: 최적화 성능은 설정된 검색 공간과 알고리즘에 크게 의존합니다.
- 시간 소요: 고차원의 하이퍼파라미터 공간을 탐색할 때 시간이 많이 소요될 수 있습니다.
Alternatives
- Optuna: HyperOpt와 유사한 기능을 제공하는 또 다른 하이퍼파라미터 최적화 라이브러리로, 직관적인 API와 강력한 기능을 제공합니다.
- Scikit-Optimize: Scikit-Learn과 잘 통합되어 사용하기 쉽고, 다양한 최적화 알고리즘을 지원합니다.
- Ray Tune: 분산 머신러닝을 지원하는 Ray 프레임워크의 하이퍼파라미터 튜닝 라이브러리로, 대규모 분산 환경에서 효율적으로 최적화를 수행할 수 있습니다.
샘플 코드
import numpy as np
import pandas as pd
np.random.seed(42)
# 데이터 크기
n_samples = 1000
# 주문 시간 (아침: 0, 점심: 1, 저녁: 2, 밤: 3)
order_time = np.random.choice([0, 1, 2, 3], size=n_samples)
# 거리 (1km ~ 20km 사이의 랜덤 값)
distance = np.random.uniform(1, 20, size=n_samples)
# 주문량 (10 ~ 100 사이의 랜덤 값)
order_volume = np.random.randint(10, 100, size=n_samples)
# 날씨 (맑음: 0, 비: 1, 눈: 2)
weather = np.random.choice([0, 1, 2], size=n_samples)
# 라이더 배차 수락율 (0.5 ~ 1.0 사이의 랜덤 값)
rider_acceptance_rate = np.random.uniform(0.5, 1.0, size=n_samples)
# 라이더 규모 (작음: 0, 중간: 1, 큼: 2)
rider_scale = np.random.choice([0, 1, 2], size=n_samples)
# 배달 시간 생성 (기본: 10분 + 거리 * 2 + 주문량 * 0.1 + 날씨 * 5 + 주문 시간 효과 + 라이더 효과)
delivery_time = 10 + distance * 2 + order_volume * 0.1 + weather * 5 + order_time * 5
# 라이더 배차 수락율과 라이더 규모에 따른 분산 추가
for i in range(n_samples):
if order_time[i] in [1, 2]: # 점심과 저녁 시간
delivery_time[i] += np.random.normal(0, 3)
else: # 다른 시간
delivery_time[i] += np.random.normal(0, 10)
if rider_scale[i] == 0: # 라이더 규모가 작음
delivery_time[i] += np.random.normal(0, 5)
elif rider_scale[i] == 1: # 라이더 규모가 중간
delivery_time[i] += np.random.normal(0, 2)
else: # 라이더 규모가 큼
delivery_time[i] += np.random.normal(0, 1)
# 데이터프레임 생성
data = pd.DataFrame({
'order_time': order_time,
'distance': distance,
'order_volume': order_volume,
'weather': weather,
'rider_acceptance_rate': rider_acceptance_rate,
'rider_scale': rider_scale,
'delivery_time': delivery_time
})
data.head()
## 모델 생성
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# 데이터 전처리
X = data.drop('delivery_time', axis=1)
y = data['delivery_time']
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42)
X_train = torch.tensor(X_train, dtype=torch.float32)
X_test = torch.tensor(X_test, dtype=torch.float32)
y_train = torch.tensor(y_train.values, dtype=torch.float32).view(-1, 1)
y_test = torch.tensor(y_test.values, dtype=torch.float32).view(-1, 1)
# 딥러닝 모델 정의
class DeliveryTimePredictor(nn.Module):
def __init__(self, input_size, hidden_size1, hidden_size2):
super(DeliveryTimePredictor, self).__init__()
self.layer1 = nn.Linear(input_size, hidden_size1)
self.layer2 = nn.Linear(hidden_size1, hidden_size2)
self.layer3 = nn.Linear(hidden_size2, 1)
def forward(self, x):
x = torch.relu(self.layer1(x))
x = torch.relu(self.layer2(x))
x = self.layer3(x)
return x
from hyperopt import fmin, tpe, hp, Trials
# 하이퍼파라미터 검색 공간 정의
space = {
'hidden_size1': hp.choice('hidden_size1', [32, 64, 128]),
'hidden_size2': hp.choice('hidden_size2', [16, 32, 64]),
'learning_rate': hp.loguniform('learning_rate', np.log(1e-4), np.log(1e-2))
}
# 목표 함수 정의
def objective(params):
hidden_size1 = params['hidden_size1']
hidden_size2 = params['hidden_size2']
learning_rate = params['learning_rate']
model = DeliveryTimePredictor(input_size=X_train.shape[1], hidden_size1=hidden_size1, hidden_size2=hidden_size2)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)
# 모델 학습
n_epochs = 50
for epoch in range(n_epochs):
model.train()
optimizer.zero_grad()
outputs = model(X_train)
loss = criterion(outputs, y_train)
loss.backward()
optimizer.step()
# 모델 평가
model.eval()
with torch.no_grad():
predictions = model(X_test)
val_loss = criterion(predictions, y_test).item()
return val_loss
# Trials 객체 생성
trials = Trials()
# 최적화 수행
best = fmin(
fn=objective,
space=space,
algo=tpe.suggest,
max_evals=50,
trials=trials
)
print("Best Hyperparameters:", best)