Transformer · 위치 인코딩

위치 인코딩 — sin과 cos를 함께 쓰는 이유

어텐션은 단어 순서를 모릅니다. 그래서 Transformer는 위치마다 고유한 사인·코사인 무늬를 임베딩에 더합니다. 그런데 왜 sin 하나가 아니라 sin과 cos를 짝으로 쓸까요? 답은 시계 바늘에 있습니다. 짝을 이룬 두 값은 원 위의 한 점이 되고, “k칸 뒤로 가기”는 그 점을 고정된 각도만큼 돌리는 일이 됩니다.

1. 어텐션은 순서를 모른다

셀프 어텐션은 모든 단어 쌍의 내적을 구하고, 그 가중치로 값 벡터를 섞습니다. 이 계산 어디에도 “몇 번째 단어인가”는 들어가지 않습니다. 그래서 입력 순서를 섞으면 출력도 똑같이 섞여 나올 뿐, 각 단어가 받는 결과는 변하지 않습니다. dog bites man과 man bites dog가 단어마다 같은 표현을 받는다는 뜻입니다.

데모 1 · 순서를 섞어 보기

6차원 임베딩과 무작위 WQ·WK·WV로 실제 셀프 어텐션을 계산합니다. 오른쪽 숫자는 “원래 순서에서 같은 단어가 받았던 출력”과의 최대 차이입니다.

위치 인코딩을 켜면 같은 단어라도 몇 번째 자리에 있느냐에 따라 입력 벡터가 달라지고, 그 차이가 어텐션 출력에도 드러납니다.

2. 사인·코사인 위치 인코딩

「Attention Is All You Need」(Vaswani 외, 2017)는 위치 pos의 벡터를 이렇게 정합니다.

$$PE_{(pos,\,2i)} = \sin\!\left(\frac{pos}{10000^{2i/d_{model}}}\right),\qquad PE_{(pos,\,2i+1)} = \cos\!\left(\frac{pos}{10000^{2i/d_{model}}}\right)$$ 짝수 차원에는 sin, 바로 옆 홀수 차원에는 cos. 같은 i를 쓰는 두 차원 (2i, 2i+1)이 한 쌍이며, 이 쌍은 같은 주파수 \(\omega_i = 1/10000^{2i/d_{model}}\)를 공유합니다.

식을 주파수로 다시 쓰면 각 쌍은 그냥 \((\sin\omega_i\,pos,\ \cos\omega_i\,pos)\)입니다. 아래 히트맵은 행이 위치, 열이 차원입니다. 왼쪽 열들은 빠르게 깜빡이고 오른쪽으로 갈수록 천천히 변합니다.

데모 2 · 위치 인코딩 히트맵

파랑 = +1, 주황 = −1. 마우스를 올리면(또는 탭하면) 그 칸의 값이 나옵니다.

선택한 칸—

3. sin 하나로는 부족하다

한 주파수만 놓고 보면 \(\sin x\)는 한 주기 안에서 같은 값을 두 번 냅니다(\(\sin x = \sin(\pi - x)\)). 값이 0.5라고 해도 올라가는 중인지 내려가는 중인지 알 수 없습니다. 짝이 되는 cos가 그 답을 줍니다. 두 지점에서 cos는 부호가 반대입니다.

데모 3 · 같은 sin 값, 다른 위치

주파수 ω 하나에 대해 sin(ω·pos)(파랑)과 cos(ω·pos)(주황)를 그렸습니다. 점선은 현재 위치의 sin 값이고, 빨간 점은 sin만 보면 현재 위치와 구분되지 않는 다른 위치입니다.

sin만으로 헷갈리는 위치—

4. 시계 바늘: 원 위의 한 점

\(\sin^2\theta + \cos^2\theta = 1\)이므로 \((\sin\theta, \cos\theta)\)는 반지름 1인 원 위의 점입니다. 가로 좌표를 sin, 세로 좌표를 cos로 두면 \(\theta = 0\)일 때 점은 12시에 있고, θ가 커질수록 시계 방향으로 돕니다. 위치 인코딩의 한 쌍은 문자 그대로 시계 바늘입니다.

그림 · 왜 (sin θ, cos θ)는 원 위에 있나

원 위의 점을 끌거나 슬라이더를 움직여 보세요. 점에서 내린 가로 길이가 sin θ(파랑), 세로 길이가 cos θ(주황)입니다. 두 변과 반지름(1)이 직각삼각형을 이루니 피타고라스 정리로 sin²θ + cos²θ = 1² = 1. 각도가 바뀌어도 반지름은 그대로 1이므로 점은 항상 원 위에 있습니다.

