Quiz / ML 앙상블 / 전체 문항
GitHub

ML 앙상블 전체 문항

배깅, 랜덤 포레스트, 부스팅, XGBoost와 LightGBM. 문항과 선택지별 해설을 글 순서대로 나열했습니다.

24문항 · 글 4편
직접 풀어보기 →

약한 모델 여러 개를 합쳐 분산을 줄이는 배깅

원문 읽기 →
개념 확인구조
BaggingClassifier(n_estimators=100, oob_score=True)를 학습했다. OOB 0.9604 테스트 0.9561

OOB 점수는 어떻게 만들어진 값인가?

  1. 훈련 데이터 전체에 대한 100그루의 다수결 정확도다.

    그렇게 재면 각 트리가 자기가 학습한 샘플까지 맞히므로 낙관적으로 부풀어 검증값으로 쓸 수 없다.

  2. 부트스트랩 샘플 100개를 각각 검증 세트로 삼아 얻은 점수의 평균이다.

    부트스트랩 샘플은 그 트리가 학습에 쓴 데이터라 검증에 쓸 수 없다. 쓰이는 것은 반대로 남겨진 쪽이다.

  3. 정답각 샘플마다 그 샘플을 학습에 쓰지 않은 트리들만 모아 낸 예측으로 잰 점수다.

    복원 추출이라 샘플 하나가 평균 36.8%의 트리에서 빠지고, 그 트리들의 다수결은 그 샘플을 한 번도 본 적 없는 앙상블의 예측이 된다. 모든 샘플에서 이렇게 구한 점수가 OOB다.

  4. 훈련 데이터의 36.8%를 미리 떼어 검증 세트로 고정해 둔 뒤 잰 점수다.

    미리 떼어 두는 것이 아니라 트리마다 남는 샘플이 다르다. 그래서 결과적으로 모든 샘플이 검증에 한 번씩 쓰인다.

해설

복원 추출로 원본 크기만큼 다시 뽑으면 한 샘플이 한 번도 걸리지 않을 확률이 자연상수의 역수, 약 36.8%로 수렴한다. 어떤 샘플을 놓고 보면 100그루 중 평균 37그루가 그 샘플을 학습에 쓰지 않았고, 그 그루들의 예측만 모아 다수결을 내면 그 샘플을 본 적 없는 앙상블의 예측이 된다. 모든 샘플에서 이렇게 구한 점수가 OOB 점수다. 교차 검증은 같은 일을 하려고 데이터를 여러 번 나눠 그만큼 다시 학습하는데, OOB는 배깅을 한 번 학습하는 도중에 공짜로 나온다. 데이터가 크거나 학습이 오래 걸릴수록 이 차이가 값을 한다. 읽을 때는 테스트 점수와 나란히 놓고 보고, OOB가 눈에 띄게 낮다면 데이터에 시간 순서나 그룹 구조가 있어 무작위 분할 자체가 맞지 않는다는 신호일 수 있다.

이 내용 다시 읽기 →
상황 적용구조
정확도 70%짜리 분류기를 다수결로 합쳤을 때의 계산이다. 3개 → 78.4% 11개 → 92.2% 21개 → 97.4%

이 계산이 실제 앙상블에서 그대로 실현되지 않는 이유는 무엇인가?

  1. 정답오류가 서로 독립이라는 가정 위에 서 있는데 실제 모델들은 같은 샘플에서 나란히 틀리기 때문이다.

    같은 데이터로 학습한 모델들은 어려운 샘플에서 함께 틀린다. 그러면 다수결의 답이 모델 하나의 답과 같아져 모델을 늘려도 얻는 것이 없다.

  2. 실제 분류기의 정확도는 샘플마다 달라 70%라는 고정값으로 계산할 수 없기 때문이다.

    샘플별 난이도 차이는 평균으로 흡수해 다룰 수 있다. 계산을 무너뜨리는 것은 평균이 아니라 오류 사이의 상관이다.

  3. 모델 수가 짝수가 되면 동점이 생겨 다수결이 성립하지 않기 때문이다.

    홀수로 두면 피할 수 있는 부수적인 문제다. 위 계산도 홀수만 쓰고 있다.

  4. 모델을 늘릴수록 각 모델에 배분되는 데이터가 줄어 개별 정확도가 떨어지기 때문이다.

    부트스트랩은 모델마다 원본 크기만큼 뽑으므로 모델을 늘려도 각 모델이 받는 데이터 양은 그대로다.

해설

다수결이 개별 모델보다 좋아지는 계산은 오류가 서로 독립이라는 가정 위에 서 있다. 모든 모델을 같은 데이터로 학습시키면 어려운 샘플에서 나란히 틀리고, 그러면 다수결의 답이 모델 하나의 답과 같아져 모델을 아무리 늘려도 얻는 것이 없다. 앙상블의 성패는 개별 모델의 성능이 아니라 모델들이 서로 얼마나 다르게 틀리는가에 달려 있다는 뜻이다. 이 이론값이 말해주는 것이 하나 더 있다. 수렴 속도가 개별 정확도에 크게 좌우된다는 점이다. 0.7짜리는 21개로 97%를 넘지만 0.6짜리는 51개를 모아도 92.6%에 그친다. 개별 성능이 0.5에 가까울수록 곡선이 완만해지므로 모델 수만으로 메우기 어려워진다.

이 내용 다시 읽기 →
상황 적용설계

배깅에 넣는 개별 트리를 가지치기 없이 끝까지 키우는 이유는 무엇인가?

  1. 깊은 트리일수록 부트스트랩 샘플의 차이를 더 잘 반영해 상관관계가 낮아지기 때문이다.

    깊이가 상관관계를 낮추는 주된 수단은 아니다. 상관관계는 강한 특성 하나가 모든 트리의 루트를 차지할 때 생기므로 깊이와는 다른 축이다.

  2. 얕은 트리는 학습이 빨라 앙상블의 이점이 상대적으로 줄기 때문이다.

    속도는 선택의 근거가 아니다. 얕은 트리를 쓰는 앙상블도 있고 그때는 다른 원리가 작동한다.

  3. 가지치기를 하면 부트스트랩 샘플마다 리프 수가 달라져 다수결의 가중치가 어긋나기 때문이다.

    다수결은 각 트리에 표 하나씩을 준다. 리프 수가 달라도 표의 무게는 같다.

  4. 정답배깅이 줄이는 것은 분산뿐이므로 편향이 낮고 분산이 큰 모델일수록 평균을 낼 때 얻는 것이 많기 때문이다.

    개별 트리가 과적합해도 상관없고 오히려 그편이 낫다. 가지치기로 편향을 올려 두면 평균을 내도 그 편향은 그대로 남는다.

해설

배깅이 손대는 것은 편향이 아니라 분산이다. 서로 독립인 예측값을 평균 내면 분산이 모델 수만큼 나뉘는데, 편향은 평균을 내도 그대로 남는다. 그래서 편향이 낮고 분산이 큰 모델을 넣어야 얻는 것이 많고, 가지치기 없이 끝까지 키운 트리가 정확히 그런 모델이다. 개별 트리가 과적합해도 상관없다는 말은 여기서 나온다. 반대로 미리 가지치기를 해서 편향을 올려 두면 평균을 내도 그 편향이 남으므로 앙상블이 벌어들일 몫이 줄어든다. 같은 이유로 이미 안정적인 모델에 배깅을 씌우는 것도 의미가 적다. 데이터가 조금 바뀌어도 계수가 거의 그대로인 모델은 부트스트랩 샘플을 100개 만들어도 거의 같은 모델 100개가 나오기 때문이다.

이 내용 다시 읽기 →
분석과 판단구조
트리 사이에 상관관계 ρ가 있을 때 평균 예측의 분산이다. Var = ρ·σ² + ((1-ρ)/B)·σ² 특성 30개 중 하나가 압도적으로 예측력이 높아 모든 트리가 그 특성을 루트에 놓는 상황이다.

