brunch

매거진 소고

You can make anything
by writing

C.S.Lewis

by 소고 Jun 25. 2017

XGBoost 사용하기

지루하고, 재미없기 짝이 없지만 꾸준한 조회수를 보장할 것 같은 글

소개


시작은 캐글(kaggle)이었다. 캐글이 무엇인지 처음 읽는 분들을 위해서 잠깐 설명하자면, <캐글>은 과학자들이 통계적 문제를 놓고 경쟁하는 온라인 플랫폼이다. 비유하자면 엔지니어들의 <쇼미더머니>랄까. 다만 누가 더 랩을 잘 하는가에 대한 평가는 심사위원이 아니라 수치로 집계된다. 지원자들은 학력, 나이에 관계없이 공개된 데이터를 다운로드하고, 평등한 시간에 문제를 푼다. 점수가 나온다. 점수는 리더보드에 실시간으로 집계된다. 마지막으로 마감 시간까지 최고의 솔루션을 제공한 팀이 상금을 탄다. 요약하자면 문제를 풀고, 그것을 제일 잘 푸는 팀이 돈을 번다. 우승 상금에 세금을 떼는가는 순위권에 들었던 적이 없어 잘 모르겠다. 아무튼 캐글의 재미난 점이 있다면, 세상에 숨어있는, 속된 말로 '재야의 고수들'이 참 많구나 하는 인식과, 고수들은 유명 기업에만 있는 것이 아니라는 점이다. 고수들은 금융계 분석가, 공학자, 대학원생, 웹 개발자, 헬스 케어 종사자 등 다양한 직업군으로 이 세상 어딘가에 함께 살고 있었다.


캐글은 전 세계가 검색하고 건드려 보는(?) 데이터 경진 플랫폼이다. <쇼미더머니>에 우승자 인터뷰가 있듯, 캐글도 우승자 인터뷰[1]가 있다. 이곳에는 단순히 데이터와 검증 방법을 설명하는 것 외에도 인터뷰이들의 직업, 사생활, 아이디어가 나온 장소(?), 에피소드 등, 인간적인 부분도 확인할 수 있는데, 이들은 공통적으로 아이디어를 공유하는데 제법 열려있다(당연히 아닌 인터뷰도 있다). 다만 오픈 콘테스트 우승자는 본인이 사용한 툴을 모두 공개해야 한다. 그리고 필자는 이 곳에서 xgboost(Extreme Gradient Boosting)를 처음 알았다.


심심찮은 비중의 우승자들이 <xgboost>를 사용했다고 했다. 그들은 xgboost가 빠른 속도로 로직을 검증하기 위해 좋은 라이브러리라고 설명했다. 마침 우리 팀은 기존에 사용하고 있던 분류기 성능을 개선하기 위한 방법을 모색하는 중이었다. 분류 알고리즘을 바꿔서 성능을 측정해보는 게 어떨까 하는 생각이 들었다. 데이터도 있고, 다 정제되어 있으니까 아구를 조금만 맞추면 결과는 금방 확인할 수 있다. xgboost를 조사했다. 요즘은 세 줄 요약 없으면 창을 닫는 시대니만큼, xgboost의 장점을 먼저 이야기해야겠다. 아, 세 줄이 아니라 네 줄이다. 네 줄 요약.


<xgboost>의 장점


1. 훌륭한 그라디언트 부스팅 라이브러리. 병렬 처리를 사용하기에 학습과 분류가 빠르다

2. 유연성이 좋다. 평가 함수를 포함하여 다양한 커스텀 최적화 옵션을 제공한다

3. 욕심쟁이(Greedy-algorithm)를 사용한 자동 가지치기가 가능하다. 따라서 과적합(Overfitting)이 잘 일어나지 않는다

4. 다른 알고리즘과 연계 활용성이 좋다. xgboost 분류기 결론부 아래에 다른 알고리즘을 붙여서 앙상블 학습이 가능하다 ResNet 마지막 바로 이전 단을 Feature layer로 응용하는 것과 비슷하다.


