강 문제 에서 나무의 가장 큰 약점을 봤습니다. 표본 개 중 개만 바꿔도 첫 나눔 자리의 표준편차가 이었습니다.
흔들리는 방향이 서로 다르면 평균이 그 흔들림을 지웁니다. 강 문제 에서 본 그 계산입니다.
그런데 그냥 여럿 심으면 전부 같은 나무가 나옵니다. 이 강의의 절반은 나무를 서로 다르게 만드는 방법이고, 나머지 절반은 그렇게 만든 숲이 무엇을 고치고 무엇을 못 고치는지입니다.
문제. 평균의 효과를 정확히 셉니다.
() 상관이 있을 때의 공식을 정리하세요.
() 수치로 확인하세요.
() 실제 나무들의 상관을 재세요.
생각의 실마리. 강에서는 독립인 것들을 평균 냈습니다. 나무들은 같은 자료로 만들었으므로 독립이 아닙니다. 그 차이가 전부입니다.
풀이. () 정리합니다.
| 무엇 | 식 | 무엇을 뜻하나 |
|---|---|---|
| 독립인 개의 평균 | 분산 나누기 | 배 정확해짐 |
| 상관 인 개 | 더하기 나머지 | 에서 멈춤 |
| 을 무한히 키움 | \rho\sigma^ | 상관이 바닥 |
| 그래서 할 일 | 상관을 낮춤 | 강의 핵심 |
을 아무리 키워도 아래로는 안 갑니다.
() 수치로 확인합니다. 로 두고 잽니다.
| 상관 | 이 | 이 | 이 |
|---|---|---|---|
상관이 이면 개 평균이 로 내려갑니다. 상관이 면 개를 평균 내도 에서 멈춥니다.
나무를 많이 심는 것보다 나무를 서로 다르게 만드는 것이 중요합니다.
() 실제 나무들의 상관을 잽니다. 베이즈 최적 정확도는 입니다.
| 어떻게 다르게 만드나 | 나무끼리 예측 상관 | 한 그루 정확도 | 그루 정확도 |
|---|---|---|---|
| 같은 자료 같은 규칙 | |||
| 붓스트랩만 | |||
| 변수 뽑기만 | |||
| 둘 다 |
첫 줄은 나무가 전부 같으므로 상관이 정확히 이고 평균이 소용없습니다. 그루가 한 그루와 같은 입니다.
붓스트랩과 변수 뽑기를 함께 쓰면 상관이 로 가장 낮습니다. 그런데 그루 정확도는 붓스트랩만 쓴 쪽이 으로 가장 높습니다.
상관만 낮추면 되는 것이 아닙니다. 개별 나무도 함께 나빠지기 때문입니다. 한 그루 정확도가 에서 으로 떨어졌습니다.
둘의 절충을 찾는 것이 이 방법의 손잡이입니다.
이 문제에서 배우는 것. 앙상블의 효과는 개별 성능과 다양성의 곱으로 정해집니다. 어느 한쪽만 밀면 손해입니다. 강의 부스팅은 이 절충을 다른 방식으로 풉니다.
확인 1-1. 상관 인 것 개를 평균 낼 때 분산이 어디서 멈추는지 쓰세요.
답. 에서 멈춥니다.
확인 1-2. 검산에서 상관 일 때 의 값을 쓰세요.
답. 입니다.
확인 1-3. 검산에서 상관이 가장 낮은 방법과 그루 정확도가 가장 높은 방법을 쓰세요.
답. 둘 다 쓰는 것과 붓스트랩만 쓰는 것입니다.
문제. 다양성을 만드는 두 장치를 봅니다.
() 붓스트랩이 자료를 얼마나 바꾸는지 세세요.
() 안 본 자료로 성능을 재세요.
() 변수 뽑기 개수를 바꿔 가며 재세요.
생각의 실마리. 강의 붓스트랩은 불확실성을 재는 도구였습니다. 여기서는 자료를 흔드는 장치로 씁니다. 같은 도구가 다른 목적에 쓰입니다.
풀이. () 붓스트랩이 자료를 얼마나 바꾸는지 셉니다.
| 표본 크기 | 뽑히지 않을 확률 | 실제 비율 | 나누기 |
|---|---|---|---|
나무마다 약 퍼센트를 안 봅니다. 그 자료로 성능을 잴 수 있습니다.
() 안 본 자료로 성능을 잽니다. 나무 그루를 심고, 각 점에 대해 그 점을 안 본 나무들만 모아 예측합니다.
| 무엇으로 쟀나 | 정확도 | 검증과의 차 |
|---|---|---|
| 학습 자료 그대로 | ||
| 안 본 자료 | ||
| 따로 둔 검증 자료 |
학습 자료로 재면 정확도가 정확히 입니다. 나무마다 그 점을 봤기 때문입니다.
안 본 자료는 만 낙관됩니다. 학습 자료의 에 비하면 훨씬 정직합니다.
강의 교차검증을 따로 안 해도 되는 것이 이 방법의 이점입니다. 학습 한 번에 성능 추정이 딸려 나옵니다.
() 변수 뽑기 개수를 바꿔 가며 잽니다. 변수는 모두 개입니다.
| 한 나눔에 보는 변수 수 | 나무끼리 상관 | 한 그루 정확도 | 그루 정확도 |
|---|---|---|---|
변수를 적게 보면 나무 하나는 나빠지고 상관은 낮아집니다. 개만 보면 상관 로 가장 낮은데 그루 정확도도 으로 가장 낮습니다.
이 자료에서는 개나 개가 가장 좋습니다. 분류에서는 변수 수의 제곱근이 관례적 기본값이고 여기서는 나 입니다.
기본값이 언제나 최선은 아닙니다. 강의 교차검증으로 골라야 합니다.
이 문제에서 배우는 것. 붓스트랩은 자료를 흔들고 변수 뽑기는 나눔을 흔듭니다. 둘 다 상관을 낮추는데, 낮추는 만큼 개별 나무를 망가뜨립니다. 그리고 붓스트랩은 공짜 성능 추정을 덤으로 줍니다.
확인 2-1. 붓스트랩에서 한 점이 안 뽑힐 확률의 극한을 쓰세요.
답. 나누기 이고 약 입니다.
확인 2-2. 검산에서 학습 자료와 안 본 자료의 낙관 크기를 쓰세요.
답. 과 입니다.
확인 2-3. 검산에서 변수 개와 개를 볼 때의 그루 정확도를 쓰세요.
답. 과 입니다.
문제. 편향과 분산으로 나눠 봅니다.
() 편향과 분산을 따로 재세요.
() 기울어진 경계가 여전히 안 되는지 보세요.
() 깊이를 제한해야 하는지 정하세요.
생각의 실마리. 평균은 분산을 줄이는 장치입니다. 편향은 안 건드립니다. 강의 분해가 그대로 적용됩니다.
풀이. () 매끄러운 함수를 계단으로 덮어야 하는 문제에서 잽니다.
| 무엇 | 편향 제곱 | 분산 | 합 | 예측값 가짓수 |
|---|---|---|---|---|
| 나무 한 그루 깊이 | ||||
| 나무 한 그루 깊이 | ||||
| 그루 깊이 | ||||
| 그루 깊이 |
깊이를 키우면 편향은 에서 로 줄고 분산이 에서 로 커집니다.
그루를 늘리면 분산만 에서 으로 줄고 편향은 에서 으로 거의 그대로입니다.
예측값 가짓수가 에서 로 늘어 계단이 매끄러워집니다. 강 문제 에서 나무 하나의 예측값이 잎 개수를 못 넘는다고 했는데, 평균을 내면 그 한계가 풀립니다.
() 기울어진 경계가 여전히 안 되는지 봅니다. 잡음이 없는 도 경계입니다.
| 무엇 | 정확도 | 남은 오류율 |
|---|---|---|
| 나무 한 그루 | ||
| 그루 | ||
| 그루 |
그루를 까지 늘려도 오류율이 에서 멈춥니다. 잡음이 하나도 없는 문제인데도 그렇습니다.
평균이 계단의 모서리를 부드럽게 할 뿐 대각선을 만들지는 못합니다. 강 문제 의 한계가 그대로 남습니다. 편향은 평균으로 안 줄어들기 때문입니다.
() 깊이를 제한해야 하는지 정합니다. 한 그루 정확도는 그 숲을 이루는 나무 그루 각각을 평균 낸 값입니다.
| 깊이 | 한 그루 정확도 | 그루 정확도 | 차 |
|---|---|---|---|
한 그루도 그루도 깊이를 키울수록 좋아집니다. 다만 둘의 차가 에서 로 벌어집니다.
깊게 키울수록 나무의 분산이 커지고 평균이 지울 것이 많아집니다. 그래서 랜덤포레스트는 가지치기를 거의 안 합니다. 강에서 나무 하나에는 필수였던 가지치기가 여기서는 불필요합니다.
이 문제에서 배우는 것. 숲은 분산 기계입니다. 편향은 개별 나무가 정하고 숲은 못 건드립니다. 그래서 개별 나무를 편향이 낮게(깊게) 만들고 분산은 숲에 맡기는 것이 옳은 전략입니다.
확인 3-1. 숲이 편향과 분산 중 무엇을 줄이는지 쓰세요.
답. 분산만 줄이고 편향은 그대로입니다.
확인 3-2. 검산에서 한 그루 깊이 와 그루의 분산을 쓰세요.
답. 와 입니다.
확인 3-3. 검산에서 도 경계에 그루를 심었을 때의 남은 오류율을 쓰세요.
답. 입니다.
문제. 숲이 주는 부산물을 봅니다.
() 섞기 중요도를 재세요.
() 나눔 이득 중요도와 견주세요.
() 근접도로 구조를 보세요.
생각의 실마리. 어떤 변수가 중요한지 알려면 그 변수를 망가뜨려 보면 됩니다. 안 본 자료가 있으므로 공짜로 잴 수 있습니다.
풀이. () 섞기 중요도를 잽니다. 참으로 쓰이는 변수는 번 번 번이고, 번은 번과 상관 인 복사본이며 번은 가짓수만 많습니다.
| 변수 | 참 계수 | 섞었을 때 정확도 하락 | 무엇을 뜻하나 |
|---|---|---|---|
| 참으로 쓰임 | |||
| 번의 복사본 | |||
| 참으로 쓰임 | |||
| 참으로 쓰임 | |||
| 가짓수만 많음 | |||
| 쓸모없음 | |||
| 쓸모없음 |
번과 번이 둘 다 높게 나옵니다. 참 정보는 하나인데 둘로 나뉘어 붙었습니다. 나무마다 변수 셋만 보므로 어떤 나무는 번을 어떤 나무는 번을 씁니다.
번은 참 계수가 인데 로 아주 낮습니다. 이진 변수라 나눌 자리가 하나뿐이기 때문입니다.
쓸모없는 변수들은 처럼 음수도 나옵니다. 섞었더니 오히려 나아진 것인데, 이것은 잡음입니다.
() 나눔 이득 중요도와 견줍니다.
| 변수 | 참 계수 | 나눔 이득 중요도 | 가짓수 |
|---|---|---|---|
번은 참 계수가 인데 번보다 높습니다. 복사본이 원본을 앞질렀습니다.
번은 참 계수가 인데 로 가장 낮습니다. 가짓수가 뿐이기 때문이고, 쓸모없는 번과 번이 번보다 네 배 높게 나옵니다.
강 문제 의 가짓수 편향이 숲에서도 그대로 남습니다. 그래서 섞기 중요도를 쓰는 것이 안전합니다.
() 근접도로 구조를 봅니다. 두 점이 같은 잎에 떨어진 비율을 근접도라 합니다.
| 두 점의 관계 | 평균 근접도 | 쌍의 개수 |
|---|---|---|
| 같은 갈래 | ||
| 다른 갈래 |
같은 갈래끼리 근접도가 네 배 이상 높습니다. 숲이 거리를 배운 셈입니다.
강 심화 의 거리 학습을 나무가 스스로 한 것입니다. 우리가 거리를 정해 준 것이 아니라 라벨을 맞추다 보니 거리가 생겼습니다. 강의 차원 축소에 이 근접도를 거리로 쓸 수 있습니다.
이 문제에서 배우는 것. 변수 중요도는 두 방법 모두 함정이 있습니다. 상관된 변수는 중요도를 나눠 갖고, 가짓수가 적은 변수는 과소평가됩니다. 순위를 그대로 믿지 말고 무엇이 왜 그렇게 나왔는지 봐야 합니다.
확인 4-1. 섞기 중요도가 무엇을 재는지 쓰세요.
답. 그 변수를 망가뜨렸을 때 성능이 얼마나 떨어지는지 잽니다.
확인 4-2. 검산에서 번 변수의 두 중요도를 쓰세요.
답. 과 입니다.
확인 4-3. 검산에서 같은 갈래와 다른 갈래의 평균 근접도를 쓰세요.
답. 과 입니다.
문제. 손잡이와 한계를 정리합니다.
() 손잡이를 정리하세요.
() 그루를 늘려 나빠지는지 확인하세요.
() 외삽이 안 되는 것을 보이세요.
생각의 실마리. 손잡이가 여럿인데 성격이 다릅니다. 그루 수는 강의 복잡도 손잡이가 아니고, 변수 뽑기 수는 맞습니다.
풀이. () 정리합니다.
| 손잡이 | 무엇을 바꾸나 | 어떻게 정하나 |
|---|---|---|
| 그루 수 | 분산만 줄임 | 안 좋아질 때까지 |
| 변수 뽑기 수 | 상관과 개별 성능 | 제곱근에서 시작 |
| 깊이 | 개별 나무의 편향 | 제한 안 해도 됨 |
| 잎 최소 크기 | 개별 나무의 분산 | 회귀는 분류는 |
| 표본 뽑기 비율 | 상관 | 이 기본 |
() 그루를 늘려 나빠지는지 확인합니다.
| 그루 수 | 검증 정확도 | 앞줄 대비 | 최적과의 차 |
|---|---|---|---|
그루의 이 그루에서 이 됩니다. 앞줄 대비 개선이 에서 로 빠르게 줄어듭니다.
에서 으로 가며 떨어진 것은 우연의 흔들림입니다. 추세로는 평평해질 뿐 나빠지지 않습니다. 강의 과적합 손잡이가 아니기 때문이고, 그루 수는 계산 비용과 맞바꾸는 것일 뿐입니다.
갈래가 치우쳤을 때를 봅니다.
| 양성 비율 | 정확도 | 양성 재현율 | 문턱을 비율로 낮추면 재현율 |
|---|---|---|---|
문턱을 그대로 쓰면 재현율이 에서 로 떨어집니다. 정확도는 오히려 에서 으로 올라가므로 정확도만 보면 못 잡습니다.
문턱을 양성 비율로 낮추면 재현율이 까지 올라옵니다. 강과 강에서 문턱과 가중치를 제대로 다룹니다.
() 한계를 정리하고 외삽을 확인합니다.
| 무엇을 못 하나 | 왜 |
|---|---|
| 기울어진 경계 | 여전히 계단으로 덮음 |
| 외삽 | 학습 범위 밖은 상수 |
| 설명 | 나무 하나의 읽힘을 잃음 |
| 아주 많은 변수 | 나눔 고르기가 느려짐 |
학습은 가 에서 사이이고 참 함수는 입니다.
| 참값 | 숲의 예측 | 차 | |
|---|---|---|---|
학습 범위 밖에서는 예측이 에 완전히 갇힙니다. 에서도 에서도 같은 값입니다. 마지막 잎의 값을 그대로 내놓기 때문입니다.
선형 모형은 이 자리에서 그냥 늘어납니다. 가정이 있기 때문이고, 가정이 없다는 것의 대가가 이것입니다.
이 문제에서 배우는 것. 랜덤포레스트는 손이 거의 안 가는 방법입니다. 표준화도 가지치기도 교차검증도 필요 없습니다. 대신 외삽을 못 하고 설명을 잃습니다. 무엇을 포기하는지 알고 써야 합니다.
확인 5-1. 그루 수가 강의 복잡도 손잡이가 아닌 이유를 쓰세요.
답. 분산만 줄이므로 늘려서 나빠지지 않기 때문입니다.
확인 5-2. 검산에서 양성 비율 일 때 두 재현율을 쓰세요.
답. 와 입니다.
확인 5-3. 검산에서 와 의 숲 예측을 쓰세요.
답. 둘 다 입니다.
| 유형 | 무엇을 묻나 | 어디를 보나 |
|---|---|---|
| 상관과 평균 | 에서 멈춤 | 문제 |
| 다양성의 대가 | 개별 나무가 나빠짐 | 문제 |
| 붓스트랩 | 퍼센트를 안 봄 | 문제 |
| 안 본 자료 | 교차검증을 대신함 | 문제 |
| 변수 뽑기 | 상관과 성능의 절충 | 문제 |
| 분산만 줄임 | 편향은 그대로 | 문제 |
| 가지치기 불필요 | 깊을수록 평균이 이득 | 문제 |
| 중요도의 함정 | 상관과 가짓수 | 문제 |
| 근접도 | 숲이 배운 거리 | 문제 |
| 외삽 불가 | 마지막 잎에 갇힘 | 문제 |
핵심 식을 한자리에 모읍니다.
| 방법 | 자료를 흔드나 | 나눔을 흔드나 | 이름 |
|---|---|---|---|
| 나무 하나 | 아니오 | 아니오 | 의사결정나무 |
| 붓스트랩만 | 예 | 아니오 | 배깅 |
| 둘 다 | 예 | 예 | 랜덤포레스트 |
| 나눔 자리까지 무작위 | 예 | 더 세게 | 극단 무작위 나무 |
문제 6. 상관 인 것 개를 평균 낼 때 분산이 어디서 멈추는지 쓰세요.
답. 에서 멈춥니다.
문제 7. 검산에서 상관 일 때 의 값을 쓰세요.
답. 입니다.
문제 8. 검산에서 상관이 가장 낮은 방법과 그루 정확도가 가장 높은 방법을 쓰세요.
답. 둘 다 쓰는 것과 붓스트랩만 쓰는 것입니다.
문제 9. 붓스트랩에서 한 점이 안 뽑힐 확률의 극한을 쓰세요.
답. 나누기 이고 약 입니다.
문제 10. 검산에서 학습 자료와 안 본 자료의 낙관 크기를 쓰세요.
답. 과 입니다.
문제 11. 검산에서 변수 개와 개를 볼 때의 그루 정확도를 쓰세요.
답. 과 입니다.
문제 12. 검산에서 한 그루 깊이 와 그루의 분산을 쓰세요.
답. 와 입니다.
문제 13. 검산에서 도 경계에 그루를 심었을 때의 남은 오류율을 쓰세요.
답. 입니다.
문제 14. 검산에서 깊이 와 일 때 한 그루와 그루의 차를 쓰세요.
답. 와 입니다.
문제 15. 검산에서 번 변수의 두 중요도를 쓰세요.
답. 과 입니다.
문제 16. 검산에서 같은 갈래와 다른 갈래의 평균 근접도를 쓰세요.
답. 과 입니다.
문제 17. 검산에서 양성 비율 일 때 두 재현율을 쓰세요.
답. 와 입니다.
문제 18. 검산에서 와 의 숲 예측을 쓰세요.
답. 둘 다 입니다.
심화 1. 평균 분산 공식을 유도하세요.
개의 예측이 각각 분산 이고 서로 상관 라 하면 이렇습니다.
| 단계 | 무엇 |
|---|---|
| 첫째 | 분산의 합에 공분산 항을 더함 |
| 둘째 | 대각 개와 비대각 개 |
| 셋째 | 으로 나눔 |
에서 둘째 항이 사라지고 만 남습니다. 상관이 앙상블의 성능 상한을 정합니다.
심화 2. 배깅이 왜 편향을 안 줄이는지 설명하세요.
붓스트랩 표본의 기댓값이 원래 자료와 같으므로, 각 나무의 기대 예측도 원래 나무와 거의 같습니다.
| 무엇 | 평균으로 |
|---|---|
| 기대 예측 | 안 바뀜 |
| 편향 | 안 바뀜 |
| 분산 | 줄어듦 |
정확히 말하면 조금 바뀝니다. 붓스트랩 표본은 원본보다 유효 표본이 작아 나무가 조금 더 얕아지고, 그만큼 편향이 조금 늘 수 있습니다. 문제 에서 편향이 에서 으로 오히려 줄었는데, 이는 잡음 범위입니다.
심화 3. 안 본 자료 추정이 언제 안 맞는지 정리하세요.
| 상황 | 무엇이 문제 |
|---|---|
| 그루가 적음 | 각 점을 안 본 나무가 몇 개 안 됨 |
| 표본이 아주 적음 | 안 본 자료 자체가 적음 |
| 자료가 시계열 | 강 문제 의 누출 |
| 개체가 여러 줄 | 같은 개체가 양쪽에 |
셋째와 넷째 줄이 위험합니다. 붓스트랩은 줄 단위로 무작위 추출하므로, 강 문제 에서 본 개체 누출이 그대로 일어납니다. 그때 안 본 자료 추정은 실제보다 훨씬 낙관됩니다.
심화 4. 극단 무작위 나무가 무엇을 더 하는지 정리하세요.
| 무엇 | 랜덤포레스트 | 극단 무작위 나무 |
|---|---|---|
| 자름 자리 | 최선을 찾음 | 무작위로 뽑음 |
| 붓스트랩 | 씀 | 대개 안 씀 |
| 나무끼리 상관 | 중간 | 더 낮음 |
| 개별 나무 | 좋음 | 나쁨 |
| 계산 | 느림 | 아주 빠름 |
자름 자리를 안 찾으므로 훨씬 빠릅니다. 문제 의 절충에서 다양성 쪽으로 더 민 것이고, 잡음이 많은 자료에서 종종 더 좋습니다.
심화 5. 랜덤포레스트가 왜 이웃 방법의 일종인지 설명하세요.
| 무엇 | 뜻 |
|---|---|
| 번째 나무에서 가 떨어진 잎 | |
| 근접도를 정규화한 값 | |
| 합 |
강 심화 의 선형 평활자입니다. 이웃을 거리로 정하지 않고 나무가 정합니다. 그래서 문제 의 근접도가 곧 이 무게이고, 랜덤포레스트는 라벨을 보고 거리를 배우는 최근접이웃이라 읽을 수 있습니다.
심화 6. 언제 랜덤포레스트를 첫 후보로 삼을지 정리하세요.
| 상황 | 쓰나 | 왜 |
|---|---|---|
| 무엇을 쓸지 모르겠음 | 예 | 손이 거의 안 감 |
| 변수 종류가 섞임 | 예 | 눈금 무관 |
| 표본이 아주 많음 | 절반 | 계산이 무거움 |
| 외삽이 필요함 | 아니오 | 범위 밖은 상수 |
| 최고 정확도가 필요함 | 아니오 | 강의 부스팅 |
첫 줄이 실무의 현실입니다. 기준선으로 랜덤포레스트를 돌려 보고, 그보다 나은 것이 필요하면 강으로 갑니다. 안 본 자료 추정이 딸려 나오므로 기준선을 세우는 비용이 거의 없습니다.
정답.
| 기호 | 읽는 법 | 뜻 |
|---|---|---|
| 배깅 | bagging | 붓스트랩 표본마다 모형을 만들어 평균 냅니다 |
| 랜덤포레스트 | random forest | 배깅에 변수 뽑기를 더한 것입니다 |
| 안 본 자료 | out-of-bag | 붓스트랩에서 안 뽑힌 자료입니다 |
| 변수 뽑기 | feature subsampling | 나눔마다 변수 일부만 봅니다 |
| 섞기 중요도 | permutation importance | 변수를 섞어 성능 하락을 잽니다 |
| 나눔 이득 중요도 | impurity importance | 나눔 이득을 다 더합니다 |
| 근접도 | proximity | 두 점이 같은 잎에 떨어진 비율입니다 |
| 극단 무작위 나무 | extremely randomized trees | 자름 자리까지 무작위로 뽑습니다 |
| 외삽 | extrapolation | 학습 범위 밖을 예측합니다 |
| 앙상블 | ensemble | 여러 모형을 합칩니다 |
다음은 219강 그래디언트 부스팅입니다. 이 강의는 나무를 나란히 심고 평균 냈습니다. 다음 강의는 나무를 줄지어 심어, 앞 나무가 틀린 것을 뒤 나무가 고치게 합니다. 그러면 편향까지 줄어듭니다.
import numpy as np
rng = np.random.default_rng(20261206)
def pw(s, n):
k = n - sum(2 if ord(c) > 0x2FFF else 1 for c in str(s))
return str(s) + " " * max(k, 0)
def rw(s, n):
k = n - sum(2 if ord(c) > 0x2FFF else 1 for c in str(s))
return " " * max(k, 0) + str(s)
def gini(y):
if len(y) == 0:
return 0.0
p = y.mean()
return float(2 * p * (1 - p))
def var_c(y):
if len(y) == 0:
return 0.0
return float(np.var(y))
class Node:
__slots__ = ("j", "t", "left", "right", "val")
def __init__(self):
self.j = -1
self.t = 0.0
self.left = None
self.right = None
self.val = 0.0
def grow(X, y, depth, crit=gini, min_leaf=1, mtry=None, r=None):
nd = Node()
nd.val = float(y.mean())
if depth <= 0 or len(y) < 2 * min_leaf or y.min() == y.max():
return nd
d = X.shape[1]
cols = np.arange(d) if mtry is None else r.choice(d, mtry, replace=False)
base = crit(y)
bg, bj, bt = 0.0, -1, 0.0
for j in cols:
v = X[:, j]
cuts = np.unique(v)
if len(cuts) > 24:
cuts = np.quantile(v, np.linspace(0.03, 0.97, 24))
for t in cuts:
m = v <= t
nl, nr = int(m.sum()), len(y) - int(m.sum())
if nl < min_leaf or nr < min_leaf:
continue
g = base - (nl * crit(y[m]) + nr * crit(y[~m])) / len(y)
if g > bg:
bg, bj, bt = g, int(j), float(t)
if bj < 0:
return nd
nd.j, nd.t = bj, bt
m = X[:, bj] <= bt
nd.left = grow(X[m], y[m], depth - 1, crit, min_leaf, mtry, r)
nd.right = grow(X[~m], y[~m], depth - 1, crit, min_leaf, mtry, r)
return nd
def predict(nd, X):
out = np.zeros(len(X))
for i in range(len(X)):
c = nd
while c.j >= 0:
c = c.left if X[i, c.j] <= c.t else c.right
out[i] = c.val
return out
def forest(X, y, B, depth=12, mtry=None, min_leaf=1, boot=True, crit=gini):
ts, oob = [], []
n = len(y)
for _ in range(B):
if boot:
s = rng.integers(0, n, n)
else:
s = np.arange(n)
ts.append(grow(X[s], y[s], depth, crit, min_leaf, mtry, rng))
oob.append(np.setdiff1d(np.arange(n), np.unique(s)))
return ts, oob
def fpred(ts, X):
return np.mean([predict(t, X) for t in ts], axis=0)
# --- 문제 1: 왜 평균이 듣는가 -------------------------------------------
print(" 217강에서 나무가 불안정하다고 했습니다. 그 불안정을 씁니다")
print(" 210강 문제 3 에서 독립인 것 m 개를 평균 내면 분산이 m 분의 1 이라 했습니다")
print(" %s %s %s"
% (pw("무엇", 22), rw("식", 26), rw("무엇을 뜻하나", 26)))
for a, b, c in [("독립인 m 개의 평균", "분산 나누기 m", "m 배 정확해짐"),
("상관 rho 인 m 개", "rho 시그마 제곱 더하기 나머지",
"rho 에서 멈춤"),
("m 을 무한히 키움", "rho 시그마 제곱", "상관이 바닥"),
("그래서 할 일", "상관을 낮춤", "218강의 핵심")]:
print(" %s %s %s" % (pw(a, 22), rw(b, 26), rw(c, 26)))
print(" 평균의 분산은 rho 시그마 제곱 더하기 1 빼기 rho 를 m 으로 나눈 것입니다")
print(" m 을 아무리 키워도 rho 시그마 제곱 아래로는 안 갑니다")
print(" 이 식을 수치로 확인합니다")
print(" %s %s %s %s"
% (pw("상관", 10), rw("m 이 1", 14), rw("m 이 10", 14),
rw("m 이 1000", 16)))
for rho in [0.0, 0.2, 0.5, 0.9]:
row = [1.0 * (rho + (1 - rho) / m) for m in [1, 10, 1000]]
print(" %s %14.6f %14.6f %16.6f"
% (pw("%.1f" % rho, 10), row[0], row[1], row[2]))
print(" 상관이 0 이면 1000 개 평균이 0.001 로 내려갑니다")
print(" 상관이 0.9 면 1000 개를 평균 내도 0.9 에서 멈춥니다")
print(" 나무를 많이 심는 것보다 나무를 서로 다르게 만드는 것이 중요합니다")
print(" 실제 나무들의 상관을 재 봅니다")
n1 = 600
X1 = rng.uniform(-3, 3, (n1, 6))
z1 = 1.5 - (X1[:, :2] ** 2).sum(axis=1) / 2.0 + 0.6 * X1[:, 2]
p1 = 1.0 / (1.0 + np.exp(-2 * z1))
y1 = (rng.uniform(0, 1, n1) < p1).astype(float)
X1t = rng.uniform(-3, 3, (2500, 6))
z1t = 1.5 - (X1t[:, :2] ** 2).sum(axis=1) / 2.0 + 0.6 * X1t[:, 2]
p1t = 1.0 / (1.0 + np.exp(-2 * z1t))
y1t = (rng.uniform(0, 1, 2500) < p1t).astype(float)
bayes1 = float(np.mean((p1t > 0.5) == y1t))
print(" 베이즈 최적 정확도는 %.6f 입니다" % bayes1)
print(" %s %s %s %s"
% (pw("어떻게 다르게 만드나", 26), rw("나무끼리 예측 상관", 24),
rw("한 그루 정확도", 20), rw("25 그루 정확도", 20)))
for nm, kw in [("같은 자료 같은 규칙", dict(boot=False, mtry=None)),
("붓스트랩만", dict(boot=True, mtry=None)),
("변수 뽑기만", dict(boot=False, mtry=2)),
("둘 다", dict(boot=True, mtry=2))]:
ts, _ = forest(X1, y1, 25, 12, kw["mtry"], 1, kw["boot"])
P = np.stack([predict(t, X1t) for t in ts])
C = np.corrcoef(P)
iu = np.triu_indices(len(ts), 1)
rho = float(np.nan_to_num(C[iu]).mean())
a1 = float(np.mean((P[0] > 0.5) == y1t))
aB = float(np.mean((P.mean(axis=0) > 0.5) == y1t))
print(" %s %24.6f %20.6f %20.6f"
% (pw(nm, 26), rho, a1, aB))
print(" 첫 줄은 나무가 전부 같으므로 상관이 1 이고 평균이 소용없습니다")
print(" 붓스트랩과 변수 뽑기를 함께 쓰면 상관이 0.34 로 가장 낮습니다")
print(" 그런데 25 그루 정확도는 붓스트랩만 쓴 쪽이 가장 높습니다")
print(" 상관만 낮추면 되는 것이 아닙니다. 개별 나무도 함께 나빠지기 때문입니다")
print(" 둘의 절충을 찾는 것이 이 방법의 손잡이입니다")
# --- 문제 2: 배깅과 랜덤포레스트 ---------------------------------------
print(" 붓스트랩이 자료를 얼마나 바꾸는지 셉니다")
print(" %s %s %s %s"
% (pw("표본 크기", 12), rw("뽑히지 않을 확률", 22), rw("실제 비율", 16),
rw("1 나누기 e", 16)))
for n in [10, 50, 500, 5000]:
theo = (1 - 1.0 / n) ** n
emp = []
for _ in range(200):
s = rng.integers(0, n, n)
emp.append(1.0 - len(np.unique(s)) / n)
print(" %s %22.6f %16.6f %16.6f"
% (pw(str(n), 12), theo, float(np.mean(emp)), float(np.exp(-1))))
print(" 표본이 커지면 뽑히지 않을 확률이 1 나누기 e 로 수렴합니다")
print(" 나무마다 약 37 퍼센트를 안 봅니다. 그 자료로 성능을 잴 수 있습니다")
print(" 158강의 붓스트랩이 여기서는 자료를 흔드는 장치로 쓰입니다")
print(" 안 본 자료로 성능을 재 봅니다")
ts2, oob2 = forest(X1, y1, 60, 12, 2, 1, True)
P2 = np.stack([predict(t, X1) for t in ts2])
oc = np.zeros(n1)
os_ = np.zeros(n1)
for b, idx in enumerate(oob2):
oc[idx] += 1
os_[idx] += P2[b][idx]
has = oc > 0
oob_acc = float(np.mean(((os_[has] / oc[has]) > 0.5) == y1[has]))
te_acc = float(np.mean((fpred(ts2, X1t) > 0.5) == y1t))
tr_acc = float(np.mean((fpred(ts2, X1) > 0.5) == y1))
print(" %s %s %s"
% (pw("무엇으로 쟀나", 26), rw("정확도", 16), rw("검증과의 차", 18)))
for nm, v in [("학습 자료 그대로", tr_acc), ("안 본 자료", oob_acc),
("따로 둔 검증 자료", te_acc)]:
print(" %s %16.6f %18.6f" % (pw(nm, 26), v, v - te_acc))
print(" 학습 자료로 재면 정확도가 1 입니다. 나무마다 그 점을 봤기 때문입니다")
print(" 안 본 자료는 0.020733 만 낙관됩니다. 학습 자료의 0.152400 에 비하면 훨씬 정직합니다")
print(" 212강의 교차검증을 따로 안 해도 되는 것이 이 방법의 이점입니다")
print(" 변수 뽑기 개수를 바꿔 가며 잽니다")
print(" %s %s %s %s"
% (pw("한 나눔에 보는 변수 수", 24), rw("나무끼리 상관", 20),
rw("한 그루 정확도", 18), rw("50 그루 정확도", 18)))
for mt in [1, 2, 3, 6]:
ts, _ = forest(X1, y1, 50, 12, mt, 1, True)
P = np.stack([predict(t, X1t) for t in ts])
iu = np.triu_indices(len(ts), 1)
rho = float(np.nan_to_num(np.corrcoef(P))[iu].mean())
print(" %s %20.6f %18.6f %18.6f"
% (pw(str(mt), 24), rho,
float(np.mean((P[0] > 0.5) == y1t)),
float(np.mean((P.mean(axis=0) > 0.5) == y1t))))
print(" 변수를 적게 보면 나무 하나는 나빠지고 상관은 낮아집니다")
print(" 1 개만 보면 상관 0.21 로 가장 낮은데 50 그루 정확도도 가장 낮습니다")
print(" 이 자료에서는 3 개나 6 개가 가장 좋습니다")
print(" 분류에서는 변수 수의 제곱근이 관례적 기본값이고 여기서는 2 나 3 입니다")
print(" 기본값이 언제나 최선은 아닙니다. 212강의 교차검증으로 골라야 합니다")
# --- 문제 3: 무엇이 좋아지고 무엇이 안 좋아지는가 -----------------------
print(" 나무 여럿이 무엇을 고치는지 편향과 분산으로 봅니다")
n3 = 300
x3 = np.linspace(-3, 3, n3).reshape(-1, 1)
f3 = np.sin(1.5 * x3[:, 0])
xv3 = np.linspace(-3, 3, 1200).reshape(-1, 1)
fv3 = np.sin(1.5 * xv3[:, 0])
print(" 매끄러운 함수를 계단으로 덮어야 하는 문제입니다")
print(" %s %s %s %s %s"
% (pw("무엇", 20), rw("편향 제곱", 16), rw("분산", 14),
rw("합", 14), rw("예측값 가짓수", 18)))
R3 = 60
for nm, B, dep in [("나무 한 그루 깊이 4", 1, 4),
("나무 한 그루 깊이 12", 1, 12),
("25 그루 깊이 12", 25, 12),
("100 그루 깊이 12", 100, 12)]:
preds = []
for _ in range(R3):
ya = f3 + rng.normal(0, 0.3, n3)
ts, _ = forest(x3, ya, B, dep, None, 2, B > 1, var_c)
preds.append(fpred(ts, xv3))
P = np.stack(preds)
bias2 = float(np.mean((P.mean(axis=0) - fv3) ** 2))
var = float(np.mean(P.var(axis=0)))
print(" %s %16.6f %14.6f %14.6f %18d"
% (pw(nm, 20), bias2, var, bias2 + var,
len(np.unique(np.round(P[0], 8)))))
print(" 깊이를 키우면 편향은 줄고 분산이 커집니다")
print(" 그루를 늘리면 분산만 줄고 편향은 그대로입니다")
print(" 예측값 가짓수가 크게 늘어 계단이 매끄러워집니다")
print(" 기울어진 경계는 여전히 못 잡는지 확인합니다")
Xd = rng.uniform(-3, 3, (800, 2))
yd = ((Xd[:, 0] + Xd[:, 1]) > 0).astype(float)
Xdt = rng.uniform(-3, 3, (3000, 2))
ydt = ((Xdt[:, 0] + Xdt[:, 1]) > 0).astype(float)
print(" 잡음이 없는 45 도 경계입니다")
print(" %s %s %s"
% (pw("무엇", 24), rw("정확도", 16), rw("남은 오류율", 18)))
for nm, B in [("나무 한 그루", 1), ("25 그루", 25), ("200 그루", 200)]:
ts, _ = forest(Xd, yd, B, 8, None, 5, B > 1)
ac = float(np.mean((fpred(ts, Xdt) > 0.5) == ydt))
print(" %s %16.6f %18.6f" % (pw(nm, 24), ac, 1 - ac))
print(" 그루를 늘려도 오류율이 0 으로 안 갑니다. 계단은 여전히 계단입니다")
print(" 평균이 계단의 모서리를 부드럽게 할 뿐 대각선을 만들지는 못합니다")
print(" 217강 문제 3 의 한계가 그대로 남습니다")
print(" 깊이를 제한해야 하는지 봅니다")
print(" 한 그루 정확도는 그 숲을 이루는 나무 50 그루 각각을 평균 낸 값입니다")
print(" %s %s %s %s"
% (pw("깊이", 8), rw("한 그루 정확도", 20), rw("50 그루 정확도", 20),
rw("차", 14)))
for dep in [2, 4, 8, 16]:
ts, _ = forest(X1, y1, 50, dep, 2, 1, True)
P = np.stack([predict(t, X1t) for t in ts])
a1 = float(np.mean([np.mean((pp > 0.5) == y1t) for pp in P]))
aB = float(np.mean((P.mean(axis=0) > 0.5) == y1t))
print(" %s %20.6f %20.6f %14.6f"
% (pw(str(dep), 8), a1, aB, aB - a1))
print(" 한 그루도 50 그루도 깊이를 키울수록 좋아집니다")
print(" 다만 둘의 차가 0.001392 에서 0.090592 로 벌어집니다")
print(" 깊게 키울수록 나무의 분산이 커지고 평균이 지울 것이 많아집니다")
print(" 그래서 랜덤포레스트는 가지치기를 거의 안 합니다")
# --- 문제 4: 변수 중요도와 근접도 ---------------------------------------
print(" 숲에서 변수 중요도를 두 가지 방법으로 잽니다")
n4 = 700
X4 = np.zeros((n4, 7))
X4[:, 0] = rng.normal(0, 1, n4)
X4[:, 1] = X4[:, 0] + rng.normal(0, 0.1, n4)
X4[:, 2] = rng.normal(0, 1, n4)
X4[:, 3] = rng.integers(0, 2, n4).astype(float)
X4[:, 4] = rng.integers(0, 40, n4).astype(float)
X4[:, 5] = rng.normal(0, 1, n4)
X4[:, 6] = rng.normal(0, 1, n4)
sc4 = 1.5 * X4[:, 0] + 1.0 * X4[:, 2] + 0.8 * X4[:, 3]
y4 = (rng.uniform(0, 1, n4) < 1 / (1 + np.exp(-sc4))).astype(float)
ts4, oob4 = forest(X4, y4, 80, 10, 3, 3, True)
def oob_acc_of(Xa):
tot, cnt = 0.0, 0
for b, idx in enumerate(oob4):
if len(idx) == 0:
continue
tot += np.sum((predict(ts4[b], Xa[idx]) > 0.5) == y4[idx])
cnt += len(idx)
return tot / cnt
base_oob = oob_acc_of(X4)
print(" 참으로 쓰이는 변수는 1 번 3 번 4 번입니다")
print(" 2 번은 1 번과 상관 0.99 인 복사본이고 5 번은 가짓수만 많습니다")
print(" %s %s %s %s"
% (pw("변수", 10), rw("참 계수", 12), rw("섞었을 때 정확도 하락", 26),
rw("무엇을 뜻하나", 24)))
tru = [1.5, 0.0, 1.0, 0.8, 0.0, 0.0, 0.0]
for j in range(7):
Xp = X4.copy()
Xp[:, j] = rng.permutation(Xp[:, j])
drop = base_oob - oob_acc_of(Xp)
tag = ("참으로 쓰임" if tru[j] > 0 else
("1 번의 복사본" if j == 1 else
("가짓수만 많음" if j == 4 else "쓸모없음")))
print(" %s %12.4f %26.6f %s"
% (pw(str(j + 1), 10), tru[j], drop, rw(tag, 24)))
print(" 섞기 중요도는 그 변수를 망가뜨려 성능이 얼마나 떨어지는지 잽니다")
print(" 1 번과 2 번이 둘 다 높게 나옵니다. 참 정보는 하나인데 둘로 나뉘어 붙었습니다")
print(" 나무마다 변수 셋만 보므로 어떤 나무는 1 번을 어떤 나무는 2 번을 씁니다")
print(" 4 번은 참 계수가 0.8 인데 0.002687 로 아주 낮습니다. 이진 변수라 나눌 자리가 하나뿐입니다")
print(" 상관된 변수와 가짓수가 적은 변수를 조심해야 합니다")
print(" 나눔 이득으로 잰 중요도와 견줍니다")
def imp_gain(ts, X, y):
out = np.zeros(X.shape[1])
def walk(c, Xa, ya):
if c.j < 0:
return
m = Xa[:, c.j] <= c.t
g = gini(ya) - (m.sum() * gini(ya[m])
+ (~m).sum() * gini(ya[~m])) / len(ya)
out[c.j] += g * len(ya)
walk(c.left, Xa[m], ya[m])
walk(c.right, Xa[~m], ya[~m])
for t in ts:
walk(t, X, y)
return out / out.sum()
ig = imp_gain(ts4, X4, y4)
print(" %s %s %s %s"
% (pw("변수", 10), rw("참 계수", 12), rw("나눔 이득 중요도", 22),
rw("가짓수", 12)))
nu = [len(np.unique(X4[:, j])) for j in range(7)]
for j in range(7):
print(" %s %12.4f %22.6f %12d"
% (pw(str(j + 1), 10), tru[j], float(ig[j]), nu[j]))
print(" 2 번은 참 계수가 0 인데 1 번보다 높습니다. 복사본이 원본을 앞질렀습니다")
print(" 4 번은 참 계수가 0.8 인데 0.015657 로 가장 낮습니다. 가짓수가 2 뿐이기 때문입니다")
print(" 쓸모없는 6 번과 7 번이 4 번보다 네 배 높게 나옵니다")
print(" 217강 문제 4 의 가짓수 편향이 숲에서도 그대로 남습니다")
print(" 그래서 섞기 중요도를 쓰는 것이 안전합니다")
print(" 근접도로 자료의 구조를 봅니다")
print(" 두 점이 같은 잎에 떨어진 비율을 근접도라 합니다")
Lf = []
for t in ts4[:30]:
lab = np.zeros(200, dtype=np.int64)
for i in range(200):
c, code = t, 1
while c.j >= 0:
if X4[i, c.j] <= c.t:
c, code = c.left, code * 2
else:
c, code = c.right, code * 2 + 1
lab[i] = code
Lf.append(lab)
Lf = np.stack(Lf)
prox = np.mean(Lf[:, :, None] == Lf[:, None, :], axis=0)
same = y4[:200][:, None] == y4[:200][None, :]
iu = np.triu_indices(200, 1)
print(" %s %s %s"
% (pw("두 점의 관계", 22), rw("평균 근접도", 18), rw("쌍의 개수", 16)))
print(" %s %18.6f %16d"
% (pw("같은 갈래", 22), float(prox[iu][same[iu]].mean()),
int(same[iu].sum())))
print(" %s %18.6f %16d"
% (pw("다른 갈래", 22), float(prox[iu][~same[iu]].mean()),
int((~same[iu]).sum())))
print(" 같은 갈래끼리 근접도가 더 높습니다. 숲이 거리를 배운 셈입니다")
print(" 215강 심화 4 의 거리 학습을 나무가 스스로 한 것입니다")
print(" 224강의 차원 축소에 이 근접도를 거리로 쓸 수 있습니다")
# --- 문제 5: 실무에서 쓰기 ---------------------------------------------
print(" 실무에서 숲을 쓸 때의 손잡이를 정리합니다")
print(" %s %s %s"
% (pw("손잡이", 22), rw("무엇을 바꾸나", 24), rw("어떻게 정하나", 26)))
for a, b, c in [("그루 수", "분산만 줄임", "안 좋아질 때까지"),
("변수 뽑기 수", "상관과 개별 성능", "제곱근에서 시작"),
("깊이", "개별 나무의 편향", "제한 안 해도 됨"),
("잎 최소 크기", "개별 나무의 분산", "회귀는 5 분류는 1"),
("표본 뽑기 비율", "상관", "1.0 이 기본")]:
print(" %s %s %s" % (pw(a, 22), rw(b, 24), rw(c, 26)))
print(" 첫 줄이 중요합니다. 그루를 늘려 나빠지는 일은 없습니다")
print(" 그루를 늘릴수록 정말 안 나빠지는지 확인합니다")
print(" %s %s %s %s"
% (pw("그루 수", 12), rw("검증 정확도", 18), rw("앞줄 대비", 16),
rw("최적과의 차", 16)))
ts5, _ = forest(X1, y1, 200, 12, 2, 1, True)
P5 = np.stack([predict(t, X1t) for t in ts5])
prev = None
for B in [1, 5, 25, 100, 200]:
ac = float(np.mean((P5[:B].mean(axis=0) > 0.5) == y1t))
dd = 0.0 if prev is None else ac - prev
prev = ac
print(" %s %18.6f %16.6f %16.6f"
% (pw(str(B), 12), ac, dd, ac - bayes1))
print(" 1 그루의 0.7384 가 100 그루에서 0.8640 이 됩니다")
print(" 100 에서 200 으로 가며 0.004800 떨어진 것은 우연의 흔들림입니다")
print(" 추세로는 평평해질 뿐 나빠지지 않습니다. 214강의 과적합 손잡이가 아니기 때문입니다")
print(" 그루 수는 계산 비용과 맞바꾸는 것일 뿐입니다")
print(" 갈래가 치우쳤을 때를 봅니다")
print(" %s %s %s %s"
% (pw("양성 비율", 12), rw("정확도", 14), rw("양성 재현율", 16),
rw("문턱을 비율로 낮추면 재현율", 30)))
for pr in [0.5, 0.2, 0.05, 0.02]:
m = 1500
Xa = rng.uniform(-3, 3, (m, 4))
sa = 1.5 * Xa[:, 0] - Xa[:, 1] + 0.5 * Xa[:, 2]
thr = np.quantile(sa, 1 - pr)
ya = (sa > thr).astype(float)
Xb = rng.uniform(-3, 3, (2000, 4))
sb = 1.5 * Xb[:, 0] - Xb[:, 1] + 0.5 * Xb[:, 2]
yb = (sb > thr).astype(float)
ts, _ = forest(Xa, ya, 40, 10, 2, 1, True)
ph = fpred(ts, Xb)
ac = float(np.mean((ph > 0.5) == yb))
rc = float(np.sum((ph > 0.5) & (yb == 1)) / max(np.sum(yb == 1), 1))
rc2 = float(np.sum((ph > pr) & (yb == 1)) / max(np.sum(yb == 1), 1))
print(" %s %14.6f %16.6f %30.6f"
% (pw("%.2f" % pr, 12), ac, rc, rc2))
print(" 0.5 문턱을 그대로 쓰면 드문 갈래의 재현율이 떨어집니다")
print(" 문턱을 양성 비율로 낮추면 재현율이 크게 올라옵니다")
print(" 226강과 227강에서 문턱과 가중치를 제대로 다룹니다")
print(" 숲의 한계를 정리합니다")
print(" %s %s"
% (pw("무엇을 못 하나", 28), rw("왜", 34)))
for a, b in [("기울어진 경계", "여전히 계단으로 덮음"),
("외삽", "학습 범위 밖은 상수"),
("설명", "나무 하나의 읽힘을 잃음"),
("아주 많은 변수", "나눔 고르기가 느려짐")]:
print(" %s %s" % (pw(a, 28), rw(b, 34)))
print(" 외삽이 안 된다는 것을 수치로 봅니다")
xe = np.linspace(0, 3, 300).reshape(-1, 1)
ye = 2.0 * xe[:, 0] + rng.normal(0, 0.2, 300)
tse, _ = forest(xe, ye, 40, 10, None, 3, True, var_c)
print(" 학습은 x 가 0 에서 3 사이입니다. 참 함수는 2x 입니다")
print(" %s %s %s %s"
% (pw("x", 10), rw("참값", 14), rw("숲의 예측", 16), rw("차", 14)))
for xq in [1.0, 2.5, 3.0, 5.0, 10.0]:
pv = float(fpred(tse, np.array([[xq]]))[0])
print(" %s %14.6f %16.6f %14.6f"
% (pw("%.1f" % xq, 10), 2.0 * xq, pv, pv - 2.0 * xq))
print(" 학습 범위 밖에서는 예측이 더 안 자랍니다. 마지막 잎의 값에 갇힙니다")
print(" 선형 모형은 이 자리에서 그냥 늘어납니다. 가정이 있기 때문입니다")
print(" 218강은 나무를 나란히 심었습니다. 219강은 줄지어 심습니다")
# 217강에서 나무가 불안정하다고 했습니다. 그 불안정을 씁니다
# 210강 문제 3 에서 독립인 것 m 개를 평균 내면 분산이 m 분의 1 이라 했습니다
# 무엇 식 무엇을 뜻하나
# 독립인 m 개의 평균 분산 나누기 m m 배 정확해짐
# 상관 rho 인 m 개 rho 시그마 제곱 더하기 나머지 rho 에서 멈춤
# m 을 무한히 키움 rho 시그마 제곱 상관이 바닥
# 그래서 할 일 상관을 낮춤 218강의 핵심
# 평균의 분산은 rho 시그마 제곱 더하기 1 빼기 rho 를 m 으로 나눈 것입니다
# m 을 아무리 키워도 rho 시그마 제곱 아래로는 안 갑니다
# 이 식을 수치로 확인합니다
# 상관 m 이 1 m 이 10 m 이 1000
# 0.0 1.000000 0.100000 0.001000
# 0.2 1.000000 0.280000 0.200800
# 0.5 1.000000 0.550000 0.500500
# 0.9 1.000000 0.910000 0.900100
# 상관이 0 이면 1000 개 평균이 0.001 로 내려갑니다
# 상관이 0.9 면 1000 개를 평균 내도 0.9 에서 멈춥니다
# 나무를 많이 심는 것보다 나무를 서로 다르게 만드는 것이 중요합니다
# 실제 나무들의 상관을 재 봅니다
# 베이즈 최적 정확도는 0.900800 입니다
# 어떻게 다르게 만드나 나무끼리 예측 상관 한 그루 정확도 25 그루 정확도
# 같은 자료 같은 규칙 1.000000 0.817600 0.817600
# 붓스트랩만 0.482265 0.830000 0.876400
# 변수 뽑기만 0.407543 0.799600 0.856800
# 둘 다 0.340908 0.802000 0.854000
# 첫 줄은 나무가 전부 같으므로 상관이 1 이고 평균이 소용없습니다
# 붓스트랩과 변수 뽑기를 함께 쓰면 상관이 0.34 로 가장 낮습니다
# 그런데 25 그루 정확도는 붓스트랩만 쓴 쪽이 가장 높습니다
# 상관만 낮추면 되는 것이 아닙니다. 개별 나무도 함께 나빠지기 때문입니다
# 둘의 절충을 찾는 것이 이 방법의 손잡이입니다
# 붓스트랩이 자료를 얼마나 바꾸는지 셉니다
# 표본 크기 뽑히지 않을 확률 실제 비율 1 나누기 e
# 10 0.348678 0.347500 0.367879
# 50 0.364170 0.361900 0.367879
# 500 0.367511 0.366660 0.367879
# 5000 0.367843 0.367932 0.367879
# 표본이 커지면 뽑히지 않을 확률이 1 나누기 e 로 수렴합니다
# 나무마다 약 37 퍼센트를 안 봅니다. 그 자료로 성능을 잴 수 있습니다
# 158강의 붓스트랩이 여기서는 자료를 흔드는 장치로 쓰입니다
# 안 본 자료로 성능을 재 봅니다
# 무엇으로 쟀나 정확도 검증과의 차
# 학습 자료 그대로 1.000000 0.152400
# 안 본 자료 0.868333 0.020733
# 따로 둔 검증 자료 0.847600 0.000000
# 학습 자료로 재면 정확도가 1 입니다. 나무마다 그 점을 봤기 때문입니다
# 안 본 자료는 0.020733 만 낙관됩니다. 학습 자료의 0.152400 에 비하면 훨씬 정직합니다
# 212강의 교차검증을 따로 안 해도 되는 것이 이 방법의 이점입니다
# 변수 뽑기 개수를 바꿔 가며 잽니다
# 한 나눔에 보는 변수 수 나무끼리 상관 한 그루 정확도 50 그루 정확도
# 1 0.211207 0.750400 0.816000
# 2 0.333808 0.761600 0.853600
# 3 0.422671 0.788400 0.870400
# 6 0.498708 0.814000 0.870800
# 변수를 적게 보면 나무 하나는 나빠지고 상관은 낮아집니다
# 1 개만 보면 상관 0.21 로 가장 낮은데 50 그루 정확도도 가장 낮습니다
# 이 자료에서는 3 개나 6 개가 가장 좋습니다
# 분류에서는 변수 수의 제곱근이 관례적 기본값이고 여기서는 2 나 3 입니다
# 기본값이 언제나 최선은 아닙니다. 212강의 교차검증으로 골라야 합니다
# 나무 여럿이 무엇을 고치는지 편향과 분산으로 봅니다
# 매끄러운 함수를 계단으로 덮어야 하는 문제입니다
# 무엇 편향 제곱 분산 합 예측값 가짓수
# 나무 한 그루 깊이 4 0.007281 0.027217 0.034498 16
# 나무 한 그루 깊이 12 0.000971 0.054524 0.055494 121
# 25 그루 깊이 12 0.000904 0.025799 0.026702 434
# 100 그루 깊이 12 0.000800 0.024110 0.024910 674
# 깊이를 키우면 편향은 줄고 분산이 커집니다
# 그루를 늘리면 분산만 줄고 편향은 그대로입니다
# 예측값 가짓수가 크게 늘어 계단이 매끄러워집니다
# 기울어진 경계는 여전히 못 잡는지 확인합니다
# 잡음이 없는 45 도 경계입니다
# 무엇 정확도 남은 오류율
# 나무 한 그루 0.954000 0.046000
# 25 그루 0.972333 0.027667
# 200 그루 0.974667 0.025333
# 그루를 늘려도 오류율이 0 으로 안 갑니다. 계단은 여전히 계단입니다
# 평균이 계단의 모서리를 부드럽게 할 뿐 대각선을 만들지는 못합니다
# 217강 문제 3 의 한계가 그대로 남습니다
# 깊이를 제한해야 하는지 봅니다
# 한 그루 정확도는 그 숲을 이루는 나무 50 그루 각각을 평균 낸 값입니다
# 깊이 한 그루 정확도 50 그루 정확도 차
# 2 0.708608 0.710000 0.001392
# 4 0.747584 0.813200 0.065616
# 8 0.767456 0.851200 0.083744
# 16 0.773408 0.864000 0.090592
# 한 그루도 50 그루도 깊이를 키울수록 좋아집니다
# 다만 둘의 차가 0.001392 에서 0.090592 로 벌어집니다
# 깊게 키울수록 나무의 분산이 커지고 평균이 지울 것이 많아집니다
# 그래서 랜덤포레스트는 가지치기를 거의 안 합니다
# 숲에서 변수 중요도를 두 가지 방법으로 잽니다
# 참으로 쓰이는 변수는 1 번 3 번 4 번입니다
# 2 번은 1 번과 상관 0.99 인 복사본이고 5 번은 가짓수만 많습니다
# 변수 참 계수 섞었을 때 정확도 하락 무엇을 뜻하나
# 1 1.5000 0.086476 참으로 쓰임
# 2 0.0000 0.075191 1 번의 복사본
# 3 1.0000 0.028972 참으로 쓰임
# 4 0.8000 0.002687 참으로 쓰임
# 5 0.0000 -0.001124 가짓수만 많음
# 6 0.0000 -0.002199 쓸모없음
# 7 0.0000 0.003420 쓸모없음
# 섞기 중요도는 그 변수를 망가뜨려 성능이 얼마나 떨어지는지 잽니다
# 1 번과 2 번이 둘 다 높게 나옵니다. 참 정보는 하나인데 둘로 나뉘어 붙었습니다
# 나무마다 변수 셋만 보므로 어떤 나무는 1 번을 어떤 나무는 2 번을 씁니다
# 4 번은 참 계수가 0.8 인데 0.002687 로 아주 낮습니다. 이진 변수라 나눌 자리가 하나뿐입니다
# 상관된 변수와 가짓수가 적은 변수를 조심해야 합니다
# 나눔 이득으로 잰 중요도와 견줍니다
# 변수 참 계수 나눔 이득 중요도 가짓수
# 1 1.5000 0.281999 700
# 2 0.0000 0.329681 700
# 3 1.0000 0.181721 700
# 4 0.8000 0.015657 2
# 5 0.0000 0.065961 40
# 6 0.0000 0.058132 700
# 7 0.0000 0.066849 700
# 2 번은 참 계수가 0 인데 1 번보다 높습니다. 복사본이 원본을 앞질렀습니다
# 4 번은 참 계수가 0.8 인데 0.015657 로 가장 낮습니다. 가짓수가 2 뿐이기 때문입니다
# 쓸모없는 6 번과 7 번이 4 번보다 네 배 높게 나옵니다
# 217강 문제 4 의 가짓수 편향이 숲에서도 그대로 남습니다
# 그래서 섞기 중요도를 쓰는 것이 안전합니다
# 근접도로 자료의 구조를 봅니다
# 두 점이 같은 잎에 떨어진 비율을 근접도라 합니다
# 두 점의 관계 평균 근접도 쌍의 개수
# 같은 갈래 0.045030 10476
# 다른 갈래 0.010127 9424
# 같은 갈래끼리 근접도가 더 높습니다. 숲이 거리를 배운 셈입니다
# 215강 심화 4 의 거리 학습을 나무가 스스로 한 것입니다
# 224강의 차원 축소에 이 근접도를 거리로 쓸 수 있습니다
# 실무에서 숲을 쓸 때의 손잡이를 정리합니다
# 손잡이 무엇을 바꾸나 어떻게 정하나
# 그루 수 분산만 줄임 안 좋아질 때까지
# 변수 뽑기 수 상관과 개별 성능 제곱근에서 시작
# 깊이 개별 나무의 편향 제한 안 해도 됨
# 잎 최소 크기 개별 나무의 분산 회귀는 5 분류는 1
# 표본 뽑기 비율 상관 1.0 이 기본
# 첫 줄이 중요합니다. 그루를 늘려 나빠지는 일은 없습니다
# 그루를 늘릴수록 정말 안 나빠지는지 확인합니다
# 그루 수 검증 정확도 앞줄 대비 최적과의 차
# 1 0.738400 0.000000 -0.162400
# 5 0.836400 0.098000 -0.064400
# 25 0.857200 0.020800 -0.043600
# 100 0.864000 0.006800 -0.036800
# 200 0.859200 -0.004800 -0.041600
# 1 그루의 0.7384 가 100 그루에서 0.8640 이 됩니다
# 100 에서 200 으로 가며 0.004800 떨어진 것은 우연의 흔들림입니다
# 추세로는 평평해질 뿐 나빠지지 않습니다. 214강의 과적합 손잡이가 아니기 때문입니다
# 그루 수는 계산 비용과 맞바꾸는 것일 뿐입니다
# 갈래가 치우쳤을 때를 봅니다
# 양성 비율 정확도 양성 재현율 문턱을 비율로 낮추면 재현율
# 0.50 0.970500 0.973140 0.973140
# 0.20 0.973500 0.908602 0.997312
# 0.05 0.990500 0.835165 0.978022
# 0.02 0.992000 0.703704 1.000000
# 0.5 문턱을 그대로 쓰면 드문 갈래의 재현율이 떨어집니다
# 문턱을 양성 비율로 낮추면 재현율이 크게 올라옵니다
# 226강과 227강에서 문턱과 가중치를 제대로 다룹니다
# 숲의 한계를 정리합니다
# 무엇을 못 하나 왜
# 기울어진 경계 여전히 계단으로 덮음
# 외삽 학습 범위 밖은 상수
# 설명 나무 하나의 읽힘을 잃음
# 아주 많은 변수 나눔 고르기가 느려짐
# 외삽이 안 된다는 것을 수치로 봅니다
# 학습은 x 가 0 에서 3 사이입니다. 참 함수는 2x 입니다
# x 참값 숲의 예측 차
# 1.0 2.000000 2.004055 0.004055
# 2.5 5.000000 5.266157 0.266157
# 3.0 6.000000 6.045422 0.045422
# 5.0 10.000000 6.045422 -3.954578
# 10.0 20.000000 6.045422 -13.954578
# 학습 범위 밖에서는 예측이 더 안 자랍니다. 마지막 잎의 값에 갇힙니다
# 선형 모형은 이 자리에서 그냥 늘어납니다. 가정이 있기 때문입니다
# 218강은 나무를 나란히 심었습니다. 219강은 줄지어 심습니다