AI
트랜스포머 (Transformer) #2
2025. 2. 14. 14:35

트랜스포머 아키택쳐

위 그림은 트랜스포머 모델의 전체 아키텍처를 도식화한 것입니다. 트랜스포머는 인코더-디코더 구조로 이루어져 있으며, 각 구성 요소는 멀티 헤드 어텐션(Multi-Head Attention), 잔차 연결(Add & Norm), 그리고 피드 포워드 네트워크(Feed Forward Network)로 구성됩니다. 이 모델의 핵심 개념은 바로 어텐션 메커니즘(Attention Mechanism)으로, 입력 문장의 각 단어가 문맥 속에서 얼마나 중요한지를 효과적으로 학습할 수 있도록 도와줍니다.

이제 트랜스포머 모델이 어떻게 동작하는지 순서대로 살펴보겠습니다.

 


Input Embedding: 단어를 벡터로 변환하는 과정

트랜스포머(Transformer) 모델에서 입력이 처리되는 첫 번째 단계는 문장을 수치화된 벡터로 변환하는 것입니다. 이 과정은 크게 두 가지 단계로 나뉩니다.

  1. 토큰화(Tokenization)
    문장을 개별 단어(혹은 서브워드 단위)로 나누고, 각 토큰을 고유한 정수 ID로 매핑합니다.
    예를 들어, 문장 "나는 호두를 사랑해" 가 있을 때, 모델이 사전에 학습한 단어 사전(vocabulary)이 다음과 같다면:문장은 [0, 1, 2]와 같은 정수 시퀀스로 변환됩니다.
  2. {"나는": 0, "호두를": 1, "사랑해": 2, ...}
  3. 임베딩(Embedding) 변환
    정수 시퀀스를 벡터로 변환하는 과정입니다. 신경망 모델은 숫자로만 연산할 수 있기 때문에, 각 단어를 고정된 크기의 실수 벡터로 매핑하는 임베딩 레이어(Embedding Layer) 를 사용합니다.예를 들어, 다음과 같은 임베딩 행렬 W가 있다고 가정해 보겠습니다.
    토큰 ID 임베딩 벡터
    0 (나는) [1.0, 0.1, 0.0, ...]
    1 (호두를) [0.7, 0.1, ..., 0.5]
    2 (사랑해) [0.0, 1.0, ..., -0.3]
    그러면 문장 [0, 1, 2] 는 다음과 같이 변환됩니다.여기서 W 행렬의 값은 학습 가능한 파라미터로, 모델이 학습을 통해 최적화합니다. 초기에는 무작위 값이지만, 훈련 데이터를 학습하면서 점점 더 의미 있는 벡터로 변하게 됩니다.
  4. [ [1.0, 0.1, 0.0, ...], # "나는" [0.7, 0.1, ..., 0.5], # "호두를" [0.0, 1.0, ..., -0.3] # "사랑해" ]
  5. 트랜스포머 모델에서는 일반적으로 학습 가능한 임베딩 행렬 W 를 사용하여 변환을 수행합니다. 이 행렬은 (어휘 크기, 임베딩 차원 수) 형태로 구성되며, 문장 내 각 단어의 정수 ID를 look-up하여 해당하는 벡터를 반환합니다.

 


포지셔널 인코딩(Positional Encoding)

트랜스포머 모델은 RNN(Recurrent Neural Networks)과 달리 입력 토큰을 한 번에 병렬 처리합니다. 하지만 이 방식에는 문장 내 단어 순서를 고려할 수 없는 문제가 있습니다. 자연어에서는 "나는 호두를 사랑해""호두를 나는 사랑해"가 의미가 다르지만, 트랜스포머는 단순한 임베딩 벡터만 사용하면 이러한 차이를 인식할 수 없습니다.

이를 해결하기 위해 포지셔널 인코딩(Positional Encoding) 을 추가하여 모델이 각 단어의 위치 정보를 학습할 수 있도록 만듭니다.

 

포지셔널 인코딩의 원리 🧩

포지셔널 인코딩은 각 단어의 위치를 고유한 벡터로 변환하여 임베딩 벡터에 더하는 방식으로 동작합니다.
수식으로 표현하면 다음과 같습니다.

$$
PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{\frac{2i}{d}}}\right)
$$
$$
PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{\frac{2i}{d}}}\right)
$$

