보이는 딥러닝 part 9 of 13

위치 인코딩은 순서를 어떻게 집어넣나

guide / / 7 sections

8편은 어텐션이 순열 등변이라는 것으로 끝났다. 입력 순서를 섞으면 출력도 똑같이 섞일 뿐, 값은 하나도 안 변한다. 어텐션에게 “앞” 과 “뒤” 는 없다.

문장에서 순서는 의미다. 그러니 순서를 따로 넣어 줘야 한다. 이번 편은 그 방법이다.

순진한 방법이 왜 안 되나

가장 먼저 떠오르는 건 위치 번호를 그냥 차원 하나로 붙이는 것이다. 토큰 i 의 벡터 뒤에 i 를 적는다.

해 보면 바로 망가진다. 어텐션 로짓은 내적이고, 내적에는 i × j 항이 그대로 들어간다.

위치 0~5     내용 기여 폭 3.30   위치 기여 폭  11.18   최대 확률 0.626
위치 100~105 내용 기여 폭 3.30   위치 기여 폭 458.39   최대 확률 1.000

내용이 만드는 로짓 차이는 3.30 인데 위치가 만드는 차이가 458 이다. 문장 뒤쪽으로 갈수록 이 비는 계속 벌어진다. 8편에서 로짓이 커지면 소프트맥스가 포화한다고 했는데, 여기서는 최대 확률이 1.000 이다. 어텐션이 내용을 아예 안 본다.

위치 정보에 필요한 조건이 여기서 나온다. 자리마다 달라야 하지만, 자리가 뒤라고 해서 더 커지면 안 된다.

사인파

트랜스포머가 쓴 답은 주기함수다. 차원을 둘씩 짝지어 각각 다른 주기의 사인·코사인을 넣는다.

def PE(L, d):
    pos = np.arange(L)[:, None]
    i = np.arange(0, d, 2)[None, :]
    w = 1.0 / (10000 ** (i / d))          # 주파수, 차원마다 다르다
    P = np.zeros((L, d))
    P[:, 0::2] = np.sin(pos * w)
    P[:, 1::2] = np.cos(pos * w)
    return P

이게 무엇을 보장하는지 하나씩 재 본다. 아래는 전부 d=64, 위치 512개다.

첫째, 모든 자리의 크기가 같다. 주파수 하나마다 sin² + cos² = 1 이므로 노름은 자리와 무관하게 sqrt(d/2) 다. 측정하면 5.6569 이고 512개 자리에서 편차가 8.9e-16 이다. 순진한 방법이 깨뜨린 조건이 바로 이것이다.

둘째, 두 자리의 내적이 거리에만 의존한다. PE(i)·PE(j)ij 를 따로 보지 않고 i-j 만 본다. 같은 대각선 위 값들의 편차를 재면 1.8e-13 으로 부동소수점 잡음이다. 삼각함수 덧셈정리를 쓰면 이유가 한 줄로 나온다.

PE(i)·PE(j) = Σ [sin(i·w) sin(j·w) + cos(i·w) cos(j·w)] = Σ cos((i-j)·w)

실제로 Σ cos(거리 · w) 와 비교하면 최대 차이가 7.1e-15 다. 절대 위치를 넣었는데 상대 위치가 나온다. 이게 사인파를 고른 진짜 이유다.

거리가 멀수록 덜 닮는다는 말은 사실이 아니다

여기서 흔히 한 걸음 더 나간다 - “그래서 멀수록 내적이 작아지고, 가까운 토큰끼리 더 닮게 된다”. 재 보면 아니다.

0 8 16 24 32 0 128 256 384 511 두 자리 사이 거리 내적 최소 2.56 (거리 406) 거리 6 부터 다시 오른다
위치 인코딩 두 자리의 내적을 거리에 대해 그린 것. 가까운 거리에서는 빠르게 떨어지지만 거리 6 부터 다시 오르내린다. 511개 구간 중 244곳에서 값이 오히려 커지고, 최소는 거리 406 이며 그 뒤 다시 올라간다.

가까운 거리에서는 맞다. 32.00, 30.92, 28.30, 25.59, 23.93, 23.50 으로 떨어진다. 그런데 거리 6에서 23.56 으로 다시 오른다. 511개 구간 중 244 곳에서 값이 커진다. 최소값은 거리 406 에서 2.56 이고, 그보다 먼 거리 511 에서는 오히려 6.42 로 올라가 있다.

당연하다. 주기함수를 더한 것이니 진동한다. 단조 감소하는 함수가 아니다. 사인파 위치 인코딩이 주는 것은 “가까울수록 닮음” 이 아니라 거리의 함수라는 사실 그 자체다. 그 함수가 거리에 대해 어떤 모양인지는 별개 문제이고, 보다시피 예쁘지 않다.

진짜 쓸모: 이동이 선형사상이다

주기함수를 고른 값어치는 다른 데 있다. 어떤 간격 k 를 정하면, 모든 자리에 대해 똑같이 작동하는 고정 행렬 M_k 가 있어서 PE(pos + k) = M_k · PE(pos) 가 된다. 자리마다 다른 행렬이 아니라 하나다.

