
Positional Encoding에서 RoPE까지, 어텐션에 순서를 부여하는 법
- 1LLM은 왜 다음 토큰 하나만 예측할까
- 2Transformer는 왜 Q, K, V 셋으로 나눴을까
- 3토큰은 왜 단어가 아닐까
- 4LLM은 왜 가장 확률 높은 토큰을 고르지 않을까
- 5Positional Encoding에서 RoPE까지, 어텐션에 순서를 부여하는 법읽는 중
어텐션의 내적 에는 토큰의 내용만 들어갑니다. 위치 와 는 어디에도 없죠. “민수는 책을 돌려줬다”와 “책을 민수는 돌려줬다”에서 “돌려줬다”가 받는 어텐션 가중치가 같아집니다. 순서를 모르면 “개가 사람을 물었다”와 “사람이 개를 물었다”를 구분할 수 없으니, 위치 정보를 따로 넣어줘야 합니다.
방법은 크게 두 가지입니다. 위치마다 고유한 벡터를 만들어 토큰에 더하는 Positional Encoding(2017)과, Q와 K를 위치만큼 돌려서 상대 거리를 내적에 새기는 RoPE(2021). 지금 쓰이는 대부분의 오픈 LLM은 RoPE를 씁니다.
어텐션은 순서를 모른다
Transformer의 어텐션 에서 점수를 매기는 부분은 Q와 K의 내적입니다. 토큰 C가 토큰 A에 주는 점수 는 A의 내용이 정할 뿐, A가 첫 번째에 있든 세 번째에 있든 달라지지 않습니다.
A와 B의 순서를 바꿔도 C가 주는 가중치는 그대로입니다. 가중치가 위치가 아니라 내용을 따라가기 때문이죠. RNN은 토큰을 하나씩 순서대로 처리하니 위치가 자연히 반영되지만 Transformer는 모든 토큰을 한꺼번에 보면서 속도를 얻는 대신 순서 정보를 잃었습니다.
Positional Encoding: 위치를 더하다
첫 해법은 단순합니다. 위치마다 고유한 벡터를 하나 만들어서 토큰 임베딩에 더하는 것입니다. 위치 0에는 벡터 A, 위치 1에는 벡터 B… 이 벡터들이 서로 달라야 모델이 “이 토큰은 몇 번째”인지 알 수 있습니다.
“Attention Is All You Need”(2017)의 저자들은 이 벡터를 사인·코사인 함수로 만들었습니다.
는 토큰 위치, 는 차원 번호, 는 전체 차원 수입니다. 분모 가 차원마다 다른 파동 속도를 정합니다.
이진 카운터와 같은 원리
원리는 이진수 카운터와 같습니다.
| pos | bit 3 | bit 2 | bit 1 | bit 0 |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 1 | 0 | 0 | 0 | 1 |
| 2 | 0 | 0 | 1 | 0 |
| 3 | 0 | 0 | 1 | 1 |
| 4 | 0 | 1 | 0 | 0 |
bit 0은 매 위치마다 뒤집히고, bit 1은 두 위치마다, bit 2는 네 위치마다 뒤집힙니다. 어떤 두 위치든 4비트 전체를 보면 반드시 구분할 수 있습니다.
사인·코사인 인코딩은 이 이진 카운터의 연속 버전입니다. 0과 1 대신 −1과 1 사이를 부드럽게 오가는 파동을 쓰고, 각 차원의 파동 속도가 다릅니다. 가 작으면 빠르게 변하고(bit 0처럼), 가 크면 느리게 변합니다(높은 비트처럼).
숫자로 보기
로 줄여서 확인해 봅시다. 차원 쌍이 두 개입니다. 쌍 0()은 분모가 이라 위치가 바뀔 때마다 값이 크게 변하고, 쌍 1()은 분모가 이라 거의 변하지 않습니다.
| pos | 쌍 0: sin | 쌍 0: cos | 쌍 1: sin | 쌍 1: cos |
|---|---|---|---|---|
| 0 | 0 | 1.00 | 0 | 1.00 |
| 1 | 0.84 | 0.54 | 0.01 | 1.00 |
| 2 | 0.91 | −0.42 | 0.02 | 1.00 |
| 3 | 0.14 | −0.99 | 0.03 | 1.00 |
앞쪽 쌍은 위치마다 값이 크게 뛰고, 뒤쪽 쌍은 거의 정지해 있습니다. 실제 모델에서 이면 64개 쌍이 각각 다른 속도로 변하니 어떤 두 위치의 벡터든 전체로 보면 구분할 수 있습니다.
이 설계에는 장점이 하나 더 있습니다. 위치 0과 위치 3의 인코딩 차이, 위치 100과 위치 103의 인코딩 차이가 같은 패턴을 따릅니다. 모델이 “3칸 앞”이라는 상대 거리를 학습할 수 있는 근거죠.
절대 위치의 한계
BERT(2018)와 GPT-2(2019)는 더 단순한 방법을 택했습니다. 사인 함수를 쓰는 대신 위치마다 벡터 하나를 테이블에 넣고 학습시키는 것이죠. BERT는 512개, GPT-2는 1024개 위치의 벡터를 학습했습니다. Vaswani et al.의 실험에서 사인·코사인과 거의 같은 성능을 보였습니다.
사인 함수든 학습 테이블이든 원리는 같습니다. 위치에 정해진 벡터를 토큰에 더합니다. 이런 방식을 절대 위치 인코딩(Absolute Positional Encoding)이라 부릅니다. 절대 위치 인코딩에는 두 가지 벽이 있습니다.
첫째, 학습 범위 밖의 위치를 처리하지 못합니다. BERT의 513번째 토큰, GPT-2의 1025번째 토큰은 참조할 벡터가 없죠. 사인 함수는 값 자체는 계산되지만, 학습 중 한 번도 본 적 없는 조합이 등장하면서 성능이 무너집니다.
둘째, 자연어에서 정말 중요한 것은 “742번째 토큰”이 아니라 “주어에서 3칸 뒤”라는 상대 거리입니다. 절대 위치만 주고 상대 거리는 모델이 알아서 파악하길 바라는 셈입니다.
RoPE: 벡터를 돌리다
절대 위치를 더하는 대신, 상대 거리가 어텐션 점수에 바로 반영되게 만들 수는 없을까요? Su et al.(2021)의 RoPE(Rotary Position Embedding)가 이 질문에 답합니다. 위치 벡터를 더하는 게 아니라 Q와 K를 위치만큼 돌립니다.
직관부터 봅시다. 위치 3에 있는 Q는 30도, 위치 7에 있는 K는 70도만큼 돌린다고 합시다. 돌린 뒤에 두 벡터의 내적을 구하면 어떻게 될까요? 내적에는 두 각도의 차이 70 - 30 = 40도만 남습니다. 절대 위치 3과 7은 사라지고 두 토큰 사이의 거리만 어텐션 점수에 들어갑니다.
정확히는 벡터의 차원을 두 개씩 묶어 각 쌍을 서로 다른 속도로 회전시킵니다. 차원 쌍 의 회전 공식은 이렇습니다.
은 토큰 위치, 는 차원 쌍마다 정해진 회전 속도입니다. 사인·코사인 인코딩과 같은 base 10000을 쓰는 건 우연이 아닙니다. 어떤 차원 쌍은 빠르게 돌아서 가까운 토큰을 구분하고, 어떤 차원 쌍은 느리게 돌아서 먼 토큰까지 커버합니다.
K에도 같은 회전을 적용합니다. 위치 의 K는 만큼 돌죠. 핵심은 회전된 Q와 K의 내적이 각도 차이 에만 의존한다는 점입니다. 나침반 바늘 두 개를 각각 다른 만큼 돌리면, 바늘 사이의 각도는 “얼마나 차이 나게 돌렸는지”로만 정해지는 것과 같습니다.
숫자로 확인하기
한 차원 쌍에서 어떤 일이 벌어지는지 따라가 봅시다. 원래 벡터가 이고 라면, 위치 의 벡터는 만큼 회전합니다.
Q가 위치 3, K가 위치 5에 있을 때 (거리 2):
Q가 위치 6, K가 위치 8에 있을 때 (거리 2):
절대 위치는 (3, 5)에서 (6, 8)로 바뀌었지만 내적은 똑같이 0.94입니다. 두 경우 모두 , 상대 거리 2에 해당하는 값이죠. 내적이 절대 위치가 아니라 상대 거리에만 의존한다는 것을 숫자로 확인할 수 있습니다.
V에는 회전을 적용하지 않습니다. 위치 정보는 어텐션 가중치를 거쳐서만 전달됩니다. K를 회전시킨 채 저장하면 KV Cache(추론 때 이전 토큰의 K, V를 재활용하는 저장소)와도 양립하죠. 이전 토큰의 K를 다시 계산할 필요가 없습니다.
LLaMA(2023) 이후 Mistral, Qwen, Gemma, DeepSeek까지 주요 오픈 모델은 사실상 전부 RoPE를 씁니다.
더하기와 돌리기
두 방식의 차이를 정리하면 이렇습니다.
| Positional Encoding (더하기) | RoPE (돌리기) | |
|---|---|---|
| 적용 대상 | 토큰 임베딩 전체 | Q와 K만 |
| 위치 정보의 종류 | 절대 위치 (“742번째”) | 상대 거리 (“3칸 앞”) |
| V에 미치는 영향 | V에도 위치 정보가 섞임 | V는 순수 의미만 보존 |
| 학습 범위 밖 | 성능 급락 | 깨지지만 차원별 보간으로 복구 가능 |
가장 큰 차이는 위치 정보가 들어가는 지점입니다. Positional Encoding은 입력 단계에서 임베딩에 위치를 섞습니다. 위치 정보가 모든 하위 레이어로 퍼지고 V에도 들어가죠. 모델이 절대 위치와 의미 정보를 스스로 분리해야 합니다.
RoPE는 어텐션 점수를 계산하는 순간에만 위치가 개입합니다. Q와 K의 각도를 틀어서 “얼마나 떨어져 있는가”를 내적에 새기고, V는 건드리지 않습니다. 위치 채널과 의미 채널이 구조적으로 분리되는 셈입니다.
학습 범위 밖에서 깨지는 이유
RoPE가 표준이 됐지만 학습 길이를 넘으면 역시 깨집니다. 원인은 차원 쌍마다 회전 속도가 다르다는 바로 그 구조에 있습니다.
빠르게 도는 차원 쌍은 학습 구간 안에서 여러 바퀴를 돕니다. 학습 범위를 넘어도 이미 본 각도가 반복될 뿐이라 괜찮습니다. 문제는 느리게 도는 차원 쌍입니다. 학습 내내 한 바퀴도 못 도는 차원은 위치보다는 의미 정보를 담는 채널처럼 동작하게 됩니다. 학습 범위를 넘는 순간 이 차원이 한 번도 본 적 없는 각도에 놓이면서 어텐션 점수가 불안정해집니다.
이 문제를 해결하는 대표적인 기법이 두 가지 있습니다.
YaRN(Peng et al., 2023)은 빠르게 도는 차원은 그대로 두고 느리게 도는 차원만 골라서 회전 간격을 보간합니다. 가까운 토큰을 구분하는 능력은 유지하면서 먼 토큰에서의 깨짐만 고치는 방식입니다.
p-RoPE는 더 과감합니다. 느리게 도는 차원의 회전을 아예 제거합니다. Gemma 4는 글로벌 어텐션 레이어에서 가장 느린 75%의 차원 쌍을 잘라내고 나머지 25%만 회전시킵니다(partial_rotary_factor: 0.25). 잘려나간 차원은 회전 없이 그대로 통과하면서 순수한 의미 채널로 돌아갑니다. 조정이 아니라 삭제입니다.
128K, 1M 토큰의 긴 문맥이 가능해진 것은 이런 확장 기법 덕입니다.
마치며
어텐션이 순서를 모른다는 문제에서 출발해 두 가지 해법을 살펴봤습니다. Positional Encoding은 위치 벡터를 임베딩에 더해서 절대 좌표를 알려주고, RoPE는 Q와 K를 회전시켜 상대 거리를 내적에 새깁니다. RoPE도 학습 범위 밖에서는 깨지니 어떤 차원을 돌리고 어떤 차원은 빼는 선택이 이어지고 있고, 위치 인코딩은 아직 풀린 문제가 아닙니다.
함께 보면 좋은 글
- LLM은 왜 다음 토큰 하나만 예측할까
- Transformer는 왜 Q, K, V 셋으로 나눴을까
- 토큰은 왜 단어가 아닐까
- LLM은 왜 가장 확률 높은 토큰을 고르지 않을까
- Gemma 4 아키텍처 총정리
참고자료
- Attention Is All You Need (Vaswani et al., NeurIPS 2017)
- RoFormer: Enhanced Transformer with Rotary Position Embedding (Su et al., 2021)
- BERT: Pre-training of Deep Bidirectional Transformers (Devlin et al., 2018)
- YaRN: Efficient Context Window Extension of Large Language Models (Peng et al., 2023)
- Round and Round We Go! What makes Rotary Positional Encodings useful? (2024)
- Gemma 4 Model Card (Google AI for Developers)