데이터 한 그릇

어텐션(Attention) 본문

NLP/밑바닥부터 시작하는 딥러닝

어텐션(Attention)

장사이언스 2022. 1. 25. 12:40

어텐션의 구조

지금까지 배운 seq2seq를 한층 더 강력하게 하는 `어텐션 메커니즘` 을 알아볼 것.

먼저 seq2seq 의 문제를 살펴보고 그 후에 어텐션의 구조를 설명.

seq2seq의 문제점

먼저 다시 복습할겸 Encoder의 원리로 돌아가면,

Encoder 에 입력 데이터, 시퀀스 데이터를 넣어주면 LSTM 계층을 거쳐서 입력 데이터를 고정된 길이의 벡터로 만들어준다.

여기서 고정된 벡터가 효율적으로 보였지만 사실 이게 큰 문제다. 인코더에서 입력 데이터를 고정된 길이로 바꾸기 때문에 손실된 정보들이 존재한다. 즉, 문장이 아무리 길어도 고정된 길이의 벡터로 바꾸기 때문에 손실되는 정보가 발생한다.

 

따라서 이러한 이유로 Encoder 를 개선해애ㅑ 한다.

(문제 1 : Encoder 가 입력 시퀀스 데이터를 고정된 길이의 벡터로 변환하는 것)

Encoder 개선

Encoder 개선의 가장 큰 포인트는 입력 문장의 길이에 따라 출력의 길이도 바꿔주는 것이다.

본래는 Encoder 의 마지막 히든 스테이트, 은닉 상태를 입력 문장의 벡터로 사용했다.

그러나 하나의 고정된 벡터의 길이를 개선하기 위해서, Encoder 의 모든 시각의 LSTM 계층에서 생성된 은닉 상태를 그 문장의 벡터로 추가한다.

예를 들어서 5개의 LSTM 시각이 존재한다면 각각 히든 스테이트를 도출하고 그걸 행순으로 이어 붙인다.

 

A = 0000, B = 1011, C = 1111, D = 1101, E = 1110 으로 각각의 LSTM 시각대로 히든 스테이트가 나왔다면,

이를 수직으로 이어분인다.

 

[[0000]

[1011]

[1111]

[1101]

[1110]]

 

이렇게 되면 Encoder 로부터 하나의 고정된 길이의 벡터의 제약에서 벗어날 수 있다.

(각 프레임워크별로 Encoder로부터 하나의 히든 스테이트를 사용할지 여러 개의 히든 스테이트를 사용할지 정할 수 있다.)

 

근데 각 히든 스테이트가 나오는 원리를 생각해보면, 각 입력 데이터가 들어갈 때 그 입력데이터에 영향을 많이 받은 채로 히든 스테이트는 생성이 된다.

따라서, 각각의 히든 스테이트는 해당되는 시각의 LSTM 입력값(단어)의 성분이 많이 들어간 히든 스테이트(벡터) 라고 할 수 있다.

 

이로써 Encoder 는 입력 문장의 길이에 비례한 정보를 인코딩할 수 있게 됐다.

Decoder 개선(1)

Decoder 개선은 해야될 게 많아서 파트를 나눈다.

 

먼저 본래의 Decoder는 Encoder 의 마지막 LSTM 히든 스테이트만을 이용했었다.

이제 Encoder를 개선해서 Encoder 에서 만들어진 입력 문장의 벡터는 하나의 히든 스테이트만으로 이루어진 게 아니기 때문에 Decoder 에서도 이 여러 개의 히든 스테이트를 전부 활용할 수 있게 바꿔야한다.

**즉, 기존보다 더욱 더 풍부해진 입력 문장의 정보를 활용할 수 있게끔 Decoder 를 개선해야한다.**

 

들어가기 앞서 우리가 영어 문장을 번역하는 과정을 자세히 생각해보자.

즉, 인간의 seq2seq 원리를 직관적으로 한 번 생각해보자.

먼저 I am a cat 이 있을 때, 우리는 직관적으로 "I" 와 "cat" 의 대응되는 한국 단어 "나", "고양이" 를 떠올릴 것이다.

그리고 이를 토대로 번역할 것이다. 즉, 어떤 단어에 집중하여 그 단어의 변환을 수시로 하게 될 것이다.

(i와 cat과 관련된 한국 단어에 집중하여 번역을 할 것.)

그렇다면 seq2seq 에도 입력과 출력의 여러 단어 중 어떤 단어끼리 서로 연관되어 있을지에 대해 학습할 수 없을까?

 

입력과 출력의 여러 단어들 중, 어떤 단어끼리 서로 연관되어 있을까?