이 상황에서 트리 수 B를 100에서 10,000으로 늘리면 어떻게 되는가?

  1. 뒷항이 100분의 1로 줄어드는 만큼 전체 분산도 거의 100분의 1이 된다.

    뒷항만 줄어들고 앞항은 그대로다. 앞항이 이미 지배적인 상황이라 전체 분산은 거의 움직이지 않는다.

  2. 정답뒷항은 사실상 사라지지만 앞항이 그대로 남아 분산이 ρ·σ²라는 바닥에서 멈춘다.

    B를 아무리 키워도 손댈 수 없는 몫이 앞항이다. 상관관계가 0.8이라면 트리를 만 그루 심어도 원래 분산의 80%가 남는다.

  3. 트리가 늘어날수록 강한 특성에 의존하는 트리의 비율이 낮아져 ρ 자체가 함께 줄어든다.

    ρ는 트리 수와 무관하다. 어느 부트스트랩 샘플로 학습하든 그 특성이 루트에 오므로 트리를 더 심어도 같은 모양이 늘어날 뿐이다.

  4. 트리가 많아지면 과적합이 심해져 분산이 다시 커지기 시작한다.

    배깅에서 트리 수를 늘리는 것은 과적합을 키우지 않는다. 늘려도 나빠지지 않는 대신 어느 지점부터 나아지지도 않을 뿐이다.

해설

상관관계를 반영한 분산 식은 두 항으로 갈리고 각각의 운명이 다르다. 뒷항은 트리 수를 늘리면 0으로 사라지며 이것이 배깅이 실제로 벌어들이는 몫이고, 앞항은 트리 수와 무관하게 그대로 남아 분산의 바닥이 된다. 상관관계가 0.8이면 만 그루를 심어도 원래 분산의 80%가 남는다. 상관관계가 커지는 전형적인 상황이 특성 하나가 지나치게 강할 때다. 어느 부트스트랩 샘플로 학습하든 모든 트리가 그 특성을 루트에 놓으므로 데이터를 다르게 줬는데도 트리 모양이 형제처럼 닮고 같은 곳에서 같이 틀린다. 그래서 다음 수는 트리 수가 아니라 상관관계 자체를 겨냥해야 한다. 트리가 볼 수 있는 것을 제한해 서로 다른 트리가 나오게 만드는 방법이 그 답이다.

이 내용 다시 읽기 →
상황 적용함정
릿지 회귀의 성능을 올리려고 BaggingRegressor로 100개를 묶었다. 학습 시간은 100배가 됐는데 점수는 소수점 셋째 자리까지 단일 모델과 같다.

왜 아무 효과가 없었는가?

  1. 회귀에서는 다수결 대신 평균을 쓰므로 앙상블 효과가 원래 나타나지 않는다.

    평균도 분산을 줄이는 정당한 집계 방식이다. 불안정한 회귀 모델을 묶으면 효과가 나타난다.

  2. 정답릿지의 규제가 이미 분산을 억제해 두어 부트스트랩 샘플이 달라도 거의 같은 모델이 나오기 때문이다.

    배깅이 줄이는 것은 분산뿐인데 그 분산이 애초에 작다. 거의 같은 모델 100개를 평균 내면 원래 모델과 다를 것이 없다.

  3. 릿지는 볼록 최적화라 해가 유일하므로 부트스트랩 샘플이 달라도 같은 해가 나오기 때문이다.

    해가 유일한 것은 데이터가 고정됐을 때다. 데이터가 달라지면 해도 달라지며, 다만 그 변화가 작다는 것이 요점이다.

  4. 선형 모델의 평균은 다시 선형 모델이라 표현력이 늘지 않기 때문이다.

    표현력을 늘리는 것은 배깅의 목적이 아니다. 트리를 묶어도 목적은 분산 감소이지 표현력 확장이 아니다.

해설

배깅이 줄이는 것은 분산뿐이다. 규제가 걸린 선형 모델처럼 데이터가 조금 바뀌어도 계수가 거의 그대로인 모델은 부트스트랩 샘플을 100개 만들어도 거의 같은 모델 100개가 나오고, 평균을 내도 원래 모델과 다를 것이 없다. 배깅의 단골 손님이 결정 트리인 것은 트리가 그만큼 불안정하기 때문이다. 같은 자리에서 자주 나오는 실수가 하나 더 있다. 복원 추출을 끄면 모든 모델이 같은 훈련 데이터를 통째로 받고, 결정 트리처럼 결정론적인 학습기라면 100그루가 전부 동일해져 앙상블 효과가 0이 된다. 배깅을 붙이기 전에 이 모델이 데이터를 흔들면 실제로 흔들리는 모델인지부터 확인하는 것이 순서다.

이 내용 다시 읽기 →
분석과 판단진단
시간 순서가 있는 로그 데이터로 배깅 모델을 만들었다. OOB 0.958 테스트 0.961 (무작위 분할) 실서비스 0.83 (배포 후 다음 주 데이터)

이 격차를 어떻게 읽어야 하는가?

  1. OOB와 테스트가 서로를 검증해 주므로 두 값이 맞다면 실서비스 하락은 모델 밖의 문제다.

    두 값은 서로 독립적인 증거가 아니다. 둘 다 무작위 분할을 전제하므로 그 전제가 틀리면 나란히 같은 방향으로 낙관적이다.

  2. 정답OOB와 테스트가 모두 무작위 분할을 전제하므로 시간 구조가 있으면 둘 다 함께 낙관적으로 나온다.

    OOB는 부트스트랩에서 빠진 샘플을 쓰는데 그 샘플의 시점이 학습에 쓰인 샘플과 뒤섞여 있다. 미래를 예측하는 상황과 다르므로 시간 기반 분할로 다시 재야 한다.

  3. 트리 수가 부족해 분산이 남은 것이므로 트리를 더 늘리면 실서비스 점수가 따라 올라간다.

    트리를 늘려도 검증 방식이 안고 있는 편향은 그대로다. 두 자릿수의 격차를 분산 감소로 메울 수 없다.

  4. OOB가 테스트보다 낮으므로 과적합이 시작된 신호이고 트리 깊이를 제한해야 한다.

    0.958과 0.961의 차이는 사실상 같은 수준이다. 격차가 벌어진 곳은 그 둘 사이가 아니라 실서비스와의 사이다.

해설

OOB 점수는 부트스트랩에서 빠진 샘플로 재는 값이라 무작위 분할과 같은 전제 위에 있다. 데이터에 시간 순서나 그룹 구조가 있으면 검증에 쓰인 샘플의 시점이 학습 샘플과 뒤섞여 있게 되므로, 과거로 미래를 맞히는 실제 상황보다 쉬운 문제를 푼 셈이 된다. 무작위 분할로 만든 테스트 세트도 같은 전제를 공유하므로 두 값이 서로 일치한다고 해서 검증이 된 것이 아니다. 나란히 같은 방향으로 낙관적일 뿐이다. 이럴 때는 시점을 기준으로 앞뒤를 나눠 다시 재야 하고, 그룹 구조가 있다면 같은 그룹이 학습과 검증에 동시에 들어가지 않도록 나눠야 한다. OOB의 값어치는 학습 한 번으로 공짜로 나온다는 데 있지 어떤 분할 전략도 대체한다는 데 있지 않다.

이 내용 다시 읽기 →

특성 무작위성으로 트리 간 상관관계를 깨는 랜덤 포레스트

원문 읽기 →
개념 확인구조

