Julie의 Tech 블로그

Catboost 모델에 대하여 - 알고리즘, 구현 코드 본문

Tech/ML, DL

Catboost 모델에 대하여 - 알고리즘, 구현 코드

Julie's tech 2022. 6. 3. 23:58
728x90

요즘 흔하게 많이 사용되는 Catboost 모델에 대해서 정리하려고 한다.

Catboost는 이름에서도 유추할 수 있듯 boosting 앙상블 기법을 사용하는 모델 중 하나이다.

논문에서는 이렇게 Catboost를 소개한다고 한다.

"CatBoost is a high-performance open source library for gradient boosting on decision trees."

이전에 앙상블과 Gradient Boosting 모델에 대해 정리한 글이 있는데 알고 읽으면 좀 더 도움이 된다.

https://blog.naver.com/ilovelatale/222320553535

 

Boosting vs Bagging

다시 간단하게 정리하자면 앙상블의 기법 중에서는 Boosting과 Bagging이 있다.

Bagging은 Bootstrap + Aggregation의 약자로 parallel하게 샘플을 추출하여 모델을 학습한다. 반면 Boosting은 Sequential하게 이미 학습된 모델을 다른 모델의 input으로 넣어 학습한다. 이 과정에서 잔차가 점점 0에 가깝게 학습되도록 모델을 넣는다.

이에 따라 Boosting은 Bagging보다 연산속도가 굉장히 느리다. 그래서 Boosting류 모델은 모두 속도 개선 로직이 포함되어 있다. 이전에 GBM모델 리뷰한 블로그 글에도 속도 개선 로직이 중요한 특징 중 하나로 있었다.

뿐만 아니라 잔차를 줄여가는 과정이기 때문에 오버피팅이 되기 쉽다는 단점도 있다. 이 때문에 정규화 과정이 Boosting계열 모델에 포함되어 있는 경우가 많다.

Catboost - level-wise 방식

Catboost 역시 마찬가지로 속도 개선 로직과 정규화 방법을 보유하고 있는 Boosting류 모델이다. 유명한 Boosting계열 모델 중 하나인 XGBoost와 Catboost가 유사한 점이라면 두 모델의 가장 큰 차이점은 트리를 boosting해나가는 방향이 같다. 두 모델은 level-wise로 트리를 만들어나간다.

이 때 level-wise란 : 트리를 만들 때 두 가지 방향으로 확장해나갈 수 있다. 하나는 level-wise, 즉 옆으로 확장하는 방법과 leaf-wise인 밑으로의 확장방식이 있다. 우리가 Data Structure(자료구조) 시간에 트리에 대해 배우면 트리 탐색 방식에 대해 배우는데, 이 때 BFS, DFS가 나오는데 그 두 탐색방식의 방향과 동일하다. 사실 level-wise이든 leaf-wise이든 두 방법 모두 트리를 full-depth로 기르고 나면 동일한 트리가 탄생한다. 하지만 실상은 우리가 full tree를 사용하지 않고 cut-off를 하기 때문에 두 방법은 유의한 차이가 있는 결과를 내는 것이다. Leaf-wise는 Split(분기)가 발생할때 특정 branch(줄기)를 따르는 것이 아니라 'global loss'를 더 줄일 수 있는 방향으로 확장하는 것을 택한다. 따라서 level-wise보다는 대개의 경우 더 정확도가 높은 트리를 빨리 발견할 수 있다. 즉 다시 말하자면 트리 노드가 많지 않을 경우 통상 level-wise보다 leaf-wise 트리가 더 높은 정확도를 보일 것이다.

Catboost - Ordered-Boosting 방식

