티스토리 뷰

[Transformer의 출현 배경]

RNN의 구조

기존의 순환 신경망(RNN)은 입력 및 출력 시퀀스를 시간 단계(time step)에 따라 순차적으로 처리한다. 이러한 특성 때문에 모든 시간 단계의 출력을 동시에 병렬로 계산할 수 없고, 반드시 이전 단계가 계산된 후에야 다음 단계의 연산이 가능하다. 이로 인해 계산 속도가 느려질 수 밖에 없다.

 

또한, 긴 시퀀스를 처리하기 위해서는 각 시간 단계의 중간 상태(hidden state)와 그래디언트(gradient)를 모두 저장해야한다. 이는 순환 모델의 구조적 특성 때문인데, RNN은 현재 시간 단계의 출력을 계산할 때, 반드시 이전 시간 단계의 hidden state를 입력으로 받아야 한다. 즉, 각 시간 단계의 출력이 이전 단계의 상태에 의존하는 구조이기 때문에, 모든 시간 단계에서의 hidden state와 gradient를 저장해두어야만 역전파가 가능하다. 시퀀스가 길어질수록 메모리에 저장해야하는 hidden state와 gradient가 기하급수적으로 늘어나기 때문에 메모리 사용량이 급격히 증가하게 된다. 이와 같은 이유로 하나의 시퀀스 길이가 길어지면 개별 샘플이 차지하는 메모리 용량이 커지게 되어, 학습 시 한 번에 처리할 수 있는 샘플 수(batch size)를 줄일 수 밖에 없다. 배치 크기가 작아지면 훈련 속도가 느려지고 계산 효율이 떨어지며, 결과적으로 모델이 안정적으로 수렴하는데 더 많은 시간이 소요된다.

 

이러한 한계를 극복하기 위해, 2014년 Bahdanau et al.은 논문 "Neural Machine Translation by Jointly Learning to Align and Translate”에서 Attention Mechanism을 제안하였다. 이 메커니즘은 기존 RNN 기반 Sequence-to-Sequence(Seq2Seq) 모델을 보완하기 위한 보조 구조로 도입되었으며, 입력 시퀀스에서 중요한 정보에 가중치를 두어 보다 효과적인 번역이 가능하도록 하였다. 다만, 해당 모델은 여전히 순환 구조를 기반으로 하고 있었다. 이후 등장한 Transformer는 이러한 순환 구조 자체를 제거하고, 전적으로 Self-Attention 메커니즘에 기반한 완전히 새로운 구조를 제안하였다.

 

[Transformer의 동작 원리]

 

논문에서는 Transformer를 구성하는 구조적 특성을 크게  Encoder-Decoder 구조, Attention mechanism, Position-wise Feed-Forward Networks, Embedding and Softmax, Positional Encoding으로 나눠서 설명하고 있다. 이 개념들을 이해하기 위해서는 각각 별도의 포스팅으로 다루는 것이 적절하겠지만, 여기서는 전체 구조를 간략히 살펴보려고 한다.

 

먼저, 아래 그림은 Transformer의 기본적인 Encoder-Decoder 구조를 보여준다.

Transformer의 Model Architecture

 

실제 Transformer 모델은 Encoder와 Decoder가 각각 6개의 동일한 구조의 반복(stack)으로 구성되어 있으며, 그 과정을 시각적으로 이해하기 위해 전체 구조를 아래와 같이 그려볼 수 있다.

전체 Transformer의 구조

 

위 그림을 보면, Encoder의 각 블록이 순차적으로 쌓여 있으며, Decoder 역시 동일한 방식으로 구성되어 있다. 여기서 주목할 점은 Encoder와 Decoder가 1:1로 직접 연결되지 않는다는 점이다. 즉, n번째 Encoder의 출력이 n번째 Decoder로 그대로 전달되는 것이 아니라, Encoder의 마지막 블록 출력이 Decoder의 모든 블록에 동일하게 전달된다는 것이다. 

 

이렇게 모든 Decoder 블록에 Encoder의 출력을 전달하는 이유는, Decoder 블록이 출력을 생성하는 과정에서 Encoder로부터 전달 받은 입력 문장 정보를 각 단계에서 반복적으로 참조하여 문맥을 정교화해나가기 위함이다. 이를 풀어서 설명하면 Decoder의 각 블록은 이전 Decoder 블록의 출력을 입력으로 받아 정보를 가공한다. 동시에 Encoder의 출력을 함께 참조함으로써 출력 문장을 점진적으로 다듬고 정제한다. 즉, 각 Decoder 블록들은 이전 단계의 정보를 누적적으로 받아들이며 문맥을 점진적으로 확장하는 연속적인 구조를 따른다. 이러한 구조 덕분에 Transformer는 입력 문장의 의미를 깊이 있게 반여하고, 출력 문장을 효율적으로 생성할 수 있다.

 

 

다시 Encoder와 Decoder의 세부 동작과정으로 돌아오면,

Transformer의 Model Architecture

 

Encoder는 두 개의 sub-layer로 구성되어 있고, 각 sub-layer에는 Residual connection과 Layer Normalization이 적용된다.

 

(1) Multi-Head Self-Attention:

  • Encoder의 첫번째 sub-layer로 self-attention을 여러 개의 헤드로 확장하여 서로 다른 가중치 행렬을 통해 다양한 관점에서 단어 간의 관계를 병렬적으로 학습할 수 있도록 한다.

 

