-
LLM 트랜스포머 동작 흐름 (1) EncodingMachine Learning 2025. 5. 18. 00:22
개요
- 토크나이징 및 임베딩
- 셀프 어텐션
- 멀티 헤드 어텐션
- Positional Encoding
- Residual
- FFN
1. 토크나이징 및 임베딩
자연어가 입력되면 가장 먼저 토큰 단위로 쪼갠다. 이 과정을 토크나이징이라고 한다.
각 토큰은 고유 ID를 가지게 된다.
언어별로 다르지만 영어는 보통 1단어 당 1~2토큰으로 표현되고,
한글은 1단어가 더 많은 토큰으로 쪼개지는 경우가 많다.
쪼개진 토큰은 임베딩 벡터로 변환된다.
비슷한 의미의 토큰이 비슷한 벡터로 변환되는데, 이는 임베딩 모델을 통해서 이뤄진다.
임베딩 모델은 king - queen, man - woman의 차이가 유사하게,
king - man , queen - woman 의 벡터가 유사하도록 학습된다.

토큰 ID를 가지고 임베딩 벡터로 변환하기 때문에 호환이 안 되는 토크나이저 - 임베딩 모델을 사용하면 같은 단어라도 전혀 다른 벡터를 가져오는 문제가 발생할 수 있다. 그래서 실제로 HuggingFace, OpenAI, Google 등은 모델과 토크나이저를 항상 함께 배포한다.
- 자연어 입력
예: "나는 사과를 좋아해" - 토크나이징
→ 서브워드 단위로 분해
→ 예: ["나", "##는", "사과", "##를", "좋아", "##해"] - 토큰 ID로 변환
→ 각 토큰을 사전(Vocabulary)에 따라 숫자로 치환
→ 예: [2034, 3012, 1893, 2011, 9021, 3005] - 임베딩 벡터로 변환
→ 토큰 ID를 임베딩 행렬에서 대응하는 벡터로 바꿈
→ 예: [768차원 벡터] × 6개 - 트랜스포머 인풋으로 사용
→ 이 임베딩 벡터들이 LLM의 입력이 됨
2. 셀프 어텐션
셀프 어텐션은 기존 RNN의 구조적 한계를 해결하기 위해서 등장했다.
- 입력을 순차적으로 하나씩 처리해야 하는 문제
- 긴 문장에서 장기 의존 관계를 처리하기 어려움
- 앞쪽의 맥락이 뒤쪽으로 전달이 어려움
- 그래서 LSTM/GRU 가 등장했지만 여전히 순차적으로 처리해야 하는 문제가 있음
셀프 어텐션은 다른 모든 단어-문맥-을 고려하면서 연산을 한 번에 처리해주는 모델이다.
I ate an apple and an orange.
I visited Apple and Google.
두 문장에서 첫 번째 문장의 apple 먹는 사과, 두 번째 문장의 apple을 회사명으로 맥락을 고려해서 벡터를 보정해준다.연산 과정은 다음과 같다.
$X_t$ : 입력 토큰 [ 1 × 768 ]
$Q_t$ = $X_t$ x $W_Q$ // [1 × 768] * ( 768 × 768 ) = [1 × 768]
$K_t$ = $X_t$ x $W_K$ // [1 × 768] * ( 768 × 768 ) = [1 × 768]
$V_t$ = $X_t$ x $W_V$ // [1 × 768] * ( 768 × 768 ) = [1 × 768]
각 토큰의 임베딩 벡터마다 Q, K, V를 계산한다.
W 가중치는 문맥을 잘 파악하도록 이미 학습된 가중치들이다.
각 토큰의 임베딩 벡터의 맥락을 고려한 보정된 벡터는 다음과 같이 구한다.
- Q와 K 로 유사도 계산
Q는 Query의 의미로 “누가 나한테 중요한가?”를 판단하는 도구.
K는 Key의 의미로 “내가 얼마나 중요한 사람인가?”를 표현하는 도구.
그래서 각 토큰 벡터마다 다른 토큰 벡터 사이의 얼마나 관계가 있는지를 전부 계산한다. 여기서 n은 토큰의 개수
Q1, K1간의 유사도, Q1, K2 간의 유사도, … , Q1, Kn 간의 유사도
Q2, K1 간의 유사도, Q2, K2 간의 유사도, … , Q2, Kn 간의 유사도
…
Qn, K1 간의 유사도, Qn, K2 간의 유사도, … , Qn, Kn 간의 유사도
결국 $Q$ x $K^T$ [n × n]의 행렬곱으로 유사도를 표현할 수 있다. 그리고 각 행마다 다른 토큰과의 유사도 결과 값을 가진다.
Query 입장에서 Key들과의 유사도. 즉 “누구를 참고할지” 스코어
이 스코어를 Softmax 처리→ Softmax {QKᵗ/$\sqrt(d_k)$} = attention weight
여기서 dk는 차원 수. 값이 너무 커지는 걸 방지하기 위함이다.
$output_1 = \sum_{j=1}^nα_jv_j$ = $α_1v_1 + α_2v_2 + … + α_nv_n$
각 행의 유사도 점수를 각 Value 벡터에 가중치를 곱하고 합산한 것이 하나의 최종 결과 벡터가 된다.
이 과정을 모든 토큰 벡터에 대해 수행. (output_2, output_3, … , output_n)