랜덤 포레스트가 배깅에 더한 것은 무엇인가?

  1. 트리마다 부트스트랩 샘플의 크기를 다르게 잡아 데이터의 다양성을 키운다.

    부트스트랩 방식은 배깅과 같다. 달라진 곳은 데이터를 뽑는 쪽이 아니라 특성을 고르는 쪽이다.

  2. 트리마다 깊이 제한을 무작위로 달리 주어 서로 다른 복잡도의 트리를 섞는다.

    깊이는 모든 트리에 같은 규칙으로 적용된다. 무작위성이 들어가는 자리는 깊이가 아니라 분기 후보다.

  3. 정답각 노드에서 분기 후보로 쓸 특성을 전체가 아니라 무작위로 고른 일부로 제한한다.

    명단에 가장 강한 특성이 없으면 그 노드는 두 번째로 좋은 특성으로 갈라진다. 트리마다 갈라지는 자리가 달라져 서로 다른 곳에서 틀리게 된다.

  4. 다수결 대신 각 트리의 검증 점수로 가중치를 매겨 예측을 합친다.

    집계 방식은 배깅과 같은 단순 다수결이다. 트리마다 표의 무게를 달리 주지 않는다.

해설

배깅에서 트리들이 닮아버리는 주범은 예측력이 유난히 강한 특성 하나다. 어느 부트스트랩 샘플을 쓰든 그 특성이 불순도를 가장 많이 줄이므로 모든 트리가 그것을 루트에 놓고, 데이터는 달랐는데 트리는 같은 자리에서 갈라져 결국 같은 샘플에서 나란히 틀린다. 랜덤 포레스트는 노드마다 후보 명단을 새로 뽑아 이 연결을 끊는다. 명단에 강한 특성이 없으면 그 노드는 두 번째로 좋은 특성으로 갈라지고, 그렇게 자란 트리들은 서로 다른 곳에서 틀린다. 대가도 분명하다. 후보를 줄이면 각 노드가 전체 중 최선이 아닌 분기를 고르게 되므로 개별 트리의 성능은 떨어진다. 개별 트리를 조금 나쁘게 만드는 대신 상관관계를 크게 낮추는 거래이고, 분산의 바닥이 상관관계로 정해지는 구조에서는 대체로 남는 장사다.

이 내용 다시 읽기 →
상황 적용진단
같은 데이터에서 트리 수를 늘리며 반복 교차 검증으로 두 모델을 비교했다. 트리 1~3그루 배깅이 근소하게 위 트리 10그루~ 랜덤 포레스트가 확실히 위 트리 50그루~ 두 곡선 모두 평평, 간격은 유지

트리가 몇 그루뿐일 때 랜덤 포레스트가 뒤지는 이유는 무엇인가?

  1. 정답개별 트리가 약해진 손해는 바로 나타나는데 상관관계를 낮춘 이득은 트리가 모여야 실현되기 때문이다.

    후보를 솎아내면 각 트리는 최선이 아닌 분기를 고르므로 곧바로 나빠진다. 서로 다르게 틀린다는 성질은 여러 트리의 표가 상쇄될 때 비로소 값을 한다.

  2. 트리가 적으면 후보로 뽑히지 못한 특성이 많아 정보의 상당 부분을 아예 쓰지 못하기 때문이다.

    후보는 노드마다 새로 뽑히므로 트리 한 그루 안에서도 여러 특성이 쓰인다. 특정 특성이 통째로 배제되지 않는다.

  3. 트리가 적으면 부트스트랩 샘플의 겹침이 커져 상관관계가 오히려 올라가기 때문이다.

    부트스트랩 샘플의 겹침 정도는 트리 수와 무관하다. 각 샘플은 독립적으로 뽑힌다.

  4. 트리가 적으면 다수결에서 동점이 자주 나와 예측이 무작위로 정해지기 때문이다.

    동점은 홀수 그루로 피할 수 있는 부수적인 문제이고, 세 그루와 열 그루 사이의 체계적인 차이를 설명하지 못한다.

해설

후보 특성을 솎아내는 것은 개별 트리를 나쁘게 만드는 대신 트리들 사이의 상관관계를 낮추는 거래다. 손해는 트리 한 그루에서 곧바로 나타나지만 이득은 여러 트리의 표가 서로를 상쇄할 때 비로소 실현되므로, 세 그루 남짓에서는 손해만 남아 배깅에 뒤진다. 열 그루쯤부터 역전되고 그 뒤로는 두 곡선이 나란히 평평해진다. 여기서 실무적으로 중요한 지점이 하나 더 나온다. 50그루를 넘기면 200그루까지 더 얻는 것이 0.002뿐이다. 트리를 늘려도 과적합이 생기지는 않으므로 상한을 정하는 것은 성능이 아니라 학습 시간과 메모리이고, OOB 점수를 켜두면 검증셋을 따로 떼지 않고도 이 수렴 지점을 확인할 수 있다.

이 내용 다시 읽기 →
상황 적용함정
유방암 데이터의 지니 기반 특성 중요도 상위 목록이다. worst area 0.1285 worst concave points 0.1283 worst perimeter 0.1271 mean concave points 0.1198 worst radius 0.0693 worst radius, worst perimeter, worst area의 상관계수는 0.978에서 0.994 사이다.

worst radius가 5위로 내려앉은 것을 어떻게 읽어야 하는가?

  1. 반지름보다 둘레와 넓이가 종양의 악성 여부를 더 직접적으로 반영한다는 뜻이다.

    셋은 사실상 같은 양을 다르게 적은 값이라 정보량에 차이가 거의 없다. 순위 차이는 정보의 질이 아니라 배분의 결과다.

  2. 정답세 특성이 같은 정보를 담고 있어 하나가 받았어야 할 중요도가 셋에 쪼개져 들어간 결과다.

    어느 노드에서든 셋 중 아무거나 뽑히면 되므로 분기 기회가 갈린다. 원 하나의 반지름과 둘레와 넓이를 각각 적어둔 것에 가까운 상황이다.

  3. 트리 수가 부족해 중요도 추정이 흔들린 것이므로 트리를 늘리면 순위가 안정된다.

    트리를 늘려도 셋이 서로를 대체할 수 있다는 사실은 그대로다. 배분의 비율만 조금 매끄러워질 뿐 쪼개짐 자체는 남는다.

  4. max_features가 sqrt라 worst radius가 후보로 뽑힐 기회를 통계적으로 덜 받았기 때문이다.

    후보 추출은 모든 특성에 같은 확률을 준다. 특정 특성만 불리해지지 않는다.

해설

상위 네 개가 0.12에서 0.13 사이에 몰려 있는 것을 네 특성이 비슷하게 중요하다고 읽으면 곤란하다. 반지름과 둘레와 넓이는 원 하나를 세 가지로 적어둔 것에 가까워 어느 노드에서든 셋 중 아무거나 뽑히면 되고, 그래서 하나가 받았어야 할 중요도가 셋에 쪼개져 들어간다. worst radius가 5위로 내려앉은 것도 실력 차이가 아니라 나눠 가진 결과다. 지니 기반 중요도에는 편향이 하나 더 있다. 임계값 후보가 많은 특성일수록 우연히 불순도를 줄이는 분할을 찾을 기회가 많아서 연속형이나 고유값이 많은 범주형이 과대평가된다. 순위를 진지하게 쓸 생각이라면 상관관계로 특성을 묶어 묶음마다 대표 하나만 남기고 다시 재는 편이 낫다.

이 내용 다시 읽기 →
분석과 판단진단
같은 모델에서 두 방식으로 중요도를 쟀다. 지니 중요도 worst area 0.1285, worst perimeter 0.1271 순열 중요도 worst perimeter 0.0009 ± 0.0026, worst radius 0.0009 ± 0.0026

