Seq2Seq (Sequence to Sequence) 와 Attention
30 Jun 2020 | NLP
본 포스트의 내용은 고려대학교 강필성 교수님의 강의 와 Jay alammar의 깃허브 , 그리고 김기현의 자연어처리 딥러닝 캠프 , 밑바닥에서 시작하는 딥러닝 2 , 한국어 임베딩 책을 참고하였습니다.
Seq2Seq(Sequence to Sequence)
Seq2Seq(Sequence to Sequence)란 아이템의 시퀀스를 입력받아 또 다른 시퀀스를 내놓는 모델을 통칭하여 일컫는 말입니다. 만약 시퀀스가 문장이라면 각각의 아이템은 단어를 나타내고, 동영상이라면 프레임 별 이미지를 나타냅니다. 입력하는 아이템의 개수와 출력하는 아이템의 개수가 같을 필요는 없습니다. 아래 그림에서도 3개의 아이템으로 구성된 시퀀스가 입력되었지만 4개의 아이템으로 구성된 시퀀스가 출력되는 것을 볼 수 있습니다.
이미지 출처 : jalammar.github.io
자연어처리에서 Seq2Seq이 사용되는 대표적인 태스크(Task)로는 기계 번역(Machine translation)이 있습니다. 아래는 위 이미지를 기계번역 관점에서 구체화한 이미지입니다. 3개의 단어로 이루어진 프랑스어 문장을 4개의 단어로 이루어진 영어 문장으로 번역하고 있습니다.
이미지 출처 : jalammar.github.io
Encoder - Decoder
Seq2Seq 모델은 크게 인코더(Encoder)와 디코더(Decoder)가 연결된 구조로 이루어져 있습니다. 인코더는 입력한 시퀀스의 정보를 어떻게 압축하여 처리할 지를 담당합니다. 디코더는 인코더가 압축하여 넘겨준 정보를 어떤 식으로 반환해낼 지에 대한 역할을 합니다.
조금 더 자세히 설명하면 인코더는 입력 시퀀스에 있는 각각의 아이템을 컴파일(Compile)하여 하나의 벡터, 즉 문맥(Context) 벡터로 표현합니다. 시퀀스를 모두 읽어낸 후에는 생성한 문맥 벡터를 디코더에게 넘겨주게 되지요. 디코더는 이를 받아 새로운 아이템 시퀀스를 출력하게 됩니다. 아래 동영상은 인코더와 디코더가 어떤 역할을 수행하는지를 잘 보여주고 있습니다.
이미지 출처 : jalammar.github.io
기계 번역 태스크라면 아래와 같이 적용될 것입니다.
이미지 출처 : jalammar.github.io
RNN(Recurrent Neural Network)
RNN(Recurrent Neural Network, 순환 신경망) Seq2Seq에서 고전적인 모델 중 하나입니다. RNN의 은닉층(Hidden layer)에서는 은닉층 위치에 해당하는 아이템에 대해 입력 벡터와 이전 은닉층이 출력한 벡터(Hidden state vector)를 동시에 입력받습니다. 신경망은 출력 벡터뿐만 아니라 갱신된 은닉 상태 벡터를 출력합니다. 이 중 은닉 상태 벡터는 다음 Time-step에 인풋과 함께 은닉층에 입력됩니다. 아래는 RNN이 첫 번째 Time step, 즉 시퀀스 내에 있는 첫 번째 아이템을 입력받을 때 동작하는 방식을 이미지로 나타낸 것입니다.
이미지 출처 : jalammar.github.io
첫 번째 Time step에서는 0번째 은닉 상태 벡터와 첫 번째 아이템의 벡터가 입력됩니다. 두 벡터를 입력받은 은닉층은 이를 계산하여 은닉 상태 벡터를 새롭게 갱신합니다(태스크마다 출력 벡터를 내놓는 시점은 달라집니다). 위에서 사용했던 예시를 통해서 각 Time step마다 은닉 상태 벡터가 갱신되는 모습을 볼 수 있습니다.
이미지 출처 : jalammar.github.io
기계 번역 관점에서는 아래와 같이 Time-step 별로 자세히 나타낼 수 있습니다. 인코더에서는 각 아이템이 입력될 때마다 은닉 상태 벡터가 업데이트 됩니다. 인코더는 가장 마지막 아이템이 입력되고 난 후에 생성된 은닉 상태 벡터를 디코더에게 넘겨주게 됩니다. 디코더는 이 벡터를 받아 적절한 아이템을 반환합니다.
이미지 출처 : jalammar.github.io
Attention
RNN의 구조적 문제점
RNN의 가장 큰 단점은 장기 의존성(Long-term dependency) 문제입니다. 순차적으로 아이템이 입력되는 RNN 구조의 특성상 디코더로 들어가는 문맥(Context) 벡터는 시퀀스 뒤쪽에 있는 아이템의 영향을 더 많이 받을 수 밖에 없습니다. 특히 시퀀스가 길어지면 시퀀스 앞쪽에 있는 단어의 영향이 거의 사라집니다. 이렇게 과거 정보가 마지막까지 전달되지 못하는 것을 장기 의존성 문제라고 합니다.
장기 의존성 문제를 해결하기 위해 Vanilla RNN의 구조를 변형한 LSTM(Long-term Short Memory, 장단기 기억망)이나 GRU(Gated Recurrent Unit)등이 제시되었습니다. 이런 변형 모델이 문제를 일부 줄여주기는 했지만 순차적으로 구조적 문제는 여전히 남아있지요.
Attention
Attention은 이를 해결하기 위해서 고안된 개념입니다. RNN구조가 보여주고 있는 문제는 문맥(Context)의 정보를 가지고 있는 은닉 상태 벡터(Hidden state vector)가 하나였기 때문에 병목현상이 발생한다는 것이었습니다. 하지만 Attention은 모델이 아이템을 출력할 때마다 입력 시퀀스에서 어떤 아이템을 주목(Attention)해야하는 지를 가중치 벡터로 표시해 줍니다.
이미지 출처 : DeepMind.com
Attention에는 두 가지 모델이 있습니다. 모델을 발표한 사람의 이름을 따서 Bahadanau Attention과 Luong Attention 이라고 부릅니다. Bahadanau의 모델은 Attention 가중치(Score)를 학습하는 신경망이 따로 있으며 Luong의 모델은 유사도를 측정하여 Attention 가중치를 만들어냅니다. 두 모델 간의 성능 차이가 거의 없기 때문에 일반적으로는 Luong attention을 사용합니다.
아래는 Attention이 적용된 RNN이 어떻게 작동하는 지를 보여주는 이미지입니다.
이미지 출처 : jalammar.github.io
인코더는 더 이상 마지막 은닉 상태 벡터만을 디코더에 입력하지 않습니다. 각 Time step 마다 생성되는 은닉 상태 벡터를 보관합니다. 그리고 인코딩이 끝나면 생성된 모든 은닉 상태 벡터를 디코더에 넘겨줍니다. 생성되는 은닉 상태 벡터를 모두 넘겨주기 때문에 시퀀스 앞쪽의 정보도 디코더에 손실없이 넘겨줄 수 있게 됩니다. 덕분에 장기 의존성 문제를 해결할 수 있지요.
그렇다면 디코더는 각각의 은닉 상태 벡터를 어떻게 활용하는 것일까요? 아래는 4번째 Time step, 즉 입력 시퀀스 아이템에 의해서 생긴 은닉 상태 벡터를 디코더가 받은 후에 일어나는 일을 순차적으로 나타낸 것입니다.
이미지 출처 : jalammar.github.io
디코더는 먼저 각각의 은닉 상태 벡터$(h_1, h_2, h_3)$를 살펴봅니다. 이 벡터들은 이전 Time-step의 은닉 상태 벡터와 입력 시퀀스의 아이템에 의해서 생성되었기 때문에 그 벡터를 생성할 시점에 입력된 아이템의 정보를 가장 많이 가지고 있습니다. 디코더의 은닉 상태 벡터는 각각의 은닉 상태 벡터와 내적한 값(Attention score)에 소프트맥스를 취해줍니다.
소프트맥스의 함숫값, 즉 위 그림에서 $(0.96, 0.02, 0.02)$ 는 각 은닉 상태 벡터에 가중치가 됩니다. 이 값은 각각의 은닉 상태 벡터와 곱해집니다. 최종적으로 이를 모두 더하여 $0.96 \times h_1 + 0.02 \times h_2 + 0.02 \times h_3$ 라는 문맥 벡터(Sum-up weighted vector)를 생성하게 됩니다. 즉 2번 단계에서 계산되는 내적값이 클수록 디코딩되는 아이템 입장에서 중요한 은닉 상태 벡터, 작을수록 중요도가 떨어지는 벡터로 판단합니다.
이렇게 생성된 컨텍스트 벡터는 디코더의 은닉 상태 벡터와 Concatenation(옆으로 붙임)하여 사용합니다. 그래서 출력 아이템을 생성하기 위해 입력되는 벡터의 사이즈는 두 벡터의 사이즈를 더한 것이 됩니다. 그리고 이를 각 출력 아이템의 FFNN(Feed-forward neural network)에 넣어 출력 아이템의 임베딩 벡터를 도출합니다. 이 과정을 도식화하면 아래와 같이 나타낼 수 있습니다.
이미지 출처 : jalammar.github.io
위 그림에서 <END>
는 입력 문장의 끝을 나타내는 토큰으로 디코더가 작동하는 시점을 알리는 신호의 역할을 합니다. $h_\text{init}$는 각 은닉 상태 벡터를 내적하여 새로운 문맥 상태 벡터 $C_4$를 생성합니다. 이 벡터는 디코더의 첫 번째 Time step의 은닉층이 생성한 $h_4$와 이어붙여집니다. 이렇게 생성된 벡터 $[C_4;h_4]$ 는 FFNN을 거쳐 가장 적합한 아이템 “I”의 임베딩 벡터를 출력하게 되지요.
다음 Time step에서는 이전 단계의 은닉층이 출력하는 은닉 상태 벡터, 즉 $h_4$가 이전 단계에서 $h_\text{init}$과 같은 역할을 하게 됩니다. 그리고 이전 단계의 <END>
와 같은 역할은 이전 단계의 출력 벡터인 “I”의 임베딩 벡터가 맡게 됩니다. 동일한 과정을 거치면 “am”의 임베딩 벡터가 출력됩니다. 이런 과정을 계속 반복하면 “a”, “student”의 임베딩 벡터가 생성되며 디코더는 <END>
혹은 <END>
벡터를 생성할 때까지 이 과정을 반복한 후에 문장 생성을 종료합니다.
출력 시퀀스의 각 아이템이 생성될 때 인코더의 은닉 상태 벡터에게 받은 Attention score를 시각화한 결과는 다음과 같습니다.
이미지 출처 : jalammar.github.io
그림으로부터 “Je” 와 “I”, “suis” 와 “am”, “étudiant” 와 “student” 가 깊은 연관을 가지고 있음을, 번역된 “a” 는 “suis” 와 “étudiant” 에 골고루 연관되어 있음을 알 수 있습니다.
의의
RNN구조든 Attention이든 자연어처리에서 Seq2Seq 구조의 발전은 기계 번역 태스크의 커다란 발전을 가져왔습니다. 게다가 RNN의 구조적 한계점을 개선한 Attention의 등장으로 긴 문장에 대해서도 좋은 성능을 보이는 모델이 개발되었지요. 이후 Self-Attention을 활용한 트랜스포머(Transformer)가 등장하게 되는 배경이 되는 아이디어로도 중요한 역할을 담당하고 있습니다.
본 포스트의 내용은 고려대학교 강필성 교수님의 강의 와 Jay alammar의 깃허브 , 그리고 김기현의 자연어처리 딥러닝 캠프 , 밑바닥에서 시작하는 딥러닝 2 , 한국어 임베딩 책을 참고하였습니다.
Seq2Seq(Sequence to Sequence)
Seq2Seq(Sequence to Sequence)란 아이템의 시퀀스를 입력받아 또 다른 시퀀스를 내놓는 모델을 통칭하여 일컫는 말입니다. 만약 시퀀스가 문장이라면 각각의 아이템은 단어를 나타내고, 동영상이라면 프레임 별 이미지를 나타냅니다. 입력하는 아이템의 개수와 출력하는 아이템의 개수가 같을 필요는 없습니다. 아래 그림에서도 3개의 아이템으로 구성된 시퀀스가 입력되었지만 4개의 아이템으로 구성된 시퀀스가 출력되는 것을 볼 수 있습니다.
이미지 출처 : jalammar.github.io
자연어처리에서 Seq2Seq이 사용되는 대표적인 태스크(Task)로는 기계 번역(Machine translation)이 있습니다. 아래는 위 이미지를 기계번역 관점에서 구체화한 이미지입니다. 3개의 단어로 이루어진 프랑스어 문장을 4개의 단어로 이루어진 영어 문장으로 번역하고 있습니다.
이미지 출처 : jalammar.github.io
Encoder - Decoder
Seq2Seq 모델은 크게 인코더(Encoder)와 디코더(Decoder)가 연결된 구조로 이루어져 있습니다. 인코더는 입력한 시퀀스의 정보를 어떻게 압축하여 처리할 지를 담당합니다. 디코더는 인코더가 압축하여 넘겨준 정보를 어떤 식으로 반환해낼 지에 대한 역할을 합니다.
조금 더 자세히 설명하면 인코더는 입력 시퀀스에 있는 각각의 아이템을 컴파일(Compile)하여 하나의 벡터, 즉 문맥(Context) 벡터로 표현합니다. 시퀀스를 모두 읽어낸 후에는 생성한 문맥 벡터를 디코더에게 넘겨주게 되지요. 디코더는 이를 받아 새로운 아이템 시퀀스를 출력하게 됩니다. 아래 동영상은 인코더와 디코더가 어떤 역할을 수행하는지를 잘 보여주고 있습니다.
이미지 출처 : jalammar.github.io
기계 번역 태스크라면 아래와 같이 적용될 것입니다.
이미지 출처 : jalammar.github.io
RNN(Recurrent Neural Network)
RNN(Recurrent Neural Network, 순환 신경망) Seq2Seq에서 고전적인 모델 중 하나입니다. RNN의 은닉층(Hidden layer)에서는 은닉층 위치에 해당하는 아이템에 대해 입력 벡터와 이전 은닉층이 출력한 벡터(Hidden state vector)를 동시에 입력받습니다. 신경망은 출력 벡터뿐만 아니라 갱신된 은닉 상태 벡터를 출력합니다. 이 중 은닉 상태 벡터는 다음 Time-step에 인풋과 함께 은닉층에 입력됩니다. 아래는 RNN이 첫 번째 Time step, 즉 시퀀스 내에 있는 첫 번째 아이템을 입력받을 때 동작하는 방식을 이미지로 나타낸 것입니다.
이미지 출처 : jalammar.github.io
첫 번째 Time step에서는 0번째 은닉 상태 벡터와 첫 번째 아이템의 벡터가 입력됩니다. 두 벡터를 입력받은 은닉층은 이를 계산하여 은닉 상태 벡터를 새롭게 갱신합니다(태스크마다 출력 벡터를 내놓는 시점은 달라집니다). 위에서 사용했던 예시를 통해서 각 Time step마다 은닉 상태 벡터가 갱신되는 모습을 볼 수 있습니다.
이미지 출처 : jalammar.github.io
기계 번역 관점에서는 아래와 같이 Time-step 별로 자세히 나타낼 수 있습니다. 인코더에서는 각 아이템이 입력될 때마다 은닉 상태 벡터가 업데이트 됩니다. 인코더는 가장 마지막 아이템이 입력되고 난 후에 생성된 은닉 상태 벡터를 디코더에게 넘겨주게 됩니다. 디코더는 이 벡터를 받아 적절한 아이템을 반환합니다.
이미지 출처 : jalammar.github.io
Attention
RNN의 구조적 문제점
RNN의 가장 큰 단점은 장기 의존성(Long-term dependency) 문제입니다. 순차적으로 아이템이 입력되는 RNN 구조의 특성상 디코더로 들어가는 문맥(Context) 벡터는 시퀀스 뒤쪽에 있는 아이템의 영향을 더 많이 받을 수 밖에 없습니다. 특히 시퀀스가 길어지면 시퀀스 앞쪽에 있는 단어의 영향이 거의 사라집니다. 이렇게 과거 정보가 마지막까지 전달되지 못하는 것을 장기 의존성 문제라고 합니다.
장기 의존성 문제를 해결하기 위해 Vanilla RNN의 구조를 변형한 LSTM(Long-term Short Memory, 장단기 기억망)이나 GRU(Gated Recurrent Unit)등이 제시되었습니다. 이런 변형 모델이 문제를 일부 줄여주기는 했지만 순차적으로 구조적 문제는 여전히 남아있지요.
Attention
Attention은 이를 해결하기 위해서 고안된 개념입니다. RNN구조가 보여주고 있는 문제는 문맥(Context)의 정보를 가지고 있는 은닉 상태 벡터(Hidden state vector)가 하나였기 때문에 병목현상이 발생한다는 것이었습니다. 하지만 Attention은 모델이 아이템을 출력할 때마다 입력 시퀀스에서 어떤 아이템을 주목(Attention)해야하는 지를 가중치 벡터로 표시해 줍니다.
이미지 출처 : DeepMind.com
Attention에는 두 가지 모델이 있습니다. 모델을 발표한 사람의 이름을 따서 Bahadanau Attention과 Luong Attention 이라고 부릅니다. Bahadanau의 모델은 Attention 가중치(Score)를 학습하는 신경망이 따로 있으며 Luong의 모델은 유사도를 측정하여 Attention 가중치를 만들어냅니다. 두 모델 간의 성능 차이가 거의 없기 때문에 일반적으로는 Luong attention을 사용합니다.
아래는 Attention이 적용된 RNN이 어떻게 작동하는 지를 보여주는 이미지입니다.
이미지 출처 : jalammar.github.io
인코더는 더 이상 마지막 은닉 상태 벡터만을 디코더에 입력하지 않습니다. 각 Time step 마다 생성되는 은닉 상태 벡터를 보관합니다. 그리고 인코딩이 끝나면 생성된 모든 은닉 상태 벡터를 디코더에 넘겨줍니다. 생성되는 은닉 상태 벡터를 모두 넘겨주기 때문에 시퀀스 앞쪽의 정보도 디코더에 손실없이 넘겨줄 수 있게 됩니다. 덕분에 장기 의존성 문제를 해결할 수 있지요.
그렇다면 디코더는 각각의 은닉 상태 벡터를 어떻게 활용하는 것일까요? 아래는 4번째 Time step, 즉 입력 시퀀스 아이템에 의해서 생긴 은닉 상태 벡터를 디코더가 받은 후에 일어나는 일을 순차적으로 나타낸 것입니다.
이미지 출처 : jalammar.github.io
디코더는 먼저 각각의 은닉 상태 벡터$(h_1, h_2, h_3)$를 살펴봅니다. 이 벡터들은 이전 Time-step의 은닉 상태 벡터와 입력 시퀀스의 아이템에 의해서 생성되었기 때문에 그 벡터를 생성할 시점에 입력된 아이템의 정보를 가장 많이 가지고 있습니다. 디코더의 은닉 상태 벡터는 각각의 은닉 상태 벡터와 내적한 값(Attention score)에 소프트맥스를 취해줍니다.
소프트맥스의 함숫값, 즉 위 그림에서 $(0.96, 0.02, 0.02)$ 는 각 은닉 상태 벡터에 가중치가 됩니다. 이 값은 각각의 은닉 상태 벡터와 곱해집니다. 최종적으로 이를 모두 더하여 $0.96 \times h_1 + 0.02 \times h_2 + 0.02 \times h_3$ 라는 문맥 벡터(Sum-up weighted vector)를 생성하게 됩니다. 즉 2번 단계에서 계산되는 내적값이 클수록 디코딩되는 아이템 입장에서 중요한 은닉 상태 벡터, 작을수록 중요도가 떨어지는 벡터로 판단합니다.
이렇게 생성된 컨텍스트 벡터는 디코더의 은닉 상태 벡터와 Concatenation(옆으로 붙임)하여 사용합니다. 그래서 출력 아이템을 생성하기 위해 입력되는 벡터의 사이즈는 두 벡터의 사이즈를 더한 것이 됩니다. 그리고 이를 각 출력 아이템의 FFNN(Feed-forward neural network)에 넣어 출력 아이템의 임베딩 벡터를 도출합니다. 이 과정을 도식화하면 아래와 같이 나타낼 수 있습니다.
이미지 출처 : jalammar.github.io
위 그림에서 <END>
는 입력 문장의 끝을 나타내는 토큰으로 디코더가 작동하는 시점을 알리는 신호의 역할을 합니다. $h_\text{init}$는 각 은닉 상태 벡터를 내적하여 새로운 문맥 상태 벡터 $C_4$를 생성합니다. 이 벡터는 디코더의 첫 번째 Time step의 은닉층이 생성한 $h_4$와 이어붙여집니다. 이렇게 생성된 벡터 $[C_4;h_4]$ 는 FFNN을 거쳐 가장 적합한 아이템 “I”의 임베딩 벡터를 출력하게 되지요.
다음 Time step에서는 이전 단계의 은닉층이 출력하는 은닉 상태 벡터, 즉 $h_4$가 이전 단계에서 $h_\text{init}$과 같은 역할을 하게 됩니다. 그리고 이전 단계의 <END>
와 같은 역할은 이전 단계의 출력 벡터인 “I”의 임베딩 벡터가 맡게 됩니다. 동일한 과정을 거치면 “am”의 임베딩 벡터가 출력됩니다. 이런 과정을 계속 반복하면 “a”, “student”의 임베딩 벡터가 생성되며 디코더는 <END>
혹은 <END>
벡터를 생성할 때까지 이 과정을 반복한 후에 문장 생성을 종료합니다.
출력 시퀀스의 각 아이템이 생성될 때 인코더의 은닉 상태 벡터에게 받은 Attention score를 시각화한 결과는 다음과 같습니다.
이미지 출처 : jalammar.github.io
그림으로부터 “Je” 와 “I”, “suis” 와 “am”, “étudiant” 와 “student” 가 깊은 연관을 가지고 있음을, 번역된 “a” 는 “suis” 와 “étudiant” 에 골고루 연관되어 있음을 알 수 있습니다.
의의
RNN구조든 Attention이든 자연어처리에서 Seq2Seq 구조의 발전은 기계 번역 태스크의 커다란 발전을 가져왔습니다. 게다가 RNN의 구조적 한계점을 개선한 Attention의 등장으로 긴 문장에 대해서도 좋은 성능을 보이는 모델이 개발되었지요. 이후 Self-Attention을 활용한 트랜스포머(Transformer)가 등장하게 되는 배경이 되는 아이디어로도 중요한 역할을 담당하고 있습니다.
Comments