Catboost 는 기존의 boosting 과정과 가장 다른 점이 있다. 기존의 Boosting 모델들은 모든 훈련 데이터를 대상으로 잔차계산을 했지만 Catboost 는 학습데이터의 일부만으로 잔차계산을 한 뒤, 이 결과로 모델을 다시 만들게 된다. 예를 들어 학습데이터가 전체 10개의 row로 이루어져있을 경우 기존 모델들은 10행 컬럼을 모두 합산하여 평균낸 뒤(= 가장 간단한 base value생성 방법) 각 행별로 잔차를 계산한 뒤, 만들어진 모델에 대해 예측값을 내고 또 각각의 잔차를 구하여 잔차를 줄이는 방향으로 학습한다. 하지만 Catboost모델은 학습데이터의 일부를 선택할 때 일종의 순서를 가지고 선택하게 된다(10행 중 특정 2개만 학습, 그 뒤 4개 학습 등). 이에 따라 "Ordered Boosting" 방법이라고 부른다. 이 때 그럼 '순서 기준'을 잘 정해주어야하는 것이 아니냐는 궁금증이 생길 수 있다. 따라서 Catboost는 순서도 사실 임의적인 것이기 때문에 데이터를 random하게 섞어주는 과정을 포함하고 있다. 이는 Boosting모델이 오버피팅을 방지하기 위해 각각 지니고 있는 기법이라 볼 수 있다.

Catboost - 범주형 변수 처리 방법

Catboost는 이름에서도 유추 가능하듯이 Cat, Category 즉 범주형 변수가 많은 데이터를 학습할 때 성능이 좋은 것으로 알려져있다. Response encodingCategorical Feature Combination이라고 하여 두 방법이 범주형 변수를 효율적으로 처리하는데 큰 장점이 된다.

Response Encoding부터 보자면, 범주형 변수를 인코딩하는 방법인데 아래 예시를 통해 자세히 알아보자.

weather 컬럼은 범주형 변수인데, cloudy, sunny, windy, rainy 총 4가지의 값을 가지고 있다. 이 때 weather 컬럼을 response encoding에 따라 값을 변환하기 위해서 각 변주형 값의 temperature (=label) 컬럼의 값을 평균을 내어준다.

  • cloudy = (10+50+70)/3 = 33.33..
  • sunny = (20+40)/2 = 30
  • rainy = 60
  • windy = 30

하지만 이렇게 진행하면 label, 즉 target에 대한 정보를 일부 학습데이터에 노출시키는 것과 같다. 우리는 모델링할 때 학습데이터에서 target과 상관관계가 높거나 동일한 컬럼은 제외하고 만드는 것이 기본 룰이다. 따라서 Catboost는 위에서 언급된 Ordered Boosting개념을 섞어 Response Encoding을 진행한다. 즉 Catboost는 학습데이터 중 일부를 선택하여 학습시키는데, 이 때 일부를 선택하는 'order'은 시간 순서에 따른다. 만약 데이터에 시계열 데이터가 없다면 임의로 시간을 정하여 순서를 정한다(Random Permutation). 데이터의 시간 순으로 나열했을 때 과거 데이터만으로 response encoding을 진행하게 되는 것이다.

  • 금요일 기준으로 cloudy = (monday cloudy temperature) = 10
  • 일요일 기준으로 cloudy = (monday cloudy + friday cloudy temperature) = (10+50)/2 = 30

* 이 때 과거 데이터가 없을 경우는 0/0이 되버리는데, 이를 방지하기 위해 Laplace smoothing방법을 이용하여 값을 대체해준다. 자세한 설명은 생략하겠다.

그 다음은 Categorical Feature Combination이다. 말 그대로 두 범주형 변수를 합쳐주는 것인데, label값을 기준으로 split을 할 때 두 범주형 변수가 서로 대체 가능하다면 이 둘을 합쳐준다. 예를 들어 우리나라 사람들의 민족 특성상 검은 머리와 검은 눈이 일반적인데, 이럴 경우 머리 컬럼과 눈색 컬럼을 합칠 수 있는 것이다. 이렇게 두 컬럼을 합쳐 one-hot encoding을 한다고 한다.