(2) Feed-Forward Network:

  • Encoder의 두번째 sub-layer로 Self-Attention 이후 각 단어의 표현을 독립적으로 비선형 변환하여 모델의 표현력을 강화하는 역할을 한다.
  • 이는 Attention 레이어에서 이미 단어 간 상호작용을 통해 문맥 정보가 충분히 통합되었기 때문에, 이후 단계에서는 각 위치에 대한 개별적인 비선형 변환만 수행하면 된다는 설계 철학에 기반한다.

 

 

Decoder는 세 개의 서브 레이어로 이루어져 있으며, 마찬가지로 각 서브 레이어에는 Residual Connection과 Layer Normalization이 적용된다.

 

(1) Masked Multi-Head Self-Attention:

  • 앞으로 올 단어를 보지 않도록 마스킹된 self-attention을 수행한다.
  • 예를 들어 "I love you" 라는 문장이 있을때, Masking을 통해  "I"만 볼 수 있는 연산, "I love"를 볼 수 있는 연산, "I love you"를 볼 수 있는 연산이 동시에 수행된다. 이렇게 함으로써 연산은 병렬로 처리되지만, 마스크 덕분에 출력 시퀀스를 순차적 생성하는 것처럼 동작하게 된다.
  • 학습 시 첫번째 Decoder 블록은 정답 문장을 단어로 임베딩한 output embedding을 입력으로 받아, 마스킹을 적용한 상태로 self-attention 연산을 병렬로 수행한다. 이후 블록들에서는 이전 Decoder의 출력을 입력으로 받아 동일하게 마스킹을 적용한 상태로 연산을 이어간다.
  • 추론 시에는 시작토큰(<sos>)하나만을 임베딩하여 Decoder의 입력으로 사용하고, 생성된 토큰을 순차적으로 누적하여 이후 블록의 입력으로 활용한다. 이 과정은 병렬 처리가 불가능하며 실제 단어가 하나씩 생성되는 방식으로 동작한다.

(2) Encoder-Decoder Cross-Attention:

  • Encoder의 출력과 Decoder의 첫번째 sub-layer의 출력을 입력으로 받아 cross-attention을 수행한다.
  • 여기서 Encoder의 출력은 Key와 Value로 사용하고, Decoder의 출력은 Query로 사용한다.
  • 일반적으로 self-attention은 Query, Key, Value가 모두 동일한 입력에서 생성되지만, 이처럼 Query, Key-Value가 서로 다른 입력(source)에서 나오면 이를 Cross-attention이라고 부른다.
  • 이 과정을 통해 Decoder는 입력시퀀스를 참조하여 더 정밀한 출력을 생성할 수 있다.

 

(3) Feed-Forward Network:

  • Cross-Attention 결과를 입력으로 받아, Encoder와 동일하게 각 위치마다 동일한 feed-forward Network가 적용(각 토큰마다 동일한 FFN이 적용)된다.

 

다음으로 Attention에 대해 살펴보면

 

Attention은 Query와 Key-Value 쌍을 매핑하여 출력을 생성하는 연산을 말한다.

Scaled Dot-Product Attention

Input이 embedding을 통해 주어지면 이를 가지고 Query, Key, Value 벡터를 만들고, Query와 Key를 내적(Dot Product)하여 attention score(중요도)를 계산한다. 

$$Score(Q, K) = QK^T$$

Query, Key, Value가 만들어지는 과정

Attention score를 계산한 뒤에는 Score 값을 Scaling한 뒤 다음에 올 토큰의 확률 값을 예측하는 Softmax를 적용하는데 스케일링 factor를 $\sqrt{d_{k}}$로 하여 나눠준다.

$$Attention Score = sofrmax\left(\frac{QK^{T}}{\sqrt{d_{k}}}\right)$$

여기서 $d_{k}$는 key의 차원(dimension)을 의미하는데, $d_{k}$가 커질 수록($d_{q}$도 마찬가지임) 내적 값이 점점 더 커지게 된다.

$$softmax(x_i) = \frac{e^{x_i}}{\sum_{j}e^{x_j}}$$

Softmax 함수의 입력 값인 $x_{i}$가 너무 크면 $e^{x_{}}$값이 급격히 증가하게 되고 이에 따라 Softmax의 출력 값들이 0 또는 1에 가까운 극단적이 값이 되어 gradient가 매우 작아지고 학습이 어려워지는 문제가 발생한다. 따라서 이를 방지하기 위해 $d_{k}$로 나누어 정규화를 하는 것이다. Softmax함수까지 거치고 나면 각 토큰별로 얼마나 중요한지를 나타내는 확률분포를 얻을 수 있게된다. 이를 Attention weight라고도 한다. 최종적으로 attention weight에 V를 곱하여 최종 output을 계산하게 된다. 중요도가 높은 토큰일 수록 V에 더 큰 영향을 주게 된다.

$$Output = Weight \times V$$

 

 

 

[출처]

RNN의 구조: https://wikidocs.net/22886

Attention Is All You Need: https://proceedings.neurips.cc/paper_files/paper/2017/file/3f5ee243547dee91fbd053c1c4a845aa-Paper.pdf

The Illustrated Transformer by Jay Alammar: https://jalammar.github.io/illustrated-transformer/