강부터 강까지 세 강의가 점들을 나누는 일을 했습니다. 이 강의는 나누지 않습니다.
강에서 주성분을 고유분해로 유도했습니다. 여기서는 쓰는 법을 봅니다. 무엇을 지키고 무엇을 잃는지, 그리고 언제 다른 방법이 필요한지입니다.
주성분은 분산을 지킵니다. 그런데 그림을 그리려면 이웃을 지켜야 하고, 그 목표를 가진 방법이 따로 있습니다.
문제. 주성분을 실제로 씁니다.
() 군집과 무엇이 다른지 정리하세요.
() 분산이 어디에 있는지 보세요.
() 몇 차원으로 줄여야 얼마나 잃는지 재세요.
생각의 실마리. 변수가 개라도 실제로 자유롭게 움직이는 방향은 몇 개뿐일 수 있습니다. 그 방향을 찾아 새 좌표로 삼는 것이 주성분입니다.
풀이. () 정리합니다.
| 무엇 | 군집 | 차원 축소 |
|---|---|---|
| 무엇을 만드나 | 덩어리 표시 | 새 좌표 |
| 점의 개수 | 그대로 | 그대로 |
| 변수의 개수 | 그대로 | 줄어듦 |
| 무엇을 지키나 | 가까운 것끼리 | 분산이나 거리 |
| 다음 단계 | 덩어리별 분석 | 무엇이든 |
() 분산이 어디에 있는지 봅니다. 변수 개인데 실제로는 차원에서 만든 자료입니다.
| 성분 | 고유값 | 설명하는 비율 | 누적 비율 |
|---|---|---|---|
셋째 성분에서 넷째 성분으로 고유값이 에서 으로 열여섯 배 떨어집니다. 그 뒤로는 에서 사이로 평평합니다.
평평해진 뒤는 전부 잡음입니다. 누적 비율이 어디서 평평해지는지가 실제 차원의 눈짐작이고, 강의 팔꿈치와 같은 종류의 그림입니다.
() 몇 차원으로 줄여야 얼마나 잃는지 잽니다.
| 줄인 차원 | 복원 오차 | 남긴 분산 비율 | 원래 거리와의 상관 |
|---|---|---|---|
차원까지 줄여도 복원 오차가 이고 거리 상관이 입니다. 차원을 다 쓸 때와 거의 같습니다.
그 뒤로는 더 남겨도 얻는 것이 적습니다. 차원으로 늘려도 복원 오차가 로 절반만 줄어듭니다.
강의 특이값 분해가 최적 근사를 준다는 것이 여기서 쓰입니다. 어떤 차원 사영보다도 주성분의 복원 오차가 작습니다.
이 문제에서 배우는 것. 고차원 자료는 대개 낮은 차원에 사실상 놓여 있습니다. 강 문제 의 내재 차원이 여기서 고유값 그림으로 보입니다. 그리고 그 차원까지 줄이는 것은 거의 공짜입니다.
확인 1-1. 주성분이 무엇을 최대로 하는지 쓰세요.
답. 남긴 방향들이 설명하는 분산입니다.
확인 1-2. 검산에서 셋째와 넷째 고유값을 쓰세요.
답. 와 입니다.
확인 1-3. 검산에서 차원으로 줄였을 때의 복원 오차와 거리 상관을 쓰세요.
답. 과 입니다.
문제. 주성분의 한계를 봅니다.
() 무엇을 지키는지 정리하세요.
() 갈래를 가르는 방향이 버려질 수 있음을 보이세요.
() 눈금이 답을 바꾸는 것을 확인하세요.
생각의 실마리. 주성분은 를 안 봅니다. 분산이 큰 방향을 남기는데, 분산이 큰 것과 쓸모 있는 것이 같다는 보장이 없습니다.
풀이. () 정리합니다.
| 무엇 | 지키나 | 왜 |
|---|---|---|
| 전체 분산 | 가장 많이 | 그것을 최대화함 |
| 먼 점들의 거리 | 잘 | 큰 방향을 남김 |
| 가까운 점들의 거리 | 잘 못함 | 작은 방향을 버림 |
| 갈래를 가르는 방향 | 보장 없음 | 분산과 무관 |
| 굽은 구조 | 못 함 | 직선 사영만 |
() 갈래를 가르는 방향이 버려질 수 있음을 보입니다. 첫 축은 분산이 크지만 갈래와 무관하고 둘째 축이 갈래를 가릅니다.
| 축 | 분산 | 갈래와의 상관 제곱 | 그 축만으로 정확도 |
|---|---|---|---|
| 주성분 | |||
| 주성분 |
첫 성분이 분산의 퍼센트를 가져가는데 갈래와의 상관 제곱은 입니다. 그 축만으로는 정확도가 으로 동전 던지기입니다.
한 차원으로 줄이면 갈래 정보를 통째로 버립니다. 분산 기준으로는 첫 성분을 남기는 것이 옳은데, 목적이 분류라면 완전히 틀립니다.
라벨을 보는 축과 견줍니다.
| 어떤 축 | 갈래와의 상관 제곱 | 그 축만으로 정확도 |
|---|---|---|
| 주성분 | ||
| 선형판별 축 |
선형판별은 갈래 사이 거리를 갈래 안 흩어짐으로 나눈 것을 최대로 합니다. 라벨이 있으면 주성분보다 선형판별이 낫고, 라벨이 없을 때만 주성분을 쓰는 이유가 이것입니다.
() 눈금이 답을 바꾸는 것을 확인합니다.
| 둘째 변수 배율 | 첫 성분이 첫 변수에 준 무게 | 첫 성분의 분산 비율 |
|---|---|---|
배율을 키우면 첫 변수에 준 무게가 에서 으로 십분의 일이 됩니다. 배율을 키운 변수들이 첫 성분을 독차지합니다.
단위가 다른 변수를 섞을 때는 표준화가 필수입니다. 상관행렬로 주성분을 하는 것이 곧 표준화한 것입니다.
이 문제에서 배우는 것. 주성분의 답은 분산이 정하고, 분산은 단위가 정합니다. 강과 강에서 거리가 단위에 딸렸듯, 여기서는 분산이 그렇습니다. 그리고 분산이 큰 방향이 쓸모 있는 방향이라는 보장은 없습니다.
확인 2-1. 주성분이 갈래를 가르는 방향을 버릴 수 있는 이유를 쓰세요.
답. 라벨을 안 보고 분산만 보기 때문입니다.
확인 2-2. 검산에서 주성분 과 선형판별 축의 갈래와의 상관 제곱을 쓰세요.
답. 와 입니다.
확인 2-3. 검산에서 배율 과 일 때 첫 성분이 첫 변수에 준 무게를 쓰세요.
답. 과 입니다.
문제. 사영의 한계를 봅니다.
() 말린 자료에서 이웃이 얼마나 깨지는지 재세요.
() 펴 놓은 좌표와 견주세요.
() 왜 다른 목표가 필요한지 정리하세요.
생각의 실마리. 주성분은 직선 사영입니다. 말린 종이를 그대로 누르면 떨어져 있던 층끼리 겹칩니다.
풀이. () 말린 종이 모양 자료에서 잽니다. 참으로는 차원입니다.
| 무엇 | 값 | 무엇을 뜻하나 |
|---|---|---|
| 첫 두 성분의 분산 비율 | 거의 다 설명 | |
| 이웃 개가 남는 비율 | 절반도 안 남음 | |
| 새로 생긴 가짜 이웃 비율 | 겹쳐 온 점들 | |
| 참 이웃의 차원 순위 중앙값 | 위 밖으로 밀림 |
분산은 를 설명하는데 이웃은 밖에 안 남습니다.
분산을 잘 지키는 것과 이웃을 잘 지키는 것이 다른 일입니다. 원래 이웃이었던 점의 순위 중앙값이 위로 밀려나고, 대신 퍼센트가 새로 온 가짜 이웃입니다.
말린 것을 그대로 눌러 층끼리 겹치게 만들기 때문입니다. 강 문제 의 굽은 띠와 같은 문제입니다.
() 펴 놓은 좌표와 견줍니다.
| 어떤 좌표 | 말린 축 와의 상관 제곱 | 갈래 순도 |
|---|---|---|
| 주성분 차원 | ||
| 펴 놓은 좌표 |
주성분 축은 말린 축 와 상관 제곱이 입니다. 감긴 방향을 전혀 못 폅니다.
갈래 순도는 둘 다 근처로 낮습니다. 높이 축이 함께 있어 평균이 그쪽으로 자르기 때문이고, 중요한 것은 순도가 아니라 감긴 축을 복원했느냐입니다.
() 왜 다른 목표가 필요한지 정리합니다.
주성분의 목적함수는 복원 오차입니다. 복원 오차를 줄이는 것과 이웃을 지키는 것은 다른 목표이고, 굽은 자료에서 그 둘이 크게 갈립니다.
그래서 이웃을 지키는 것을 목표로 삼는 방법이 필요합니다.
이 문제에서 배우는 것. 방법을 고르는 것은 목적함수를 고르는 것입니다. 강에서 "무엇을 최소화할 것인가"를 물었을 때와 같습니다. 그림을 그릴 것이면 이웃을 목적함수에 넣어야 합니다.
확인 3-1. 주성분이 굽은 구조를 못 펴는 이유를 쓰세요.
답. 직선 사영이라 떨어져 있던 층끼리 겹치기 때문입니다.
확인 3-2. 검산에서 첫 두 성분의 분산 비율과 이웃이 남는 비율을 쓰세요.
답. 와 입니다.
확인 3-3. 검산에서 주성분 차원과 말린 축 의 상관 제곱을 쓰세요.
답. 입니다.
문제. t-SNE를 봅니다.
() 방법들을 정리하세요.
() 말린 자료에서 주성분과 견주세요.
() 손잡이가 무엇을 바꾸는지 보세요.
생각의 실마리. "이웃을 지킨다"를 수식으로 적어야 합니다. 고차원에서 이웃일 확률과 저차원에서 이웃일 확률을 정의하고 그 둘을 맞추면 됩니다.
풀이. () 정리합니다.
| 무엇 | 무엇을 최소화 | 성질 |
|---|---|---|
| 주성분 | 복원 오차 | 직선이고 뒤집을 수 있음 |
| 다차원 척도법 | 거리의 차 | 먼 거리를 중시 |
| t-SNE | 이웃 분포의 KL | 가까운 것만 중시 |
| UMAP | 이웃 그래프의 교차엔트로피 | 전체 구조를 더 지킴 |
강의 KL 발산이 목적함수입니다. KL은 비대칭이라 가 큰데 가 작으면 크게 벌하고 그 반대는 덜 벌합니다. 그래서 가까운 것을 지키는 데 집중합니다.
저차원에서 분포를 쓰는 것이 이름의 입니다. 꼬리가 두꺼워 먼 점을 멀리 밀어낼 여유를 줍니다.
() 말린 자료에서 주성분과 견줍니다.
| 무엇 | 이웃 개가 남는 비율 | 신뢰도 | 차원에서의 순도 |
|---|---|---|---|
| 주성분 | |||
| t-SNE |
t-SNE가 이웃을 훨씬 많이 지킵니다. 에서 으로 오릅니다.
그런데 차원에서 평균으로 자른 순도는 오히려 조금 낮습니다. 에서 입니다.
이웃을 지키는 것과 갈래를 가르는 것은 다른 목표입니다. t-SNE는 말린 구조를 펴서 이어진 띠로 만들 뿐 두 동강 내지 않습니다.
대신 뒤집을 수 없고 새 점을 넣을 수 없습니다. 주성분은 행렬 하나라 새 점을 그냥 곱하면 됩니다.
() 손잡이가 무엇을 바꾸는지 봅니다. 덩어리 셋짜리 자료입니다.
| 복잡도 | 이웃 개가 남는 비율 | 신뢰도 | 덩어리 사이 평균 거리 |
|---|---|---|---|
복잡도가 작으면 아주 가까운 이웃만 봅니다. 크면 더 넓게 봐서 전체 배치가 달라집니다.
덩어리 사이 평균 거리가 에서 으로 크게 바뀝니다. 자료는 그대로인데 손잡이만 바꿨습니다.
그 거리를 믿으면 안 됩니다. 그림에서 두 덩어리가 멀어 보인다고 실제로 먼 것이 아닙니다.
이 문제에서 배우는 것. t-SNE는 이웃을 지키는 대가로 나머지를 다 포기합니다. 덩어리 사이 거리도, 덩어리 크기도, 전체 배치도 손잡이에 딸립니다. 그림은 이웃 관계만 읽어야 합니다.
확인 4-1. t-SNE가 무엇을 최소화하는지 쓰세요.
답. 고차원과 저차원 이웃 분포 사이의 KL 발산입니다.
확인 4-2. 검산에서 주성분과 t-SNE의 이웃이 남는 비율을 쓰세요.
답. 과 입니다.
확인 4-3. 검산에서 복잡도 와 의 덩어리 사이 평균 거리를 쓰세요.
답. 과 입니다.
문제. 실제로 씁니다.
() 쓰임을 정리하세요.
() 잡음을 줄이면 예측이 좋아지는지 재세요.
() 쓸 성분을 버리는 예와 그림 읽기의 함정을 보세요.
생각의 실마리. 차원 축소의 쓰임은 여럿인데, 뒤집을 수 있어야 하는가로 갈립니다. 그림만 그릴 것이면 t-SNE가 되고, 나머지는 대개 주성분입니다.
풀이. () 정리합니다.
| 쓰임 | 무엇을 쓰나 | 어느 방법 |
|---|---|---|
| 잡음 줄이기 | 작은 성분 버림 | 주성분 |
| 계산 줄이기 | 적은 차원으로 | 주성분이나 무작위 사영 |
| 그림 그리기 | 차원 배치 | t-SNE나 UMAP |
| 특성 만들기 | 새 좌표를 변수로 | 주성분 |
| 압축 | 복원 가능해야 함 | 주성분 |
셋째 줄만 t-SNE이고 나머지는 주성분입니다. 뒤집을 수 있어야 하기 때문입니다.
() 잡음을 줄이면 예측이 좋아지는지 잽니다. 변수 개인데 참 신호는 차원입니다.
| 몇 성분을 쓰나 | 검증 오차 | 남긴 분산 비율 |
|---|---|---|
| 전부 (개) | ||
성분 개만 쓰면 검증 오차가 로 개 다 쓸 때의 보다 작습니다.
버린 개가 잡음이었기 때문입니다. 강의 정규화와 같은 효과이고, 분산을 퍼센트만 남겼는데 예측은 더 좋습니다.
() 쓸 성분을 버리는 예를 봅니다. 가 분산이 작은 둘째 변수에만 딸린 자료입니다.
| 무엇을 쓰나 | 검증 오차 | 남긴 분산 비율 |
|---|---|---|
| 두 변수 다 | ||
| 첫 성분만 |
분산의 퍼센트를 남겼는데 검증 오차가 에서 로 스물여섯 배가 됩니다.
강 문제 에서 정보가 있는 방향과 분산이 큰 방향이 다르다고 했습니다. 부분최소제곱은 를 보면서 축을 고르므로 이 문제가 없습니다.
그림을 읽을 때 조심할 것을 정리합니다.
| 흔한 오해 | 실제로는 |
|---|---|
| 덩어리 크기가 뜻이 있다 | 손잡이가 정함 |
| 덩어리 사이 거리가 뜻이 있다 | 거의 뜻 없음 |
| 모양이 뜻이 있다 | 돌려도 같은 그림 |
| 덩어리가 보이면 군집이 있다 | 없어도 보임 |
마지막 줄을 확인합니다. 차원 표준정규에서 뽑은 자료로 군집이 없습니다.
| 무엇 | 값 |
|---|---|
| 조각으로 나눈 크기 | |
| 차원에서의 가까운 이웃 거리 비 | |
| 고차원에서 같은 값 |
군집이 없는데도 그림에는 네 조각이 고르게 생깁니다.
가까운 이웃과 전체 평균 거리의 비가 고차원에서 인데 차원에서 입니다. 강의 거리 집중이 t-SNE를 지나며 인위적으로 풀린 것이고, 그래서 없던 덩어리가 보입니다.
이 문제에서 배우는 것. 차원 축소는 정보를 버리는 일입니다. 버린 것이 잡음이면 이득이고 신호면 손해인데, 주성분은 그 둘을 구분할 방법이 없습니다. 그리고 t-SNE 그림은 이웃 말고는 아무것도 안 담습니다.
확인 5-1. 그림 그리기 말고 대개 주성분을 쓰는 이유를 쓰세요.
답. 뒤집을 수 있고 새 점을 그냥 곱하면 되기 때문입니다.
확인 5-2. 검산에서 전부 쓸 때와 성분 개를 쓸 때의 검증 오차를 쓰세요.
답. 과 입니다.
확인 5-3. 검산에서 군집 없는 자료의 이웃 거리 비를 고차원과 차원에서 쓰세요.
답. 와 입니다.
| 유형 | 무엇을 묻나 | 어디를 보나 |
|---|---|---|
| 고유값 그림 | 어디서 평평해지나 | 문제 |
| 복원 오차 | 몇 차원이면 되나 | 문제 |
| 갈래 방향 상실 | 분산과 무관 | 문제 |
| 눈금 민감 | 표준화 필수 | 문제 |
| 굽은 구조 | 층이 겹침 | 문제 |
| 이웃 보존 | 분산 보존과 다름 | 문제 |
| t-SNE의 목적 | 이웃 분포의 KL | 문제 |
| 복잡도 | 배치를 바꿈 | 문제 |
| 잡음 줄이기 | 예측이 좋아짐 | 문제 |
| 그림의 함정 | 없어도 보임 | 문제 |
핵심 식을 한자리에 모읍니다.
| 방법 | 뒤집을 수 있나 | 새 점을 넣을 수 있나 | 무엇을 지키나 |
|---|---|---|---|
| 주성분 | 예 | 예 | 분산과 먼 거리 |
| 다차원 척도법 | 아니오 | 어려움 | 모든 거리 |
| t-SNE | 아니오 | 아니오 | 가까운 이웃 |
| UMAP | 아니오 | 가능 | 이웃과 전체 구조 |
문제 6. 주성분이 무엇을 최대로 하는지 쓰세요.
답. 남긴 방향들이 설명하는 분산입니다.
문제 7. 검산에서 셋째와 넷째 고유값을 쓰세요.
답. 와 입니다.
문제 8. 검산에서 차원으로 줄였을 때의 복원 오차와 거리 상관을 쓰세요.
답. 과 입니다.
문제 9. 검산에서 주성분 과 선형판별 축의 갈래와의 상관 제곱을 쓰세요.
답. 와 입니다.
문제 10. 검산에서 배율 과 일 때 첫 성분이 첫 변수에 준 무게를 쓰세요.
답. 과 입니다.
문제 11. 검산에서 첫 두 성분의 분산 비율과 이웃이 남는 비율을 쓰세요.
답. 와 입니다.
문제 12. 검산에서 주성분 차원과 말린 축 의 상관 제곱을 쓰세요.
답. 입니다.
문제 13. t-SNE가 무엇을 최소화하는지 쓰세요.
답. 고차원과 저차원 이웃 분포 사이의 KL 발산입니다.
문제 14. 검산에서 주성분과 t-SNE의 이웃이 남는 비율을 쓰세요.
답. 과 입니다.
문제 15. 검산에서 복잡도 와 의 덩어리 사이 평균 거리를 쓰세요.
답. 과 입니다.
문제 16. 검산에서 전부 쓸 때와 성분 개를 쓸 때의 검증 오차를 쓰세요.
답. 과 입니다.
문제 17. 검산에서 두 변수 다 쓸 때와 첫 성분만 쓸 때의 검증 오차를 쓰세요.
답. 과 입니다.
문제 18. 검산에서 군집 없는 자료의 이웃 거리 비를 고차원과 차원에서 쓰세요.
답. 와 입니다.
심화 1. 주성분의 두 가지 유도를 잇으세요.
| 관점 | 무엇을 하나 | 답 |
|---|---|---|
| 분산 최대화 | 를 최대로 | 가장 큰 고유벡터 |
| 복원 오차 최소화 | 를 최소로 | 같은 것 |
전체 노름이 고정이므로 사영 노름을 최대화하는 것과 오차를 최소화하는 것이 같습니다. 강의 정사영이 여기서 다시 나오고, 강의 특이값 분해가 두 문제의 공통 답을 줍니다.
심화 2. 커널 주성분을 정리하세요.
| 무엇 | 주성분 | 커널 주성분 |
|---|---|---|
| 무엇을 분해 | 공분산 행렬 | 중심화한 그람 행렬 |
| 크기 | ||
| 굽은 구조 | 못 폄 | 펼 수 있음 |
| 새 점 | 그냥 곱함 | 커널 계산 필요 |
강의 커널 요령을 주성분에 끼운 것입니다. 특성 공간에서 직선 사영이 원래 공간에서는 굽은 곡면이 되므로, 문제 의 말린 자료를 펼 수 있습니다. 대신 행렬이라 표본이 많으면 무겁습니다.
심화 3. t-SNE에서 분포를 쓰는 이유를 정리하세요.
| 무엇 | 정규분포를 쓰면 | 분포를 쓰면 |
|---|---|---|
| 꼬리 | 얇음 | 두꺼움 |
| 먼 점 | 억지로 가깝게 | 멀리 둬도 됨 |
| 결과 | 뭉쳐 버림 | 덩어리가 갈라짐 |
| 이름 | SNE | t-SNE |
강의 거리 집중 때문입니다. 고차원에서는 대부분의 점이 중간쯤 거리에 있는데, 저차원에서 그 많은 점을 다 중간에 두려면 자리가 모자랍니다. 꼬리가 두꺼우면 조금만 밀어내도 확률이 충분히 낮아져 자리가 생깁니다.
심화 4. 무작위 사영과 주성분을 견주세요.
| 무엇 | 주성분 | 무작위 사영 |
|---|---|---|
| 자료를 보나 | 예 | 아니오 |
| 계산 | 특이값 분해 | 행렬 곱 하나 |
| 보장 | 최적 복원 | 강의 거리 보존 |
| 필요한 차원 | 고유값이 정함 |
강 문제 에서 무작위 사영이 원래 차원과 무관하게 통한다고 했습니다. 자료를 안 보므로 한 번 훑을 필요도 없고, 표본이 아주 많거나 실시간으로 들어올 때 쓸 수 있습니다.
심화 5. 주성분의 성분 수를 고르는 방법을 정리하세요.
| 방법 | 무엇을 보나 | 한계 |
|---|---|---|
| 누적 비율 | 이나 퍼센트 | 기준이 임의 |
| 팔꿈치 | 고유값이 꺾이는 자리 | 눈으로 정함 |
| 카이저 규칙 | 고유값이 이상 | 표준화했을 때만 |
| 평행 분석 | 무작위 자료와 비교 | 계산이 비쌈 |
| 다음 단계 성능 | 교차검증 | 가장 정직함 |
강 문제 의 고르기와 정확히 같은 목록입니다. 넷째 줄이 그 강의의 간격 통계량에 해당하고, 마지막 줄이 여기서도 가장 믿을 만합니다.
심화 6. 언제 어느 방법을 쓸지 정리하세요.
| 상황 | 무엇을 쓰나 | 왜 |
|---|---|---|
| 전처리로 차원 축소 | 주성분 | 뒤집을 수 있음 |
| 자료를 눈으로 보기 | t-SNE나 UMAP | 이웃을 지킴 |
| 표본이 아주 많음 | 무작위 사영 | 자료를 안 봄 |
| 굽은 구조가 확실함 | 커널 주성분이나 UMAP | 펼 수 있음 |
| 라벨이 있음 | 선형판별 | 갈래를 봄 |
마지막 줄이 문제 의 교훈입니다. 라벨이 있는데 주성분을 쓰는 것은 정보를 버리는 것이고, 강의 특성공학에서 이 판단이 다시 나옵니다.
정답.
| 기호 | 읽는 법 | 뜻 |
|---|---|---|
| 주성분분석 | principal component analysis | 분산이 큰 방향으로 축을 갈아탑니다 |
| 고유값 그림 | scree plot | 성분별 고유값을 그린 그림입니다 |
| 복원 오차 | reconstruction error | 줄였다 되돌렸을 때의 차이입니다 |
| 선형판별분석 | linear discriminant analysis | 갈래를 가르는 축을 찾습니다 |
| 이웃 보존 | neighborhood preservation | 원래 이웃이 그대로 남는 비율입니다 |
| 신뢰도 | trustworthiness | 가짜 이웃이 얼마나 적은지 잽니다 |
| t-SNE | t-distributed SNE | 이웃 분포의 KL을 최소화합니다 |
| 복잡도 | perplexity | 이웃을 몇 개쯤 볼지 정합니다 |
| UMAP | uniform manifold approximation | 이웃 그래프를 맞춥니다 |
| 커널 주성분 | kernel PCA | 특성 공간에서 주성분을 합니다 |
다음은 225강 이상탐지입니다. 이 강의까지 네 강의가 자료의 큰 흐름을 봤습니다. 다음 강의는 반대입니다. 흐름에서 벗어난 드문 것을 찾습니다.
import numpy as np
rng = np.random.default_rng(20270117)
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 d2(A, B):
return ((A[:, None, :] - B[None, :, :]) ** 2).sum(axis=2)
def pca(X, m):
mu = X.mean(axis=0)
Z = X - mu
U, S, Vt = np.linalg.svd(Z, full_matrices=False)
return mu, Vt[:m], S
def tsne(X, m=2, perp=30.0, iters=500, lr=200.0, r=None):
n = len(X)
D = d2(X, X)
P = np.zeros((n, n))
for i in range(n):
lo, hi = 1e-8, 1e8
for _ in range(60):
beta = 0.5 * (lo + hi)
w = np.exp(-D[i] * beta)
w[i] = 0.0
sw = w.sum() + 1e-12
pj = w / sw
H = -np.sum(pj[pj > 0] * np.log(pj[pj > 0]))
if H > np.log(perp):
lo = beta
else:
hi = beta
P[i] = pj
P = (P + P.T) / (2 * n)
P = np.maximum(P, 1e-12)
Y = r.normal(0, 1e-2, (n, m))
gains = np.ones((n, m))
vel = np.zeros((n, m))
for it in range(iters):
DY = d2(Y, Y)
W = 1.0 / (1.0 + DY)
np.fill_diagonal(W, 0.0)
Q = np.maximum(W / W.sum(), 1e-12)
pq = (P * 4.0 if it < 100 else P) - Q
G = 4.0 * (((pq * W)[:, :, None]
* (Y[:, None, :] - Y[None, :, :])).sum(axis=1))
mom = 0.5 if it < 100 else 0.8
gains = np.where(np.sign(G) != np.sign(vel), gains + 0.2, gains * 0.8)
gains = np.maximum(gains, 0.01)
vel = mom * vel - lr * gains * G
Y = Y + vel
Y = Y - Y.mean(axis=0)
return Y
def kmeans(X, k, r, tries=10):
best = None
for _ in range(tries):
C = X[r.permutation(len(X))[:k]].copy()
lab = np.zeros(len(X), dtype=np.int64)
for _ in range(150):
new = np.argmin(d2(X, C), axis=1)
if np.array_equal(new, lab):
break
lab = new
for j in range(k):
mm = lab == j
if mm.any():
C[j] = X[mm].mean(axis=0)
w = float(np.sum(np.min(d2(X, C), axis=1)))
if best is None or w < best[1]:
best = (lab, w)
return best[0]
def purity(lab, z, k):
tot = 0
for j in range(k):
mm = lab == j
if mm.any():
tot += np.bincount(z[mm]).max()
return float(tot / len(z))
def trust(Xh, Yl, k=12):
n = len(Xh)
DH = d2(Xh, Xh)
DL = d2(Yl, Yl)
np.fill_diagonal(DH, np.inf)
np.fill_diagonal(DL, np.inf)
rh = np.argsort(np.argsort(DH, axis=1), axis=1)
nl = np.argsort(DL, axis=1)[:, :k]
s = 0.0
for i in range(n):
for j in nl[i]:
if rh[i, j] >= k:
s += rh[i, j] - k + 1
return float(1.0 - 2.0 * s / (n * k * (2 * n - 3 * k - 1)))
# --- 문제 1: 축을 갈아탑니다 --------------------------------------------
print(" 221강부터 223강까지는 점들을 나눴습니다")
print(" 이번에는 나누지 않고 같은 자료를 더 적은 차원으로 적습니다")
print(" %s %s %s"
% (pw("무엇", 22), rw("군집", 22), rw("차원 축소", 24)))
for a, b, c in [("무엇을 만드나", "덩어리 표시", "새 좌표"),
("점의 개수", "그대로", "그대로"),
("변수의 개수", "그대로", "줄어듦"),
("무엇을 지키나", "가까운 것끼리", "분산이나 거리"),
("다음 단계", "덩어리별 분석", "무엇이든")]:
print(" %s %s %s" % (pw(a, 22), rw(b, 22), rw(c, 24)))
print(" 88강에서 주성분을 고유분해로 유도했습니다. 여기서는 쓰는 법을 봅니다")
print(" 분산이 가장 큰 방향부터 차례로 고릅니다")
print(" 분산이 어디에 얼마나 있는지 봅니다")
n1, d1 = 500, 20
B = rng.normal(0, 1, (3, d1))
t1 = rng.normal(0, 1, (n1, 3)) * np.array([3.0, 1.5, 0.6])
X1 = t1 @ B + rng.normal(0, 0.3, (n1, d1))
mu1, V1, S1 = pca(X1, d1)
ev = S1 ** 2 / (n1 - 1)
print(" 변수 20 개인데 실제로는 3 차원에서 만든 자료입니다")
print(" %s %s %s %s"
% (pw("성분", 10), rw("고유값", 18), rw("설명하는 비율", 20),
rw("누적 비율", 18)))
cum = np.cumsum(ev) / ev.sum()
for i in [0, 1, 2, 3, 5, 10, 19]:
print(" %s %18.6f %20.6f %18.6f"
% (pw(str(i + 1), 10), ev[i], ev[i] / ev.sum(), cum[i]))
print(" 처음 세 성분이 대부분을 설명하고 나머지는 잡음입니다")
print(" 누적 비율이 어디서 평평해지는지가 실제 차원의 눈짐작입니다")
print(" 221강의 팔꿈치와 같은 종류의 그림입니다")
print(" 몇 차원으로 줄여야 얼마나 잃는지 잽니다")
print(" %s %s %s %s"
% (pw("줄인 차원", 12), rw("복원 오차", 18), rw("남긴 분산 비율", 20),
rw("원래 거리와의 상관", 22)))
DH = np.sqrt(d2(X1, X1))
iu = np.triu_indices(n1, 1)
for m in [1, 2, 3, 5, 10, 20]:
mu, V, _ = pca(X1, m)
Z = (X1 - mu) @ V.T
Xr = Z @ V + mu
err = float(np.mean(((X1 - Xr) ** 2).sum(axis=1)))
DL = np.sqrt(d2(Z, Z))
co = float(np.corrcoef(DH[iu], DL[iu])[0, 1])
print(" %s %18.6f %20.6f %22.6f"
% (pw(str(m), 12), err, float(cum[m - 1]), co))
print(" 3 차원까지 줄여도 복원 오차가 작고 거리 상관이 0.99 를 넘습니다")
print(" 그 뒤로는 더 줄여도 얻는 것이 적고 잃는 것만 큽니다")
print(" 84강의 특이값 분해가 최적 근사를 준다는 것이 여기서 쓰입니다")
# --- 문제 2: 무엇을 지키고 무엇을 잃는가 --------------------------------
print(" 주성분이 무엇을 지키는지 정확히 봅니다")
print(" %s %s %s"
% (pw("무엇", 24), rw("지키나", 16), rw("왜", 28)))
for a, b, c in [("전체 분산", "가장 많이", "그것을 최대화함"),
("먼 점들의 거리", "잘", "큰 방향을 남김"),
("가까운 점들의 거리", "잘 못함", "작은 방향을 버림"),
("갈래를 가르는 방향", "보장 없음", "분산과 무관"),
("굽은 구조", "못 함", "직선 사영만")]:
print(" %s %s %s" % (pw(a, 24), rw(b, 16), rw(c, 28)))
print(" 넷째 줄이 실무에서 가장 자주 문제가 됩니다")
print(" 갈래를 가르는 방향이 분산이 작을 수 있음을 봅니다")
n2 = 600
z2 = rng.integers(0, 2, n2)
X2 = np.zeros((n2, 2))
X2[:, 0] = rng.normal(0, 5.0, n2)
X2[:, 1] = np.where(z2 == 1, 1.0, -1.0) + rng.normal(0, 0.35, n2)
print(" 첫 축은 분산이 크지만 갈래와 무관하고 둘째 축이 갈래를 가릅니다")
mu2, V2, S2 = pca(X2, 2)
ev2 = S2 ** 2 / (n2 - 1)
print(" %s %s %s %s"
% (pw("축", 22), rw("분산", 16), rw("갈래와의 상관 제곱", 24),
rw("그 축만으로 정확도", 22)))
Z2 = (X2 - mu2) @ V2.T
for i in [0, 1]:
r2 = float(np.corrcoef(Z2[:, i], z2.astype(float))[0, 1] ** 2)
thr = np.median(Z2[:, i])
ac = max(float(np.mean((Z2[:, i] > thr) == (z2 == 1))),
float(np.mean((Z2[:, i] > thr) == (z2 == 0))))
print(" %s %16.6f %24.6f %22.6f"
% (pw("주성분 %d" % (i + 1), 22), ev2[i], r2, ac))
print(" 첫 성분이 분산을 거의 다 가져가는데 갈래와의 상관은 0 에 가깝습니다")
print(" 한 차원으로 줄이면 갈래 정보를 통째로 버립니다")
print(" 주성분은 라벨을 안 보므로 이런 일이 생깁니다")
print(" 라벨을 보는 축과 견줍니다")
m0 = X2[z2 == 0].mean(axis=0)
m1 = X2[z2 == 1].mean(axis=0)
Sw = np.cov(X2[z2 == 0].T) + np.cov(X2[z2 == 1].T)
wl = np.linalg.solve(Sw + 1e-9 * np.eye(2), m1 - m0)
wl = wl / np.linalg.norm(wl)
pl = X2 @ wl
print(" %s %s %s"
% (pw("어떤 축", 26), rw("갈래와의 상관 제곱", 24),
rw("그 축만으로 정확도", 22)))
r2p = float(np.corrcoef(Z2[:, 0], z2.astype(float))[0, 1] ** 2)
acp = max(float(np.mean((Z2[:, 0] > np.median(Z2[:, 0])) == (z2 == 1))),
float(np.mean((Z2[:, 0] > np.median(Z2[:, 0])) == (z2 == 0))))
r2l = float(np.corrcoef(pl, z2.astype(float))[0, 1] ** 2)
acl = max(float(np.mean((pl > np.median(pl)) == (z2 == 1))),
float(np.mean((pl > np.median(pl)) == (z2 == 0))))
print(" %s %24.6f %22.6f" % (pw("주성분 1", 26), r2p, acp))
print(" %s %24.6f %22.6f" % (pw("선형판별 축", 26), r2l, acl))
print(" 선형판별은 갈래 사이 거리를 갈래 안 흩어짐으로 나눈 것을 최대로 합니다")
print(" 라벨이 있으면 주성분보다 선형판별이 낫습니다")
print(" 라벨이 없을 때만 주성분을 쓰는 이유가 이것입니다")
print(" 눈금이 답을 바꾸는 것도 봅니다")
print(" %s %s %s"
% (pw("둘째 변수 배율", 18), rw("첫 성분이 첫 변수에 준 무게", 30),
rw("첫 성분의 분산 비율", 24)))
for sc in [1.0, 3.0, 10.0]:
A = X1 * np.concatenate([[1.0], np.full(d1 - 1, sc)])
mu, V, S = pca(A, d1)
e = S ** 2 / (n1 - 1)
print(" %s %30.6f %24.6f"
% (pw("%.0f" % sc, 18), float(abs(V[0, 0])), float(e[0] / e.sum())))
print(" 배율을 키우면 그 변수들이 첫 성분을 독차지합니다")
print(" 단위가 다른 변수를 섞을 때는 표준화가 필수입니다")
print(" 상관행렬로 주성분을 하는 것이 곧 표준화한 것입니다")
# --- 문제 3: 굽은 구조는 못 폅니다 --------------------------------------
print(" 굽은 구조에서 무엇이 안 되는지 봅니다")
n3 = 500
t3 = rng.uniform(1.5 * np.pi, 4.5 * np.pi, n3)
h3 = rng.uniform(0, 10, n3)
X3 = np.stack([t3 * np.cos(t3), h3, t3 * np.sin(t3)], axis=1)
z3 = (t3 > 3.0 * np.pi).astype(np.int64)
print(" 말린 종이 모양 자료입니다. 참으로는 2 차원입니다")
mu3, V3, S3 = pca(X3, 3)
ev3 = S3 ** 2 / (n3 - 1)
Z3 = (X3 - mu3) @ V3.T
DH3 = np.sqrt(d2(X3, X3))
np.fill_diagonal(DH3, np.inf)
nnH = np.argsort(DH3, axis=1)[:, :10]
DL3 = np.sqrt(d2(Z3[:, :2], Z3[:, :2]))
np.fill_diagonal(DL3, np.inf)
nnL = np.argsort(DL3, axis=1)[:, :10]
keep3 = float(np.mean([len(set(nnH[i]) & set(nnL[i])) / 10.0
for i in range(n3)]))
fake = float(np.mean([sum(1 for j in nnL[i] if j not in set(nnH[i]))
/ 10.0 for i in range(n3)]))
rkL = np.argsort(np.argsort(DL3, axis=1), axis=1)
rank_of_true = float(np.median([rkL[i, j] for i in range(n3)
for j in nnH[i]]))
print(" %s %s %s"
% (pw("무엇", 30), rw("값", 20), rw("무엇을 뜻하나", 26)))
print(" %s %20.6f %s"
% (pw("첫 두 성분의 분산 비율", 30),
float((ev3[0] + ev3[1]) / ev3.sum()), rw("거의 다 설명", 26)))
print(" %s %20.6f %s"
% (pw("이웃 10 개가 남는 비율", 30), keep3, rw("절반도 안 남음", 26)))
print(" %s %20.6f %s"
% (pw("새로 생긴 가짜 이웃 비율", 30), fake, rw("겹쳐 온 점들", 26)))
print(" %s %20.6f %s"
% (pw("참 이웃의 2 차원 순위 중앙값", 30), rank_of_true,
rw("10 위 밖으로 밀림", 26)))
print(" 분산은 0.91 을 설명하는데 이웃은 0.42 밖에 안 남습니다")
print(" 분산을 잘 지키는 것과 이웃을 잘 지키는 것이 다른 일입니다")
print(" 말린 것을 그대로 눌러 층끼리 겹치게 만들기 때문입니다")
print(" 213강 문제 4 의 굽은 띠와 같은 문제입니다")
print(" 펴 놓은 좌표를 알면 얼마나 다른지 봅니다")
tt = np.stack([t3 / t3.std(), h3 / h3.std()], axis=1)
DT = np.sqrt(d2(tt, tt))
np.fill_diagonal(DT, np.inf)
nnT = np.argsort(DT, axis=1)[:, :10]
print(" %s %s %s"
% (pw("어떤 좌표", 26), rw("말린 축 t 와의 상관 제곱", 28),
rw("갈래 순도", 16)))
c1 = max(float(np.corrcoef(Z3[:, i], t3)[0, 1] ** 2) for i in range(2))
c2 = float(np.corrcoef(tt[:, 0], t3)[0, 1] ** 2)
print(" %s %28.6f %16.6f"
% (pw("주성분 2 차원", 26), c1,
purity(kmeans(Z3[:, :2], 2, rng), z3, 2)))
print(" %s %28.6f %16.6f"
% (pw("펴 놓은 좌표", 26), c2, purity(kmeans(tt, 2, rng), z3, 2)))
print(" 주성분 축은 말린 축 t 와 상관이 낮습니다. 감긴 방향을 못 폅니다")
print(" 갈래 순도는 둘 다 낮습니다. 높이 축이 함께 있어 k 평균이 그쪽으로 자릅니다")
print(" 중요한 것은 순도가 아니라 감긴 축을 복원했느냐입니다")
print(" 그래서 이웃을 지키는 것을 목표로 삼는 방법이 필요합니다")
print(" 이웃을 지키는 방법이 무엇을 다르게 하는지 정리합니다")
print(" %s %s %s"
% (pw("무엇", 20), rw("무엇을 최소화", 26), rw("성질", 24)))
for a, b, c in [("주성분", "복원 오차", "직선이고 뒤집을 수 있음"),
("다차원 척도법", "거리의 차", "먼 거리를 중시"),
("t-SNE", "이웃 분포의 KL", "가까운 것만 중시"),
("UMAP", "이웃 그래프의 교차엔트로피", "전체 구조를 더 지킴")]:
print(" %s %s %s" % (pw(a, 20), rw(b, 26), rw(c, 24)))
print(" t-SNE 는 고차원에서 이웃일 확률과 저차원에서 이웃일 확률을 맞춥니다")
print(" 203강의 KL 발산이 목적함수입니다")
print(" 말린 자료에 t-SNE 를 걸어 봅니다")
idx = rng.permutation(n3)[:300]
Xs = X3[idx]
zs = z3[idx]
Y3 = tsne(Xs, 2, 30.0, 400, 200.0, rng)
mu4, V4, _ = pca(Xs, 2)
Zp = (Xs - mu4) @ V4.T
DHs = np.sqrt(d2(Xs, Xs))
np.fill_diagonal(DHs, np.inf)
nnHs = np.argsort(DHs, axis=1)[:, :10]
print(" %s %s %s %s"
% (pw("무엇", 20), rw("이웃 10 개가 남는 비율", 26), rw("신뢰도", 14),
rw("2 차원에서의 순도", 22)))
for nm, Y in [("주성분", Zp), ("t-SNE", Y3)]:
DLs = np.sqrt(d2(Y, Y))
np.fill_diagonal(DLs, np.inf)
nnLs = np.argsort(DLs, axis=1)[:, :10]
keep = float(np.mean([len(set(nnHs[i]) & set(nnLs[i])) / 10.0
for i in range(300)]))
print(" %s %26.6f %14.6f %22.6f"
% (pw(nm, 20), keep, trust(Xs, Y, 10),
purity(kmeans(Y, 2, rng), zs, 2)))
print(" t-SNE 가 이웃을 훨씬 많이 지킵니다. 0.547000 에서 0.872333 으로 오릅니다")
print(" 그런데 2 차원에서 k 평균으로 자른 순도는 오히려 조금 낮습니다")
print(" 이웃을 지키는 것과 갈래를 가르는 것은 다른 목표입니다")
print(" t-SNE 는 말린 구조를 펴서 이어진 띠로 만들 뿐 두 동강 내지 않습니다")
print(" 대신 뒤집을 수 없고 새 점을 넣을 수 없습니다")
print(" 주성분은 행렬 하나라 새 점을 그냥 곱하면 됩니다")
print(" t-SNE 의 손잡이가 무엇을 바꾸는지 봅니다")
print(" %s %s %s %s"
% (pw("복잡도", 12), rw("이웃 10 개가 남는 비율", 26), rw("신뢰도", 14),
rw("덩어리 사이 평균 거리", 24)))
Xs2 = np.vstack([rng.normal(0, 0.5, (100, 5)),
rng.normal(0, 0.5, (100, 5)) + np.array([4., 0, 0, 0, 0]),
rng.normal(0, 0.5, (100, 5)) + np.array([0, 4., 0, 0, 0])])
zs2 = np.array([0] * 100 + [1] * 100 + [2] * 100)
DH2 = np.sqrt(d2(Xs2, Xs2))
np.fill_diagonal(DH2, np.inf)
nnH2 = np.argsort(DH2, axis=1)[:, :10]
for pp in [5.0, 30.0, 80.0]:
Yp = tsne(Xs2, 2, pp, 400, 200.0, rng)
DL2 = np.sqrt(d2(Yp, Yp))
np.fill_diagonal(DL2, np.inf)
nnL2 = np.argsort(DL2, axis=1)[:, :10]
keep = float(np.mean([len(set(nnH2[i]) & set(nnL2[i])) / 10.0
for i in range(300)]))
cen = np.stack([Yp[zs2 == j].mean(axis=0) for j in range(3)])
gap = float(np.sqrt(d2(cen, cen))[np.triu_indices(3, 1)].mean())
print(" %s %26.6f %14.6f %24.6f"
% (pw("%.0f" % pp, 12), keep, trust(Xs2, Yp, 10), gap))
print(" 복잡도가 작으면 아주 가까운 이웃만 봅니다")
print(" 복잡도가 크면 더 넓게 봐서 전체 배치가 달라집니다")
print(" 덩어리 사이 거리가 손잡이에 따라 크게 바뀝니다. 그 거리를 믿으면 안 됩니다")
# --- 문제 5: 실무에서 쓰기 ---------------------------------------------
print(" 차원 축소를 무엇에 쓰는지 정리합니다")
print(" %s %s %s"
% (pw("쓰임", 22), rw("무엇을 쓰나", 24), rw("어느 방법", 24)))
for a, b, c in [("잡음 줄이기", "작은 성분 버림", "주성분"),
("계산 줄이기", "적은 차원으로", "주성분이나 무작위 사영"),
("그림 그리기", "2 차원 배치", "t-SNE 나 UMAP"),
("특성 만들기", "새 좌표를 변수로", "주성분"),
("압축", "복원 가능해야 함", "주성분")]:
print(" %s %s %s" % (pw(a, 22), rw(b, 24), rw(c, 24)))
print(" 셋째 줄만 t-SNE 이고 나머지는 주성분입니다. 뒤집을 수 있어야 하기 때문입니다")
print(" 주성분으로 잡음을 줄이면 예측이 좋아지는지 봅니다")
n5 = 400
B5 = rng.normal(0, 1, (4, 40))
t5 = rng.normal(0, 1, (n5, 4))
X5 = t5 @ B5 + rng.normal(0, 1.5, (n5, 40))
w5 = rng.normal(0, 1, 4)
y5 = t5 @ w5 + rng.normal(0, 0.5, n5)
t5t = rng.normal(0, 1, (2000, 4))
X5t = t5t @ B5 + rng.normal(0, 1.5, (2000, 40))
y5t = t5t @ w5 + rng.normal(0, 0.5, 2000)
def ols_err(A, ya, B_, yb):
w = np.linalg.pinv(A) @ ya
return float(np.mean((yb - B_ @ w) ** 2))
print(" 변수 40 개인데 참 신호는 4 차원입니다")
print(" %s %s %s"
% (pw("몇 성분을 쓰나", 18), rw("검증 오차", 18), rw("남긴 분산 비율", 22)))
mu5, V5, S5 = pca(X5, 40)
cum5 = np.cumsum(S5 ** 2) / (S5 ** 2).sum()
print(" %s %18.6f %22.6f"
% (pw("전부 (40 개)", 18), ols_err(X5, y5, X5t, y5t), 1.0))
for m in [2, 4, 8, 20]:
Zt = (X5 - mu5) @ V5[:m].T
Zv = (X5t - mu5) @ V5[:m].T
print(" %s %18.6f %22.6f"
% (pw(str(m), 18), ols_err(Zt, y5, Zv, y5t), float(cum5[m - 1])))
print(" 성분 4 개만 쓰면 40 개 다 쓸 때보다 검증 오차가 작습니다")
print(" 버린 36 개가 잡음이었기 때문입니다. 211강의 정규화와 같은 효과입니다")
print(" 다만 주성분은 y 를 안 보므로 쓸 성분을 버릴 수도 있습니다")
print(" 주성분이 쓸 성분을 버리는 예를 봅니다")
n6 = 400
X6 = np.zeros((n6, 2))
X6[:, 0] = rng.normal(0, 6.0, n6)
X6[:, 1] = rng.normal(0, 0.5, n6)
y6 = X6[:, 1] * 3.0 + rng.normal(0, 0.3, n6)
X6t = np.stack([rng.normal(0, 6.0, 2000), rng.normal(0, 0.5, 2000)], axis=1)
y6t = X6t[:, 1] * 3.0 + rng.normal(0, 0.3, 2000)
mu6, V6, S6 = pca(X6, 2)
print(" y 가 분산이 작은 둘째 변수에만 딸린 자료입니다")
print(" %s %s %s"
% (pw("무엇을 쓰나", 22), rw("검증 오차", 18), rw("남긴 분산 비율", 22)))
e6 = S6 ** 2
print(" %s %18.6f %22.6f"
% (pw("두 변수 다", 22), ols_err(X6, y6, X6t, y6t), 1.0))
Z6 = (X6 - mu6) @ V6[:1].T
Z6t = (X6t - mu6) @ V6[:1].T
print(" %s %18.6f %22.6f"
% (pw("첫 성분만", 22), ols_err(Z6, y6, Z6t, y6t),
float(e6[0] / e6.sum())))
print(" 분산의 99 퍼센트를 남겼는데 예측이 완전히 무너집니다")
print(" 207강 문제 3 에서 정보가 있는 방향과 분산이 큰 방향이 다르다고 했습니다")
print(" 부분최소제곱은 y 를 보면서 축을 고르므로 이 문제가 없습니다")
print(" t-SNE 그림을 읽을 때 조심할 것을 정리합니다")
print(" %s %s"
% (pw("흔한 오해", 30), rw("실제로는", 36)))
for a, b in [("덩어리 크기가 뜻이 있다", "손잡이가 정함"),
("덩어리 사이 거리가 뜻이 있다", "거의 뜻 없음"),
("모양이 뜻이 있다", "돌려도 같은 그림"),
("덩어리가 보이면 군집이 있다", "없어도 보임")]:
print(" %s %s" % (pw(a, 30), rw(b, 36)))
print(" 군집이 없는 자료에 t-SNE 를 걸어 봅니다")
Xr7 = rng.normal(0, 1, (250, 10))
Y7 = tsne(Xr7, 2, 20.0, 400, 200.0, rng)
lab7 = kmeans(Y7, 4, rng)
cnt7 = sorted([int((lab7 == j).sum()) for j in range(4)])
DL7 = np.sqrt(d2(Y7, Y7))
print(" 10 차원 표준정규에서 뽑은 자료입니다. 군집이 없습니다")
print(" %s %s"
% (pw("무엇", 30), rw("값", 24)))
print(" %s %24s" % (pw("4 조각으로 나눈 크기", 30),
rw(" ".join(str(v) for v in cnt7), 24)))
print(" %s %24.6f" % (pw("2 차원에서의 실루엣 비슷한 값", 30),
float(np.mean(np.sort(DL7, axis=1)[:, 1:6].mean(axis=1)
/ DL7.mean()))))
print(" %s %24.6f" % (pw("고차원에서 같은 값", 30),
float(np.mean(np.sort(np.sqrt(d2(Xr7, Xr7)),
axis=1)[:, 1:6].mean(axis=1)
/ np.sqrt(d2(Xr7, Xr7)).mean()))))
print(" 군집이 없는데도 그림에는 조각이 생깁니다")
print(" 가까운 이웃과 전체 평균 거리의 비가 2 차원에서 훨씬 작아집니다")
print(" 213강의 거리 집중이 t-SNE 를 지나며 인위적으로 풀린 것입니다")
print(" 224강은 축을 갈아탔습니다. 225강은 드문 것을 찾습니다")
# 221강부터 223강까지는 점들을 나눴습니다
# 이번에는 나누지 않고 같은 자료를 더 적은 차원으로 적습니다
# 무엇 군집 차원 축소
# 무엇을 만드나 덩어리 표시 새 좌표
# 점의 개수 그대로 그대로
# 변수의 개수 그대로 줄어듦
# 무엇을 지키나 가까운 것끼리 분산이나 거리
# 다음 단계 덩어리별 분석 무엇이든
# 88강에서 주성분을 고유분해로 유도했습니다. 여기서는 쓰는 법을 봅니다
# 분산이 가장 큰 방향부터 차례로 고릅니다
# 분산이 어디에 얼마나 있는지 봅니다
# 변수 20 개인데 실제로는 3 차원에서 만든 자료입니다
# 성분 고유값 설명하는 비율 누적 비율
# 1 193.541202 0.814647 0.814647
# 2 40.525439 0.170578 0.985225
# 3 1.989455 0.008374 0.993599
# 4 0.119526 0.000503 0.994102
# 6 0.106502 0.000448 0.995027
# 11 0.094387 0.000397 0.997113
# 20 0.064527 0.000272 1.000000
# 처음 세 성분이 대부분을 설명하고 나머지는 잡음입니다
# 누적 비율이 어디서 평평해지는지가 실제 차원의 눈짐작입니다
# 221강의 팔꿈치와 같은 종류의 그림입니다
# 몇 차원으로 줄여야 얼마나 잃는지 잽니다
# 줄인 차원 복원 오차 남긴 분산 비율 원래 거리와의 상관
# 1 43.947574 0.814647 0.949933
# 2 3.503187 0.985225 0.999598
# 3 1.517710 0.993599 0.999965
# 5 1.285307 0.994579 0.999974
# 10 0.778774 0.996715 0.999989
# 20 0.000000 1.000000 1.000000
# 3 차원까지 줄여도 복원 오차가 작고 거리 상관이 0.99 를 넘습니다
# 그 뒤로는 더 줄여도 얻는 것이 적고 잃는 것만 큽니다
# 84강의 특이값 분해가 최적 근사를 준다는 것이 여기서 쓰입니다
# 주성분이 무엇을 지키는지 정확히 봅니다
# 무엇 지키나 왜
# 전체 분산 가장 많이 그것을 최대화함
# 먼 점들의 거리 잘 큰 방향을 남김
# 가까운 점들의 거리 잘 못함 작은 방향을 버림
# 갈래를 가르는 방향 보장 없음 분산과 무관
# 굽은 구조 못 함 직선 사영만
# 넷째 줄이 실무에서 가장 자주 문제가 됩니다
# 갈래를 가르는 방향이 분산이 작을 수 있음을 봅니다
# 첫 축은 분산이 크지만 갈래와 무관하고 둘째 축이 갈래를 가릅니다
# 축 분산 갈래와의 상관 제곱 그 축만으로 정확도
# 주성분 1 24.869728 0.000895 0.515000
# 주성분 2 1.130520 0.888502 0.988333
# 첫 성분이 분산을 거의 다 가져가는데 갈래와의 상관은 0 에 가깝습니다
# 한 차원으로 줄이면 갈래 정보를 통째로 버립니다
# 주성분은 라벨을 안 보므로 이런 일이 생깁니다
# 라벨을 보는 축과 견줍니다
# 어떤 축 갈래와의 상관 제곱 그 축만으로 정확도
# 주성분 1 0.000895 0.515000
# 선형판별 축 0.889396 0.988333
# 선형판별은 갈래 사이 거리를 갈래 안 흩어짐으로 나눈 것을 최대로 합니다
# 라벨이 있으면 주성분보다 선형판별이 낫습니다
# 라벨이 없을 때만 주성분을 쓰는 이유가 이것입니다
# 눈금이 답을 바꾸는 것도 봅니다
# 둘째 변수 배율 첫 성분이 첫 변수에 준 무게 첫 성분의 분산 비율
# 1 0.094281 0.814647
# 3 0.030414 0.833573
# 10 0.009090 0.835830
# 배율을 키우면 그 변수들이 첫 성분을 독차지합니다
# 단위가 다른 변수를 섞을 때는 표준화가 필수입니다
# 상관행렬로 주성분을 하는 것이 곧 표준화한 것입니다
# 굽은 구조에서 무엇이 안 되는지 봅니다
# 말린 종이 모양 자료입니다. 참으로는 2 차원입니다
# 무엇 값 무엇을 뜻하나
# 첫 두 성분의 분산 비율 0.917569 거의 다 설명
# 이웃 10 개가 남는 비율 0.418400 절반도 안 남음
# 새로 생긴 가짜 이웃 비율 0.581600 겹쳐 온 점들
# 참 이웃의 2 차원 순위 중앙값 11.000000 10 위 밖으로 밀림
# 분산은 0.91 을 설명하는데 이웃은 0.42 밖에 안 남습니다
# 분산을 잘 지키는 것과 이웃을 잘 지키는 것이 다른 일입니다
# 말린 것을 그대로 눌러 층끼리 겹치게 만들기 때문입니다
# 213강 문제 4 의 굽은 띠와 같은 문제입니다
# 펴 놓은 좌표를 알면 얼마나 다른지 봅니다
# 어떤 좌표 말린 축 t 와의 상관 제곱 갈래 순도
# 주성분 2 차원 0.054840 0.554000
# 펴 놓은 좌표 1.000000 0.522000
# 주성분 축은 말린 축 t 와 상관이 낮습니다. 감긴 방향을 못 폅니다
# 갈래 순도는 둘 다 낮습니다. 높이 축이 함께 있어 k 평균이 그쪽으로 자릅니다
# 중요한 것은 순도가 아니라 감긴 축을 복원했느냐입니다
# 그래서 이웃을 지키는 것을 목표로 삼는 방법이 필요합니다
# 이웃을 지키는 방법이 무엇을 다르게 하는지 정리합니다
# 무엇 무엇을 최소화 성질
# 주성분 복원 오차 직선이고 뒤집을 수 있음
# 다차원 척도법 거리의 차 먼 거리를 중시
# t-SNE 이웃 분포의 KL 가까운 것만 중시
# UMAP 이웃 그래프의 교차엔트로피 전체 구조를 더 지킴
# t-SNE 는 고차원에서 이웃일 확률과 저차원에서 이웃일 확률을 맞춥니다
# 203강의 KL 발산이 목적함수입니다
# 말린 자료에 t-SNE 를 걸어 봅니다
# 무엇 이웃 10 개가 남는 비율 신뢰도 2 차원에서의 순도
# 주성분 0.547000 0.969786 0.606667
# t-SNE 0.872333 0.997665 0.580000
# t-SNE 가 이웃을 훨씬 많이 지킵니다. 0.547000 에서 0.872333 으로 오릅니다
# 그런데 2 차원에서 k 평균으로 자른 순도는 오히려 조금 낮습니다
# 이웃을 지키는 것과 갈래를 가르는 것은 다른 목표입니다
# t-SNE 는 말린 구조를 펴서 이어진 띠로 만들 뿐 두 동강 내지 않습니다
# 대신 뒤집을 수 없고 새 점을 넣을 수 없습니다
# 주성분은 행렬 하나라 새 점을 그냥 곱하면 됩니다
# t-SNE 의 손잡이가 무엇을 바꾸는지 봅니다
# 복잡도 이웃 10 개가 남는 비율 신뢰도 덩어리 사이 평균 거리
# 5 0.540000 0.966548 57.356876
# 30 0.580333 0.974275 38.137354
# 80 0.434000 0.952778 23.503003
# 복잡도가 작으면 아주 가까운 이웃만 봅니다
# 복잡도가 크면 더 넓게 봐서 전체 배치가 달라집니다
# 덩어리 사이 거리가 손잡이에 따라 크게 바뀝니다. 그 거리를 믿으면 안 됩니다
# 차원 축소를 무엇에 쓰는지 정리합니다
# 쓰임 무엇을 쓰나 어느 방법
# 잡음 줄이기 작은 성분 버림 주성분
# 계산 줄이기 적은 차원으로 주성분이나 무작위 사영
# 그림 그리기 2 차원 배치 t-SNE 나 UMAP
# 특성 만들기 새 좌표를 변수로 주성분
# 압축 복원 가능해야 함 주성분
# 셋째 줄만 t-SNE 이고 나머지는 주성분입니다. 뒤집을 수 있어야 하기 때문입니다
# 주성분으로 잡음을 줄이면 예측이 좋아지는지 봅니다
# 변수 40 개인데 참 신호는 4 차원입니다
# 몇 성분을 쓰나 검증 오차 남긴 분산 비율
# 전부 (40 개) 0.345778 1.000000
# 2 0.494179 0.420608
# 4 0.320365 0.674051
# 8 0.321844 0.728757
# 20 0.325333 0.861720
# 성분 4 개만 쓰면 40 개 다 쓸 때보다 검증 오차가 작습니다
# 버린 36 개가 잡음이었기 때문입니다. 211강의 정규화와 같은 효과입니다
# 다만 주성분은 y 를 안 보므로 쓸 성분을 버릴 수도 있습니다
# 주성분이 쓸 성분을 버리는 예를 봅니다
# y 가 분산이 작은 둘째 변수에만 딸린 자료입니다
# 무엇을 쓰나 검증 오차 남긴 분산 비율
# 두 변수 다 0.089797 1.000000
# 첫 성분만 2.307059 0.992881
# 분산의 99 퍼센트를 남겼는데 예측이 완전히 무너집니다
# 207강 문제 3 에서 정보가 있는 방향과 분산이 큰 방향이 다르다고 했습니다
# 부분최소제곱은 y 를 보면서 축을 고르므로 이 문제가 없습니다
# t-SNE 그림을 읽을 때 조심할 것을 정리합니다
# 흔한 오해 실제로는
# 덩어리 크기가 뜻이 있다 손잡이가 정함
# 덩어리 사이 거리가 뜻이 있다 거의 뜻 없음
# 모양이 뜻이 있다 돌려도 같은 그림
# 덩어리가 보이면 군집이 있다 없어도 보임
# 군집이 없는 자료에 t-SNE 를 걸어 봅니다
# 10 차원 표준정규에서 뽑은 자료입니다. 군집이 없습니다
# 무엇 값
# 4 조각으로 나눈 크기 56 59 62 73
# 2 차원에서의 실루엣 비슷한 값 0.132690
# 고차원에서 같은 값 0.557972
# 군집이 없는데도 그림에는 조각이 생깁니다
# 가까운 이웃과 전체 평균 거리의 비가 2 차원에서 훨씬 작아집니다
# 213강의 거리 집중이 t-SNE 를 지나며 인위적으로 풀린 것입니다
# 224강은 축을 갈아탔습니다. 225강은 드문 것을 찾습니다