Catboost - 수치형 변수 처리 방법

수치형 변수는 다른 일반 트리 모델과 동일하게 처리한다고 한다. 분기가 발생하면 Gain, 즉 정보의 획득량이 높은 방향대로 나뉘게 된다. 수치형 변수가 많게 되면 lightGBM 모델처럼 시간이 오래 걸린다. 따라서 범주형 변수가 많을 때 이 모델을 사용하기를 추천한다. 또 Sparse한 matrix는 적용하기 어렵다고 한다.

Catboost - 장점

Catboost는 시계열 데이터를 효율적으로 처리하는 것으로 알려져있다.

또 속도가 매우 빨라 실제 상용화되고 있는 서비스에 예측 기능을 삽입하고자 할 때 효과적일 수 있다. XGBoost보다 예측 속도가 8배 빠르다고 알려져있다.

imbalanced dataset도 class_weight 파라미터 조정을 통해 예측력을 높일 수도 있다.

그 외 Catboost는 오버피팅을 피하기 위해 내부적으로 여러 방법(random permutation, overfitting detector)을 갖추고 있어 속도 뿐만 아니라 예측력도 굉장히 높다.

아래 그림은 대표적인 public ML dataset의 테스트 데이터셋으로 log-loss를 계산한 값이다. 보다싶이 Catboost 모델이 가장 낮은 값을 보이고 있다.

Catboost 모델 구현

Catboost모델을 Python으로 구현하는 코드로 이 글을 마무리하려고 한다.

Catboost모델은 시계열 데이터로 진행할 경우 has_time = True로 파라미터를 셋팅해주어야한다. 그래야 ordered boosting시에 임의로 정한 시간대로 random permutation을 하지 않는다.

Catboost Python official docs는 https://catboost.ai/en/docs/concepts/python-quickstart 링크를 통해 접근이 가능하다.

아주 간단하게 구현하면 아래와 같다. CatBoostClassifier이 사용되었는데, Regressor 패키지도 있다.

import numpy as np

from catboost import CatBoostClassifier, Pool

# initialize data
train_data = np.random.randint(0,
                               100, 
                               size=(100, 10))

train_labels = np.random.randint(0,
                                 2,
                                 size=(100))

test_data = catboost_pool = Pool(train_data, 
                                 train_labels)

model = CatBoostClassifier(iterations=2,
                           depth=2,
                           learning_rate=1,
                           loss_function='Logloss',
                           verbose=True)
# train the model
model.fit(train_data, train_labels)
# make the prediction using the resulting model
preds_class = model.predict(test_data)
preds_proba = model.predict_proba(test_data)
print("class = ", preds_class)
print("proba = ", preds_proba)

Classifier, Regressor구분 없이 아래와 같이 .predict()함수를 통해서도 구분이 가능하다.

import numpy as np
from catboost import CatBoost, Pool

# read the dataset

train_data = np.random.randint(0, 
                               100, 
                               size=(100, 10))
train_labels = np.random.randint(0, 
                                2, 
                                size=(100))
test_data = np.random.randint(0, 
                                100, 
                                size=(50, 10))
                                
train_pool = Pool(train_data, 
                  train_labels)

test_pool = Pool(test_data) 
# specify training parameters via map

param = {'iterations':5}
model = CatBoost(param)
#train the model
model.fit(train_pool) 
# make the prediction using the resulting model
preds_class = model.predict(test_pool, prediction_type='Class')
preds_proba = model.predict(test_pool, prediction_type='Probability')
preds_raw_vals = model.predict(test_pool, prediction_type='RawFormulaVal')
print("Class", preds_class)
print("Proba", preds_proba)
print("Raw", preds_raw_vals)

이 외에 좀 더 실용적인 구현 case를 참고하려면 아래 Kaggle 노트북을 참고하는 것을 추천한다.

https://www.kaggle.com/code/prashant111/catboost-classifier-in-python/notebook


 
반응형