보이는 딥러닝 part 13 of 13

63만 개짜리로 글자를 예측해 본다

guide / / 6 sections

부품은 다 봤다. 이제 이어 붙여 돌린다. 목표는 성능이 아니라 재는 것이다.

말뭉치는 이 블로그의 영문 글 13편이다. 그림 SVG 를 걷어내면 문자 75,353 개, 고유 문자 100 개다. 블로그가 계속 늘어나면 말뭉치도 변하니, 학습에 쓴 그대로를 /data/part13-corpus.txt 에 고정해 두었다. 아래 숫자는 전부 그 파일에서 나온다. 90 대 10 으로 나눠 67,817 자로 학습하고 7,536 자로 검증한다. 모형은 12편의 결론대로 Pre-LN 이고 블록 3개, d=128, 머리 4개, 문맥 128자, 파라미터 637,156 개다.

손실의 눈금부터 세운다

숫자를 보기 전에 그 숫자가 무엇인지 정해 둬야 한다. 손실은 교차 엔트로피이고, 아무것도 모르는 모형은 균등하게 찍는다. 어휘가 100 이니 그때 손실은 ln(100) = 4.6052 다.

확인은 정확하게 된다. 마지막 층의 가중치와 편향을 0으로 두면 로짓이 전부 같아져 예측이 완전 균등이 된다.

head 를 0으로:  손실 4.605171     ln(100) = 4.605170

차이가 5.4e-07 이다. 실제 초기값에서는 4.7140 으로 조금 위인데, 무작위 head 가 만든 로짓 차이가 예측을 균등에서 살짝 밀어낸 만큼이다.

위쪽 눈금이 정해졌으니 아래쪽도 하나 필요하다. 바이그램, 즉 앞 글자만 보고 다음 글자를 세는 것이다. 학습 데이터에서 세어 검증에서 재면 2.6501 이다. 트랜스포머가 이 선을 못 넘으면 존재 이유가 없다.

균등 예측       4.6052   퍼플렉서티 100
바이그램        2.6501   퍼플렉서티  14.2

퍼플렉서티는 exp(손실) 이고 몇 갈래로 헷갈리는지로 읽으면 된다. 균등은 100갈래, 바이그램은 14.2갈래다.

돌린다

1 2 3 4 0 1000 2000 3000 4000 5000 걸음 손실 균등 예측 ln(100)=4.61 바이그램 2.65 최저 1.751 (3000걸음) 훈련 검증
문자 언어모형의 훈련·검증 손실. 위 두 가로선은 균등 예측(4.61)과 바이그램 세는 것(2.65)이다. 200걸음이면 바이그램을 따라잡고, 3000걸음에서 검증이 1.751 로 바닥을 찍은 뒤 돌아선다. 훈련 손실은 끝까지 내려간다.
걸음      훈련      검증
   1    4.5573    4.5573
 200    2.6367    2.6474      <- 바이그램 따라잡음
1000    2.1031    2.2001
3000    1.2432    1.7510      <- 검증 최저
5000    0.8344    2.0272

200 걸음이면 바이그램을 따라잡는다. 앞 글자 하나 세는 일을 배우는 데 그 정도가 든다.

최저는 3000 걸음의 1.7510, 퍼플렉서티 5.8 이다. 바이그램의 14.2 에서 5.8 로 내려왔으니, 문맥을 더 보는 것이 실제로 값을 한다.

그리고 7편이 그대로 재현된다. 3000 걸음을 넘기면 훈련 손실은 계속 내려가는데 검증은 올라간다. 끝에서 훈련 0.8344, 검증 2.0272 다. 훈련만 보면 좋아지는 중이고 실제로는 나빠지는 중이다. 말뭉치가 6만자뿐이니 63만 파라미터가 외우기에 충분하다.

무엇을 배웠나

같은 모형에서 걸음별로 뽑아 봤다. 앞부분만 옮긴다.

걸음 0     ]TUoF%힣`Y)oYgEk1 fb3cqieQZqsV6{O²h`|F#a {_m:O.5Σ-VE·.

걸음 200   `1   (힣,  o 1Ee1  b3      as   Oghe        0.
           0he.0.     9     -19alongis.    ug    s

걸음 1000  `` ovan`, ong vo abe tientas to
           fre is steme- rivaresimere. Sonsing isactinalongis.