순열 중요도가 전부 0에 붙어 있는 것을 어떻게 해석해야 하는가?

  1. 모델이 실제로는 아무 특성에도 의존하지 않고 클래스 비율만 예측하고 있다는 뜻이다.

    그렇다면 정확도가 다수 클래스 비율에 머물렀을 것이다. 모델은 잘 맞히고 있으며 다만 특성 하나를 망가뜨려도 흔들리지 않을 뿐이다.

  2. 정답특성 하나를 망가뜨려도 같은 정보를 담은 다른 특성이 남아 있어 예측이 흔들리지 않는다는 뜻이다.

    worst perimeter를 섞어도 worst radius와 worst area가 그대로 있으므로 모델이 무너지지 않는다. 두 방법이 다른 답을 내는 것은 특성들이 심하게 겹쳐 있다는 신호다.

  3. 순열 중요도는 테스트셋에서 재므로 표본이 적어 값이 0으로 수렴한 것이다.

    표본이 적으면 값이 흔들릴 뿐 0으로 수렴하지 않는다. 반복 횟수를 늘려도 중앙값은 그대로 0 근처에 머문다.

  4. 지니 중요도가 학습 시점의 값이라 과적합된 값이고 순열 중요도만 신뢰할 수 있다는 뜻이다.

    어느 한쪽이 맞고 다른 쪽이 틀린 상황이 아니다. 둘은 서로 다른 질문에 답하며, 어긋남 자체가 읽어야 할 정보다.

해설

순열 중요도는 학습이 끝난 모델에 검증 데이터를 넣되 특성 하나의 값만 행 사이에서 섞어 다시 예측하고 점수가 얼마나 떨어지는지를 잰다. 여기서 값이 전부 0에 붙은 것은 모델이 무너지지 않았기 때문이다. 한 특성을 망가뜨려도 같은 정보를 담은 다른 특성이 그대로 남아 예측이 흔들리지 않는다. 두 방법이 이렇게 다른 답을 내면 어느 쪽이 맞는지를 따질 것이 아니라 특성들이 서로 심하게 겹쳐 있다는 신호로 읽어야 한다. 이 상태에서는 어느 방법으로 재도 개별 특성의 중요도라는 것이 성립하지 않는다. 함께 기억할 것은 중요도가 인과가 아니라는 점이다. 중요도가 0인 특성도 쓸모없는 특성이 아니라 다른 특성이 이미 같은 정보를 담고 있어 이 모델이 고르지 않은 특성인 경우가 많다.

이 내용 다시 읽기 →
상황 적용운영
잡음이 많은 데이터에서 랜덤 포레스트가 과적합해 검증 점수가 훈련 점수에 한참 못 미친다. max_depth를 걸지 min_samples_leaf를 올릴지 고민 중이다.

어느 쪽을 먼저 손대는 편이 나은가?

  1. max_depth다. 트리 전체의 크기를 직접 정하므로 과적합 정도를 예측 가능하게 통제할 수 있다.

    예측 가능하다는 것이 곧 적절하다는 뜻은 아니다. 데이터가 촘촘한 가지까지 같은 깊이에서 잘리므로 살릴 수 있는 규칙까지 잃는다.

  2. max_depth다. 메모리 사용량이 깊이에 지수적으로 비례하므로 배포 부담까지 함께 줄일 수 있다.

    메모리는 실제로 리프 개수에 비례하지만 그것은 부수적인 효과다. 성능 손실을 기준으로 보면 다른 선택이 낫다.

  3. min_samples_leaf다. 트리 수를 늘리는 것과 같은 효과를 내면서 학습 시간도 함께 줄기 때문이다.

    트리 수를 늘리는 것은 분산을 줄이는 일이고 리프 조건은 개별 트리의 복잡도를 낮추는 일이라 성격이 다르다.

  4. 정답min_samples_leaf다. 데이터가 성긴 가지만 골라서 멈추게 하므로 촘촘한 쪽의 규칙은 그대로 남는다.

    깊이 제한은 트리 전체를 같은 칼로 자르는데 리프 최소 샘플 수는 표본이 부족한 자리에서만 작동한다. 잡음을 규칙으로 새기는 것은 대개 그 성긴 가지들이다.

해설

과적합을 잡을 때 깊이 제한은 트리 전체를 같은 깊이에서 자르므로 데이터가 촘촘해 믿을 만한 가지까지 함께 잘린다. 리프 최소 샘플 수는 표본이 부족한 자리에서만 분할을 멈추게 하므로 잡음을 규칙으로 새기는 성긴 가지만 골라서 막는다. 잡음이 많은 데이터에서 이 값을 5에서 20 사이로 올리면 눈에 띄게 안정된다. 같은 선택이 배포에서도 값을 한다. 랜덤 포레스트는 학습된 트리를 전부 들고 있어야 예측할 수 있고 트리 하나의 크기는 리프 개수에 비례하므로, 완전히 자란 트리 1000그루면 직렬화한 모델이 수백 MB에 이르기도 한다. 이럴 때 트리 수를 줄이는 것보다 리프 최소 샘플 수를 올리는 쪽이 성능 손실이 적다.

이 내용 다시 읽기 →
분석과 판단설계
특성 100개짜리 회귀 문제에 RandomForestRegressor를 기본값으로 돌렸더니 트리들이 서로 너무 닮아 보인다. 상위 몇 개 특성이 거의 모든 트리의 루트를 차지하고 있다.

무엇을 확인해야 하는가?

  1. n_estimators가 부족한 것이므로 트리 수를 먼저 크게 늘려야 한다.

    트리를 늘려도 상관관계가 만든 분산의 바닥은 그대로다. 트리 수로는 손댈 수 없는 항이 문제다.

  2. bootstrap이 꺼져 있는지 확인해야 한다. 꺼져 있으면 모든 트리가 같은 데이터를 받는다.

    확인해 볼 값이기는 하지만 기본값이 켜져 있고, 켜져 있어도 후보 특성 제한이 없으면 트리는 여전히 닮는다.

  3. 정답회귀의 max_features 기본값이 전체 특성이라 후보 제한이 없는 상태, 즉 사실상 배깅인지 확인해야 한다.

    sklearn의 회귀 기본값은 전체 특성이라 분류와 달리 후보를 솎아내지 않는다. 다른 구현과 참고 문헌은 회귀에서 전체의 3분의 1을 기본으로 잡는다.

  4. max_depth가 None이라 트리가 끝까지 자란 탓이므로 깊이를 제한해야 한다.

    깊이는 트리의 크기를 정할 뿐 루트에서 어느 특성이 뽑히는지를 바꾸지 못한다. 트리들이 닮는 원인과 무관하다.

해설

sklearn의 후보 특성 기본값은 분류에서 전체 특성 수의 제곱근이지만 회귀에서는 전체다. 회귀 쪽 기본값은 후보 제한이 아예 없다는 뜻이고, 그 상태는 사실상 배깅이라 트리들이 강한 특성을 나란히 루트에 놓게 된다. 다른 구현과 참고 문헌은 회귀에서 전체의 3분의 1을 기본으로 잡으므로 이 값을 줄여보는 것이 첫 수다. 방향은 한 축 위에 있다. 후보를 줄이면 트리 간 상관관계가 내려가는 대신 개별 트리가 약해지고 늘리면 반대가 된다. 값이 전체와 같아지면 후보 제한이 사라져 배깅이 되고, 지나치게 줄이면 개별 트리가 너무 약해진다. 최적점은 양 끝이 아니라 가운데 어딘가이므로 교차 검증으로 훑어야 한다.

이 내용 다시 읽기 →

틀린 것만 다시 배우는 부스팅, AdaBoost와 Gradient Boosting

원문 읽기 →
개념 확인구조