그림에서 $z_1$이 $output_1$ 에 해당.
이 연산이 끝나면 비로소 문맥 전체가 반영된, 그리고 특정 차원으로 투영된 벡터들로 변환이 완료된다.
3. 멀티 헤드 어텐션

어텐션을 수행하면 it이 animal과 관련이 있다는 것을 파악할 수 있다.
하지만 it이 정말 tired와 관련이 적을까? 이 문제를 해결하기 위해 멀티 헤드 어텐션이 등장함.

멀티 헤드 어텐션은 다양한 관점에서 문맥을 더 풍부하게 이해할 수 있게 해준다.
각 토큰 벡터를 512차원으로 투영한다고 하자.
단일 어텐션은 하나의 Q/K/V 를 생성하여 어텐션 수행 후 출력이 ∈ $\R^{n×512}$ 가 된다.
멀티 헤드 어텐션은 가령 h=8, 전체 모델 차원 $d_{model}$ = 512,
→ 각 헤드는 $d_k = d_v = 64$
각 헤드마다 다른 W^Q, W^K, W^V를 통해 8쌍의 Q/K/V 생성
8개의 어텐션 병렬 수행 → 8개의 출력 벡터 $\R^{n×64}$
이 8개의 출력 벡터를 concat으로 이어 붙여서 $\R^{n×512}$를 만든다.
4. Positional Encoding
아무리 Self-attention, multi-head attention 이 도입됐다지만, 입력 문장 내 단어의 순서에도 의미가 담겨 있어서 이를 활용하지 못하는 것은 아까움.
Input embedding 에 순서 정보를 넣자 ⇒ Positional Encoding
- 임베딩 벡터를 “약간” 수정해야 함 -> 많이 수정하면 단어의 의미가 달라짐
- 모든 위치에서 다른 값을 가져야 함
- 가까우면 비슷하게 멀면 다르게
위 조건에 따라 임베딩 벡터에 작은 벡터를 더해주는 방식을 취한다.
“어떻게 작은 벡터를 더해주는가” 는 삼각함수를 활용한다고 한다. 자세한 내용은 생략…
5. Residual
Self-attention 을 잘 훈련시켜서 hidden state 값을 잘 뽑아 내더라도, input embedding 값 그대로를 이후 레이어에 전달하는 것은 긴 맥락을 유지하는 데 도움이 될 것 같다
⇒ 그리고 실제로 도움이 됨 : Residual connection
아래 그림에서 Add & Normalize에 해당한다. x1, x2의 임베딩 벡터를 더해주고 있다.

6. Feed Foward
셀프 어텐션 후 문맥이 반영된 벡터에 대해 각 토큰 벡터 하나 하나에 독립적으로 적용.
$FFN(x) = W_2⋅ReLU(W_1x + b_1)+b_2$
x가 셀프 어텐션을 거친 각 토큰 벡터
- 입력 차원: $d_{model}$ (예: 768)
- 중간 차원: $d_{ff}$=4×$d_{model}$(예: 3072)
- 다시 $d_{model}$로 축소
왜 이렇게 하냐?
- 중간에 차원을 키워서 비선형적인 표현력을 더 크게 확보한다.
- 다시 축소해서 원래 모델의 차원에 맞춰준다.
→ 어텐션으로 "단어 간 관계"를 반영한 뒤,
FFN은 각 단어의 의미를 더 깊게 생각하고 정제하는 "개별 사고 과정"을 거친다고 보면 된다고 한다.
여기까지 이해하면 인코딩은 끝 !
디코딩은 다음 포스팅에서 !

참고)
'Machine Learning' 카테고리의 다른 글
LLM 트랜스포머 동작 흐름 (2) Decoding (0) 2025.06.20 Back Propagation 오차역전법 (0) 2024.06.08