Note ≡ 랜덤한 하이퍼파라미터 탐색
그리드 서치가 최적의 매개변수 조합을 찾을 수 있는 강력한 도구이지만 가능한 모든 매개변수 조합을 평가하기 위해 계산 비용이 매우 많이 듭니다. 사이킷런에는 여러 가지 매개변수 조합을 샘플링하는 다른 방법으로 랜덤 서치(randomized search)가 있습니다.
랜덤 서치는 일반적으로 그리드 서치와 비슷한 성능을 내지만 훨씬 비용과 시간이 적게 듭니다. 특히 랜덤 서치에서 60개의 파라미터 조합이 있다면 최적의 성능에서 5% 이내에 있는 솔루션을 얻을 확률이 95%입니다.11
RandomizedSearchCV 클래스를 사용하여 제한된 횟수 안에서 샘플링 분포로부터 랜덤한 매개변수 조합을 뽑습니다. 자세한 내용과 사용 예제는 사이킷런의 온라인 문서를 참고하세요(http://scikit-learn.org/stable/modules/grid_search.html#randomized-parameter-optimization).
Note ≡
역주 매개변수 탐색 범위가 넓거나 규제 매개변수 C처럼 연속적인 값을 탐색해야 하는 경우에 RandomizedSearchCV가 더 효율적입니다. 이 클래스는 n_iter 매개변수로 탐색 횟수를 조정할 수 있어 컴퓨팅 자원에 맞게 탐색을 실행할 수 있습니다. GridSearchCV 클래스에는 탐색할 매개변수 값을 리스트로 전달했습니다. RandomizedSearchCV에는 샘플링 가능한 분포를 지정해야 합니다. 예를 들어 scipy.stats.uniform, scipy.stats.randint 객체 등이 있습니다. 사이킷런 0.22 버전에서 scipy.stats.reciprocal을 사용한 로그 균등 분포(log-uniform distribution) 클래스 loguniform을 제공합니다.
다음 예에서 그리드 서치에서 사용한 것과 동일한 범위로 매개변수 C를 탐색해 보겠습니다. 대신 여기서는 탐색 횟수를 30회로 줄여서 수행합니다.
>>> from sklearn.model_selection import RandomizedSearchCV
>>> from sklearn.utils.fixes import loguniform
>>> distribution = loguniform(0.0001, 1000.0)
>>> param_dist = [{'svc_ _C': distribution,
... 'svc_ _kernel': ['linear']},
... {'svc_ _C': distribution,
... 'svc_ _gamma': distribution,
... 'svc_ _kernel': ['rbf']}]
>>> rs = RandomizedSearchCV(estimator=pipe_svc,
... param_distributions=param_dist,
... n_iter=30,
... cv=10,
... random_state=1,
... n_jobs=-1)
>>> rs = rs.fit(X_train, y_train)
>>> print(rs.best_score_)
>>> print(rs.best_params_)
0.9824637681159419
{'svc_ _C': 210.6644070836221, 'svc_ _gamma': 0.0006861724481510375, 'svc_ _kernel': 'rbf'}
결과에서 알 수 있듯이 RandomizedSearchCV의 탐색 횟수는 절반 이상 적지만 더 좋은 성능을 내는 매개변수 조합을 찾았습니다.
사이킷런 0.24 버전에서 추가된 HalvingGridSearchCV는 모든 파라미터 조합에 대해 제한된 자원으로 실행한 후 가장 좋은 후보를 골라서 더 많은 자원을 투여하는 식으로 반복적으로 탐색을 수행합니다. 이런 방식을 SH(Successive Halving)라고 부릅니다. HalvingGridSearchCV의 resource 매개변수는 반복마다 늘려 갈 자원을 정의합니다. 기본값은 'n_samples'로 샘플 개수입니다. 이외에도 탐색 대상 모델에서 양의 정수 값을 가진 매개변수를 지정할 수 있습니다. 예를 들어 랜덤 포레스트의 n_estimators가 가능합니다.
factor 매개변수는 반복마다 선택할 후보의 비율을 지정합니다. 기본값은 3으로 후보 중에서 성능이 높은 1/3만 다음 반복으로 전달합니다. max_resources 매개변수는 각 후보가 사용할 최대 자원을 지정합니다. 기본값은 'auto'로 resources='n_samples'일 때 샘플 개수가 됩니다.
min_resources는 첫 번째 반복에서 각 후보가 사용할 최소 자원을 지정합니다. resources='n_samples'이고 min_resources='smallest'이면 회귀일 때 cv×2가 되고 분류일 때는 cv×클래스 개수×2가 됩니다. 그 외에는 1입니다. min_resources='exhaust'이면 앞에서 계산한 값과 max_resources를 factor**n_required_iterations로 나눈 몫 중 큰 값입니다. 기본값은 'exhaust'입니다(n_required_iterations는 logfactor(전체 후보 개수)+1입니다).
마지막으로 aggressive_elimination 매개변수를 True로 지정하면 마지막 반복에서 factor만큼 후보가 남을 수 있도록 자원을 늘리지 않고 초기에 반복을 여러 번 진행합니다. 기본값은 False입니다.
HalvingGridSearchCV를 위스콘신 유방암 데이터셋에 적용해 보겠습니다. 다음 코드를 실행하려면 사이킷런 0.24 버전 이상이 필요합니다. 이 코드는 https://github.com/rickiepark/python-machine-learning-book-3rd-edition/blob/master/ch06/HalvingGridSearchCV.ipynb에 있습니다.
HalvingGridSearchCV는 아직 실험적이기 때문에 sklearn.experimental 패키지 아래에 있는 enable_halving_search_cv를 임포트해야 사용할 수 있습니다. verbose=1로 지정하면 각 반복 과정을 자세히 살펴볼 수 있습니다.
>>> from sklearn.experimental import enable_halving_search_cv
>>> from sklearn.model_selection import HalvingGridSearchCV
>>> hgs = HalvingGridSearchCV(estimator=pipe_svc,
... param_grid=param_grid,
... cv=10,
... n_jobs=-1, verbose=1)
>>> hgs = hgs.fit(X_train, y_train)
>>> print(hgs.best_score_)
>>> print(hgs.best_params_)
n_iterations: 3
n_required_iterations: 4
n_possible_iterations: 3
min_resources_: 40
max_resources_: 455
aggressive_elimination: False
factor: 3
----------
iter: 0
n_candidates: 72
n_resources: 40
Fitting 10 folds for each of 72 candidates, totalling 720 fits
----------
iter: 1
n_candidates: 24
n_resources: 120
Fitting 10 folds for each of 24 candidates, totalling 240 fits
----------
iter: 2
n_candidates: 8
n_resources: 360
Fitting 10 folds for each of 8 candidates, totalling 80 fits
0.9746031746031747
{'svc_ _C': 10.0, 'svc_ _gamma': 0.01, 'svc_ _kernel': 'rbf'}
출력 결과를 보면 첫 번째 반복(iter: 0)에서 72개의 후보를 40개의 샘플로 교차 검증을 수행합니다. 여기에서 72/3 = 24개의 후보를 뽑아 두 번째 반복(iter: 1)을 수행합니다. 두 번째 반복에서는 40×3 = 120개의 샘플을 사용합니다. 같은 방식으로 세 번째 반복(iter: 2)에서는 여덟 개의 후보가 360개의 샘플로 평가됩니다. 최종 결과는 97.4%로 GridSearchCV보다 조금 낮습니다. 찾은 매개변수 조합도 달라진 것을 볼 수 있습니다.
세 번의 반복 동안 HalvingGridSearchCV가 수행한 교차 검증 횟수는 모두 104번입니다. 각 교차 검증에 걸린 시간은 cv_results_ 속성의 mean_fit_time에 저장되어 있습니다. 이를 GridSearchCV와 비교해 보면 5배 이상 빠른 것을 볼 수 있습니다.
>>> print(np.sum(hgs.cv_results_['mean_fit_time']))
0.3786303520202636
각 반복 단계에서 사용한 샘플 개수와 후보 개수는 각각 n_resources_ 속성과 n_candidates_ 속성에 저장되어 있습니다.
>>> print('자원 리스트:', hgs.n_resources_)
>>> print('후보 리스트:', hgs.n_candidates_)
자원 리스트: [40, 120, 360]
후보 리스트: [72, 24, 8]
11 Random search for hyper-parameter optimization. Bergstra J, Bengio Y. Journal of Machine Learning Research. pp. 281-305, 2012