여기서:

  • $ pos $는 단어의 위치(Position)
  • $ i $는 벡터 차원의 인덱스
  • $ d $는 임베딩 차원 수

즉, 위치에 따라 사인(Sin)과 코사인(Cosine) 함수를 적용하여 위치 벡터를 생성합니다. 이 방식은 위치가 커질수록 점진적으로 변화하는 패턴을 가지며, 모델이 상대적인 단어 순서를 학습하는 데 유리합니다.

 

포지셔널 인코딩 적용 방식 🏗️

생성된 포지셔널 인코딩 벡터는 각 단어의 임베딩 벡터에 단순히 더하는(Addition) 방식으로 적용됩니다.

$$
Z = X + PE
$$

여기서:

  • $ X $는 임베딩된 입력 벡터
  • $ PE $는 포지셔널 인코딩 벡터
  • $ Z $는 위치 정보가 반영된 최종 입력 벡터

이렇게 하면, 같은 단어라도 문장 내 위치에 따라 다른 벡터를 가지게 되므로 트랜스포머가 단어의 순서를 학습할 수 있습니다.

 

포지셔널 인코딩의 장점 🌟

1️⃣ 순차적 구조 없이도 단어 순서 반영

  • RNN처럼 순차적으로 처리할 필요 없이 병렬 연산이 가능하면서도 단어의 순서를 고려할 수 있습니다.

2️⃣ 상대적 위치 정보 학습 가능

  • 사인과 코사인 함수의 주기성을 활용해, 모델이 단어 간의 상대적 거리도 학습할 수 있습니다.

3️⃣ 추론 시 길이 확장 가능

  • 미리 정의한 최대 길이(max_len) 내에서 새로운 문장에도 쉽게 적용할 수 있습니다.

 


인코더(Encoder): 반복되는 트랜스포머의 핵심 구조

트랜스포머의 인코더는 Nx개의 동일한 블록을 반복적으로 쌓는 구조로 이루어져 있습니다. 각각의 인코더 블록은 멀티-헤드 어텐션(Multi-Head Attention)피드 포워드 네트워크(Feed Forward Network, FFN) 를 포함하며, 각 단계마다 잔차 연결(Add & Norm) 을 수행하여 학습을 안정화합니다.

💡 멀티-헤드 어텐션의 세부 동작 방식은 이전 블로그 글에서 다뤘으므로 여기서는 생략합니다.

인코더 블록의 구성

각 인코더 블록은 다음과 같은 단계를 거쳐 입력을 변환합니다.

1️⃣ 멀티-헤드 어텐션 (Multi-Head Attention)

  • 입력 벡터에서 자기 어텐션(Self-Attention) 을 수행하여 문맥 정보를 반영한 새로운 벡터를 생성합니다.
  • 동일한 입력을 여러 개의 어텐션 헤드(Head)에서 독립적으로 처리한 후, 이를 결합하여 표현력을 높입니다.

2️⃣ Add & Norm (Residual Connection + Layer Normalization)

  • 멀티-헤드 어텐션의 출력을 원래 입력에 더하는 잔차 연결(Residual Connection) 을 수행합니다.
  • 이어서 Layer Normalization (Layer Norm) 을 적용하여 학습 안정성을 높입니다.

잔차 연결이 중요한 이유?
잔차 연결을 사용하면, 학습 초기에 그라디언트가 너무 작아지면서 발생하는 기울기 소실(Vanishing Gradient) 문제를 완화할 수 있습니다.