배깅이 분산을 줄이는 데 반해 부스팅이 편향을 줄이는 이유는 무엇인가?

  1. 부스팅이 훨씬 많은 트리를 쓰므로 모델의 표현력이 근본적으로 크기 때문이다.

    트리 수는 오히려 배깅 쪽이 많은 경우도 흔하다. 갈리는 곳은 개수가 아니라 각 트리가 무엇을 타겟으로 학습하느냐다.

  2. 정답새 트리가 앞선 모델이 남긴 오차를 타겟으로 삼으므로 모두가 공통으로 못 맞추던 부분을 겨냥하기 때문이다.

    배깅에서는 어느 트리도 다른 트리가 무엇을 틀렸는지 모르므로 제각각인 들쭉날쭉함만 평균에 깎인다. 부스팅은 남은 오차를 다음 트리에 넘겨 그 공통 부분을 직접 줄인다.

  3. 부스팅이 트리를 깊게 키워 개별 트리의 편향이 애초에 낮기 때문이다.

    부스팅은 오히려 얕은 트리를 쓴다. 개별 트리는 약한 채로 두고 누적으로 편향을 메운다.

  4. 부스팅이 샘플에 가중치를 주어 어려운 데이터를 여러 번 학습하므로 데이터가 실질적으로 늘어나기 때문이다.

    가중치는 AdaBoost의 구현 수단이고 데이터 양이 늘어나는 것도 아니다. 그래디언트 부스팅은 가중치 없이도 같은 목표를 이룬다.

해설

배깅은 트리 수백 그루를 서로 모르는 채로 학습시킨 뒤 투표로 합친다. 어느 트리도 다른 트리가 무엇을 틀렸는지 모르므로 트리마다 제각각인 들쭉날쭉함은 평균에 깎여 사라지지만, 모든 트리가 공통으로 못 맞추는 부분은 그대로 남는다. 분산은 줄고 편향은 그대로다. 부스팅은 트리를 하나씩 순서대로 만들되 새 트리가 앞선 모델이 남긴 오차만 겨냥하므로 그 공통 부분을 직접 줄인다. 여기서 다른 성질도 따라 나온다. 앞 트리의 예측이 있어야 다음 트리의 타겟이 정해지므로 트리 사이를 병렬화할 수 없어 같은 100그루라도 몇 배 오래 걸리고, 트리를 늘리면 과적합이 실제로 늘어난다. 잡음이 많거나 병렬로 돌려야 하면 배깅, 데이터가 깨끗하고 마지막 몇 퍼센트를 짜내야 하면 부스팅이다.

이 내용 다시 읽기 →
상황 적용구조
AdaBoost에서 분류기 가중치는 가중 오류율로 정해진다. ε = 0.5 → α = 0 ε = 0.3 → α = 0.42 ε = 0.1 → α = 1.10 ε = 0.7 → α = -0.42

이 값 하나가 하는 일은 무엇인가?

  1. 최종 투표에서의 지분만 정한다. 샘플 가중치는 별도의 규칙으로 갱신된다.

    샘플 가중치를 키우고 줄이는 배수가 바로 이 값의 지수다. 두 역할이 같은 숫자에 묶여 있다.

  2. 다음 라운드에서 뽑을 샘플의 개수를 정해 어려운 샘플을 더 많이 복원 추출하게 한다.

    AdaBoost는 샘플을 다시 뽑지 않고 가중치만 바꾼다. 데이터 구성은 라운드마다 그대로다.

  3. 학습률에 곱해져 다음 트리의 기여를 얼마나 줄일지 정한다.

    학습률로 기여를 줄이는 것은 그래디언트 부스팅의 구조다. 여기서 이 값은 학습률과 무관하게 오류율만으로 정해진다.

  4. 정답최종 투표에서의 지분과 그 분류기가 틀린 샘플의 가중치를 밀어 올리는 폭을 동시에 정한다.

    잘 맞춘 분류기일수록 투표에서 큰 몫을 갖고, 같은 값이 오분류 샘플의 가중치를 키우는 배수의 지수가 된다. 오류율이 0.5를 넘으면 수식상 음수가 되어 예측을 뒤집어 쓰는 것과 같아진다.

해설

분류기 가중치는 가중 오류율 하나로 정해지는데 이 값이 두 가지 일을 동시에 한다. 잘 맞춘 분류기일수록 최종 투표에서 큰 지분을 갖고, 같은 값이 그 분류기가 틀린 샘플의 가중치를 밀어 올리는 폭도 정한다. 오류율이 정확히 0.5면 값이 0이 되어 찍기와 같으므로 버려지고, 0.5를 넘으면 수식상 음수가 되어 예측을 뒤집어 쓰는 것과 같아진다. 다만 실제 구현들은 이런 분류기를 만나면 그 라운드에서 버리거나 중단하는 쪽을 택한다. 실제로 샘플 20개로 두 라운드만 돌려보면 균등하던 0.05가 연속으로 틀린 샘플에서 0.25까지 올라 출발점의 다섯 배가 되고, 한 번도 틀리지 않은 샘플은 0.021까지 내려간다. 세 번째 분류기가 보는 데이터는 사실상 그 두 샘플이다.

이 내용 다시 읽기 →
상황 적용함정
라벨링 오류가 섞인 데이터에 AdaBoost를 돌렸더니 라운드가 진행될수록 검증 점수가 떨어진다.

무엇이 일어나고 있는가?

  1. 정답아무리 학습해도 못 맞추는 샘플의 가중치만 지수적으로 커져 앙상블 전체가 그 몇 개에 맞춰 비틀린다.

    가중치가 라운드마다 배수로 곱해지며 누적되므로 계속 틀리는 샘플이 나머지 데이터를 압도한다. 라벨이 잘못 붙은 샘플은 정의상 계속 틀리는 샘플이다.

  2. 약한 학습기의 오류율이 0.5를 넘어 분류기 가중치가 음수가 되면서 예측이 뒤집혔다.

    부호가 뒤집혀 반영되는 것은 설계된 동작이라 그 자체로 성능을 떨어뜨리지 않는다. 문제는 어느 샘플에 무게가 쏠리느냐다.

  3. 라운드가 늘어날수록 개별 분류기가 강해져 결정 그루터기의 역할을 넘어서기 때문이다.

    약한 학습기의 구조는 라운드마다 같다. 깊이 1짜리 그루터기는 몇 번째 라운드든 그대로다.

  4. 정규화 단계에서 가중치의 합을 1로 맞추므로 정상 샘플의 가중치가 0으로 언더플로된다.

    정규화는 비율을 유지한 채 합만 맞추는 연산이라 값이 0으로 사라지지 않는다. 상대적으로 작아질 뿐이다.

해설

AdaBoost의 가중치는 라운드마다 배수로 곱해지며 누적된다. 라벨이 잘못 붙은 샘플처럼 아무리 학습해도 못 맞추는 데이터가 있으면 그 샘플의 가중치만 지수적으로 커져 나머지를 압도하고, 결국 앙상블 전체가 이상치 몇 개를 맞추려고 비틀린다. 부스팅이 이상치에 민감하다는 말의 구체적인 내용이 이것이고, 배깅이 이상치에 둔감한 것과 정확히 대비된다. 이상치가 섞인 데이터라면 그래디언트 부스팅이 낫고, 회귀에서는 손실 함수를 후버 손실로 바꿔 큰 잔차의 영향을 잘라낼 수 있다. 손실 함수를 갈아 끼우는 것만으로 이런 대응이 가능하다는 점이 그래디언트 부스팅이 살아남은 이유이기도 하다.

이 내용 다시 읽기 →
분석과 판단구조
Gradient Boosting에서 다음 트리가 학습하는 타겟은 손실 함수의 음의 그래디언트다. 제곱 오차 손실 → 타겟이 y - F, 즉 잔차 로그 손실 → 타겟이 y - p

