Seq2Seq에서 Transformer까지: 문맥 누적에서 직접 참조로 · 2편

Attention Seq2Seq - 마지막 상태만 보지 말고 직접 보자

Vanilla Seq2Seq의 병목을 어떻게 해결할까? Decoder가 Encoder의 모든 hidden state를 "필요에 따라" 직접 참고하는 방식. 이것이 Attention 메커니즘이고, 현대 딥러닝의 기초가 됩니다.

1편에서는 하나의 문맥 벡터(Context Vector)에 모든 정보를 구겨 넣다가 앞쪽 정보가 흐려지는 Vanilla Seq2Seq의 한계를 알아보았습니다.

이 답답한 병목 현상을 깨부수기 위해 딥러닝 고수들이 던진 질문은 단순했습니다. “디코더가 단어를 예측할 때 마다, 인코더의 원문을 처음부터 끝까지 다시 직접 들여다보게 하면 어떨까?” 이 상상을 현실로 만든 것이 바로 Attention(어텐션) 메커니즘입니다.


인코더의 모든 은닉 상태를 개방하다

바닐라 구조에서는 디코더가 인코더의 맨 마막 은닉 상태($h_3$)만 받아 쓸 수 있었습니다. 원문을 다시 볼 수 없는 가혹한 환경이었죠.

하지만 Attention 구조로 넘어오면서 디코더는 인코더 만들어낸 모든 은닉 상태($h_1, h_2, h_3$)를 통째로 넘겨받습니다. 이제 디코더는 매 순간 단어를 만들 때마다 인코더의 어떤 상태를 더 많이 참고할지 스스로 결정할 수 있는 선택권을 가집니다.


가중치($w$)의 계산: “어디를 얼마나 볼 것인가?”

디코더가 인코더의 모든 상태를 볼 수 있다면, 현재 예측하려는 단어와 가장 관련이 깊은 곳에 ‘집중(Attention)’해야 합니다. 이때 얼마나 집중할지를 결정하는 비율이 바로 가중치($w$)입니다.

새로운 문맥 벡터 $c_k$는 인코더의 상태들에 이 가중치를 곱해서 합한 값이 됩니다.

\[c_k = w_{k1} h_1 + w_{k2} h_2 + w_{k3} h_3\]

여기서 가중치 $w$는 고정된 값이 아닙니다. 디코더의 현재 상 $S_k$와 각 인코더 상태 $h_i$가 얼마나 연관되어 있는지 매번 계산하여 동적으로 바뀝니다.

\[w_{ki} = \text{softmax}(\text{score}(S_k, h_i))\]

가장 직관적고 널리 쓰이는 Dot-Product Attention 방식을 예로 들면, 두 벡터를 내적($\langle \cdot, \cdot \rangle$)하여 점수를 냅니다. 두 벡터가 가리키는 방향이 유사할수록 점수가 높아집니다.

\[\text{score}(S_k, h_i) = \langle S_k, h_i \rangle\]

직관적 이해: Query, Key, 그리고 Value

이 과정을 이해하는 가장 좋은 방법은 훗날 Transformer의 핵심이 되는 Query(질문), Key(검색 대상), Value(값)의 관계로 치환해 보는 것입니다.

  • Query ($S_k$): 디코더가 던지는 질문 (“나 지금 다음 단어 만들어야 하는데, 입력 중에서 뭐랑 제일 연관 있어?”)
  • Key ($h_i$): 인코더 상태들의 간판 (“난 첫 번째 단어 정보야”, “난 두 번째 단어 정보야”)
  • Value ($h_i$): 실제 알맹이 정보 (연관도가 높다고 판단되면 이 값을 많이 가져옴)

디코더($Query$)가 인코더의 각 상태($Key$)와 자신을 비교하여 연관도 점수를 매기고, 그 점수 비율대로 인코더의 정보($Value$)를 쏙쏙 골라 담아 새로운 컨텍스트 벡터($c_k$)를 만드는 것입니다.

덕분에 문장이 아무리 길어져도 디코더는 앞쪽에 나온 단어를 정확하게 조준해서 번역할 수 있게 되었습니다.


다음 편 예고




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • ROUGE
  • 덕 타이핑, 그리고 파이썬이 타입을 다루는 방식
  • 행렬 곱의 네 가지 관점
  • How to teach your embedding model new words
  • classmethod vs staticmethod in Python