sin θ (가로)—
cos θ (세로)—
sin²θ + cos²θ—
$$\theta = \omega_i \cdot pos$$ 단어 하나를 지날 때마다 바늘이 ωi만큼 돕니다. 0번 단어는 12시, 1번 단어는 ωi, 2번 단어는 2ωi만큼 돌아간 자리. 위치는 곧 바늘이 가리키는 방향입니다.

pos에서 pos+k로 가면 각도가 \(\omega_i k\)만큼 늘어납니다. 이 증가량에는 pos가 들어 있지 않습니다. 바늘이 지금 3시에 있든 8시에 있든 “30° 더 돌려라”라는 명령은 같습니다.

데모 4 · dmodel = 16의 시계 8개

진한 점 = pos, 연한 점 = pos+k, 보라색 호 = 두 점 사이의 회전각 ωik. pos 슬라이더를 움직여 보세요. 바늘은 돌지만 호의 길이는 그대로입니다.

5. k칸 이동 = 회전 행렬

원 위의 점을 φ만큼 돌리는 연산은 2×2 회전 행렬 하나입니다. 덧셈 정리 \(\sin(a+b) = \sin a\cos b + \cos a\sin b\), \(\cos(a+b) = \cos a\cos b - \sin a\sin b\)를 행렬로 쓰면 다음과 같습니다.

$$\begin{pmatrix}\sin(\omega_i(pos+k))\\ \cos(\omega_i(pos+k))\end{pmatrix} = \underbrace{\begin{pmatrix}\cos(\omega_i k) & \sin(\omega_i k)\\ -\sin(\omega_i k) & \cos(\omega_i k)\end{pmatrix}}_{M_k\ (\text{pos가 없다})}\begin{pmatrix}\sin(\omega_i\,pos)\\ \cos(\omega_i\,pos)\end{pmatrix}$$ Mk는 k에만 달려 있습니다. 모델이 “3칸 앞 단어”를 찾는 법을 선형 변환 하나로 배우면, 그 변환은 문장의 어느 위치에서든 똑같이 통합니다. 원논문이 이 방식을 고른 이유로 직접 든 성질입니다(“PEpos+k can be represented as a linear function of PEpos”).

데모 5a · 행렬을 직접 곱해 보기

Mk·PE(pos)를 계산해 PE(pos+k)와 비교합니다. pos를 바꿔도 Mk의 숫자는 하나도 안 바뀝니다.

sin만 있다면 어떨까요? \(\sin(\omega(pos+k))\)를 만들려면 \(\cos(\omega\,pos)\)가 필요한데, 그 값이 벡터에 없습니다. 아래 그림은 모든 pos에 대해 가로축 sin(ω·pos), 세로축 sin(ω(pos+k))를 찍은 것입니다. 점들이 직선이 아니라 타원을 그립니다. 같은 가로 값에 세로 값이 두 개 있으니 어떤 함수로도, 하물며 선형 함수로도 만들 수 없습니다.

데모 5b · sin만으로 k칸 뒤를 예측할 수 있나

파랑 점 = (sin ω·pos, sin ω(pos+k)). 주황 선 = 최소제곱으로 맞춘 최선의 직선.

sin만 쓴 직선 예측의 최대 오차—
(sin, cos) 쌍 + Mk의 최대 오차—

6. 크기는 일정하고, 내적은 거리만 본다

쌍마다 \(\sin^2 + \cos^2 = 1\)이므로 위치 벡터 전체의 길이는 위치와 상관없이 항상 \(\sqrt{d_{model}/2}\)입니다. 두 위치 벡터의 내적은 쌍별로 \(\sin a\sin b + \cos a\cos b = \cos(a-b)\)가 되어

$$PE(pos)\cdot PE(pos+k) = \sum_{i=0}^{d_{model}/2-1}\cos(\omega_i k)$$ 오른쪽에 pos가 없습니다. 두 바늘이 이루는 각도의 코사인을 다 더한 값이라, 절대 위치가 아니라 두 단어 사이의 거리 k에만 의존합니다.

sin만 쓰면 사정이 다릅니다. pos=0이면 벡터 전체가 0이고, 위치마다 신호 세기가 들쭉날쭉하며, 내적도 기준 위치에 따라 모양이 바뀝니다.

처음 보는 분을 위해

