정규화는 초기값을 지운다
4편의 결론은 초기값이 조건이라는 것이었다. 작게 두면 신호가 20층에서 1e-16
이 되고, 크게 두면 층 사이 기울기가 1e9 배 벌어졌다. Xavier 만 양쪽을
피했다.
정규화는 그 조건을 아예 없애 버린다. 층마다 한 줄을 더하면 초기값이 무엇이든 같은 곳에 도착한다. 얼마나 같은지부터 보자.
한 줄을 더한다
가중치를 곱한 직후, 활성함수를 통과시키기 전에 평균을 빼고 표준편차로 나눈다.
z = h @ W
z = (z - z.mean(0)) / (z.std(0) + 1e-5) # 배치 정규화
h = np.tanh(z)
mean(0) 은 배치 방향 평균이다. 같은 뉴런이 배치 안 512개 샘플에서 낸 값을
모아 그 평균과 표준편차를 쓴다. 나중에 볼 층 정규화는 여기만 다르다.
4편과 같은 20층 그물에 이 한 줄만 넣는다. 다만 여기서는 세 초기값이 같은 난수를
공유하도록 다시 뽑았다 - 상수배만 다르게 두어야 정규화가 셋을 하나로 만드는 것이
보이기 때문이다. 그래서 정규화 없는 Xavier 가 4편 표의 1.62e-01 이 아니라
1.543e-01 로 조금 다르다.
초기값 정규화 없음(20층) 배치 정규화(20층)
0.01 1.135e-16 0.6310
1.0 9.742e-01 0.6310
Xavier 1.543e-01 0.6310
세 값이 소수점 넷째 자리까지 같다. 우연이 아니라 정의상 그렇다.
정확히 무엇이 1이 되는지부터 짚자. 정규화되는 것은 활성함수를 통과하기 전의
z 다. 층의 출력 tanh(z) 는 1이 아니라 0.6310 이고, 그래서 표에 그 값이
찍힌다. 층마다 z 가 같은 분포로 되돌려지니 그 뒤도 매번 같은 값이 나온다.
셋이 같아지는 이유는 더 단순하다. 배치 정규화는 양수 상수배를 완전히 지운다.
BN(cz) = BN(z) 이다. 정확히는 위 코드의 eps 가 분모에 상수로 붙어 있어
완전한 불변은 아니고, z 와 100z 의 차이가 4e-5 만큼 남는다. 활성 0.6310
의 0.007% 라 표에는 흔적도 안 남는다 - eps 를 빼면 차이는 5e-15, 부동소수점
잡음뿐이다. 초기값 0.01, 1.0, Xavier 는 같은 난수에 곱한 상수만 다르므로,
정규화를 붙이는 순간 셋은 같은 그물이 된다. 4편에서 “곱셈의 밑” 이라고
불렀던 값이 층마다 강제로 리셋되는 것이고, 상수배가 아닌 초기화(직교 초기화
같은)에는 이 논리가 그대로 가지는 않는다.
이게 정규화의 실제 값어치다. 학습이 빨라지는 것보다 먼저, 초기값이라는 하이퍼파라미터가 사라진다. 20층짜리를 세우면서 스케일을 맞추느라 애쓸 필요가 없어진다.
대가 1: 배치에 의존한다
공짜가 아니다. 평균과 표준편차를 배치에서 추정하기 때문이다. 20층 활성을 배치 크기만 바꿔 가며 재면 이렇게 나온다.
배치 배치 정규화 층 정규화
4 0.6756 0.6261
8 0.6507 0.6339
32 0.6346 0.6272
256 0.6317 0.6287
512 0.6310 0.6282
배치 정규화는 배치가 작을수록 값이 위로 뜬다. 층 정규화는 배치와 무관하게 평평하다.
여기서 “배치가 작으면 표준편차를 작게 추정하니 더 크게 나눠져서 값이 커진다” 고
설명하기 쉽다. 틀렸다. 정규화는 그 배치를 그 배치 자신의 표준편차로 나누므로,
출력의 표준편차는 배치 크기와 무관하게 정확히 1이다. 재 보면 배치 4에서
0.999983, 배치 512에서 0.999990 으로 차이가 없다. 추정이 얼마나 빗나갔든
자기 자신으로 나눈 결과는 언제나 1이다.
진짜 원인은 모양이다. n 개를 자기들끼리 표준화하면 각 값이 sqrt(n-1) 을
넘을 수 없다. 배치 4면 |z| ≤ 1.732, 배치 512면 22.6 이다. 분포의 꼬리가
잘리고 첨도가 배치 4에서 1.80, 배치 512에서 2.99 로 달라진다. 이건 어림이
아니라 정확히 3(n-1)/(n+1) 이다 - 정규분포의 3 에서 배치가 작을수록 멀어진다.
tanh 는
큰 값을 눌러 작게 만드는 함수인데, 배치가 작으면 누를 큰 값 자체가 없다.
그래서 출력이 덜 눌리고 표준편차가 높게 나온다.
증거는 두 가지다. 활성함수를 선형으로 바꾸면 배치 의존성이 완전히 사라진다 -
배치 4와 512 모두 1.0000 이다. 그리고 정확히 N(0,1) 인 값을 넣고 정규화 +
tanh 를 한 층만 통과시켜도 배치 4에서 0.6768, 배치 512에서 0.6281 로
차이가 이미 다 나타난다. 20층이 쌓여서 생긴 게 아니다.
그래도 추정량 자체는 볼 값이 있다. 표본 표준편차는 두 가지로 어긋난다.
배치 편향(평균적으로 얼마나 작게 나오나) 산포(뽑을 때마다 얼마나 흔들리나)
2 -0.4363 0.4253
4 -0.2020 0.3366
8 -0.0979 0.2449
32 -0.0237 0.1244
256 -0.0030 0.0440
산포는 1/sqrt(2·배치) 에 맞는다. 배치 32에서 예측 0.1250, 측정 0.1244 다.
편향은 -3/(4·배치) 를 따른다. 둘 다 배치가 클 때의 1차 근사여서, 표의 첫 줄인
배치 2에서는 예측이 14~17% 어긋난다. 배치 8부터는 몇 % 안쪽이다.
편향이 왜 -3/(4n) 인지는 두 조각이다. NumPy 의 .std() 가 n-1 이 아니라 n
으로 나눠서 -1/(2n), 그리고 분산이 아니라 그 제곱근을 재기 때문에 젠센
부등식으로 -1/(4n) 이 더 붙는다. 그래서 ddof=1 로 바꿔도 배치 8에서 3.5%는
그대로 남는다. 프레임워크의 BatchNorm 도 편향 분산을 쓰므로 NumPy 만의 문제도
아니다.
이 추정 오차가 학습 중에 하는 일은 스케일을 바꾸는 게 아니라 매 배치마다 값을 흔드는 것이다. 다음 절의 규제 효과가 거기서 나온다.
대가 2: 학습과 추론이 달라진다
배치 통계를 쓴다는 건, 같은 샘플이라도 어떤 샘플들과 함께 배치에 들어갔느냐에 따라 출력이 달라진다는 뜻이다. 학습 중에는 그 흔들림이 일종의 규제로 작동해 도움이 되기도 한다.
문제는 추론이다. 샘플 하나를 넣을 때는 배치가 없다. 그래서 배치 정규화는 학습 동안 평균과 분산의 이동평균을 따로 모아 두고, 추론에서는 그 값을 쓴다. 학습 경로와 추론 경로가 다른 계산을 하는 셈이고, 배치가 작거나 학습·추론 분포가 다르면 그 차이가 드러난다.
층 정규화가 나온 자리가 여기다. 배치 방향 대신 한 샘플 안의 특징 방향으로 정규화한다.
z = (z - z.mean(1, keepdims=True)) / (z.std(1, keepdims=True) + 1e-5)
축 하나만 바뀌었는데 성질이 달라진다. 다른 샘플을 쳐다보지 않으니 배치 크기와 무관하고, 학습과 추론이 같은 계산을 하고, 샘플마다 길이가 다른 데이터에도 쓸 수 있다. 트랜스포머가 층 정규화를 쓰는 이유가 그것이다.
무엇이 남는가
정규화가 초기화를 완전히 대체하지는 않는다. 위 표에서 정규화를 붙인 세 초기값이 같은 곳에 도착했지만, 그건 크기가 같아진 것이고 가중치가 담고 있는 방향은 여전히 초기값이 정한다. 그리고 정규화 층 자체에도 스케일과 이동 파라미터가 붙어서, 그 초기값은 또 정해 줘야 한다.
그래서
- 정규화는 층마다 곱셈의 밑을 1로 리셋한다. 초기값 셋이 20층 뒤
0.6310으로 같아지는 것이 그 증거다 - 얻는 것은 속도보다 먼저 초기값 민감도의 소멸이다
- 배치 정규화의 출력은 배치 크기와 무관하게 분산이 1이다. 배치가 작을 때 값이
뜨는 것은 추정 편향이 아니라 자기 표준화가 꼬리를
sqrt(배치-1)로 자르기 때문이고, 활성함수를 선형으로 바꾸면 그 차이가 사라진다 - 추정 오차는 스케일이 아니라 배치마다의 흔들림으로 나타난다. 편향
-3/(4·배치), 산포1/sqrt(2·배치)이며 둘 다 큰 배치에서의 근사다 - 층 정규화는 배치를 쳐다보지 않아 그 문제가 없다. 대신 정규화 축이 달라지므로 같은 것이 아니다
다음 편은 학습이 잘 되는 것과 잘 맞히는 것이 다르다는 이야기다. 훈련 손실을 0까지 내려놓고 새 데이터에서 재 보면 무슨 일이 벌어지는지, 다항식 하나로 끝까지 밀어 본다.
Comments