개요

지난 글 마지막에 Seq2Seq의 문제를 하나 남겨뒀다.

문장 전체를 벡터 하나에 밀어 넣는다.

이번 글은 그 문제를 뚫은 Attention이다. 개인적으로는 이 시리즈에서 가장 중요한 아이디어라고 생각한다. 다음 글의 Transformer도, 지금의 대화형 AI도 전부 이 아이디어 위에 올라가 있기 때문이다.

교육 자료는 대외비라 슬라이드 이미지나 예제 데이터는 싣지 않는다. 개념 흐름만 내가 이해한 방식으로 재구성했고, 그림은 전부 생성했다.


1. 병목 문제

먼저 문제를 정확히 보자.

병목 문제

Seq2Seq에서 인코더는 문장을 끝까지 읽고 마지막 요약 벡터 하나를 만든다. 디코더는 오직 그 벡터만 보고 번역문 전체를 써야 한다.

문제는 벡터 크기가 고정이라는 점이다.

  • 5단어짜리 문장도 벡터 하나
  • 100단어짜리 문단도 똑같은 크기의 벡터 하나

입력이 길어져도 통로는 그대로다. 담을 수 있는 양이 정해져 있으니 넘치는 정보는 뭉개진다. 특히 앞부분일수록 심하다. 뒤에 나온 단어들에 계속 덮어써지기 때문이다.

이걸 병목(bottleneck) 문제라고 부른다.

더 쉬운 비유로

시험 감독이 이렇게 말한다고 하자. “교과서를 30분 동안 읽어라. 그다음엔 책을 걷어갈 테니 기억만으로 시험을 봐라.”

짧은 단원이면 어떻게든 되겠지만, 300쪽짜리를 읽고 다 외우는 건 불가능하다. 머리가 나빠서가 아니라 조건이 이상한 것이다.

그럼 어떻게 해야 할까? 답은 간단하다. 문제 풀 때 책을 다시 펴보게 해주면 된다.

Attention이 정확히 그걸 한다.


2. Attention의 아이디어

Attention = 디코더가 단어를 만들 때마다, 원문 전체를 다시 훑어보고 필요한 부분을 골라 참고하게 하는 방법

핵심은 두 가지다.

  • 마지막 요약 하나만 보지 않는다. 인코더가 각 단어를 읽으며 만든 모든 중간 결과에 접근할 수 있다
  • 매번 다시 계산한다. 지금 만들 단어가 무엇이냐에 따라 어디를 볼지가 달라진다

두 번째가 특히 중요하다. 번역문에서 “cats”를 쓸 차례라면 원문의 “고양이를”을 집중해서 봐야 하고, “like”를 쓸 차례라면 “좋아해”를 봐야 한다. 단어마다 봐야 할 곳이 다르다.

요약본 하나를 잘 만드는 게 문제가 아니었다. 요약본만 보게 강제한 것이 문제였다.


3. 실제로 어떻게 계산하나

여기가 이번 글의 핵심이다. 숫자로 따라가 보자.

Attention 세 단계

원문이 “나는 고양이를 정말 좋아해”이고, 지금 번역문에서 “cats”를 만들 차례라고 하자.

1단계 — 관련도 점수 매기기

지금 만들 단어와 원문의 각 단어가 얼마나 관련 있는지를 숫자로 잰다.

원문 단어 점수
나는 0.5
고양이를 3.0
정말 1.0
좋아해 2.0

“cats”를 만들려면 “고양이를”이 제일 중요하니 점수가 높게 나온다.

2단계 — 확률로 바꾸기

점수를 그대로 쓰면 곤란하다. 크기가 제각각이고 합이 얼마인지도 모르니 “비중”으로 쓸 수가 없다.

그래서 소프트맥스를 씌운다. 지난 글에서 잠깐 나왔던, 전부 더하면 1이 되도록 만드는 함수다.

원문 단어 점수 → 비중
나는 0.5 0.05
고양이를 3.0 0.63
정말 1.0 0.09
좋아해 2.0 0.23

이제 합이 1이다. “고양이를에 63%, 좋아해에 23%를 쓰겠다”처럼 읽을 수 있게 됐다.

점수가 3.0과 2.0으로 1밖에 차이 안 났는데 비중은 0.63과 0.23으로 세 배 가까이 벌어졌다. 소프트맥스가 큰 값을 더 크게 벌리는 성질이 있기 때문이다. “적당히 중요한 것”보다 “제일 중요한 것”에 확실히 몰아주는 효과가 있다.

3단계 — 그 비율대로 섞기

이제 원문 단어들의 벡터를 구한 비중대로 섞는다.

이번 단어를 위한 요약
 = 나는×0.05 + 고양이를×0.63 + 정말×0.09 + 좋아해×0.23

이렇게 나온 값이 “cats를 만들기 위한 맞춤 요약”이다. 그리고 다음 단어를 만들 때는 1단계부터 다시 한다. 비중이 새로 계산되니 요약도 새로 만들어진다.

요약이 하나에서 단어 개수만큼으로 늘어난 셈이다. 병목은 통로가 하나뿐이라 생긴 문제였으니, 통로를 필요한 만큼 만들어버린 것이다.


4. Query, Key, Value로 다시 보기

이 과정을 부르는 정식 용어가 있다. 처음엔 이 세 단어가 제일 헷갈렸는데, 역할을 나눠 부르는 이름일 뿐이다.