이 정의가 잔차 대신 그래디언트를 쓰는 이유를 가장 잘 설명하는 것은 무엇인가?

  1. 그래디언트가 잔차보다 수치적으로 안정해 큰 오차에서도 발산하지 않기 때문이다.

    제곱 오차에서는 둘이 문자 그대로 같은 값이다. 안정성 때문에 고른 정의가 아니다.

  2. 그래디언트를 쓰면 트리 대신 어떤 학습기든 넣을 수 있게 되기 때문이다.

    학습기의 종류는 이 정의와 무관하게 자유롭다. 달라지는 쪽은 학습기가 아니라 손실 함수다.

  3. 정답손실 함수를 무엇으로 바꾸든 절차가 한 글자도 달라지지 않게 하려는 것이다.

    제곱 오차에서 음의 그래디언트가 잔차와 같아지므로 잔차 학습은 특수한 경우다. 미분 가능한 손실만 있으면 회귀든 분류든 순위 학습이든 같은 절차가 돌아간다.

  4. 잔차는 회귀에서만 정의되므로 분류에서는 확률의 로그 비를 대신 쓰기 위해서다.

    분류에서도 정답과 예측 확률의 차이라는 형태로 잔차 비슷한 값이 나온다. 요점은 특정 손실에 맞춘 유도가 아니라 어떤 손실에도 통하는 하나의 절차다.

해설

제곱 오차 손실을 쓰면 음의 그래디언트가 정답에서 예측을 뺀 값, 즉 잔차와 정확히 같아진다. 잔차를 학습한다는 직관이 성립하는 것은 이 손실에서이고, 그래디언트로 정의해 두면 손실 함수를 갈아 끼워도 절차가 한 글자도 달라지지 않는다. 분류에 쓰는 로그 손실이라면 타겟이 정답에서 예측 확률을 뺀 형태로 바뀌지만 나머지는 그대로다. 이 구조를 함수 공간에서의 경사하강법으로 읽을 수도 있다. 파라미터를 옮기는 대신 모델 자체를 옮기며, 트리 한 그루가 한 스텝이고 학습률이 스텝 크기다. 그래디언트 부스팅이 살아남은 것은 성능 때문이라기보다 미분 가능한 손실만 있으면 무엇이든 같은 틀에 넣을 수 있다는 이 유연함 때문이다.

이 내용 다시 읽기 →
상황 적용운영
n_estimators=200, max_depth=3으로 고정하고 학습률만 바꿨다. 0.001 훈련 0.6291 테스트 0.6224 0.01 훈련 0.9930 테스트 0.9580 0.1 훈련 1.0000 테스트 0.9580 1.0 훈련 1.0000 테스트 0.9441

이 표를 어떻게 읽어야 하는가?

  1. 0.001이 가장 안전한 값이고 트리 수를 늘릴 여유가 없으므로 0.01을 타협안으로 고르면 된다.

    0.001은 안전한 것이 아니라 200그루 예산 안에서 아예 학습되지 않은 상태다. 훈련 정확도가 0.63에 머문 것이 그 증거다.

  2. 정답학습률과 트리 수의 곱이 총 학습량이라 0.001은 트리가 더 필요하고 1.0은 초반 몇 그루에 끌려간다.

    학습률을 낮추면 같은 지점에 도달하는 데 더 많은 트리가 필요하다. 반대로 1.0은 트리 한 그루가 잔차를 통째로 삼켜버려 앙상블의 성격을 잃는다.

  3. 훈련 정확도가 1.0에 닿은 0.1과 1.0은 과적합이므로 훈련이 1.0 미만인 0.01만 후보로 남는다.

    훈련 정확도가 1.0인 것 자체는 판단 근거가 되지 못한다. 0.1은 0.01과 같은 테스트 정확도에 도달했다.

  4. 0.01과 0.1의 테스트 정확도가 같으므로 두 값 사이에 실질적인 차이가 없고 학습률은 튜닝할 필요가 없다.

    여기서 같아진 것은 200그루라는 예산 안에서 둘 다 충분히 수렴했기 때문이다. 예산이 달라지면 결과도 달라진다.

해설

학습률은 트리 한 그루의 기여를 얼마나 줄일지 정하므로 낮추면 같은 지점에 도달하는 데 더 많은 트리가 필요하다. 대략 학습률과 트리 수의 곱이 총 학습량이라고 보면 된다. 0.001은 200그루를 다 쓰고도 출발점에서 거의 못 벗어났고, 0.01과 0.1은 같은 예산 안에서 둘 다 충분히 수렴했으며, 1.0은 트리 한 그루가 잔차를 통째로 삼켜 초반 몇 그루에 끌려가는 모델이 됐다. 실무에서는 학습률을 0.05 근처로 낮게 잡고 트리 수를 넉넉히 준 다음 멈추는 시점을 조기 종료에 맡기는 쪽이 편하다. 다만 조기 종료가 찾아주는 것은 최고 성능 지점이 아니라 검증 손실이 더 이상 줄지 않는 지점이다. 검증용으로 떼어낸 몫이 작을수록 이 판단이 흔들리므로 데이터가 작다면 중단 조건을 넉넉히 주는 편이 안전하다.

이 내용 다시 읽기 →
분석과 판단설계
랜덤 포레스트에서 트리를 끝까지 키우는 것이 기본이었기에 GradientBoostingClassifier에도 max_depth=8을 줬다. 훈련 점수는 곧바로 1.0이 되는데 검증 점수는 기본값인 3보다 나쁘다.

왜 반대로 해야 하는가?

  1. 깊은 트리는 학습이 느려 같은 시간 안에 충분한 트리 수를 확보하지 못하기 때문이다.

    시간이 아니라 구조의 문제다. 시간을 넉넉히 줘 트리를 다 학습시켜도 검증 점수는 나아지지 않는다.

  2. 정답첫 트리가 훈련 데이터를 거의 맞춰버려 다음 트리가 학습할 잔차에 잡음만 남기 때문이다.

    오차를 나눠 갖는 부스팅의 구조 자체가 무너진다. 배깅은 강한 모델의 분산을 평균으로 깎아내는 쪽이고 부스팅은 약한 모델의 편향을 누적으로 메우는 쪽이라 요구하는 개별 모델이 정반대다.

  3. 깊은 트리는 리프마다 샘플이 적어 잔차의 부호가 뒤집히면서 학습이 발산하기 때문이다.

    잔차의 부호는 뒤집혀도 정상이며 발산도 일어나지 않는다. 학습률이 스텝 크기를 눌러주기 때문이다.

  4. 깊은 트리에서는 음의 그래디언트를 근사하는 정확도가 떨어져 경사하강 방향이 어긋나기 때문이다.

    깊을수록 그래디언트를 오히려 더 정확하게 근사한다. 문제는 근사가 부정확해서가 아니라 지나치게 정확해서 남는 것이 없다는 데 있다.

해설

깊은 트리는 혼자서도 훈련 데이터를 거의 맞춘다. 그러면 두세 번째 트리가 학습할 잔차에 잡음밖에 남지 않아 오차를 나눠 갖는 부스팅의 구조 자체가 무너진다. 랜덤 포레스트가 트리를 끝까지 키우는 것을 기본값으로 두는 것과 정확히 반대이고, 이유는 두 방법이 요구하는 개별 모델이 다르기 때문이다. 배깅은 강한 모델의 분산을 평균으로 깎아내는 쪽이라 편향이 낮고 분산이 큰 트리를 원하고, 부스팅은 약한 모델의 편향을 누적으로 메우는 쪽이라 각 트리가 약해야 한다. 부스팅의 권장 깊이가 3에서 5 사이인 것도 그래서다. 앙상블에서 하이퍼파라미터를 옮길 때는 값 자체를 따라가지 말고 그 방법이 무엇을 줄이려 하는지부터 확인해야 한다.

이 내용 다시 읽기 →

XGBoost와 LightGBM은 실제로 무엇이 다른가

원문 읽기 →
개념 확인구조