앞서 개선한 h를 Decoder의 첫 lstm 이 받아들이고 입력값을 받아들인 후 출력 값이 "I" 가 나왔다고 해보자.

그러면 Encoder 로부터 받은 h에서 I에 해당하는 벡터(히든 스테이트) 를 선택하면 된다.

하지만 이는 선택의 방법이기 때문에 미분이 되지 않는다. (미분이 되지 않으면 오차 역전파를 사용할 수 없다.)

 

따라서 단어 하나를 선택하는 게 아니라 h단어(Encoder로부터 온)를 모두 선택한다.

그리고 각 단어에 대해서 얼마나 중요한지 나타내는 가중치를 구한다.

 

Encoder h 벡터와 가중치 벡터를 곱해서 (모든 단어들) 가중합을 구한다. 이때 도출된 결과 벡터를 맥락 벡터라고 부른다.

만일 "나는 고양이로소이다" 의 h 행렬 중에서 "나" 에 가중치가 크게 부여됐다면, 앞서 도출된 맥락 벡터는 "나" 의 성분이 많이 포함된 맥락 벡터다.(이게 선택된 벡터) 즉, "나" 벡터를 선택하는 작업을 이 가중합으로 대체하고 있다고 할 수 있다.

(나 벡터를 선택하는 작업은, Decoder 한 계층이 "I" 를 출력했을 때, 그것과 관계있는 단어를 Encoder h 에서 선택하는 걸 말한다. 이 선택하는 과정에서 Encoder h 에 가중치를 부여해서 관련있는 Encoder h 중의 단어를 선택한다.)

 

(코드와 벡터 곱 형상에서 형상을 바꾸는 코드 과정은 밑바닥 책 참조)

Decoder 개선(2)

우리는 가중치 a를 통해서 맥락 벡터 c를 도출했다. (이를 통해서 Encoder h 벡터 단어 중에서 관련 단어 한 개를 선택했다.)

그렇다면 Encoder h에 부여했던 가중치들은 어떻게 구하는걸까?

 

이 과정을 이해하기 위해서는 우선 Decoder 의 첫 번째 시각 LSTM 은닉 상태(h) 벡터가 도출되는 과정을 살펴봐야한다.

지금 우리의 목표는 Decoder 첫 LSTM 의 은닉 벡터가 Encoder h의 각 단어 벡터와 얼마나 유사한지 수치로 나타내는 것.

두 벡터간의 유사도를 비교하는 일반적인 방법으로는 두 벡터를 내적하면 된다. 내적을 통해서 두 벡터가 얼마나 같은 방향을 향하고 있는가를 알아내려고 한다.

 

따라서 Encoder h(앞으로 hs라고 하자) 각각의 단어 벡터에 Decoder 은닉상태 h 벡터(첫 시각 LSTM 출력 벡터) 를 곱해준다.

그러면 hs 의 각 단어 벡터와 h벡터의 유사도가 도출된다. 그 값을 s라고 하자. (이 s는 정규화 되기 전)

Softmax 함수를 이용해서 s를 정규화시킨다. 그러면 각각의 확률이 도출되는 a가 나온다.

이게 바로 가중치 a.

 

정리하면, Decoder 에서 출력되는 단어와 관련있는 hs안의 단어를 선택하는 과정은 hs 단어들에 각각 가중치를 곱해서 가장 큰 값을 기록하고 있는 단어와 관련있는 맥락 벡터를 만드는 과정인데, 이때 가중치를 hs 각각의 벡터와 Decoder h 벡터의 내적합 그리고 Softmax 정규화 과정을 통해서 구한다.

Decoder 개선(3)

앞서 Decoder 개선 (1) 을 통해서 만든 함수(밑바닥 책에서 만든 함수 = Wegithed sum)

Decoder 개선(2) 를 통해서 만든 함수(밑바닥 책에 만든 함수 = Attention Weight) 를 이용해서 Attention 함수를 만들려고 한다.

Weighted sum 함수는 가중치 a와 hs 간의 가중합을 하여 맥락 벡터 c를 구하는 함수다.

Attention Weight 함수는 위에서 사용하는 가중치 a를 구하는 함수로, Encoder 가 출력하는 각 단어의 벡터 hs에 주목하여(나는 기준으로 하여로 이해했다. hs각 벡터를 기준으로 해서 Decoder 에서 나온 h와 얼마나 유사한지 구한다.) 해당 단어의 가중치 a를 구한다.

 

결국 정리해서 어텐션 기술의 핵심은 다음과 같다.

Encoder 로부터 전달된 hs벡터 중에서 중요한 원소에 주목하여(Decoder 입력값과 유사한 원소) 맥락 벡터를 구하고, 그 값을 위쪽 계층에 전달한다.(밑바닥 354pg 참조)

 

