단원과 단원에서 어떻게 학습시킬지를 세웠습니다. 그런데 정작 무엇을 학습시킬지는 안 정했습니다. 지금까지 모형은 계속 선형이었습니다.
네 점짜리 배타적 논리합이 그 예이고, 직선 하나로는 원리적으로 불가능합니다. 그런데 층을 하나 얹으면 풀립니다.
이 강의는 그 사실을 확인하고, 깊이가 무엇을 주는지 세고, 마지막에 신경망도 결국 선형 분류라는 것을 보입니다.
문제. 한계를 확인합니다.
() 퍼셉트론이 무엇인지 정리하세요.
() 네 가지 논리 함수를 푸세요.
() 왜 못 푸는지 보이세요.
() 층을 하나 얹으세요.
생각의 실마리. 퍼셉트론은 가중합의 부호를 봅니다. 그러면 결정 경계가 직선 하나이고, 그 직선으로 못 가르는 배치가 있으면 못 풉니다.
풀이. () 정리합니다.
| 무엇 | 무엇인가 | 덧붙임 |
|---|---|---|
| 무엇을 계산하나 | 가중합의 부호 | 선형 판별 |
| 무엇을 배우나 | 가중치와 치우침 | 틀리면 고침 |
| 무엇을 보장하나 | 선형 분리 가능하면 유한 걸음 | 수렴 정리 |
| 무엇을 못 하나 | 선형으로 못 가르는 문제 | 배타적 논리합 |
셋째 줄이 강한 결과이고 넷째 줄이 그 한계입니다. 강의 선형 판별과 같은 자리에 있습니다.
() 네 가지 논리 함수를 풉니다.
| 함수 | 퍼셉트론이 푸나 | 맞힌 개수 |
|---|---|---|
| 논리곱 | 예 | |
| 논리합 | 예 | |
| 함의 | 예 | |
| 배타적 논리합 | 아니오 |
앞의 셋은 풀고 배타적 논리합만 못 풉니다.
이 실행에서는 네 점 중 둘만 맞혔습니다. 직선 하나로는 아무리 잘 골라도 셋까지가 한계입니다.
() 왜 못 푸는지 보입니다. 풀린다면 네 부등식이 동시에 성립해야 합니다.
| 어느 점 | 무엇을 요구하나 | 부등식 |
|---|---|---|
| 점 이 | 가 이하 | |
| 점 이 | 가 양수 | |
| 점 이 | 가 양수 | |
| 점 이 | 가 이하 |
둘째와 셋째를 더하면 입니다.
첫째에서 이므로 도 양수여야 합니다.
그런데 넷째가 그것을 이하라 합니다. 모순입니다.
선형 판별로는 원리적으로 불가능합니다. 학습을 오래 돌리는 문제가 아닙니다.
() 층을 하나 얹습니다. 은닉 단위 둘을 두고 손으로 가중치를 정합니다.
| 입력 | 은닉 | 은닉 | 출력 | 정답 |
|---|---|---|---|---|
은닉 은 논리합이고 은닉 는 논리곱입니다.
층 하나가 새 특징을 만들고 그 위에서 선형으로 갈립니다.
이 문제에서 배우는 것. 신경망이 하는 일은 새로운 함수족을 발명하는 것이 아닙니다. 원래 좌표에서 안 갈리던 것을 갈릴 수 있는 좌표로 옮기는 것이고, 마지막 판단은 여전히 선형입니다.
확인 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. 이 강의가 다음 강의로 어떻게 이어지는지 정리하세요.
| 이 강의에서 | 다음에서 |
|---|---|
| 활성함수가 표현력을 줌 | 강 활성함수의 수학 |
| 은닉층 하나면 되나 | 강 보편근사정리 |
| 깊으면 학습이 안 됨 | 강 층을 지나는 그래디언트 |
| 조각과 경계 | 강 합성곱 |
| 표현이 따로 존재 | 전이학습 |
강은 그 활성함수를 하나씩 뜯어 봅니다. 왜 정류 선형이 표준이 됐고 무엇이 문제인지가 다음 물음입니다.
정답.
| 기호 | 읽는 법 | 뜻 |
|---|---|---|
| 퍼셉트론 | perceptron | 가중합의 부호로 판단하는 선형 분류기입니다 |
| 배타적 논리합 | exclusive or | 두 입력이 다를 때만 참인 함수입니다 |
| 은닉층 | hidden layer | 입력과 출력 사이의 층입니다 |
| 다층 신경망 | multilayer perceptron | 은닉층이 있는 신경망입니다 |
| 활성함수 | activation function | 층마다 원소별로 적용하는 비선형 함수입니다 |
| 정류 선형 | rectified linear unit | 음수를 으로 자르는 활성함수입니다 |
| 조각 | linear region | 신경망이 선형인 입력 영역입니다 |
| 활성 패턴 | activation pattern | 어느 단위가 켜졌는지의 부호 벡터입니다 |
| 표현 학습 | representation learning | 좌표 자체를 배우는 일입니다 |
| 전이학습 | transfer learning | 배운 표현을 다른 문제에 옮겨 씁니다 |
다음은 240강 활성함수의 수학입니다. 이 강의에서 활성함수가 표현력을 준다는 것을 봤습니다. 다음 강의는 그 활성함수를 하나씩 뜯어 왜 정류 선형이 표준이 됐는지를 봅니다.
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 softmax(Z):
E = np.exp(Z - Z.max(axis=1, keepdims=True))
return E / E.sum(axis=1, keepdims=True)
def ce(Z, Y):
mx = Z.max(axis=1, keepdims=True)
lse = mx[:, 0] + np.log(np.exp(Z - mx).sum(axis=1))
return float(np.mean(lse - (Z * Y).sum(axis=1)))
def init(sizes, rr, last_small=True):
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 last_small and i == len(sizes) - 2:
W = W * 0.05
Ws.append(W)
bs.append(np.zeros(sizes[i + 1]))
return Ws, bs
def forward(Ws, bs, X):
pre, acts = [], [X]
Hh = X
for i in range(len(Ws)):
Z = Hh @ Ws[i] + bs[i]
pre.append(Z)
Hh = relu(Z) if i < len(Ws) - 1 else Z
acts.append(Hh)
return pre, acts
def backward(Ws, bs, X, Y):
m = len(X)
pre, acts = forward(Ws, bs, X)
D = (softmax(pre[-1]) - Y) / m
gW = [None] * len(Ws)
gb = [None] * len(Ws)
for i in range(len(Ws) - 1, -1, -1):
gW[i] = acts[i].T @ D
gb[i] = D.sum(axis=0)
if i > 0:
D = (D @ Ws[i].T) * (pre[i - 1] > 0)
return gW, gb, ce(pre[-1], Y)
def train(Ws, bs, X, Y, lr, steps, B, rr, mom=0.9):
vW = [np.zeros_like(w) for w in Ws]
vb = [np.zeros_like(b) for b in bs]
m = len(X)
for t in range(steps):
idx = rr.integers(0, m, min(B, m))
gW, gb, _ = backward(Ws, bs, X[idx], Y[idx])
for i in range(len(Ws)):
vW[i] = mom * vW[i] + gW[i]
vb[i] = mom * vb[i] + gb[i]
Ws[i] -= lr * vW[i]
bs[i] -= lr * vb[i]
return Ws, bs
def acc(Ws, bs, X, lab):
_, a = forward(Ws, bs, X)
return float((a[-1].argmax(axis=1) == lab).mean())
def onehot(lab, K):
Y = np.zeros((len(lab), K))
Y[np.arange(len(lab)), lab] = 1.0
return Y
print("=" * 78)
print("239강 퍼셉트론과 다층 신경망 코드 검산")
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(" 215강의 선형 판별과 같은 자리에 있습니다")
print()
print(" (2) 네 가지 논리 함수를 풀어 봅니다")
XL = np.array([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]])
tasks = [
("논리곱", np.array([0, 0, 0, 1])),
("논리합", np.array([0, 1, 1, 1])),
("함의", np.array([1, 1, 0, 1])),
("배타적 논리합", np.array([0, 1, 1, 0])),
]
def perceptron(X, t, iters=2000):
w = np.zeros(X.shape[1])
b = 0.0
for _ in range(iters):
err = 0
for i in range(len(X)):
pred = 1 if X[i] @ w + b > 0 else 0
if pred != t[i]:
w += (t[i] - pred) * X[i]
b += (t[i] - pred)
err += 1
if err == 0:
return w, b, True
return w, b, False
w0 = max(pw(t[0]) for t in tasks + [("함수", 0)])
print(" " + rw("함수", w0) + " " + rl("퍼셉트론이 푸나", 18) + " " + rl("맞힌 개수", 12))
for nm, t in tasks:
ww, bb, ok = perceptron(XL, t)
pred = (XL @ ww + bb > 0).astype(int)
print(" " + rw(nm, w0) + " " + rl("예" if ok else "아니오", 18) + " " + rl("%d" % int((pred == t).sum()), 12))
print(" 앞의 셋은 풀고 배타적 논리합만 못 풉니다")
print(" 이 실행에서는 네 점 중 둘만 맞혔습니다")
print(" 직선 하나로는 아무리 잘 골라도 셋까지가 한계입니다")
print()
print(" (3) 왜 못 푸는지 보입니다")
print(" 배타적 논리합이 풀린다면 네 부등식이 동시에 성립해야 합니다")
rows = [
("점 0 0 이 0", "b 가 0 이하", "b <= 0"),
("점 0 1 이 1", "w2 더하기 b 가 양수", "w2 + b > 0"),
("점 1 0 이 1", "w1 더하기 b 가 양수", "w1 + b > 0"),
("점 1 1 이 0", "w1 더하기 w2 더하기 b 가 0 이하", "w1 + w2 + b <= 0"),
]
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(" 둘째와 셋째를 더하면 w1 더하기 w2 더하기 2b 가 양수입니다")
print(" 첫째에서 b 가 0 이하이므로 w1 더하기 w2 더하기 b 도 양수여야 합니다")
print(" 그런데 넷째가 그것을 0 이하라 합니다. 모순입니다")
print(" 선형 판별로는 원리적으로 불가능합니다")
print()
print(" (4) 층을 하나 얹으면 되는 것을 봅니다")
Wh = np.array([[1.0, 1.0], [1.0, 1.0]])
bh = np.array([0.0, -1.0])
Wo = np.array([[1.0], [-2.0]])
bo = np.array([0.0])
Hh = relu(XL @ Wh + bh)
out = Hh @ Wo + bo
print(" 은닉 단위 둘을 두고 손으로 가중치를 정합니다")
print(" " + rl("입력", 10) + " " + rl("은닉 1", 10) + " " + rl("은닉 2", 10) + " " + rl("출력", 10) + " " + rl("정답", 8))
for i in range(4):
print(" " + rl("%d %d" % (XL[i, 0], XL[i, 1]), 10) + " " + rl("%.4f" % Hh[i, 0], 10) + " " + rl("%.4f" % Hh[i, 1], 10) + " " + rl("%.4f" % out[i, 0], 10) + " " + rl("%d" % tasks[3][1][i], 8))
print(" 은닉 1 은 논리합이고 은닉 2 는 논리곱입니다")
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) 활성함수를 빼면 어떻게 되는지 봅니다")
r = np.random.default_rng(20239)
n = 1200
ang = r.uniform(0, 2 * np.pi, n)
rad = r.uniform(0, 1, n)
Xc = np.column_stack([rad * np.cos(ang), rad * np.sin(ang)])
lab = (rad > 0.6).astype(int)
Yc = onehot(lab, 2)
tr = np.arange(900)
te = np.arange(900, n)
print(" 가운데 원과 바깥 고리를 가르는 문제입니다")
print(" " + rw("무엇", 22) + " " + rl("파라미터 수", 14) + " " + rl("학습 정확도", 14) + " " + rl("따로 뗀 정확도", 16))
for nm, sizes, linear in [("선형 하나", [2, 2], False), ("활성 없이 세 층", [2, 32, 32, 2], True), ("활성 넣고 세 층", [2, 32, 32, 2], False)]:
Ws, bs = init(sizes, np.random.default_rng(5))
npar = sum(x.size for x in Ws) + sum(x.size for x in bs)
if linear:
def fwd_lin(Ws, bs, X):
Hh = X
for i in range(len(Ws)):
Hh = Hh @ Ws[i] + bs[i]
return Hh
vW = [np.zeros_like(x) for x in Ws]
vb = [np.zeros_like(x) for x in bs]
rr = np.random.default_rng(7)
for t in range(3000):
idx = rr.integers(0, len(tr), 64)
Xi, Yi = Xc[tr][idx], Yc[tr][idx]
hs = [Xi]
Hh = Xi
for i in range(len(Ws)):
Hh = Hh @ Ws[i] + bs[i]
hs.append(Hh)
D = (softmax(hs[-1]) - Yi) / len(Xi)
for i in range(len(Ws) - 1, -1, -1):
gW = hs[i].T @ D
gb = D.sum(axis=0)
if i > 0:
D = D @ Ws[i].T
vW[i] = 0.9 * vW[i] + gW
vb[i] = 0.9 * vb[i] + gb
Ws[i] -= 0.02 * vW[i]
bs[i] -= 0.02 * vb[i]
a_tr = float((fwd_lin(Ws, bs, Xc[tr]).argmax(axis=1) == lab[tr]).mean())
a_te = float((fwd_lin(Ws, bs, Xc[te]).argmax(axis=1) == lab[te]).mean())
else:
Ws, bs = train(Ws, bs, Xc[tr], Yc[tr], 0.05, 3000, 64, np.random.default_rng(7))
a_tr = acc(Ws, bs, Xc[tr], lab[tr])
a_te = acc(Ws, bs, Xc[te], lab[te])
print(" " + rw(nm, 22) + " " + rl("%d" % npar, 14) + " " + rl("%.6f" % a_tr, 14) + " " + rl("%.6f" % a_te, 16))
print(" 활성함수 없이 층을 쌓으면 선형 하나와 같습니다")
print(" 행렬 곱의 곱이 다시 행렬 곱이기 때문입니다")
print(" 활성함수를 넣는 순간 표현력이 달라집니다")
print()
print(" (3) 선형 층의 곱이 정말 한 행렬인지 확인합니다")
r2g = np.random.default_rng(30239)
A1 = r2g.normal(0, 1, (4, 6))
A2 = r2g.normal(0, 1, (6, 5))
A3 = r2g.normal(0, 1, (5, 3))
Xt = r2g.normal(0, 1, (7, 4))
step = Xt @ A1 @ A2 @ A3
one = Xt @ (A1 @ A2 @ A3)
print(" " + rw("무엇", 26) + " " + rl("모양", 12) + " " + rl("두 계산의 최대 차", 20))
print(" " + rw("세 번 곱한 것", 26) + " " + rl(str(step.shape), 12) + " " + rl("%.2e" % float(np.abs(step - one).max()), 20))
print(" " + rw("먼저 합쳐 한 번 곱한 것", 26) + " " + rl(str(one.shape), 12) + " " + rl("%.2e" % 0.0, 20))
print(" 합친 행렬의 계수는 %d 입니다" % int(np.linalg.matrix_rank(A1 @ A2 @ A3)))
print(" 층을 셋 쌓아도 계수가 가장 좁은 층에 갇힙니다")
print(" 84강의 계수 개념이 여기서 표현력의 한계로 나타납니다")
print()
print(" (4) 은닉 단위 수를 늘려 봅니다")
print(" 배타적 논리합을 닮은 네 조각 문제를 풉니다")
r3 = np.random.default_rng(40239)
n2 = 2000
Xq = r3.uniform(-1, 1, (n2, 2))
labq = ((Xq[:, 0] > 0) ^ (Xq[:, 1] > 0)).astype(int)
Yq = onehot(labq, 2)
trq = np.arange(1400)
teq = np.arange(1400, n2)
print(" " + rl("은닉 단위 수", 14) + " " + rl("파라미터 수", 14) + " " + rl("학습 정확도", 14) + " " + rl("따로 뗀 정확도", 16))
for h in [1, 2, 4, 16, 64]:
Ws, bs = init([2, h, 2], np.random.default_rng(9))
npar = sum(x.size for x in Ws) + sum(x.size for x in bs)
Ws, bs = train(Ws, bs, Xq[trq], Yq[trq], 0.05, 4000, 64, np.random.default_rng(11))
print(" " + rl("%d" % h, 14) + " " + rl("%d" % npar, 14) + " " + rl("%.6f" % acc(Ws, bs, Xq[trq], labq[trq]), 14) + " " + rl("%.6f" % acc(Ws, bs, Xq[teq], labq[teq]), 16))
print(" 은닉 단위 하나로는 반밖에 못 맞힙니다")
print(" 둘부터 풀리기 시작하고 넷이면 거의 완전합니다")
print(" 네 조각을 가르려면 경계가 둘 필요하고 단위 하나가 경계 하나입니다")
print()
print("문제 3. 넓게 할까 깊게 할까")
print()
print(" (1) 두 방향을 정리합니다")
rows = [
("넓게", "한 층의 단위를 늘림", "파라미터가 선형으로"),
("깊게", "층을 늘림", "조각이 지수로"),
("표현력", "깊이가 유리한 함수가 있음", "합성으로 만들어짐"),
("학습", "깊으면 어려워짐", "246강"),
]
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(50239)
n3 = 3000
Xs = r4.uniform(-3, 3, (n3, 2))
z = np.sin(1.8 * Xs[:, 0]) * np.cos(1.8 * Xs[:, 1])
labs = (z > 0).astype(int)
Ys = onehot(labs, 2)
trs = np.arange(2200)
tes = np.arange(2200, n3)
cfgs = [
("한 층 넓게", [2, 96, 2]),
("두 층", [2, 40, 40, 2]),
("네 층", [2, 24, 24, 24, 24, 2]),
("여덟 층", [2, 15, 15, 15, 15, 15, 15, 15, 15, 2]),
]
w0 = max(pw(c[0]) for c in cfgs + [("구조", 0)])
print(" 사인 곱 코사인 부호를 맞히는 문제입니다. 조각이 여럿입니다")
print(" " + rw("구조", w0) + " " + rl("층 수", 10) + " " + rl("파라미터 수", 14) + " " + rl("학습 정확도", 14) + " " + rl("따로 뗀 정확도", 16))
for nm, sizes in cfgs:
Ws, bs = init(sizes, np.random.default_rng(13))
npar = sum(x.size for x in Ws) + sum(x.size for x in bs)
Ws, bs = train(Ws, bs, Xs[trs], Ys[trs], 0.03, 6000, 64, np.random.default_rng(17))
print(" " + rw(nm, w0) + " " + rl("%d" % (len(sizes) - 1), 10) + " " + rl("%d" % npar, 14) + " " + rl("%.6f" % acc(Ws, bs, Xs[trs], labs[trs]), 14) + " " + rl("%.6f" % acc(Ws, bs, Xs[tes], labs[tes]), 16))
print(" 파라미터 수가 비슷한데 결과가 다릅니다")
print(" 너무 깊으면 오히려 나빠집니다. 233강 문제 2 의 신호 소실 때문입니다")
print()
print(" (3) 한 층이 만들 수 있는 조각 수를 셉니다")
print(" 2 차원 평면에 직선 n 개를 그으면 조각이 최대 몇 개인지 셉니다")
print(" " + rl("직선 수", 10) + " " + rl("최대 조각 수", 14) + " " + rl("앞 대비 늘어난 수", 20))
prev = None
for nh in [1, 2, 4, 8, 16, 96]:
reg = 1 + nh + nh * (nh - 1) // 2
inc = "기준" if prev is None else "%d" % (reg - prev)
print(" " + rl("%d" % nh, 10) + " " + rl("%d" % reg, 14) + " " + rl(inc, 20))
prev = reg
print(" 직선 하나를 더 그으면 조각이 앞의 직선 수 더하기 1 만큼 늘어납니다")
print(" 그래서 조각 수가 직선 수의 제곱에 비례합니다")
print(" 은닉 단위 하나가 직선 하나이므로 한 층 신경망의 상한이 이 값입니다")
print(" 층을 쌓으면 앞 층이 만든 조각을 뒤 층이 다시 자릅니다")
print(" 그래서 같은 단위 수로도 훨씬 많은 조각을 만들 수 있습니다")
print(" (4) 실제로 조각이 몇 개 생기는지 셉니다")
def count_regions(Ws, bs, X):
pats = set()
Hh = X
codes = []
for i in range(len(Ws) - 1):
Z = Hh @ Ws[i] + bs[i]
codes.append((Z > 0).astype(np.int8))
Hh = relu(Z)
C = np.hstack(codes)
for row in C:
pats.add(row.tobytes())
return len(pats)
r5 = np.random.default_rng(60239)
grid = r5.uniform(-3, 3, (4000, 2))
print(" 학습이 끝난 신경망에 격자 점 4000 개를 넣어 활성 패턴을 셉니다")
print(" " + rw("구조", w0) + " " + rl("전체 단위", 12) + " " + rl("한 층이라면 상한", 18) + " " + rl("실제 조각 수", 16) + " " + rl("상한 대비", 12))
for nm, sizes in cfgs:
Ws, bs = init(sizes, np.random.default_rng(13))
Ws, bs = train(Ws, bs, Xs[trs], Ys[trs], 0.03, 6000, 64, np.random.default_rng(17))
total = sum(sizes[1:-1])
bound = 1 + total + total * (total - 1) // 2
cnt = count_regions(Ws, bs, grid)
print(" " + rw(nm, w0) + " " + rl("%d" % total, 12) + " " + rl("%d" % bound, 18) + " " + rl("%d" % cnt, 16) + " " + rl("%.6f" % (cnt / float(bound)), 12))
print(" 같은 전체 단위 수를 한 층에 몰아넣었을 때의 상한과 견줍니다")
print(" 깊은 신경망이 상한 대비 더 높은 비율을 씁니다")
print(" 다만 어느 것도 상한 근처에는 못 갑니다. 학습이 필요한 만큼만 만들기 때문입니다")
print()
print("문제 4. 신경망도 결국 선형 분류입니다")
print()
print(" (1) 무엇이 달라지는지 정리합니다")
rows = [
("마지막 층", "선형 분류기", "216강과 같음"),
("앞의 층들", "좌표를 만드는 일", "228강의 특성공학"),
("차이", "손으로가 아니라 배워서", "이것이 전부"),
("그래서", "깊은 특성공학이라 불러도 됨", "이름만 다름"),
]
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) 마지막 은닉층에서 선형으로 갈리는지 봅니다")
Ws, bs = init([2, 32, 32, 2], np.random.default_rng(21))
Ws, bs = train(Ws, bs, Xs[trs], Ys[trs], 0.03, 6000, 64, np.random.default_rng(23))
_, acts = forward(Ws, bs, Xs)
Hlast = acts[-2]
def logit_fit(Xm, ym, steps=2000, lr=0.5, lam=1e-4):
A = np.hstack([Xm, np.ones((len(Xm), 1))])
b = np.zeros(A.shape[1])
for _ in range(steps):
pr = 1.0 / (1.0 + np.exp(-np.clip(A @ b, -500, 500)))
b -= lr * (A.T @ (pr - ym) / len(ym) + lam * b)
return b
def logit_acc(Xm, ym, b):
A = np.hstack([Xm, np.ones((len(Xm), 1))])
pr = 1.0 / (1.0 + np.exp(-np.clip(A @ b, -500, 500)))
return float(((pr > 0.5).astype(int) == ym).mean())
b_raw = logit_fit(Xs[trs], labs[trs].astype(float))
b_h = logit_fit(Hlast[trs], labs[trs].astype(float))
print(" 원래 좌표와 마지막 은닉층 좌표에서 각각 로지스틱을 돌립니다")
print(" " + rw("어느 좌표에서", 22) + " " + rl("차원", 10) + " " + rl("학습 정확도", 14) + " " + rl("따로 뗀 정확도", 16))
print(" " + rw("원래 입력", 22) + " " + rl("%d" % Xs.shape[1], 10) + " " + rl("%.6f" % logit_acc(Xs[trs], labs[trs].astype(float), b_raw), 14) + " " + rl("%.6f" % logit_acc(Xs[tes], labs[tes].astype(float), b_raw), 16))
print(" " + rw("마지막 은닉층", 22) + " " + rl("%d" % Hlast.shape[1], 10) + " " + rl("%.6f" % logit_acc(Hlast[trs], labs[trs].astype(float), b_h), 14) + " " + rl("%.6f" % logit_acc(Hlast[tes], labs[tes].astype(float), b_h), 16))
print(" 원래 좌표에서는 선형으로 못 가릅니다")
print(" 마지막 은닉층 좌표에서는 선형으로 갈립니다")
print(" 신경망이 한 일은 갈릴 수 있는 좌표를 만든 것입니다")
print()
print(" (3) 층을 지날수록 갈리기 쉬워지는지 봅니다")
Ws4, bs4 = init([2, 32, 32, 32, 2], np.random.default_rng(25))
Ws4, bs4 = train(Ws4, bs4, Xs[trs], Ys[trs], 0.03, 6000, 64, np.random.default_rng(27))
_, acts4 = forward(Ws4, bs4, Xs)
print(" 네 층 신경망의 각 층에서 선형 분류를 해 봅니다")
print(" " + rl("어느 층", 12) + " " + rl("차원", 10) + " " + rl("선형 분류 학습 정확도", 24) + " " + rl("따로 뗀 정확도", 16))
for li in range(len(acts4) - 1):
Hm = acts4[li]
bb = logit_fit(Hm[trs], labs[trs].astype(float))
nm = "입력" if li == 0 else "은닉 %d" % li
print(" " + rl(nm, 12) + " " + rl("%d" % Hm.shape[1], 10) + " " + rl("%.6f" % logit_acc(Hm[trs], labs[trs].astype(float), bb), 24) + " " + rl("%.6f" % logit_acc(Hm[tes], labs[tes].astype(float), bb), 16))
print(" 층을 지날수록 선형으로 갈리기 쉬워집니다")
print(" 각 층이 조금씩 좌표를 펴 나갑니다")
print(" 224강의 차원축소가 정보를 줄이는 것이라면 여기는 늘려서 펴는 것입니다")
print()
print(" (4) 은닉층을 얼려 두고 마지막만 다시 학습해 봅니다")
Wf = [x.copy() for x in Ws]
bf = [x.copy() for x in bs]
r6 = np.random.default_rng(70239)
Wf[-1] = r6.normal(0, 0.05, Wf[-1].shape)
bf[-1] = np.zeros_like(bf[-1])
_, af0 = forward(Wf, bf, Xs)
before = float((af0[-1].argmax(axis=1) == labs).mean())
Hfix = acts[-2]
vW = np.zeros_like(Wf[-1])
vb = np.zeros_like(bf[-1])
for t in range(3000):
idx = r6.integers(0, len(trs), 64)
Hi = Hfix[trs][idx]
Yi = Ys[trs][idx]
Zi = Hi @ Wf[-1] + bf[-1]
D = (softmax(Zi) - Yi) / len(Hi)
vW = 0.9 * vW + Hi.T @ D
vb = 0.9 * vb + D.sum(axis=0)
Wf[-1] -= 0.05 * vW
bf[-1] -= 0.05 * vb
Zte = Hfix @ Wf[-1] + bf[-1]
after = float((Zte.argmax(axis=1) == labs).mean())
print(" 마지막 층만 지우고 다시 학습시킵니다")
print(" " + rw("무엇", 26) + " " + rl("전체 정확도", 14))
print(" " + rw("마지막 층을 지운 직후", 26) + " " + rl("%.6f" % before, 14))
print(" " + rw("마지막 층만 다시 학습", 26) + " " + rl("%.6f" % after, 14))
print(" " + rw("처음부터 다 학습한 것", 26) + " " + rl("%.6f" % float((acts[-1].argmax(axis=1) == labs).mean()), 14))
print(" 은닉층이 만든 좌표만 있으면 마지막 층은 금방 다시 배웁니다")
print(" 다시 학습한 쪽이 오히려 조금 낫습니다. 마지막 층만 3000 걸음 더 돌았기 때문입니다")
print(" 학습된 표현이 마지막 층과 따로 존재한다는 뜻입니다")
print(" 전이학습이 되는 이유가 이것입니다")
print()
print("문제 5. 실무에서 쓰기")
print()
print(" (1) 구조를 정하는 순서를 정리합니다")
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(" (2) 파라미터 수가 성능을 정하지 않는 것을 봅니다")
print(" 같은 파라미터 수 근처에서 구조만 바꿉니다")
print(" " + rw("구조", 20) + " " + rl("파라미터 수", 14) + " " + rl("따로 뗀 정확도", 16))
for nm, sizes in [("2 에서 200", [2, 200, 2]), ("2 에서 100 둘", [2, 100, 100, 2]), ("2 에서 45 넷", [2, 45, 45, 45, 45, 2])]:
Ws2, bs2 = init(sizes, np.random.default_rng(31))
npar = sum(x.size for x in Ws2) + sum(x.size for x in bs2)
Ws2, bs2 = train(Ws2, bs2, Xs[trs], Ys[trs], 0.03, 6000, 64, np.random.default_rng(33))
print(" " + rw(nm, 20) + " " + rl("%d" % npar, 14) + " " + rl("%.6f" % acc(Ws2, bs2, Xs[tes], labs[tes]), 16))
print(" 파라미터가 열 배 많은 두 층이 한 층보다 겨우 0.01 좋습니다")
print(" 파라미터가 그보다 적은 네 층이 가장 좋습니다")
print(" 무엇을 쌓느냐가 몇 개를 쌓느냐보다 중요합니다")
print()
print(" (3) 출력층을 어떻게 두는지 정리합니다")
rows = [
("두 갈래 분류", "출력 하나에 로지스틱", "또는 둘에 소프트맥스"),
("여러 갈래 분류", "갈래 수만큼에 소프트맥스", "203강"),
("회귀", "출력 하나에 활성 없음", "제곱 손실"),
("여러 목표", "목표 수만큼", "손실을 더함"),
("확률이 필요하면", "마지막에 활성을 안 넣음", "손실과 함께 묶음"),
]
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(" 마지막 줄이 231강 문제 3 에서 본 것입니다")
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(" 240강은 그 활성함수를 하나씩 뜯어 봅니다")
print()
print("=" * 78)
print("검산 끝")
print("=" * 78)
# ==============================================================================
# 239강 퍼셉트론과 다층 신경망 코드 검산
# ==============================================================================
#
# 문제 1. 퍼셉트론이 못 하는 것
#
# (1) 퍼셉트론이 무엇인지 정리합니다
# 무엇 무엇인가 덧붙임
# 무엇을 계산하나 가중합의 부호 선형 판별
# 무엇을 배우나 가중치와 치우침 틀리면 고침
# 무엇을 보장하나 선형 분리 가능하면 유한 걸음 수렴 정리
# 무엇을 못 하나 선형으로 못 가르는 문제 배타적 논리합
# 셋째 줄이 강한 결과이고 넷째 줄이 그 한계입니다
# 215강의 선형 판별과 같은 자리에 있습니다
#
# (2) 네 가지 논리 함수를 풀어 봅니다
# 함수 퍼셉트론이 푸나 맞힌 개수
# 논리곱 예 4
# 논리합 예 4
# 함의 예 4
# 배타적 논리합 아니오 2
# 앞의 셋은 풀고 배타적 논리합만 못 풉니다
# 이 실행에서는 네 점 중 둘만 맞혔습니다
# 직선 하나로는 아무리 잘 골라도 셋까지가 한계입니다
#
# (3) 왜 못 푸는지 보입니다
# 배타적 논리합이 풀린다면 네 부등식이 동시에 성립해야 합니다
# 어느 점 무엇을 요구하나 부등식
# 점 0 0 이 0 b 가 0 이하 b <= 0
# 점 0 1 이 1 w2 더하기 b 가 양수 w2 + b > 0
# 점 1 0 이 1 w1 더하기 b 가 양수 w1 + b > 0
# 점 1 1 이 0 w1 더하기 w2 더하기 b 가 0 이하 w1 + w2 + b <= 0
# 둘째와 셋째를 더하면 w1 더하기 w2 더하기 2b 가 양수입니다
# 첫째에서 b 가 0 이하이므로 w1 더하기 w2 더하기 b 도 양수여야 합니다
# 그런데 넷째가 그것을 0 이하라 합니다. 모순입니다
# 선형 판별로는 원리적으로 불가능합니다
#
# (4) 층을 하나 얹으면 되는 것을 봅니다
# 은닉 단위 둘을 두고 손으로 가중치를 정합니다
# 입력 은닉 1 은닉 2 출력 정답
# 0 0 0.0000 0.0000 0.0000 0
# 0 1 1.0000 0.0000 1.0000 1
# 1 0 1.0000 0.0000 1.0000 1
# 1 1 2.0000 1.0000 0.0000 0
# 은닉 1 은 논리합이고 은닉 2 는 논리곱입니다
# 배타적 논리합은 논리합 빼기 두 배의 논리곱입니다
# 층 하나가 새 특징을 만들고 그 위에서 선형으로 갈립니다
#
# 문제 2. 층이 무엇을 만드는가
#
# (1) 층의 역할을 정리합니다
# 무엇 무엇을 하나 덧붙임
# 선형 부분 축을 갈아탐 회전과 늘이기
# 활성함수 굽힘 이것이 없으면 층이 무의미
# 층을 쌓음 굽은 조각을 겹침 표현력이 커짐
# 마지막 층 선형 분류 만든 좌표 위에서
#
# (2) 활성함수를 빼면 어떻게 되는지 봅니다
# 가운데 원과 바깥 고리를 가르는 문제입니다
# 무엇 파라미터 수 학습 정확도 따로 뗀 정확도
# 선형 하나 6 0.577778 0.596667
# 활성 없이 세 층 1218 0.577778 0.596667
# 활성 넣고 세 층 1218 0.996667 0.986667
# 활성함수 없이 층을 쌓으면 선형 하나와 같습니다
# 행렬 곱의 곱이 다시 행렬 곱이기 때문입니다
# 활성함수를 넣는 순간 표현력이 달라집니다
#
# (3) 선형 층의 곱이 정말 한 행렬인지 확인합니다
# 무엇 모양 두 계산의 최대 차
# 세 번 곱한 것 (7, 3) 7.11e-15
# 먼저 합쳐 한 번 곱한 것 (7, 3) 0.00e+00
# 합친 행렬의 계수는 3 입니다
# 층을 셋 쌓아도 계수가 가장 좁은 층에 갇힙니다
# 84강의 계수 개념이 여기서 표현력의 한계로 나타납니다
#
# (4) 은닉 단위 수를 늘려 봅니다
# 배타적 논리합을 닮은 네 조각 문제를 풉니다
# 은닉 단위 수 파라미터 수 학습 정확도 따로 뗀 정확도
# 1 7 0.638571 0.648333
# 2 12 0.847143 0.816667
# 4 22 0.982143 0.978333
# 16 82 0.996429 0.993333
# 64 322 0.996429 0.993333
# 은닉 단위 하나로는 반밖에 못 맞힙니다
# 둘부터 풀리기 시작하고 넷이면 거의 완전합니다
# 네 조각을 가르려면 경계가 둘 필요하고 단위 하나가 경계 하나입니다
#
# 문제 3. 넓게 할까 깊게 할까
#
# (1) 두 방향을 정리합니다
# 무엇 무엇을 하나 덧붙임
# 넓게 한 층의 단위를 늘림 파라미터가 선형으로
# 깊게 층을 늘림 조각이 지수로
# 표현력 깊이가 유리한 함수가 있음 합성으로 만들어짐
# 학습 깊으면 어려워짐 246강
#
# (2) 파라미터 수를 맞춰 견줍니다
# 사인 곱 코사인 부호를 맞히는 문제입니다. 조각이 여럿입니다
# 구조 층 수 파라미터 수 학습 정확도 따로 뗀 정확도
# 한 층 넓게 2 482 0.851364 0.848750
# 두 층 3 1842 0.915455 0.913750
# 네 층 5 1922 0.949091 0.946250
# 여덟 층 9 1757 0.825455 0.831250
# 파라미터 수가 비슷한데 결과가 다릅니다
# 너무 깊으면 오히려 나빠집니다. 233강 문제 2 의 신호 소실 때문입니다
#
# (3) 한 층이 만들 수 있는 조각 수를 셉니다
# 2 차원 평면에 직선 n 개를 그으면 조각이 최대 몇 개인지 셉니다
# 직선 수 최대 조각 수 앞 대비 늘어난 수
# 1 2 기준
# 2 4 2
# 4 11 7
# 8 37 26
# 16 137 100
# 96 4657 4520
# 직선 하나를 더 그으면 조각이 앞의 직선 수 더하기 1 만큼 늘어납니다
# 그래서 조각 수가 직선 수의 제곱에 비례합니다
# 은닉 단위 하나가 직선 하나이므로 한 층 신경망의 상한이 이 값입니다
# 층을 쌓으면 앞 층이 만든 조각을 뒤 층이 다시 자릅니다
# 그래서 같은 단위 수로도 훨씬 많은 조각을 만들 수 있습니다
# (4) 실제로 조각이 몇 개 생기는지 셉니다
# 학습이 끝난 신경망에 격자 점 4000 개를 넣어 활성 패턴을 셉니다
# 구조 전체 단위 한 층이라면 상한 실제 조각 수 상한 대비
# 한 층 넓게 96 4657 367 0.078806
# 두 층 80 3241 1019 0.314409
# 네 층 96 4657 1775 0.381147
# 여덟 층 120 7261 1666 0.229445
# 같은 전체 단위 수를 한 층에 몰아넣었을 때의 상한과 견줍니다
# 깊은 신경망이 상한 대비 더 높은 비율을 씁니다
# 다만 어느 것도 상한 근처에는 못 갑니다. 학습이 필요한 만큼만 만들기 때문입니다
#
# 문제 4. 신경망도 결국 선형 분류입니다
#
# (1) 무엇이 달라지는지 정리합니다
# 무엇 무엇인가 덧붙임
# 마지막 층 선형 분류기 216강과 같음
# 앞의 층들 좌표를 만드는 일 228강의 특성공학
# 차이 손으로가 아니라 배워서 이것이 전부
# 그래서 깊은 특성공학이라 불러도 됨 이름만 다름
#
# (2) 마지막 은닉층에서 선형으로 갈리는지 봅니다
# 원래 좌표와 마지막 은닉층 좌표에서 각각 로지스틱을 돌립니다
# 어느 좌표에서 차원 학습 정확도 따로 뗀 정확도
# 원래 입력 2 0.506818 0.476250
# 마지막 은닉층 32 0.930909 0.910000
# 원래 좌표에서는 선형으로 못 가릅니다
# 마지막 은닉층 좌표에서는 선형으로 갈립니다
# 신경망이 한 일은 갈릴 수 있는 좌표를 만든 것입니다
#
# (3) 층을 지날수록 갈리기 쉬워지는지 봅니다
# 네 층 신경망의 각 층에서 선형 분류를 해 봅니다
# 어느 층 차원 선형 분류 학습 정확도 따로 뗀 정확도
# 입력 2 0.506818 0.476250
# 은닉 1 32 0.523182 0.557500
# 은닉 2 32 0.695000 0.696250
# 은닉 3 32 0.963636 0.942500
# 층을 지날수록 선형으로 갈리기 쉬워집니다
# 각 층이 조금씩 좌표를 펴 나갑니다
# 224강의 차원축소가 정보를 줄이는 것이라면 여기는 늘려서 펴는 것입니다
#
# (4) 은닉층을 얼려 두고 마지막만 다시 학습해 봅니다
# 마지막 층만 지우고 다시 학습시킵니다
# 무엇 전체 정확도
# 마지막 층을 지운 직후 0.472667
# 마지막 층만 다시 학습 0.922333
# 처음부터 다 학습한 것 0.897667
# 은닉층이 만든 좌표만 있으면 마지막 층은 금방 다시 배웁니다
# 다시 학습한 쪽이 오히려 조금 낫습니다. 마지막 층만 3000 걸음 더 돌았기 때문입니다
# 학습된 표현이 마지막 층과 따로 존재한다는 뜻입니다
# 전이학습이 되는 이유가 이것입니다
#
# 문제 5. 실무에서 쓰기
#
# (1) 구조를 정하는 순서를 정리합니다
# 순서 무엇을 하나
# 먼저 층 수를 두셋으로 시작합니다
# 다음 너비를 늘려 봅니다
# 다음 학습이 되면 깊이를 늘립니다
# 안 되면 정규화와 잔차 연결을 넣습니다
# 언제나 파라미터 수보다 학습 가능성을 봅니다
#
# (2) 파라미터 수가 성능을 정하지 않는 것을 봅니다
# 같은 파라미터 수 근처에서 구조만 바꿉니다
# 구조 파라미터 수 따로 뗀 정확도
# 2 에서 200 1002 0.870000
# 2 에서 100 둘 10602 0.880000
# 2 에서 45 넷 6437 0.948750
# 파라미터가 열 배 많은 두 층이 한 층보다 겨우 0.01 좋습니다
# 파라미터가 그보다 적은 네 층이 가장 좋습니다
# 무엇을 쌓느냐가 몇 개를 쌓느냐보다 중요합니다
#
# (3) 출력층을 어떻게 두는지 정리합니다
# 무엇 출력층 덧붙임
# 두 갈래 분류 출력 하나에 로지스틱 또는 둘에 소프트맥스
# 여러 갈래 분류 갈래 수만큼에 소프트맥스 203강
# 회귀 출력 하나에 활성 없음 제곱 손실
# 여러 목표 목표 수만큼 손실을 더함
# 확률이 필요하면 마지막에 활성을 안 넣음 손실과 함께 묶음
# 마지막 줄이 231강 문제 3 에서 본 것입니다
# 소프트맥스와 교차엔트로피를 한 연산으로 묶습니다
#
# (4) 이 강의를 한 장으로 모읍니다
# 무엇 한 줄로
# 퍼셉트론이 못 하는 것 선형으로 못 가르는 문제입니다
# 층 하나가 하는 일 새 좌표를 만듭니다
# 활성함수가 없으면 층을 쌓아도 선형 하나입니다
# 깊이가 주는 것 조각 수가 지수로 늘어납니다
# 다만 너무 깊으면 학습이 안 됩니다
# 마지막 층은 여전히 선형 분류입니다
# 그래서 신경망은 좌표를 배우는 특성공학입니다
# 240강은 그 활성함수를 하나씩 뜯어 봅니다
#
# ==============================================================================
# 검산 끝
# ==============================================================================