요약하자면 빠르고 유연하다는 게 xgboost가 가진 장점이다. 다큐먼트[2]를 찾아 읽었다. 적용도 그리 어렵지 않아 보였다. 그렇지만 xgboost가 그저 시도하기 쉽기 때문에 실험을 시작해선 안 될 것이다. 이 기술이 우리 팀에 실제로 필요한가 판단이 필요했다. 간단한 고민에 근거를 적기 시작했다.


왜 딥러닝 학습 기법 소개가 아니냐고?


요즘 딥러닝, 참 핫(hot)하고, 잇(it)하고, 댓(that)하다. 어떤 딥러닝 기계 학습은 필자보다 학부 재학생이 훨씬 더 깊게 알고 있는 경우도 있다. 그렇지만 딥러닝 학습 기법이 모든 데이터 문제의 만능 솔루션이라고 말할 수는 없다. 여기에는 다양한 이유를 댈 수 있겠지만 <차원의 저주The Curse of Dimensionality>가 있다. 차원의 저주란, 데이터의 차원이 증가할수록, 해당 공간의 크기가 기하급수적으로 증가하기 때문에 데이터 밀도가 희박해져 문제를 해결하기 어려워지는 것을 말한다. 큰 면적을 칠하는데 손톱만 한 붓은 비효율적이다. 반대로 세밀한 표현을 해야 하는 곳에 롤러는 적합하지 않은 도구다. 마찬가지로 딥러닝 학습 기법은 데이터의 숫자만큼이나 피쳐가 풍부한 문제에 적합하다. 보유하고 있는 데이터의 숫자가 많지 않을 때도 문제다. 딥러닝 학습 기법이 아주 훌륭한 미래 먹거리라는 말엔 동의하지만, 모든 데이터 문제를 딥러닝 학습으로 뚝딱 해결할 수 있는 것은 아니다.


초기 데이터 분석가들은 필터(filter)를 사용해서 이미지 차원을 효과적으로 축소할 수 있지 않을까 생각했다. 그리고 필터를 적용한 결과를 얕은(shallow) 기계 학습에 사용했다. 결과는 좋지 않았다. 그들은 시금치를 물에 너무 오래 데쳤다. 그러니까, 이미지 정보를 요약하면서 얻게 되는 유용한 정보의 양 보다 과정 속에 손실되는 정보가 훨씬 많았다.



그렇지만 과학자들은 실패 대신 뽀샵(?)을 얻었다. 피부 뽀얗게 하기, 턱 깎기, 다리 늘리기, 외곽선 따기 등. 우리가 지금 쓰고 있는 수많은 포토샵 필터들은 과학자들이 이미지 정보의 차원을 효율적으로 축소하다 발견하게 된 부수 효과(additional effect)다. 세상에, 뽀샵을 만든 사람들이 체크무늬 남방과 카고 바지를 즐겨 입는 사람들이라니. 말하자면 지구에서 가장 미감이 떨어진다고 평가받는 직업군 종사자들이 쏘아 올린 작은 공이랄까. 물론 의도한 것은 아니었다. 시작은 다차원 정보를 가공해서 액기스를 뽑아내기 위한 방법 모색이었니까. 어쨌든, 다차원 데이터에 필터를 씌워 기존의 얕은 기계학습에 끼워 맞추는 것은 좋지 않은 해법임이 드러났다. 결국 과학자들은 딥러닝 학습 기법이 다차원 처리 문제에 적합한 솔루션임을 알아낸다. 다만 다차원 처리 모델을 구축하기 위해선 어마어마한 숫자의 병렬 처리 머신이 필요하다. 그래픽카드 얘기다. 컴퓨터 업그레이드에 관심이 있는 분들이라면 알고 있겠지만, 요즘 그래픽카드 재고가 바닥이다. 그 많던 그래픽카드들은 누가 다 먹지 않았다. 요즘 그래픽카드는 채굴과 기계 학습을 위해 팬을 돌리느라 바쁘다.


