강부터 강까지 재고 다루고 만들고 골랐습니다. 이 강의는 마지막 물음에 답합니다.
그런데 이 물음에는 서로 아주 다른 네 가지가 섞여 있습니다. "무엇을 보나"와 "무엇을 바꾸면 되나"는 같은 질문이 아닙니다.
이 강의는 전역 중요도·부분 의존·한 예측의 설명 셋을 보고, 각각이 무엇을 보장하고 무엇을 보장하지 않는지를 수치로 가립니다.
문제. 물음을 가릅니다.
() 서로 다른 물음을 정리하세요.
() 중요도가 인과가 아님을 보이세요.
() 겹친 변수에서 중요도가 어떻게 갈리는지 보세요.
생각의 실마리. "이 변수가 중요하다"는 말은 두 가지로 읽힙니다. 예측에 쓰인다는 뜻과 바꾸면 결과가 바뀐다는 뜻입니다. 모형은 앞의 것만 압니다.
풀이. () 정리합니다.
| 무엇을 묻나 | 무엇에 대한 답 | 자료로 되나 |
|---|---|---|
| 이 모형이 무엇을 보나 | 모형을 설명 | 가능함 |
| 이 예측이 왜 나왔나 | 한 예측을 설명 | 가능함 |
| 이 변수를 바꾸면 어떻게 되나 | 개입의 결과 | 자료만으로는 안 됨 |
| 무엇이 원인인가 | 인과 | 설계가 필요함 |
앞 두 줄만 이 강의가 답합니다. 아래 두 줄은 변수 중요도로 답할 수 없습니다.
() 참 원인이 숨은 이고 과 는 의 그림자인 자료로 확인합니다.
| 변수 | 계수 | 와의 상관 | 참 인과 효과 |
|---|---|---|---|
과 는 참 인과 효과가 인데 계수가 과 입니다.
을 실제로 바꿔도 는 안 바뀝니다. 가 안 바뀌기 때문입니다.
모형은 무엇이 를 예측하는지 말하지 무엇이 를 만드는지 말하지 않습니다.
() 겹친 두 변수의 계수가 얼마나 흔들리는지 봅니다. 과 의 상관은 입니다. 자료를 번 다시 뽑습니다.
| 무엇 | 평균 | 표준편차 |
|---|---|---|
| 의 계수 | ||
| 의 계수 | ||
| 두 계수의 합 |
각각의 표준편차는 근처인데 합의 표준편차는 로 절반 이하입니다.
두 계수의 상관이 입니다. 하나가 커지면 다른 하나가 정확히 그만큼 작아집니다.
겹친 변수는 서로의 자리를 뺏습니다. 그래서 하나만 보면 안 됩니다.
이 문제에서 배우는 것. 중요도가 큰 변수는 예측에 쓰이는 변수이지 원인이 아닙니다. 그리고 겹친 변수들 사이에서는 개별 값보다 합이 훨씬 안정합니다.
확인 1-1. 모형이 답할 수 없는 물음 둘을 쓰세요.
답. 이 변수를 바꾸면 어떻게 되는지와 무엇이 원인인지입니다.
확인 1-2. 검산에서 의 계수와 참 인과 효과를 쓰세요.
답. 과 입니다.
확인 1-3. 검산에서 두 계수 각각의 표준편차와 합의 표준편차를 쓰세요.
답. 와 그리고 입니다.
문제. 모형 전체를 설명합니다.
() 방법들을 정리하세요.
() 나무의 이득 중요도가 편향되는지 보세요.
() 치환 중요도를 재세요.
() 겹친 변수에서 어떻게 되는지 보세요.
생각의 실마리. 나무는 자를 자리를 찾습니다. 그러면 자를 자리가 많은 변수가 우연히라도 좋은 자리를 찾을 확률이 높습니다.
풀이. () 정리합니다.
| 방법 | 어디에 쓰나 | 무엇이 문제인가 |
|---|---|---|
| 표준화 계수 | 선형모형만 | 겹치면 흔들림 |
| 나무의 이득 합 | 학습 자료에서 | 칸 많은 변수에 유리 |
| 치환 중요도 | 아무 모형에나 | 겹치면 둘 다 작아짐 |
| 빼고 다시 학습 | 가장 정직함 | 변수 수만큼 다시 학습 |
| 샤플리 값 | 공평한 배분 | 계산이 비쌈 |
() 참 관계가 하나뿐이고 나머지 셋은 잡음인 자료로 봅니다.
| 변수 | 서로 다른 값 수 | 이득 중요도 |
|---|---|---|
| 이어진 값 잡음 | ||
| 참 변수 두 칸 | ||
| 네 칸 잡음 | ||
| 이어진 값 잡음 |
참 변수 가 로 가장 큽니다. 여기까지는 맞습니다.
그런데 잡음인 과 가 합쳐서 를 가져갑니다. 같은 잡음인 는 뿐입니다.
차이는 서로 다른 값의 개수뿐입니다. 은 개이고 는 개입니다. 값이 이어진 변수는 자를 자리가 많아 우연히 이득을 냅니다.
() 치환 중요도를 잽니다. 변수 하나씩 값을 섞어 오차가 얼마나 오르는지 봅니다.
| 변수 | 학습에서 오른 폭 | 따로 뗀 데서 오른 폭 |
|---|---|---|
| 이어진 값 잡음 | ||
| 참 변수 두 칸 | ||
| 네 칸 잡음 | ||
| 이어진 값 잡음 |
참 변수 만 양쪽에서 크게 오릅니다.
잡음 변수는 학습에서만 오릅니다. 가 학습에서 인데 따로 뗀 데서 입니다. 외운 것을 흐트러뜨렸기 때문이지 정보가 있어서가 아닙니다.
치환 중요도는 반드시 학습에 안 쓴 자료에서 재야 합니다.
() 겹친 변수에서 어떻게 되는지 봅니다. 과 의 상관이 입니다.
| 무엇을 섞었나 | 오른 폭 |
|---|---|
| 만 | |
| 만 | |
| 과 함께 | |
| 만 |
하나씩 섞은 두 값을 더하면 인데 함께 섞으면 입니다.
한쪽을 섞어도 다른 쪽이 그 정보를 대신 들고 있기 때문입니다.
겹친 변수는 묶어서 봐야 합니다. 하나씩 더한 값은 실제 몫보다 작습니다.
이 문제에서 배우는 것. 전역 중요도는 어디서 재느냐와 무엇과 겹치느냐에 크게 달립니다. 나무의 이득은 값이 많은 변수 쪽으로 기울고, 치환 중요도는 학습에서 재면 외운 것을 중요하다고 말합니다.
확인 2-1. 나무의 이득 중요도가 어느 변수에 유리한지 쓰세요.
답. 서로 다른 값이 많은 변수입니다.
확인 2-2. 검산에서 의 학습과 따로 뗀 데서의 오른 폭을 쓰세요.
답. 과 입니다.
확인 2-3. 검산에서 만·만·함께 섞었을 때의 오른 폭을 쓰세요.
답. 와 그리고 입니다.
문제. 한 변수의 모양을 그립니다.
() 무엇을 그리는지 정리하세요.
() 부분 의존이 무엇을 감추는지 보세요.
() 개별 조건부 곡선의 기울기를 보세요.
() 없는 조합을 만들어 내는 것을 확인하세요.
생각의 실마리. 부분 의존은 다른 변수를 평균 내고 한 변수만 움직입니다. 그런데 평균은 서로 반대인 것을 지웁니다.
풀이. () 정리합니다.
| 무엇 | 어떻게 | 무엇을 보나 |
|---|---|---|
| 부분 의존 | 다른 변수를 평균 내고 하나만 움직임 | 평균 모양 |
| 개별 조건부 | 관측마다 한 줄씩 | 흩어짐이 보임 |
| 누적 국소 효과 | 가까운 점들만 씀 | 겹쳐도 안 망가짐 |
() 가 이면 기울기가 이고 이면 인 자료로 봅니다.
| 값 | 부분 의존 평균 | 가 인 줄 | 가 인 줄 |
|---|---|---|---|
부분 의존 곡선은 에서 사이로 거의 평평합니다.
그런데 갈래를 나눠 보면 한쪽은 에서 로 내려가고 다른 쪽은 에서 로 올라갑니다.
평평한 부분 의존 곡선은 효과가 없다는 뜻이 아닙니다. 두 방향이 서로를 지웠을 수도 있습니다.
() 관측마다 곡선 하나를 그려 기울기를 잽니다.
| 무엇 | 평균 기울기 | 표준편차 |
|---|---|---|
| 모든 관측 | ||
| 가 인 관측 | ||
| 가 인 관측 |
전체 평균 기울기는 로 에 가까운데 표준편차가 입니다.
이 큰 흩어짐이 상호작용의 신호입니다. 평균이 이고 흩어짐이 크면 반드시 갈라서 봐야 합니다.
갈래 안에서는 표준편차가 정확히 입니다. 변수가 과 둘뿐이라 가 같으면 곡선이 완전히 같기 때문입니다.
() 없는 조합을 만들어 내는 것을 확인합니다. 과 의 상관은 입니다.
| 값 | 부분 의존 | 도 도 그 자리인 관측 수 |
|---|---|---|
이 인데 가 평균인 점은 학습 자료에 하나도 없습니다.
그런데 부분 의존은 그 자리의 값을 으로 태연히 그립니다. 모형이 본 적 없는 곳에서의 추측입니다.
강 문제 에서 본 바깥 추정이 여기서 다시 나옵니다.
이 문제에서 배우는 것. 부분 의존 곡선은 평균이 감추는 것과 없는 조합에서의 추측 두 가지 위험을 함께 안고 있습니다. 그래서 개별 곡선을 함께 그리고, 곡선 아래에 실제 관측이 있는지 봐야 합니다.
확인 3-1. 평평한 부분 의존 곡선이 뜻하는 것을 쓰세요.
답. 효과가 없거나 두 방향이 서로를 지웠거나 둘 중 하나입니다.
확인 3-2. 검산에서 모든 관측의 평균 기울기와 표준편차를 쓰세요.
답. 와 입니다.
확인 3-3. 검산에서 이 일 때의 부분 의존과 그 자리 관측 수를 쓰세요.
답. 과 입니다.
문제. 예측 하나를 설명합니다.
() 방법들을 정리하세요.
() 샤플리 값을 계산하세요.
() 같은 변수가 점마다 다른 기여를 내는 것을 보세요.
() 국소 대리 모형과 견주세요.
생각의 실마리. 예측 하나를 변수들에게 나눠 주려면 나눗셈 규칙이 필요합니다. 협조 게임 이론에서 모든 참여 순서를 평균 내는 것이 유일한 공평한 배분입니다.
풀이. () 정리합니다.
| 방법 | 무엇을 하나 | 무엇이 문제인가 |
|---|---|---|
| 선형 모형의 기여 | 계수 곱하기 값 | 선형이면 정확함 |
| 국소 대리 모형 | 근처에서 직선으로 흉내 | 근처를 어떻게 정하나 |
| 샤플리 값 | 모든 순서의 평균 기여 | 계산이 의 승 |
| 반사실 설명 | 무엇을 바꾸면 뒤집히나 | 실행 가능성 |
() 참 관계가 인 자료에서 계산합니다. 학습 자료 평균 예측은 입니다.
| 점 | 예측 | 기여 | 기여 | 기여 | 합과 예측의 차 |
|---|---|---|---|---|---|
기여의 합에 평균을 더하면 정확히 예측이 됩니다. 마지막 열이 모두 입니다.
이것이 샤플리 값의 유일한 보장입니다. "이 변수가 원인이다"도 아니고 "이만큼 바꾸면 된다"도 아닙니다. 예측을 남김없이 나눈다는 것뿐입니다.
() 같은 변수가 점마다 다른 기여를 내는 것을 봅니다.
| 점 | 값 | 값 | 기여 | 기여 나누기 값 |
|---|---|---|---|---|
마지막 열이 에서 까지 다릅니다. 선형모형이면 모두 같은 값이어야 합니다.
그리고 가 큰 순서와 정확히 같은 순서로 커집니다. 가 일 때 이고 일 때 입니다.
가 클수록 한 단위의 값이 커지기 때문입니다. 전역 중요도 하나로는 이것을 못 봅니다.
() 국소 대리 모형과 견줍니다. 한 점 근처에서만 직선으로 흉내 냅니다.
| 점 | 근처 폭 | 직선 기울기 | 직선 기울기 | 흉내 낸 결정계수 |
|---|---|---|---|---|
같은 점인데 근처 폭에 따라 기울기가 에서 까지 갈립니다.
근처 폭이 이면 결정계수가 으로 떨어집니다. 넓게 잡으면 직선으로 흉내가 안 나기 때문입니다.
좁히면 잘 맞지만 쓸 점이 적어 기울기가 흔들립니다. 에서 결정계수가 다시 으로 내려간 것이 그것입니다.
근처를 어떻게 정하느냐가 설명을 정합니다. 답이 하나가 아닙니다.
이 문제에서 배우는 것. 한 예측의 설명은 전역 중요도가 못 보는 것을 봅니다. 다만 샤플리 값이 보장하는 것은 합이 예측이 된다는 것 하나뿐이고, 국소 대리 모형은 근처를 정하는 손잡이에 답이 딸립니다.
확인 4-1. 샤플리 값이 보장하는 것 하나를 쓰세요.
답. 기여의 합에 평균을 더하면 예측이 된다는 것입니다.
확인 4-2. 검산에서 점 과 점 의 기여 나누기 값을 쓰세요.
답. 와 입니다.
확인 4-3. 검산에서 근처 폭 과 일 때의 기울기를 쓰세요.
답. 와 입니다.
문제. 실제로 씁니다.
() 무엇을 믿을지 정리하세요.
() 설명이 얼마나 흔들리는지 재세요.
() 감사에 쓰일 때 확인할 것을 정리하세요.
() 단원을 한자리에 모으세요.
생각의 실마리. 중요도 표를 받은 사람은 대개 그것을 개입 지침으로 읽습니다. "이 변수가 등이니 여기에 투자하자"입니다.
풀이. () 정리합니다.
| 무엇 | 얼마나 믿나 | 조건 |
|---|---|---|
| 따로 뗀 자료의 치환 중요도 | 믿을 만함 | 학습에서 재지 않기 |
| 나무의 이득 중요도 | 참고만 | 칸 많은 변수에 쏠림 |
| 부분 의존 | 갈라서 함께 | 평균이 감춤 |
| 샤플리 값 | 한 점 설명에 | 합이 예측이 됨 |
| 어떤 것도 인과 아님 | 언제나 | 개입을 말할 수 없음 |
() 자료를 번 다시 뽑아 매번 숲을 새로 학습하고 중요도를 잽니다.
| 변수 | 중요도 평균 | 표준편차 | 가장 낮았던 값 | 등이었던 비율 |
|---|---|---|---|---|
순위는 번 모두 같았습니다. 이 언제나 등입니다.
그런데 의 값은 에서 사이를 오갑니다. 배 넘게 차이 납니다.
순위는 믿을 만하고 값의 크기는 그만큼 못 믿습니다. 중요도를 보고할 때는 값 하나가 아니라 흔들리는 폭도 함께 적습니다.
() 감사에 쓰일 때 확인할 것을 정리합니다.
| 무엇을 묻나 | 왜 |
|---|---|
| 어떤 자료에서 쟀나 | 따로 뗀 자료여야 합니다 |
| 겹친 변수를 묶었나 | 하나씩 보면 둘 다 작아집니다 |
| 평균이 감추고 있나 | 개별 곡선의 흩어짐을 봅니다 |
| 본 적 있는 자리인가 | 부분 의존은 없는 조합도 그립니다 |
| 얼마나 흔들리나 | 자료를 다시 뽑아 재봅니다 |
| 인과로 읽히고 있나 | 개입의 결과가 아닙니다 |
마지막 줄이 가장 자주 어겨집니다.
() 단원을 한자리에 모읍니다.
| 강 | 무엇을 물었나 | 한 줄 답 |
|---|---|---|
| 무엇으로 재나 | ROC와 PR과 눈금맞춤 | |
| 치우침을 어떻게 다루나 | 문턱이 먼저 | |
| 무엇을 넣나 | 맞는 변수 하나 | |
| 손잡이를 어떻게 고르나 | 로그 눈금에서 넓게 | |
| 무엇을 보고 판단했나 | 설명은 인과가 아님 |
다섯 강이 하나의 물음을 나눠 답합니다. 만든 모형을 어떻게 믿을 것인가입니다.
이 문제에서 배우는 것. 설명은 모형에 대한 사실이지 세상에 대한 사실이 아닙니다. 그래서 보고할 때는 어디서 쟀는지·얼마나 흔들리는지·무엇과 겹쳤는지를 함께 적어야 하고, 개입 지침으로 읽히지 않게 못 박아야 합니다.
확인 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. 이 강의를 한 문장으로 정리하세요.
| 무엇을 말하나 | 무엇을 안 말하나 |
|---|---|
| 모형이 무엇을 쓰나 | 무엇이 원인인가 |
| 예측이 어떻게 나뉘나 | 무엇을 바꿔야 하나 |
| 평균 모양이 어떤가 | 개별 관측이 어떤가 |
| 값이 얼마나 흔들리나 | 값이 참인가 |
오른쪽 열에 답하려면 자료가 아니라 설계가 필요합니다.
정답.
| 기호 | 읽는 법 | 뜻 |
|---|---|---|
| 전역 중요도 | global importance | 모형 전체에서 변수가 얼마나 쓰이는지입니다 |
| 이득 중요도 | gain importance | 나무가 자를 때 줄인 오차의 합입니다 |
| 치환 중요도 | permutation importance | 변수를 섞었을 때 오차가 오르는 폭입니다 |
| 부분 의존 | partial dependence | 다른 변수를 평균 내고 하나만 움직인 곡선입니다 |
| 개별 조건부 기대 | individual conditional expectation | 관측마다 그린 곡선입니다 |
| 누적 국소 효과 | accumulated local effects | 구간 안의 관측만 써서 누적한 곡선입니다 |
| 샤플리 값 | Shapley value | 모든 참여 순서를 평균 낸 공평한 배분입니다 |
| 국소 대리 모형 | local surrogate | 한 점 근처에서 간단한 모형으로 흉내 냅니다 |
| 반사실 설명 | counterfactual explanation | 무엇을 바꾸면 판정이 뒤집히는지입니다 |
| 그림자 변수 | proxy variable | 숨은 원인과 상관이 높은 변수입니다 |
여기까지가 단원이고 장 머신러닝의 지도학습·비지도학습·평가가 끝났습니다. 단원부터는 시간이 흐르는 자료를 다룹니다. 지금까지는 관측들이 서로 독립이라고 봤는데, 어제가 오늘을 만드는 자료에서는 그 가정부터 무너집니다.
import itertools
import numpy as np
def fact(k):
v = 1.0
for i in range(2, k + 1):
v *= i
return v
def pw(s):
return sum(2 if ord(c) > 0x1100 else 1 for c in str(s))
def rw(s, w):
return str(s) + ' ' * max(0, w - pw(s))
def rl(s, w):
return ' ' * max(0, w - pw(s)) + str(s)
def ridge(X, y, lam=1e-4):
A = np.hstack([X, np.ones((len(X), 1))])
G = A.T @ A + lam * np.eye(A.shape[1])
G[-1, -1] -= lam
return np.linalg.solve(G, A.T @ y)
def pred(X, b):
return np.hstack([X, np.ones((len(X), 1))]) @ b
def mse(a, b):
return float(np.mean((a - b) ** 2))
def r2(yt, yp):
return float(1 - ((yt - yp) ** 2).sum() / ((yt - yt.mean()) ** 2).sum())
def tree_fit(X, y, depth, mn=8, cols=None):
if depth == 0 or len(y) < mn:
return ('L', float(y.mean()), len(y))
js = range(X.shape[1]) if cols is None else cols
best = None
for j in js:
qs = np.quantile(X[:, j], np.linspace(0.1, 0.9, 9))
for t in np.unique(qs):
m = X[:, j] <= t
if m.sum() < mn or (~m).sum() < mn:
continue
s = ((y[m] - y[m].mean()) ** 2).sum() + ((y[~m] - y[~m].mean()) ** 2).sum()
if best is None or s < best[0]:
best = (s, j, t, m)
if best is None:
return ('L', float(y.mean()), len(y))
s, j, t, m = best
tot = ((y - y.mean()) ** 2).sum()
return ('N', j, t, tot - s, len(y),
tree_fit(X[m], y[m], depth - 1, mn, cols),
tree_fit(X[~m], y[~m], depth - 1, mn, cols))
def tree_pred(T, X):
if T[0] == 'L':
return np.full(len(X), T[1])
m = X[:, T[1]] <= T[2]
o = np.empty(len(X))
if m.sum():
o[m] = tree_pred(T[5], X[m])
if (~m).sum():
o[~m] = tree_pred(T[6], X[~m])
return o
def tree_gain(T, p, out):
if T[0] == 'L':
return
out[T[1]] += T[3]
tree_gain(T[5], p, out)
tree_gain(T[6], p, out)
def forest_fit(X, y, ntree, depth, mtry, rr):
ts = []
for _ in range(ntree):
idx = rr.integers(0, len(y), len(y))
cols = rr.permutation(X.shape[1])[:mtry]
ts.append(tree_fit(X[idx], y[idx], depth, 8, list(cols)))
return ts
def forest_pred(ts, X):
return np.mean([tree_pred(T, X) for T in ts], axis=0)
print("=" * 78)
print("230강 모형 해석과 변수 중요도 코드 검산")
print("=" * 78)
print()
print("문제 1. 무엇을 설명하려는 것인가")
print()
print(" (1) 서로 다른 물음을 가릅니다")
rows = [
("이 모형이 무엇을 보나", "모형을 설명", "가능함"),
("이 예측이 왜 나왔나", "한 예측을 설명", "가능함"),
("이 변수를 바꾸면 어떻게 되나", "개입의 결과", "자료만으로는 안 됨"),
("무엇이 원인인가", "인과", "설계가 필요함"),
]
w = [max(pw(r[i]) for r in rows + [("무엇을 묻나", "무엇에 대한 답", "자료로 되나")]) for i in range(3)]
print(" " + rw("무엇을 묻나", w[0]) + " " + rw("무엇에 대한 답", w[1]) + " " + "자료로 되나")
for r in rows:
print(" " + rw(r[0], w[0]) + " " + rw(r[1], w[1]) + " " + r[2])
print(" 앞 두 줄만 이 강의가 답합니다")
print(" 아래 두 줄은 변수 중요도로 답할 수 없습니다")
print()
print(" (2) 중요도가 인과가 아님을 봅니다")
r = np.random.default_rng(20230)
n = 1500
z = r.normal(0, 1, n)
x1 = z + r.normal(0, 0.3, n)
x2 = z + r.normal(0, 0.3, n)
x3 = r.normal(0, 1, n)
y = 2.0 * z + 0.5 * x3 + r.normal(0, 0.5, n)
X = np.column_stack([x1, x2, x3])
b = ridge(X, y)
print(" 참 원인은 숨은 z 이고 x1 과 x2 는 z 의 그림자입니다")
print(" " + rw("변수", 10) + " " + rl("계수", 12) + " " + rl("y 와의 상관", 14) + " " + rl("참 인과 효과", 14))
truth = [0.0, 0.0, 0.5]
for j, nm in enumerate(["x1", "x2", "x3"]):
print(" " + rw(nm, 10) + " " + rl("%.6f" % b[j], 12) + " " + rl("%.6f" % float(np.corrcoef(X[:, j], y)[0, 1]), 14) + " " + rl("%.6f" % truth[j], 14))
print(" x1 과 x2 는 참 인과 효과가 0 인데 계수와 상관이 모두 큽니다")
print(" x1 을 실제로 바꿔도 y 는 안 바뀝니다. z 가 안 바뀌기 때문입니다")
print(" 모형은 무엇이 y 를 예측하는지 말하지 무엇이 y 를 만드는지 말하지 않습니다")
print()
print(" (3) 겹친 변수에서 중요도가 어떻게 갈리는지 봅니다")
print(" x1 과 x2 의 상관은 %.6f 입니다" % float(np.corrcoef(x1, x2)[0, 1]))
res = []
for rep in range(200):
rr = np.random.default_rng(700 + rep)
idx = rr.integers(0, n, n)
bb = ridge(X[idx], y[idx])
res.append((bb[0], bb[1]))
res = np.array(res)
print(" 자료를 200 번 다시 뽑아 계수가 얼마나 흔들리는지 봅니다")
print(" " + rw("무엇", 22) + " " + rl("평균", 12) + " " + rl("표준편차", 12))
print(" " + rw("x1 의 계수", 22) + " " + rl("%.6f" % res[:, 0].mean(), 12) + " " + rl("%.6f" % res[:, 0].std(), 12))
print(" " + rw("x2 의 계수", 22) + " " + rl("%.6f" % res[:, 1].mean(), 12) + " " + rl("%.6f" % res[:, 1].std(), 12))
print(" " + rw("두 계수의 합", 22) + " " + rl("%.6f" % (res[:, 0] + res[:, 1]).mean(), 12) + " " + rl("%.6f" % (res[:, 0] + res[:, 1]).std(), 12))
print(" 두 계수의 상관은 %.6f 입니다" % float(np.corrcoef(res[:, 0], res[:, 1])[0, 1]))
print(" 각각은 크게 흔들리는데 합은 훨씬 안정합니다")
print(" 겹친 변수는 서로의 자리를 뺏습니다. 하나만 보면 안 됩니다")
print()
print("문제 2. 전역 중요도")
print()
print(" (1) 방법들을 정리합니다")
rows = [
("표준화 계수", "선형모형만", "겹치면 흔들림"),
("나무의 이득 합", "학습 자료에서", "칸 많은 변수에 유리"),
("치환 중요도", "아무 모형에나", "겹치면 둘 다 작아짐"),
("빼고 다시 학습", "가장 정직함", "변수 수만큼 다시 학습"),
("샤플리 값", "공평한 배분", "계산이 비쌈"),
]
w = [max(pw(r[i]) for r in rows + [("방법", "어디에 쓰나", "무엇이 문제인가")]) for i in range(3)]
print(" " + rw("방법", w[0]) + " " + rw("어디에 쓰나", w[1]) + " " + "무엇이 문제인가")
for r in rows:
print(" " + rw(r[0], w[0]) + " " + rw(r[1], w[1]) + " " + r[2])
print()
print(" (2) 나무의 이득 중요도가 칸 많은 변수에 쏠리는지 봅니다")
r2g = np.random.default_rng(30230)
m = 900
g1 = r2g.normal(0, 1, m)
g2 = r2g.integers(0, 2, m).astype(float)
g3 = r2g.integers(0, 4, m).astype(float)
g4 = r2g.normal(0, 1, m)
yg = 1.5 * g2 + r2g.normal(0, 1.0, m)
Xg = np.column_stack([g1, g2, g3, g4])
T = tree_fit(Xg, yg, 6)
gain = np.zeros(4)
tree_gain(T, 4, gain)
gain = gain / gain.sum()
names = ["g1 이어진 값 잡음", "g2 참 변수 두 칸", "g3 네 칸 잡음", "g4 이어진 값 잡음"]
print(" 참 관계는 g2 하나뿐이고 나머지 셋은 잡음입니다")
print(" " + rw("변수", 20) + " " + rl("서로 다른 값 수", 16) + " " + rl("이득 중요도", 14))
uq = [len(np.unique(Xg[:, j])) for j in range(4)]
for j in range(4):
print(" " + rw(names[j], 20) + " " + rl("%d" % uq[j], 16) + " " + rl("%.6f" % gain[j], 14))
print(" 참 변수 g2 가 %.6f 로 가장 큽니다" % gain[1])
print(" 그런데 잡음인 g1 과 g4 가 합쳐서 %.6f 를 가져갑니다" % (gain[0] + gain[3]))
print(" 값이 이어진 변수는 자를 자리가 많아 우연히 이득을 냅니다")
print(" 두 칸짜리 g3 대비 g1 은 서로 다른 값이 %d 배입니다" % (uq[0] // uq[2]))
print()
print(" (3) 치환 중요도를 잽니다")
r3 = np.random.default_rng(40230)
tr = np.arange(600)
te = np.arange(600, m)
ts = forest_fit(Xg[tr], yg[tr], 30, 5, 3, np.random.default_rng(5))
base_tr = mse(forest_pred(ts, Xg[tr]), yg[tr])
base_te = mse(forest_pred(ts, Xg[te]), yg[te])
print(" 숲을 학습한 뒤 변수 하나씩 값을 섞어 오차가 얼마나 오르는지 봅니다")
print(" " + rw("변수", 20) + " " + rl("학습에서 오른 폭", 18) + " " + rl("따로 뗀 데서 오른 폭", 22))
for j in range(4):
dtr, dte = [], []
for rep in range(20):
Za = Xg[tr].copy()
Za[:, j] = Za[r3.permutation(len(tr)), j]
dtr.append(mse(forest_pred(ts, Za), yg[tr]) - base_tr)
Zb = Xg[te].copy()
Zb[:, j] = Zb[r3.permutation(len(te)), j]
dte.append(mse(forest_pred(ts, Zb), yg[te]) - base_te)
print(" " + rw(names[j], 20) + " " + rl("%.6f" % float(np.mean(dtr)), 18) + " " + rl("%.6f" % float(np.mean(dte)), 22))
print(" 참 변수 g2 만 크게 오릅니다")
print(" 학습에서 잰 값과 따로 뗀 데서 잰 값이 다릅니다")
print(" 잡음 변수는 학습에서만 오릅니다. 외운 것을 흐트러뜨렸기 때문입니다")
print(" 치환 중요도는 반드시 학습에 안 쓴 자료에서 재야 합니다")
print()
print(" (4) 겹친 변수에서 치환 중요도가 어떻게 되는지 봅니다")
Xd = np.column_stack([x1, x2, x3])
tr2 = np.arange(1000)
te2 = np.arange(1000, n)
ts2 = forest_fit(Xd[tr2], y[tr2], 30, 5, 3, np.random.default_rng(6))
b2 = mse(forest_pred(ts2, Xd[te2]), y[te2])
print(" x1 과 x2 는 상관이 %.6f 로 거의 같은 정보를 담습니다" % float(np.corrcoef(x1, x2)[0, 1]))
print(" " + rw("무엇을 섞었나", 20) + " " + rl("오른 폭", 14))
mix = {}
for nm, js in [("x1 만", [0]), ("x2 만", [1]), ("x1 과 x2 함께", [0, 1]), ("x3 만", [2])]:
d = []
for rep in range(20):
Z = Xd[te2].copy()
for j in js:
Z[:, j] = Z[r3.permutation(len(te2)), j]
d.append(mse(forest_pred(ts2, Z), y[te2]) - b2)
mix[nm] = float(np.mean(d))
print(" " + rw(nm, 20) + " " + rl("%.6f" % mix[nm], 14))
print(" 하나씩 섞은 두 값을 더하면 %.6f 인데 함께 섞으면 %.6f 입니다" % (mix["x1 만"] + mix["x2 만"], mix["x1 과 x2 함께"]))
print(" 한쪽을 섞어도 다른 쪽이 그 정보를 대신 들고 있기 때문입니다")
print(" 겹친 변수는 묶어서 봐야 합니다. 하나씩 더한 값은 실제 몫보다 작습니다")
print()
print("문제 3. 부분 의존과 개별 조건부")
print()
print(" (1) 무엇을 그리는지 정리합니다")
rows = [
("부분 의존", "다른 변수를 평균 내고 하나만 움직임", "평균 모양"),
("개별 조건부", "관측마다 한 줄씩", "흩어짐이 보임"),
("누적 국소 효과", "가까운 점들만 씀", "겹쳐도 안 망가짐"),
]
w = [max(pw(r[i]) for r in rows + [("무엇", "어떻게", "무엇을 보나")]) for i in range(3)]
print(" " + rw("무엇", w[0]) + " " + rw("어떻게", w[1]) + " " + "무엇을 보나")
for r in rows:
print(" " + rw(r[0], w[0]) + " " + rw(r[1], w[1]) + " " + r[2])
print()
print(" (2) 부분 의존이 평균을 내며 무엇을 감추는지 봅니다")
r4 = np.random.default_rng(50230)
n2 = 1200
u1 = r4.uniform(-2, 2, n2)
u2 = r4.integers(0, 2, n2).astype(float)
yu = np.where(u2 > 0.5, 2.0 * u1, -2.0 * u1) + r4.normal(0, 0.4, n2)
Xu = np.column_stack([u1, u2])
tsu = forest_fit(Xu[:900], yu[:900], 40, 6, 2, np.random.default_rng(7))
grid_u = np.linspace(-2, 2, 5)
print(" u2 가 0 이면 기울기가 -2 이고 1 이면 +2 인 자료입니다")
print(" " + rl("u1 값", 10) + " " + rl("부분 의존 평균", 16) + " " + rl("u2 가 0 인 줄", 16) + " " + rl("u2 가 1 인 줄", 16))
for g in grid_u:
Z = Xu[:900].copy()
Z[:, 0] = g
pdv = float(forest_pred(tsu, Z).mean())
Z0 = Z.copy(); Z0[:, 1] = 0.0
Z1 = Z.copy(); Z1[:, 1] = 1.0
print(" " + rl("%.4f" % g, 10) + " " + rl("%.6f" % pdv, 16) + " " + rl("%.6f" % float(forest_pred(tsu, Z0).mean()), 16) + " " + rl("%.6f" % float(forest_pred(tsu, Z1).mean()), 16))
print(" 부분 의존 곡선은 거의 평평합니다. 두 방향이 서로를 지웠기 때문입니다")
print(" 갈래를 나눠 보면 한쪽은 내려가고 한쪽은 올라갑니다")
print(" 평평한 부분 의존 곡선은 효과가 없다는 뜻이 아닙니다")
print()
print(" (3) 개별 조건부 곡선의 기울기를 봅니다")
Z = Xu[:900].copy()
sl = []
for i in range(900):
zi = np.tile(Xu[i], (5, 1))
zi[:, 0] = grid_u
v = forest_pred(tsu, zi)
sl.append(float((v[-1] - v[0]) / (grid_u[-1] - grid_u[0])))
sl = np.array(sl)
mask0 = Xu[:900, 1] < 0.5
print(" 관측마다 곡선 하나를 그려 기울기를 잽니다")
print(" " + rw("무엇", 20) + " " + rl("평균 기울기", 14) + " " + rl("표준편차", 12))
print(" " + rw("모든 관측", 20) + " " + rl("%.6f" % sl.mean(), 14) + " " + rl("%.6f" % sl.std(), 12))
print(" " + rw("u2 가 0 인 관측", 20) + " " + rl("%.6f" % sl[mask0].mean(), 14) + " " + rl("%.6f" % sl[mask0].std(), 12))
print(" " + rw("u2 가 1 인 관측", 20) + " " + rl("%.6f" % sl[~mask0].mean(), 14) + " " + rl("%.6f" % sl[~mask0].std(), 12))
print(" 전체 평균 기울기는 %.6f 로 0 에 가깝습니다" % sl.mean())
print(" 그런데 표준편차가 %.6f 입니다. 이 큰 흩어짐이 상호작용의 신호입니다" % sl.std())
print(" 갈래 안에서는 표준편차가 정확히 0 입니다")
print(" 변수가 u1 과 u2 둘뿐이라 u2 가 같으면 곡선이 완전히 같기 때문입니다")
print(" 평균이 0 이고 흩어짐이 크면 반드시 갈라서 봐야 합니다")
print()
print(" (4) 부분 의존이 없는 조합을 만들어 내는 것을 봅니다")
r5 = np.random.default_rng(60230)
n3 = 1000
v1 = r5.normal(0, 1, n3)
v2 = v1 * 0.95 + r5.normal(0, 0.31, n3)
yv = 1.0 * v1 + 1.0 * v2 + r5.normal(0, 0.4, n3)
Xv = np.column_stack([v1, v2])
tsv = forest_fit(Xv[:700], yv[:700], 40, 6, 2, np.random.default_rng(8))
print(" v1 과 v2 의 상관은 %.6f 입니다" % float(np.corrcoef(v1, v2)[0, 1]))
print(" 부분 의존은 v1 을 크게 두고 v2 를 그대로 두는 조합도 계산합니다")
print(" " + rl("v1 값", 10) + " " + rl("부분 의존", 14) + " " + rl("v1 도 v2 도 그 자리인 관측 수", 28))
for g in [-2.5, -1.0, 0.0, 1.0, 2.5]:
Z2 = Xv[:700].copy()
Z2[:, 0] = g
mu2 = float(Xv[:700, 1].mean())
cnt = int(((np.abs(Xv[:700, 0] - g) < 0.5) & (np.abs(Xv[:700, 1] - mu2) < 0.5)).sum())
print(" " + rl("%.4f" % g, 10) + " " + rl("%.6f" % float(forest_pred(tsv, Z2).mean()), 14) + " " + rl("%d" % cnt, 28))
print(" v1 이 크게 치우쳤는데 v2 는 평균인 점이 실제로는 거의 없습니다")
print(" 그런 자리에서의 예측은 모형이 본 적 없는 곳에서의 추측입니다")
print(" 213강 문제 3 에서 본 바깥 추정이 여기서 다시 나옵니다")
print()
print("문제 4. 한 예측을 설명하기")
print()
print(" (1) 방법들을 정리합니다")
rows = [
("선형 모형의 기여", "계수 곱하기 값", "선형이면 정확함"),
("국소 대리 모형", "근처에서 직선으로 흉내", "근처를 어떻게 정하나"),
("샤플리 값", "모든 순서의 평균 기여", "계산이 2 의 p 승"),
("반사실 설명", "무엇을 바꾸면 뒤집히나", "실행 가능성"),
]
w = [max(pw(r[i]) for r in rows + [("방법", "무엇을 하나", "무엇이 문제인가")]) for i in range(3)]
print(" " + rw("방법", w[0]) + " " + rw("무엇을 하나", w[1]) + " " + "무엇이 문제인가")
for r in rows:
print(" " + rw(r[0], w[0]) + " " + rw(r[1], w[1]) + " " + r[2])
print()
print(" (2) 샤플리 값을 직접 계산합니다")
r6 = np.random.default_rng(70230)
n4 = 1200
w1 = r6.normal(0, 1, n4)
w2 = r6.normal(0, 1, n4)
w3 = r6.normal(0, 1, n4)
yw = 1.5 * w1 - 1.0 * w2 + 0.8 * w1 * w2 + 0.3 * w3 + r6.normal(0, 0.3, n4)
Xw = np.column_stack([w1, w2, w3])
trw = np.arange(900)
tew = np.arange(900, n4)
tsw = forest_fit(Xw[trw], yw[trw], 40, 6, 3, np.random.default_rng(9))
base = float(forest_pred(tsw, Xw[trw]).mean())
def val(S, xi):
Z = Xw[trw].copy()
for j in S:
Z[:, j] = xi[j]
return float(forest_pred(tsw, Z).mean())
def shapley(xi):
p = 3
phi = np.zeros(p)
for j in range(p):
rest = [k for k in range(p) if k != j]
for size in range(p):
for S in itertools.combinations(rest, size):
wgt = fact(size) * fact(p - size - 1) / fact(p)
phi[j] += wgt * (val(list(S) + [j], xi) - val(list(S), xi))
return phi
print(" 학습 자료 평균 예측은 %.6f 입니다" % base)
print(" 따로 뗀 자료에서 세 점을 골라 기여를 나눕니다")
print(" " + rl("점", 6) + " " + rl("예측", 12) + " " + rl("w1 기여", 12) + " " + rl("w2 기여", 12) + " " + rl("w3 기여", 12) + " " + rl("합과 예측의 차", 16))
for i in [900, 901, 902]:
xi = Xw[i]
phi = shapley(xi)
pr = float(forest_pred(tsw, xi.reshape(1, -1))[0])
print(" " + rl("%d" % (i - 899), 6) + " " + rl("%.6f" % pr, 12) + " " + rl("%.6f" % phi[0], 12) + " " + rl("%.6f" % phi[1], 12) + " " + rl("%.6f" % phi[2], 12) + " " + rl("%.6f" % (pr - base - phi.sum()), 16))
print(" 기여의 합에 평균을 더하면 예측이 됩니다. 마지막 열이 그 확인입니다")
print(" 이것이 샤플리 값의 유일한 보장입니다")
print()
print(" (3) 같은 변수가 점마다 다른 기여를 내는 것을 봅니다")
print(" 참 관계에 w1 곱하기 w2 항이 있습니다")
print(" " + rl("점", 6) + " " + rl("w1 값", 12) + " " + rl("w2 값", 12) + " " + rl("w1 기여", 12) + " " + rl("기여 나누기 값", 16))
for i in [903, 904, 905, 906]:
xi = Xw[i]
phi = shapley(xi)
print(" " + rl("%d" % (i - 902), 6) + " " + rl("%.6f" % xi[0], 12) + " " + rl("%.6f" % xi[1], 12) + " " + rl("%.6f" % phi[0], 12) + " " + rl("%.6f" % (phi[0] / xi[0]), 16))
print(" 마지막 열이 점마다 다릅니다. 선형모형이면 모두 같은 값이어야 합니다")
print(" w2 가 클수록 w1 한 단위의 값이 커지기 때문입니다")
print(" 전역 중요도 하나로는 이것을 못 봅니다")
print()
print(" (4) 국소 대리 모형과 견줍니다")
r7 = np.random.default_rng(80230)
print(" 한 점 근처에서만 직선으로 흉내 내고 기울기를 봅니다")
print(" " + rl("점", 6) + " " + rl("근처 폭", 10) + " " + rl("직선 기울기 w1", 16) + " " + rl("직선 기울기 w2", 16) + " " + rl("흉내 낸 결정계수", 18))
xi = Xw[900]
for hbw in [0.3, 0.8, 2.0]:
d = np.sqrt(((Xw[trw] - xi) ** 2).sum(axis=1))
ww = np.exp(-(d / hbw) ** 2)
sel = ww > 0.05
Xs = Xw[trw][sel]
ys = forest_pred(tsw, Xs)
wt = ww[sel]
A = np.hstack([Xs, np.ones((len(Xs), 1))])
W = A * wt[:, None]
bb = np.linalg.solve(W.T @ A + 1e-6 * np.eye(4), W.T @ ys)
fit = A @ bb
q = 1 - (wt * (ys - fit) ** 2).sum() / (wt * (ys - np.average(ys, weights=wt)) ** 2).sum()
print(" " + rl("1", 6) + " " + rl("%.4f" % hbw, 10) + " " + rl("%.6f" % bb[0], 16) + " " + rl("%.6f" % bb[1], 16) + " " + rl("%.6f" % float(q), 18))
print(" 근처 폭을 넓히면 흉내 낸 결정계수가 떨어집니다")
print(" 좁히면 잘 맞지만 쓸 점이 적어 기울기가 흔들립니다")
print(" 근처를 어떻게 정하느냐가 설명을 정합니다. 답이 하나가 아닙니다")
print()
print("문제 5. 실무에서 쓰기")
print()
print(" (1) 무엇을 믿을지 정리합니다")
rows = [
("따로 뗀 자료의 치환 중요도", "믿을 만함", "학습에서 재지 않기"),
("나무의 이득 중요도", "참고만", "칸 많은 변수에 쏠림"),
("부분 의존", "갈라서 함께", "평균이 감춤"),
("샤플리 값", "한 점 설명에", "합이 예측이 됨"),
("어떤 것도 인과 아님", "언제나", "개입을 말할 수 없음"),
]
w = [max(pw(r[i]) for r in rows + [("무엇", "얼마나 믿나", "조건")]) for i in range(3)]
print(" " + rw("무엇", w[0]) + " " + rw("얼마나 믿나", w[1]) + " " + "조건")
for r in rows:
print(" " + rw(r[0], w[0]) + " " + rw(r[1], w[1]) + " " + r[2])
print()
print(" (2) 설명이 얼마나 흔들리는지 봅니다")
r8 = np.random.default_rng(90230)
imps = []
for rep in range(30):
rr = np.random.default_rng(300 + rep)
idx = rr.integers(0, len(trw), len(trw))
tsr = forest_fit(Xw[trw][idx], yw[trw][idx], 20, 5, 3, rr)
bb = mse(forest_pred(tsr, Xw[tew]), yw[tew])
row = []
for j in range(3):
Z = Xw[tew].copy()
Z[:, j] = Z[rr.permutation(len(tew)), j]
row.append(mse(forest_pred(tsr, Z), yw[tew]) - bb)
imps.append(row)
imps = np.array(imps)
print(" 자료를 30 번 다시 뽑아 매번 숲을 새로 학습하고 중요도를 잽니다")
print(" " + rw("변수", 10) + " " + rl("중요도 평균", 14) + " " + rl("표준편차", 12) + " " + rl("가장 낮았던 값", 16) + " " + rl("1 등이었던 비율", 18))
rank1 = np.argmax(imps, axis=1)
for j, nm in enumerate(["w1", "w2", "w3"]):
print(" " + rw(nm, 10) + " " + rl("%.6f" % imps[:, j].mean(), 14) + " " + rl("%.6f" % imps[:, j].std(), 12) + " " + rl("%.6f" % imps[:, j].min(), 16) + " " + rl("%.6f" % float((rank1 == j).mean()), 18))
print(" 순위는 30 번 모두 같았습니다. w1 이 언제나 1 등입니다")
print(" 그런데 w1 의 값은 %.6f 에서 %.6f 사이를 오갑니다" % (imps[:, 0].min(), imps[:, 0].max()))
print(" 순위는 믿을 만하고 값의 크기는 그만큼 못 믿습니다")
print(" 중요도를 보고할 때는 값 하나가 아니라 흔들리는 폭도 함께 적습니다")
print()
print(" (3) 설명이 감사에 쓰일 때 무엇을 확인할지 봅니다")
rows = [
("어떤 자료에서 쟀나", "따로 뗀 자료여야 합니다"),
("겹친 변수를 묶었나", "하나씩 보면 둘 다 작아집니다"),
("평균이 감추고 있나", "개별 곡선의 흩어짐을 봅니다"),
("본 적 있는 자리인가", "부분 의존은 없는 조합도 그립니다"),
("얼마나 흔들리나", "자료를 다시 뽑아 재봅니다"),
("인과로 읽히고 있나", "개입의 결과가 아닙니다"),
]
w = [max(pw(r[i]) for r in rows + [("무엇을 묻나", "왜")]) for i in range(2)]
print(" " + rw("무엇을 묻나", w[0]) + " " + "왜")
for r in rows:
print(" " + rw(r[0], w[0]) + " " + r[1])
print(" 마지막 줄이 가장 자주 어겨집니다")
print(" 중요도 표를 받은 사람은 대개 그것을 개입 지침으로 읽습니다")
print()
print(" (4) 06 단원을 한자리에 모읍니다")
rows = [
("226", "무엇으로 재나", "ROC 와 PR 과 눈금맞춤"),
("227", "치우침을 어떻게 다루나", "문턱이 먼저"),
("228", "무엇을 넣나", "맞는 변수 하나"),
("229", "손잡이를 어떻게 고르나", "로그 눈금에서 넓게"),
("230", "무엇을 보고 판단했나", "설명은 인과가 아님"),
]
w = [max(pw(r[i]) for r in rows + [("강", "무엇을 물었나", "한 줄 답")]) for i in range(3)]
print(" " + rw("강", w[0]) + " " + rw("무엇을 물었나", w[1]) + " " + "한 줄 답")
for r in rows:
print(" " + rw(r[0], w[0]) + " " + rw(r[1], w[1]) + " " + r[2])
print(" 다섯 강이 하나의 물음을 나눠 답합니다")
print(" 만든 모형을 어떻게 믿을 것인가입니다")
print(" 07 단원부터는 시간이 흐르는 자료를 다룹니다")
print()
print("=" * 78)
print("검산 끝")
print("=" * 78)
# ==============================================================================
# 230강 모형 해석과 변수 중요도 코드 검산
# ==============================================================================
#
# 문제 1. 무엇을 설명하려는 것인가
#
# (1) 서로 다른 물음을 가릅니다
# 무엇을 묻나 무엇에 대한 답 자료로 되나
# 이 모형이 무엇을 보나 모형을 설명 가능함
# 이 예측이 왜 나왔나 한 예측을 설명 가능함
# 이 변수를 바꾸면 어떻게 되나 개입의 결과 자료만으로는 안 됨
# 무엇이 원인인가 인과 설계가 필요함
# 앞 두 줄만 이 강의가 답합니다
# 아래 두 줄은 변수 중요도로 답할 수 없습니다
#
# (2) 중요도가 인과가 아님을 봅니다
# 참 원인은 숨은 z 이고 x1 과 x2 는 z 의 그림자입니다
# 변수 계수 y 와의 상관 참 인과 효과
# x1 0.898118 0.895771 0.000000
# x2 0.988487 0.899331 0.000000
# x3 0.503632 0.228616 0.500000
# x1 과 x2 는 참 인과 효과가 0 인데 계수와 상관이 모두 큽니다
# x1 을 실제로 바꿔도 y 는 안 바뀝니다. z 가 안 바뀌기 때문입니다
# 모형은 무엇이 y 를 예측하는지 말하지 무엇이 y 를 만드는지 말하지 않습니다
#
# (3) 겹친 변수에서 중요도가 어떻게 갈리는지 봅니다
# x1 과 x2 의 상관은 0.920705 입니다
# 자료를 200 번 다시 뽑아 계수가 얼마나 흔들리는지 봅니다
# 무엇 평균 표준편차
# x1 의 계수 0.893354 0.039932
# x2 의 계수 0.993539 0.041638
# 두 계수의 합 1.886894 0.018251
# 두 계수의 상관은 -0.900703 입니다
# 각각은 크게 흔들리는데 합은 훨씬 안정합니다
# 겹친 변수는 서로의 자리를 뺏습니다. 하나만 보면 안 됩니다
#
# 문제 2. 전역 중요도
#
# (1) 방법들을 정리합니다
# 방법 어디에 쓰나 무엇이 문제인가
# 표준화 계수 선형모형만 겹치면 흔들림
# 나무의 이득 합 학습 자료에서 칸 많은 변수에 유리
# 치환 중요도 아무 모형에나 겹치면 둘 다 작아짐
# 빼고 다시 학습 가장 정직함 변수 수만큼 다시 학습
# 샤플리 값 공평한 배분 계산이 비쌈
#
# (2) 나무의 이득 중요도가 칸 많은 변수에 쏠리는지 봅니다
# 참 관계는 g2 하나뿐이고 나머지 셋은 잡음입니다
# 변수 서로 다른 값 수 이득 중요도
# g1 이어진 값 잡음 900 0.067759
# g2 참 변수 두 칸 2 0.834741
# g3 네 칸 잡음 4 0.004005
# g4 이어진 값 잡음 900 0.093495
# 참 변수 g2 가 0.834741 로 가장 큽니다
# 그런데 잡음인 g1 과 g4 가 합쳐서 0.161254 를 가져갑니다
# 값이 이어진 변수는 자를 자리가 많아 우연히 이득을 냅니다
# 두 칸짜리 g3 대비 g1 은 서로 다른 값이 225 배입니다
#
# (3) 치환 중요도를 잽니다
# 숲을 학습한 뒤 변수 하나씩 값을 섞어 오차가 얼마나 오르는지 봅니다
# 변수 학습에서 오른 폭 따로 뗀 데서 오른 폭
# g1 이어진 값 잡음 0.097987 0.008133
# g2 참 변수 두 칸 0.990933 0.881994
# g3 네 칸 잡음 0.066349 -0.019967
# g4 이어진 값 잡음 0.137180 0.013071
# 참 변수 g2 만 크게 오릅니다
# 학습에서 잰 값과 따로 뗀 데서 잰 값이 다릅니다
# 잡음 변수는 학습에서만 오릅니다. 외운 것을 흐트러뜨렸기 때문입니다
# 치환 중요도는 반드시 학습에 안 쓴 자료에서 재야 합니다
#
# (4) 겹친 변수에서 치환 중요도가 어떻게 되는지 봅니다
# x1 과 x2 는 상관이 0.920705 로 거의 같은 정보를 담습니다
# 무엇을 섞었나 오른 폭
# x1 만 1.282839
# x2 만 2.991254
# x1 과 x2 함께 5.311459
# x3 만 0.316150
# 하나씩 섞은 두 값을 더하면 4.274094 인데 함께 섞으면 5.311459 입니다
# 한쪽을 섞어도 다른 쪽이 그 정보를 대신 들고 있기 때문입니다
# 겹친 변수는 묶어서 봐야 합니다. 하나씩 더한 값은 실제 몫보다 작습니다
#
# 문제 3. 부분 의존과 개별 조건부
#
# (1) 무엇을 그리는지 정리합니다
# 무엇 어떻게 무엇을 보나
# 부분 의존 다른 변수를 평균 내고 하나만 움직임 평균 모양
# 개별 조건부 관측마다 한 줄씩 흩어짐이 보임
# 누적 국소 효과 가까운 점들만 씀 겹쳐도 안 망가짐
#
# (2) 부분 의존이 평균을 내며 무엇을 감추는지 봅니다
# u2 가 0 이면 기울기가 -2 이고 1 이면 +2 인 자료입니다
# u1 값 부분 의존 평균 u2 가 0 인 줄 u2 가 1 인 줄
# -2.0000 -0.016948 3.854916 -3.820585
# -1.0000 0.000375 2.039873 -2.003184
# 0.0000 -0.078907 -0.127010 -0.031651
# 1.0000 0.117081 -1.968141 2.165559
# 2.0000 -0.034114 -3.905814 3.769361
# 부분 의존 곡선은 거의 평평합니다. 두 방향이 서로를 지웠기 때문입니다
# 갈래를 나눠 보면 한쪽은 내려가고 한쪽은 올라갑니다
# 평평한 부분 의존 곡선은 효과가 없다는 뜻이 아닙니다
#
# (3) 개별 조건부 곡선의 기울기를 봅니다
# 관측마다 곡선 하나를 그려 기울기를 잽니다
# 무엇 평균 기울기 표준편차
# 모든 관측 -0.004292 1.918759
# u2 가 0 인 관측 -1.940182 0.000000
# u2 가 1 인 관측 1.897486 0.000000
# 전체 평균 기울기는 -0.004292 로 0 에 가깝습니다
# 그런데 표준편차가 1.918759 입니다. 이 큰 흩어짐이 상호작용의 신호입니다
# 갈래 안에서는 표준편차가 정확히 0 입니다
# 변수가 u1 과 u2 둘뿐이라 u2 가 같으면 곡선이 완전히 같기 때문입니다
# 평균이 0 이고 흩어짐이 크면 반드시 갈라서 봐야 합니다
#
# (4) 부분 의존이 없는 조합을 만들어 내는 것을 봅니다
# v1 과 v2 의 상관은 0.955310 입니다
# 부분 의존은 v1 을 크게 두고 v2 를 그대로 두는 조합도 계산합니다
# v1 값 부분 의존 v1 도 v2 도 그 자리인 관측 수
# -2.5000 -1.911006 0
# -1.0000 -0.711018 42
# 0.0000 -0.050819 188
# 1.0000 0.614919 23
# 2.5000 1.419703 0
# v1 이 크게 치우쳤는데 v2 는 평균인 점이 실제로는 거의 없습니다
# 그런 자리에서의 예측은 모형이 본 적 없는 곳에서의 추측입니다
# 213강 문제 3 에서 본 바깥 추정이 여기서 다시 나옵니다
#
# 문제 4. 한 예측을 설명하기
#
# (1) 방법들을 정리합니다
# 방법 무엇을 하나 무엇이 문제인가
# 선형 모형의 기여 계수 곱하기 값 선형이면 정확함
# 국소 대리 모형 근처에서 직선으로 흉내 근처를 어떻게 정하나
# 샤플리 값 모든 순서의 평균 기여 계산이 2 의 p 승
# 반사실 설명 무엇을 바꾸면 뒤집히나 실행 가능성
#
# (2) 샤플리 값을 직접 계산합니다
# 학습 자료 평균 예측은 0.035109 입니다
# 따로 뗀 자료에서 세 점을 골라 기여를 나눕니다
# 점 예측 w1 기여 w2 기여 w3 기여 합과 예측의 차
# 1 -2.270111 -2.122092 -0.065973 -0.117154 -0.000000
# 2 0.094178 0.349023 -0.123803 -0.166151 0.000000
# 3 1.416824 0.485353 0.708612 0.187749 0.000000
# 기여의 합에 평균을 더하면 예측이 됩니다. 마지막 열이 그 확인입니다
# 이것이 샤플리 값의 유일한 보장입니다
#
# (3) 같은 변수가 점마다 다른 기여를 내는 것을 봅니다
# 참 관계에 w1 곱하기 w2 항이 있습니다
# 점 w1 값 w2 값 w1 기여 기여 나누기 값
# 1 0.080148 0.247885 0.167495 2.089832
# 2 0.717305 -0.086984 0.982771 1.370088
# 3 2.278753 -0.063808 3.573526 1.568194
# 4 -2.205759 -0.308981 -2.594842 1.176395
# 마지막 열이 점마다 다릅니다. 선형모형이면 모두 같은 값이어야 합니다
# w2 가 클수록 w1 한 단위의 값이 커지기 때문입니다
# 전역 중요도 하나로는 이것을 못 봅니다
#
# (4) 국소 대리 모형과 견줍니다
# 한 점 근처에서만 직선으로 흉내 내고 기울기를 봅니다
# 점 근처 폭 직선 기울기 w1 직선 기울기 w2 흉내 낸 결정계수
# 1 0.3000 1.837814 -2.033250 0.912303
# 1 0.8000 1.359891 -1.797415 0.956634
# 1 2.0000 1.381954 -1.338302 0.887460
# 근처 폭을 넓히면 흉내 낸 결정계수가 떨어집니다
# 좁히면 잘 맞지만 쓸 점이 적어 기울기가 흔들립니다
# 근처를 어떻게 정하느냐가 설명을 정합니다. 답이 하나가 아닙니다
#
# 문제 5. 실무에서 쓰기
#
# (1) 무엇을 믿을지 정리합니다
# 무엇 얼마나 믿나 조건
# 따로 뗀 자료의 치환 중요도 믿을 만함 학습에서 재지 않기
# 나무의 이득 중요도 참고만 칸 많은 변수에 쏠림
# 부분 의존 갈라서 함께 평균이 감춤
# 샤플리 값 한 점 설명에 합이 예측이 됨
# 어떤 것도 인과 아님 언제나 개입을 말할 수 없음
#
# (2) 설명이 얼마나 흔들리는지 봅니다
# 자료를 30 번 다시 뽑아 매번 숲을 새로 학습하고 중요도를 잽니다
# 변수 중요도 평균 표준편차 가장 낮았던 값 1 등이었던 비율
# w1 5.218296 0.438930 4.406796 1.000000
# w2 2.915563 0.299151 2.230144 0.000000
# w3 0.034740 0.010551 0.013049 0.000000
# 순위는 30 번 모두 같았습니다. w1 이 언제나 1 등입니다
# 그런데 w1 의 값은 4.406796 에서 6.289994 사이를 오갑니다
# 순위는 믿을 만하고 값의 크기는 그만큼 못 믿습니다
# 중요도를 보고할 때는 값 하나가 아니라 흔들리는 폭도 함께 적습니다
#
# (3) 설명이 감사에 쓰일 때 무엇을 확인할지 봅니다
# 무엇을 묻나 왜
# 어떤 자료에서 쟀나 따로 뗀 자료여야 합니다
# 겹친 변수를 묶었나 하나씩 보면 둘 다 작아집니다
# 평균이 감추고 있나 개별 곡선의 흩어짐을 봅니다
# 본 적 있는 자리인가 부분 의존은 없는 조합도 그립니다
# 얼마나 흔들리나 자료를 다시 뽑아 재봅니다
# 인과로 읽히고 있나 개입의 결과가 아닙니다
# 마지막 줄이 가장 자주 어겨집니다
# 중요도 표를 받은 사람은 대개 그것을 개입 지침으로 읽습니다
#
# (4) 06 단원을 한자리에 모읍니다
# 강 무엇을 물었나 한 줄 답
# 226 무엇으로 재나 ROC 와 PR 과 눈금맞춤
# 227 치우침을 어떻게 다루나 문턱이 먼저
# 228 무엇을 넣나 맞는 변수 하나
# 229 손잡이를 어떻게 고르나 로그 눈금에서 넓게
# 230 무엇을 보고 판단했나 설명은 인과가 아님
# 다섯 강이 하나의 물음을 나눠 답합니다
# 만든 모형을 어떻게 믿을 것인가입니다
# 07 단원부터는 시간이 흐르는 자료를 다룹니다
#
# ==============================================================================
# 검산 끝
# ==============================================================================