제가 학습한 것을 제 방식대로 이해한 내용을 기술합니다. 현대 자연어 생성 이론에서 굉장히 중요한 부분을 다루는 만큼 나름의 검수를 거쳤습니다만, 오역과 정보의 왜곡이 존재할 수 있음을 알립니다. 개선 사항에 대한 댓글과 문의를 환영합니다.
과거 번역이나 chatbot 답변 생성 등의 작업을 수행하는 Seq2Seq(sequence-to-sequence)에서 가장 널리 쓰이던 아키텍처는 순환 신경망 계열 유닛(Recurrent Unit)을 활용한 Encoder-Decoder 구조였다. Encoder는 개별 문장의 문맥 정보를 담은 Context Vector(보통 Encoder의 마지막 hidden state)를 출력해서 Decoder로 넘겨주고, Decoder는 해당 문장의 문맥을 참고하여 새로운 문장을 생성한다. 그러나 이 구조에는 몇 가지 한계가 존재한다. 우선 Encoder가 입력받은 문장의 모든 맥락 정보를 고정된 크기의 Context Vector에 압축하게 되면, 기존 문장 내 정보들이 온전히 보존되지 않을 가능성이 있다(Bottleneck Problem). 또한 특정 단어를 번역할 차례에서 해당 단어를 번역하는데 실질적으로 도움이 되는 원문 단어들과 그렇지 않은 단어들을 알아내야 하는데, 기존 Seq2Seq 구조의 Decoder는 이러한 작업을 수행할 수 없다.
이러한 현상들은 Decoder의 매 time step에서 단 하나의 Context Vector를 참조하기 때문에 발생한다. 매번 forward passing하면서 상이한 hidden state가 입력되긴 하지만, 이들은 모두 하나의 Context Vector의 확장판에 불과하다.
"나는 어제 가족들과 맛있는 스테이크를 먹었다."라는 문장을 영어로 번역한다고 가정하자. 영어 단어 'ate'를 생성할 때, 입력 문장 뒷부분에 있는 '먹었다'를 가장 주의 깊게 봐야할 것이다. 'yesterday'라는 단어를 생성할 때는 입력 문장 앞부분에 있는 '어제'와 연결되어야 할 것이다. 그러나 기본적인 Decoder는 '나', '어제', '가족', '맛있다', '스테이크', '먹었다' 등 문장의 모든 정보가 섞여 압축된 Context Vector만을 참고하기 때문에, 'ate'나 'yesterday' 등 특정 단어를 생성하려는 그 순간에 필요한 정보를 집중해서 들여다보기 어렵다. 이러한 현상은 입력 문장이 길어질수록 심화될 것이다.
이러한 한계점들을 극복하고, 모델이 마치 사람처럼 입력 문장의 특정 부분에 주목하여 작업 성능을 향상시킬 수 있도록 고안된 것이 Attention Mechanism이다. Attention은 Decoder가 매 time step에서 출력을 생성할 때, Encoder의 전체 입력 문장 중에서 현재의 예측과 가장 관련성이 높은 부분에 더 높은 가중치를 부여한다. 가중치 정보를 입력받은 Recurrent Unit은 해당 정보들을 보다 집중적으로 반영하여 결과를 출력할 수 있게 한다. 이를 통해 Bottleneck Problem을 해소하고, 입력 문장의 길이에 관계없이 유연하고 효과적으로 정보를 선택하고 활용할 수 있게 되었다.
본 포스팅은 Attention Mechanism이 Seq2Seq 구조에 어떠한 형태로 녹아들었는지, 상기 일련의 과정이 구체적으로 어떻게 일어나는지, 이러한 Attention Seq2Seq 구조가 지니는 한계에는 어떠한 것들이 있는지 살펴본다.
Concept
Attention Mechanism은 출력 문장의 각 단어를 예측할 때마다, 입력 문장을 구성하는 단어들이 예측에 기여하는 정도가 상이하다는 개념에서 출발한다. "나는 당신을 사랑합니다."라는 문장을 "I love you."로 번역할 때 단어 'I'를 생성할 때는 '나는'을 많이 참조하겠지만, 이 '나는'은 'love'라는 단어를 생성할 때는 비교적 중요하지 않은 단어일 것이다. 이러한 개념을 구현하려면 하나의 Context Vector로 모든 단어들을 예측해야 했던 기존 방식과 달리, 입력 문장과 현재 생성하려는 단어 간 개별적 맥락을 고려한 Context Vector를 Decoder의 매 time step마다 새로이 적용해야 한다.
Query, Key, and Value
앞서 말한 대로 모든 Context Vector는 특정 예측에서 입력 문장 중 어떤 부분들이 중요한지 담고 있어야 한다. 그렇다면 Decoder는 어떻게 입력 문장의 특정 부분에 집중(Attention)할 수 있을까? Attention Mechanism은 세 가지 정보 간 연산을 통해 단어 별 중요도, 즉 연관성을 담은 Vector를 만들어 낸다. 일반적으로 이들은 각각 Query, Key, Value라고 불린다.
- Query: Decoder의 특정 time step의 hidden state($H_t$). "나는 지금 이러한 문맥 상태인데, 현 시점 입력 문장에서 필요한 정보들은 무엇인가?"라고 질문하는 주체.
- Key: Encoder의 모든 time step의 hidden states. Query는 Key에게 "$H_t$와 입력 문장 각 단어들 간 연관성 얼마인가?"라고 질문한다.
- Value: Encoder의 모든 time step의 hidden states. Key와 짝을 이루는 실제 "정보 값"으로, Key를 통해 연관성, 즉 유사도를 찾았다면 Value는 그 유사도에 따라 가져올 실제 정보에 해당한다(Key와 Value의 소스는 동일하게 Encoder의 모든 $H$지만, 개념적으로 다른 역할을 수행한다).
Computation: w/ Dot-Product Attention
이제 Query, Key, Value를 활용한 Attention Mechanism의 구체적인 연산 과정을 살펴보자. 여러 Attention Scoring 방식 중, 비교적 직관적이고 널리 알려진 Dot-Product Attention을 기준으로 설명한다.
Decoder가 매 time step $t$마다 출력 단어를 생성하는 과정은 다음과 같은 단계로 이루어진다.
- $Q$, $K$, $V$ 행렬 준비
먼저 Attention 연산에 필요한 세 가지 주요 행렬을 준비한다.
- Query 행렬 ($Q$): Decoder의 각 time step별 hidden states를 행으로 쌓은 행렬.
- Key 행렬 ($K$): Encoder의 모든 time step의 hidden states를 행으로 쌓은 행렬.
- Value 행렬 ($V$): $K$와 마찬가지로 Encoder의 모든 time step의 hidden states를 행으로 쌓은 행렬.
- Attention Score 계산
모든 Query 벡터($Q$)와 모든 Key 벡터($K$) 간의 유사도를 행렬 곱으로 계산하여 Attention Score 행렬을 얻는다. 이 행렬의 각 행은 하나의 특정 Decoder $H_t$가 Encoder의 모든 hidden states와 얼마나 유사한지를 나타내는 점수들의 집합과 같다.
$$\text{Scores} = QK^T$$
- Attention Weight 분포 계산
계산된 점수 행렬 $\text{Scores}$에 Softmax 함수를 적용하여, 합이 1이 되는 확률 분포 형태의 $\text{Attention Weight}$ 행렬을 생성한다. 이 행렬의 각 행은 하나의 Query가 모든 Key에 대해 각각 얼마나 '집중'해야 하는지를 나타내는 비율 분포가 된다.
$$\text{AttentionWeights} = \text{softmax}(\text{Scores})$$
- 최종 Context Vector 행렬 계산
계산된 $\text{AttentionWeights}$ 행렬과 $V$ 행렬을 곱하여, 최종적인 문맥 정보가 담긴 $\text{Output}$ 행렬을 계산한다.
$$\text{Output} = \text{AttentionWeights} \cdot V$$
이렇게 생성된 $\text{Output}$행렬의 각 행은 고정된 값에서 파생된 것이 아닌, Decoder의 매 time step마다 새롭게 계산된 값을 가진 동적인 벡터이다. 이 벡터에는 현재 예측에 가장 중요하다고 판단된 입력 문장의 정보가 Weighted Sum의 형태로 담겨 있다.
- 다음 단어 예측
마지막으로, Decoder는 각 타임 스텝 $t$마다, 위에서 계산된 $\text{Output}$ 행렬의 $t$번째 행(즉, time step $t$에서의 Context Vector)과 자신의 hidden state $H_t$를 결합하여 최종적으로 다음에 올 단어의 확률 분포를 예측한다. 이러한 일련의 과정을 통해 Attention Mechanism은 매 순간 필요한 정보에 '주목'하여 Bottleneck Problem를 해결하고, 보다 정확하고 문맥에 맞는 결과를 생성할 수 있게 된다.
Limitations of Attention Seq2Seq
Attention Mechanism은 Decoder의 매 time step에서 필요한 정보에만 집중하여 예측하는 방법을 통해 분명 Seq2Seq 모델의 성능을 획기적으로 끌어올렸다. 그러나 해당 모델 역시 그 근간은 Recurrent Unit에 두고 있다는 점에서, 태생적인 한계를 완전히 벗어나지는 못했다.
- 순차적 계산의 한계와 병렬 처리의 어려움
가장 본질적인 한계이다. Attention을 사용하더라도, Encoder와 Decoder는 여전히 RNN(또는 LSTM, GRU)으로 구성되어 있다. RNN은 $H_{t-1}$을 $H_t$를 계산하는 데 사용하는 sequential한 구조를 가진다. 이러한 순차성은 단어의 순서 정보를 자연스럽게 처리하게 해주지만, 병렬 처리(parallelization)를 불가능하게 만든다. 즉, 특정 단어를 처리하기 위해서는 그 전까지의 모든 단어들의 계산이 반드시 끝나야 한다. 이는 대량의 데이터를 고성능 하드웨어(GPU, TPU)로 한 번에 처리하는 Deep Learning의 장점을 온전히 활용하지 못하게 만들며, 결과적으로 긴 문장에 대한 학습 속도를 저해하는 주된 요인이 되었다.
- 여전히 남아있는 정보 전달의 경로 문제
Attention은 Decoder가 Encoder의 모든 부분에 직접 접근할 수 있는 지름길를 제공했다. 하지만 이는 Encoder와 Decoder 사이에만 해당될 뿐, Encoder, Decoder 각각의 내부에서 정보가 전달되는 경로는 여전히 순차적이고 길다. Encoder가 긴 문장을 처리할 때 문장 맨 앞의 단어 정보가 맨 뒤의 단어 정보와 상호작용하려면, 그 사이의 모든 time step을 거쳐야만 한다. 이 긴 경로를 따라 정보가 전달되면서 희석되거나 왜곡될 가능성은 여전히 남아있다.
"그렇다면 이 순차적인 RNN 구조 자체를 아예 없애고, 오직 Attention만으로 모든 것을 처리할 수는 없을까...?"