XGBoost와 LightGBM 하이퍼파라미터 튜닝 가이드

목차

    1. 왜 이 글을 쓰게 되었는가?

    xgboost와 lightgbm은 tabula데이터를 다루는 경진대회에서 가장 많이 쓰이는 모델이다. 몇번의 경진대회 경험에 의하면 text classification의 경우에도 딥러닝 모형에 비해 학습시간은 적게 소요되면서 더 나은 성능을 내는 경우가 많았다. 회사에서는 주로 클래스 불균형이 있는 이진분류 문제를 다루는데, 이 경우에도 기본적인 딥러닝 모형들(DNN, RNN, CNN)에 비해 학습속도와 성능 모두 xgboost나 lightgbm이 월등히 나은 모습을 보인다.

    경험적으로 보면 하이퍼파라미터 튜닝보다는 파생변수 생성에 집중하는 것이 성능을 높이는데 더 효과적인 방법이다. 하지만, 기본적인 하이퍼파라미터들이 어떤 의미인지 정확히 알고 사용할 수 있어야 효과적인 학습이 가능하기도 하고, 프로젝트를 하다보면 어떤 경우에는 하이퍼파라미터 튜닝을 통한 성능 쥐어짜기가 필요할 때도 있다. 그래서 한 번쯤 정리해 보는 것이 도움이 될 것 같아 이 글을 쓰게 되었다.

    2. 글을 쓰면서 참고한 자료

    https://sites.google.com/view/lauraepp/parameters

     

    Laurae++: xgboost / LightGBM - Parameters

    Parameters

    sites.google.com

    xgboost와 lightgbm 하이퍼파라미터에 관해 정리된 자료 중 가장 퀄리티가 높은 자료라고 생각한다. lightgbm 관련된 자료를 찾다보면 자주 마주치게 되는 분인데 이분의 깃허브를 보면 다양한 프로젝트에 관여하고 계신 것 같다.

    3. 글에서 다룰 하이퍼파라미터 목록

    (1) Booster Method
    (2) Tree Building Method
    (3) Learning rate
    (4) Number of Iterations
    (5) Maximum Depth
    (6) Maximum leaves
    (7) Row Sampling
    (8) Column Sampling by Tree
    (9) Positive Binary Scaling

    참고한 웹사이트에는 총 86개의 하이퍼파라미터에 대한 설명이 있지만 (내가) 자주 사용하는 8개를 추려 설명해보려고 한다. 사실 대부분의 하이퍼파라미터는 디폴트 값으로 사용해도 크게 문제가 되지 않는다. 오히려 잘못 사용할 경우 학습에 좋지 않은 영향을 줄 가능성이 높다. Laurae님의 설명에서도 많은 하이퍼파라미터에

    Tips: leave it alone unless you know what you are doing.

    라는 문구가 붙어있다.

    각 하이퍼파라미터는

    요약
    xgboost와 lightgbm 비교
    사용법

    의 세 가지 항목으로 나누어 정리하고자 한다.

    4. 하이퍼파라미터 설명

    (1) Booster Method

    요약
    부스팅 방법을 결정하는 파라미터로 모형 성능, 학습 시간, 램 사용량이 영향을 준다. 무엇을 사용해야 할 지 잘 모르겠으면 디폴트 설정을 그대로 두는 것이 좋다.
    xgboost, lightgbm 비교
    xgboost에서는 booster(사이킷런 API에서도 동일), lightgbm에서는 boosting(사이컷런 API에서는 boosting_type). xgboost, lightgbm 모두 디폴트는 gradient boosting tree이다(xgboost: 'gbtree', lightgbm: 'gbdt'). Decision tree와 Stochastic Gradient Descent(SGD; 학습 데이터의 일부(mini-batch)만 사용하여 loss function 계산)를 이용하여 부스팅을 수행한다.

    xgboost와 lightgbm에 함께 있는 옵션으로 'dart'(Dropout Additive Regression Trees)가 있다. dart는 신경망의 드롭아웃을 적용시킨 방법이다. Rashmi and Gilad-Barchrach(2015)이 과도한 특수화(over-specialization) 문제를 해결고자 DART를 제안했다. DART는 트리 수준에서 작동하며 개별 특성이 아니라 트리를 완전히 누락시킨다.[각주:1]

    xgboost에만 있는 옵션으로는 'gblinear'(Generalized Linear Model boosting)가 있는데 이 옵션을 사용하게 되는 경우는 경험상 거의 없었다. GLM에 부스팅을 적용한 것인데 이 방법에 대해 더 궁금한 것이 있다면 r의 mboost 패키지 튜토리얼 문서를 읽어보는 것을 추천한다.

    lightgbm에만 있는 옵션으로는 'rf'와 'goss'가 있다. xgboost기반 rf는 XGBRFClassifier로 구현되어 있으니 'goss' 를 제외한 나머지 옵션은 xgboost와 lightgbm 모두에서 사용할 수 있다. 'rf'는 부스팅이 아닌 랜덤포레스트 모형(배깅)을 만드는 방법이고, 'goss'(Gradient-based One-Side Sampling)는 SGD를 더 빠르고 더 잘 수렴시키기 위해 서브샘플링을 이용하는 방법이다.
      사용법  
    잘 모르겠으면 디폴트 설정인 GBDT를 사용하는 것이 좋다. DART는 과적합을 제어할 수 있는 방법이므로 한 번쯤 테스트 해볼만 하고, lightgbm을 사용한다면 GOSS도 함께 테스트 해볼만 하다.  

    (2) Tree Building Method

      요약  
    트리를 만드는 방법을 설정하는 파라미터로 xgboost에만 존재하며, 모델성능과 학습시간에 영향을 준다.

    정확한 학습을 위해서는 Exact method('exact'), 학습속도를 높이기 위해서는 Approximate method('approx', 'hist', 'gpu_hist')를 이용하는 것이 좋다.  
      xgboost, lightgbm 비교  
    xgboost에만 존재하는 파라미터이다. xgboost의 파라미터 이름은 tree_method(사이킷런 API에서도 동일)이고 'exact', 'approx', 'hist', 'gpu_hist'(gpu를 사용할 수 있는 경우에만)를 선택할 수 있다.

    'exact'는 트리 스플릿을 위해 모든 데이터 포인트를 고려하는 방법이어서 정확한 스플릿이 가능하지만 분산처리를 할 수 없어 학습속도가 느리다는 단점이 있다.

    'approx', 'hist', 'gpu_hist'는 모든 데이터 포인트를 고려하여 스플릿을 하지 않고, 분위수나 히스토그램을 기준으로 데이터를 분할하여(논문에서는 버킷을 만든다고 표현) 스플릿을 수행한 후 information gain이 가장 높은 버킷의 스플릿을 최종 선택한다. 'approx'는 loss function 업데이트에 loss function의 hessian(loss function의 second-order partial derivatives)을 사용하지만 'hist'와 'gpu_hist'는 사전에 지정한 값을 사용한다는 차이점이 있다. lightgbm의 학습 방법이 'hist'인데 구현 방법에는 차이가 있다고 한다([참고자료] xgboost 공식문서).
      사용법  
    데이터 행수가 10만 단위가 넘어간다면 xgboost에서는 'gpu_hist'를 쓰는 것이 정신건강에 이롭다. 개인적인 경험에 의하면 10배 이상의 학습시간 차이가 있다. 데이터 행수가 적다면 정확도를 위해 'exact'를 사용하고, 데이터 행수가 많은데 gpu를 사용할 수 없다면 'hist', gpu를 사용할 수 있다면 'gpu_hist'를 선택하는 것이 좋다.

    (3) Learning rate

      요약  
    부스팅 각 이터레이션 마다 곱해지는 가중치(loss function의 step size)로 모형 성능과 학습시간에 영향을 준다.

    보통 작을수록 모형 성능 향상에 도움이 되지만 학습 시간은 길어지는 trade-off가 있다.

    하이퍼파라미터 튜닝시에는 0.1~0.3 정도의 값을 사용하고, 최종 모형 학습시에는 0.05이하의 값을 사용하는 것이 좋다.  
      xgboost, lightgbm 비교  
    xgboost에서는 eta, lightgbm에서는 learning_rate, 사이킷런 API에서는 둘다 learning_rate로 사용할 수 있다. xgboost에서 디폴트 값은 0.3, lightgbm에서 디폴트 값은 0.1인데, 두 패키지의 학습속도 차이를 반영한 설정으로 보인다.  
      사용법  
    learning rate를 옵티마이저에 넣어서 튜닝할 필요는 없다. 주어진 컴퓨팅 환경을 고려하여 적당한 값을 정한 후 그 값에서 다른 파라미터들을 튜닝하는 것이 좋다. 옵티마이저에 learning rate를 포함시켜서 0.0202048 같은 값에 오버피팅 시키는 것은 불필요한 시도이다.

    최종 학습시에는 0.05 이하의 값을 사용하여 모형 성능 향상에 초점을 맞추고, 하이퍼파라미터 튜닝시에는 0.1이상의 값을 사용하여 학습속도를 높이는 것이 좋다.

    단, learning rate를 크게 잡았을때 모형 적합이 잘 되지 않는다면, 이 값을 기준으로 튜닝한 다른 하이퍼파라미터의 조합이 learning rate를 낮추었을때는 최적 조합이 아니게 될 가능성이 높다. 경험상 하이퍼파라미터 튜닝시에도 learning rate를 너무 큰 값으로는 설정하지 않는 것이 좋다.  

    (4) Number of Iterations

      요약  
    부스팅 이터레이션 수로 모델 성능과 학습시간, 램 사용량에 영향을 준다. 클수록 train 데이터에 오버피팅 된다. 큰 값을 넣은 후에 early stopping과 함께 사용하는 것이 좋다.  
      xgboost, lightgbm 비교  
    xgboost에서는 nrounds, lightgbm에서는 num_iterations이고, 사이킷런 API에서는 모두 n_estimators로 사용할 수 있다.
      사용법  
    early stopping 없이 이터레이션 수의 값을 크게 넣으면 학습데이터에 오버피팅된다. train 데이터 전체를 사용하여 최종모형을 학습할 때에는 cross validation을 early stopping과 함께 수행하여 각 fold별 iteration 수를 구한 후 그 평균값에 10%정도 더 큰 값을 사용하는 것이 좋다고 한다.

    early stopping은 보통 50 정도의 값을 사용한다. early stopping은 xgboost, lightgbm 모두에서 early_stopping_rounds로 사용할 수 있고, 사이킷런 API에서도 동일하다.  

    (5) Maximum Depth

      요약  
    각 트리의 최대 깊이. 최대 리프 수(Maximum Leaves), 모형 성능, 학습 시간에 영향을 준다. 클수록 학습 데이터에 피팅이 잘 되지만 오버피팅될 가능성도 높아진다. 보통 3~12의 값을 사용한다. 가장 먼저 튜닝해야 할 하이퍼파라미터이다.  
    xgboost, lightgbm 비교
    xgboost와 lightgbm 모두 파라미터 이름은 max_depth 이다(사이킷런 API도 동일), xgboost의 디폴트 값은 6이고, lightgbm의 디폴트 값은 -1이다.

    무한 깊이(가능한 최대로 트리 스플릿)로 학습시키려면 xgboost에서는 0, lightgbm에서는 -1로 설정하면 된다.

    xgboost에서 GPU를 사용할 때는(tree_method='gpu_hist') 최대 16까지 설정할 수 있다.
    사용법
    그라디언트 부스팅에서 가장 민감한 하이퍼라미터이므로 max_depth를 가장 먼저 튜닝하는 것이 좋다.

    데이터의 복잡도에 따라 필요한 트리 깊이가 다른데 너무 크게 잡으면 학습 데이터에 오버피팅 될 수 있고, 반대로 너무 작게 잡으면 학습이 제대로 안되는 언더피팅 상황이 될 수도 있다.

    lightgbm 같이 leaf-wise로 학습하는 경우에는 max_depth를 -1로 설정하는 것이 효과적이라고 알려져 있다. 경험적으로 보았을때도 lightgbm은 디폴트 설정을 사용할때가 보통 성능이 가장 좋았고, xgboost의 경우 max_depth에 따른 성능 차이가 큰 경우가 많았다.

    Maximum Leaves = 2^depth-1 의 관계가 있다. 예를 들어, Maximum Depth가 10인 경우 Maximum Leaves는 1023이다.

    (6) Maximum Leaves

    요약
    각 트리의 최대 리프 수. 트리의 최대 깊이, 모형 성능, 학습속도에 영향을 미친다. Maximum Depth와 함께 튜닝하는 것이 좋다.
    xgboost, lightgbm 비교
    Maximum Leaves의 파라미터명은 xgboost에서는 max_leaves, lightgbm에서는 num_leaves이다. xgboost의 디폴트 값은 255이고, lightgbm의 디폴트 값은 31이다.
    사용법
    그라디언트 부스팅에서 두번째로 민감한 하이퍼파라미터Maximum Depth와 함께 튜닝해주는 것이 좋다.

    Maximum Depth를 크게 설정 했을때 Maximum Leaves를 조절하면 규제로 작용하여 트리가 너무 거대해지는 것을 막을 수 있다. lightgbm에서 디폴트 값이 31로 작은 것도 lightgbm의 디폴트가 무한 깊이로 트리를 만드는 것이기 때문에 너무 거대한 트리가 만들어져 train 데이터에 과적합 되는 것을 막기 위한 설정으로 보인다.

    Maximum Depth 별로 여러 개의 Maximum Leaves의 조합을 만들어 cross validation 성능이 가장 좋은 조합을 찾아보는 것이 좋다.

    (7) Row Sampling

    요약
    각 이터레이션에 사용되는 행의 비율. Early Stopping, 모형 성능, 학습시간에 영향을 미친다. 보통 0.7 정도의 값을 사용하고, 너무 세세하게 튜닝할 필요는 없다.
    xgboost, lightgbm 비교
    xgboost의 파라미터 명은 subsample이고, lightgbm의 파라미터 명은 bagging_fraction이다. 사이킷런 API에서는 둘 다 subsample로 사용할 수 있다. xgboost와 lightgbm 모두 디폴트 값은 1이고,  lightgbm에서 boosting_type='goss' 일 때는 Row sampling이 허용되지 않는다.

    lightgbm에서 bagging_freq 디폴트 값이 0인데, 0이면 row sampling을 하지 않는 것이다. xgboost에서는 original stochastic gradient descent를 적용했기 때문에 이 bagging_freq을 설정해주는 부분이 없고, lightgbm에서 bagging_freq을 1로 설정하면 xgboost와 동일한 방식으로 학습할 수 있다.
    사용법
    그라디언트 부스팅에서 세번째로 민감한 하이퍼파라미터column sampling과 함께 튜닝하는 것이 좋다. 특정 랜덤 시드에서 0.728472 같이 아주 특이한 값을 사용할 때 오버피팅이 발생한다.

    (8) Column Sampling by Tree

    요약
    각 이터레이션에 사용되는 칼럼의 비율. Early Stopping, 모형 성능, 학습시간에 영향을 미친다. 보통 0.7 정도의 값을 사용하고, 너무 세세하게 튜닝할 필요는 없다.
    xgboost, lightgbm 비교
    xgboost의 파라미터명은 colsample_bytree이고, lightgbm의 파라미터명은 feature_fraction이다. 사이컷런 API에서는 모두 colsample_bytree로 사용할 수 있다. xgboost와 lightgbm 모두 디폴트 값은 1이다.
    사용법
    그라디언트 부스팅에서 세번째로 민감한 하이퍼파라미터row sampling과 함께 튜닝하는 것이 좋다. 특정 랜덤 시드에서 0.728472 같이 아주 특이한 값을 사용할 때 오버피팅이 발생한다.

    경험상 row sampling 보다는 column sampling이 모형성능과 학습시간에 더 큰 영향을 준다. text classification 같이 칼럼 차원이 만단위가 넘어 갈 수 있는 경우에 column sampling 비율을 0.3 정도로 작게 설정해주면 학습 시간단축에 효과적이고, 모형 성능 향상에도 도움이 되었다.

    (9) Positive Binary Scaling

    요약
    이진분류 문제(label: {0, 1})를 풀 때 posivite label(label: 1)에 가중치를 부여하는 것. 모형 성능과 학습시간에 영향을 준다. 잘 모르겠으면 디폴트 값을 사용하는 것이 좋다.
    xgboost, lightgbm 비교
    xgboost, lightgbm 모두 파라미터 명은 scale_pos_weight이고, 디폴트 값은 1이다.
    사용법
    positive label은 반드시 수가 더 적은 쪽이어야 한다. positive label에 가중치를 곱해주면 모형은 cost-sensitive training을 하게 되고, cost-sensitive training은 모형 학습에 직접적으로 영향을 주는 부스터에 적용된다.

    개인적인 경험에 의하면 scale_pos_weight를 조절해주는 것 보다는 이 값은 디폴트로 두고, 언더샘플링 같이 데이터셋에 대한 샘플링 기법을 적용하는 것이 더 효과적이었다. Machine Learning Mastery 블로그에 있는 벤치마크 테스트 결과에서도 scale_pos_weight 값에 따른 AUC 변화가 거의 없는 것을 확인할 수 있다.

    5. 글을 마치며

    처음 글을 쓰기 시작했을때는 최대한 많은 수의 하이퍼파라미터를 커버하고 싶었지만, 회사 업무를 하면서 글을 쓰는게 생각보다 쉽지 않았다. 원래 계획은 경진대회 데이터셋을 사용한 하이퍼파라미터 튜닝 예제도 함께 작성하는 것이었는데, 이 부분은 추후에 시리즈 형식으로 작성해야 할 것 같다. 또한, 개인적인 경험에 의존하여 설명한 부분이 상당히 많아서 이에 대한 보완도 필요해 보인다.

    ※ 글을 읽다가 잘못된 부분이나 추가되면 좋을 것 같은 팁이 있으면 댓글로 남겨주시면 감사하겠습니다.

    1. 퀀트 투자를 위한 머신러닝 딥러닝 알고리듬 트레이딩 2/e, 스테판 젠슨 지음 홍창수, 이기홍 옮김, 에이콘, p588-589 [본문으로]