재귀와 합성곱 part 1 of 13

재귀 한 걸음이 하는 계산

guide / / 6 sections

앞 시리즈는 텐서에서 시작해 학습률, 역전파, 기울기 소실, 미니배치, 정규화, 일반화까지 오고 나서 8편에서 곧장 어텐션으로 갔다. 그 사이가 비어 있다. 어텐션 전에 순서를 다루던 방법이 있었고, 그것들을 열어 보지 않으면 어텐션이 무엇을 바꾼 것인지도 말할 수 없다.

제일 단순한 것부터 연다. 재귀 신경망이다.

한 걸음이 하는 계산

문장을 왼쪽부터 한 글자씩 읽는데, 지금까지 읽은 것을 벡터 하나에 담아 들고 간다. 그 벡터를 은닉 상태라고 부른다. 글자를 하나 읽을 때마다 하는 일은 이게 전부다.

h = torch.zeros(683)                 # 처음엔 아무것도 모른다
for t in range(128):
    x = tok[idx[t]]                  # 이번 글자의 임베딩 (128,)
    h = torch.tanh(W_ih @ x + b_ih + W_hh @ h + b_hh)

새 글자를 한 번 변환하고, 들고 있던 상태를 한 번 변환하고, 더한 다음 tanh 로 누른다. 그게 한 걸음이다. 128자를 읽으면 이 줄을 128번 돈다.

여기서 이상한 점이 하나 있다. W_ihW_hht 가 안 붙는다. 128번을 같은 행렬로 돈다. 20층짜리 신경망은 층마다 다른 가중치를 갖는데, 재귀는 128번을 같은 것으로 통과한다. 앞 시리즈 4편이 층에 대해 잰 기울기 소실이 여기서는 길이에 대해 일어나는 이유가 그것이고, 그 얘기는 8편에서 다시 한다.

파라미터의 73%가 한 행렬에 있다

폭 683 으로 잡으면 63만 개가 이렇게 나뉜다.

tok.weight        (100, 128)     12,800   글자 -> 임베딩
rec.weight_ih_l0  (683, 128)     87,424   임베딩 -> 상태
rec.weight_hh_l0  (683, 683)    466,489   상태 -> 상태
bias 넷            (683) x 4       2,732
head.weight       (100, 683)     68,300   상태 -> 다음 글자
head.bias         (100)             100
                                637,845

W_hh 하나가 466,489 개, 전체의 73.1% 다. 재귀 신경망의 파라미터는 거의 전부 상태를 상태로 옮기는 데 들어간다. 입력을 받는 데 쓰는 것은 87,424 개 로 그 5분의 1도 안 된다.

폭을 두 배로 하면 W_hh 는 네 배가 된다. 재귀 모형의 폭을 키우기가 비싼 이유다.

유닛 하나를 꺼내 본다

은닉 상태는 683개짜리 숫자 묶음이다. 그중 하나만 뽑아서 글자를 따라가며 보면 읽을 수 있는 것이 나오기도 한다.

7편에서 쓴 것과 같은 프로토콜로 800걸음 학습시킨 RNN 에서, 유닛마다 활성과 몇 가지 성질의 상관을 재면

                    유닛    상관
공백 여부            546   +0.675
따옴표 안            292   +0.226
개행 뒤 몇 번째       594   -0.174
대문자 여부           459   +0.169

546 번이 눈에 띈다. 실제 문장에 넣고 그 유닛만 그려 보면

은닉 유닛 546 하나가 글자를 따라가며 내는 값 -1 +0 +1 W i d t h · 2 · h a s · t w o · k i n k s · a n d · c a n n o t · i m i t a t e 자리 32 의 글자 하나만 바꿨을 때 남는 상태 차이 0.00 0.25 0.50 0.75 1.00 GRU LSTM RNN 0 8 16 32 64 95 바꾼 자리에서 뒤로 몇 자
위: 683개 은닉 유닛 중 546번 하나가 글자를 하나씩 읽으며 내는 값. 세로선이 띄어쓰기 자리인데, 거기마다 값이 1 근처로 올라간다. 아래: 자리 32 의 글자 하나만 다른 글 두 개를 넣고 은닉 상태의 차이를 잰 것. 세 구조 다 네 자 만에 절반, 서른두 자 만에 0.01 아래로 떨어진다.

