머신러닝&딥러닝

Deep Learning #6 Attention

seungbeomdo 2023. 3. 2. 15:25

1. Encoder-Decoder 구조

1.1. Encoder-Decoder 구조(=Seq2Seq)

  • RNN이 One-to-Many, Many-to-Many 등의 다양한 구조를 갖는다는 것을 이전 RNN 포스팅에서 살펴보았다. 그 중 한 형태인 Encoder-Decoder 구조는 Many-to-Many 구조를 약간 변형한 것이라고도 볼 수 있는데, 다음과 같은 형태이다.

 

 

  • 인코더-디코더는 이름에서 알 수 있듯이 인코더와 디코더로 이루어진 구조이다. 인코더 파트에서는 주어진 인풋 시퀀스를 히든 스테이트로 변환한다. 디코더 파트는 히든 스테이트를 받아서 아웃풋 시퀀스를 생성한다.

 

  • 왜 이런 구조를 고안해냈을까? NLP의 맥락에서 이해해보자. 가령 한국어 문장을 영어로 번역하는 RNN 모델을 만든다고 하면, Many-to-Many 구조도 사용할 수는 있다. 인풋 시퀀스와 아웃풋 시퀀스를 구성하는 각 벡터들이 문장을 구성하는 단어들의 임베딩 벡터라고 생각하면 된다.
  • 그런데 문장의 각 단어들을 번역하기 위해서는, 단어들을 순서대로 번역하는 것도 한 방법이지만, 우선 문장 전체를 이해한 다음에 각 단어들을 번역했을 때 더 자연스러운 결과를 얻을 수 있다. 이런 관점에서 문제를 해결하는 것이 인코더-디코더 구조이다.

 

  • 인코더 파트에서 우선 한국어 문장의 단어 시퀀스들을 모두 받아 RNN을 통해 최종 히든 스테이트를 생성한다. 이 최종 히든 스테이트에는 문장을 구성하는 모든 단어들의 정보가 농축돼있다. 그리고 디코더 파트에서는 이 히든 스테이트를 영어 문장의 단어 시퀀스들로 바꿔낸다.

 

1.2. Encoder-Decoder의 학습 방법

  • 인코더-디코더 구조의 학습 방법은 일반적인 BPTT와 동일하다. 산출된 아웃풋 시퀀스와 라벨 시퀀스 간의 오차를 역전파시켜 RNN의 가중치 행렬들을 학습한다.

 

  • 그런데 약간의 문제가 있다. 인코더-디코더 구조는 Teacher-Forcing이라는 방법으로 학습을 진행한다. 다음과 같은 구조를 보자.

 

https://doi.org/10.1016/j.chaos.2020.110045

 

  • 인풋 시퀀스들을 받아서 아웃풋 시퀀스의 벡터들의 추정량을 제시하고 있다. 근데 이때 제시된 추정량이 실제 라벨과 일치하든 말든, 그 다음 인풋 벡터로 주어지는 값은 실제 라벨이다. 틀린 아웃풋을 내놓더라도 그때그때 바로 교정해서 인풋에는 영향이 없도록 하는 이러한 학습 방법을 티처포싱이라고 한다.

 

  • 그런데 이것은 train 과정에서만 가능하다. 왜냐하면 train 과정에서는 라벨이 주어져있기 때문이다. 그런데 test에서는? test에서는 라벨이 주어져있지 않다. 따라서 다음과 같이 아웃풋 시퀀스를 제시할 것이다.

 

https://doi.org/10.1016/j.chaos.2020.110045

 

  • 즉 이전 단계에서 제시한 아웃풋의 '추정량'이 그 다음 단계의 인풋이 된다(y에 hat이 붙어있다는 사실에 주목하자). 그런데 이 아웃풋 추정량은 실제 라벨이 아니라 추정량이기 때문에, 인풋으로 들어오는 값에는 일정한 오차가 존재하게 된다. 

2. Attention