사설이 길었다. 만약, 데이터가 (상대적으로) 저차원인 문제를 해결하려면 다른 모델이 필요하다. 이런 학습 세트는 유저가 아무리 새로운 피처를 만들어도 그 수가 음성/이미지의 원본(raw) 개수만 못하다. 이런 데이터를 가지고 딥러닝 모델을 구축하면 초보적인 분류기만 못한 성능을 낼 수도 있다. 결국 데이터 과학자들은 데이터의 복잡도와 샘플 숫자에 따라 적합한 모델을 발견할 수 있어야 한다.


캐글도 마찬가지다. 캐글은 거의 모든 문제 안에 <커뮤니티 게시판>이 있다. 그곳에는 상위 랭커들이 "자신은 어떤 방식으로 베이스라인을 만들었다"며 노하우를 공개한다. 여태까지 딥러닝 기계 학습을 사용한 베이스라인은 딱 두 번 봤다. 나머지는 피처 엔지니어링에 비중을 둔다. 경진 후반으로 갈수록 앙상블 방식이 아니면 우승이 어렵다. <How to win Kaggle competitions (역: 캐글에서 우승할 수 있는 방법)>을 검색하면 앙상블 얘기부터 나온다. 모델은 최적의 문제 해결을 위한 도구다. 한 가지 도구만으로 최상의 맛을 내는 음식을 만드는 것은 어렵다. 효율적인 요리를 위해선 도구의 이해가 필수다. 적절한 이해와 선택이 필요하다.


이제 xgboost가 등장 할 차례다.



1. 기본 원리


xgboost는 기본적으로 부스팅(Boosting, or Additive Training)이라 불리는 기술을 사용한다. 부스팅은 약한 분류기를 세트로 묶어서 정확도를 예측하는 기법이다. 모래 자갈, 먼지가 섞여 있는 물질에 여러 타입의 체를 가지고 조합해서 그것을 분류하는 과정과 유사하다.


예를 들어, 어떤 학습기 M에 대하여 Y를 예측할 확률은 아래와 같다(1).


Y = M(x) + error (1)


만약 'error'에 대하여 조금 더 상세히 분류할 수 있는 모델 G가 있다면(2), (단, error > error2)


error = G(x) + error2 (2)


로 표현할 수 있을 것이고, 여기에 ‘error2'를 더 세밀하게 분리할 수 있는 모델 H가 있다면(3) (단, error2 > error3)


error2 = H(x) + error3 (3)


으로 표현이 가능할 것이다. (1)에 (2), (3)을 적용할 경우(4),


Y = M(x) + G(x) + H(x) + error3 (4)


로 표현이 가능하다. 이는 학습기 M을 단독으로 사용했을 때보다 정확도가 높을 것이다. (error > error3) 그렇지만 (4)의 분류기는 개선의 여지가 있다. M, G, H 각각 분류기의 성능이 다른데, 모두 같은 비중(1*M + 1*G 1*H)을 두고 있기 때문에 임의의 x에 대하여 서로 간섭하며 오류를 높이는 결과를 낼 수 있기 때문이다. 이제 각 모델 앞에 비중(weights)을 두고, 기계 학습으로 최적의 비중을 찾자(5). 이것은 (4)의 모델보다 훨씬 더 좋은 성능(error3 > error4)을 내는 분류기가 될 것이다.


Y = alpha * M(x) + beta * G(x) + gamma * H(x) + error4 (5)


말하자면, xgboost는 욕심쟁이(Greedy algorithm)를 사용하여 분류기 M, G, H를 발견하고, 분산처리를 사용하여 빠른(Extreme) 속도로 적합한 비중 파라메터를 찾는 알고리즘이다. 분류기는 Regression Score를 사용하여 정확도 스코어(accuracy score)를 측정하고, 각 순서에 따라 강한 분류기부터 약한 분류기까지 랜덤 하게 생성된다. 이렇게 만들어진 분류기를 트리(tree)라고 하며, 분류기를 조합한 최종 알고리즘을 포레스트(forest)라고 한다. 여기까지가 기본적인 boosting algorithm 원리다.