띄어쓰기마다 0.9 근처로 올라가고 글자에서는 내려온다. 683개 숫자 중 하나가 “방금 띄어쓰기를 읽었다” 는 뜻으로 쓰이고 있다.

이게 왜 쓸모 있는지는 바로 보인다. 다음 글자를 맞히는 데 지금이 단어 첫머리인지 아닌지가 크게 다르다. 단어 첫 글자의 분포와 단어 중간 글자의 분포가 다르니까.

나머지 셋은 상관이 0.2 언저리로, 읽을 수 있다고 말하기 어렵다. 하나가 하나를 맡는 경우는 드물고 대개는 여러 유닛에 흩어져 있다. 여기서는 마침 하나 나온 것이다.

글자 하나를 바꾸면

“들고 간다” 는 말이 어디까지 사실인지 재 본다. 똑같은 글 두 개를 만들고 자리 32 의 글자 하나만 바꾼 다음, 그 뒤로 은닉 상태가 얼마나 다른 채로 남는지 본다.

   뒤로     RNN    LSTM     GRU
      1   0.760   0.824   0.820
      4   0.332   0.385   0.343
      8   0.122   0.161   0.124
     32   0.004   0.007   0.005
     95   0.000   0.000   0.000

바꾼 직후에는 상태의 76~82% 가 달라진다. 그런데 네 자 만에 절반이 사라지고, 서른두 자 뒤에는 1% 도 안 남는다.

게이트가 있는 LSTM 과 GRU 도 거의 같다. 잊는 속도가 구조로 정해지는 게 아니라 학습으로 정해진다는 뜻인데, 그 얘기는 게이트를 직접 열어 보는 2편에서 한다.

여기서 기억해 둘 것은 하나다. 재귀 상태의 기억은 네 자 남짓이다. 그런데 7편에서 보겠지만 이 모형들은 다음 글자 맞히기를 꽤 잘한다. 대부분의 글자는 바로 앞 몇 자로 정해지기 때문이다.

남는 것

tanh 를 썼는데 왜 그것인지는 안 짚었다. relu 를 쓰면 상태가 발산하기 쉽고, 그래서 재귀에서는 눌러 주는 함수를 쓴다 - 재 보지는 않았다.

유닛 상관도 선형 상관이라, 두 유닛이 짝을 이뤄 무언가를 나타내는 경우는 못 잡는다. 하나짜리 탐침으로 안 잡히는 것이 훨씬 많을 것이다.

그리고 이 편의 숫자는 800걸음 학습한 모형에서 나왔다. 더 오래 학습시키면 7편에서 보듯 검증 손실이 오르는데, 그때 유닛이 무엇을 나타내는지는 안 봤다.

그래서

  • 재귀 한 걸음은 h = tanh(W_ih·x + W_hh·h + b) 한 줄이다. 새 글자 한 번, 들고 있던 상태 한 번, 더하고 누른다
  • 128자를 같은 행렬로 128번 돈다. 층마다 가중치가 다른 깊은 신경망과 다르다
  • 파라미터 637,845 개 중 466,489 개, 73.1% 가 상태를 상태로 옮기는 W_hh 다. 입력을 받는 몫은 87,424 개뿐이다
  • 683개 유닛 중 546 번은 띄어쓰기와 상관이 +0.675 다. 실제 글에서 띄어쓰기 마다 0.9 근처로 켜진다
  • 글자 하나를 바꾸면 상태의 76% 가 달라지지만 네 자 만에 절반, 서른두 자 뒤에는 1% 미만이 남는다
  • 게이트가 있는 LSTM · GRU 도 이 속도가 거의 같다

Comments