3️⃣ 피드 포워드 네트워크 (Feed Forward Network, FFN)

  • 어텐션 연산 후 얻은 벡터를 개별적으로 처리하는 두 개의 완전 연결 레이어(Fully Connected Layers) 로 구성됩니다.
  • 일반적으로 ReLU 활성 함수를 사용하여 비선형성을 부여합니다.

$$
FFN(x) = \max(0, xW_1 + b_1) W_2 + b_2
$$

이 단계는 개별 토큰의 표현을 강화하는 역할을 합니다.

4️⃣ Add & Norm (Residual Connection + Layer Normalization)

  • 피드 포워드 네트워크의 출력을 이전 단계 출력에 더하고, 다시 한 번 Layer Normalization을 적용합니다.

 

🔁 Nx번 반복: 깊은 인코더 네트워크 형성

위의 과정은 하나의 인코더 블록에서 이루어지며, 트랜스포머에서는 이 블록을 여러 번(Nx) 반복하여 인코더를 구성합니다.

  • 논문에서는 기본적으로 Nx = 6 (6개의 인코더 블록)을 사용했습니다.
  • 더 깊은 네트워크를 쌓을수록 모델이 더 복잡한 패턴을 학습할 수 있습니다.

 

💡 이 반복 구조의 의미?

  • 인코더 블록이 여러 개 쌓이면서 어텐션과 FFN을 여러 번 거친 결과가 최종 인코더 출력을 형성합니다.
  • 초기 단계에서 학습된 단순한 특징이 고차원의 풍부한 의미 표현으로 발전합니다.
  • 각 인코더 블록이 점점 더 추상적인 표현을 학습하면서 문장의 의미를 정교하게 인코딩합니다.

 


디코더 입력: Output Embedding과 Positional Encoding

1️⃣ 디코더의 입력: Shifted Right Output

디코더는 이전 단어를 기반으로 다음 단어를 예측하는 방식으로 동작합니다.
하지만 첫 번째 단어는 이전 단어가 없기 때문에, 시작을 알리는 <START> 토큰만 입력됩니다.

예제: 정답 문장이 다음과 같다면

["나는", "호두를", "사랑해", "<EOS>"]

디코더의 입력은 한 칸 오른쪽으로 이동(Shift Right) 하며, 첫 번째 입력은 <START> 토큰 하나만 포함됩니다.

🏗️ 디코더 입력 (Shifted Right)

1st step: ["<START>"]  
2nd step: ["<START>", "나는"]  
3rd step: ["<START>", "나는", "호두를"]  
4th step: ["<START>", "나는", "호두를", "사랑해"]  

이처럼 첫 번째 스텝에서는 <START> 토큰만 입력되며,
이후 디코더는 이전까지 생성된 단어들을 입력으로 사용하면서 점진적으로 시퀀스를 확장합니다.

이제 이렇게 변환된 입력을 Embedding + Positional Encoding 과정을 거쳐 벡터로 변환한 후, 디코더 블록으로 전달합니다. 🚀

 

2️⃣ Output Embedding: 정수 ID를 벡터로 변환

디코더의 입력(Shifted Right Output)은 정수 ID로 변환된 상태이므로, 이를 벡터로 변환하기 위해 임베딩 레이어(Embedding Layer) 를 사용합니다.
이 과정은 인코더의 입력 임베딩과 동일하며, 각각의 정수 ID를 고정된 차원의 밀집 벡터(Dense Vector) 로 변환합니다.

 

3️⃣ Positional Encoding 추가

디코더 역시 인코더와 마찬가지로 입력 순서 정보가 포함되어 있지 않으므로 Positional Encoding을 더해줍니다.

  • 이전에 설명한 것처럼, 사인(Sin)과 코사인(Cosine) 함수를 이용하여 포지션 벡터를 생성합니다.
  • 이를 Output Embedding 벡터와 더해(position-wise addition) 디코더가 문장의 순서를 학습할 수 있도록 합니다.

 


디코더 내부: Masked Multi-Head Attention, 인코더-디코더 어텐션