xgboost는 트리를 만들 때 CART(Classification And Regression Trees)라 불리는 앙상블 모델을 사용한다. 이후 트리 부스팅을 사용하여, 각 분류 기간 비중(weights)을 최적화한다. CART 모델은 일반적인 의사결정 트리(Decision Tree)와 조금 다르다. 리프 노드 하나에 대해서만 디시전 벨류를 갖는는 의사결정 트리와 달리, CART 방식은 모든 리프들이 모델의 최종 스코어에 연관되어 있다. 따라서 의사결정 트리가 분류를 제대로 했는지에 대해서만 초점을 맞추는 반면, CART는 같은 분류 결과를 갖는 모델끼리도 모델의 우위를 비교할 수 있다(스코어를 비교하면 된다). 즉, 모든 트레이닝 세트 X에 대하여 포레스트에 넣고, 결괏값으로 나오는 점수의 절대값을 더한다. 많은 데이터를 +와 -의 방향으로 보낼수록, 좋은 분류 모델이라 할 수 있다.



설명이 길었으니 예를 하나 들어 보자. 여기, 다섯 명의 그룹에 대해, “컴퓨터 게임을 좋아하는가”를 밝히기 위한 두 가지 트리 모델이 있다(tree1/2). 트리 하나에 대하여 모든 리프 노드는 결괏값으로 분류된 사람들이 컴퓨터 게임을 좋아하는지에 대한 스코어가 남는다. 예를 들어, 아래 트리(tree1)에서 파란색 옷을 입은 남성의 점수는 +2 점으로, 컴퓨터 게임을 좋아하는 사람이다. 반면 빨간색 옷을 입은 여성의 점수는 +0.1로, 그녀가 컴퓨터 게임을 좋아하는지 확신하기 어렵다. 다만, 그녀에 비해 나이가 15세 이상의 그룹은 컴퓨터 게임을 선호하지 않는 것 같다(-1 점). tree2도 마찬가지다. 결국, tree1과 tree2를 조합하면, 파란색 옷을 입은 남성이 컴퓨터 게임을 좋아하는가에 대한 최종 점수를 얻을 수 있다.



tree1(남성) + tree2(남성) = +2 + 0.9 = 2.9 (게임을 좋아함)
tree1(여성) + tree2(여성) = +0.1 -0.9 = -0.8 (게임을 좋아하지 않음)


이제 빨간 옷을 입은 여성은 (tree1만 사용했을 때완 달리) 게임을 좋아하지 않는 것으로 드러났다. 그렇지만 앞치마를 한 여성은 두 가지 모델을 섞으면서 정체가 모호해졌다.


tree1(앞치마를 한 여성) + tree2(앞치마를 한 여성) = -1 + 0.9 = -0.1 (모호함)


이것은 tree1과 tree2에 같은 비중(coefficient)을 두었기 때문이다. 만약 tree1을 구성하는 <나이/성별 그룹> 점수보다 tree2를 구성하는 질문지 <컴퓨터를 매일 쓰는 사람>가 컴퓨터 게임 선호에 중요하다고 생각한다면, 즉


y = a* tree1(x) + b* tree2(x) + error (단, a > 0, b > 0)


에서 b>a 일 경우, 앞치마를 한 여성을 포함, 컴퓨터 게임을 선호하는 그룹을 모두 분류할 수 있게 된다. 게임 셋이다.


여기까지, 이렇게 어떤 모델이 유효한지, 적절한지를 찾아내는 과정을 부스팅(boosting)이라고 한다.


여기까지만 설명하면 허전하다. 학창 시절, 어떤 교수는 학생들 발표가 끝날 때마다 이런 말을 했다. “그래서, 수식은?”. 지금도 눈을 감으면 그 목소리가 들리는 것 같아 고개를 털었다. 해야지, 수식.



