강에서 활성함수가 표현력을 준다는 것을 봤습니다. 없으면 층을 쌓아도 선형 하나로 접혔습니다.
아무 비선형 함수나 되는 것이 아닙니다. 역전파가 지나가야 하고, 그러려면 도함수가 살아 있어야 합니다.
이 강의는 여섯 함수를 나란히 놓고 값과 도함수를 재고, 깊은 신경망에서 무엇이 무너지는지 확인합니다.
문제. 조건을 정합니다.
() 요구 조건을 정리하세요.
() 여섯 함수의 값과 도함수를 보세요.
() 도함수를 수치 미분으로 확인하세요.
() 계산 비용을 세세요.
생각의 실마리. 활성함수는 원소마다 한 번씩 불립니다. 층이 백 개이고 너비가 천이면 십만 번이므로, 함수 하나의 비용이 그대로 곱해집니다.
풀이. () 정리합니다.
| 무엇 | 왜 필요한가 | 어디서 |
|---|---|---|
| 비선형 | 선형이면 층이 접힘 | 강 문제 |
| 미분 가능 | 역전파가 지나가야 함 | 거의 모든 곳에서 |
| 도함수가 살아 있음 | 이면 신호가 막힘 | 강 문제 |
| 계산이 쌈 | 원소마다 부르므로 | 지수 하나도 비쌈 |
| 출력 범위 | 너무 커지면 안 됨 | 다음 층의 입력 |
셋째 줄이 실무에서 가장 크게 듭니다.
() 여섯 함수의 값을 봅니다.
| 함수 | 에서 | 에서 | 에서 | 에서 | 에서 |
|---|---|---|---|---|---|
| 로지스틱 | |||||
| 하이퍼볼릭 탄젠트 | |||||
| 정류 선형 | |||||
| 새는 정류 선형 | |||||
| 지수 선형 | |||||
| 가우스 오차 선형 |
도함수입니다.
| 함수 | 에서 | 에서 | 에서 | 에서 | 에서 |
|---|---|---|---|---|---|
| 로지스틱 | |||||
| 하이퍼볼릭 탄젠트 | |||||
| 정류 선형 | |||||
| 새는 정류 선형 | |||||
| 지수 선형 | |||||
| 가우스 오차 선형 |
로지스틱과 탄젠트는 양끝에서 도함수가 에 붙습니다. 에서 과 입니다.
정류 선형은 음수 쪽에서 정확히 입니다.
가우스 오차 선형은 도함수가 음수가 되기도 합니다. 에서 이고, 비단조 함수이기 때문입니다.
() 도함수를 수치 미분으로 확인합니다.
| 함수 | 최대 상대 오차 | 어디서 큰가 |
|---|---|---|
| 로지스틱 | 일반 자리 | |
| 하이퍼볼릭 탄젠트 | 일반 자리 | |
| 정류 선형 | 근처 | |
| 새는 정류 선형 | 근처 | |
| 지수 선형 | 근처 | |
| 가우스 오차 선형 | 일반 자리 |
정류 선형은 근처에서 상대 오차가 입니다. 미분 불가능한 점이 하나 있기 때문입니다.
강 심화 에서 본 대로 측도 인 점이라 실전에서는 거의 안 부딪힙니다. 다만 그래디언트 검사에서 몇 개 원소만 실패하면 이것을 의심해야 합니다.
() 계산 비용을 셉니다.
| 함수 | 무엇이 필요한가 | 값 |
|---|---|---|
| 정류 선형 | 비교 하나 | 가장 쌈 |
| 새는 정류 선형 | 비교 하나와 곱 하나 | 거의 같음 |
| 하이퍼볼릭 탄젠트 | 지수 둘 | 비쌈 |
| 로지스틱 | 지수 하나 | 비쌈 |
| 지수 선형 | 음수에서만 지수 | 가운데 |
| 가우스 오차 선형 | 지수 하나와 곱 여럿 | 가장 비쌈 |
정류 선형이 표준이 된 첫째 이유가 이것입니다. 비교 한 번이면 끝입니다.
이 문제에서 배우는 것. 활성함수는 수학적 성질과 계산 비용을 함께 봐야 합니다. 그리고 미분 불가능한 점 하나는 실전에서 문제가 안 됩니다.
확인 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. 로지스틱이 은닉층에서는 최악인데 출력층에서 표준인 이유를 쓰세요.
답. 고르는 기준이 다르고 출력층은 층을 더 지나지 않기 때문입니다.
| 유형 | 무엇을 묻나 | 어디를 보나 |
|---|---|---|
| 요구 조건 | 도함수가 살아 있어야 함 | 문제 |
| 계산 비용 | 정류 선형이 가장 쌈 | 문제 |
| 도함수 최댓값 | 인지 아닌지 | 문제 |
| 깊이 의 신호 | 로지스틱만 10^ | 문제 |
| 죽은 단위 | 정류 선형만 | 문제 |
| 학습률과 죽음 | 에서 퍼센트 | 문제 |
| 출력 평균 | 중심이 아님 | 문제 |
| 얕으면 | 아무거나 비슷 | 문제 |
| 새는 기울기 | 이면 선형 | 문제 |
| 구조가 더 큼 | 깊이 에서 | 문제 |
여섯 함수를 한자리에 모읍니다.
| 함수 | 도함수 최댓값 | 죽나 | 비용 | 언제 |
|---|---|---|---|---|
| 로지스틱 | 안 죽음 | 비쌈 | 출력층만 | |
| 탄젠트 | 안 죽음 | 비쌈 | 순환 구조 | |
| 정류 선형 | 죽음 | 쌈 | 기본 | |
| 새는 정류 선형 | 안 죽음 | 쌈 | 죽음이 걱정될 때 | |
| 지수 선형 | 안 죽음 | 가운데 | 대안 | |
| 가우스 오차 선형 | 안 죽음 | 비쌈 | 트랜스포머 |
문제 6. 활성함수의 도함수가 이면 무엇이 일어나는지 쓰세요.
답. 그 단위는 신호가 막혀 영원히 안 배웁니다.
문제 7. 검산에서 탄젠트의 에서의 도함수를 쓰세요.
답. 입니다.
문제 8. 검산에서 정류 선형의 최대 상대 오차와 그 자리를 쓰세요.
답. 이고 근처입니다.
문제 9. 로지스틱의 도함수 최댓값과 그 결과를 쓰세요.
답. 이고 층마다 최소 네 배씩 줄어듭니다.
문제 10. 검산에서 깊이 일 때 로지스틱과 탄젠트의 첫 층 나누기 마지막을 쓰세요.
답. 과 입니다.
문제 11. 검산에서 깊이 일 때 로지스틱과 탄젠트의 따로 뗀 정확도를 쓰세요.
답. 과 입니다.
문제 12. 정류 선형에서만 죽은 단위가 생기는 이유를 쓰세요.
답. 음수 쪽 도함수가 정확히 인 유일한 함수이기 때문입니다.
문제 13. 검산에서 학습률 과 의 죽은 비율을 쓰세요.
답. 과 입니다.
문제 14. 검산에서 정류 선형의 출력 평균과 입력 대비 분산 비를 쓰세요.
답. 와 입니다.
문제 15. 검산에서 새는 정류 선형의 기울기 과 의 따로 뗀 정확도를 쓰세요.
답. 과 입니다.
문제 16. 검산에서 앞은 탄젠트 끝만 정류 선형의 따로 뗀 정확도를 쓰세요.
답. 입니다.
문제 17. 깊이 에서 무엇이 잔차 연결의 이득인지 쓰세요.
답. 이 이 됩니다.
문제 18. 은닉층과 출력층의 활성함수를 고르는 기준 차이를 쓰세요.
답. 은닉층은 학습이 되게 고르고 출력층은 값의 범위로 고릅니다.
심화 1. 정류 선형이 왜 표현력을 잃지 않는지 보이세요.
정류 선형은 조각마다 선형이지만 전체로는 선형이 아닙니다.
| 무엇 | 왜 |
|---|---|
| 조각 안에서 | 항등함수 또는 |
| 조각 경계에서 | 기울기가 바뀜 |
| 여러 단위를 합치면 | 조각 직선 |
강 문제 의 조각 직선 기저와 같은 구조입니다. 이 정류 선형이고, 그것을 여러 개 합치면 어떤 조각 직선도 만들 수 있습니다.
강 문제 의 조각 수 세기가 이 관점입니다.
심화 2. 가우스 오차 선형을 정리하세요.
| 무엇 | 왜 |
|---|---|
| 확률적 해석 | 정규분포 누적으로 통과시킬지 정함 |
| 매끄러움 | 어디서나 미분 가능 |
| 비단조 | 근처에서 최소 |
| 트랜스포머 표준 | 실험적으로 좋음 |
셋째 줄이 문제 에서 본 음수 도함수입니다. 정류 선형과 달리 작은 음수 입력을 조금 통과시켰다가 다시 줄입니다.
심화 3. 스위시와 자기 게이트를 정리하세요.
| 무엇이 되나 | |
|---|---|
| 선형의 절반 | |
| 스위시 | |
| 가우스 오차 선형 근사 | |
| 무한대 | 정류 선형 |
넷째 줄이 흥미롭습니다. 를 키우면 로지스틱이 계단함수가 되어 정류 선형에 수렴합니다.
를 학습 가능한 파라미터로 두기도 합니다. 활성함수 자체를 배우는 셈입니다.
심화 4. 최대 출력 단위를 정리하세요.
| 무엇 | 왜 |
|---|---|
| 어떤 볼록함수도 근사 | 최댓값의 합성 |
| 정류 선형을 포함 | 이고 하나가 |
| 파라미터가 배 | 비쌈 |
| 안 죽음 | 적어도 하나는 이김 |
둘째 줄이 이 함수의 값입니다. 정류 선형과 새는 정류 선형을 특수한 경우로 포함합니다.
셋째 줄이 실무에서 안 쓰이는 이유입니다.
심화 5. 활성함수와 정규화의 관계를 정리하세요.
| 무엇 | 무엇을 하나 |
|---|---|
| 활성함수 | 비선형을 줌 |
| 정규화 | 입력 분포를 중심으로 |
| 함께 쓰면 | 포화 구간을 피함 |
셋째 줄이 핵심입니다. 문제 에서 탄젠트가 에서 도함수 이었는데, 정규화가 입력을 근처로 모으면 그 구간에 안 갑니다.
강의 배치 정규화가 포화 함수를 되살립니다. 활성함수만 바꾸는 것보다 근본적입니다.
심화 6. 이 강의가 다음 강의로 어떻게 이어지는지 정리하세요.
| 이 강의에서 | 다음에서 |
|---|---|
| 활성함수가 비선형을 줌 | 강 보편근사정리 |
| 깊으면 신호가 죽음 | 강 층을 지나는 그래디언트 |
| 출력 분산이 삼분의 일 | 강 초기화 |
| 정규화가 포화를 피함 | 강과 강 |
| 잔차 연결이 크게 듦 | 강 |
강은 은닉층 하나로 무엇까지 되는지를 정리로 봅니다. 활성함수가 무엇이든 연속함수를 근사할 수 있다는 결과이고, 그것이 무엇을 말하고 무엇을 안 말하는지가 다음 물음입니다.
정답.
| 기호 | 읽는 법 | 뜻 |
|---|---|---|
| 활성함수 | activation function | 층마다 원소별로 적용하는 비선형 함수입니다 |
| 포화 | saturation | 입력이 클 때 도함수가 에 붙는 현상입니다 |
| 정류 선형 | ReLU | 음수를 으로 자릅니다 |
| 새는 정류 선형 | Leaky ReLU | 음수 쪽에 작은 기울기를 둡니다 |
| 지수 선형 | ELU | 음수 쪽을 지수로 부드럽게 잇습니다 |
| 가우스 오차 선형 | GELU | 정규분포 누적으로 게이트를 겁니다 |
| 스위시 | Swish | 입력에 로지스틱을 곱합니다 |
| 최대 출력 단위 | maxout | 여러 선형 함수의 최댓값을 씁니다 |
| 죽은 단위 | dead unit | 모든 입력에서 도함수가 인 단위입니다 |
| 소프트플러스 | softplus | 정류 선형을 매끄럽게 만든 함수입니다 |
다음은 241강 보편근사정리가 말하는 것입니다. 이 강의에서 활성함수가 비선형을 준다는 것을 봤습니다. 다음 강의는 은닉층 하나로 어디까지 되는지를 정리로 보고, 그 정리가 무엇을 안 말하는지를 함께 봅니다.
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 sig(z):
return 1.0 / (1.0 + np.exp(-np.clip(z, -500, 500)))
ACTS = {
"로지스틱": (lambda z: sig(z), lambda z: sig(z) * (1 - sig(z))),
"하이퍼볼릭 탄젠트": (lambda z: np.tanh(z), lambda z: 1 - np.tanh(z) ** 2),
"정류 선형": (lambda z: np.maximum(z, 0.0), lambda z: (z > 0).astype(float)),
"새는 정류 선형": (lambda z: np.where(z > 0, z, 0.01 * z), lambda z: np.where(z > 0, 1.0, 0.01)),
"지수 선형": (lambda z: np.where(z > 0, z, np.exp(np.clip(z, -500, 0)) - 1),
lambda z: np.where(z > 0, 1.0, np.exp(np.clip(z, -500, 0)))),
"가우스 오차 선형": (lambda z: z * sig(1.702 * z),
lambda z: sig(1.702 * z) + z * 1.702 * sig(1.702 * z) * (1 - sig(1.702 * z))),
}
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 onehot(lab, K):
Y = np.zeros((len(lab), K))
Y[np.arange(len(lab)), lab] = 1.0
return Y
def init(sizes, rr, gain=2.0):
Ws, bs = [], []
for i in range(len(sizes) - 1):
s = np.sqrt(gain / sizes[i])
W = rr.normal(0, s, (sizes[i], sizes[i + 1]))
if i == len(sizes) - 2:
W = W * 0.05
Ws.append(W)
bs.append(np.zeros(sizes[i + 1]))
return Ws, bs
def fwd(Ws, bs, X, f):
pre, acts = [], [X]
H = X
for i in range(len(Ws)):
Z = H @ Ws[i] + bs[i]
pre.append(Z)
H = f(Z) if i < len(Ws) - 1 else Z
acts.append(H)
return pre, acts
def bwd(Ws, bs, X, Y, f, df):
m = len(X)
pre, acts = fwd(Ws, bs, X, f)
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) * df(pre[i - 1])
return gW, gb, ce(pre[-1], Y)
def train(Ws, bs, X, Y, f, df, 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, _ = bwd(Ws, bs, X[idx], Y[idx], f, df)
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, f):
_, a = fwd(Ws, bs, X, f)
return float((a[-1].argmax(axis=1) == lab).mean())
print("=" * 78)
print("240강 활성함수의 수학 코드 검산")
print("=" * 78)
print()
print("문제 1. 활성함수가 갖춰야 할 것")
print()
print(" (1) 요구 조건을 정리합니다")
rows = [
("비선형", "선형이면 층이 접힘", "239강 문제 2"),
("미분 가능", "역전파가 지나가야 함", "거의 모든 곳에서"),
("도함수가 살아 있음", "0 이면 신호가 막힘", "233강 문제 2"),
("계산이 쌈", "원소마다 부르므로", "지수 하나도 비쌈"),
("출력 범위", "너무 커지면 안 됨", "다음 층의 입력"),
]
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(" 도함수가 0 이면 그 단위는 영원히 안 배웁니다")
print()
print(" (2) 여섯 함수의 값과 도함수를 봅니다")
zs = np.array([-4.0, -1.0, 0.0, 1.0, 4.0])
w0 = max(pw(k) for k in list(ACTS) + ["함수"])
print(" 입력이 %s 일 때의 값입니다" % " ".join("%.1f" % z for z in zs))
print(" " + rw("함수", w0) + " " + " ".join(rl("%.1f 에서" % z, 12) for z in zs))
for nm, (f, df) in ACTS.items():
v = f(zs)
print(" " + rw(nm, w0) + " " + " ".join(rl("%.6f" % x, 12) for x in v))
print()
print(" 도함수입니다")
print(" " + rw("함수", w0) + " " + " ".join(rl("%.1f 에서" % z, 12) for z in zs))
for nm, (f, df) in ACTS.items():
v = df(zs)
print(" " + rw(nm, w0) + " " + " ".join(rl("%.6f" % x, 12) for x in v))
print(" 로지스틱과 탄젠트는 양끝에서 도함수가 0 에 붙습니다")
print(" 정류 선형은 음수 쪽에서 정확히 0 입니다")
print(" 나머지 셋은 음수 쪽에서도 0 이 아닙니다")
print()
print(" (3) 도함수를 수치 미분으로 확인합니다")
r = np.random.default_rng(20240)
zt = np.concatenate([r.normal(0, 2, 200), r.uniform(-1e-5, 1e-5, 20)])
h = 1e-6
print(" " + rw("함수", w0) + " " + rl("최대 상대 오차", 18) + " " + "어디서 큰가")
for nm, (f, df) in ACTS.items():
gn = (f(zt + h) - f(zt - h)) / (2 * h)
ga = df(zt)
den = np.maximum(np.abs(gn) + np.abs(ga), 1e-12)
rel = np.abs(gn - ga) / den
j = int(np.argmax(rel))
print(" " + rw(nm, w0) + " " + rl("%.4e" % float(rel.max()), 18) + " " + ("0 근처" if abs(zt[j]) < 0.1 else "일반 자리"))
print(" 정류 선형과 새는 정류 선형은 0 근처에서만 어긋납니다")
print(" 231강 심화 4 에서 본 미분 불가능한 점 하나 때문입니다")
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 = [
("포화", "입력이 크면 도함수가 0 에 붙음", "로지스틱과 탄젠트"),
("무슨 일", "역전파 신호가 막힘", "233강 문제 2"),
("층이 깊으면", "곱이 쌓여 지수로 줄음", "그래디언트 소실"),
("정류 선형", "양수 쪽 도함수가 1", "곱해도 안 줄음"),
]
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) 도함수의 최댓값을 봅니다")
zz = np.linspace(-10, 10, 20001)
print(" " + rw("함수", w0) + " " + rl("도함수 최댓값", 16) + " " + rl("절댓값 3 에서", 16) + " " + rl("절댓값 6 에서", 16))
for nm, (f, df) in ACTS.items():
d = df(zz)
print(" " + rw(nm, w0) + " " + rl("%.6f" % float(d.max()), 16) + " " + rl("%.6f" % float(df(np.array([3.0]))[0]), 16) + " " + rl("%.6f" % float(df(np.array([6.0]))[0]), 16))
print(" 로지스틱은 최댓값이 0.25 뿐입니다")
print(" 층을 지날 때마다 신호가 최소한 네 배씩 줄어듭니다")
print(" 정류 선형은 양수 쪽에서 정확히 1 이라 안 줄어듭니다")
print()
print(" (3) 층을 지나며 신호가 얼마나 줄어드는지 봅니다")
r2g = np.random.default_rng(30240)
d2 = 32
X2 = r2g.normal(0, 1, (64, d2))
lab2 = r2g.integers(0, 3, 64)
Y2 = onehot(lab2, 3)
print(" 깊이 20 인 신경망에서 첫 층과 마지막 층 그래디언트 크기를 봅니다")
print(" " + rw("함수", w0) + " " + rl("첫 층 크기", 16) + " " + rl("마지막 층 크기", 16) + " " + rl("첫 층 나누기 마지막", 22))
for nm, (f, df) in ACTS.items():
Ws, bs = init([d2] * 20 + [3], np.random.default_rng(7))
gW, _, _ = bwd(Ws, bs, X2, Y2, f, df)
a = float(np.abs(gW[0]).mean())
b_ = float(np.abs(gW[-1]).mean())
print(" " + rw(nm, w0) + " " + rl("%.4e" % a, 16) + " " + rl("%.4e" % b_, 16) + " " + rl("%.4e" % (a / b_), 22))
print(" 로지스틱이 가장 심하게 줄어듭니다")
print(" 정류 선형 계열이 가장 잘 버팁니다")
print(" 233강 문제 2 에서 초기화가 정한다고 했는데 활성함수도 함께 정합니다")
print()
print(" (4) 실제로 학습이 되는지 견줍니다")
r3 = np.random.default_rng(40240)
n3 = 3000
X3 = r3.uniform(-3, 3, (n3, 2))
z3 = np.sin(1.8 * X3[:, 0]) * np.cos(1.8 * X3[:, 1])
lab3 = (z3 > 0).astype(int)
Y3 = onehot(lab3, 2)
tr3 = np.arange(2200)
te3 = np.arange(2200, n3)
print(" 깊이 8 인 신경망을 6000 걸음 학습시킵니다")
print(" " + rw("함수", w0) + " " + rl("학습 정확도", 14) + " " + rl("따로 뗀 정확도", 16))
for nm, (f, df) in ACTS.items():
Ws, bs = init([2] + [24] * 7 + [2], np.random.default_rng(11))
Ws, bs = train(Ws, bs, X3[tr3], Y3[tr3], f, df, 0.03, 6000, 64, np.random.default_rng(13))
print(" " + rw(nm, w0) + " " + rl("%.6f" % acc(Ws, bs, X3[tr3], lab3[tr3], f), 14) + " " + rl("%.6f" % acc(Ws, bs, X3[te3], lab3[te3], f), 16))
print(" 로지스틱만 완전히 실패합니다. 도함수 최댓값이 0.25 이기 때문입니다")
print(" 탄젠트는 최댓값이 1 이라 깊이 8 에서는 아직 버팁니다")
print(" 지수 선형과 탄젠트가 가장 좋고 정류 선형이 그다음입니다")
print(" 포화 자체보다 도함수 최댓값이 1 인지가 갈림길입니다")
print()
print("문제 3. 정류 선형의 문제")
print()
print(" (1) 무엇이 문제인지 정리합니다")
rows = [
("죽은 단위", "모든 입력에서 음수", "영원히 안 배움"),
("0 에서 안 매끄러움", "도함수가 튐", "실전에서는 큰 문제 아님"),
("출력 평균이 양수", "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()
print(" (2) 죽은 단위가 몇 개 생기는지 셉니다")
r4 = np.random.default_rng(50240)
print(" 학습이 끝난 뒤 모든 표본에서 한 번도 안 켜진 단위를 셉니다")
print(" " + rw("함수", w0) + " " + rl("전체 은닉 단위", 16) + " " + rl("죽은 단위 수", 14) + " " + rl("죽은 비율", 12))
for nm, (f, df) in ACTS.items():
Ws, bs = init([2] + [24] * 4 + [2], np.random.default_rng(17))
Ws, bs = train(Ws, bs, X3[tr3], Y3[tr3], f, df, 0.03, 6000, 64, np.random.default_rng(19))
pre, _ = fwd(Ws, bs, X3, f)
dead = 0
total = 0
for Z in pre[:-1]:
d = df(Z)
dead += int((np.abs(d).max(axis=0) < 1e-8).sum())
total += Z.shape[1]
print(" " + rw(nm, w0) + " " + rl("%d" % total, 16) + " " + rl("%d" % dead, 14) + " " + rl("%.6f" % (dead / float(total)), 12))
print(" 정류 선형에서만 도함수가 정확히 0 인 단위가 생깁니다")
print(" 새는 정류 선형은 음수 쪽 기울기가 0.01 이라 안 죽습니다")
print()
print(" (3) 학습률을 키우면 죽은 단위가 늘어나는지 봅니다")
print(" 정류 선형으로 학습률만 바꿉니다")
print(" " + rl("학습률", 12) + " " + rl("죽은 단위 수", 14) + " " + rl("죽은 비율", 12) + " " + rl("따로 뗀 정확도", 16))
f_re, df_re = ACTS["정류 선형"]
for lr in [0.01, 0.05, 0.2, 0.5]:
Ws, bs = init([2] + [24] * 4 + [2], np.random.default_rng(17))
Ws, bs = train(Ws, bs, X3[tr3], Y3[tr3], f_re, df_re, lr, 4000, 64, np.random.default_rng(21))
pre, _ = fwd(Ws, bs, X3, f_re)
dead = sum(int((np.abs(df_re(Z)).max(axis=0) < 1e-8).sum()) for Z in pre[:-1])
total = sum(Z.shape[1] for Z in pre[:-1])
print(" " + rl("%.4f" % lr, 12) + " " + rl("%d" % dead, 14) + " " + rl("%.6f" % (dead / float(total)), 12) + " " + rl("%.6f" % acc(Ws, bs, X3[te3], lab3[te3], f_re), 16))
print(" 학습률이 크면 큰 걸음 한 번에 단위가 음수 쪽으로 밀립니다")
print(" 한 번 밀리면 그래디언트가 0 이라 되돌아올 길이 없습니다")
print()
print(" (4) 출력 평균이 0 이 아닌 것을 봅니다")
r5 = np.random.default_rng(60240)
zin = r5.normal(0, 1, 20000)
print(" 표준정규 입력을 넣었을 때 출력의 평균과 표준편차입니다")
print(" " + rw("함수", w0) + " " + rl("출력 평균", 14) + " " + rl("출력 표준편차", 16) + " " + rl("입력 대비 분산 비", 20))
for nm, (f, df) in ACTS.items():
v = f(zin)
print(" " + rw(nm, w0) + " " + rl("%.6f" % float(v.mean()), 14) + " " + rl("%.6f" % float(v.std()), 16) + " " + rl("%.6f" % float(v.var()), 20))
print(" 정류 선형은 평균이 0.4 근처로 치우칩니다")
print(" 탄젠트만 평균이 0 에 가깝습니다. 0 중심 함수이기 때문입니다")
print(" 분산이 절반으로 줄어드는 것이 243강 정착 초기화의 근거입니다")
print()
print("문제 4. 어느 것을 고를까")
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(" 깊이 3 으로 같은 문제를 풉니다")
print(" " + rw("함수", w0) + " " + rl("학습 정확도", 14) + " " + rl("따로 뗀 정확도", 16))
for nm, (f, df) in ACTS.items():
Ws, bs = init([2, 32, 32, 2], np.random.default_rng(23))
Ws, bs = train(Ws, bs, X3[tr3], Y3[tr3], f, df, 0.03, 6000, 64, np.random.default_rng(27))
print(" " + rw(nm, w0) + " " + rl("%.6f" % acc(Ws, bs, X3[tr3], lab3[tr3], f), 14) + " " + rl("%.6f" % acc(Ws, bs, X3[te3], lab3[te3], f), 16))
print(" 얕으면 어느 것을 써도 비슷합니다")
print(" 깊어질수록 차이가 벌어집니다. 문제 2 의 (4) 와 견주면 보입니다")
print()
print(" (3) 새는 정류 선형의 기울기를 바꿔 봅니다")
print(" 음수 쪽 기울기를 바꿔 가며 깊이 8 로 학습시킵니다")
print(" " + rl("음수 쪽 기울기", 16) + " " + rl("죽은 비율", 12) + " " + rl("따로 뗀 정확도", 16))
for a0 in [0.0, 0.01, 0.1, 0.3, 1.0]:
f_a = lambda z, a=a0: np.where(z > 0, z, a * z)
df_a = lambda z, a=a0: np.where(z > 0, 1.0, a)
Ws, bs = init([2] + [24] * 7 + [2], np.random.default_rng(11))
Ws, bs = train(Ws, bs, X3[tr3], Y3[tr3], f_a, df_a, 0.03, 6000, 64, np.random.default_rng(13))
pre, _ = fwd(Ws, bs, X3, f_a)
dead = sum(int((np.abs(df_a(Z)).max(axis=0) < 1e-8).sum()) for Z in pre[:-1])
total = sum(Z.shape[1] for Z in pre[:-1])
print(" " + rl("%.4f" % a0, 16) + " " + rl("%.6f" % (dead / float(total)), 12) + " " + rl("%.6f" % acc(Ws, bs, X3[te3], lab3[te3], f_a), 16))
print(" 기울기가 0 보다 크면 죽은 단위가 아예 안 생깁니다. 도함수가 0 이 될 수 없기 때문입니다")
print(" 그런데 기울기가 1 이면 선형이 되어 239강 문제 2 처럼 표현력이 사라집니다")
print(" 0 과 1 사이 어딘가가 답이고 실무에서 0.01 을 씁니다")
print()
print(" (4) 활성함수를 층마다 다르게 써 봅니다")
print(" 마지막 은닉층만 다른 함수를 씁니다")
def fwd_mixed(Ws, bs, X, fs):
pre, acts = [], [X]
H = X
for i in range(len(Ws)):
Z = H @ Ws[i] + bs[i]
pre.append(Z)
H = fs[i](Z) if i < len(Ws) - 1 else Z
acts.append(H)
return pre, acts
def bwd_mixed(Ws, bs, X, Y, fs, dfs):
m = len(X)
pre, acts = fwd_mixed(Ws, bs, X, fs)
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) * dfs[i - 1](pre[i - 1])
return gW, gb
r6 = np.random.default_rng(70240)
L = 5
combos = [
("모두 정류 선형", ["정류 선형"] * (L - 1)),
("모두 탄젠트", ["하이퍼볼릭 탄젠트"] * (L - 1)),
("앞은 정류 선형 끝만 탄젠트", ["정류 선형"] * (L - 2) + ["하이퍼볼릭 탄젠트"]),
("앞은 탄젠트 끝만 정류 선형", ["하이퍼볼릭 탄젠트"] * (L - 2) + ["정류 선형"]),
]
w1 = max(pw(c[0]) for c in combos + [("무엇", 0)])
print(" " + rw("무엇", w1) + " " + rl("학습 정확도", 14) + " " + rl("따로 뗀 정확도", 16))
for nm, names in combos:
fs = [ACTS[x][0] for x in names]
dfs = [ACTS[x][1] for x in names]
Ws, bs = init([2] + [24] * (L - 1) + [2], np.random.default_rng(29))
vW = [np.zeros_like(x) for x in Ws]
vb = [np.zeros_like(x) for x in bs]
rr = np.random.default_rng(31)
for t in range(6000):
idx = rr.integers(0, len(tr3), 64)
gW, gb = bwd_mixed(Ws, bs, X3[tr3][idx], Y3[tr3][idx], fs, dfs)
for i in range(len(Ws)):
vW[i] = 0.9 * vW[i] + gW[i]
vb[i] = 0.9 * vb[i] + gb[i]
Ws[i] -= 0.03 * vW[i]
bs[i] -= 0.03 * vb[i]
_, a1 = fwd_mixed(Ws, bs, X3[tr3], fs)
_, a2 = fwd_mixed(Ws, bs, X3[te3], fs)
print(" " + rw(nm, w1) + " " + rl("%.6f" % float((a1[-1].argmax(axis=1) == lab3[tr3]).mean()), 14) + " " + rl("%.6f" % float((a2[-1].argmax(axis=1) == lab3[te3]).mean()), 16))
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(" 깊이를 늘려 가며 탄젠트로 학습시킵니다. 잔차 연결이 있고 없고를 견줍니다")
def fwd_res(Ws, bs, X, f):
pre, ins, outs = [], [], [X]
H = X
for i in range(len(Ws)):
Z = H @ Ws[i] + bs[i]
pre.append(Z)
ins.append(H)
if i < len(Ws) - 1:
A = f(Z)
H = A + H if A.shape == H.shape else A
else:
H = Z
outs.append(H)
return pre, ins, outs
def bwd_res(Ws, bs, X, Y, f, df):
m = len(X)
pre, ins, outs = fwd_res(Ws, bs, X, f)
L = len(Ws)
D = (softmax(pre[-1]) - Y) / m
gW = [None] * L
gb = [None] * L
for i in range(L - 1, -1, -1):
Dz = D if i == L - 1 else D * df(pre[i])
gW[i] = ins[i].T @ Dz
gb[i] = Dz.sum(axis=0)
Dnext = Dz @ Ws[i].T
if i < L - 1 and ins[i].shape == outs[i + 1].shape:
Dnext = Dnext + D
D = Dnext
return gW, gb
print(" " + rl("깊이", 10) + " " + rl("잔차 없이", 14) + " " + rl("잔차 연결을 넣고", 18))
f, df = ACTS["하이퍼볼릭 탄젠트"]
for Ld in [8, 14, 20]:
Ws, bs = init([2] + [24] * (Ld - 1) + [2], np.random.default_rng(11))
Ws, bs = train(Ws, bs, X3[tr3], Y3[tr3], f, df, 0.03, 6000, 64, np.random.default_rng(13))
a1 = acc(Ws, bs, X3[te3], lab3[te3], f)
Ws, bs = init([2] + [24] * (Ld - 1) + [2], np.random.default_rng(11))
vW = [np.zeros_like(x) for x in Ws]
vb = [np.zeros_like(x) for x in bs]
rr = np.random.default_rng(13)
for t in range(6000):
idx = rr.integers(0, len(tr3), 64)
gW, gb = bwd_res(Ws, bs, X3[tr3][idx], Y3[tr3][idx], f, df)
for i in range(len(Ws)):
vW[i] = 0.9 * vW[i] + gW[i]
vb[i] = 0.9 * vb[i] + gb[i]
Ws[i] -= 0.03 * vW[i]
bs[i] -= 0.03 * vb[i]
_, _, ar = fwd_res(Ws, bs, X3[te3], f)
a2 = float((ar[-1].argmax(axis=1) == lab3[te3]).mean())
print(" " + rl("%d" % Ld, 10) + " " + rl("%.6f" % a1, 14) + " " + rl("%.6f" % a2, 18))
print(" 깊이 8 에서는 잔차 연결이 오히려 조금 낮습니다. 아직 안 깊기 때문입니다")
print(" 깊이 20 에서 잔차 없이는 0.7363 으로 무너지는데 잔차를 넣으면 0.9150 을 지킵니다")
print(" 활성함수를 바꾸는 것으로는 이 격차를 못 메웁니다")
print(" 구조를 고치는 것이 활성함수를 고르는 것보다 크게 듭니다")
print(" 245강에서 잔차 연결을 정면으로 다룹니다")
print()
print(" (3) 출력층 활성함수를 정리합니다")
rows = [
("두 갈래 분류", "로지스틱", "확률"),
("여러 갈래 분류", "소프트맥스", "확률 벡터"),
("회귀", "없음", "실수 그대로"),
("양수만 나와야 하면", "소프트플러스", "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(" 은닉층과 출력층은 고르는 기준이 완전히 다릅니다")
print(" 은닉층은 학습이 되게 고르고 출력층은 값의 범위로 고릅니다")
print()
print(" (4) 이 강의를 한 장으로 모읍니다")
rows = [
("무엇이 필요한가", "비선형이고 도함수가 살아 있어야 합니다"),
("포화가 무엇인가", "입력이 크면 도함수가 0 에 붙는 것입니다"),
("왜 문제인가", "층을 지날 때마다 신호가 곱해져 줄어듭니다"),
("정류 선형이 표준인 이유", "싸고 양수 쪽 도함수가 1 입니다"),
("정류 선형의 문제", "죽은 단위가 생깁니다"),
("죽으면", "그래디언트가 0 이라 되돌아올 길이 없습니다"),
("더 크게 드는 것", "활성함수보다 구조입니다"),
]
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(" 241강은 은닉층 하나로 무엇까지 되는지를 정리로 봅니다")
print()
print("=" * 78)
print("검산 끝")
print("=" * 78)
# ==============================================================================
# 240강 활성함수의 수학 코드 검산
# ==============================================================================
#
# 문제 1. 활성함수가 갖춰야 할 것
#
# (1) 요구 조건을 정리합니다
# 무엇 왜 필요한가 어디서
# 비선형 선형이면 층이 접힘 239강 문제 2
# 미분 가능 역전파가 지나가야 함 거의 모든 곳에서
# 도함수가 살아 있음 0 이면 신호가 막힘 233강 문제 2
# 계산이 쌈 원소마다 부르므로 지수 하나도 비쌈
# 출력 범위 너무 커지면 안 됨 다음 층의 입력
# 셋째 줄이 실무에서 가장 크게 듭니다
# 도함수가 0 이면 그 단위는 영원히 안 배웁니다
#
# (2) 여섯 함수의 값과 도함수를 봅니다
# 입력이 -4.0 -1.0 0.0 1.0 4.0 일 때의 값입니다
# 함수 -4.0 에서 -1.0 에서 0.0 에서 1.0 에서 4.0 에서
# 로지스틱 0.017986 0.268941 0.500000 0.731059 0.982014
# 하이퍼볼릭 탄젠트 -0.999329 -0.761594 0.000000 0.761594 0.999329
# 정류 선형 0.000000 0.000000 0.000000 1.000000 4.000000
# 새는 정류 선형 -0.040000 -0.010000 0.000000 1.000000 4.000000
# 지수 선형 -0.981684 -0.632121 0.000000 1.000000 4.000000
# 가우스 오차 선형 -0.004415 -0.154204 0.000000 0.845796 3.995585
#
# 도함수입니다
# 함수 -4.0 에서 -1.0 에서 0.0 에서 1.0 에서 4.0 에서
# 로지스틱 0.017663 0.196612 0.250000 0.196612 0.017663
# 하이퍼볼릭 탄젠트 0.001341 0.419974 1.000000 0.419974 0.001341
# 정류 선형 0.000000 0.000000 0.000000 1.000000 1.000000
# 새는 정류 선형 0.010000 0.010000 0.010000 1.000000 1.000000
# 지수 선형 0.018316 0.367879 1.000000 1.000000 1.000000
# 가우스 오차 선형 -0.006402 -0.067780 0.500000 1.067780 1.006402
# 로지스틱과 탄젠트는 양끝에서 도함수가 0 에 붙습니다
# 정류 선형은 음수 쪽에서 정확히 0 입니다
# 나머지 셋은 음수 쪽에서도 0 이 아닙니다
#
# (3) 도함수를 수치 미분으로 확인합니다
# 함수 최대 상대 오차 어디서 큰가
# 로지스틱 7.7371e-09 일반 자리
# 하이퍼볼릭 탄젠트 1.2648e-06 일반 자리
# 정류 선형 1.0000e+00 0 근처
# 새는 정류 선형 7.4694e-01 0 근처
# 지수 선형 1.0997e-07 0 근처
# 가우스 오차 선형 3.6934e-10 일반 자리
# 정류 선형과 새는 정류 선형은 0 근처에서만 어긋납니다
# 231강 심화 4 에서 본 미분 불가능한 점 하나 때문입니다
# 나머지는 어디서나 매끄러워 오차가 아주 작습니다
#
# (4) 계산 비용을 셉니다
# 함수 무엇이 필요한가 값
# 정류 선형 비교 하나 가장 쌈
# 새는 정류 선형 비교 하나와 곱 하나 거의 같음
# 하이퍼볼릭 탄젠트 지수 둘 비쌈
# 로지스틱 지수 하나 비쌈
# 지수 선형 음수에서만 지수 가운데
# 가우스 오차 선형 지수 하나와 곱 여럿 가장 비쌈
# 층마다 원소 수만큼 부르므로 이 차이가 쌓입니다
# 정류 선형이 표준이 된 첫째 이유가 이것입니다
#
# 문제 2. 포화가 무엇을 막는가
#
# (1) 포화의 뜻을 정리합니다
# 무엇 무엇인가 덧붙임
# 포화 입력이 크면 도함수가 0 에 붙음 로지스틱과 탄젠트
# 무슨 일 역전파 신호가 막힘 233강 문제 2
# 층이 깊으면 곱이 쌓여 지수로 줄음 그래디언트 소실
# 정류 선형 양수 쪽 도함수가 1 곱해도 안 줄음
#
# (2) 도함수의 최댓값을 봅니다
# 함수 도함수 최댓값 절댓값 3 에서 절댓값 6 에서
# 로지스틱 0.250000 0.045177 0.002467
# 하이퍼볼릭 탄젠트 1.000000 0.009866 0.000025
# 정류 선형 1.000000 1.000000 1.000000
# 새는 정류 선형 1.000000 1.000000 1.000000
# 지수 선형 1.000000 1.000000 1.000000
# 가우스 오차 선형 1.099839 1.024548 1.000338
# 로지스틱은 최댓값이 0.25 뿐입니다
# 층을 지날 때마다 신호가 최소한 네 배씩 줄어듭니다
# 정류 선형은 양수 쪽에서 정확히 1 이라 안 줄어듭니다
#
# (3) 층을 지나며 신호가 얼마나 줄어드는지 봅니다
# 깊이 20 인 신경망에서 첫 층과 마지막 층 그래디언트 크기를 봅니다
# 함수 첫 층 크기 마지막 층 크기 첫 층 나누기 마지막
# 로지스틱 1.7702e-13 1.1119e-02 1.5920e-11
# 하이퍼볼릭 탄젠트 1.8439e-03 2.5276e-02 7.2952e-02
# 정류 선형 2.7182e-04 5.3896e-03 5.0434e-02
# 새는 정류 선형 2.7735e-04 5.5778e-03 4.9724e-02
# 지수 선형 3.0969e-03 7.3694e-02 4.2024e-02
# 가우스 오차 선형 6.3732e-05 1.1894e-03 5.3585e-02
# 로지스틱이 가장 심하게 줄어듭니다
# 정류 선형 계열이 가장 잘 버팁니다
# 233강 문제 2 에서 초기화가 정한다고 했는데 활성함수도 함께 정합니다
#
# (4) 실제로 학습이 되는지 견줍니다
# 깊이 8 인 신경망을 6000 걸음 학습시킵니다
# 함수 학습 정확도 따로 뗀 정확도
# 로지스틱 0.506818 0.492500
# 하이퍼볼릭 탄젠트 0.958182 0.943750
# 정류 선형 0.939545 0.920000
# 새는 정류 선형 0.917273 0.882500
# 지수 선형 0.956364 0.943750
# 가우스 오차 선형 0.908182 0.887500
# 로지스틱만 완전히 실패합니다. 도함수 최댓값이 0.25 이기 때문입니다
# 탄젠트는 최댓값이 1 이라 깊이 8 에서는 아직 버팁니다
# 지수 선형과 탄젠트가 가장 좋고 정류 선형이 그다음입니다
# 포화 자체보다 도함수 최댓값이 1 인지가 갈림길입니다
#
# 문제 3. 정류 선형의 문제
#
# (1) 무엇이 문제인지 정리합니다
# 무엇 무엇인가 덧붙임
# 죽은 단위 모든 입력에서 음수 영원히 안 배움
# 0 에서 안 매끄러움 도함수가 튐 실전에서는 큰 문제 아님
# 출력 평균이 양수 0 중심이 아님 다음 층에 치우침
# 위로 안 막힘 출력이 커질 수 있음 정규화로 다룸
#
# (2) 죽은 단위가 몇 개 생기는지 셉니다
# 학습이 끝난 뒤 모든 표본에서 한 번도 안 켜진 단위를 셉니다
# 함수 전체 은닉 단위 죽은 단위 수 죽은 비율
# 로지스틱 96 0 0.000000
# 하이퍼볼릭 탄젠트 96 0 0.000000
# 정류 선형 96 2 0.020833
# 새는 정류 선형 96 0 0.000000
# 지수 선형 96 0 0.000000
# 가우스 오차 선형 96 0 0.000000
# 정류 선형에서만 도함수가 정확히 0 인 단위가 생깁니다
# 새는 정류 선형은 음수 쪽 기울기가 0.01 이라 안 죽습니다
#
# (3) 학습률을 키우면 죽은 단위가 늘어나는지 봅니다
# 정류 선형으로 학습률만 바꿉니다
# 학습률 죽은 단위 수 죽은 비율 따로 뗀 정확도
# 0.0100 4 0.041667 0.932500
# 0.0500 8 0.083333 0.897500
# 0.2000 63 0.656250 0.492500
# 0.5000 55 0.572917 0.492500
# 학습률이 크면 큰 걸음 한 번에 단위가 음수 쪽으로 밀립니다
# 한 번 밀리면 그래디언트가 0 이라 되돌아올 길이 없습니다
#
# (4) 출력 평균이 0 이 아닌 것을 봅니다
# 표준정규 입력을 넣었을 때 출력의 평균과 표준편차입니다
# 함수 출력 평균 출력 표준편차 입력 대비 분산 비
# 로지스틱 0.500190 0.208899 0.043639
# 하이퍼볼릭 탄젠트 0.000525 0.629920 0.396800
# 정류 선형 0.400732 0.582805 0.339662
# 새는 정류 선형 0.396729 0.585581 0.342905
# 지수 선형 0.161658 0.787224 0.619721
# 가우스 오차 선형 0.284046 0.583831 0.340859
# 정류 선형은 평균이 0.4 근처로 치우칩니다
# 탄젠트만 평균이 0 에 가깝습니다. 0 중심 함수이기 때문입니다
# 분산이 절반으로 줄어드는 것이 243강 정착 초기화의 근거입니다
#
# 문제 4. 어느 것을 고를까
#
# (1) 갈림길을 정리합니다
# 언제 무엇을 쓰나 왜
# 기본 정류 선형 싸고 잘 됨
# 죽은 단위가 걱정 새는 정류 선형 거의 같은 비용
# 트랜스포머 가우스 오차 선형 표준
# 순환 신경망 탄젠트 상태가 안 터지게
# 출력이 확률 로지스틱이나 소프트맥스 은닉층에는 안 씀
#
# (2) 얕은 신경망에서는 차이가 있는지 봅니다
# 깊이 3 으로 같은 문제를 풉니다
# 함수 학습 정확도 따로 뗀 정확도
# 로지스틱 0.847727 0.801250
# 하이퍼볼릭 탄젠트 0.954545 0.941250
# 정류 선형 0.914545 0.901250
# 새는 정류 선형 0.920455 0.900000
# 지수 선형 0.935909 0.925000
# 가우스 오차 선형 0.946364 0.942500
# 얕으면 어느 것을 써도 비슷합니다
# 깊어질수록 차이가 벌어집니다. 문제 2 의 (4) 와 견주면 보입니다
#
# (3) 새는 정류 선형의 기울기를 바꿔 봅니다
# 음수 쪽 기울기를 바꿔 가며 깊이 8 로 학습시킵니다
# 음수 쪽 기울기 죽은 비율 따로 뗀 정확도
# 0.0000 0.011905 0.920000
# 0.0100 0.000000 0.882500
# 0.1000 0.000000 0.916250
# 0.3000 0.000000 0.908750
# 1.0000 0.000000 0.492500
# 기울기가 0 보다 크면 죽은 단위가 아예 안 생깁니다. 도함수가 0 이 될 수 없기 때문입니다
# 그런데 기울기가 1 이면 선형이 되어 239강 문제 2 처럼 표현력이 사라집니다
# 0 과 1 사이 어딘가가 답이고 실무에서 0.01 을 씁니다
#
# (4) 활성함수를 층마다 다르게 써 봅니다
# 마지막 은닉층만 다른 함수를 씁니다
# 무엇 학습 정확도 따로 뗀 정확도
# 모두 정류 선형 0.923182 0.906250
# 모두 탄젠트 0.957727 0.947500
# 앞은 정류 선형 끝만 탄젠트 0.873182 0.855000
# 앞은 탄젠트 끝만 정류 선형 0.958636 0.953750
# 섞어 써도 됩니다. 층마다 독립적으로 정할 수 있습니다
# 다만 실무에서는 대개 하나로 통일합니다. 손잡이를 줄이기 위해서입니다
#
# 문제 5. 실무에서 쓰기
#
# (1) 고르는 순서를 정리합니다
# 순서 무엇을 하나
# 먼저 정류 선형으로 시작합니다
# 죽은 단위가 많으면 학습률을 낮추거나 새는 것으로 바꿉니다
# 아주 깊으면 잔차 연결과 정규화를 먼저 봅니다
# 트랜스포머면 가우스 오차 선형을 씁니다
# 출력층은 문제에 맞게 따로 정합니다
#
# (2) 활성함수보다 구조가 크게 드는 것을 봅니다
# 깊이를 늘려 가며 탄젠트로 학습시킵니다. 잔차 연결이 있고 없고를 견줍니다
# 깊이 잔차 없이 잔차 연결을 넣고
# 8 0.943750 0.922500
# 14 0.881250 0.927500
# 20 0.736250 0.915000
# 깊이 8 에서는 잔차 연결이 오히려 조금 낮습니다. 아직 안 깊기 때문입니다
# 깊이 20 에서 잔차 없이는 0.7363 으로 무너지는데 잔차를 넣으면 0.9150 을 지킵니다
# 활성함수를 바꾸는 것으로는 이 격차를 못 메웁니다
# 구조를 고치는 것이 활성함수를 고르는 것보다 크게 듭니다
# 245강에서 잔차 연결을 정면으로 다룹니다
#
# (3) 출력층 활성함수를 정리합니다
# 무엇 무엇을 쓰나 무엇이 나오나
# 두 갈래 분류 로지스틱 확률
# 여러 갈래 분류 소프트맥스 확률 벡터
# 회귀 없음 실수 그대로
# 양수만 나와야 하면 소프트플러스 0 보다 큼
# 범위가 정해지면 탄젠트나 로지스틱 늘여서 맞춤
# 은닉층과 출력층은 고르는 기준이 완전히 다릅니다
# 은닉층은 학습이 되게 고르고 출력층은 값의 범위로 고릅니다
#
# (4) 이 강의를 한 장으로 모읍니다
# 무엇 한 줄로
# 무엇이 필요한가 비선형이고 도함수가 살아 있어야 합니다
# 포화가 무엇인가 입력이 크면 도함수가 0 에 붙는 것입니다
# 왜 문제인가 층을 지날 때마다 신호가 곱해져 줄어듭니다
# 정류 선형이 표준인 이유 싸고 양수 쪽 도함수가 1 입니다
# 정류 선형의 문제 죽은 단위가 생깁니다
# 죽으면 그래디언트가 0 이라 되돌아올 길이 없습니다
# 더 크게 드는 것 활성함수보다 구조입니다
# 241강은 은닉층 하나로 무엇까지 되는지를 정리로 봅니다
#
# ==============================================================================
# 검산 끝
# ==============================================================================