DL&ML/concept

Seq2Seq, Auto Regressive, Attention, Teacher Forcing, Input Feeding

식피두 2021. 4. 7. 14:21

최근에 번역 모델을 직접 구현해보면서 공부하게 된 구조 seq2seq.

 

개념만 알고 직접 구현해본 적은 없어서 굳이 호기심을 가지고 해본 것인데, 새롭게 배운 것이 꽤 있다.

 

이해하기 위해선 RNN이나, LSTM 등이 대충 어떻게 생겨먹었는지 정도는 알아야 함.

 

seq2seq, *Auto Regressive

뭐, 대략 아래와 같은 구조로 이루어져있다.

인코더 및 디코더 블럭은 RNN 계열의 모델로 구성 되며, 여러 레이어로 쌓아서 쓰기도 한다.

 

번역 모델을 생각해봤을 때,

인코더에 문장을 구성하는 단어 토큰을 입력으로 넣어

마지막 타임 스텝의 히든 스테이트 값을 뽑으면

문장의 정보가 함축 되어 있을 것이라고 기대할 수 있다.

(압축 & 해제, 오토 인코더 느낌 ; latent space 어딘가로 위치 시킴 ; context vector)

 

디코더는 인코더의 마지막 스탭 히든 스테이트 값을 기반으로

한 타임 스텝씩 출력을 생성하게 된다. 

 

* 참고로 인코더의 초기 히든 스테이트 값은 0으로 채워진 벡터지만

디코더의 초기 히든 스테이트 값은 인코더의 마지막 타임스텝의 히든 스테이트 벡터이다.

 

번역 모델이라면 영어 토큰을 인코더의 입력으로 넣고

출력으로 한글 토큰을 한 타임 스텝씩 예측하게 되는 그림.

seq2seq (udacity/deep-learning)

실제 구현에서는 디코더의 아웃풋 벡터에 FC Layer를 거쳐 (제너레2터 ; Generator)

출력하려는 도메인(한글)의 단어 개수만큼 출력을 뽑고 난 후

소프트맥스를 취해 토큰에 대한 확률 분포로 변환,

가장 높은 확률 값을 갖는 단어를 선택하게 된다. (특정 타임스텝 기준)

 

특정 타임스텝에서 예측 된 단어는 다음 타임스텝의 디코더 인풋으로 들어가게 된다.

최초의 타임 스텝에선 보통 <BOS> 같은 생성의 시작을 알리는 토큰을 넣어 디코더가 최초의 출력을 내뱉도록 한다.

t 타임 스텝에서 출력 된 것은 마찬가지로 t+1 타임 스텝의 입력으로 들어감...

 

요러한 자기 회귀 속성을 가진 태스크를 Auto-Regressive task라고 부른다. 

 

AR 모델을 학습할 땐 teacher forcing이라는 방법을 써야 하는데, 이건 뒤에서..

 

그럼 잠깐, 

인코더는 반드시 AR task인가?

아니다. 그렇기 때문에 굳이 한 타임 스텝씩 전개해나갈 필요가 없다.

따라서 bi-directional RNN 계열의 모델을 적용해서 더 풍부한 표현을 얻을 수도 있다.

 

디코더에선 bi-directional RNN을 적용할 수 없다.

 

* 만약 인코더에 bi-directional RNN을 적용하면 디코더의 initial hidden state를 초기화 할 때

크기의 miss-match가 발생하게 되어 (2*hidden_dim vs. 1*hidden_dim)

평균을 내던지, 맥스값을 취하던지 해서 크기를 맞춰 주어야 한다.

 

*어텐션(Attention)

실제로는 토큰 길이만큼 RNN을 거치기 때문에 아래와 같아질 텐데,

문장이 길수록, 토큰이 많을 수록 중간에 encoder - decoder를 잇는

hidden state 벡터에 정보를 압축하는 데 있어 한계가 생길 것이다.

https://wikidocs.net/24996

이를 해결하는게 어텐션 메커니즘이다.

 

해쉬 자료구조를 생각해보면 키 값을 전달 했을 때 값이 하나가 바로 튀어 나온다.

어텐션은 소프트 해쉬 느낌이다.

 

값 하나가 특정지어 리턴 되는게 아니라,

관련 있는 값들이 모두 weighted average되서 리턴된다.

 

어텐션에서 기억해야할 단어

Query, Key, Value

 

seq2seq의 구조적 한계로 중간의 히든 벡터에 정보가 다 압축 되지 못할 수 있으므로

한 스텝씩 디코딩하는 단계에서 최종 출력을 생성하기 전에

인코더의 모든 스텝의 출력 히든 벡터(Key)(스택 RNN이라면 마지막 레이어) 

현재 스텝의 디코더의 히든 벡터를 이용해 Query를 날려

관련 있는 정보를 취합(Attention weight * Value)한 뒤

