ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • LLM 트랜스포머 동작 흐름 (1) Encoding
    Machine Learning 2025. 5. 18. 00:22

    개요

    1. 토크나이징 및 임베딩
    2. 셀프 어텐션
    3. 멀티 헤드 어텐션
    4. Positional Encoding
    5. Residual
    6. FFN

    1. 토크나이징 및 임베딩

    자연어가 입력되면 가장 먼저 토큰 단위로 쪼갠다. 이 과정을 토크나이징이라고 한다.

    각 토큰은 고유 ID를 가지게 된다.

    언어별로 다르지만 영어는 보통 1단어 당 1~2토큰으로 표현되고,

    한글은 1단어가 더 많은 토큰으로 쪼개지는 경우가 많다.

    쪼개진 토큰은 임베딩 벡터로 변환된다.

    비슷한 의미의 토큰이 비슷한 벡터로 변환되는데, 이는 임베딩 모델을 통해서 이뤄진다.

    임베딩 모델은 king - queen, man - woman의 차이가 유사하게,

    king - man , queen - woman 의 벡터가 유사하도록 학습된다.

     

    토큰 ID를 가지고 임베딩 벡터로 변환하기 때문에 호환이 안 되는 토크나이저 - 임베딩 모델을 사용하면 같은 단어라도 전혀 다른 벡터를 가져오는 문제가 발생할 수 있다. 그래서 실제로 HuggingFace, OpenAI, Google 등은 모델과 토크나이저를 항상 함께 배포한다.

    1. 자연어 입력
      예: "나는 사과를 좋아해"
    2. 토크나이징
      → 서브워드 단위로 분해
      → 예: ["나", "##는", "사과", "##를", "좋아", "##해"]
    3. 토큰 ID로 변환
      → 각 토큰을 사전(Vocabulary)에 따라 숫자로 치환
      → 예: [2034, 3012, 1893, 2011, 9021, 3005]
    4. 임베딩 벡터로 변환
      → 토큰 ID를 임베딩 행렬에서 대응하는 벡터로 바꿈
      → 예: [768차원 벡터] × 6개
    5. 트랜스포머 인풋으로 사용
      → 이 임베딩 벡터들이 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α_j​v_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

    1. 임베딩 벡터를 “약간” 수정해야 함 -> 많이 수정하면 단어의 의미가 달라짐
    2. 모든 위치에서 다른 값을 가져야 함
    3. 가까우면 비슷하게 멀면 다르게

    위 조건에 따라 임베딩 벡터에 작은 벡터를 더해주는 방식을 취한다.

    “어떻게 작은 벡터를 더해주는가” 는 삼각함수를 활용한다고 한다. 자세한 내용은 생략…

    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은 각 단어의 의미를 더 깊게 생각하고 정제하는 "개별 사고 과정"을 거친다고 보면 된다고 한다.


    여기까지 이해하면 인코딩은 끝 !

    디코딩은 다음 포스팅에서 !

    참고)

    https://jalammar.github.io/illustrated-transformer/

    https://skyil.tistory.com/256

    'Machine Learning' 카테고리의 다른 글

    LLM 트랜스포머 동작 흐름 (2) Decoding  (0) 2025.06.20
    Back Propagation 오차역전법  (0) 2024.06.08
Designed by Tistory.