Adventure Time - Finn 3
본문 바로가기
AI/ML

Transformer 포지셔널 인코딩

by hyun9_9 2026. 4. 21.

rnn이 자연어 처리에서 유용했던 이유는?

단어의 위치에 따라 단어를 순차적으로 입력 받아서 처리하는 RNN의 특성으로 인해

각 단어의 위치 정보를 가질수있다

하지만 트랜스 포머는 단어 입력을 순차적으로 받는 방식이 아니므로 단어의 위치 정보를 다른 방식으로 알려줄 필요가 있다

트랜스포머는 단어의 위치 정보를 얻기 위해서

각 단어의 임베딩 벡터에 위치 정보들을 더해 모델의 입력으로 사용하는데

이를 포지셔널 인코딩이라고 한다.

위 그림은 입력으로 사용되는 임베딩 벡터들이 트랜스포머의 입력으로 사용되기 전에 포지셔널 인코딩의 값이 더해지는 것을 보여준다.

임베딩 벡터가 인코더의 입력으로 사용되기 전 포지셔널 인코딩 값이 더해지는 과정을 시각화하면 위와 같다

포지셔널 인코딩 값들은 어떤 값이기에 위치정보를 반영해줄수 있을까?

트랜스포머는 위치 정보를 가진 값을 만들기 위해서 아래의 두개의 함수를 사용한다

포지셔널 인코딩(Positional Encoding)이 수학적으로는 복잡해 보이지만, 그 핵심 아이디어를 관통하는 직관은 생각보다 간단합니다. 트랜스포머는 데이터를 한꺼번에 병렬로 처리하기 때문에, "각 단어가 문장 속에서 몇 번째에 있는지"를 전혀 모르는 상태입니다. 그래서 우리는 각 단어에 고유한 '위치 정보(주소)'를 부여해서 더해주기로 한 것이죠.

그런데 왜 하필 사인(sin)과 코사인(cos) 함수를 사용할까요? 그리고 왜 하필 $10000$ 같은 복잡한 숫자를 쓸까요? 이를 이해하기 위한 가장 쉬운 비유를 들어드리겠습니다.

1. 직관적인 이해: "이진수(Binary)의 원리"

우리가 숫자를 셀 때, 이진법을 떠올려 보세요.

숫자 이진수
0 0000
1 0001
2 0010
3 0011
4 0100

보시면, 오른쪽으로 갈수록 값이 훨씬 빠르게 변합니다.

  • 오른쪽 끝 칸은 0, 1, 0, 1... 아주 빠르게 바뀝니다.
  • 왼쪽으로 갈수록 0, 0, 1, 1... 점점 느리게 바뀝니다.

트랜스포머의 포지셔널 인코딩도 정확히 이 원리입니다. "빠르게 변하는 값"부터 "천천히 변하는 값"까지 섞어서 각 단어마다 고유한 패턴(주소)을 만들어내는 것입니다.

2. 사인/코사인 그래프의 역할

이진수처럼 0과 1로 딱 끊어지는 값이 아니라, 연속적인 값을 사용하면 모델이 위치 정보를 더 부드럽게 학습할 수 있습니다. 그래서 사인/코사인 함수를 사용합니다.

위의 그래프처럼, 파동의 주기(주파수)를 다르게 설정하면 다음과 같은 효과가 생깁니다.

  • 주기가 짧은 함수(빠른 파동): 단어 간의 아주 미세한 위치 차이를 구분합니다.
  • 주기가 긴 함수(느린 파동): 문장 전체에서 단어가 앞쪽에 있는지, 뒤쪽에 있는지 큰 흐름을 파악합니다.

이 두 가지를 조합하면, "특정 위치(pos)에서는 모든 차원의 사인/코사인 값이 유니크한 조합"이 됩니다. 즉, 같은 단어라도 위치가 다르면 이 고유한 '파동 패턴'이 더해지기 때문에 벡터값이 완전히 달라지게 됩니다.

3. 왜 사인과 코사인을 섞어서 쓸까요?

수식에서 $2i$와 $2i+1$을 나누어 쓰는 이유는 두 가지 파형을 섞어서 위치 정보를 더 정교하게 만들기 위해서입니다.

  • 사인($2i$)코사인($2i+1$)은 90도만큼 위상 차이가 납니다.
  • 이 두 값을 짝지어 사용하면, 모델은 위치 정보를 상대적인 거리로 인식하기 훨씬 쉬워집니다. 사인과 코사인의 성질 덕분에, 어떤 위치 $pos$와 다른 위치 $pos+k$ 사이의 관계를 행렬 연산만으로 쉽게 계산할 수 있기 때문입니다.

4. 핵심 정리

결국 포지셔널 인코딩은 다음과 같은 과정입니다.

  1. 좌표 생성: 문장의 길이만큼 $0, 1, 2, ...$ 등의 위치 정보($pos$)를 준비합니다.
  2. 파동 조합: 각 위치에 대해 사인/코사인 함수를 사용하여, 각 차원($i$)마다 주파수가 다른 파동값을 계산합니다.
  3. GPS 태그 부착: 이렇게 만들어진 **'위치 좌표 벡터'**를 실제 **'단어 임베딩 벡터'**에 더합니다.

즉, 단어 벡터에 "나는 문장의 3번째에 있어"라는 GPS 정보를 덧씌우는 것이라고 이해하시면 가장 정확합니다. 이 덕분에 트랜스포머는 비록 단어를 동시에 입력받더라도, "이 단어는 3번째에 있는 단어구나"라고 순서를 정확히 인지할 수 있게 됩니다.