현재 디코더의 히든 벡터와 concat(정보 보충)해서 최종 출력을 생성하는 흐름이다.

 

다시 말하면,

 

특정 타임 스텝의 디코더의 출력 모양은 (batch_size, 1, hidden_size)

모든 타임 스텝의 인코더의 출력 모양은 (batch_size, m, hidden_size)

 

만약 인코더와 디코더의 출력 벡터가 비슷한 데이터로 학습이 되었다면,

벡터끼리 내적을 했을 때 유사하다면 1에 가깝게, 유사하지 않다면 0에 가깝게 나올 것..

 

하지만 번역 모델 기준으로 봤을 때

인코더는 영어, 디코더는 한글 데이터를 기반으로 학습되는 셈이라...

성격이 서로 다른 공간이라고 봐야한다.

 

따라서 이 서로 다른 공간의 다름을 어느 정도 보정해주기 위해서 디코더의 출력에

hidden_size * hidden_size의 FC_layer를 한 차례 통과시켜 공간 상의 위치를 보정해 준 뒤

디코더 - 인코더 출력 간의 유사도를 측정하면 한결 나은 값을 얻을 수 있을 것이다.

(요 레이어를 잘 학습하는게 좋은 Query를 만드는 것과 직결)

 

Attention Score = softmax(linear_transform(Q) * K) = (batch_size, 1, m)

 

어텐션 스코어가 얻어지면, 해당 스코어를 다시 인코더의 출력 히든 벡터에 element-wise 곱 해준 뒤

모두 reduce_sum 해서 (batch_size, 1, hidden_size)로 만들어 준다.

 

Context Vector = sum(Attension Score ⊙ Value, axis=1) = (batch_size, 1, hidden_size)

 

이렇게 인코더의 글로벌 타임 스텝에 대해 Query를 날려 

Query와 유사한 Key 값들의 Weight을 구하고

Value(=Key)를 Weighted Average 해서 Context Vector를 얻게 된다.

 

디코더의 현재 타임 스텝의 최종 출력값을 구하기 위한 FC Layer + softmax 를 통과 하기 전에

디코더 히든 스테이트 + Context Vector를 dim=-1로 Concat한 것을

원래 디코더의 히든 스테이트 크기로 만들어주는 FC layer와 tanh를 한 차례 적용해준다.

 

이렇게 함으로써 더 풍부한 정보를 바탕으로 값을 예측할 수 있게 해주는 것.

 

이게 어텐션 메커니즘이다.

 

seq2seq attention with input feeding 도식화 (김기현님 언어처리 자료)

학습 방식

입력 문장이 주어졌을 때 출력/번역 문장이 나올 확률을 최대화 하면 된다.

 

즉, 입력 문장 전체와, j 타임스텝 이전까지 예측 된 출력 토큰들이 주어졌을 때

j 타임스텝의 출력 토큰을 잘 예측할 확률을 최대화 하면 된다.

걍 크로스 엔트로피로 학습하믄 된다.

 

Teacher forcing & Input feeding

근데 그냥 학습한다고 마냥 쉽게 되는게 아니다.

 

auto-regressive 태스크는 학습할 때와 예측할 때 동작 방식이 다르다.

 

예측 할 때는 앞선 seq2seq 설명과 같이

t 타임 스텝에 디코더 출력이 t+1 타임 스텝의 입력으로 들어가게 된다.

 

그러면 학습할 때는 어떻게 할건데?

아직 엉망인 디코더의 출력을 다음 스텝의 입력으로 넣을 순 없다.

 

따라서 디코더의 이전 출력은 무시하고,

실제 정답을 강제로 넣어주는 과정이 필요한데,

이를 Teacher Forcing이라고 한다.

 

Teacher Forcing 으로도 어쨌든 학습이 될 텐데,

 

학습 과정에서도 예측 할 때 처럼, 이전 스텝의 정보까지 같이 활용할 수는 없을까?

해서 나온 방법이 Input Feeding이다.

아래 그림과 같이 t 타임 스텝의 최종 히든 스테이트 값, h tilde 값을

다음 스텝의 토큰 임베딩 값에 concat시켜버리기.

 

이렇게 해주면 학습과 예측 방식의 괴리를 어느정도 보완해줄 수 있다.

또, 예측 단계에서 argmax 취한 가장 확률 높은 토큰 인덱스가 다음 스텝의 인풋으로 들어갈 텐데

argmax를 취하는 단계에서 발생하는 정보 손실까지 보완이 가능해진다.

 

Input feeding (김기현님 언어처리 자료)

 

 

추론 방법은 다음 글을 참고..

 

Seq2Seq & Beam Search

앞서 Seq2Seq의 구조와 학습 방법에 대해서 알아봤었다. Seq2Seq, Auto Regressive, Attention, Teacher Forcing, Input Feeding 최근에 번역 모델을 직접 구현해보면서 공부하게 된 구조 seq2seq. 개념만 알고 직..

aimaster.tistory.com