최종적인 Attention 구조

Attention Weight(hs, h) - a(가중치) 도출

Weighted Sum(hs,a) - c(맥락벡터) 도출

이 둘을 합쳐서 Attention 계층.

 

LSTM 과 Affine 계층 사이에 넣어주면 된다.

Encoder 로부터 도출된 hs는 모든 시각의 Attention에 적용된다.

Affine 계층은 Attention 으로부터의 맥락 벡터 c 와 해당 시각 LSTM 의 은닉 상태h를 입력값으로 받는다.

이 두 개 벡터를 입력받는다는 것은 역시 두 벡터를 연결(concatenate) 한 값을 입력 받는다는 의미다.

어텐션을 갖춘 seq2seq 구현

밑바닥 코드 참조

 

어텐션 평가

정확도 계산해보면, Peeky 를 사용한 것보다 Attention 이 성능이 더 좋다.

각 시간의 attention 가중치를 저장학 있으므로 이를 기준으로 시각화 할 수 있다.

그러면 기계가 어떤 단어의 대응 관계를 잘 파악하고있는지 확인할 수 있다.

(잘 집중해서 찾아냈는지)

어텐션에 관한 남은 이야기

지금까지 Attention 에 대해서 살펴봤는데, 다루지 못한 주제 몇 가지를 더 다뤄보겠다.

양방향 RNN

seq2seq 의 Encoder 에 초점을 둬보자.

앞에서 seq2seq 모델의 Encoder 를 개선해서 문장의 고정된 벡터의 길이를 보완했다.

마지막 시각의 LSTM 의 은닉상태를 최종 출력값으로 결정하는 게 아니라, 각 시각의 LSTM 의 은닉상태를 한데 모아서 마지막 출력으로 결정한다. 따라서 마지막 은닉상태 hs 의 각 행은 각 행에 대응하는 단어의 성분이 많이 포함되어있다.

 

lstm 의 구조를 생각해보면서, 만일 "나는 고양이로소이다." 라는 입력데이터가 Encoder 에 들어갔다고 가정해보자. 마찬가지로 hs가 구성이 될텐데, 고양이에 해당되는 벡터는 "나","는" 의 정보가 들어갔을 것이다. 이때 전체적인 균형을 생각해서 "고양이" 에 들어가는 주변정보를 균형있게 넣고 싶을 수 있다. 즉, 왼쪽에 있는 주변 맥락 정보 뿐만 아니라 오른쪽의 맥락 정보도 사용하고 싶을 수 있다. **그래서 LSTM 을 양방향으로 처리하는 방법을 생각할 수 있다.**

 

양방향 LSTM 의 구조는 순전파 방향의 LSTM 계층 뿐만 아니라 역방향으로 흐르는 LSTM 계층을 새롭게 만들면 된다. 순전파 방향의 LSTM 은닉 상태와 역전파 방향의 LSTM 은닉상태를 연결(혹은 합, 평균) 한 값을 각 LSTM 시각의 최종 은닉상태로 결정한다. (hs 를 말하는 최종상태가 아니라, lstm 계층의 각 시각.)

 

이처럼 양방향으로 처리하면서, 각 단어에 대응하는 은닉상태 벡터에는 좌우 양방향의 맥락 단어 정보들이 담길 수 있다. (균형잡인 인코딩 가능)

Attention 계층 사용 방법

앞서 우리가 만든 Attention 모형은 LSTM 계층과 Affine 계층 사이에 Attention 계층을 설정했다. (LSTM < Attention < Affine) Affine 계층은 LSTM 에서 만든 은닉 상태와 Attention 에서 만든 맥락 벡터를 입력값으로 받았다. 하지만 Attention 계층을 꼭 LSTM 과 Affine 사이에 두어 그 출력값을 Affine 계층에 전달할 필요는 없다.

 

다른 방법으로는 Attention 계층의 출력값을 다음 시각의 LSTM 계층에 전달할 수도 있다.

seq2seq 심층화와 skip 연결

실제 번역 등 현실에서의 애플리케이션들은 풀어야 할 문제가 훨씬 복잡하다.

따라서 문제를 더 심화시킬 필요가 있는데, Attention 을 심화시키는 방법 중 하나는 Attention 층을 깊게 만들어서 표현력을 풍부하게 만드는 것이다.

 

앞서서는 Encoder 와 Decoder 의 LSTM 계층이 얕았는데, 표현력을 풍부하게 한 seq2seq 모델에서는 Encoder 와 Decoder 의 LSTM 계층을 깊게 쌓을 수 있다. (보통 Encoder 와 Decoder 의 LSTM 층은 통일하는 게 일반적이다. Encoder 가 3층이면 Decoder 도 3층)

 

