강에서 층 하나가 배타적 논리합을 풀었고, 강에서 활성함수가 표현력을 준다는 것을 봤습니다. 그러면 자연스러운 물음이 남습니다.
답은 놀랍습니다. 연속함수라면 무엇이든 임의의 정밀도로 근사할 수 있습니다. 층이 하나면 됩니다.
몇 개가 필요한지, 학습이 그것을 찾는지, 구간 밖에서 어떻게 되는지 모두 침묵합니다. 이 강의는 정리가 말하는 것과 안 말하는 것을 수치로 가릅니다.
문제. 진술을 봅니다.
() 정리하세요.
() 계단으로 손으로 근사하세요.
() 실제로 가중치를 정해 확인하세요.
() 정리가 안 말하는 것을 정리하세요.
생각의 실마리. 연속함수는 좁은 구간에서 거의 상수입니다. 그러면 구간을 잘게 쪼개고 각 조각에서 상수로 맞추면 됩니다. 계단을 쌓는 것입니다.
풀이. () 정리합니다.
| 무엇 | 무엇인가 | 덧붙임 |
|---|---|---|
| 무엇을 근사하나 | 닫힌 유계 구간의 연속함수 | 옹골 집합 위에서 |
| 무엇으로 | 은닉층 하나짜리 신경망 | 층 하나면 충분 |
| 얼마나 가깝게 | 임의의 양수 오차 안으로 | 최대 오차 기준 |
| 무엇이 필요한가 | 활성함수가 다항식이 아니면 됨 | 아주 약한 조건 |
넷째 줄이 뜻밖입니다. 조건이 아주 약합니다. 강에서 활성함수가 없으면 안 된다고 했는데, 그것이 정확히 이 조건입니다. 다항식이면 층을 쌓아도 다항식 하나로 접힙니다.
() 계단으로 손으로 근사합니다. 목표는 입니다.
| 조각 수 | 필요한 정류 선형 단위 | 최대 오차 | 앞 대비 |
|---|---|---|---|
| 기준 | |||
조각을 두 배로 하면 오차가 정확히 절반이 됩니다. 넷째 열이 에 수렴합니다.
계단 하나는 정류 선형 두 개로 만듭니다. 오르는 것 하나와 내리는 것 하나입니다.
그래서 단위를 늘리면 언제든 원하는 오차 안으로 들어갑니다. 이것이 정리의 증명 아이디어입니다.
() 실제로 가중치를 정해 확인합니다. 은닉 개로 계단 개를 만듭니다.
| 목표값 | 신경망 출력 | 차이 | |
|---|---|---|---|
전체 구간 최대 오차는 입니다.
학습을 하나도 안 하고 가중치를 직접 적어 만든 것입니다. 정리는 이런 구성이 존재한다는 것만 말합니다.
() 정리가 안 말하는 것을 정리합니다.
| 물음 | 정리가 답하나 | 덧붙임 |
|---|---|---|
| 단위가 몇 개 필요한가 | 안 말함 | 지수일 수도 |
| 학습으로 찾을 수 있나 | 안 말함 | 존재만 말함 |
| 구간 밖에서는 | 안 말함 | 유계 구간 위에서만 |
| 일반화가 되나 | 안 말함 | 근사와 다른 물음 |
| 깊이가 왜 필요한가 | 안 말함 | 층 하나로 된다고만 |
다섯 줄 모두 실무에서 정작 알고 싶은 것입니다.
정리는 가능성의 하한을 긋고 그 위는 아무것도 말하지 않습니다.
이 문제에서 배우는 것. 보편근사정리는 강한 정리처럼 들리지만 약한 주장입니다. "할 수 있다"만 말하고 **"어떻게" "얼마나" "정말로"**는 전부 침묵합니다.
확인 1-1. 보편근사정리의 조건을 쓰세요.
답. 활성함수가 다항식이 아니면 됩니다.
확인 1-2. 검산에서 조각 개와 개의 최대 오차를 쓰세요.
답. 과 입니다.
확인 1-3. 검산에서 손으로 만든 신경망의 전체 구간 최대 오차를 쓰세요.
답. 입니다.
문제. 개수를 셉니다.
() 무엇이 개수를 정하는지 정리하세요.
() 진동이 빠를수록 얼마나 필요한지 보세요.
() 차원이 늘면 어떻게 되는지 세세요.
() 실제로 학습시켜 재 보세요.
생각의 실마리. 문제 에서 오차가 조각 수에 반비례했습니다. 그러면 함수가 얼마나 빨리 변하는지가 조각 수를 정합니다.
풀이. () 정리합니다.
| 무엇 | 어떻게 드나 | 덧붙임 |
|---|---|---|
| 함수가 얼마나 굽었나 | 많이 굽으면 많이 필요 | 진동 수 |
| 차원 | 차원마다 조각이 곱해짐 | 차원의 저주 |
| 원하는 정밀도 | 오차의 역수에 비례 | 차원에서 |
| 깊이를 쓰면 | 지수로 줄 수 있음 | 강 문제 |
() 조각 직선으로 근사하고 최대 오차가 아래가 되는 최소 조각 수를 찾습니다.
| 진동수 | 필요한 조각 수 | 진동수 대비 |
|---|---|---|
셋째 열이 에서 사이로 거의 일정합니다.
진동이 두 배 빨라지면 조각도 대략 두 배 필요합니다.
() 차원이 늘면 어떻게 되는지 셉니다. 각 축을 등분하면 격자 조각이 개 생깁니다.
| 축마다 등분 | 차원 | 차원 | 차원 | 차원 |
|---|---|---|---|---|
차원에서 축마다 등분만 해도 조각이 조를 넘습니다.
강의 차원의 저주가 여기서 근사의 비용으로 나타납니다. 정리는 존재를 말하되 실용성을 말하지 않습니다.
() 실제로 학습시켜 재 봅니다. 진동수 인 사인함수를 은닉층 하나로 배웁니다. 학습률은 여섯 개 격자에서 최선을 고릅니다.
| 은닉 단위 수 | 최대 오차 | 평균 제곱 오차 |
|---|---|---|
단위 까지는 최대 오차가 과 로 사실상 못 배웁니다.
부터 꺾이고 에서 입니다.
평균 제곱 오차는 에서 로 이백 배 넘게 줄어듭니다. 정리가 말한 대로이지만, 얼마나 필요한지는 해 봐야 압니다.
이 문제에서 배우는 것. 필요한 단위 수는 함수의 진동과 차원이 정합니다. 차원에서는 진동에 비례하지만 차원이 늘면 지수로 폭발하고, 그때 정리는 아무 도움이 안 됩니다.
확인 2-1. 진동이 두 배 빨라지면 조각이 몇 배 필요한지 쓰세요.
답. 대략 두 배입니다.
확인 2-2. 검산에서 차원에서 축마다 등분했을 때의 조각 수를 쓰세요.
답. 개입니다.
확인 2-3. 검산에서 은닉 개와 개의 평균 제곱 오차를 쓰세요.
답. 과 입니다.
문제. 존재와 도달을 가릅니다.
() 두 물음을 가르세요.
() 손으로 만든 해와 학습한 해를 견주세요.
() 초기화를 바꿔 여러 번 학습시키세요.
() 못 찾는 경우를 만드세요.
() 표현력 부족인지 최적화 실패인지 가리세요.
생각의 실마리. 정리는 좋은 가중치가 있다고 말합니다. 그런데 경사하강이 그 자리로 간다는 보장은 없습니다.
풀이. () 가릅니다.
| 물음 | 누가 답하나 | 답 |
|---|---|---|
| 존재하는가 | 정리가 답함 | 예 |
| 찾을 수 있는가 | 정리가 안 답함 | 최적화 문제 |
| 무엇이 막나 | 손실면이 안 볼록함 | 국소 최소와 안장점 |
| 실제로는 | 대개 찾음 | 왜인지는 미해결 |
() 손으로 만든 해와 학습한 해를 견줍니다. 손 구성은 은닉 개로 오차 이었습니다.
| 은닉 단위 수 | 학습한 해의 최대 오차 | 손으로 만든 해 대비 |
|---|---|---|
은닉 개만으로 손 구성의 절반 오차입니다. 개짜리 손 구성보다 다섯 배 적은 단위로 두 배 정확합니다.
손으로 만든 것은 계단이라 조각 안에서 상수인데 학습한 것은 기울기를 씁니다.
존재를 보이는 구성이 좋은 구성일 필요는 없습니다. 증명은 가장 단순한 구성을 쓰지 가장 좋은 것을 쓰지 않습니다.
() 초기화를 바꿔 열 번 학습시킵니다.
| 은닉 단위 수 | 최대 오차 평균 | 가장 좋은 것 | 가장 나쁜 것 |
|---|---|---|---|
단위 에서는 좋은 것과 나쁜 것이 네 배 넘게 갈립니다. 과 입니다.
단위 에서는 와 로 좁혀집니다.
넉넉하게 두는 것이 학습을 쉽게 만듭니다. 표현력이 남으면 어느 초기화에서 출발해도 쓸 만한 자리에 갑니다.
() 못 찾는 경우를 만듭니다. 은닉 개로 진동수를 올려 봅니다.
| 진동수 | 최대 오차 | 목표 진폭 대비 | 배웠나 |
|---|---|---|---|
| 배움 | |||
| 배움 | |||
| 못 배움 | |||
| 못 배움 |
진동수 부터 최대 오차가 목표 진폭을 넘습니다. 사실상 상수를 내놓는 것입니다.
단위가 모자라서인지 최적화가 실패해서인지는 이 표로는 못 가립니다.
() 가려 봅니다. 진동수 를 손 구성과 학습으로 견줍니다.
| 무엇 | 은닉 단위 수 | 최대 오차 |
|---|---|---|
| 손으로 만든 계단 | ||
| 손으로 만든 계단 | ||
| 손으로 만든 계단 | ||
| 학습으로 찾은 것 | ||
| 학습으로 찾은 것 | ||
| 학습으로 찾은 것 |
은닉 개 손 구성이 인데 학습으로 찾은 개는 입니다.
표현력은 충분한데 학습이 그 자리를 못 찾은 것입니다. 여섯 배 넘게 나쁩니다.
정리가 답하지 않는 자리가 정확히 여기입니다. 존재는 보장되고 도달은 보장되지 않습니다.
이 문제에서 배우는 것. 표현력과 학습 가능성은 다른 물음입니다. 그리고 그 둘이 갈라지는 것을 보이려면 손으로 만든 해와 학습한 해를 같은 단위 수에서 견주면 됩니다.
확인 3-1. 정리가 답하는 물음과 안 답하는 물음을 쓰세요.
답. 존재하는가에는 답하고 찾을 수 있는가에는 안 답합니다.
확인 3-2. 검산에서 학습한 은닉 개와 손 구성 개의 최대 오차를 쓰세요.
답. 과 입니다.
확인 3-3. 검산에서 진동수 일 때 손 구성 개와 학습 개의 최대 오차를 쓰세요.
답. 과 입니다.
문제. 범위를 봅니다.
() 무엇이 문제인지 정리하세요.
() 구간 밖에서 무엇이 나오는지 보세요.
() 정말 선형인지 확인하세요.
() 활성함수를 바꿔 보세요.
생각의 실마리. 정리는 닫힌 유계 구간 위에서만 성립합니다. 바깥은 아예 논의 대상이 아닙니다.
풀이. () 정리합니다.
| 무엇 | 무엇인가 | 덧붙임 |
|---|---|---|
| 정리의 범위 | 닫힌 유계 구간 | 옹골 집합 |
| 바깥에서는 | 아무 보장 없음 | 정류 선형은 선형으로 뻗음 |
| 실무에서 | 학습 자료의 범위 밖 | 분포 이동 |
| 어떻게 아나 | 입력이 범위 안인지 확인 | 강 이상탐지 |
() 진동수 인 사인을 에서 까지만 학습시킵니다.
| 참값 | 신경망 출력 | 차이 | 어디인가 | |
|---|---|---|---|---|
| 구간 안 | ||||
| 구간 안 | ||||
| 구간 안 | ||||
| 구간 밖 | ||||
| 구간 밖 | ||||
| 구간 밖 |
구간 안에서는 오차가 아래입니다.
에서 참값이 인데 출력은 입니다. 열 배가 넘습니다.
참값이 진동하는데 출력은 직선으로 뻗습니다.
() 정말 선형인지 확인합니다.
| 범위 | 출력 기울기 | 이차 차분 |
|---|---|---|
| 에서 | ||
| 에서 | ||
| 에서 | ||
| 에서 |
기울기가 네 구간에서 모두 로 같고 이차 차분이 입니다.
바깥에서는 정확히 선형입니다. 모든 정류 선형이 한쪽으로 굳었기 때문입니다. 강 문제 의 조각으로 보면, 바깥은 하나의 무한한 조각입니다.
강 문제 의 없는 조합에서의 추측과 같은 이야기입니다.
() 활성함수를 바꿔 봅니다.
| 참값 | 정류 선형 | 로지스틱 | |
|---|---|---|---|
정류 선형은 에서 까지 뻗고 로지스틱은 로 눕습니다.
둘 다 참값과 무관합니다. 어느 쪽도 바깥을 맞히지 못합니다.
다만 로지스틱 쪽이 터지지는 않습니다. 출력이 유계라 최소한 예측이 폭주하지는 않습니다.
이 문제에서 배우는 것. 신경망의 예측은 학습 자료의 범위 안에서만 뜻이 있습니다. 바깥에서는 활성함수가 정한 기계적인 외삽이 나올 뿐이고, 그것을 예측이라 부르면 안 됩니다.
확인 4-1. 정류 선형 신경망이 학습 구간 밖에서 어떤 함수인지 쓰세요.
답. 정확히 선형입니다.
확인 4-2. 검산에서 일 때 참값과 신경망 출력을 쓰세요.
답. 와 입니다.
확인 4-3. 검산에서 일 때 정류 선형과 로지스틱의 출력을 쓰세요.
답. 과 입니다.
문제. 정리를 읽습니다.
() 어떻게 읽을지 정리하세요.
() 같은 단위 예산을 넓게와 깊게 나누세요.
() 정리와 실무의 물음을 견주세요.
() 이 강의를 한 장으로 모으세요.
생각의 실마리. 정리를 잘못 읽으면 **"층 하나면 되니 깊게 할 필요 없다"**는 결론이 나옵니다. 그것이 맞는지 봅니다.
풀이. () 정리합니다.
| 무엇 | 무엇인가 | 왜 |
|---|---|---|
| 이렇게 읽으면 안 됨 | 층 하나면 충분하다 | 개수를 안 말함 |
| 이렇게 읽으면 안 됨 | 신경망은 무엇이든 배운다 | 학습을 안 말함 |
| 이렇게 읽어야 함 | 표현력은 병목이 아니다 | 다른 것이 병목 |
| 실제 병목 | 최적화와 일반화 | 강이 아니라 강부터 |
() 진동수 를 같은 단위 예산으로 여러 구조에서 배웁니다.
| 구조 | 전체 단위 | 최대 오차 |
|---|---|---|
| 한 층 | ||
| 두 층 | ||
| 세 층 | ||
| 여섯 층 |
앞의 셋은 모두 근처로 못 배웁니다.
여섯 층 만 로 배웁니다. 네 배 넘게 좋습니다.
같은 단위 수인데 깊은 쪽이 훨씬 낫습니다. 정리는 층 하나로 된다고 했지만 실제로는 깊은 쪽이 쉽게 찾습니다.
강 문제 의 조각 수가 그 이유입니다. 같은 단위로 깊게 쌓으면 조각이 훨씬 많아집니다.
() 정리와 실무의 물음을 견줍니다.
| 물음 | 정리가 답하나 | 어디서 |
|---|---|---|
| 표현할 수 있는가 | 정리가 예라 함 | 닫힘 |
| 몇 개로 표현하는가 | 정리가 안 말함 | 문제 |
| 학습이 찾는가 | 정리가 안 말함 | 문제 |
| 본 적 없는 자리는 | 정리가 안 말함 | 문제 |
| 일반화하는가 | 정리가 안 말함 | 강 |
첫 줄만 정리가 답합니다. 나머지 넷이 이 과목의 나머지 전부입니다.
() 이 강의를 한 장으로 모읍니다.
| 무엇 | 한 줄로 |
|---|---|
| 정리가 말하는 것 | 은닉층 하나로 연속함수를 근사할 수 있습니다 |
| 조건 | 활성함수가 다항식만 아니면 됩니다 |
| 안 말하는 것 하나 | 단위가 몇 개 필요한지입니다 |
| 안 말하는 것 둘 | 학습이 그것을 찾는지입니다 |
| 안 말하는 것 셋 | 구간 밖에서 어떻게 되는지입니다 |
| 그래서 | 표현력은 병목이 아닙니다 |
| 진짜 병목 | 최적화와 일반화입니다 |
이 문제에서 배우는 것. 보편근사정리의 실무적 값은 안심하고 다른 것을 걱정하게 해 주는 것입니다. 표현력이 모자라서 안 되는 경우는 드물고, 대개 최적화나 일반화가 문제입니다.
확인 5-1. 보편근사정리를 잘못 읽는 두 방식을 쓰세요.
답. 층 하나면 충분하다는 것과 신경망은 무엇이든 배운다는 것입니다.
확인 5-2. 검산에서 한 층 과 여섯 층 의 최대 오차를 쓰세요.
답. 와 입니다.
확인 5-3. 정리를 어떻게 읽어야 하는지 쓰세요.
답. 표현력은 병목이 아니라는 뜻으로 읽습니다.
| 유형 | 무엇을 묻나 | 어디를 보나 |
|---|---|---|
| 정리의 조건 | 다항식만 아니면 됨 | 문제 |
| 계단 구성 | 오차가 조각 수에 반비례 | 문제 |
| 필요한 단위 | 진동과 차원 | 문제 |
| 차원의 저주 | 차원에서 조 | 문제 |
| 존재와 도달 | 다른 물음 | 문제 |
| 손 구성 대 학습 | 학습이 더 좋기도 나쁘기도 | 문제 |
| 구간 밖 | 정확히 선형 | 문제 |
| 활성함수와 외삽 | 뻗거나 눕거나 | 문제 |
| 넓게와 깊게 | 깊은 쪽이 쉽게 찾음 | 문제 |
| 정리의 값 | 표현력은 병목이 아님 | 문제 |
정리가 답하는 것과 안 답하는 것을 한자리에 모읍니다.
| 물음 | 답하나 | 누가 답하나 |
|---|---|---|
| 표현할 수 있는가 | 예 | 강 |
| 몇 개가 필요한가 | 아니오 | 근사 이론 |
| 학습이 찾는가 | 아니오 | 강부터 |
| 구간 밖은 | 아니오 | 아무도 |
| 일반화하는가 | 아니오 | 강 |
문제 6. 보편근사정리의 조건을 쓰세요.
답. 활성함수가 다항식이 아니면 됩니다.
문제 7. 검산에서 조각 개와 개의 최대 오차를 쓰세요.
답. 과 입니다.
문제 8. 검산에서 손으로 만든 신경망의 전체 구간 최대 오차를 쓰세요.
답. 입니다.
문제 9. 진동이 두 배 빨라지면 조각이 몇 배 필요한지 쓰세요.
답. 대략 두 배입니다.
문제 10. 검산에서 차원에서 축마다 등분했을 때의 조각 수를 쓰세요.
답. 개입니다.
문제 11. 검산에서 은닉 개와 개의 평균 제곱 오차를 쓰세요.
답. 과 입니다.
문제 12. 정리가 답하는 물음과 안 답하는 물음을 쓰세요.
답. 존재하는가에는 답하고 찾을 수 있는가에는 안 답합니다.
문제 13. 검산에서 학습한 은닉 개와 손 구성 개의 최대 오차를 쓰세요.
답. 과 입니다.
문제 14. 검산에서 진동수 일 때 손 구성 개와 학습 개의 최대 오차를 쓰세요.
답. 과 입니다.
문제 15. 정류 선형 신경망이 학습 구간 밖에서 어떤 함수인지 쓰세요.
답. 정확히 선형입니다.
문제 16. 검산에서 일 때 참값과 신경망 출력을 쓰세요.
답. 와 입니다.
문제 17. 검산에서 일 때 정류 선형과 로지스틱의 출력을 쓰세요.
답. 과 입니다.
문제 18. 검산에서 한 층 과 여섯 층 의 최대 오차를 쓰세요.
답. 와 입니다.
심화 1. 왜 다항식이면 안 되는지 보이세요.
활성함수가 차수 인 다항식이라 합시다.
| 단계 | 무엇 |
|---|---|
| 첫째 | 선형 조합의 다항식도 차수 |
| 둘째 | 은닉층 하나면 언제나 차수 |
| 셋째 | 차수 다항식만 만들 수 있음 |
| 넷째 | 연속함수 전체를 못 덮음 |
강 문제 의 선형이 인 특수한 경우입니다.
이것이 정리의 조건이 필요충분에 가까운 이유입니다. 다항식만 빼면 어떤 함수든 됩니다.
심화 2. 너비와 깊이의 쌍대 정리를 정리하세요.
| 정리 | 무엇을 말하나 |
|---|---|
| 너비 보편성 | 층 하나에 단위를 무한히 늘리면 |
| 깊이 보편성 | 너비를 입력 차원 더하기 상수로 고정하고 깊이를 늘리면 |
둘째 줄이 덜 알려진 결과입니다. 너비가 면 깊이만으로 보편근사가 됩니다.
둘 다 존재만 말합니다. 어느 쪽도 실무의 물음에 답하지 않습니다.
심화 3. 근사 오차의 차수를 정리하세요.
| 무엇 | 뜻 |
|---|---|
| 단위 수 | |
| 함수의 매끄러움 | |
| 입력 차원 |
가 지수에 들어가는 것이 차원의 저주입니다. 문제 의 ()이 이 식의 다른 얼굴입니다.
신경망이 실제로 잘 되는 이유는 자료가 저차원 다양체 위에 있기 때문이라는 설명이 있습니다. 그러면 유효 가 작아집니다.
심화 4. 무한 너비 극한을 미리 봅니다.
| 무엇 | 무엇이 되나 |
|---|---|
| 너비를 무한대로 | 가우스 과정 |
| 학습을 함께 보면 | 신경 접선 커널 |
| 그때 손실면은 | 볼록해짐 |
셋째 줄이 흥미롭습니다. 무한히 넓으면 최적화 문제가 사라집니다.
그런데 실제 신경망은 유한하고, 유한하기 때문에 어렵고 또 잘 됩니다. 무한 너비 이론은 왜 되는지의 한 조각입니다.
심화 5. 근사와 일반화를 가르세요.
| 무엇 | 무엇을 재나 |
|---|---|
| 근사 오차 | 최선의 신경망과 참 함수의 거리 |
| 추정 오차 | 배운 것과 최선의 신경망의 거리 |
| 최적화 오차 | 실제로 찾은 것과 배울 수 있는 것의 거리 |
보편근사정리는 첫 줄만 다룹니다.
강의 편향과 분산에서 첫 줄이 편향이고 둘째 줄이 분산입니다. 셋째 줄은 그 분해에 아예 안 나옵니다.
단위를 늘리면 첫 줄은 줄지만 둘째 줄이 늘어납니다. 그것이 강의 벌점이 필요한 이유입니다.
심화 6. 이 강의가 다음 강의로 어떻게 이어지는지 정리하세요.
| 이 강의에서 | 다음에서 |
|---|---|
| 표현력은 병목이 아님 | 강 최적화가 병목 |
| 깊으면 쉽게 찾음 | 강 층을 지나는 그래디언트 |
| 깊으면 학습이 어려움 | 강부터 강 |
| 구간 밖 외삽 | 분포 이동 |
| 손 구성과 학습의 격차 | 손실면의 모양 |
강은 그 최적화 병목을 정면으로 봅니다. 층을 지나며 그래디언트가 어떻게 변형되는지가 다음 물음입니다.
정답.
| 기호 | 읽는 법 | 뜻 |
|---|---|---|
| 보편근사정리 | universal approximation theorem | 은닉층 하나로 연속함수를 근사할 수 있다는 정리입니다 |
| 옹골 집합 | compact set | 닫혀 있고 유계인 집합입니다 |
| 최대 오차 | sup norm error | 구간 전체에서 가장 큰 차이입니다 |
| 계단 구성 | step construction | 계단을 쌓아 함수를 흉내 내는 방법입니다 |
| 근사 오차 | approximation error | 최선의 신경망과 참 함수의 거리입니다 |
| 최적화 오차 | optimization error | 찾은 것과 찾을 수 있던 것의 거리입니다 |
| 외삽 | extrapolation | 학습 범위 밖에서의 예측입니다 |
| 차원의 저주 | curse of dimensionality | 차원이 늘면 필요한 자료가 지수로 커집니다 |
| 깊이 보편성 | depth universality | 너비를 고정하고 깊이만으로 근사하는 결과입니다 |
| 신경 접선 커널 | neural tangent kernel | 무한 너비 극한에서의 학습 동역학입니다 |
다음은 242강 층을 지나는 그래디언트입니다. 이 강의에서 표현력은 병목이 아니라는 것을 봤습니다. 다음 강의는 진짜 병목인 최적화를 봅니다. 층을 지나며 그래디언트가 어떻게 커지고 작아지는지를 야코비로 정확히 셉니다.
import numpy as np
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 relu(z):
return np.maximum(z, 0.0)
def sig(z):
return 1.0 / (1.0 + np.exp(-np.clip(z, -500, 500)))
def step_net(f, K, lo=-3.0, hi=3.0):
span = (hi - lo) / K
lo, hi = lo - span, hi + span
K = K + 2
edges = np.linspace(lo, hi, K + 1)
mids = (edges[:-1] + edges[1:]) / 2
vals = f(mids)
e = 1e-4
W1 = np.array([1.0 / e for _ in range(2 * K)]).reshape(1, -1)
b1 = np.array([v for k in range(K) for v in (-edges[k] / e, -edges[k + 1] / e)])
W2 = np.array([v for k in range(K) for v in (vals[k], -vals[k])]).reshape(-1, 1)
return W1, b1, W2
def step_eval(W1, b1, W2, xg):
H = np.clip(relu(xg.reshape(-1, 1) @ W1 + b1), 0.0, 1.0)
return (H @ W2).ravel()
print("=" * 78)
print("241강 보편근사정리가 말하는 것 코드 검산")
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(" 239강에서 활성함수가 없으면 안 된다고 했는데 그것이 정확히 이 조건입니다")
print()
print(" (2) 계단으로 쌓아 손으로 근사해 봅니다")
def target(x):
return np.sin(2.0 * x) + 0.3 * x
xs = np.linspace(-3, 3, 2001)
print(" 목표 함수는 사인 2x 더하기 0.3x 입니다")
print(" 구간을 K 조각으로 나눠 각 조각에서 상수로 맞춥니다")
print(" " + rl("조각 수", 10) + " " + rl("필요한 정류 선형 단위", 24) + " " + rl("최대 오차", 14) + " " + rl("앞 대비", 12))
prev = None
for K in [4, 8, 16, 32, 64]:
edges = np.linspace(-3, 3, K + 1)
mids = (edges[:-1] + edges[1:]) / 2
vals = target(mids)
approx = np.zeros_like(xs)
for k in range(K):
m = (xs >= edges[k]) & (xs < edges[k + 1])
approx[m] = vals[k]
approx[xs >= edges[-1]] = vals[-1]
err = float(np.abs(approx - target(xs)).max())
rat = "기준" if prev is None else "%.6f" % (err / prev)
print(" " + rl("%d" % K, 10) + " " + rl("%d" % (2 * K), 24) + " " + rl("%.6f" % err, 14) + " " + rl(rat, 12))
prev = err
print(" 조각을 두 배로 하면 오차가 대략 절반이 됩니다")
print(" 계단 하나는 정류 선형 두 개로 만듭니다. 오르는 것 하나와 내리는 것 하나입니다")
print(" 그래서 단위를 늘리면 언제든 원하는 오차 안으로 들어갑니다")
print()
print(" (3) 실제로 신경망 가중치를 손으로 정해 확인합니다")
Kh = 40
W1h, b1h, W2h = step_net(target, Kh)
hand_out = step_eval(W1h, b1h, W2h, xs)
print(" 은닉 단위 %d 개로 계단 %d 개를 만듭니다" % (2 * (Kh + 2), Kh + 2))
print(" 양 끝을 한 조각씩 넓혀 경계에서 값이 0 으로 떨어지지 않게 합니다")
print(" " + rl("x", 10) + " " + rl("목표값", 14) + " " + rl("신경망 출력", 14) + " " + rl("차이", 14))
for xv in [-2.5, -1.0, 0.0, 1.0, 2.5]:
j = int(np.argmin(np.abs(xs - xv)))
print(" " + rl("%.4f" % xs[j], 10) + " " + rl("%.6f" % target(xs[j]), 14) + " " + rl("%.6f" % hand_out[j], 14) + " " + rl("%.6f" % abs(hand_out[j] - target(xs[j])), 14))
print(" 전체 구간 최대 오차는 %.6f 입니다" % float(np.abs(hand_out - target(xs)).max()))
print(" 학습을 하나도 안 하고 가중치를 직접 적어 만든 것입니다")
print(" 정리는 이런 구성이 존재한다는 것만 말합니다")
print()
print(" (4) 정리가 안 말하는 것을 정리합니다")
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. 단위가 몇 개 필요한가")
print()
print(" (1) 무엇이 개수를 정하는지 정리합니다")
rows = [
("함수가 얼마나 굽었나", "많이 굽으면 많이 필요", "진동 수"),
("차원", "차원마다 조각이 곱해짐", "차원의 저주"),
("원하는 정밀도", "오차의 역수에 비례", "1 차원에서"),
("깊이를 쓰면", "지수로 줄 수 있음", "239강 문제 3"),
]
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) 진동이 빠를수록 단위가 몇 배 필요한지 봅니다")
def fit_pieces(f, K, lo=-3, hi=3, nx=4001):
xg = np.linspace(lo, hi, nx)
edges = np.linspace(lo, hi, K + 1)
approx = np.zeros_like(xg)
for k in range(K):
m = (xg >= edges[k]) & (xg <= edges[k + 1])
if m.sum() < 2:
continue
xk = xg[m]
yk = f(xk)
A = np.column_stack([xk, np.ones_like(xk)])
c = np.linalg.lstsq(A, yk, rcond=None)[0]
approx[m] = A @ c
return float(np.abs(approx - f(xg)).max())
print(" 조각 직선으로 근사하고 최대 오차가 0.05 아래가 되는 최소 조각 수를 찾습니다")
print(" " + rl("진동수", 10) + " " + rl("필요한 조각 수", 16) + " " + rl("진동수 대비", 14))
for om in [1.0, 2.0, 4.0, 8.0]:
f = lambda x, o=om: np.sin(o * x)
K = 2
while K < 4000:
if fit_pieces(f, K) < 0.05:
break
K = int(K * 1.3) + 1
print(" " + rl("%.4f" % om, 10) + " " + rl("%d" % K, 16) + " " + rl("%.6f" % (K / om), 14))
print(" 진동이 두 배 빨라지면 조각도 대략 두 배 필요합니다")
print(" 셋째 열이 거의 일정합니다")
print()
print(" (3) 차원이 늘면 어떻게 되는지 셉니다")
print(" 각 축을 m 등분하면 격자 조각이 m 의 차원 제곱만큼 생깁니다")
print(" " + rl("축마다 등분", 14) + " " + rl("1 차원", 12) + " " + rl("2 차원", 12) + " " + rl("5 차원", 14) + " " + rl("10 차원", 18))
for m in [2, 4, 8, 16]:
print(" " + rl("%d" % m, 14) + " " + rl("%d" % m, 12) + " " + rl("%d" % (m ** 2), 12) + " " + rl("%d" % (m ** 5), 14) + " " + rl("%d" % (m ** 10), 18))
print(" 10 차원에서 축마다 16 등분만 해도 조각이 1 조를 넘습니다")
print(" 209강의 차원의 저주가 여기서 근사의 비용으로 나타납니다")
print(" 그래서 정리는 존재를 말하되 실용성을 말하지 않습니다")
print()
print(" (4) 실제로 학습시켜 단위 수를 재 봅니다")
rng = np.random.default_rng(20241)
def make_data(om, n=4000):
X = rng.uniform(-3, 3, (n, 1))
y = np.sin(om * X[:, 0])
return X, y
def train_reg(X, y, hidden, steps, lr, seed):
rr = np.random.default_rng(seed)
d = X.shape[1]
W1 = rr.normal(0, np.sqrt(2.0 / d), (d, hidden))
knots = rr.uniform(-3, 3, hidden)
b1 = -(W1[0] * knots)
W2 = rr.normal(0, np.sqrt(2.0 / hidden), (hidden, 1)) * 0.5
b2 = np.zeros(1)
vW1 = np.zeros_like(W1); vb1 = np.zeros_like(b1)
vW2 = np.zeros_like(W2); vb2 = np.zeros_like(b2)
m = len(X)
for t in range(steps):
idx = rr.integers(0, m, 128)
Xi, yi = X[idx], y[idx]
Z1 = Xi @ W1 + b1
H1 = relu(Z1)
out = (H1 @ W2 + b2).ravel()
d_out = (2.0 * (out - yi) / len(Xi)).reshape(-1, 1)
gW2 = H1.T @ d_out
gb2 = d_out.sum(axis=0)
D1 = (d_out @ W2.T) * (Z1 > 0)
gW1 = Xi.T @ D1
gb1 = D1.sum(axis=0)
vW1 = 0.9 * vW1 + gW1; vb1 = 0.9 * vb1 + gb1
vW2 = 0.9 * vW2 + gW2; vb2 = 0.9 * vb2 + gb2
W1 -= lr * vW1; b1 -= lr * vb1
W2 -= lr * vW2; b2 -= lr * vb2
return W1, b1, W2, b2
def best_reg(X, y, hidden, steps, seed, evalf):
best = None
old = np.seterr(over='ignore', invalid='ignore')
for lr in [0.0005, 0.001, 0.002, 0.005, 0.01, 0.02]:
P = train_reg(X, y, hidden, steps, lr, seed)
e = evalf(P)
if np.isfinite(e) and (best is None or e < best[0]):
best = (e, lr, P)
np.seterr(**old)
if best is None:
return (float('inf'), 0.0, None)
return best
def max_err(W1, b1, W2, b2, om):
xg = np.linspace(-3, 3, 2001).reshape(-1, 1)
out = (relu(xg @ W1 + b1) @ W2 + b2).ravel()
return float(np.abs(out - np.sin(om * xg[:, 0])).max())
print(" 진동수 4 인 사인함수를 은닉층 하나로 배웁니다")
X4, y4 = make_data(4.0)
xg = np.linspace(-3, 3, 2001).reshape(-1, 1)
yg4 = np.sin(4.0 * xg[:, 0])
print(" 학습률은 여섯 개 격자에서 최선을 고릅니다")
print(" " + rl("은닉 단위 수", 14) + " " + rl("최대 오차", 14) + " " + rl("평균 제곱 오차", 16))
for hnum in [4, 8, 16, 32, 64]:
ev = lambda P: float(np.abs((relu(xg @ P[0] + P[1]) @ P[2] + P[3]).ravel() - yg4).max())
e, lrb, P = best_reg(X4, y4, hnum, 8000, 31, ev)
o = (relu(xg @ P[0] + P[1]) @ P[2] + P[3]).ravel()
print(" " + rl("%d" % hnum, 14) + " " + rl("%.6f" % e, 14) + " " + rl("%.6f" % float(((o - yg4) ** 2).mean()), 16))
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) 손으로 만든 해와 학습한 해를 견줍니다")
print(" 문제 1 에서 손으로 만든 신경망은 은닉 %d 개로 오차 %.6f 였습니다" % (2 * (Kh + 2), float(np.abs(hand_out - target(xs)).max())))
Xt = rng.uniform(-3, 3, (4000, 1))
yt = target(Xt[:, 0])
print(" 같은 목표 함수를 학습으로 풀어 봅니다")
print(" " + rl("은닉 단위 수", 14) + " " + rl("학습한 해의 최대 오차", 24) + " " + rl("손으로 만든 해 대비", 22))
hand_err = float(np.abs(hand_out - target(xs)).max())
tg = target(xg[:, 0])
for hnum in [16, 40, 80, 160]:
ev = lambda P: float(np.abs((relu(xg @ P[0] + P[1]) @ P[2] + P[3]).ravel() - tg).max())
e2, lrb, P = best_reg(Xt, yt, hnum, 12000, 41, ev)
print(" " + rl("%d" % hnum, 14) + " " + rl("%.6f" % e2, 24) + " " + rl("%.6f" % (e2 / hand_err), 22))
print(" 학습한 해가 손으로 만든 해보다 훨씬 좋습니다")
print(" 손으로 만든 것은 계단이라 조각 안에서 상수인데 학습한 것은 기울기를 씁니다")
print(" 존재를 보이는 구성이 좋은 구성일 필요는 없습니다")
print()
print(" (3) 초기화를 바꿔 여러 번 학습시켜 봅니다")
print(" 같은 구조를 씨앗만 바꿔 열 번 학습시킵니다")
print(" " + rl("은닉 단위 수", 14) + " " + rl("최대 오차 평균", 18) + " " + rl("가장 좋은 것", 16) + " " + rl("가장 나쁜 것", 16))
for hnum in [8, 16, 64]:
errs = []
for sd in range(10):
ev = lambda P: float(np.abs((relu(xg @ P[0] + P[1]) @ P[2] + P[3]).ravel() - tg).max())
e2, lrb, P = best_reg(Xt, yt, hnum, 8000, 100 + sd, ev)
errs.append(e2)
errs = np.array(errs)
print(" " + rl("%d" % hnum, 14) + " " + rl("%.6f" % errs.mean(), 18) + " " + rl("%.6f" % errs.min(), 16) + " " + rl("%.6f" % errs.max(), 16))
print(" 단위가 적으면 초기화에 따라 크게 갈립니다")
print(" 단위가 많으면 어느 씨앗이든 비슷한 자리에 갑니다")
print(" 넉넉하게 두는 것이 학습을 쉽게 만듭니다")
print()
print(" (4) 못 찾는 경우를 만들어 봅니다")
print(" 진동이 아주 빠른 함수를 은닉 64 개로 배워 봅니다")
print(" " + rl("진동수", 10) + " " + rl("최대 오차", 14) + " " + rl("목표 진폭 대비", 16) + " " + "배웠나")
for om in [1.0, 4.0, 12.0, 30.0]:
Xo, yo = make_data(om, 6000)
yo_g = np.sin(om * xg[:, 0])
ev = lambda P: float(np.abs((relu(xg @ P[0] + P[1]) @ P[2] + P[3]).ravel() - yo_g).max())
e2, lrb, P = best_reg(Xo, yo, 64, 12000, 51, ev)
print(" " + rl("%.4f" % om, 10) + " " + rl("%.6f" % e2, 14) + " " + rl("%.6f" % (e2 / 1.0), 16) + " " + ("배움" if e2 < 0.3 else "못 배움"))
print(" 진동이 빨라지면 같은 단위로 못 배웁니다")
print(" 단위가 모자라서인지 최적화가 실패해서인지는 이 표로는 못 가립니다")
print(" 다음에서 가려 봅니다")
print()
print(" (5) 표현력 부족인지 최적화 실패인지 가립니다")
print(" 진동수 12 를 손으로 만든 계단 신경망과 학습한 것으로 견줍니다")
om = 12.0
xgl = np.linspace(-3, 3, 2001)
ytrue = np.sin(om * xgl)
print(" " + rw("무엇", 26) + " " + rl("은닉 단위 수", 14) + " " + rl("최대 오차", 14))
for Kc in [64, 128, 256]:
W1c, b1c, W2c = step_net(lambda t: np.sin(om * t), Kc)
oc = step_eval(W1c, b1c, W2c, xgl)
print(" " + rw("손으로 만든 계단 %d" % Kc, 26) + " " + rl("%d" % (2 * (Kc + 2)), 14) + " " + rl("%.6f" % float(np.abs(oc - ytrue).max()), 14))
Xo, yo = make_data(om, 6000)
yo_g = np.sin(om * xg[:, 0])
for hnum in [128, 256, 512]:
ev = lambda P: float(np.abs((relu(xg @ P[0] + P[1]) @ P[2] + P[3]).ravel() - yo_g).max())
e2, lrb, P = best_reg(Xo, yo, hnum, 20000, 61, ev)
print(" " + rw("학습으로 찾은 것", 26) + " " + rl("%d" % hnum, 14) + " " + rl("%.6f" % e2, 14))
print(" 같은 단위 수에서 손으로 만든 것이 훨씬 낫습니다")
print(" 표현력은 충분한데 학습이 그 자리를 못 찾은 것입니다")
print(" 정리가 답하지 않는 자리가 정확히 여기입니다")
print()
print("문제 4. 구간 밖에서는 어떻게 되는가")
print()
print(" (1) 무엇이 문제인지 정리합니다")
rows = [
("정리의 범위", "닫힌 유계 구간", "옹골 집합"),
("바깥에서는", "아무 보장 없음", "정류 선형은 선형으로 뻗음"),
("실무에서", "학습 자료의 범위 밖", "분포 이동"),
("어떻게 아나", "입력이 범위 안인지 확인", "225강 이상탐지"),
]
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) 학습 구간 밖에서 무엇이 나오는지 봅니다")
Xn, yn = make_data(2.0, 4000)
yn_g = np.sin(2.0 * xg[:, 0])
ev = lambda P: float(np.abs((relu(xg @ P[0] + P[1]) @ P[2] + P[3]).ravel() - yn_g).max())
_, _, Pn = best_reg(Xn, yn, 64, 12000, 71, ev)
W1n, b1n, W2n, b2n = Pn
print(" 진동수 2 인 사인을 -3 에서 3 까지만 학습시킵니다")
print(" " + rl("x", 10) + " " + rl("참값", 14) + " " + rl("신경망 출력", 14) + " " + rl("차이", 14) + " " + "어디인가")
for xv in [0.0, 2.0, 3.0, 4.0, 6.0, 10.0]:
xa = np.array([[xv]])
o = float((relu(xa @ W1n + b1n) @ W2n + b2n)[0, 0])
t = float(np.sin(2.0 * xv))
print(" " + rl("%.4f" % xv, 10) + " " + rl("%.6f" % t, 14) + " " + rl("%.6f" % o, 14) + " " + rl("%.6f" % abs(o - t), 14) + " " + ("구간 안" if abs(xv) <= 3 else "구간 밖"))
print(" 구간 안에서는 잘 맞습니다")
print(" 구간 밖에서는 참값이 진동하는데 출력은 직선으로 뻗습니다")
print(" 정류 선형 신경망은 바깥에서 언제나 선형이기 때문입니다")
print()
print(" (3) 바깥에서 정말 선형인지 확인합니다")
print(" x 를 크게 두고 기울기를 봅니다")
print(" " + rl("x 범위", 16) + " " + rl("출력 기울기", 16) + " " + rl("이차 차분", 16))
for lo, hi in [(4.0, 6.0), (10.0, 12.0), (50.0, 52.0), (200.0, 202.0)]:
xa = np.linspace(lo, hi, 5).reshape(-1, 1)
o = (relu(xa @ W1n + b1n) @ W2n + b2n).ravel()
d1 = np.diff(o) / np.diff(xa[:, 0])
d2 = np.diff(d1)
print(" " + rl("%.1f 에서 %.1f" % (lo, hi), 16) + " " + rl("%.6f" % float(d1.mean()), 16) + " " + rl("%.2e" % float(np.abs(d2).max()), 16))
print(" 기울기가 어느 구간에서나 같고 이차 차분이 0 입니다")
print(" 바깥에서는 정확히 선형입니다. 모든 정류 선형이 한쪽으로 굳었기 때문입니다")
print(" 230강 문제 3 의 없는 조합에서의 추측과 같은 이야기입니다")
print()
print(" (4) 활성함수를 바꾸면 달라지는지 봅니다")
def train_reg_act(X, y, hidden, steps, lr, seed, f, df):
rr = np.random.default_rng(seed)
d = X.shape[1]
W1 = rr.normal(0, np.sqrt(2.0 / d), (d, hidden))
knots = rr.uniform(-3, 3, hidden)
b1 = -(W1[0] * knots)
W2 = rr.normal(0, np.sqrt(2.0 / hidden), (hidden, 1)) * 0.5
b2 = np.zeros(1)
vW1 = np.zeros_like(W1); vb1 = np.zeros_like(b1)
vW2 = np.zeros_like(W2); vb2 = np.zeros_like(b2)
m = len(X)
for t in range(steps):
idx = rr.integers(0, m, 128)
Xi, yi = X[idx], y[idx]
Z1 = Xi @ W1 + b1
H1 = f(Z1)
o = (H1 @ W2 + b2).ravel()
d_out = (2.0 * (o - yi) / len(Xi)).reshape(-1, 1)
gW2 = H1.T @ d_out
gb2 = d_out.sum(axis=0)
D1 = (d_out @ W2.T) * df(Z1)
gW1 = Xi.T @ D1
gb1 = D1.sum(axis=0)
vW1 = 0.9 * vW1 + gW1; vb1 = 0.9 * vb1 + gb1
vW2 = 0.9 * vW2 + gW2; vb2 = 0.9 * vb2 + gb2
W1 -= lr * vW1; b1 -= lr * vb1
W2 -= lr * vW2; b2 -= lr * vb2
return W1, b1, W2, b2
print(" 로지스틱을 쓰면 바깥에서 어떻게 되는지 봅니다")
best_s = None
for lrq in [0.002, 0.005, 0.01, 0.02]:
Pq = train_reg_act(Xn, yn, 64, 12000, lrq, 71, sig, lambda z: sig(z) * (1 - sig(z)))
eq = float(np.abs((sig(xg @ Pq[0] + Pq[1]) @ Pq[2] + Pq[3]).ravel() - yn_g).max())
if np.isfinite(eq) and (best_s is None or eq < best_s[0]):
best_s = (eq, Pq)
W1s, b1s, W2s, b2s = best_s[1]
print(" " + rl("x", 10) + " " + rl("참값", 14) + " " + rl("정류 선형", 14) + " " + rl("로지스틱", 14))
for xv in [3.0, 6.0, 20.0, 100.0]:
xa = np.array([[xv]])
o1 = float((relu(xa @ W1n + b1n) @ W2n + b2n)[0, 0])
o2 = float((sig(xa @ W1s + b1s) @ W2s + b2s)[0, 0])
print(" " + rl("%.4f" % xv, 10) + " " + rl("%.6f" % float(np.sin(2.0 * xv)), 14) + " " + rl("%.6f" % o1, 14) + " " + rl("%.6f" % o2, 14))
print(" 정류 선형은 끝없이 뻗고 로지스틱은 상수로 눕습니다")
print(" 둘 다 참값과 무관합니다. 어느 쪽도 바깥을 맞히지 못합니다")
print(" 다만 로지스틱 쪽이 터지지는 않습니다")
print()
print("문제 5. 실무에서 쓰기")
print()
print(" (1) 정리를 어떻게 읽을지 정리합니다")
rows = [
("이렇게 읽으면 안 됨", "층 하나면 충분하다", "개수를 안 말함"),
("이렇게 읽으면 안 됨", "신경망은 무엇이든 배운다", "학습을 안 말함"),
("이렇게 읽어야 함", "표현력은 병목이 아니다", "다른 것이 병목"),
("실제 병목", "최적화와 일반화", "241강이 아니라 242강부터"),
]
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) 같은 단위 예산을 넓게와 깊게 나눠 봅니다")
def train_deep(X, y, sizes, steps, lr, seed):
rr = np.random.default_rng(seed)
Ws, bs = [], []
for i in range(len(sizes) - 1):
s = np.sqrt(2.0 / sizes[i])
W = rr.normal(0, s, (sizes[i], sizes[i + 1]))
if i == len(sizes) - 2:
W = W * 0.5
Ws.append(W)
if i == 0 and sizes[0] == 1:
knots = rr.uniform(-3, 3, sizes[1])
bs.append(-(W[0] * knots))
else:
bs.append(np.zeros(sizes[i + 1]))
vW = [np.zeros_like(x) for x in Ws]
vb = [np.zeros_like(x) for x in bs]
m = len(X)
for t in range(steps):
idx = rr.integers(0, m, 128)
Xi, yi = X[idx], y[idx]
pre, acts = [], [Xi]
H = Xi
for i in range(len(Ws)):
Z = H @ Ws[i] + bs[i]
pre.append(Z)
H = relu(Z) if i < len(Ws) - 1 else Z
acts.append(H)
D = (2.0 * (acts[-1].ravel() - yi) / len(Xi)).reshape(-1, 1)
for i in range(len(Ws) - 1, -1, -1):
gW = acts[i].T @ D
gb = D.sum(axis=0)
if i > 0:
D = (D @ Ws[i].T) * (pre[i - 1] > 0)
vW[i] = 0.9 * vW[i] + gW
vb[i] = 0.9 * vb[i] + gb
Ws[i] -= lr * vW[i]
bs[i] -= lr * vb[i]
return Ws, bs
def deep_err(Ws, bs, om):
xg = np.linspace(-3, 3, 2001).reshape(-1, 1)
H = xg
for i in range(len(Ws)):
Z = H @ Ws[i] + bs[i]
H = relu(Z) if i < len(Ws) - 1 else Z
return float(np.abs(H.ravel() - np.sin(om * xg[:, 0])).max())
om = 12.0
Xo, yo = make_data(om, 6000)
print(" 진동수 12 를 같은 단위 예산으로 여러 구조에서 배웁니다")
print(" " + rw("구조", 22) + " " + rl("전체 단위", 12) + " " + rl("최대 오차", 14))
for nm, sizes in [("한 층 96", [1, 96, 1]), ("두 층 48", [1, 48, 48, 1]), ("세 층 32", [1, 32, 32, 32, 1]), ("여섯 층 16", [1] + [16] * 6 + [1])]:
beste = None
old3 = np.seterr(over='ignore', invalid='ignore')
for lrq in [0.002, 0.005, 0.01, 0.02]:
Ws, bs = train_deep(Xo, yo, sizes, 20000, lrq, 81)
e = deep_err(Ws, bs, om)
if np.isfinite(e) and (beste is None or e < beste):
beste = e
np.seterr(**old3)
print(" " + rw(nm, 22) + " " + rl("%d" % sum(sizes[1:-1]), 12) + " " + rl("%.6f" % beste, 14))
print(" 같은 단위 수인데 깊은 쪽이 훨씬 낫습니다")
print(" 정리는 층 하나로 된다고 했지만 실제로는 깊은 쪽이 쉽게 찾습니다")
print(" 239강 문제 3 의 조각 수가 그 이유입니다")
print()
print(" (3) 정리가 보장하는 것과 실무의 물음을 견줍니다")
rows = [
("표현할 수 있는가", "정리가 예라 함", "닫힘"),
("몇 개로 표현하는가", "정리가 안 말함", "문제 2"),
("학습이 찾는가", "정리가 안 말함", "문제 3"),
("본 적 없는 자리는", "정리가 안 말함", "문제 4"),
("일반화하는가", "정리가 안 말함", "210강"),
]
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(" (4) 이 강의를 한 장으로 모읍니다")
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(" 242강은 그 최적화 병목을 층을 지나는 그래디언트로 봅니다")
print()
print("=" * 78)
print("검산 끝")
print("=" * 78)
# ==============================================================================
# 241강 보편근사정리가 말하는 것 코드 검산
# ==============================================================================
#
# 문제 1. 정리가 무엇을 말하는가
#
# (1) 진술을 정리합니다
# 무엇 무엇인가 덧붙임
# 무엇을 근사하나 닫힌 유계 구간의 연속함수 옹골 집합 위에서
# 무엇으로 은닉층 하나짜리 신경망 층 하나면 충분
# 얼마나 가깝게 임의의 양수 오차 안으로 최대 오차 기준
# 무엇이 필요한가 활성함수가 다항식이 아니면 됨 아주 약한 조건
# 넷째 줄이 뜻밖입니다. 조건이 아주 약합니다
# 239강에서 활성함수가 없으면 안 된다고 했는데 그것이 정확히 이 조건입니다
#
# (2) 계단으로 쌓아 손으로 근사해 봅니다
# 목표 함수는 사인 2x 더하기 0.3x 입니다
# 구간을 K 조각으로 나눠 각 조각에서 상수로 맞춥니다
# 조각 수 필요한 정류 선형 단위 최대 오차 앞 대비
# 4 8 1.222495 기준
# 8 16 0.794139 0.649605
# 16 32 0.422523 0.532051
# 32 64 0.214528 0.507732
# 64 128 0.107675 0.501916
# 조각을 두 배로 하면 오차가 대략 절반이 됩니다
# 계단 하나는 정류 선형 두 개로 만듭니다. 오르는 것 하나와 내리는 것 하나입니다
# 그래서 단위를 늘리면 언제든 원하는 오차 안으로 들어갑니다
#
# (3) 실제로 신경망 가중치를 손으로 정해 확인합니다
# 은닉 단위 84 개로 계단 42 개를 만듭니다
# 양 끝을 한 조각씩 넓혀 경계에서 값이 0 으로 떨어지지 않게 합니다
# x 목표값 신경망 출력 차이
# -2.4990 0.209790 0.229403 0.019613
# -0.9990 -1.209828 -1.221460 0.011632
# 0.0000 0.000000 -0.171938 0.171938
# 0.9990 1.209828 1.221460 0.011632
# 2.4990 -0.209790 -0.229403 0.019613
# 전체 구간 최대 오차는 0.171938 입니다
# 학습을 하나도 안 하고 가중치를 직접 적어 만든 것입니다
# 정리는 이런 구성이 존재한다는 것만 말합니다
#
# (4) 정리가 안 말하는 것을 정리합니다
# 물음 정리가 답하나 덧붙임
# 단위가 몇 개 필요한가 안 말함 지수일 수도
# 학습으로 찾을 수 있나 안 말함 존재만 말함
# 구간 밖에서는 안 말함 유계 구간 위에서만
# 일반화가 되나 안 말함 근사와 다른 물음
# 깊이가 왜 필요한가 안 말함 층 하나로 된다고만
# 다섯 줄 모두 실무에서 정작 알고 싶은 것입니다
# 정리는 가능성의 하한을 긋고 그 위는 아무것도 말하지 않습니다
#
# 문제 2. 단위가 몇 개 필요한가
#
# (1) 무엇이 개수를 정하는지 정리합니다
# 무엇 어떻게 드나 덧붙임
# 함수가 얼마나 굽었나 많이 굽으면 많이 필요 진동 수
# 차원 차원마다 조각이 곱해짐 차원의 저주
# 원하는 정밀도 오차의 역수에 비례 1 차원에서
# 깊이를 쓰면 지수로 줄 수 있음 239강 문제 3
#
# (2) 진동이 빠를수록 단위가 몇 배 필요한지 봅니다
# 조각 직선으로 근사하고 최대 오차가 0.05 아래가 되는 최소 조각 수를 찾습니다
# 진동수 필요한 조각 수 진동수 대비
# 1.0000 8 8.000000
# 2.0000 20 10.000000
# 4.0000 36 9.000000
# 8.0000 62 7.750000
# 진동이 두 배 빨라지면 조각도 대략 두 배 필요합니다
# 셋째 열이 거의 일정합니다
#
# (3) 차원이 늘면 어떻게 되는지 셉니다
# 각 축을 m 등분하면 격자 조각이 m 의 차원 제곱만큼 생깁니다
# 축마다 등분 1 차원 2 차원 5 차원 10 차원
# 2 2 4 32 1024
# 4 4 16 1024 1048576
# 8 8 64 32768 1073741824
# 16 16 256 1048576 1099511627776
# 10 차원에서 축마다 16 등분만 해도 조각이 1 조를 넘습니다
# 209강의 차원의 저주가 여기서 근사의 비용으로 나타납니다
# 그래서 정리는 존재를 말하되 실용성을 말하지 않습니다
#
# (4) 실제로 학습시켜 단위 수를 재 봅니다
# 진동수 4 인 사인함수를 은닉층 하나로 배웁니다
# 학습률은 여섯 개 격자에서 최선을 고릅니다
# 은닉 단위 수 최대 오차 평균 제곱 오차
# 4 1.074010 0.452304
# 8 1.106960 0.190873
# 16 0.618547 0.025530
# 32 0.463264 0.006473
# 64 0.265738 0.001841
# 단위를 늘릴수록 오차가 줄어듭니다
# 정리가 말한 대로입니다. 다만 얼마나 필요한지는 해 봐야 압니다
#
# 문제 3. 학습으로 찾을 수 있는가
#
# (1) 두 물음을 가릅니다
# 물음 누가 답하나 답
# 존재하는가 정리가 답함 예
# 찾을 수 있는가 정리가 안 답함 최적화 문제
# 무엇이 막나 손실면이 안 볼록함 국소 최소와 안장점
# 실제로는 대개 찾음 왜인지는 미해결
#
# (2) 손으로 만든 해와 학습한 해를 견줍니다
# 문제 1 에서 손으로 만든 신경망은 은닉 84 개로 오차 0.171938 였습니다
# 같은 목표 함수를 학습으로 풀어 봅니다
# 은닉 단위 수 학습한 해의 최대 오차 손으로 만든 해 대비
# 16 0.089827 0.522438
# 40 0.054742 0.318381
# 80 0.026693 0.155248
# 160 0.034435 0.200274
# 학습한 해가 손으로 만든 해보다 훨씬 좋습니다
# 손으로 만든 것은 계단이라 조각 안에서 상수인데 학습한 것은 기울기를 씁니다
# 존재를 보이는 구성이 좋은 구성일 필요는 없습니다
#
# (3) 초기화를 바꿔 여러 번 학습시켜 봅니다
# 같은 구조를 씨앗만 바꿔 열 번 학습시킵니다
# 은닉 단위 수 최대 오차 평균 가장 좋은 것 가장 나쁜 것
# 8 0.691764 0.228839 0.997630
# 16 0.121028 0.086471 0.179120
# 64 0.058009 0.037884 0.099125
# 단위가 적으면 초기화에 따라 크게 갈립니다
# 단위가 많으면 어느 씨앗이든 비슷한 자리에 갑니다
# 넉넉하게 두는 것이 학습을 쉽게 만듭니다
#
# (4) 못 찾는 경우를 만들어 봅니다
# 진동이 아주 빠른 함수를 은닉 64 개로 배워 봅니다
# 진동수 최대 오차 목표 진폭 대비 배웠나
# 1.0000 0.020967 0.020967 배움
# 4.0000 0.217997 0.217997 배움
# 12.0000 1.077258 1.077258 못 배움
# 30.0000 1.080803 1.080803 못 배움
# 진동이 빨라지면 같은 단위로 못 배웁니다
# 단위가 모자라서인지 최적화가 실패해서인지는 이 표로는 못 가립니다
# 다음에서 가려 봅니다
#
# (5) 표현력 부족인지 최적화 실패인지 가립니다
# 진동수 12 를 손으로 만든 계단 신경망과 학습한 것으로 견줍니다
# 무엇 은닉 단위 수 최대 오차
# 손으로 만든 계단 64 132 0.549406
# 손으로 만든 계단 128 260 0.279876
# 손으로 만든 계단 256 516 0.140496
# 학습으로 찾은 것 128 1.117063
# 학습으로 찾은 것 256 0.751501
# 학습으로 찾은 것 512 0.941129
# 같은 단위 수에서 손으로 만든 것이 훨씬 낫습니다
# 표현력은 충분한데 학습이 그 자리를 못 찾은 것입니다
# 정리가 답하지 않는 자리가 정확히 여기입니다
#
# 문제 4. 구간 밖에서는 어떻게 되는가
#
# (1) 무엇이 문제인지 정리합니다
# 무엇 무엇인가 덧붙임
# 정리의 범위 닫힌 유계 구간 옹골 집합
# 바깥에서는 아무 보장 없음 정류 선형은 선형으로 뻗음
# 실무에서 학습 자료의 범위 밖 분포 이동
# 어떻게 아나 입력이 범위 안인지 확인 225강 이상탐지
#
# (2) 학습 구간 밖에서 무엇이 나오는지 봅니다
# 진동수 2 인 사인을 -3 에서 3 까지만 학습시킵니다
# x 참값 신경망 출력 차이 어디인가
# 0.0000 0.000000 -0.002971 0.002971 구간 안
# 2.0000 -0.756802 -0.769127 0.012325 구간 안
# 3.0000 -0.279415 -0.299943 0.020527 구간 안
# 4.0000 0.989358 1.267376 0.278018 구간 밖
# 6.0000 -0.536573 4.402014 4.938587 구간 밖
# 10.0000 0.912945 10.671289 9.758344 구간 밖
# 구간 안에서는 잘 맞습니다
# 구간 밖에서는 참값이 진동하는데 출력은 직선으로 뻗습니다
# 정류 선형 신경망은 바깥에서 언제나 선형이기 때문입니다
#
# (3) 바깥에서 정말 선형인지 확인합니다
# x 를 크게 두고 기울기를 봅니다
# x 범위 출력 기울기 이차 차분
# 4.0 에서 6.0 1.567319 3.55e-15
# 10.0 에서 12.0 1.567319 1.42e-14
# 50.0 에서 52.0 1.567319 5.68e-14
# 200.0 에서 202.0 1.567319 2.27e-13
# 기울기가 어느 구간에서나 같고 이차 차분이 0 입니다
# 바깥에서는 정확히 선형입니다. 모든 정류 선형이 한쪽으로 굳었기 때문입니다
# 230강 문제 3 의 없는 조합에서의 추측과 같은 이야기입니다
#
# (4) 활성함수를 바꾸면 달라지는지 봅니다
# 로지스틱을 쓰면 바깥에서 어떻게 되는지 봅니다
# x 참값 정류 선형 로지스틱
# 3.0000 -0.279415 -0.299943 -0.353660
# 6.0000 -0.536573 4.402014 2.296965
# 20.0000 0.745113 26.344477 3.123498
# 100.0000 -0.873297 151.729983 3.210056
# 정류 선형은 끝없이 뻗고 로지스틱은 상수로 눕습니다
# 둘 다 참값과 무관합니다. 어느 쪽도 바깥을 맞히지 못합니다
# 다만 로지스틱 쪽이 터지지는 않습니다
#
# 문제 5. 실무에서 쓰기
#
# (1) 정리를 어떻게 읽을지 정리합니다
# 무엇 무엇인가 왜
# 이렇게 읽으면 안 됨 층 하나면 충분하다 개수를 안 말함
# 이렇게 읽으면 안 됨 신경망은 무엇이든 배운다 학습을 안 말함
# 이렇게 읽어야 함 표현력은 병목이 아니다 다른 것이 병목
# 실제 병목 최적화와 일반화 241강이 아니라 242강부터
#
# (2) 같은 단위 예산을 넓게와 깊게 나눠 봅니다
# 진동수 12 를 같은 단위 예산으로 여러 구조에서 배웁니다
# 구조 전체 단위 최대 오차
# 한 층 96 96 1.025984
# 두 층 48 96 1.000385
# 세 층 32 96 1.017842
# 여섯 층 16 96 0.241617
# 같은 단위 수인데 깊은 쪽이 훨씬 낫습니다
# 정리는 층 하나로 된다고 했지만 실제로는 깊은 쪽이 쉽게 찾습니다
# 239강 문제 3 의 조각 수가 그 이유입니다
#
# (3) 정리가 보장하는 것과 실무의 물음을 견줍니다
# 물음 정리가 답하나 어디서
# 표현할 수 있는가 정리가 예라 함 닫힘
# 몇 개로 표현하는가 정리가 안 말함 문제 2
# 학습이 찾는가 정리가 안 말함 문제 3
# 본 적 없는 자리는 정리가 안 말함 문제 4
# 일반화하는가 정리가 안 말함 210강
# 첫 줄만 정리가 답합니다. 나머지 넷이 이 과목의 나머지 전부입니다
#
# (4) 이 강의를 한 장으로 모읍니다
# 무엇 한 줄로
# 정리가 말하는 것 은닉층 하나로 연속함수를 근사할 수 있습니다
# 조건 활성함수가 다항식만 아니면 됩니다
# 안 말하는 것 하나 단위가 몇 개 필요한지입니다
# 안 말하는 것 둘 학습이 그것을 찾는지입니다
# 안 말하는 것 셋 구간 밖에서 어떻게 되는지입니다
# 그래서 표현력은 병목이 아닙니다
# 진짜 병목 최적화와 일반화입니다
# 242강은 그 최적화 병목을 층을 지나는 그래디언트로 봅니다
#
# ==============================================================================
# 검산 끝
# ==============================================================================