level-wise 성장과 leaf-wise 성장의 차이는 무엇인가?

  1. level-wise는 트리를 위에서 아래로 키우고 leaf-wise는 리프에서 위로 거슬러 올라간다.

    둘 다 루트에서 시작해 아래로 자란다. 갈리는 것은 방향이 아니라 다음에 어느 노드를 분할할지 고르는 규칙이다.

  2. level-wise는 모든 특성을 후보로 보고 leaf-wise는 이득이 큰 특성만 후보로 남긴다.

    특성 후보를 추리는 것은 분할점 탐색의 이야기다. 성장 방식이 정하는 것은 어느 노드를 다음에 쪼갤지다.

  3. 정답level-wise는 같은 깊이의 노드를 전부 분할하고 leaf-wise는 존재하는 리프 중 이득이 가장 큰 하나만 분할한다.

    분할 횟수가 같아도 나오는 트리의 모양이 다르다. leaf-wise는 손실이 줄어드는 곳에만 예산을 쓰므로 트리가 한쪽으로 깊어진다.

  4. level-wise는 트리마다 깊이를 고정하고 leaf-wise는 트리마다 깊이를 다르게 정한다.

    깊이를 고정하거나 달리하는 것은 하이퍼파라미터가 정한다. 성장 방식은 분할 대상을 고르는 규칙이다.

해설

level-wise는 같은 깊이의 노드를 전부 분할한 다음 아래로 내려가고, leaf-wise는 지금 존재하는 리프 전체 중에서 이득이 가장 큰 하나만 골라 분할한다. 분할 횟수가 같아도 결과로 나오는 트리의 모양이 다르다. 리프 수가 같다면 leaf-wise 쪽이 훈련 손실을 더 많이 깎는데, 손실이 줄어드는 곳에만 예산을 쓰기 때문이다. 대신 트리가 한쪽으로만 깊어져서 데이터가 작으면 그 깊은 가지가 소수 샘플의 특징을 그대로 외운다. LightGBM이 작은 데이터에서 과적합한다는 말은 이 성질을 가리킨다. 다만 이 대비는 라이브러리의 차이가 아니라 기본값의 차이다. XGBoost도 성장 정책을 바꾸면 leaf-wise로 자라고, 이때는 깊이 대신 리프 수로 트리 크기를 제한한다.

이 내용 다시 읽기 →
상황 적용함정
XGBoost에서 max_depth=7로 좋은 결과를 얻어 LightGBM으로 옮기면서 같은 크기가 되도록 리프 수를 127로 맞췄다. 훈련 손실은 훨씬 낮아졌는데 검증 점수가 떨어진다.

무엇이 잘못됐는가?

  1. 리프 수와 깊이는 단위가 달라 서로 변환할 수 없으므로 애초에 옮겨 적으면 안 된다.

    변환 자체는 성립한다. 깊이 7의 균형 트리 리프가 최대 2⁷ = 128개라 크기의 대응은 맞고, 문제는 그 리프가 어떤 모양으로 소비되느냐다.

  2. 정답leaf-wise 트리는 같은 리프 수를 훨씬 깊은 형태로 쓰므로 제한이 사실상 풀린 것과 같아진다.

    127개 리프를 균형 잡힌 깊이 7이 아니라 한쪽으로 파고든 훨씬 깊은 형태로 소비한다. 공식 튜닝 문서가 이 경우 127이 아니라 70에서 80 사이를 권하는 이유다.

  3. LightGBM은 히스토그램 분할만 쓰므로 분할점 후보가 부족해 리프를 다 채우지 못한다.

    히스토그램 구간은 수백 개라 리프 127개를 만드는 데 부족하지 않다. 분할점 정밀도와 무관한 문제다.

  4. 깊이 제한이 함께 걸리지 않아 트리가 무한히 자라 학습이 발산한 것이다.

    리프 수 상한이 걸려 있으므로 무한히 자라지 않는다. 발산이 아니라 과적합이다.

해설

깊이 7의 균형 트리는 리프가 최대 2⁷ = 128개라, 그 근처로 리프 수를 잡으면 크기가 대응되는 것처럼 보인다. 하지만 leaf-wise 트리는 같은 리프 수를 훨씬 깊은 형태로 소비하므로, 그대로 옮기면 제한이 사실상 풀린 것과 다름없다. 공식 튜닝 문서는 깊이 7로 좋은 정확도가 나오는 데이터라면 리프 수를 127이 아니라 70에서 80 사이로 권한다. 순서도 정해져 있다. 리프 수를 먼저 정하고 깊이는 뒤에서 받쳐주는 안전장치로 둔다. 여기에 리프 최소 행 수까지 걸어 리프에 최소 몇 행은 남게 강제하는 것이 실질적인 제동 장치다. 두 라이브러리 사이에서 맞춰야 할 축은 깊이가 아니라 리프 수라고 기억하면 된다.

이 내용 다시 읽기 →
상황 적용구조
GOSS는 그래디언트 절댓값 상위 20%를 전부 남기고 나머지에서 전체 대비 10%를 무작위로 뽑는다. 그리고 무작위로 뽑힌 행의 그래디언트와 헤시안에 8을 곱한다. 배율 = (1 - 0.2) / 0.1 = 8

이 배율을 곱하는 이유는 무엇인가?

  1. 무작위로 뽑힌 행이 정보량이 적으므로 가중치를 올려 학습 기여를 맞추기 위해서다.

    정보량이 적다는 이유로 가중치를 올리지 않는다. 보정의 목적은 기여를 키우는 것이 아니라 원래 비중을 되돌리는 것이다.

  2. 그래디언트가 작은 행에서 수치 언더플로가 나지 않도록 값을 키워두기 위해서다.

    언더플로는 여기서 문제가 되지 않고, 배율도 데이터가 아니라 두 샘플링 비율로만 정해진다.

  3. 상위 20%가 중복 선택될 수 있으므로 그만큼 아래쪽의 무게를 늘려 균형을 맞추기 위해서다.

    상위 구간은 중복 없이 전부 그대로 가져간다. 보정 대상은 솎아낸 아래쪽이다.

  4. 정답작은 그래디언트 쪽을 솎아낸 만큼 분포가 왜곡되므로 남은 표본으로 원래 비중을 복원하기 위해서다.

    아래쪽 80%에서 전체의 10%만 남겼으므로 뽑힌 한 행이 여덟 행을 대표한다. 이 배율을 곱하지 않으면 분할 이득 계산이 큰 그래디언트 쪽으로 치우친다.

해설

GOSS는 그래디언트 절댓값이 큰 행은 지금 모델이 크게 틀리고 있어 정보량이 많고 작은 행은 이미 잘 맞추고 있어 얻을 것이 적다는 관찰에서 출발한다. 그래서 앞쪽은 전부 남기고 뒤쪽만 솎아내는데, 그냥 끝내면 작은 그래디언트 쪽의 비중이 줄어들어 분포가 왜곡된다. 아래쪽 80%에서 전체의 10%만 남겼으므로 뽑힌 한 행이 여덟 행을 대표하는 셈이고, 그 배율을 그래디언트와 헤시안에 곱해 원래 비중을 복원한다. 한 가지 짚어둘 것은 GOSS가 기본값이 아니라는 점이다. LightGBM의 행 샘플링 기본값은 평범한 배깅이고 GOSS는 직접 켜야 한다. 논문이 말한 큰 폭의 속도 향상도 GOSS 하나가 아니라 히스토그램 분할과 특성 묶기까지 합친 결과다.

이 내용 다시 읽기 →
분석과 판단함정
XGBoost 설정을 LightGBM으로 옮겼다. XGBoost subsample=0.8, min_child_weight=20 LightGBM bagging_fraction=0.8, min_child_weight=20 실행은 되는데 두 모델의 동작이 전혀 다르다.