2.1. Attention의 기본 아이디어

  • 인코더-디코더의 한계는 다음과 같다. 인코더 파트에서 발생시킨 최종 히든 스테이트만이 디코더 파트의 인풋이 되기 때문에, 결국 인풋 시퀀스 전체의 정보를 모두 반영하지는 못하게 된다. 즉 장기기억의 문제가 존재한다.

 

  • 물론 장기기억의 문제를 완화하기 위해서 LSTM, GRU 등의 기법들이 동원이 되었지만, 그럼에도 이 RNN 구조 자체의 구조적인 문제를 완벽히 해결할 수는 없다. 따라서 제시된 아이디어는, 인코더에서 생성한 최종 히든 스테이트 하나만 전달해주는 것이 아니라, 인코더의 각 시점에서 생성한 모든 히든 스테이트를 다 디코더로 전달해주자는 것이다.

 

  • 기본적인 인코더-디코더 모델에서는 디코더 파트에서 아웃풋 시퀀스의 추정값들을 제시할 때, 최종 히든 스테이트 하나만 가지고 우려먹으면서 모든 아웃풋 시퀀스를 생성했다. 그런데 Attention이 결합된 인코더-디코더에서는 아웃풋 시퀀스를 구성하는 각 벡터를 생성할 때마다 인코더에서 생성한 모든 히든 스테이트들을 참조해서 생성한다.

 

  • 단, 모든 히든 스테이트들을 동일한 가중치로 참조하는 것이 아니고, 해당 단계에서 추정해야 할 아웃풋 벡터와 연관이 높은 히든 스테이트에 집중해서 참조한다. 그래서 이걸 "Attention"이라고 부른다.

 

2.2. Attention Value

  • 그럼 인코더에서 생성한 히든 스테이트들 중에 어떤 것에 더 집중을 해야 할까? 그 가중치를 Attention Value라고 부른다. 아래에서 어텐션값을 계산하는 방법을 설명한다.

 

https://wikidocs.net/22893

 

  • 모델의 큰 그림을 잊지 말자. 인코더에서는 각 단계에서 계산된 히든 스테이트들이 있다. 그리고 디코더도 RNN이므로 각 단계에서 생성한 히든 스테이트들이 있다. 어텐션이란, 현재 단계에서의 디코더 히든 스테이트와, 인코더 히든 스테이트 간의 유사도에 따라서 결정되는 가중치이다. 유사도가 높은 인코더 히든 스테이트에 더 많은 가중치를 둔다.

 

  • 기본 아이디어가 이렇다는 것을 이해하면 나머지는 그냥 계산일 뿐이다. 먼저 말을 조금 줄이기 위해서 인코더 히든 스테이트들을 Key, 현재 단계에서의 디코더 히든 스테이트를 Query라고 부르자. 여기서는 Key가 LSTM 셀 위에 나오는 붉은 점들이고, Query는 디코더에서 suis 시점에서의 히든 스테이트이다.

 

  • 각 Key들이 Query와 얼마나 유사한지 알아보기 위해서 각각의 Key들과 Query를 내적한다(Key는 모든 시점에서의 그것이므로 여러개이지만, Query는 현재 단계의 그것만을 가리키므로 하나이다).
    • 왜 내적(inner product)을 할까? 내적은 기하학적으로는 두 벡터 간의 사잇각을 구하는 방법이기도 하다. 두 벡터가 유사하다는 것은 변수 공간에서, 원점으로부터 출발한 두 벡터가 이루는 사잇각이 작다는 것을 의미한다. 그리고 사잇각이 작을 수록 내적의 값은 커진다. 따라서 내적이 클수록 두 벡터는 유사하다고 해석한다.

 

  • 그 내적의 결과를 Attention Score라고 부른다. 그리고 Attention score에 softmax function을 취한다. softmax를 취하면 그 값이 확률이 되므로, 이 녀석은 현 단계에서의 아웃풋을 발생시키기 위해 참조할 각각의 Key들에 부여할 가중치인 셈이다. 이를 Values라고 부른다. 혹은 Attention Distribution이라고 부른다. 그림에서는 softmax 함수 위에 솟은 bar plot들이다.

 

  • 마지막으로 Attention value들을 계산했으므로, 이 값의 가중치에 따라 Key들을 합한다. 즉 인코더의 히든 스테이트들을 Attention해야 할 가중치에 따라 합한 것이다. 그 결과를 Attention Ouput이라고 한다. Attnetion Output은 Query, 즉 현 단계의 디코더 히든 스테이트와 concate되어서 현 단계의 아웃풋 벡터를 생성한다.

 

2.3. Bi-directional RNN

  • 그런데 여전히 RNN 구조의 치명적인 한계가 있다. 그것은 인풋 시퀀스를 앞에서부터 순차적으로 입력한다는 것이다. 그런데 문장 내에서의 특정 단어는 그 단어 앞에서 등장한 단어 시퀀스에 의해서 의미가 결정되기도 하지만 그 뒤에 등장한 단어 시퀀스들에도 영향을 받는다.

 

  • 그래서 앞에서부터 입력하는 forward RNN과, 뒤에서부터 입력하는 backward RNN을 엮어서 히든 스테이트들을 사용하자는 아이디어가 있다. 이게 바로 Bi-directional RNN이다.

 

  • 그런데 결국은 다음 포스팅에서 다룰 Transfomer가 이 모든 것을 해결해버렸기 때문에 자세히는 다루지 않는다.