cos(A − B) 공식은 무엇을 말하나

고등학교에서 배운 이 공식이 위의 내용을 전부 설명합니다.

\(\cos(A-B) = \cos A\cos B + \sin A\sin B\)

  1. 시계 바늘 두 개를 떠올립니다. 하나는 각도 A, 다른 하나는 각도 B만큼 돌아가 있습니다. 위치 인코딩에서는 두 단어의 바늘입니다.
  2. 오른쪽은 “두 바늘이 얼마나 같은 쪽을 보는가”입니다. 세로끼리 곱하고(cos·cos), 가로끼리 곱해서(sin·sin) 더한 값, 즉 내적입니다. 같은 방향이면 1, 직각이면 0, 정반대면 −1이 나옵니다.
  3. 왼쪽은 그 값이 두 바늘 사이의 각도(A − B)만으로 정해진다고 말합니다. 바늘이 각각 어디를 가리키는지는 상관없습니다.
  4. 위치 인코딩에서 사이각은 “몇 칸 떨어졌나”(k)로 정해집니다. 5번과 8번 단어든 100번과 103번 단어든, 3칸 차이면 사이각이 같고 내적도 같습니다.

sin과 cos가 둘 다 필요한 이유: 오른쪽 두 항은 따로 보면 바늘 위치에 따라 출렁입니다. 그런데 한쪽이 줄면 다른 쪽이 그만큼 늘어나는 시소처럼 움직여서, 더한 값은 늘 일정합니다. 한쪽만 쓰면(sin만 쓰면) 출렁이는 값이 그대로 남습니다. 아래 슬라이더로 확인해 보세요.

두 단어는 항상 1칸 떨어져 있습니다(ω = 0.5, k = 1).

데모 6 · 벡터 길이와 내적 곡선

위: 위치별 벡터 길이. 아래: 기준 위치 p0 세 개에서 잰 PE(p0)·PE(p0+k). sin·cos 쌍이면 세 곡선이 한 줄로 겹칩니다.

7. 시계가 여러 개인 이유

쌍이 \(d_{model}/2\)개이니 시계도 그만큼 있고, 각각 속도가 다릅니다. 주기는 \(2\pi/\omega_i = 2\pi\cdot 10000^{2i/d_{model}}\)로, 가장 빠른 시계의 \(2\pi \approx 6.3\)칸부터 가장 느린 시계의 거의 \(2\pi \times 10000\)칸까지 기하급수로 늘어납니다.

초침·분침·시침을 함께 봐야 정확한 시각을 알 수 있는 것과 같습니다. 여러 속도의 시계를 함께 보면 위치가 하나로 정해집니다.

쌍 iωi주기(칸)이웃 간 회전각역할

dmodel = 512 기준, 256쌍 중 일부.

정리

sin은 위치를 표시하고, 짝이 되는 cos는 그 위치를 회전 각도로 만듭니다. 덕분에 ① k칸 이동이 pos와 무관한 회전 행렬이 되고, ② 한 주기 안에서 위치가 모호하지 않으며, ③ 벡터 길이가 일정하고 내적은 거리에만 의존합니다.

8. 그다음: RoPE

사인·코사인 위치 인코딩은 위치 벡터를 임베딩에 더합니다. 그러면 어텐션 점수 \(q\cdot k\)를 계산할 때 단어 내용과 위치가 뒤섞여, “거리만 본다”는 성질이 그대로 남지 않습니다. 요즘 LLM 대부분이 쓰는 RoPE(Rotary Position Embedding, Su 외 2021)는 이 회전 아이디어를 한 걸음 더 밀고 갑니다. 쿼리와 키 벡터의 각 2차원 쌍을 위치에 비례하는 각도만큼 직접 돌립니다.

$$\big(R(m\theta)\,q\big)\cdot\big(R(n\theta)\,k\big) = q^\top R(m\theta)^\top R(n\theta)\,k = q^\top R\big((n-m)\theta\big)\,k$$ 회전 행렬끼리는 각도가 빼기로 합쳐지므로, 점수에는 절대 위치 m, n 대신 상대 거리 n−m만 남습니다.

데모 8 · RoPE: 둘 다 옮기면 점수는 그대로

2차원 쿼리(파랑)와 키(주황)를 각자의 위치 m, n만큼 돌립니다. “둘 다 +5”를 누르면 두 벡터가 같이 돌고 내적은 변하지 않습니다.

n − m—
회전 후 점수 q′·k′—

9. 확인 퀴즈