어긋난 곳은 어디인가?

  1. 정답행 샘플링은 빈도 파라미터를 함께 주지 않아 꺼져 있고, min_child_weight는 양쪽 모두 헤시안 합이라 리프에 20행을 요구하지 않는다.

    LightGBM의 행 샘플링은 빈도를 1 이상으로 주지 않으면 아무 일도 하지 않는다. 그리고 이 이름은 양쪽 모두 헤시안 합이라 행 개수를 강제하려면 리프 최소 행 수 파라미터를 써야 한다.

  2. 행 샘플링 파라미터의 이름만 다를 뿐 동작은 같고, min_child_weight가 LightGBM에서는 행 개수를 뜻해 제약이 훨씬 세진다.

    방향이 반대다. LightGBM에서 행 개수를 뜻하는 것은 별칭이 다른 파라미터이고, 이 이름은 헤시안 합의 별칭이다.

  3. min_child_weight는 양쪽에서 같은 뜻이고 행 샘플링 비율의 기준이 트리 단위와 노드 단위로 달라 결과가 갈린 것이다.

    행 샘플링은 양쪽 모두 트리 단위로 적용된다. 문제는 기준이 아니라 빈도 파라미터 없이는 적용 자체가 되지 않는다는 점이다.

  4. 두 라이브러리의 기본 성장 방식이 달라 같은 파라미터도 다른 효과를 내는 것이므로 파라미터 자체는 잘못 옮기지 않았다.

    성장 방식 차이는 별개로 존재하지만 여기서는 파라미터가 실제로 잘못 옮겨졌다. 하나는 꺼져 있고 하나는 단위가 다르다.

해설

이름만 보고 옮기면 다른 모델이 만들어지는 자리가 두 군데 있다. 하나는 행 샘플링이다. LightGBM의 비율 파라미터는 빈도 파라미터를 함께 1 이상으로 주지 않으면 아무 일도 하지 않으므로, 비율만 적어두고 서브샘플링이 걸렸다고 생각하는 실수가 여기서 나온다. 다른 하나는 리프 제약이다. min_child_weight는 양쪽 모두 리프의 헤시안 합에 대한 하한이지 행 개수가 아니다. LightGBM에서 이 이름은 헤시안 합 파라미터의 별칭이고, 행 개수를 강제하는 것은 이름이 비슷한 다른 파라미터의 별칭이다. 기본값도 XGBoost의 min_child_weight는 헤시안 합 1, LightGBM의 min_data_in_leaf는 행 수 20으로 단위 자체가 다르다. 파라미터를 옮길 때는 이름이 아니라 그 값이 무엇의 단위인지부터 확인해야 한다.

이 내용 다시 읽기 →
상황 적용구조
원-핫 인코딩된 세 컬럼을 한 컬럼으로 묶은 결과다. city_seoul 1, 0, 0, 1, 0 city_busan 0, 1, 0, 0, 1 city_daegu 0, 0, 1, 0, 0 city_bundled 1, 2, 3, 1, 2

이 변환이 성립하는 조건과 얻는 것은 무엇인가?

  1. 세 컬럼의 상관이 낮을 때 성립하고, 특성 사이의 중복을 제거해 과적합을 줄인다.

    상관의 높낮이가 조건이 아니다. 필요한 것은 동시에 0이 아닌 값을 갖지 않는다는 배타성이고, 얻는 것도 정확도가 아니라 속도다.

  2. 세 컬럼의 카디널리티가 같을 때 성립하고, 범주형을 정수로 바꿔 트리가 직접 분할할 수 있게 한다.

    카디널리티가 조건이 아니고, 범주형 직접 분할은 별개의 기능이다. 이 변환은 히스토그램을 만드는 비용을 줄인다.

  3. 정답세 컬럼이 서로 배타적일 때 성립하고, 정보를 잃지 않은 채 히스토그램 계산 비용을 줄인다.

    한 행에서 많아야 하나만 0이 아니므로 값이 겹치지 않아 되돌릴 수 있다. 히스토그램을 만드는 비용은 특성 수에 비례하므로 희소한 고차원 데이터일수록 이득이 크다.

  4. 세 컬럼의 결측 패턴이 같을 때 성립하고, 결측치를 한 번만 처리하면 되도록 만든다.

    결측 처리와 무관한 변환이다. 트리 계열은 결측이 갈 방향을 따로 학습하므로 묶는다고 처리가 줄지 않는다.

해설

특성 묶기는 동시에 0이 아닌 값을 갖는 일이 없는 특성들, 즉 서로 배타적인 특성들을 한 컬럼으로 합친다. 원-핫 인코딩 결과가 전형적인 대상인데, 한 행에서 많아야 하나만 0이 아니므로 값의 범위를 어긋나게 배치하면 정보를 하나도 잃지 않고 되돌릴 수 있다. 얻는 것은 속도다. 히스토그램을 만드는 비용은 특성 수에 비례하므로 희소한 고차원 데이터일수록 이득이 크다. 다만 애초에 원-핫으로 펼치지 않는 편이 낫다는 점도 함께 기억할 만하다. 습관적으로 펼치면 차원이 불어나고 트리는 한 번에 범주 하나만 떼어내는 분할밖에 할 수 없게 된다. 세 라이브러리 모두 범주형을 직접 다루는 경로가 있으니 그쪽을 먼저 시도하는 것이 순서다.

이 내용 다시 읽기 →
분석과 판단구조
XGBoost가 손실 함수를 2차까지 테일러 전개해 얻는 리프 최적값이다. w* = -( Σ g ) / ( Σ h + λ ) g는 1차 도함수, h는 2차 도함수, λ는 정규화 계수다.

이 식에서 읽어야 할 것은 무엇인가?

  1. 2차 도함수를 쓰면 손실 함수의 종류에 무관해지므로 어떤 손실에도 같은 식이 그대로 쓰인다.

    손실이 달라지면 1차와 2차 도함수의 값이 달라진다. 형태가 같을 뿐 계산되는 값은 손실마다 다르다.

  2. 정답정규화가 분모에 들어앉아 리프 값을 눌러주고, 헤시안까지 쓰므로 같은 트리 수에서 손실을 더 깎는다.

    λ가 클수록 리프 값이 0 쪽으로 당겨져 예측이 보수적으로 바뀐다. 그래디언트만 쓰는 방식보다 뉴턴법에 가까워 수렴이 빠르다.

  3. 분모가 0이 될 수 있으므로 λ는 수치 안정성을 위해 넣은 값이고 정규화 효과는 부수적이다.

    순서가 반대다. λ는 목적 함수에 명시적으로 들어 있는 정규화 항에서 나왔고, 분모를 지켜주는 것은 부수적인 효과다.

  4. 헤시안 합이 클수록 리프 값이 커지므로 샘플이 많은 리프가 더 큰 예측을 내놓는다.

    헤시안 합은 분모에 있으므로 클수록 리프 값이 작아진다. 방향이 반대다.

해설

손실 함수를 현재 예측 주변에서 2차까지 전개하면 각 리프의 최적값과 분할 이득이 닫힌 형태로 나온다. 그 식의 분모에 정규화 계수가 들어앉아 있어 값이 클수록 리프 값이 0 쪽으로 당겨지고 예측이 보수적으로 바뀐다. 정규화가 학습이 끝난 뒤의 가지치기가 아니라 분할 여부를 판단하는 그 순간에 비용으로 반영된다는 뜻이다. 목적 함수에는 리프 하나를 늘릴 때마다 물리는 비용도 함께 들어 있어 분할 자체를 억제한다. 헤시안까지 쓰는 것은 수렴 쪽의 이득이다. 그래디언트만 쓰는 방식보다 뉴턴법에 가까워져 같은 트리 수에서 손실을 더 깎는다. 정규화를 목적 함수에 박아 넣고 깊이로 트리를 통제하는 이 보수적인 설계가 XGBoost의 성격을 만든다.

이 내용 다시 읽기 →

이 페이지는 문항 품질을 점검하는 용도로도 씁니다. 한 화면에서 전부 훑어야 중복된 문항, 쏠린 난이도, 소거법으로 풀리는 선택지가 보입니다.

Thank You for Visiting My Blog, Have a Good Day 😆