여기서, <Complexity of the Trees>란 모델의 복잡도. 그러니까 편향-분산 트레이드오프(Bias-variance tradeoff, or dilemma)를 맞춰주는 함수다. CART와 분산 처리로 수 없이 많은 트리가 만들어질 텐데, 그중에서 액기스만 뽑아 숲을 만들겠다는 소리다.



앞서 부스팅이란, 라운드가 지날수록(t) 모델의 에러를 줄이는 과정이라 설명했다. 즉, 목적 함수에서 로스 펑션(Loss function)을 t=1이 될 때까지 전개할 수 있다. 간단한 이해를 위해, 유저가 Square loss로 정의했다고 가정하자.



이때 빨간 원이 들어간 부분이 t-1 라운드의 loss가 된다. 즉, t 라운드의 문제가 t-1 라운드 문제로 달라졌다. 이제 이것을 <테일러급수 전개>를 사용하여 목적 함수를 근사하자. 테일러급수 전개는 이 식의 레지듀얼 폼을 일반화하기 좋은 형태로 마사지하기 위함이다. 이론적으로는 테일러급수 전개와 본래 함수가 차이가 없게 수렴하기 때문이고, 엔지니어링적 관점에서는 구조적이라 컴퓨터가 계산하기 좋게 뽀갤(?) 수 있다.



이제 t 시점에서 문제는 g 함수(first-order gradient)와 h 함수(second-order gradient)로 표현이 가능해졌다. 로스 함수 L에 대하여 t 시점에 상수로 판단되는 폼을 제외하면,



다음과 같이 미분 형태의 계산만으로 로스 함수를 재정의 할 수 있다. 여기서 함수 g와 h는 t 시점보다 작은 형태(t-1 시점)의 미분이므로, 무한히 전개했을 때 convergence 하는 형태를 찾을 수 있다. xgboost는 g와 h에 대한 모든 근사식을 사용자가 정의할 수 있고, 기본적으로 테일러 전개 선형 근사식을 사용하여 분산 처리하기 좋은 형태로 함수를 분리한다.


상단 수식에 있는 오메가 폼은 트리의 비중(weights)을 조절하는 조정 함수다. xgboost는 오메가 폼을



라고 정의했는데, 이것은 논문의 아이디어다. 즉, 최적의 복잡도 계산 방식은 개선의 여지가 있다. 결국 리프 스코어(L2 norm of leaves wight)와 리프 갯수(gamma)가 모델(f_t)의 복잡도를 결정함을 알 수 있다. 이제 모든 아이디어를 목적 함수에 적용하고, 이것을 weights 기준으로 정렬하면,



짜잔! 목적 함수는 T 개의 weight 이차식의 합으로 전개가 가능하다. 이때 G와 H에 대하여 최소점이 되는 x와 그때의 최솟값을 구하면 각각,


다음과 같다. 이제 수식을 축약하자. Sum of g는 G로, Sum of h는 H로. 그러면 목적 함수에 대하여 최적의 비중(weight) w*와 목적함수의 최적 값은,



다 끝났다!


우리는 이제 어떤 트리 T가 주어졌을 때, 최적의 스코어를 갖는 목적 함수를 구했고, 최적의 비중 w*도 알게 됐다.


응? 끝났다고? 사실 감이 안 올 수도 있다. 간단히 설명하자면, 위에서 했던 과정을 통해 목적 함수를 단순하게 전개할 수 있고, 그것이 수렴하는 최적값이 있다는 확신 덕분에, 우리는 매 순간 노드에 대해 가지를 쳤을 때 얻을 수 있는 정보 이득을 계산할 수 있다. 따라서 남은 것은 트리의 가지를 무한히 늘리면서 나무를 탐욕스럽게(greedy) 키우면 되는데, 저자는 이것을 스플릿 파인딩(Split Finding)이라 이름 붙였다. 트리 가지(binary)를 나누는 시점에서 정보 획득(Information Gain)을 순차적으로 계산하고, 그 점수가 마이너스일 때 가지를 잘라내는 과정이다.