걸음 5000  `16, a=0, 36, 112003  0.3009849
           ```
           The smallest `1.000h`. **`shape(2*x) = 0
           `dL/{w_h` andeds share number `sqrt(d) = sum(0)`

걸음 200에서 띄어쓰기가 생기고, 1000에서 영어 단어 모양이 나오고, 5000에서 마크다운이 나온다. 백틱, 코드 펜스, ** 강조, sqrt(d), dL/dw 비슷한 것들이다.

정직하게 말하면 이건 영어를 배운 게 아니라 이 말뭉치의 겉모양을 배운 것이다. 말뭉치가 기술 문서 마크다운이니 그 껍데기가 먼저 나온다. 0.272 같은 숫자는 12편에 실제로 쓴 값이고, 외운 것에 가깝다.

인과 마스크를 빼면

어텐션은 기본적으로 모든 자리를 본다. 다음 글자를 맞히는 일에서 다음 글자는 입력 안에 이미 들어 있다. 위치 t 의 정답은 위치 t+1 의 입력이다. 그래서 뒤를 못 보게 막는 삼각 마스크가 필요하다.

빼고 돌려 보면 이렇게 된다.

걸음    훈련      검증
 300   2.5317   2.5530
 600   0.3393   0.3348
 900   0.0473   0.0506
1200   0.0306   0.0331

손실이 0.03 이다. 바이그램의 2.65 는 물론이고 제대로 학습한 모형의 1.75 보다 한참 아래다. 검증 손실까지 같이 내려가니 과적합 신호도 안 뜬다. 7편에서 말한 “검증으로 판단하라” 조차 여기서는 안 통한다.

정답을 옆에서 읽는 쪽이 훨씬 싸기 때문이다. 위치별 표현 같은 것을 조금은 배웠을 수도 있지만, 다음 글자를 맞히는 능력으로 재면 학습 전보다 못하다. 확인은 한 줄이면 된다. 같은 모형에 마스크를 켜고 다시 재면,

마스크 켜고 잰 훈련 손실   5.9703

학습을 아예 안 한 4.6052 보다도 나쁘다. 이 실패가 무서운 이유가 여기 있다. 손실 곡선이 아름답게 내려가고 검증도 따라 내려가는데, 실제 성능은 찍는 것보다 못하다.

헤드 하나를 꺼내 본다

10편에서 헤드가 서로 다른 것을 볼 자리는 있지만 강제되지는 않는다고 했다. 실제로 학습한 이 모형에서 재 보자. 각 헤드가 몇 칸 앞을 보는지, 상대 거리별 평균 가중치다.

블록 헤드   거리0   거리1   거리2   거리3   거리4
 0    0    0.017  0.020  0.016  0.016  0.015
 0    1    0.016  0.017  0.018  0.017  0.016
 0    2    0.005  0.879  0.005  0.004  0.005
 0    3    0.019  0.030  0.017  0.017  0.017

블록 0의 헤드 2가 바로 앞 글자에 0.879 를 준다. 자기 자신에는 0.005, 나머지 어디에도 0.005 언저리다. 거의 순수한 “앞 글자 보기” 헤드가 학습으로 생겼다.

같은 블록의 나머지 세 헤드는 어느 거리에나 0.015 에서 0.030 이다. 얼마나 균등한지 엔트로피로 재면 분명해진다. 같은 자리에서 볼 수 있는 곳에 고르게 뿌렸을 때의 엔트로피가 4.551 인데, 세 헤드가 4.430, 4.440, 4.391 로 그 0.97 배다. 최대 가중치도 0.030, 0.029, 0.040 이다. 사실상 아무 데도 안 보고 있다. 헤드 2는 같은 척도로 0.660, 비로 0.145 다. 10편에서 “구조는 자리를 마련해 줄 뿐” 이라고 한 것이 한 블록 안에서 이렇게 갈린다.

블록 1과 2에서는 12개 헤드 중 11개가 거리 1을 최대로 보되, 0.20 에서 0.34 정도로 완만하게 퍼진 모양이다. 블록 0의 헤드 2처럼 뾰족한 것은 하나뿐이다.

그래서

  • 손실의 눈금은 두 개다. 위는 ln(어휘수), 여기서는 4.6052 이고 head 를 0으로 두면 5.4e-07 안에서 정확히 재현된다. 아래는 바이그램 2.6501
  • 200걸음이면 바이그램을 따라잡고, 3000걸음에서 검증 1.7510 으로 바닥을 친다. 퍼플렉서티로 14.2 에서 5.8
  • 그 뒤로는 7편이다. 훈련 0.8344 로 계속 내려가는 동안 검증은 2.0272 로 올라간다
  • 인과 마스크를 빼면 손실이 0.03 까지 내려가는데, 같은 모형을 마스크 켜고 재면 5.9703 으로 학습 전보다 나쁘다. 곡선이 예쁘다고 배운 게 아니다
  • 헤드는 강제 없이도 특화될 수 있다. 블록 0 헤드 2가 앞 글자에 0.879. 같은 블록의 나머지 셋은 균등에 가깝게 놀고 있다

열세 편을 마친다. 재는 일이 무엇에 쓰이는지가 이 시리즈의 답인데, 성능을 올리는 데 쓰이지 않았다. 틀린 것을 찾는 데 쓰였다.

4편에서 신호가 죽었을 때 범인은 tanh 처럼 보였지만 미분은 1.0000, 가장 클 수 있는 값이었다. 6편에서 배치가 작을 때 값이 뜨는 것은 표준편차 추정이 빗나가서 같았지만, 출력의 표준편차는 어느 배치에서나 정확히 1이었다. 10편에서는 헤드의 랭크 상한이 어떤 패턴을 막는다고 썼다가, 랭크 2면 어떤 순열이든 만들 수 있다는 것을 재고 그 절을 통째로 다시 썼다. 그리고 이번 편에서는 손실 곡선이 0.03 까지 아름답게 내려가는 모델이 사실은 찍는 것보다 못했다.

네 번 다 그럴듯한 설명이 먼저 있었고, 재고 나서 틀린 것으로 밝혀졌다. 그림을 그리는 이유가 그것이다.

Comments