여기서 Decoder 의 Attention 출력 값의 흐름에 대해서 살펴보면, Attention 은 첫 번째 LSTM 계층으로부터 은닉상태를 입력값으로 받고 그 출력 값을 첫 번째 LSTM 계층 이후의 두 번째, 세 번째, n번째 LSTM 그리고 Affine 계층으로 값을 전달한다.

 

이렇게 층을 깊게 할 때 사용되는 중요한 기법이 존재하는데, 바로 **"skip 연결"** 이다. skip 연결은 계층을 넘어 "선을 연결" (기존 입력값 이외에 새로 선하나 추가하는데 그게 건너 뛰어서 연결)하는 단순한 기법이다.

 

이때 skip 선의 값과 원래의 계층을 거쳐서 나타난 값이 더해지는 "+" 접속부가 있는데, 이 부분이 핵심이다.

오차역전파시 "+" 는 기울기를 그대로 흘러보내는데 skip 연결의 기울기가 아무런 영향을 받지 않고 모든 계층으로 흐르게 된다.(양갈래 다, 375pg) 따라서 층이 깊어져도 기울기가 소실 혹은 폭발되지 않고 전파된다.

어텐션 응용

구글 신경망 기계번역(GNMT)

기계 번역의 역사

규칙 기반 번역 -> 용례 기반 번역 -> '통계 기반 번역' -> 현재는 신경망 기계 번역(NMT, Neural Machine Translate)

 

구글 번역은 실제로 신경망 기계 번역을 사용 중이다. 이 신경망 이름은 **구글 신경망 기계 번역(GNMT)** 라고 불린다.

 

우리가 만든 모델에서 더욱 더 심화해서 모델을 개선했는데, LSTM 계층의 다층화, 양방향 LSTM, skip 연결 등등을 시도했으며 그 이외에도 낮은 빈도의 단어 처리, 추론 고속화를 위한 양자화 등 다양한 연구가 이뤄지고 있다.

 

트랜스 포머

RNN 은 이전 시각에서 계산한 결과를 이용하여 순서대로 계산한다.

따라서 RNN의 계산을 시간 방향으로 병렬 계산하기란 (기본적으로는) 불가능하다.

따라서 최근 연구는 RNN을 사용하지 않거나, 병렬 계산할 수 있는 RNN 연구가 이루어지고 있다.

이러한 연구들 중에서 유명한 것이 "Attention is all you need" 논문에 나온 **트랜스포머** 기법이다.

논문 제목에서 말하듯이 모델은 RNN 을 사용하지 않고 어텐션을 사용해 처리한다.

(트랜스포머 말고도 RNN 제거 연구는 활발히 이루어지고 있는데, 그 중에 다른 하나는 합성곱 계층을 이용하는 연구가 있다. 합성곱을 진행하게 되면 계산을 병렬 처리가 가능하다.)

 

논문에서 제안한 트랜스포머는 어텐션으로 구성된다. 그 중에서 ***셀프어텐션(Self-Attention)*** 기술을 이용하는 게 핵심이다. (자신에 대한 주목)

하나의 시계열 데이터를 대상으로 하는 어텐션. 하나의 시계열 데이터 내에서 각 원소가 다른 원소들과 어떻게 관련되는지를 살펴보는 취지.

 

본래의 우리가 배웠던 Attention 구조를 다시 상기해보자. Attention 에 들어오는 입력값들에 대해서 다시 생각해보자. Attention 에 들어오는 입력값은 Encoder 로부터 들어온 시계열 데이터 그리고 LSTM 으로부터 들어온 시계열 데이터였다. (두 입력값은 다른 시계열 데이터로부터 나온다.)

하지만 셀프 어텐션(Self-Attention) 은 두 입력선이 모두 하나의 시계열로부터 나온다. (380pg)

이렇게 하면 하나의 시계열 데이터의 각 원소들의 대응 관계가 구해진다.

뉴럴 튜링 머신(NTM)

외부 메모리를 활용한 신경망 확장 연구 예에서는 메모리를 읽고 쓰는 데 어텐션을 이용했다.

(사람이 공부할 때, 정보를 글로 쓰고 읽는 것처럼)

'NLP > 밑바닥부터 시작하는 딥러닝' 카테고리의 다른 글

RNN을 사용한 문장 생성(seq2seq모델)  (0) 2022.01.28
게이트가 추가된 RNN  (0) 2022.01.21
순환신경망(RNN)  (0) 2022.01.20
word2vec 개선  (4) 2022.01.19
word2vec  (0) 2022.01.18
Comments