트랜스포머 디코더는 Nx번 반복되는 동일한 구조의 블록으로 구성되어 있으며, 각 블록은 다음과 같은 순서로 연산을 수행합니다.

  1. Masked Multi-Head Attention
  2. Multi-Head Attention (인코더의 출력과 결합됨)
  3. Feed Forward Network

이 과정이 N번 반복되면서 최종적인 출력을 생성하는 과정이 이루어집니다.

 

1️⃣ Masked Multi-Head Attention: 미래 단어를 가려야 하는 이유 🤔

디코더에서 첫 번째로 수행되는 어텐션은 Masked Multi-Head Attention 입니다.
이는 디코딩 과정에서 모델이 아직 생성되지 않은 미래 단어를 참고하지 못하도록 하는 역할을 합니다.

🚨 왜 Masking이 필요할까?

  • 인코더에서는 입력 문장 전체를 한 번에 처리하므로 모든 단어를 서로 참고할 수 있었습니다.
  • 그러나 디코더에서는 왼쪽에서 오른쪽으로 순차적으로 단어를 생성해야 하므로,
    • 현재 단어를 예측할 때 이후 단어를 보지 못하도록 해야 합니다.
  • Masking을 적용하지 않으면, 모델이 정답을 미리 참조하는 데이터 누수(Data Leakage) 가 발생할 수 있습니다.

🏗️ Masked Attention의 동작 방식

일반적인 어텐션은 Query(질의), Key(키), Value(값) 행렬을 사용하여 아래와 같이 계산됩니다.

$$
\text{Attention}(Q, K, V) = \text{softmax} \left( \frac{QK^T}{\sqrt{d_k}} \right) V
$$

그러나 Masked Multi-Head Attention에서는 미래 단어를 가리기 위해 마스크(masking) 행렬을 추가합니다.
즉, 정답 문장이 ["나는", "호두를", "사랑해"] 라면,

현재 단어 참조할 수 있는 단어
"나는" ["나는"]
"호두를" ["나는", "호두를"]
"사랑해" ["나는", "호두를", "사랑해"]

미래 단어를 가리기 위해 softmax 이전에 -∞ 값을 적용하는 마스킹 연산을 추가합니다.
이렇게 하면 어텐션 점수가 계산될 때 마스킹된 부분은 무한대의 작은 값이 되어, softmax를 거친 후 0이 됩니다.

출력 예시:

tensor([[  0., -inf, -inf, -inf, -inf],
        [  0.,   0., -inf, -inf, -inf],
        [  0.,   0.,   0., -inf, -inf],
        [  0.,   0.,   0.,   0., -inf],
        [  0.,   0.,   0.,   0.,   0.]])

이렇게 하면 현재 단어까지만 볼 수 있도록 제한됩니다. ✅

 

2️⃣ Multi-Head Attention (인코더 결과 이용)

디코더의 두 번째 어텐션은 인코더의 출력과 결합하여 동작하는 Multi-Head Attention 입니다.
이전 단계의 Masked Multi-Head Attention과의 차이점은 다음과 같습니다.

어텐션 종류 Query Key & Value
Masked Multi-Head Attention 디코더의 입력 디코더의 입력
Multi-Head Attention (인코더-디코더) 디코더의 입력 인코더의 출력

즉, 현재까지 생성된 디코더의 출력(Query) 를 사용하여 인코더에서 나온 Key-Value 쌍과 어텐션을 수행하는 방식입니다.

🏗️ 이 과정이 중요한 이유

  • 인코더는 전체 입력 문장에서 정보를 추출하여, 문맥적으로 중요한 부분을 벡터로 저장합니다.
  • 디코더는 이 벡터를 활용하여 "이전까지 생성한 단어""입력 문장에서 중요한 정보" 를 결합하여 더 정확한 출력을 만듭니다.

이제 이 어텐션을 거친 정보는 Feed Forward Network 를 통과한 후, 최종적으로 디코더의 출력을 생성하는 과정으로 이어집니다. 🚀

 


디코더 결과 처리: Linear → Softmax → Output Probabilities

트랜스포머 디코더의 마지막 단계는 디코더 블록을 거친 출력을 실제 단어로 변환하는 과정입니다.
이 과정은 크게 3단계로 진행됩니다.

  1. Linear Layer: 디코더 출력을 단어 사전 크기(vocab size)와 동일한 차원의 벡터로 변환
  2. Softmax: 변환된 벡터를 확률 분포로 변환
  3. Output Probabilities: 가장 높은 확률을 가진 단어를 선택하여 최종 출력

1️⃣ Linear Layer: 단어 사전 크기로 변환

디코더 블록을 거친 최종 출력 벡터는 고정된 크기(임베딩 차원 d_model)의 연속적인 벡터입니다.
이 벡터를 그대로 사용할 수는 없으므로, Linear 변환을 적용하여 단어 사전 크기(vocab size)와 동일한 차원으로 변환해야 합니다.

📌 이 과정이 필요한 이유

  • 디코더의 출력은 d_model 차원의 벡터
  • 하지만 모델이 예측할 단어는 단어 사전 크기(vocab size) 중 하나여야 함
  • 따라서 가중치 행렬 $ W $ 를 곱해 vocab_size 차원의 벡터로 변환

수식으로 표현하면:

$$
Y = X W + b
$$

  • $ X $: 디코더의 최종 출력 ($d_{model}$ 차원)
  • $ W $: 학습 가능한 가중치 행렬 ($d_{model} \times $vocab size)
  • $ b $: 편향 벡터
  • $ Y $: 변환된 벡터 (vocab size 차원)

2️⃣ Softmax: 확률 분포로 변환

Linear 레이어를 통과한 출력 벡터는 단어 사전 크기와 동일한 차원을 가지는 숫자 벡터입니다.
이제 Softmax를 적용하여 확률 분포(probability distribution)로 변환합니다.

Softmax 공식

$$
P(y_i) = \frac{e^{y_i}}{\sum_{j} e^{y_j}}
$$
여기서:

  • $ P(y_i) $는 단어 $ y_i $ 가 정답일 확률
  • $ y_i $ 는 Linear 레이어를 통과한 벡터 값
  • 모든 단어의 확률 합이 1이 되도록 정규화

📌 Softmax를 적용하면?

  • 모델이 예측한 벡터가 각 단어에 대한 확률 값으로 변환됨
  • 가장 높은 확률을 가진 단어를 선택하면 최종 출력이 됨

예를 들어, 단어 사전이 ["나는", "호두를", "사랑해"] 세 개만 있다고 가정했을 때:

Linear 출력:  [3.2, 1.8, 2.5]  (가중치 연산 결과)
Softmax 출력: [0.65, 0.12, 0.23]  (확률로 변환됨)

👉 가장 확률이 높은 "나는" (0.65) 가 선택됨 ✅

 

3️⃣ Output Probabilities: 최종 단어 선택

Softmax를 적용한 후, 가장 높은 확률을 가진 단어를 최종 출력으로 선택합니다.

  • Greedy Decoding: 확률이 가장 높은 단어를 바로 선택
  • Beam Search: 여러 후보를 고려하면서 더 나은 문장을 탐색

📌 이 과정에서 중요한 점

  • Softmax 확률이 가장 높은 단어를 선택하는 방식으로 문장이 점차 생성됨
  • 반복적으로 새로운 단어를 예측하면서 최종 문장을 완성함

'AI' 카테고리의 다른 글

vLLM의 핵심 이해: PagedAttention  (0) 2025.03.25
vLLM 이란?  (0) 2025.03.25
트랜스포머 (Transformer) #3  (0) 2025.03.24
트랜스포머 (Transformer) #1  (0) 2025.02.13
LangChain 이란 무엇인가?  (2) 2024.09.20