강은 나무를 나란히 심고 평균을 냈습니다. 그래서 분산은 줄었지만 편향은 그대로였습니다. 도 경계에서 그루를 심어도 오류율이 에서 멈췄습니다.
첫 나무가 남긴 잔차를 둘째 나무가 맞추고, 그 잔차를 셋째 나무가 맞춥니다. 얕은 나무를 이어 붙여 깊은 모형을 만듭니다.
그리고 이 절차가 함수 공간에서의 경사하강이라는 것을 봅니다. 강의 경사하강이 모수가 아니라 함수를 움직이는 꼴로 다시 나옵니다.
문제. 부스팅의 골격을 세웁니다.
() 나란히 심기와 무엇이 다른지 정리하세요.
() 잔차를 이어 맞춰 보세요.
() 편향과 분산을 나란히 재세요.
생각의 실마리. 평균은 같은 것을 여러 번 재는 일입니다. 아무리 평균 내도 재는 자가 틀렸으면 못 고칩니다. 틀린 만큼을 다시 재야 합니다.
풀이. () 정리합니다.
| 무엇 | 나란히 심기 | 줄지어 심기 |
|---|---|---|
| 나무를 어떻게 만드나 | 서로 무관하게 | 앞의 잔차를 보고 |
| 무엇을 줄이나 | 분산 | 편향 |
| 개별 나무 | 깊게 | 얕게 |
| 그루를 늘리면 | 평평해짐 | 과적합할 수 있음 |
| 병렬로 되나 | 예 | 아니오 |
둘째 줄이 핵심입니다. 부스팅은 편향을 줄입니다.
() 잔차를 이어 맞춰 봅니다. 깊이 짜리 나무를 한 그루씩 더하고 학습률은 입니다.
| 그루 수 | 학습 오차 | 검증 오차 | 잔차의 표준편차 |
|---|---|---|---|
한 그루로는 계단 넷뿐인데 그루를 더하면 검증 오차가 가 됩니다. 강에서 깊이 나무 하나가 못 하던 일입니다.
나무 하나하나는 얕은데 합이 깊은 모형이 됩니다. 강의 평균은 이렇게 못 합니다. 평균은 편향을 못 줄이기 때문입니다.
() 편향과 분산을 나란히 잽니다.
| 무엇 | 편향 제곱 | 분산 | 합 |
|---|---|---|---|
| 숲 깊이 그루 | |||
| 숲 깊이 그루 | |||
| 부스팅 깊이 그루 | |||
| 부스팅 깊이 그루 |
숲은 깊이 에서 편향이 입니다. 깊이를 로 키워야 이 되는데, 그때 분산이 에서 로 커집니다.
부스팅은 같은 깊이 로 편향을 까지 줄입니다. 얕은 나무를 이어 붙였기 때문입니다.
그루를 까지 늘리면 편향이 으로 더 줄어듭니다. 대신 분산이 에서 으로 커져 합은 오히려 늘었습니다.
이 문제에서 배우는 것. 앙상블의 두 방식은 다른 것을 줄입니다. 나란히 심으면 분산이, 줄지어 심으면 편향이 줄어듭니다. 그래서 개별 나무의 깊이를 정하는 방향이 정반대입니다.
확인 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. 이차 근사로 잎 값을 유도하세요.
손실을 현재 예측 근처에서 이차까지 전개합니다.
한 잎 안에서 가 상수 이므로 잎마다 따로 최소화합니다.
| 단계 | 무엇 |
|---|---|
| 첫째 | 잎 안에서 \sum(g_{i}w+\tfrac12 h_{i}w^{2})+\tfrac12\lambda w^ |
| 둘째 | 로 미분해 |
| 셋째 |
이 값을 대입하면 나눔의 이득 공식이 나옵니다. 그 이득으로 나눌 자리를 고르는 것이 강의 지니 대신 쓰는 자입니다. 손실이 자를 정합니다.
심화 2. 에이다부스트가 지수 손실의 부스팅임을 정리하세요.
| 무엇 | 값 |
|---|---|
| 음의 기울기 | y\,e^ |
| 가중치 해석 | 틀린 점의 무게가 커짐 |
| 최적 계수 | |
| 약한 학습기 | 대개 그루터기 |
기울기가 "부호 곱하기 무게"이므로, 무게를 바꿔 가며 약한 분류기를 학습시키는 절차와 같아집니다. 에이다부스트는 년에 나왔고 이것이 경사하강이라는 해석은 나중에 붙었습니다. 지수 손실은 이상점에 아주 약하고, 그래서 요즘은 로지스틱 손실을 씁니다.
심화 3. 부스팅이 왜 과적합에 의외로 강한지 정리하세요.
| 무엇 | 왜 |
|---|---|
| 여백이 계속 커짐 | 이미 맞힌 점도 더 확실하게 |
| 학습률이 축소 | 경로와 비슷 |
| 얕은 나무 | 개별 복잡도가 낮음 |
| 그래도 과적합함 | 문제 의 표 |
첫 줄이 여백 이론입니다. 학습 오류가 이 된 뒤에도 검증 오차가 더 줄어드는 경우가 있는데, 점들이 경계에서 더 멀어지기 때문이라는 설명입니다. 다만 문제 에서 봤듯 언제나 그런 것은 아니고, 잡음이 많으면 분명히 과적합합니다.
심화 4. 히스토그램 부스팅이 왜 빠른지 설명하세요.
| 무엇 | 정확한 나눔 | 히스토그램 나눔 |
|---|---|---|
| 후보 자리 | 값마다 | 통 개 |
| 정렬 | 필요 | 한 번만 |
| 한 나눔 비용 | 더하기 통 수 | |
| 정확도 | 조금 나음 | 거의 같음 |
변수마다 값을 통에 미리 담아 두면 나눔 이득을 통 단위로 더할 수 있습니다. 부모의 히스토그램에서 한쪽 자식의 것을 빼면 다른 자식이 나오므로 절반만 계산하면 됩니다. 이것이 요즘 구현들이 빠른 주된 이유입니다.
심화 5. 결측값을 부스팅에서 다루는 방법을 쓰세요.
| 방법 | 무엇을 하나 |
|---|---|
| 기본 방향 학습 | 결측을 어느 쪽에 보낼지 이득으로 정함 |
| 따로 한 통 | 결측을 하나의 값으로 |
| 미리 대치 | 다른 방법으로 채움 |
첫 줄이 요즘 표준입니다. 나눔마다 결측을 왼쪽에 보낸 경우와 오른쪽에 보낸 경우의 이득을 둘 다 계산해 더 좋은 쪽을 기본 방향으로 저장합니다. 강 심화 의 대리 나눔보다 싸고, 결측 자체가 정보일 때 그것을 그대로 씁니다.
심화 6. 언제 부스팅을 쓸지 정리하세요.
| 상황 | 쓰나 | 왜 |
|---|---|---|
| 표 형태 자료에 최고 정확도 | 예 | 대개 가장 좋음 |
| 손잡이 맞출 시간이 없음 | 아니오 | 강이 나음 |
| 잡음이 아주 많음 | 절반 | 조기 종료를 꼭 |
| 표본이 아주 적음 | 아니오 | 과적합하기 쉬움 |
| 이미지나 소리 | 아니오 | S의 신경망 |
첫 줄과 마지막 줄이 실무의 경계입니다. 표 형태 자료에서는 부스팅이, 격자 구조가 있는 자료에서는 신경망이 이깁니다. 나무는 화소 사이의 이웃 관계를 모르기 때문입니다.
정답.
| 기호 | 읽는 법 | 뜻 |
|---|---|---|
| 부스팅 | boosting | 약한 모형을 이어 붙여 강하게 만듭니다 |
| 그래디언트 부스팅 | gradient boosting | 손실의 음의 기울기를 나무로 맞춥니다 |
| 함수 경사하강 | functional gradient descent | 함수 공간에서 한 걸음씩 내려갑니다 |
| 학습률 | learning rate | 한 걸음의 크기이자 축소 계수입니다 |
| 축소 | shrinkage | 각 나무의 기여를 줄입니다 |
| 잎 값 벌점 | leaf penalty | 잎 값이 튀는 것을 막습니다 |
| 후버 손실 | Huber loss | 가까이는 제곱 멀리는 절댓값입니다 |
| 조기 종료 | early stopping | 검증이 안 좋아지면 멈춥니다 |
| 가법 모형 | additive model | 변수마다 따로 더합니다 |
| 히스토그램 부스팅 | histogram boosting | 값을 통에 담아 빠르게 나눕니다 |
다음은 220강 서포트 벡터 머신과 커널입니다. 강부터 이 강의까지 다섯 방법이 모두 공간을 어떻게 나눌지를 다뤘습니다. 마지막 방법은 접근이 다릅니다. 경계를 직접 긋되 가장 여유 있는 자리에 긋고, 그 자리를 정하는 데 몇 개의 점만 씁니다.
import numpy as np
rng = np.random.default_rng(20261213)
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)
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_reg(X, g, h, depth, min_leaf=1, lam=1.0, mtry=None, r=None):
nd = Node()
nd.val = -g.sum() / (h.sum() + lam)
if depth <= 0 or len(g) < 2 * min_leaf:
return nd
d = X.shape[1]
cols = np.arange(d) if mtry is None else r.choice(d, mtry, replace=False)
base = -(g.sum() ** 2) / (h.sum() + lam)
bg, bj, bt = 1e-12, -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.04, 0.96, 24))
for t in cuts:
m = v <= t
nl = int(m.sum())
if nl < min_leaf or len(g) - nl < min_leaf:
continue
sc = -(g[m].sum() ** 2) / (h[m].sum() + lam) \
- (g[~m].sum() ** 2) / (h[~m].sum() + lam)
if base - sc > bg:
bg, bj, bt = base - sc, int(j), float(t)
if bj < 0:
return nd
nd.j, nd.t = bj, bt
m = X[:, bj] <= bt
nd.left = grow_reg(X[m], g[m], h[m], depth - 1, min_leaf, lam, mtry, r)
nd.right = grow_reg(X[~m], g[~m], h[~m], depth - 1, min_leaf, lam, 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 gb_fit(X, y, M, eta=0.1, depth=3, loss="sq", lam=1.0, min_leaf=5,
sub=1.0, mtry=None):
n = len(y)
if loss == "sq":
F0 = float(y.mean())
else:
p = float(np.clip(y.mean(), 1e-6, 1 - 1e-6))
F0 = float(np.log(p / (1 - p)))
F = np.full(n, F0)
trees = []
for _ in range(M):
if loss == "sq":
g = F - y
h = np.ones(n)
else:
p = 1.0 / (1.0 + np.exp(-F))
g = p - y
h = np.maximum(p * (1 - p), 1e-6)
if sub < 1.0:
idx = rng.permutation(n)[:max(int(n * sub), 2 * min_leaf)]
else:
idx = np.arange(n)
t = grow_reg(X[idx], g[idx], h[idx], depth, min_leaf, lam, mtry, rng)
F = F + eta * predict(t, X)
trees.append(t)
return F0, trees
def gb_pred(F0, trees, X, eta=0.1, M=None):
out = np.full(len(X), F0)
for t in trees[:M]:
out = out + eta * predict(t, X)
return out
# --- 문제 1: 잔차를 이어서 맞춥니다 -------------------------------------
print(" 218강은 나무를 나란히 심고 평균을 냈습니다")
print(" 이번에는 줄지어 심어 앞 나무가 틀린 것을 뒤 나무가 고칩니다")
print(" %s %s %s"
% (pw("무엇", 20), rw("나란히 심기", 24), rw("줄지어 심기", 24)))
for a, b, c in [("나무를 어떻게 만드나", "서로 무관하게", "앞의 잔차를 보고"),
("무엇을 줄이나", "분산", "편향"),
("개별 나무", "깊게", "얕게"),
("그루를 늘리면", "평평해짐", "과적합할 수 있음"),
("병렬로 되나", "예", "아니오")]:
print(" %s %s %s" % (pw(a, 20), rw(b, 24), rw(c, 24)))
print(" 둘째 줄이 핵심입니다. 부스팅은 편향을 줄입니다")
print(" 가장 단순한 형태부터 봅니다. 제곱오차에서 잔차를 맞춥니다")
n1 = 400
x1 = np.linspace(-3, 3, n1).reshape(-1, 1)
f1 = np.sin(1.5 * x1[:, 0]) + 0.3 * x1[:, 0]
y1 = f1 + rng.normal(0, 0.3, n1)
xv = np.linspace(-3, 3, 1500).reshape(-1, 1)
fv = np.sin(1.5 * xv[:, 0]) + 0.3 * xv[:, 0]
print(" 깊이 2 짜리 나무를 한 그루씩 더해 갑니다. 학습률은 0.3 입니다")
F0, ts1 = gb_fit(x1, y1, 200, 0.3, 2)
print(" %s %s %s %s"
% (pw("그루 수", 12), rw("학습 오차", 16), rw("검증 오차", 16),
rw("잔차의 표준편차", 20)))
for M in [1, 2, 5, 20, 80, 200]:
tr = float(np.mean((y1 - gb_pred(F0, ts1, x1, 0.3, M)) ** 2))
te = float(np.mean((fv - gb_pred(F0, ts1, xv, 0.3, M)) ** 2))
print(" %s %16.6f %16.6f %20.6f"
% (pw(str(M), 12), tr, te,
float(np.std(y1 - gb_pred(F0, ts1, x1, 0.3, M)))))
print(" 한 그루로는 계단 넷뿐인데 200 그루를 더하면 곡선이 됩니다")
print(" 나무 하나하나는 얕은데 합이 깊은 모형이 됩니다")
print(" 218강의 평균은 이렇게 못 합니다. 평균은 편향을 못 줄입니다")
print(" 두 방법의 편향과 분산을 나란히 잽니다")
print(" %s %s %s %s"
% (pw("무엇", 24), rw("편향 제곱", 16), rw("분산", 14), rw("합", 14)))
R1 = 40
def rf_fit(X, y, B, depth, min_leaf=3):
ts = []
n = len(y)
for _ in range(B):
s = rng.integers(0, n, n)
g = -y[s]
h = np.ones(n)
ts.append(grow_reg(X[s], g, h, depth, min_leaf, 1e-9, None, rng))
return ts
for nm, kind in [("숲 깊이 2 25 그루", ("rf", 2, 25)),
("숲 깊이 8 25 그루", ("rf", 8, 25)),
("부스팅 깊이 2 20 그루", ("gb", 2, 20)),
("부스팅 깊이 2 200 그루", ("gb", 2, 200))]:
P = []
for _ in range(R1):
ya = f1 + rng.normal(0, 0.3, n1)
if kind[0] == "rf":
ts = rf_fit(x1, ya, kind[2], kind[1])
P.append(np.mean([predict(t, xv) for t in ts], axis=0))
else:
a0, tt = gb_fit(x1, ya, kind[2], 0.3, kind[1])
P.append(gb_pred(a0, tt, xv, 0.3))
P = np.stack(P)
b2 = float(np.mean((P.mean(axis=0) - fv) ** 2))
vr = float(np.mean(P.var(axis=0)))
print(" %s %16.6f %14.6f %14.6f" % (pw(nm, 24), b2, vr, b2 + vr))
print(" 숲은 깊이를 키워야 편향이 줄고 그때 분산이 커집니다")
print(" 부스팅은 얕은 나무를 이어 붙여 편향을 줄입니다")
print(" 그루를 늘릴수록 편향이 계속 줄어드는 것이 부스팅의 성질입니다")
# --- 문제 2: 왜 이것이 경사하강인가 ------------------------------------
print(" 잔차를 맞추는 것이 왜 경사하강인지 봅니다")
print(" 제곱오차의 예측값에 대한 미분이 예측 빼기 정답입니다")
print(" %s %s %s %s"
% (pw("손실", 20), rw("F 에 대한 미분", 22), rw("무엇을 맞추나", 20),
rw("어디서 봤나", 18)))
for a, b, c, d in [("제곱오차", "F 빼기 y", "잔차", "205강"),
("절댓값", "부호", "부호만", "188강"),
("로지스틱", "p 빼기 y", "확률 오차", "208강"),
("지수", "마이너스 y exp", "가중 부호", "197강")]:
print(" %s %s %s %s" % (pw(a, 20), rw(b, 22), rw(c, 20), rw(d, 18)))
print(" 나무는 그 미분의 마이너스 방향을 근사하는 걸음입니다")
print(" 함수 공간에서 한 걸음씩 내려가는 것이라 함수 경사하강이라 합니다")
print(" 걸음 크기가 정말 손실을 줄이는지 확인합니다")
F0b, tsb = gb_fit(x1, y1, 60, 0.3, 2)
print(" %s %s %s %s"
% (pw("걸음", 10), rw("학습 손실", 16), rw("앞 걸음 대비 감소", 22),
rw("기울기 노름", 18)))
prev = None
for M in [0, 1, 2, 5, 20, 60]:
Fv = gb_pred(F0b, tsb, x1, 0.3, M)
L = float(np.mean((y1 - Fv) ** 2))
gn = float(np.linalg.norm(Fv - y1) / n1)
dd = 0.0 if prev is None else prev - L
prev = L
print(" %s %16.6f %22.6f %18.6f"
% (pw(str(M), 10), L, dd, gn))
print(" 손실이 걸음마다 줄고 기울기 노름도 함께 줄어듭니다")
print(" 107강의 경사하강과 같은 그림입니다. 다만 모수가 아니라 함수를 움직입니다")
print(" 학습률과 그루 수가 맞바뀌는 것을 봅니다")
print(" %s %s %s %s"
% (pw("학습률", 12), rw("가장 좋은 그루 수", 22), rw("그때 검증 오차", 20),
rw("학습률 곱하기 그루", 22)))
for eta in [0.02, 0.05, 0.2, 0.5]:
a0, tt = gb_fit(x1, y1, 600, eta, 2)
es = [float(np.mean((fv - gb_pred(a0, tt, xv, eta, M)) ** 2))
for M in range(1, 601, 5)]
k = int(np.argmin(es))
Mb = 1 + 5 * k
print(" %s %22d %20.6f %22.6f"
% (pw("%.2f" % eta, 12), Mb, es[k], eta * Mb))
print(" 학습률을 2.5 배 줄이니 그루 수가 2.53 배 늘었습니다")
print(" 앞의 세 줄은 마지막 열이 6.2 에서 6.6 사이로 비슷합니다")
print(" 둘의 곱이 실질 손잡이입니다. 그것이 얼마나 멀리 가는지를 정합니다")
print(" 0.50 은 격자의 최소 자리에 걸려 곱이 어긋났습니다")
print(" 그리고 학습률이 작을수록 도달한 검증 오차가 조금씩 더 낮습니다")
print(" 학습률이 작을수록 왜 나은지 봅니다")
print(" %s %s %s %s"
% (pw("학습률", 12), rw("그루 20 검증 오차", 22), rw("가장 좋은 검증 오차", 24),
rw("둘의 차", 14)))
for eta in [0.05, 0.2, 0.5, 1.0]:
a0, tt = gb_fit(x1, y1, 600, eta, 2)
e20 = float(np.mean((fv - gb_pred(a0, tt, xv, eta, 20)) ** 2))
es = [float(np.mean((fv - gb_pred(a0, tt, xv, eta, M)) ** 2))
for M in range(1, 601, 5)]
print(" %s %22.6f %24.6f %14.6f"
% (pw("%.2f" % eta, 12), e20, min(es), e20 - min(es)))
print(" 가운데 열은 20 그루에서 얼마나 왔는지이고 오른쪽 열은 끝까지 갔을 때입니다")
print(" 학습률 0.05 는 20 그루로는 아직 멀지만 끝까지 가면 0.008246 으로 가장 낮습니다")
print(" 학습률 1.0 은 20 그루로 이미 다 왔는데 그 자리가 0.015275 로 가장 나쁩니다")
print(" 한 걸음이 크면 빨리 가는 대신 좋은 자리를 지나칩니다")
print(" 211강의 정규화와 같습니다. 학습률이 곧 축소 계수입니다")
# --- 문제 3: 손실을 바꿔 끼웁니다 --------------------------------------
print(" 손실을 바꾸면 다른 문제가 됩니다")
n3 = 700
X3 = rng.uniform(-3, 3, (n3, 4))
z3 = 1.4 - (X3[:, :2] ** 2).sum(axis=1) / 2.0 + 0.7 * X3[:, 2]
p3 = 1.0 / (1.0 + np.exp(-2 * z3))
y3 = (rng.uniform(0, 1, n3) < p3).astype(float)
X3t = rng.uniform(-3, 3, (2500, 4))
z3t = 1.4 - (X3t[:, :2] ** 2).sum(axis=1) / 2.0 + 0.7 * X3t[:, 2]
p3t = 1.0 / (1.0 + np.exp(-2 * z3t))
y3t = (rng.uniform(0, 1, 2500) < p3t).astype(float)
bayes3 = float(np.mean((p3t > 0.5) == y3t))
print(" 분류로 넘어갑니다. 베이즈 최적 정확도는 %.6f 입니다" % bayes3)
F03, ts3 = gb_fit(X3, y3, 300, 0.1, 3, "log")
print(" %s %s %s %s"
% (pw("그루 수", 12), rw("학습 로그손실", 20), rw("검증 로그손실", 20),
rw("검증 정확도", 16)))
for M in [1, 10, 50, 150, 300]:
Ftr = gb_pred(F03, ts3, X3, 0.1, M)
Fte = gb_pred(F03, ts3, X3t, 0.1, M)
ptr = 1.0 / (1.0 + np.exp(-Ftr))
pte = 1.0 / (1.0 + np.exp(-Fte))
ltr = float(-np.mean(y3 * np.log(ptr + 1e-12)
+ (1 - y3) * np.log(1 - ptr + 1e-12)))
lte = float(-np.mean(y3t * np.log(pte + 1e-12)
+ (1 - y3t) * np.log(1 - pte + 1e-12)))
print(" %s %20.6f %20.6f %16.6f"
% (pw(str(M), 12), ltr, lte, float(np.mean((pte > 0.5) == y3t))))
print(" 학습 로그손실은 계속 줄어드는데 검증은 150 에서 0.271255 로 최소를 찍고 돌아섭니다")
print(" 그런데 검증 정확도는 300 까지 계속 올라갑니다")
print(" 212강 문제 3 에서 본 대로 어느 눈금으로 고르느냐가 답을 바꿉니다")
print(" 218강과 다릅니다. 부스팅은 그루를 늘리면 과적합할 수 있습니다")
print(" 그래서 조기 종료가 필요합니다. 214강 문제 4 의 그 조기 종료입니다")
print(" 손실마다 이상점에 얼마나 끌리는지 봅니다")
print(" %s %s %s %s"
% (pw("손실", 18), rw("잔차 1 일 때 기울기", 24),
rw("잔차 5 일 때 기울기", 24), rw("이상점에 끌리나", 22)))
for nm, gf, tag in [("제곱오차", lambda r: r, "매우"),
("절댓값", lambda r: np.sign(r), "적게"),
("후버 1.0", lambda r: np.clip(r, -1, 1), "적게")]:
print(" %s %24.6f %24.6f %s"
% (pw(nm, 18), float(gf(np.array([1.0]))[0]),
float(gf(np.array([5.0]))[0]), rw(tag, 22)))
print(" 제곱오차는 잔차가 다섯 배면 기울기도 다섯 배입니다")
print(" 절댓값과 후버는 멀리 있는 점에 덜 끌립니다")
print(" 188강에서 본 이상점 문제가 손실 고르기로 풀립니다")
print(" 이상점을 넣고 실제로 견줍니다")
y1o = y1.copy()
bad = rng.permutation(n1)[:12]
y1o[bad] += rng.choice([-1.0, 1.0], 12) * 6.0
print(" 400 개 중 12 개를 6 만큼 튀게 만들었습니다")
print(" %s %s %s"
% (pw("손실", 18), rw("검증 오차", 18), rw("이상점 없을 때 대비", 24)))
def gb_fit_loss(X, y, M, eta, depth, kind, delta=1.0):
F = np.full(len(y), float(np.median(y)))
ts = []
for _ in range(M):
r = y - F
if kind == "sq":
g = -r
elif kind == "abs":
g = -np.sign(r)
else:
g = -np.clip(r, -delta, delta)
h = np.ones(len(y))
t = grow_reg(X, g, h, depth, 5, 1e-9, None, rng)
F = F + eta * predict(t, X)
ts.append(t)
return float(np.median(y)), ts
base_e = {}
for nm, kind in [("제곱오차", "sq"), ("절댓값", "abs"), ("후버 1.0", "hub")]:
a0, tt = gb_fit_loss(x1, y1, 150, 0.2, 2, kind)
base_e[nm] = float(np.mean((fv - gb_pred(a0, tt, xv, 0.2)) ** 2))
for nm, kind in [("제곱오차", "sq"), ("절댓값", "abs"), ("후버 1.0", "hub")]:
a0, tt = gb_fit_loss(x1, y1o, 150, 0.2, 2, kind)
e = float(np.mean((fv - gb_pred(a0, tt, xv, 0.2)) ** 2))
print(" %s %18.6f %24.6f" % (pw(nm, 18), e, e - base_e[nm]))
print(" 제곱오차가 0.082815 만큼 나빠져 가장 크게 흔들립니다")
print(" 절댓값은 오히려 0.002561 좋아지고 후버는 0.004451 만 나빠집니다")
print(" 다만 이상점이 없으면 제곱오차가 0.010801 로 셋 중 가장 좋습니다")
print(" 튼튼한 손실은 공짜가 아닙니다. 깨끗한 자료에서는 손해입니다")
print(" 손실을 고르는 것이 곧 무엇을 중요하게 볼지 정하는 것입니다")
# --- 문제 4: 벌점과 무작위를 넣습니다 ----------------------------------
print(" 부스팅에도 정규화를 겁니다")
print(" %s %s %s"
% (pw("무엇", 22), rw("무엇을 막나", 24), rw("어디서 봤나", 22)))
for a, b, c in [("학습률", "한 걸음이 너무 큼", "문제 2"),
("나무 깊이", "상호작용 차수", "이 문제"),
("잎 값에 벌점", "잎 하나가 튐", "211강"),
("표본 뽑기", "같은 점에 매달림", "218강"),
("변수 뽑기", "같은 변수만 씀", "218강"),
("조기 종료", "그루가 너무 많음", "문제 3")]:
print(" %s %s %s" % (pw(a, 22), rw(b, 24), rw(c, 22)))
print(" 211강의 벌점이 잎 값에 그대로 걸립니다. 잎 값 공식이 이렇습니다")
print(" 잎 값은 기울기 합의 마이너스를 이차 미분 합 더하기 lambda 로 나눈 것입니다")
print(" 잎 값 벌점을 바꿔 가며 잽니다")
print(" %s %s %s %s"
% (pw("lambda", 12), rw("잎 값의 최대 절댓값", 24), rw("검증 로그손실", 20),
rw("검증 정확도", 16)))
for lam in [0.0, 1.0, 10.0, 100.0]:
a0, tt = gb_fit(X3, y3, 150, 0.1, 3, "log", lam)
vals = []
for t in tt:
st = [t]
while st:
c = st.pop()
if c.j < 0:
vals.append(abs(c.val))
else:
st.append(c.left)
st.append(c.right)
Fte = gb_pred(a0, tt, X3t, 0.1)
pte = 1.0 / (1.0 + np.exp(-Fte))
lte = float(-np.mean(y3t * np.log(pte + 1e-12)
+ (1 - y3t) * np.log(1 - pte + 1e-12)))
print(" %s %24.6f %20.6f %16.6f"
% (pw("%.1f" % lam, 12), float(max(vals)), lte,
float(np.mean((pte > 0.5) == y3t))))
print(" lambda 를 키우면 잎 값이 작아집니다. 211강의 축소와 같습니다")
print(" 아주 크게 걸면 걸음이 너무 작아져 오히려 나빠집니다")
print(" 깊이가 무엇을 정하는지 봅니다")
print(" 깊이 d 인 나무는 변수 d 개까지의 상호작용을 담습니다")
print(" %s %s %s %s"
% (pw("깊이", 8), rw("담을 수 있는 상호작용", 24), rw("검증 정확도", 18),
rw("최적과의 차", 16)))
for dep in [1, 2, 3, 6]:
a0, tt = gb_fit(X3, y3, 200, 0.1, dep, "log")
Fte = gb_pred(a0, tt, X3t, 0.1)
ac = float(np.mean((Fte > 0) == y3t))
print(" %s %24s %18.6f %16.6f"
% (pw(str(dep), 8), rw("변수 %d 개까지" % dep, 24), ac, ac - bayes3))
print(" 깊이 1 은 변수 하나씩만 봅니다. 이것을 가법 모형이라 합니다")
print(" 참 경계에 제곱 항이 있으므로 깊이 1 로는 부족합니다")
print(" 깊이를 필요 이상으로 키우면 분산만 늘어납니다")
print(" 표본 뽑기와 변수 뽑기를 넣어 봅니다")
print(" %s %s %s"
% (pw("무엇을 넣었나", 26), rw("검증 정확도", 18), rw("최적과의 차", 18)))
for nm, kw in [("아무것도 안 넣음", dict(sub=1.0, mtry=None)),
("표본 절반씩", dict(sub=0.5, mtry=None)),
("변수 둘씩", dict(sub=1.0, mtry=2)),
("둘 다", dict(sub=0.5, mtry=2))]:
a0, tt = gb_fit(X3, y3, 200, 0.1, 3, "log", 1.0, 5,
kw["sub"], kw["mtry"])
ac = float(np.mean((gb_pred(a0, tt, X3t, 0.1) > 0) == y3t))
print(" %s %18.6f %18.6f" % (pw(nm, 26), ac, ac - bayes3))
print(" 이 자료에서는 무작위를 넣어도 좋아지지 않습니다")
print(" 표본 700 에 변수 4 개뿐이라 더 조일 것이 없기 때문입니다")
print(" 변수가 수백 개이고 표본이 적을 때라야 이 장치가 듭니다")
print(" 218강에서는 상관을 낮추는 장치였고 여기서는 정규화 장치입니다")
print(" 같은 도구가 다른 자리에서 다른 일을 합니다")
# --- 문제 5: 실무에서 쓰기 ---------------------------------------------
print(" 조기 종료를 제대로 해 봅니다")
sp = rng.permutation(n3)
tr_i, va_i = sp[:500], sp[500:]
a0, tt = gb_fit(X3[tr_i], y3[tr_i], 400, 0.1, 3, "log")
print(" 학습 500 검증 200 으로 나누고 검증 손실이 가장 낮은 자리를 찾습니다")
print(" %s %s %s %s"
% (pw("그루 수", 12), rw("검증 로그손실", 20), rw("최소에서 얼마나 지났나", 26),
rw("멈출 때인가", 16)))
lv = []
for M in range(1, 401):
Fv2 = gb_pred(a0, tt, X3[va_i], 0.1, M)
pv2 = 1.0 / (1.0 + np.exp(-Fv2))
lv.append(float(-np.mean(y3[va_i] * np.log(pv2 + 1e-12)
+ (1 - y3[va_i]) * np.log(1 - pv2 + 1e-12))))
Mb = int(np.argmin(lv)) + 1
for M in [10, 50, Mb, 200, 400]:
print(" %s %20.6f %26d %s"
% (pw(str(M), 12), lv[M - 1], max(M - Mb, 0),
rw("예" if M - Mb >= 50 else "아니오", 16)))
print(" 가장 좋은 자리는 %d 그루입니다" % Mb)
print(" 그 뒤로 50 그루를 더 심어도 안 좋아지면 멈추는 것이 표준입니다")
print(" 212강의 교차검증으로 그루 수를 고르는 것과 같은 일입니다")
print(" 숲과 부스팅을 나란히 견줍니다")
print(" %s %s %s %s"
% (pw("방법", 26), rw("검증 정확도", 18), rw("최적과의 차", 16),
rw("나무 수", 12)))
rf_ts = rf_fit(X3, (y3 - y3.mean()), 200, 10, 3)
rf_p = np.mean([predict(t, X3t) for t in rf_ts], axis=0) + y3.mean()
print(" %s %18.6f %16.6f %12d"
% (pw("숲 200 그루 깊이 10", 26),
float(np.mean((rf_p > 0.5) == y3t)),
float(np.mean((rf_p > 0.5) == y3t)) - bayes3, 200))
for nm, M, dep, eta in [("부스팅 100 그루 깊이 3", 100, 3, 0.1),
("부스팅 300 그루 깊이 3", 300, 3, 0.1),
("부스팅 300 그루 깊이 1", 300, 1, 0.1)]:
a1, t1 = gb_fit(X3, y3, M, eta, dep, "log")
ac = float(np.mean((gb_pred(a1, t1, X3t, eta) > 0) == y3t))
print(" %s %18.6f %16.6f %12d"
% (pw(nm, 26), ac, ac - bayes3, M))
print(" 부스팅 100 그루가 숲 200 그루와 같은 자리입니다")
print(" 부스팅 300 그루는 0.886000 으로 더 갑니다. 숲은 더 심어도 안 갑니다")
print(" 깊이 1 로 줄이면 0.879200 으로 떨어집니다. 상호작용을 못 담기 때문입니다")
print(" 대신 손잡이가 많고 조기 종료가 필요합니다")
print(" 숲은 대충 돌려도 되고 부스팅은 손이 갑니다")
print(" 실무 손잡이를 정리합니다")
print(" %s %s %s"
% (pw("손잡이", 20), rw("어디서 시작하나", 22), rw("어떻게 움직이나", 26)))
for a, b, c in [("학습률", "0.05 에서 0.1", "작게 하고 그루를 늘림"),
("그루 수", "조기 종료로", "검증이 정함"),
("깊이", "3 에서 6", "상호작용이 깊으면 키움"),
("잎 값 벌점", "1.0", "과적합하면 키움"),
("표본 뽑기", "0.5 에서 0.8", "표본이 많으면 낮춤")]:
print(" %s %s %s" % (pw(a, 20), rw(b, 22), rw(c, 26)))
print(" 첫 줄과 둘째 줄이 한 쌍입니다. 문제 2 에서 곱이 손잡이라 했습니다")
print(" 부스팅이 못 하는 것도 그대로입니다")
xe = np.linspace(0, 3, 300).reshape(-1, 1)
ye = 2.0 * xe[:, 0] + rng.normal(0, 0.2, 300)
ae, te_ = gb_fit(xe, ye, 200, 0.1, 3)
print(" 학습은 x 가 0 에서 3 사이이고 참 함수는 2x 입니다")
print(" %s %s %s %s"
% (pw("x", 10), rw("참값", 14), rw("부스팅 예측", 16), rw("차", 14)))
for xq in [1.0, 3.0, 5.0, 10.0]:
pv = float(gb_pred(ae, te_, np.array([[xq]]), 0.1)[0])
print(" %s %14.6f %16.6f %14.6f"
% (pw("%.1f" % xq, 10), 2.0 * xq, pv, pv - 2.0 * xq))
print(" 나무를 쓰는 한 학습 범위 밖은 상수입니다. 218강과 같습니다")
print(" 기울어진 경계도 여전히 계단으로 덮습니다")
print(" 219강은 나무를 줄지어 심었습니다. 220강은 경계를 직접 긋습니다")
# 218강은 나무를 나란히 심고 평균을 냈습니다
# 이번에는 줄지어 심어 앞 나무가 틀린 것을 뒤 나무가 고칩니다
# 무엇 나란히 심기 줄지어 심기
# 나무를 어떻게 만드나 서로 무관하게 앞의 잔차를 보고
# 무엇을 줄이나 분산 편향
# 개별 나무 깊게 얕게
# 그루를 늘리면 평평해짐 과적합할 수 있음
# 병렬로 되나 예 아니오
# 둘째 줄이 핵심입니다. 부스팅은 편향을 줄입니다
# 가장 단순한 형태부터 봅니다. 제곱오차에서 잔차를 맞춥니다
# 깊이 2 짜리 나무를 한 그루씩 더해 갑니다. 학습률은 0.3 입니다
# 그루 수 학습 오차 검증 오차 잔차의 표준편차
# 1 0.493973 0.432777 0.702832
# 2 0.329616 0.259716 0.574122
# 5 0.151224 0.071727 0.388875
# 20 0.091072 0.009224 0.301781
# 80 0.081324 0.010224 0.285173
# 200 0.076966 0.011971 0.277427
# 한 그루로는 계단 넷뿐인데 200 그루를 더하면 곡선이 됩니다
# 나무 하나하나는 얕은데 합이 깊은 모형이 됩니다
# 218강의 평균은 이렇게 못 합니다. 평균은 편향을 못 줄입니다
# 두 방법의 편향과 분산을 나란히 잽니다
# 무엇 편향 제곱 분산 합
# 숲 깊이 2 25 그루 0.062939 0.007537 0.070476
# 숲 깊이 8 25 그루 0.000457 0.016809 0.017266
# 부스팅 깊이 2 20 그루 0.003893 0.006110 0.010002
# 부스팅 깊이 2 200 그루 0.000930 0.011096 0.012026
# 숲은 깊이를 키워야 편향이 줄고 그때 분산이 커집니다
# 부스팅은 얕은 나무를 이어 붙여 편향을 줄입니다
# 그루를 늘릴수록 편향이 계속 줄어드는 것이 부스팅의 성질입니다
# 잔차를 맞추는 것이 왜 경사하강인지 봅니다
# 제곱오차의 예측값에 대한 미분이 예측 빼기 정답입니다
# 손실 F 에 대한 미분 무엇을 맞추나 어디서 봤나
# 제곱오차 F 빼기 y 잔차 205강
# 절댓값 부호 부호만 188강
# 로지스틱 p 빼기 y 확률 오차 208강
# 지수 마이너스 y exp 가중 부호 197강
# 나무는 그 미분의 마이너스 방향을 근사하는 걸음입니다
# 함수 공간에서 한 걸음씩 내려가는 것이라 함수 경사하강이라 합니다
# 걸음 크기가 정말 손실을 줄이는지 확인합니다
# 걸음 학습 손실 앞 걸음 대비 감소 기울기 노름
# 0 0.794957 0.000000 0.044580
# 1 0.493973 0.300985 0.035142
# 2 0.329616 0.164356 0.028706
# 5 0.151224 0.178392 0.019444
# 20 0.091072 0.060153 0.015089
# 60 0.082176 0.008896 0.014333
# 손실이 걸음마다 줄고 기울기 노름도 함께 줄어듭니다
# 107강의 경사하강과 같은 그림입니다. 다만 모수가 아니라 함수를 움직입니다
# 학습률과 그루 수가 맞바뀌는 것을 봅니다
# 학습률 가장 좋은 그루 수 그때 검증 오차 학습률 곱하기 그루
# 0.02 331 0.008239 6.620000
# 0.05 131 0.008246 6.550000
# 0.20 31 0.008577 6.200000
# 0.50 31 0.010462 15.500000
# 학습률을 2.5 배 줄이니 그루 수가 2.53 배 늘었습니다
# 앞의 세 줄은 마지막 열이 6.2 에서 6.6 사이로 비슷합니다
# 둘의 곱이 실질 손잡이입니다. 그것이 얼마나 멀리 가는지를 정합니다
# 0.50 은 격자의 최소 자리에 걸려 곱이 어긋났습니다
# 그리고 학습률이 작을수록 도달한 검증 오차가 조금씩 더 낮습니다
# 학습률이 작을수록 왜 나은지 봅니다
# 학습률 그루 20 검증 오차 가장 좋은 검증 오차 둘의 차
# 0.05 0.166495 0.008246 0.158249
# 0.20 0.011825 0.008577 0.003248
# 0.50 0.011252 0.010462 0.000789
# 1.00 0.015800 0.015275 0.000525
# 가운데 열은 20 그루에서 얼마나 왔는지이고 오른쪽 열은 끝까지 갔을 때입니다
# 학습률 0.05 는 20 그루로는 아직 멀지만 끝까지 가면 0.008246 으로 가장 낮습니다
# 학습률 1.0 은 20 그루로 이미 다 왔는데 그 자리가 0.015275 로 가장 나쁩니다
# 한 걸음이 크면 빨리 가는 대신 좋은 자리를 지나칩니다
# 211강의 정규화와 같습니다. 학습률이 곧 축소 계수입니다
# 손실을 바꾸면 다른 문제가 됩니다
# 분류로 넘어갑니다. 베이즈 최적 정확도는 0.896400 입니다
# 그루 수 학습 로그손실 검증 로그손실 검증 정확도
# 1 0.560793 0.550975 0.737600
# 10 0.400221 0.419030 0.862400
# 50 0.211986 0.283443 0.878000
# 150 0.123285 0.271255 0.882800
# 300 0.073105 0.292806 0.886000
# 학습 로그손실은 계속 줄어드는데 검증은 150 에서 0.271255 로 최소를 찍고 돌아섭니다
# 그런데 검증 정확도는 300 까지 계속 올라갑니다
# 212강 문제 3 에서 본 대로 어느 눈금으로 고르느냐가 답을 바꿉니다
# 218강과 다릅니다. 부스팅은 그루를 늘리면 과적합할 수 있습니다
# 그래서 조기 종료가 필요합니다. 214강 문제 4 의 그 조기 종료입니다
# 손실마다 이상점에 얼마나 끌리는지 봅니다
# 손실 잔차 1 일 때 기울기 잔차 5 일 때 기울기 이상점에 끌리나
# 제곱오차 1.000000 5.000000 매우
# 절댓값 1.000000 1.000000 적게
# 후버 1.0 1.000000 1.000000 적게
# 제곱오차는 잔차가 다섯 배면 기울기도 다섯 배입니다
# 절댓값과 후버는 멀리 있는 점에 덜 끌립니다
# 188강에서 본 이상점 문제가 손실 고르기로 풀립니다
# 이상점을 넣고 실제로 견줍니다
# 400 개 중 12 개를 6 만큼 튀게 만들었습니다
# 손실 검증 오차 이상점 없을 때 대비
# 제곱오차 0.093616 0.082815
# 절댓값 0.021622 -0.002561
# 후버 1.0 0.015227 0.004451
# 제곱오차가 0.082815 만큼 나빠져 가장 크게 흔들립니다
# 절댓값은 오히려 0.002561 좋아지고 후버는 0.004451 만 나빠집니다
# 다만 이상점이 없으면 제곱오차가 0.010801 로 셋 중 가장 좋습니다
# 튼튼한 손실은 공짜가 아닙니다. 깨끗한 자료에서는 손해입니다
# 손실을 고르는 것이 곧 무엇을 중요하게 볼지 정하는 것입니다
# 부스팅에도 정규화를 겁니다
# 무엇 무엇을 막나 어디서 봤나
# 학습률 한 걸음이 너무 큼 문제 2
# 나무 깊이 상호작용 차수 이 문제
# 잎 값에 벌점 잎 하나가 튐 211강
# 표본 뽑기 같은 점에 매달림 218강
# 변수 뽑기 같은 변수만 씀 218강
# 조기 종료 그루가 너무 많음 문제 3
# 211강의 벌점이 잎 값에 그대로 걸립니다. 잎 값 공식이 이렇습니다
# 잎 값은 기울기 합의 마이너스를 이차 미분 합 더하기 lambda 로 나눈 것입니다
# 잎 값 벌점을 바꿔 가며 잽니다
# lambda 잎 값의 최대 절댓값 검증 로그손실 검증 정확도
# 0.0 3.874030 0.273973 0.881600
# 1.0 1.626724 0.271255 0.882800
# 10.0 1.383697 0.270479 0.885200
# 100.0 0.550358 0.324107 0.877200
# lambda 를 키우면 잎 값이 작아집니다. 211강의 축소와 같습니다
# 아주 크게 걸면 걸음이 너무 작아져 오히려 나빠집니다
# 깊이가 무엇을 정하는지 봅니다
# 깊이 d 인 나무는 변수 d 개까지의 상호작용을 담습니다
# 깊이 담을 수 있는 상호작용 검증 정확도 최적과의 차
# 1 변수 1 개까지 0.877200 -0.019200
# 2 변수 2 개까지 0.882400 -0.014000
# 3 변수 3 개까지 0.885600 -0.010800
# 6 변수 6 개까지 0.878000 -0.018400
# 깊이 1 은 변수 하나씩만 봅니다. 이것을 가법 모형이라 합니다
# 참 경계에 제곱 항이 있으므로 깊이 1 로는 부족합니다
# 깊이를 필요 이상으로 키우면 분산만 늘어납니다
# 표본 뽑기와 변수 뽑기를 넣어 봅니다
# 무엇을 넣었나 검증 정확도 최적과의 차
# 아무것도 안 넣음 0.885600 -0.010800
# 표본 절반씩 0.880000 -0.016400
# 변수 둘씩 0.884800 -0.011600
# 둘 다 0.885600 -0.010800
# 이 자료에서는 무작위를 넣어도 좋아지지 않습니다
# 표본 700 에 변수 4 개뿐이라 더 조일 것이 없기 때문입니다
# 변수가 수백 개이고 표본이 적을 때라야 이 장치가 듭니다
# 218강에서는 상관을 낮추는 장치였고 여기서는 정규화 장치입니다
# 같은 도구가 다른 자리에서 다른 일을 합니다
# 조기 종료를 제대로 해 봅니다
# 학습 500 검증 200 으로 나누고 검증 손실이 가장 낮은 자리를 찾습니다
# 그루 수 검증 로그손실 최소에서 얼마나 지났나 멈출 때인가
# 10 0.405842 0 아니오
# 50 0.265373 0 아니오
# 95 0.243483 0 아니오
# 200 0.275045 105 예
# 400 0.331600 305 예
# 가장 좋은 자리는 95 그루입니다
# 그 뒤로 50 그루를 더 심어도 안 좋아지면 멈추는 것이 표준입니다
# 212강의 교차검증으로 그루 수를 고르는 것과 같은 일입니다
# 숲과 부스팅을 나란히 견줍니다
# 방법 검증 정확도 최적과의 차 나무 수
# 숲 200 그루 깊이 10 0.880000 -0.016400 200
# 부스팅 100 그루 깊이 3 0.880800 -0.015600 100
# 부스팅 300 그루 깊이 3 0.886000 -0.010400 300
# 부스팅 300 그루 깊이 1 0.879200 -0.017200 300
# 부스팅 100 그루가 숲 200 그루와 같은 자리입니다
# 부스팅 300 그루는 0.886000 으로 더 갑니다. 숲은 더 심어도 안 갑니다
# 깊이 1 로 줄이면 0.879200 으로 떨어집니다. 상호작용을 못 담기 때문입니다
# 대신 손잡이가 많고 조기 종료가 필요합니다
# 숲은 대충 돌려도 되고 부스팅은 손이 갑니다
# 실무 손잡이를 정리합니다
# 손잡이 어디서 시작하나 어떻게 움직이나
# 학습률 0.05 에서 0.1 작게 하고 그루를 늘림
# 그루 수 조기 종료로 검증이 정함
# 깊이 3 에서 6 상호작용이 깊으면 키움
# 잎 값 벌점 1.0 과적합하면 키움
# 표본 뽑기 0.5 에서 0.8 표본이 많으면 낮춤
# 첫 줄과 둘째 줄이 한 쌍입니다. 문제 2 에서 곱이 손잡이라 했습니다
# 부스팅이 못 하는 것도 그대로입니다
# 학습은 x 가 0 에서 3 사이이고 참 함수는 2x 입니다
# x 참값 부스팅 예측 차
# 1.0 2.000000 2.032574 0.032574
# 3.0 6.000000 5.844425 -0.155575
# 5.0 10.000000 5.844425 -4.155575
# 10.0 20.000000 5.844425 -14.155575
# 나무를 쓰는 한 학습 범위 밖은 상수입니다. 218강과 같습니다
# 기울어진 경계도 여전히 계단으로 덮습니다
# 219강은 나무를 줄지어 심었습니다. 220강은 경계를 직접 긋습니다