아래 수식에서 L은 Left side children score이고, R은 Right side children score다. 특정 depth에서 가지를 친다고 했을 때 정보 획득량 Gain은,


이다. 이제 랜덤으로 기준을 정하고 Gain이 최대가 되도록 가지를 치는 트리를 생성한다. 그리고 그 과정을 (많이) 반복한다. 마지막으로 점수가 높은(양의 실수 값을 갖는) 트리를 조합하여 부스팅 한다. 결과로 T개의 트리가 조합된 최적의 분류 모델을 얻을 수 있다.


만약 필자의 설명이 부족하다고 느끼는 독자가 있다면, 저자 논문[3]이나 직강[4]을 참조하는 게 좋겠다.


2. 파라메터


xgboost 파라메터는 크게 3 가지 종류가 있다. 하나는 도구의 모양을 결정하는 일반 파라메터(General Parameters), 다른 하나는 부스터 파라메터(Booster Parameters)로, 트리마다 가지를 칠 때 적용하는 옵션을 정의한다. 마지막으로 학습 과정 파라메터(Learning Task Parameters)라 하여 최적화 퍼포먼스를 결정하는 파라메터가 있다. 소개하고자 하면 끝도 없을 테니, 지면상(이미 많이 썼지만) 꼭 필요한 것들 위주로 소개한다. 더 많은 정보는 여기[5]서 확인할 수 있다.


2-1. 일반 파라메터


booster: 어떤 부스터 구조를 쓸지 결정한다. 이것은 gbtree, gblinear, dart가 있다.

nthread: 몇 개의 쓰레드를 동시에 처리하도록 할지 결정한다. 디폴트는 “가능한 한 많이”.

num_feature: feature 차원의 숫자를 정해야 하는 경우 옵션을 세팅한다. 디폴트는 “가능한 한 많이.”


2-2. 부스팅 파라메터


eta: learning rate다. 트리에 가지가 많을수록 과적합(overfitting) 하기 쉽다. 매 부스팅 스탭마다 weight를 주어 부스팅 과정에 과적합이 일어나지 않도록 한다

gamma: 정보 획득(Information Gain)에서 -r로 표현한 바 있다. 이것이 커지면, 트리 깊이가 줄어들어 보수적인 모델이 된다. 디폴트 값은 0이다

max_depth: 한 트리의 maximum depth. 숫자를 키울수록 모델의 복잡도가 커진다. 과적합 하기 쉽다. 디폴트는 6. 이때 리프 노드의 개수는 최대 2^6 = 64개

lambda(L2 reg-form): L2 Regularization Form에 달리는 weights이다. 숫자가 클수록 보수적인 모델이 된다

