RNN에 attention 더하기
RNN
RNN은 시퀀스 단위로 입력과 출력을 처리하는 모델을 말해요. 여기서 시퀀스는 “순서가 있는 데이터”, 우리가 쓰는 언어의 문장같은 게 시퀀스인데요.(정확하게는 단어나 문장만이 아니라 시간 순서가 있는 모든 데이터를 포괄하는 뜻). 쉽게 말해 한글 문장을 입력하면, 영어 문장으로 번역해서 출력해주는 것을 상상하면 됩니다. 이런 시퀀스를 처리하기 위해 개발된 가장 기본적인 모델이 RNN이죠.
순서가 있는 데이터이기 때문에, 이전 상태를 다음 상태에 기억시키기 위해 정보를 넘겨주면서 가는 방식이에요.
이걸로 번역기 모델을 짠다고 치면,(이게 Seq2seq 모델인데) 크게 이런 구조로 짜여 있어요.
인코더 RNN -> 컨텍스트 벡터 -> 디코더 RNN
인코더는 입력받은 문장을 일정한 크기를 가진 벡터로 바꿔줘요. 이걸 컨텍스트 벡터라고 하는데, 문장의 정보를 압축적으로 담고 있어요. 그리고 이 컨텍스트 벡터를 디코더에 넘겨요. 디코더는 컨텍스트 벡터와 이전에 생성한 출력을 참고해서 다음 출력을 생성하고요.
그런데 여기서 몇 가지 문제가 있었어요.
문장이 엄청나게 길어지는 경우에 인공지능의 성능이 떨어지는 거예요. 기존의 방식에서는 모델이 긴 문장을 통째로 기억했다가 통째로 번역해서 뱉어내는 방식으로 했어요. 이게 무슨 말이냐면, 입력한 문장 전체를 하나의 고정된 벡터로 압축하려고 한거에요. 컨텍스트 벡터가 고정된 길이의 벡터라서 정보가 많아지면 압축이 많이 되야 하고, 작은 상자에 엄청 많은 짐을 구겨 넣으려고 하는 느낌? 그 과정에서 정보가 사라지기도 하고, 특정 부분은 잘 기억하지 못한다던가 하는 문제가 생겼고요.
그래서 이걸 어떻게 해결할까! 하다가 나온 것이 어텐션입니다.
어텐션을 일단 큰 그림으로 말씀드리면, 사람이 문장을 읽을 때처럼 내가 번역해야 할 부분을 원문에 매치되는 부분을 찾아서 집중적으로 보면서 번역하는 것이라고 할 수 있어요. 선택과 집중인데요.
조금 더 자세하게 보면,
인코더에 문장이 입력된다고 해볼께요. 이때 문장 안에 있는 각 요소들에 히든 스테이트라고 하는 정보들이 있는데, 이 히든 스테이트도 디코더에 함께 넘겨요.
디코더는 문장을 출력해야하겠죠? 디코더는 현재 자기 시점의 히든 스테이트를 가지고 있는데, 이 히든 스테이트와 인코더의 히든 스테이트를 비교해서 점수를 계산해요. 점수 계산은 어텐션 펑션이라고 하는 함수로 계산하고, 이걸 소프트맥스를 거쳐서 확률 분포를 갖도록 변환해요. 이렇게 얻은 확률 점수는 가중치로 사용해요. 모든 히든 스테이트의 가중합을 계산하고 이걸로 다시 새로운 컨텍스트 벡터를 생성한뒤, 이 컨텍스트 벡터를 사용해 출력을 생성합니다.
예를 들어서, I love you라는 문장이 인코더로 들어왔고, 디코더가 “나는”을 생성할 시점이라고 해볼께요. 이때 어텐션 점수를 계산해서 I 가 가장 높은 점수인 0.7, love 는 0.2 you는 0.1로 계산됐다고 하면, 새로운 컨텍스트 벡터는 0.7에 I의 히든 스테이트를 곱하고, 0.2에 love의 히든 스테이트를 곱하고, 0.1에 you 히든 스테이트를 곱해서 다 더해줘요. 그렇게 얻은 컨텍스트 벡터가 “나는”을 출력하는 거에요.