각 주파수 짝이 [sin(pos·w), cos(pos·w)] 이니 k 만큼 미는 것이 각도 k·w 만큼의 회전이기 때문이다. 회전 행렬은 pos 에 안 달려 있다.

측정으로 확인한다. 위치 512개를 차원 64개에 맞추므로 미지수보다 식이 여덟 배 많다. 우연히 맞을 수 있는 상황이 아니다.

   k      사인파        무작위 임베딩
   1     4.9e-14           0.504
   2     5.0e-14           0.489
   5     4.8e-14           0.548
  17     3.9e-14           0.501
  50     1.6e-13           0.549

사인파는 잔차가 부동소수점 잡음이고, 같은 크기의 무작위 임베딩은 0.5 언저리다

  • 상대오차로 0.898, 사실상 아무것도 못 맞춘다.

이 차이가 실질적이다. 어텐션이 “세 칸 앞” 같은 관계를 보려면 QK 를 만드는 W 가 그 관계를 표현할 수 있어야 하는데, W 가 하는 일은 선형변환이다. 사인파에서는 그 관계가 이미 선형사상으로 존재하므로 배우기만 하면 된다. 무작위로 자리마다 벡터를 하나씩 준 것에는 그런 사상이 아예 없다.

학습형 위치 임베딩이 못 쓸 것이라는 뜻은 아니다. 실제로 잘 쓰이고, 자리별 벡터를 데이터에서 배운다. 다만 이 구조를 거저 얻는 게 아니라 배워야 한다는 차이가 있다.

덤: 훈련 길이 밖

사인파는 식이라 아무 자리에나 값이 있다. 512개까지만 보고 학습해도 위치 4000의 노름은 5.6569 로 같고, 4000과 4001의 내적은 30.917 로 위치 0과 1의 값과 정확히 같다. 학습형 임베딩은 표에 없는 자리에 줄 값이 없다.

물론 값이 있다는 것과 모델이 그 구간에서 잘 동작한다는 것은 다른 얘기다. 실제로 훈련 길이를 넘기면 성능이 떨어지고, 그래서 요즘 모델은 회전 위치 인코딩처럼 다른 방식을 쓴다. 사인파가 보장하는 건 “정의된다” 까지다.

값은 치른다

공짜가 아니다. 위치를 더하면 그만큼 내용이 흐려진다. 8편의 토큰 여섯 개에 위치 인코딩을 더해 보면 첫 행이 이렇게 바뀐다.

             t0     t1     t2     t3     t4     t5
위치 없음   0.510  0.030  0.015  0.389  0.039  0.016
위치 더함   0.610  0.213  0.020  0.140  0.012  0.004

t0 의 주제 짝은 t3 인데, 그 몫이 0.389 에서 0.140 으로 떨어졌다. 대신 바로 옆자리인 t10.030 에서 0.213 으로 올랐다. 위치가 내용과 경쟁한다.

이 예에서는 위치가 이겼는데, 임베딩 크기와 위치 인코딩 크기의 비가 그것을 정한다. 실제 모델은 임베딩을 sqrt(d) 배 키운 뒤 더해서 이 비를 조정한다.

그래도 목적은 이뤘다. 순열 등변성을 다시 재면 이제 깨져 있다.

Z  = X + PE(6, d)          # 원래 순서에 위치를 더한 것
Zp = X[perm] + PE(6, d)    # 토큰만 섞고, 위치는 자리에 그대로 둔다

np.allclose(att(X[perm]), att(X)[perm])    # 위치 없음: True
np.allclose(att(Zp),      att(Z)[perm])    # 위치 더함: False

Zp 를 만들 때 PE섞지 않은 것이 핵심이다. 위치까지 같이 섞으면 att(Z[perm]) 은 다시 등변이 된다 - 자리표를 토큰에 붙여 들고 다니면 자리를 옮긴 게 아니기 때문이다.

그래서

  • 어텐션에 순서를 넣으려면 자리마다 다르되 뒤 자리가 더 커지면 안 된다. 위치를 원시 정수로 붙이면 위치 100 근처에서 로짓 기여가 4583.30 으로 내용을 덮고 최대 확률이 1.000 이 된다
  • 사인파는 모든 자리의 노름을 5.6569 로 같게 두고, 두 자리의 내적을 거리만의 함수 Σ cos(거리·w) 로 만든다. 절대 위치를 넣었는데 상대 위치가 나온다
  • 그 함수가 거리에 대해 단조 감소한다는 말은 틀렸다. 511개 구간 중 244곳에서 오히려 커지고 최소는 거리 406 이다
  • 진짜 값어치는 이동이 고정 선형사상이라는 것이다. 잔차 5e-14 대 무작위 임베딩 0.5. 상대 위치 관계를 W 가 배우기만 하면 되는 형태로 놓아 준다
  • 더하는 만큼 내용은 흐려진다. 주제 짝의 몫이 0.389 에서 0.140 으로 떨어진다

다음 편에서는 머리를 여러 개로 쪼갠다. 어텐션 하나가 평균 하나를 낸다면, 여러 개는 무엇을 따로 보는지 실제로 꺼내 본다.

Comments