alpha(L1 reg-form): L1 Regularization Form weights다. 숫자가 클수록 보수적인 모델이 된다 (팁은 3에 소개하겠다


2-3. 학습 과정 파라메터

objective: 목적 함수다. reg:linear(linear-regression), binary:logistic(binary-logistic classification), count:poisson(count data poison regression) 등 다양하다

eval_metric: 모델의 평가 함수를 조정하는 함수다. rmse(root mean square error), logloss(log-likelihood), map(mean average precision) 등, 해당 데이터의 특성에 맞게 평가 함수를 조정한다


2-4. 커맨드 라인 파라메터

num_rounds: boosting 라운드를 결정한다. 랜덤 하게 생성되는 모델이니만큼 이 수가 적당히 큰 게 좋다. epoch 옵션과 동일하다


3. 실제 동작과 팁


파라메터를 제대로 이해하고 있어야 모델을 구축하는데 시행착오가 적어진다. 가령, 이진 분류(binary-classification) 문제에 linear-regression을 적용해놓고, L1, L2 파라메터를 조정하고 있다면, 큰 시간을 낭비할 것이다. 필자는 약 40번의 실험을 했는데, 이때 파라메터 우선순위는 다음과 같다. 이것은 어디까지나 팁일 뿐임을 밝힌다.


1. booster (부스터 모양)
2. eval_metric (평가 함수) / objective (목적 함수)
3. eta (러닝 레이트)
4. L1 form (L1 레귤러라이제이션 폼이 L2보다 아웃라이어에 민감하다)
5. L2 form


다시 한번 말하지만 파라메터 조정 우선순위는 사용자가 임의로 결정하는 부분이다. 아주 간단하게 모든 옵션 대비 for loop을 돌려도 될 것이다. 참고 삼아 이야기하자면, xgboost 디폴트 옵션과 필자가 찾은 베스트 파라메터의 Accuracy(F1-Score)는 각각 0.52(F1: 0.02) / 0.91(F1: 0.907)이다. 차이가 제법 난다.


내부 데이터를 공개하는 것은 고용불안을 초래할 수 있다. UCI Data를 가지고 간단한 실험을 해 보자. 데이터 이름은 <Pima Indian diabetes set>으로, 세계에서 당뇨병 발병 비율이 가장 높은 애리조나 주의 피마 인디언을 대상으로 조사한 자료다[6]. 자료 속성은 8가지다. 여기까지 설명할 필요는 없지만, 세계 질병을 구원하기 위한 연구자의 기분을 내보고자 설명을 덧붙인다.


1. 임신 횟수
2. 경구 포도당 내성 검사에서 혈장 포도당 농도 (2시간 이후 측정)
3. 확장기 혈압 (mmHg)
4. 삼두근 피부 두께 (mm)
5. 인슐린 혈청 (μU / ml) 저항성 (2시간 이후 측정)
6. 체질량 지수 (체중 (kg / 체중) ^ 2)
7. 당뇨병 내력
8. 나이 (세)


이제 코드를 돌려 보자. 코드는 예제를 참조[7]했고, 트레이닝 데이터 세트만 있기에 완벽한 실험이라곤 할 수는 없다. 그렇지만 xgboost의 강력한 기능인 Feature Importance를 확인하기엔 더할 나위 없다.


결과를 확인하자.


금방이다. 결과에 의하면 체질량 지수가(속성 6) 높을 수록, 경구 포도당 내성 저항이 약할 수록(속성 2), 가족 중 당뇨병 내력이 있을 때(속성 7) 당뇨병에 걸릴 확률이 높다. 나이(속성 8)는 그다음이다. 우리나라 사람들의 체질은 모르겠지만 피마 인디언들의 결과는 이렇다. 굳이 xgboost로 하지 않아도 되는 실험이라 말할 수도 있겠다. UCI 데이터 세트는 이미 ‘거의 풀린’ 문제가 올라오니까. 그렇지만 위의 결과처럼 Feature Importance, 즉, 내가 만든 피처가 실험 결과에 얼마나 유효한가. 또는 모델이 어떤 피처에 얼마나 의존하고 있는지를 시각화하는 기능은 강력하다. 캐글 위너들은 자신들이 만든 피처를 가지고 importance를 보면서 미세하게 튜닝했다고 한다. ‘유효성’을 확인할 수 있다는 점에서, xgboost는 제법 괜찮은 툴이다.


튜닝이 막바지일 무렵, 우리 팀 데이터를 가지고 실험한 Feature Importance를 공개한다. 보면 알겠지만, 아무리 피처를 많이 만들어도 유효한 것이 아니면 거의 소용이 없음을 확인할 수 있다. 몇몇 피처는 없앨때 성능이 올라가기도 한다. 선무당이 분류기 망친다. 필자 얘기다.


가장 실험 결과가 나빴을 때와, 좋았을 때의 분포도도 공개한다. 실험은 바이너리 클래시피케이션(binary classification)으로, 0.5 이상이면 1로 레이블 하고, 0.5 미만일 때 0을 레이블 한다.



워스트 케이스(위). 학습 결과가 0.5 근방에 몰려있기 때문에 모델이 분류를 제대로 수행하지 못하고 있음을 알 수 있다. 반대로 베스트 케이스(아래)는 결과 누적 분포도가 0과 1 사이에 몰려 있다. 모델이 분류를 잘 수행하고 있다는 소리다. 좋다.



결론


xgboost는 빠르고, 쓰기 편하며, 직관적인 모델이다. 데이터 정제가 잘 되어 있는 '온실 속 문제'에서 빛을 발휘하기보다는 실무에서 피처를 생성하고, 테스트하고, 튜닝하는 과정에서 쓰기 좋은 툴이다. 부스팅 모델링이 필요한 상황이라면, 진지하게 권유할만하다. 여기까지 쓰고 나니, 캐글 위너들이 xgboost를 사용해서 우승하고, “이제 프라이빗 컨테스트에 참가할 수 있어서 기쁘다”라고 말하는 인터뷰가 생각난다. 미지의 세계는 어떤 곳일까. 그곳은 황량하고, 아마도 재밌는 곳이겠지.


뱀발


모든 실무가 그렇겠지만, 데이터 평가는 필수다. 모든 데이터를 학습에 사용하면 곤란하다. K겹 교차 검증(k-fold cross validation, 한국형 뭐시기를 의미하는 K가 아니다)을 생각하고 수치로 검증해야 한다. 간단한 문제들이야 검안으로 되네, 잘 안 되네를 구분할 수 있지만, 그보다 앞서 제대로 실험을 설계했을 때 깔끔한 결과를 얻을 수 있다. 문제 해결 초반, 이를 잘 구분하지 못해 긴 시행착오를 겪었다. 이 자리를 빌려 라이언에게 감사 인사를 남긴다. 그는 무한한 인내심과 귀여운(?) 투정으로 함께 문제를 풀었다.


이 자료가 나가기까지 많은 분들이 수고해주셨다. 우선 필자와 함께 문제를 풀었던 라이언과 저스틴에게 감사 인사를 전한다. 아티클을 검토해준 ‘팀 보스’ 틴틴과 ‘갓’ 엘튼/벨라에게도 감사하다 말하고 싶다. 기술 블로그에 무슨 감사 인사야 싶겠지만 팀이 아니면 이 고생이 다 무슨 소용인가. 그리고 피마 인디언, XGBoost 저자인 티안치 첸과 카를로스 게스트린 교수가 생각난다. 게스트린 교수는 현재 애플에 있다. 티안치 첸은 구글 인턴을 하고 워싱턴 대학 박사 과정에 있다. 과연 게스트린 교수가 그를 애플에 데려갈지 기대가 크다. 마지막으로 여기까지 긴 글을 읽은 독자분. 대단하다. 이 글을 읽고 끝까지 비판적 시각을 유지할 수 있었다면, 당신은 데이터 과학자 혹은 활자 중독자임이 분명하다. 축하한다. 읽어주셔서 감사하다. 그리고 당신의 모델링에 행운을 빈다.


Fin.





출처:


커버 이미지: 피마 면직물 붐 시절, 애리조나 엘로이 지구에서 면을 따는 소년, 1920년 (http://arizonaexperience.org/remember/pima-cotton-boom)

[1] http://blog.kaggle.com/category/winners-interviews/

[2] https://xgboost.readthedocs.io/en/latest/

[3] https://arxiv.org/abs/1603.02754

[4] http://homes.cs.washington.edu/~tqchen/pdf/BoostedTree.pdf

[5] http://xgboost.readthedocs.io/en/latest/parameter.html#general-parameters

[6] https://archive.ics.uci.edu/ml/datasets/Pima+Indians+Diabetes

[7] http://machinelearningmastery.com/feature-importance-and-feature-selection-with-xgboost-in-python/

매거진의 이전글 넛셸
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari