강은 거리로, 강은 분포로 답을 찾았습니다. 이번에는 셋째 방법입니다.
"소득이 만 원 이하입니까"라고 묻고, 그 답에 따라 다음 질문을 던집니다. 사람이 결정을 내리는 방식과 같아서 규칙을 그대로 읽을 수 있습니다.
이 강의는 그 질문을 고르는 자를 만들고, 언제 멈출지 정하고, 이 방법이 무엇을 원리적으로 못 하는지까지 봅니다. 마지막 못 하는 것이 강과 강을 낳습니다.
문제. 나눔의 자를 정합니다.
() 이웃 방법과 무엇이 다른지 정리하세요.
() 순도를 재는 자 셋을 계산하세요.
() 오분류율을 왜 안 쓰는지 보이세요.
생각의 실마리. 좋은 질문은 답을 갈라 주는 질문입니다. 갈랐다는 것을 재려면 한 마디가 얼마나 순수한지를 재는 자가 필요합니다.
풀이. () 정리합니다.
| 무엇 | 이웃 방법 | 나무 |
|---|---|---|
| 공간을 어떻게 나누나 | 점 주위로 | 축에 나란한 상자로 |
| 예측 비용 | 표본에 비례 | 깊이에 비례 |
| 사람이 읽을 수 있나 | 어려움 | 규칙으로 읽힘 |
| 눈금에 민감한가 | 매우 | 전혀 |
| 결측값 | 곤란 | 따로 보낼 수 있음 |
상자로 나누므로 변수의 눈금을 바꿔도 답이 안 바뀝니다. 강 문제 에서 배율 이 정확도를 에서 로 떨어뜨렸는데, 나무는 그런 일이 없습니다. 라는 질문은 단조 변환에 안 변하기 때문입니다.
() 순도를 재는 자 셋을 계산합니다.
| 양성 비율 | 지니 | 엔트로피 | 오분류율 |
|---|---|---|---|
셋 다 에서 가장 크고 과 에서 입니다. 그런데 모양이 다릅니다. 지니와 엔트로피는 굽어 있고 오분류율은 꺾인 직선 둘입니다.
() 오분류율을 왜 안 쓰는지 보입니다. 부모 마디에 양성 음성 이 있고 두 나눔을 견줍니다.
| 나눔 | 왼쪽 (양성 음성) | 오른쪽 (양성 음성) |
|---|---|---|
| 가 나눔 | ||
| 나 나눔 |
| 자 | 가 나눔의 이득 | 나 나눔의 이득 | 어느 쪽을 고르나 |
|---|---|---|---|
| 지니 | 나 | ||
| 엔트로피 | 나 | ||
| 오분류율 | 고를 수 없음 |
오분류율은 두 나눔의 이득이 정확히 같아 고를 수가 없습니다. 둘 다 오분류 개를 남기기 때문입니다.
지니와 엔트로피는 나 나눔을 고릅니다. 오른쪽이 완전히 순수해지기 때문이고, 그것은 그 뒤에 더 나눌 것이 없다는 뜻입니다.
강의 젠센 부등식에서 굽음이 이 차이를 만듭니다. 굽은 함수는 극단으로 쪼갤수록 가중평균이 더 많이 내려가고, 직선은 그렇지 않습니다.
이 문제에서 배우는 것. 나눔의 자는 지금의 오류율이 아니라 앞으로 나눌 여지를 재야 합니다. 굽은 함수가 그 여지를 재고, 오분류율은 못 잽니다. 그래서 나무를 키울 때는 지니나 엔트로피를 쓰고, 깎을 때는 오분류율을 씁니다.
확인 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. 최적 나무를 찾는 것이 왜 어려운지 정리하세요.
| 무엇 | 어려움 |
|---|---|
| 가능한 나무의 수 | 변수와 자름의 조합으로 지수 |
| 최적 나무 찾기 | NP 어려움 |
| 실제로 쓰는 방법 | 탐욕적으로 한 번에 하나씩 |
| 그 대가 | 두 번 나눠야 좋아지는 자리를 놓침 |
탐욕적 방법은 앞을 안 봅니다. 배타적 논리합에서 첫 나눔의 이득이 정확히 인데도, 두 번 나누면 완전히 갈립니다. 그래서 이득 문턱으로 멈추면 안 되고 크게 키운 뒤 깎아야 합니다. 문제 의 표가 그 근거입니다.
심화 2. 지니와 엔트로피의 관계를 정리하세요.
| 무엇 | 지니 | 엔트로피 |
|---|---|---|
| 근처 | 거의 같음 | 거의 같음 |
| 가 극단 | 덜 가파름 | 더 가파름 |
| 계산 | 곱셈 하나 | 로그 둘 |
| 고르는 나눔 | 거의 같음 | 거의 같음 |
근처에서 테일러 전개하면 둘이 상수배로 같습니다. 실제로 고르는 나눔이 거의 안 달라지므로 어느 쪽을 써도 됩니다. 지니가 로그를 안 쓰므로 조금 빠릅니다.
심화 3. 결측값을 나무에서 다루는 방법을 정리하세요.
| 방법 | 무엇을 하나 | 언제 쓰나 |
|---|---|---|
| 대리 나눔 | 비슷하게 가르는 다른 변수 | CART |
| 양쪽으로 보냄 | 무게를 나눠 양쪽에 | C4.5 |
| 따로 한 갈래 | 결측을 하나의 값으로 | 결측이 정보일 때 |
| 미리 대치 | 다른 방법으로 채움 | 결측이 적을 때 |
셋째 줄이 흥미롭습니다. "검사를 안 받았다"는 사실 자체가 정보인 경우가 많고, 나무는 그것을 그대로 한 갈래로 쓸 수 있습니다. 선형 모형은 이렇게 못 합니다.
심화 4. 나무가 왜 편향이 낮고 분산이 높은지 설명하세요.
| 무엇 | 나무 | 선형 모형 |
|---|---|---|
| 표현할 수 있는 함수 | 잎을 늘리면 무엇이든 | 직선만 |
| 편향 | 낮음 | 참이 직선이 아니면 높음 |
| 분산 | 높음 | 낮음 |
| 이유 | 나눔이 자료에 딸림 | 계수가 전체 평균에 딸림 |
넷째 줄이 핵심입니다. 나무의 나눔 자리는 몇 개의 관측이 정합니다. 경계 근처의 점 하나가 자리를 옮기면 자름이 따라 옮깁니다. 선형 모형의 계수는 모든 점의 평균이라 하나가 바뀌어도 만 움직입니다.
심화 5. 나무의 예측 함수가 어떤 꼴인지 쓰세요.
| 무엇 | 뜻 |
|---|---|
| R_ | 잎이 맡은 상자 |
| c_ | 그 안의 평균 |
| 잎 개수 | |
| 상자들 | 서로 안 겹치고 다 덮음 |
이것이 강 심화 의 선형 평활자입니다. 무게가 "같은 잎에 있으면 , 아니면 "입니다. 최근접이웃과 같은 꼴인데 이웃을 거리가 아니라 상자로 정합니다.
심화 6. 나무를 언제 쓰고 언제 안 쓸지 정리하세요.
| 상황 | 나무를 쓰나 | 왜 |
|---|---|---|
| 규칙을 설명해야 함 | 예 | 그대로 읽힘 |
| 변수 종류가 섞임 | 예 | 눈금 무관 |
| 경계가 매끄러움 | 아니오 | 계단으로 덮어야 함 |
| 변수가 아주 많음 | 절반 | 나눔 고르기가 느려짐 |
| 정확도가 최우선 | 아니오 | 강과 강을 씀 |
마지막 줄이 실무의 현실입니다. 나무 하나를 그대로 쓰는 경우는 드물고, 설명이 필요할 때나 여러 그루의 재료로 씁니다. 다음 두 강의가 그 여러 그루입니다.
정답.
| 기호 | 읽는 법 | 뜻 |
|---|---|---|
| 의사결정나무 | decision tree | 질문을 이어 붙여 답을 냅니다 |
| 마디 | node | 질문 하나가 붙는 자리입니다 |
| 잎 | leaf | 더 안 나누고 답을 내는 자리입니다 |
| 지니 불순도 | Gini impurity | 입니다 |
| 정보 이득 | information gain | 나누기 전후 불순도의 차입니다 |
| 이득비 | gain ratio | 이득을 나눔의 가짓수로 나눕니다 |
| 비용 복잡도 가지치기 | cost-complexity pruning | 잎 개수에 벌점을 걸어 깎습니다 |
| 대리 나눔 | surrogate split | 결측일 때 대신 쓰는 나눔입니다 |
| 축에 나란함 | axis-aligned | 한 변수만 보고 자릅니다 |
| 불안정 | instability | 자료가 조금 바뀌면 크게 달라집니다 |
다음은 218강 랜덤포레스트입니다. 이 강의가 나무의 불안정을 약점으로 확인했습니다. 다음 강의는 그 불안정을 자산으로 바꿉니다.
import numpy as np
rng = np.random.default_rng(20261129)
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 entropy(y):
if len(y) == 0:
return 0.0
p = y.mean()
if p <= 0 or p >= 1:
return 0.0
return float(-p * np.log2(p) - (1 - p) * np.log2(1 - p))
def mis(y):
if len(y) == 0:
return 0.0
p = y.mean()
return float(min(p, 1 - p))
def best_split(X, y, crit=gini, min_leaf=1):
n, d = X.shape
base = crit(y)
best = (0.0, -1, 0.0)
for j in range(d):
v = X[:, j]
cuts = np.unique(v)
if len(cuts) > 40:
cuts = np.quantile(v, np.linspace(0.02, 0.98, 40))
for t in cuts:
m = v <= t
nl, nr = int(m.sum()), int((~m).sum())
if nl < min_leaf or nr < min_leaf:
continue
g = base - (nl * crit(y[m]) + nr * crit(y[~m])) / n
if g > best[0]:
best = (g, j, float(t))
return best
class Node:
def __init__(self):
self.j = -1
self.t = 0.0
self.left = None
self.right = None
self.val = 0.0
self.n = 0
def grow(X, y, depth, crit=gini, min_leaf=1, min_gain=0.0):
nd = Node()
nd.val = float(y.mean())
nd.n = len(y)
if depth <= 0 or len(y) < 2 * min_leaf or y.min() == y.max():
return nd
g, j, t = best_split(X, y, crit, min_leaf)
if j < 0 or g <= min_gain:
return nd
nd.j, nd.t = j, t
m = X[:, j] <= t
nd.left = grow(X[m], y[m], depth - 1, crit, min_leaf, min_gain)
nd.right = grow(X[~m], y[~m], depth - 1, crit, min_leaf, min_gain)
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 leaves(nd):
if nd.j < 0:
return 1
return leaves(nd.left) + leaves(nd.right)
# --- 문제 1: 질문을 하나씩 던져 나눕니다 --------------------------------
print(" 215강은 거리로 216강은 분포로 답을 찾았습니다")
print(" 이번에는 예 아니오 질문을 이어 붙여 답을 찾습니다")
print(" %s %s %s"
% (pw("무엇", 20), rw("이웃 방법", 22), rw("나무", 22)))
for a, b, c in [("공간을 어떻게 나누나", "점 주위로", "축에 나란한 상자로"),
("예측 비용", "표본에 비례", "깊이에 비례"),
("사람이 읽을 수 있나", "어려움", "규칙으로 읽힘"),
("눈금에 민감한가", "매우", "전혀"),
("결측값", "곤란", "따로 보낼 수 있음")]:
print(" %s %s %s" % (pw(a, 20), rw(b, 22), rw(c, 22)))
print(" 상자로 나누므로 변수의 눈금을 바꿔도 답이 안 바뀝니다")
print(" 나눔의 좋고 나쁨을 재는 자를 정합니다")
print(" 한 마디의 순도가 얼마나 오르는지로 잽니다")
print(" %s %s %s %s"
% (pw("양성 비율", 12), rw("지니", 14), rw("엔트로피", 14),
rw("오분류율", 14)))
for p in [0.0, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0]:
yy = np.array([1.0] * int(round(p * 1000)) + [0.0] * int(round((1 - p) * 1000)))
print(" %s %14.6f %14.6f %14.6f"
% (pw("%.1f" % p, 12), gini(yy), entropy(yy), mis(yy)))
print(" 셋 다 0.5 에서 가장 크고 0 과 1 에서 0 입니다")
print(" 지니와 엔트로피는 굽어 있고 오분류율은 꺾인 직선 둘입니다")
print(" 굽어 있어야 나눔의 이득이 제대로 잡힙니다. 곧 확인합니다")
print(" 오분류율을 쓰면 왜 안 되는지 봅니다")
print(" 부모 마디에 양성 400 음성 400 이 있습니다. 두 나눔을 견줍니다")
yB = np.array([1.0] * 400 + [0.0] * 400)
mA = np.zeros(800, dtype=bool)
mA[:300] = True
mA[400:500] = True
mB = np.zeros(800, dtype=bool)
mB[:200] = True
mB[400:800] = True
print(" %s %s %s"
% (pw("나눔", 12), rw("왼쪽 (양성 음성)", 22), rw("오른쪽 (양성 음성)", 24)))
for nm, m in [("가 나눔", mA), ("나 나눔", mB)]:
print(" %s %s %s"
% (pw(nm, 12),
rw("%d %d" % (int(yB[m].sum()), int((1 - yB[m]).sum())), 22),
rw("%d %d" % (int(yB[~m].sum()), int((1 - yB[~m]).sum())), 24)))
print(" %s %s %s %s"
% (pw("자", 14), rw("가 나눔의 이득", 20), rw("나 나눔의 이득", 20),
rw("어느 쪽을 고르나", 22)))
for nm, f in [("지니", gini), ("엔트로피", entropy), ("오분류율", mis)]:
b = f(yB)
g1 = b - (mA.sum() * f(yB[mA]) + (~mA).sum() * f(yB[~mA])) / 800
g2 = b - (mB.sum() * f(yB[mB]) + (~mB).sum() * f(yB[~mB])) / 800
print(" %s %20.6f %20.6f %s"
% (pw(nm, 14), g1, g2,
rw("가" if g1 > g2 + 1e-12
else ("나" if g2 > g1 + 1e-12 else "고를 수 없음"), 22)))
print(" 오분류율은 두 나눔의 이득이 정확히 같아 고를 수가 없습니다")
print(" 지니와 엔트로피는 나 나눔을 고릅니다. 오른쪽이 완전히 순수해지기 때문입니다")
print(" 나 나눔이 나은 것은 그 뒤에 더 나눌 것이 적기 때문입니다")
print(" 132강의 젠센 부등식에서 굽음이 이 차이를 만듭니다")
# --- 문제 2: 나무를 키우고 깎습니다 -------------------------------------
print(" 실제로 나무를 키워 봅니다")
n2 = 600
X2 = rng.uniform(-3, 3, (n2, 2))
z2 = 1.2 - (X2 ** 2).sum(axis=1) / 3.0
p2 = 1.0 / (1.0 + np.exp(-3 * z2))
y2 = (rng.uniform(0, 1, n2) < p2).astype(float)
Xt2 = rng.uniform(-3, 3, (3000, 2))
zt2 = 1.2 - (Xt2 ** 2).sum(axis=1) / 3.0
pt2 = 1.0 / (1.0 + np.exp(-3 * zt2))
yt2 = (rng.uniform(0, 1, 3000) < pt2).astype(float)
bayes2 = float(np.mean((pt2 > 0.5) == yt2))
print(" 참 경계가 원인 자료입니다. 베이즈 최적 정확도는 %.6f 입니다" % bayes2)
print(" %s %s %s %s %s"
% (pw("깊이", 8), rw("잎 개수", 12), rw("학습 정확도", 16),
rw("검증 정확도", 16), rw("최적과의 차", 16)))
for d in [1, 2, 3, 5, 8, 15]:
t = grow(X2, y2, d)
tr = float(np.mean((predict(t, X2) > 0.5) == y2))
te = float(np.mean((predict(t, Xt2) > 0.5) == yt2))
print(" %s %12d %16.6f %16.6f %16.6f"
% (pw(str(d), 8), leaves(t), tr, te, te - bayes2))
print(" 깊이를 키우면 학습 정확도가 1 로 갑니다. 잎마다 한 점씩 담기 때문입니다")
print(" 검증 정확도는 어느 깊이에서 가장 좋고 그 뒤로 떨어집니다")
print(" 214강의 복잡도 손잡이가 여기서는 깊이입니다")
print(" 나무의 유효 자유도가 잎 개수인 것을 확인합니다")
print(" %s %s %s %s"
% (pw("깊이", 8), rw("잎 개수", 12), rw("학습 오분류율", 18),
rw("검증 빼기 학습", 18)))
for d in [1, 2, 3, 5, 8, 15]:
t = grow(X2, y2, d)
tr = float(np.mean((predict(t, X2) > 0.5) != y2))
te = float(np.mean((predict(t, Xt2) > 0.5) != yt2))
print(" %s %12d %18.6f %18.6f"
% (pw(str(d), 8), leaves(t), tr, te - tr))
print(" 잎이 늘수록 격차가 벌어집니다. 잎 하나가 모수 하나 노릇을 합니다")
print(" 214강 문제 1 의 낙관이 잎 개수에 비례합니다")
print(" 멈추는 규칙 세 가지를 견줍니다")
print(" %s %s %s %s"
% (pw("멈추는 규칙", 22), rw("잎 개수", 12), rw("검증 정확도", 16),
rw("무엇이 문제인가", 24)))
for nm, kw, why in [("깊이 제한 3", dict(depth=3), "너무 얕을 수 있음"),
("잎 최소 30 개", dict(depth=20, min_leaf=30), "가지마다 다름"),
("이득 문턱 0.01", dict(depth=20, min_gain=0.01),
"앞을 못 봄"),
("제한 없음", dict(depth=20), "완전히 외움")]:
t = grow(X2, y2, kw.get("depth", 20), gini,
kw.get("min_leaf", 1), kw.get("min_gain", 0.0))
te = float(np.mean((predict(t, Xt2) > 0.5) == yt2))
print(" %s %12d %16.6f %s"
% (pw(nm, 22), leaves(t), te, rw(why, 24)))
print(" 이득 문턱은 지금 나눔만 봅니다. 두 번 나눠야 좋아지는 자리를 놓칩니다")
print(" 그래서 크게 키운 뒤 깎는 것이 표준입니다")
print(" 비용 복잡도 가지치기를 해 봅니다")
def cc_prune(nd, alpha, X, y):
if nd.j < 0:
return nd, 1, float(np.sum((y - nd.val) ** 2))
m = X[:, nd.j] <= nd.t
lft, nl, el = cc_prune(nd.left, alpha, X[m], y[m])
rgt, nr, er = cc_prune(nd.right, alpha, X[~m], y[~m])
err_sub = el + er
err_leaf = float(np.sum((y - y.mean()) ** 2))
if err_leaf + alpha * 1 <= err_sub + alpha * (nl + nr):
z = Node()
z.val, z.n = float(y.mean()), len(y)
return z, 1, err_leaf
nd.left, nd.right = lft, rgt
return nd, nl + nr, err_sub
print(" 크게 키운 나무를 alpha 를 올려 가며 깎습니다")
print(" %s %s %s %s"
% (pw("alpha", 12), rw("잎 개수", 12), rw("검증 정확도", 16),
rw("최적과의 차", 16)))
for al in [0.0, 0.5, 2.0, 8.0, 30.0]:
big = grow(X2, y2, 20)
pr, nl, _ = cc_prune(big, al, X2, y2)
te = float(np.mean((predict(pr, Xt2) > 0.5) == yt2))
print(" %s %12d %16.6f %16.6f"
% (pw("%.1f" % al, 12), nl, te, te - bayes2))
print(" alpha 를 올리면 잎이 줄고 어느 자리에서 검증이 가장 좋습니다")
print(" 211강의 벌점과 같은 꼴입니다. 여기서는 잎 개수에 벌점을 겁니다")
print(" alpha 를 교차검증으로 고르는 것이 212강 그대로입니다")
# --- 문제 3: 나무가 못 하는 것 -----------------------------------------
print(" 나무가 잘하는 경계와 못하는 경계를 봅니다")
print(" %s %s %s"
% (pw("참 경계", 22), rw("나무 정확도", 18), rw("로지스틱 정확도", 20)))
def logit_acc(Xa, ya, Xb, yb, steps=4000, lr=0.3):
A = np.hstack([Xa, np.ones((len(Xa), 1))])
w = np.zeros(A.shape[1])
for _ in range(steps):
p = 1.0 / (1.0 + np.exp(-A @ w))
w -= lr * (A.T @ (p - ya)) / len(ya)
B = np.hstack([Xb, np.ones((len(Xb), 1))])
return float(np.mean(((B @ w) > 0) == yb))
for nm, f in [("배타적 논리합", lambda Z: 3.0 * np.sign(Z[:, 0] * Z[:, 1])),
("45 도 기울어진 직선", lambda Z: 2.0 * (Z[:, 0] + Z[:, 1])),
("원", lambda Z: 3.0 * (1.2 - (Z ** 2).sum(axis=1) / 3.0))]:
Xa = rng.uniform(-3, 3, (800, 2))
ya = (rng.uniform(0, 1, 800) < 1 / (1 + np.exp(-f(Xa)))).astype(float)
Xb = rng.uniform(-3, 3, (3000, 2))
yb = (rng.uniform(0, 1, 3000) < 1 / (1 + np.exp(-f(Xb)))).astype(float)
t = grow(Xa, ya, 6, gini, 10)
at = float(np.mean((predict(t, Xb) > 0.5) == yb))
print(" %s %18.6f %20.6f" % (pw(nm, 22), at, logit_acc(Xa, ya, Xb, yb)))
print(" 배타적 논리합은 나무가 두 번 나눠 잡습니다. 로지스틱은 못 잡습니다")
print(" 45 도 기울어진 직선은 계단으로 근사해야 해서 손해가 납니다")
print(" 로지스틱은 반대입니다. 직선은 잡고 나머지 둘은 못 잡습니다")
print(" 기울어진 직선을 계단으로 근사하는 비용을 봅니다")
Xd = rng.uniform(-3, 3, (3000, 2))
yd = ((Xd[:, 0] + Xd[:, 1]) > 0).astype(float)
Xdt = rng.uniform(-3, 3, (5000, 2))
ydt = ((Xdt[:, 0] + Xdt[:, 1]) > 0).astype(float)
print(" 잡음이 없는 45 도 경계입니다. 깊이를 늘려 가며 봅니다")
print(" %s %s %s %s"
% (pw("깊이", 8), rw("잎 개수", 12), rw("검증 정확도", 16),
rw("남은 오류율", 16)))
for d in [1, 2, 4, 6, 8, 10]:
t = grow(Xd, yd, d, gini, 5)
te = float(np.mean((predict(t, Xdt) > 0.5) == ydt))
print(" %s %12d %16.6f %16.6f"
% (pw(str(d), 8), leaves(t), te, 1 - te))
print(" 잎을 네 배로 늘려야 오류율이 겨우 절반쯤 됩니다")
print(" 깊이 8 부터 잎이 45 에서 멈추는 것은 잎 최소 5 개 제한 때문입니다")
print(" 계단으로 대각선을 덮으려면 잎이 지수로 필요합니다")
print(" 회전 하나가 나무에게는 아주 비싼 변환입니다")
print(" 회전을 미리 해 주면 어떻게 되는지 봅니다")
R = np.array([[1.0, 1.0], [1.0, -1.0]]) / np.sqrt(2)
print(" %s %s %s"
% (pw("자료를 어떻게 주나", 26), rw("깊이 4 정확도", 20),
rw("잎 개수", 14)))
for nm, A, B in [("그대로", Xd, Xdt), ("45 도 돌려서", Xd @ R.T, Xdt @ R.T)]:
t = grow(A, yd, 4, gini, 5)
print(" %s %20.6f %14d"
% (pw(nm, 26), float(np.mean((predict(t, B) > 0.5) == ydt)),
leaves(t)))
print(" 돌려 주면 깊이 1 짜리 나눔 하나로 완전히 갈립니다")
print(" 228강의 특성공학이 하는 일이 이것입니다")
print(" 나무는 축을 못 만듭니다. 주어진 축으로만 자릅니다")
# --- 문제 4: 회귀 나무와 변수 중요도 ------------------------------------
print(" 나무로 회귀도 합니다. 자를 제곱오차로 바꾸면 됩니다")
def var_crit(y):
if len(y) == 0:
return 0.0
return float(np.var(y))
n4 = 500
X4 = rng.uniform(-3, 3, (n4, 2))
f4 = np.sin(X4[:, 0]) + 0.5 * X4[:, 1]
y4 = f4 + rng.normal(0, 0.3, n4)
X4t = rng.uniform(-3, 3, (3000, 2))
f4t = np.sin(X4t[:, 0]) + 0.5 * X4t[:, 1]
print(" %s %s %s %s"
% (pw("깊이", 8), rw("잎 개수", 12), rw("학습 오차", 16),
rw("검증 오차", 16)))
for d in [1, 2, 4, 6, 10]:
t = grow(X4, y4, d, var_crit, 5)
tr = float(np.mean((y4 - predict(t, X4)) ** 2))
te = float(np.mean((f4t - predict(t, X4t)) ** 2)) + 0.09
print(" %s %12d %16.6f %16.6f"
% (pw(str(d), 8), leaves(t), tr, te))
print(" 검증 오차에는 줄일 수 없는 잡음 분산 0.09 를 더해 눈금을 맞췄습니다")
print(" 잎 안에서는 평균 하나를 냅니다. 예측이 계단 모양입니다")
print(" 매끄러운 함수를 계단으로 덮으므로 편향이 남습니다")
print(" 215강의 이웃 방법과 같은 약점입니다. 218강의 평균이 이것을 고칩니다")
print(" 나무가 낼 수 있는 값이 몇 가지인지 셉니다")
print(" %s %s %s %s"
% (pw("깊이", 8), rw("잎 개수", 12), rw("서로 다른 예측값 수", 24),
rw("참 함수의 치역 폭", 22)))
for d in [1, 2, 4, 6, 10]:
t = grow(X4, y4, d, var_crit, 5)
pv = predict(t, X4t)
print(" %s %12d %24d %22.6f"
% (pw(str(d), 8), leaves(t), len(np.unique(pv)),
float(f4t.max() - f4t.min())))
print(" 예측값의 가짓수가 잎 개수를 못 넘습니다")
print(" 나무 하나로는 매끄러운 곡선을 못 냅니다")
print(" 변수 중요도를 재 봅니다")
n5 = 800
X5 = rng.uniform(-3, 3, (n5, 5))
y5 = ((2.0 * X5[:, 0] + 0.5 * X5[:, 1]
+ rng.normal(0, 1, n5)) > 0).astype(float)
def imp(nd, X, y, d):
out = np.zeros(d)
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])
walk(nd, X, y)
return out / out.sum()
t5 = grow(X5, y5, 6, gini, 10)
iv = imp(t5, X5, y5, 5)
print(" 참 계수는 첫 변수 2.0 둘째 변수 0.5 이고 나머지 셋은 0 입니다")
print(" %s %s %s"
% (pw("변수", 12), rw("참 계수", 14), rw("나무가 준 중요도", 22)))
for j, tc in enumerate([2.0, 0.5, 0.0, 0.0, 0.0]):
print(" %s %14.6f %22.6f"
% (pw(str(j + 1), 12), tc, float(iv[j])))
print(" 쓸모없는 변수 셋에도 0 이 아닌 중요도가 붙습니다")
print(" 나눌 자리를 찾다가 우연히 좋아 보인 자리를 골랐기 때문입니다")
print(" 230강에서 이 편향을 다루는 법을 봅니다")
print(" 가짓수가 많은 변수가 유리해지는 편향을 봅니다")
n6 = 600
y6 = (rng.uniform(0, 1, n6) < 0.5).astype(float)
X6 = np.stack([rng.integers(0, 2, n6).astype(float),
rng.integers(0, 5, n6).astype(float),
rng.integers(0, 50, n6).astype(float),
rng.uniform(0, 1, n6)], axis=1)
print(" 네 변수 모두 라벨과 아무 상관이 없습니다")
print(" %s %s %s"
% (pw("변수의 가짓수", 16), rw("가장 좋은 나눔의 이득", 26),
rw("실제로 시도한 자리 수", 24)))
for j, nm in enumerate(["2 가지", "5 가지", "50 가지", "연속"]):
g, _, _ = best_split(X6[:, [j]], y6, gini, 1)
nu = len(np.unique(X6[:, j]))
print(" %s %26.6f %24d"
% (pw(nm, 16), g, min(nu, 40)))
print(" 아무 정보가 없는데도 시도한 자리가 많을수록 이득이 커 보입니다")
print(" 연속 변수가 50 가지보다 낮은 것은 분위 40 개로만 잘라 봤기 때문입니다")
print(" 212강 문제 3 의 여러 번 보기 문제가 변수 안에서 일어난 것입니다")
print(" 그래서 이득을 나눔의 가짓수로 나누는 이득비를 쓰기도 합니다")
# --- 문제 5: 실무에서 쓰기 ---------------------------------------------
print(" 나무의 실무 성질을 정리합니다")
print(" %s %s %s"
% (pw("성질", 22), rw("좋은 점", 22), rw("나쁜 점", 32)))
for a, b, c in [("규칙으로 읽힘", "설명이 쉬움", "깊으면 못 읽음"),
("눈금 무관", "표준화 불필요", "없음"),
("결측값", "따로 보낼 수 있음", "규칙이 복잡해짐"),
("변수 선택 내장", "쓸모없는 변수 무시", "중요도가 치우침"),
("불안정", "없음", "자료가 조금 바뀌면 다른 나무")]:
print(" %s %s %s" % (pw(a, 22), rw(b, 22), rw(c, 32)))
print(" 마지막 줄이 가장 큰 약점입니다. 수치로 확인합니다")
print(" 자료를 조금 바꾸면 나무가 얼마나 달라지는지 잽니다")
print(" %s %s %s %s"
% (pw("몇 개를 바꿨나", 16), rw("첫 나눔 변수가 같은 비율", 28),
rw("첫 나눔 자리의 표준편차", 26), rw("예측이 같은 비율", 22)))
base_t = grow(X2, y2, 4, gini, 10)
base_p = predict(base_t, Xt2) > 0.5
for k in [1, 5, 30, 120]:
same_j, cuts, same_p = 0, [], []
for _ in range(30):
idx = rng.permutation(n2)[:k]
Xm = X2.copy()
Xm[idx] = rng.uniform(-3, 3, (k, 2))
t = grow(Xm, y2, 4, gini, 10)
same_j += int(t.j == base_t.j)
cuts.append(t.t)
same_p.append(float(np.mean((predict(t, Xt2) > 0.5) == base_p)))
print(" %s %28.6f %26.6f %22.6f"
% (pw(str(k), 16), same_j / 30.0, float(np.std(cuts)),
float(np.mean(same_p))))
print(" 한 점만 바꿔도 첫 나눔 자리가 흔들립니다")
print(" 120 개를 바꾸면 첫 나눔 변수마저 자주 바뀝니다")
print(" 이 불안정이 218강 랜덤포레스트가 이용하는 성질입니다")
print(" 나무 하나와 나무 여럿의 차이를 미리 봅니다")
print(" %s %s %s"
% (pw("몇 그루", 12), rw("검증 정확도", 18), rw("최적과의 차", 18)))
for B in [1, 5, 25, 100]:
votes = np.zeros(len(Xt2))
for _ in range(B):
s = rng.integers(0, n2, n2)
t = grow(X2[s], y2[s], 8, gini, 5)
votes += (predict(t, Xt2) > 0.5).astype(float)
te = float(np.mean((votes / B > 0.5) == yt2))
print(" %s %18.6f %18.6f" % (pw(str(B), 12), te, te - bayes2))
print(" 한 그루보다 여럿의 다수결이 낫습니다. 210강 문제 3 의 평균입니다")
print(" 나무가 불안정하기 때문에 평균이 크게 듣습니다")
print(" 217강은 나무 하나를 봤습니다. 218강은 여러 그루를 섞습니다")
# 215강은 거리로 216강은 분포로 답을 찾았습니다
# 이번에는 예 아니오 질문을 이어 붙여 답을 찾습니다
# 무엇 이웃 방법 나무
# 공간을 어떻게 나누나 점 주위로 축에 나란한 상자로
# 예측 비용 표본에 비례 깊이에 비례
# 사람이 읽을 수 있나 어려움 규칙으로 읽힘
# 눈금에 민감한가 매우 전혀
# 결측값 곤란 따로 보낼 수 있음
# 상자로 나누므로 변수의 눈금을 바꿔도 답이 안 바뀝니다
# 나눔의 좋고 나쁨을 재는 자를 정합니다
# 한 마디의 순도가 얼마나 오르는지로 잽니다
# 양성 비율 지니 엔트로피 오분류율
# 0.0 0.000000 0.000000 0.000000
# 0.1 0.180000 0.468996 0.100000
# 0.3 0.420000 0.881291 0.300000
# 0.5 0.500000 1.000000 0.500000
# 0.7 0.420000 0.881291 0.300000
# 0.9 0.180000 0.468996 0.100000
# 1.0 0.000000 0.000000 0.000000
# 셋 다 0.5 에서 가장 크고 0 과 1 에서 0 입니다
# 지니와 엔트로피는 굽어 있고 오분류율은 꺾인 직선 둘입니다
# 굽어 있어야 나눔의 이득이 제대로 잡힙니다. 곧 확인합니다
# 오분류율을 쓰면 왜 안 되는지 봅니다
# 부모 마디에 양성 400 음성 400 이 있습니다. 두 나눔을 견줍니다
# 나눔 왼쪽 (양성 음성) 오른쪽 (양성 음성)
# 가 나눔 300 100 100 300
# 나 나눔 200 400 200 0
# 자 가 나눔의 이득 나 나눔의 이득 어느 쪽을 고르나
# 지니 0.125000 0.166667 나
# 엔트로피 0.188722 0.311278 나
# 오분류율 0.250000 0.250000 고를 수 없음
# 오분류율은 두 나눔의 이득이 정확히 같아 고를 수가 없습니다
# 지니와 엔트로피는 나 나눔을 고릅니다. 오른쪽이 완전히 순수해지기 때문입니다
# 나 나눔이 나은 것은 그 뒤에 더 나눌 것이 적기 때문입니다
# 132강의 젠센 부등식에서 굽음이 이 차이를 만듭니다
# 실제로 나무를 키워 봅니다
# 참 경계가 원인 자료입니다. 베이즈 최적 정확도는 0.878000 입니다
# 깊이 잎 개수 학습 정확도 검증 정확도 최적과의 차
# 1 2 0.698333 0.666000 -0.212000
# 2 4 0.698333 0.666000 -0.212000
# 3 7 0.790000 0.781000 -0.097000
# 5 18 0.881667 0.844667 -0.033333
# 8 42 0.916667 0.838667 -0.039333
# 15 90 0.991667 0.815333 -0.062667
# 깊이를 키우면 학습 정확도가 1 로 갑니다. 잎마다 한 점씩 담기 때문입니다
# 검증 정확도는 어느 깊이에서 가장 좋고 그 뒤로 떨어집니다
# 214강의 복잡도 손잡이가 여기서는 깊이입니다
# 나무의 유효 자유도가 잎 개수인 것을 확인합니다
# 깊이 잎 개수 학습 오분류율 검증 빼기 학습
# 1 2 0.301667 0.032333
# 2 4 0.301667 0.032333
# 3 7 0.210000 0.009000
# 5 18 0.118333 0.037000
# 8 42 0.083333 0.078000
# 15 90 0.008333 0.176333
# 잎이 늘수록 격차가 벌어집니다. 잎 하나가 모수 하나 노릇을 합니다
# 214강 문제 1 의 낙관이 잎 개수에 비례합니다
# 멈추는 규칙 세 가지를 견줍니다
# 멈추는 규칙 잎 개수 검증 정확도 무엇이 문제인가
# 깊이 제한 3 7 0.781000 너무 얕을 수 있음
# 잎 최소 30 개 13 0.857000 가지마다 다름
# 이득 문턱 0.01 67 0.838667 앞을 못 봄
# 제한 없음 95 0.812667 완전히 외움
# 이득 문턱은 지금 나눔만 봅니다. 두 번 나눠야 좋아지는 자리를 놓칩니다
# 그래서 크게 키운 뒤 깎는 것이 표준입니다
# 비용 복잡도 가지치기를 해 봅니다
# 크게 키운 나무를 alpha 를 올려 가며 깎습니다
# alpha 잎 개수 검증 정확도 최적과의 차
# 0.0 95 0.812667 -0.065333
# 0.5 70 0.822333 -0.055667
# 2.0 9 0.855333 -0.022667
# 8.0 5 0.857000 -0.021000
# 30.0 1 0.666000 -0.212000
# alpha 를 올리면 잎이 줄고 어느 자리에서 검증이 가장 좋습니다
# 211강의 벌점과 같은 꼴입니다. 여기서는 잎 개수에 벌점을 겁니다
# alpha 를 교차검증으로 고르는 것이 212강 그대로입니다
# 나무가 잘하는 경계와 못하는 경계를 봅니다
# 참 경계 나무 정확도 로지스틱 정확도
# 배타적 논리합 0.945000 0.573333
# 45 도 기울어진 직선 0.867333 0.889000
# 원 0.870667 0.685000
# 배타적 논리합은 나무가 두 번 나눠 잡습니다. 로지스틱은 못 잡습니다
# 45 도 기울어진 직선은 계단으로 근사해야 해서 손해가 납니다
# 로지스틱은 반대입니다. 직선은 잡고 나머지 둘은 못 잡습니다
# 기울어진 직선을 계단으로 근사하는 비용을 봅니다
# 잡음이 없는 45 도 경계입니다. 깊이를 늘려 가며 봅니다
# 깊이 잎 개수 검증 정확도 남은 오류율
# 1 2 0.736800 0.263200
# 2 4 0.845600 0.154400
# 4 16 0.941200 0.058800
# 6 40 0.973400 0.026600
# 8 45 0.980200 0.019800
# 10 45 0.980200 0.019800
# 잎을 네 배로 늘려야 오류율이 겨우 절반쯤 됩니다
# 깊이 8 부터 잎이 45 에서 멈추는 것은 잎 최소 5 개 제한 때문입니다
# 계단으로 대각선을 덮으려면 잎이 지수로 필요합니다
# 회전 하나가 나무에게는 아주 비싼 변환입니다
# 회전을 미리 해 주면 어떻게 되는지 봅니다
# 자료를 어떻게 주나 깊이 4 정확도 잎 개수
# 그대로 0.941200 16
# 45 도 돌려서 0.998800 4
# 돌려 주면 깊이 1 짜리 나눔 하나로 완전히 갈립니다
# 228강의 특성공학이 하는 일이 이것입니다
# 나무는 축을 못 만듭니다. 주어진 축으로만 자릅니다
# 나무로 회귀도 합니다. 자를 제곱오차로 바꾸면 됩니다
# 깊이 잎 개수 학습 오차 검증 오차
# 1 2 0.849826 0.834216
# 2 4 0.372302 0.390377
# 4 16 0.135591 0.196160
# 6 48 0.079096 0.154662
# 10 77 0.062486 0.145824
# 검증 오차에는 줄일 수 없는 잡음 분산 0.09 를 더해 눈금을 맞췄습니다
# 잎 안에서는 평균 하나를 냅니다. 예측이 계단 모양입니다
# 매끄러운 함수를 계단으로 덮으므로 편향이 남습니다
# 215강의 이웃 방법과 같은 약점입니다. 218강의 평균이 이것을 고칩니다
# 나무가 낼 수 있는 값이 몇 가지인지 셉니다
# 깊이 잎 개수 서로 다른 예측값 수 참 함수의 치역 폭
# 1 2 2 4.980707
# 2 4 4 4.980707
# 4 16 16 4.980707
# 6 48 48 4.980707
# 10 77 77 4.980707
# 예측값의 가짓수가 잎 개수를 못 넘습니다
# 나무 하나로는 매끄러운 곡선을 못 냅니다
# 변수 중요도를 재 봅니다
# 참 계수는 첫 변수 2.0 둘째 변수 0.5 이고 나머지 셋은 0 입니다
# 변수 참 계수 나무가 준 중요도
# 1 2.000000 0.859694
# 2 0.500000 0.108201
# 3 0.000000 0.011775
# 4 0.000000 0.017465
# 5 0.000000 0.002865
# 쓸모없는 변수 셋에도 0 이 아닌 중요도가 붙습니다
# 나눌 자리를 찾다가 우연히 좋아 보인 자리를 골랐기 때문입니다
# 230강에서 이 편향을 다루는 법을 봅니다
# 가짓수가 많은 변수가 유리해지는 편향을 봅니다
# 네 변수 모두 라벨과 아무 상관이 없습니다
# 변수의 가짓수 가장 좋은 나눔의 이득 실제로 시도한 자리 수
# 2 가지 0.000000 2
# 5 가지 0.000626 5
# 50 가지 0.007020 40
# 연속 0.004476 40
# 아무 정보가 없는데도 시도한 자리가 많을수록 이득이 커 보입니다
# 연속 변수가 50 가지보다 낮은 것은 분위 40 개로만 잘라 봤기 때문입니다
# 212강 문제 3 의 여러 번 보기 문제가 변수 안에서 일어난 것입니다
# 그래서 이득을 나눔의 가짓수로 나누는 이득비를 쓰기도 합니다
# 나무의 실무 성질을 정리합니다
# 성질 좋은 점 나쁜 점
# 규칙으로 읽힘 설명이 쉬움 깊으면 못 읽음
# 눈금 무관 표준화 불필요 없음
# 결측값 따로 보낼 수 있음 규칙이 복잡해짐
# 변수 선택 내장 쓸모없는 변수 무시 중요도가 치우침
# 불안정 없음 자료가 조금 바뀌면 다른 나무
# 마지막 줄이 가장 큰 약점입니다. 수치로 확인합니다
# 자료를 조금 바꾸면 나무가 얼마나 달라지는지 잽니다
# 몇 개를 바꿨나 첫 나눔 변수가 같은 비율 첫 나눔 자리의 표준편차 예측이 같은 비율
# 1 1.000000 0.072889 0.984967
# 5 0.966667 1.087571 0.974200
# 30 0.600000 1.444201 0.941267
# 120 0.533333 1.744747 0.923689
# 한 점만 바꿔도 첫 나눔 자리가 흔들립니다
# 120 개를 바꾸면 첫 나눔 변수마저 자주 바뀝니다
# 이 불안정이 218강 랜덤포레스트가 이용하는 성질입니다
# 나무 하나와 나무 여럿의 차이를 미리 봅니다
# 몇 그루 검증 정확도 최적과의 차
# 1 0.810667 -0.067333
# 5 0.860667 -0.017333
# 25 0.860333 -0.017667
# 100 0.867000 -0.011000
# 한 그루보다 여럿의 다수결이 낫습니다. 210강 문제 3 의 평균입니다
# 나무가 불안정하기 때문에 평균이 크게 듣습니다
# 217강은 나무 하나를 봤습니다. 218강은 여러 그루를 섞습니다