이름 정체 하는 일
Query (질의) 지금 만들 단어 쪽 “나는 무엇을 찾고 있는가”
Key (열쇠) 원문의 각 단어 “나는 어떤 정보를 가졌는가”
Value (값) 원문의 각 단어 “실제로 가져갈 내용”

앞의 계산에 그대로 대응된다.

  1. Query와 Key를 비교해서 점수를 낸다 → 1단계
  2. 점수를 소프트맥스로 비중으로 바꾼다 → 2단계
  3. 그 비중으로 Value를 섞는다 → 3단계

더 쉬운 비유로

도서관에서 자료를 찾는 상황과 같다.

  • Query = 내가 찾는 주제 (“고양이 기르는 법”)
  • Key = 책등에 붙은 제목 — 찾을 때 비교하는 대상
  • Value = 책 속의 실제 내용 — 찾은 뒤 가져가는 것

제목을 훑어 관련도를 매기고(1단계), 어느 책을 얼마나 참고할지 정하고(2단계), 그 비율대로 내용을 뽑아 정리한다(3단계).

Key와 Value가 왜 따로 있는지도 여기서 이해된다. 검색에 쓰기 좋은 형태(제목)와 실제로 필요한 형태(내용)가 다르기 때문이다.

Query와 Key를 비교하는 방법은 여러 가지가 있다. 두 벡터를 곱해서 더하는 방식이 가장 흔하고, 작은 신경망을 하나 두고 계산하는 방식도 있다. 어떻게 재느냐만 다르고 하는 일은 같다.


5. Attention이 가져온 변화

① 성능이 크게 올랐다

특히 긴 문장에서 차이가 컸다. 병목이 사라졌으니 당연하다. 기존 방식은 문장이 길어질수록 번역 품질이 뚝뚝 떨어졌는데, Attention을 붙이면 길어져도 성능이 잘 유지됐다.

② 거리가 사라졌다

이게 개인적으로 가장 인상 깊은 부분이다.

RNN에서 30단어 떨어진 단어를 참고하려면 30단계를 거쳐야 했다. 그 사이에 신호가 희미해지는 게 기울기 소실 문제였다.

Attention은 한 번에 직접 연결된다. 30단어 떨어져 있든 3단어 떨어져 있든 똑같이 한 걸음이다.

거리라는 개념 자체가 없어진 것에 가깝다. 이게 나중에 “그럼 순서대로 처리할 필요가 있나?”라는 질문으로 이어진다.

③ 모델이 어디를 봤는지 보인다

정렬 행렬

계산된 비중을 그대로 그림으로 그리면, 어느 단어를 볼 때 어디를 참고했는지가 드러난다.

재밌는 건 아무도 이걸 가르쳐주지 않았다는 점이다. “고양이를”과 “cats”가 대응된다고 알려준 적이 없는데, 번역을 잘하려고 애쓰다 보니 저절로 그렇게 정렬됐다.

과거에는 이 단어 대응 관계를 사람이 따로 만들어 넣어야 했다. Attention은 그걸 부산물로 얻는다.

그리고 이건 해석 가능성 측면에서도 값지다. 지난 시리즈에서 “왜 그렇게 판단했는지 설명 못 하는 모델은 실무에서 못 쓴다”고 정리했는데, Attention은 최소한 “어디를 보고 그렇게 말했는지”는 보여준다.


6. 그런데 여전히 남은 것

Attention은 Seq2Seq의 병목을 뚫었다. 하지만 아직 RNN 위에 얹혀 있는 부가 기능이다.

즉 이런 상태다.

  • 인코더도 디코더도 여전히 RNN·LSTM이다
  • 그러니 단어를 하나씩 순서대로 처리해야 한다
  • 앞 단어 계산이 끝나야 다음 단어를 시작할 수 있으니 병렬 처리가 안 된다

여기서 아주 대담한 질문이 나온다.

RNN이 하던 “정보 전달”을 Attention이 더 잘한다면, 굳이 RNN이 필요할까?

이 질문에 “필요 없다”고 답한 것이 다음 글의 주제, Transformer다.


정리

  • 병목 문제 — Seq2Seq는 문장 전체를 고정 크기 벡터 하나에 압축한다. 길수록 뭉개진다
  • Attention = 단어를 만들 때마다 원문 전체를 다시 보고 필요한 부분을 골라 쓰는 방법
  • 계산은 세 단계
    1. 지금 만들 단어와 원문 단어들의 관련도 점수
    2. 소프트맥스로 합이 1인 비중으로 변환
    3. 그 비중대로 원문 벡터를 섞어서 이번 단어용 요약을 만든다
  • Query·Key·Value = 찾는 것 / 비교 대상 / 가져갈 내용. 도서관에서 자료 찾기와 같은 구조
  • 효과 — 긴 문장 성능 향상, 거리 무관하게 직접 연결, 어디를 봤는지 보인다
  • 남은 문제 — 아직 RNN 위에 얹힌 기능이라 순서대로 처리해야 하고, 병렬화가 안 된다

다음 글에서

다음 글에서는 Self-AttentionTransformer를 다룬다. “RNN을 아예 빼버리면 어떻게 되는가”라는 질문에서 출발해, 지금 거의 모든 언어 모델의 기본 구조가 된 형태까지 정리한다.

한줄 평

  • Attention Is All You Need