questionet

Attention, self-attention, transformer 본문

Deep learning/NLP 모델 설명

Attention, self-attention, transformer

orthanc 2021. 3. 14. 14:55

LSTM, GRU에 이어서 questionet.tistory.com/37?category=961868

이 페이지는 밑바닥부터 시작하는 딥러닝 2권과 다음 강의를 정리, 부연한 것이다
www.youtube.com/watch?v=JVLY1S0H6AQ
www.youtube.com/watch?v=YAgjfMR9R_M&list=PL5-TkQAfAZFbzxjBHtzdVCWE0Zbhomg7r&index=13


1 seq2seq with attention 과
2 image captioning with attention 을 통해
attention의 원리에 대해 이해한 후

3 attention자체에 대해 살펴보자. 


1. seq2seq with attention

도착어와 대응관계에 있는 출발어가 뭔지 
seq2seq에게 학습시켜서 스스로 골라내게 할 수 없을까?

다시 말해, 입력단어와 출력단어들 중, 어떤 단어끼리 서로 관련되어 있는지를 
seq2seq에게 학습시켜서 스스로 짝짓게 할 수 없을까?

바꿔 말해, seq2seq가 필요한 정보에만 '주목'하게 할 수 있을까?

더 일반화해서 말하면,
어떤 딥러닝 모델에서 어떤 결과를 추론해 낼 때
그 추론에 가장 필요한 정보를 선택해서 스스로 학습하게 할 수 있을까?
이걸 해내는 게 Attention이다.


먼저 seq2seq 모델에 attention을 적용하는 과정을 통해 Attention의 구조를 살펴보자.

1. 그럼 먼저 해야할 것은 hs에서 특정 단어벡터를 '선택'하게 하는 방법을 구현하는 것이다.

hs는 Encoder에서 매 시각별 LSTM의 은닉상태 벡터 h를 모두 모은 행렬이다.
(기존 LSTM이하 RNN모델의 은닉상태 벡터 h는 벡터 '하나'에 이전 모든 시점의 은닉상태 정보가 담겨 있었다)

즉  hs는 입력된 모든 단어들 각각의 은닉상태가 다 모여 있는 '행렬'이다. 

우리가 원하는 건 decoder의 각 LSTM계층에서 결과를 출력해낼 때
그 결과를 내기 위해 가장 필요한 정보만
hs에서 decoder가 스스로 뽑아내게 하는 것이었다. 

이제 '가중치'라는 벡터 a가 있다고 해보자.
(a가 뭔지는 이 다음에 설명돼있다. 지금은 decoder가 hs에서 적합한 정보를 '선택하는 방법'을 먼저 살펴본다)

 a는 확률분포처럼 각 원소가 0, 1사이의 스칼라이며, 모든 원소의 총합은 1이 되는 벡터다.

a hs를 행렬곱한 값을 
hs에서 decoder가 필요하다고 판단해 뽑아낸 벡터라고 간주하게 되면,

hs에서 특정 단어벡터를 '선택'하게 하는 방법을 구현한 것과 같게 된다.

그 행렬곱의 결과 나오는 벡터를 맥락벡터 c라고 하고
이 연산을 가중합 weight sum 이라고 부르자.

위 예시를 보면
hs는 5 x 4 행렬이다.  a는 1 x 5 벡터로 써서  a와 hs를 행렬곱한 결과가 가중합이 된다.
이때 가중합의 shape은 1 x 4 가 된다. 

이것이 Attention 구조를 이루는 첫번째 요소인 weight sum이다.

2. 그럼 가중치 a 의 값은 어떻게 구할까?
이게 핵심이다.

 

a hs의 행렬곱 연산은 
decoder가 매 시각 출력하는 결과에
가장 중요하다고 판단되는 벡터를
'선택하게 하는' 역할을 했다.

hs의 각 은닉 상태 벡터 중,
추론에 가장 기여도가 큰 벡터를 선택하게 하는 근거를 제공하는 게 a 였다.

그럼 각각의 기여도, 다시 말해 a의 각 원소값은 어떻게 구해야 될까?


Decoder의 LSTM 계층은
Encoder에서 건네 받은 hs와 
자신의 이전 시점 출력결과를
현재 시점의 입력값으로 받아 
매 시각 decoder LSTM계층의 은닉상태 h를 출력한다.

hhs의 각 단어 벡터와 얼마나 비슷한지를 계산하면
그 값을 우리가 구하고자 하는 각 단어 벡터의 기여도로 간주해 볼 수 있다.

유사도를 계산 방법은 hhs의 각 벡터를
내적하는 방법이 있다.


(내적은 두 벡터가 얼마나 같은 방향을 향하고 있는가를 의미하기도 하므로)
questionet.tistory.com/32?category=967355 단어 벡터, 코사인 유사도 참고

'hhs의 각 단어 벡터들이 얼마나 비슷한지'를 구하는 방법은 내적 말고도
유사도 점수를 출력하는 작은 신경망을 사용하는 사례도 있다고 한다.
어쨌든 계산한 결과값(scalar)을

alignment score라고 한다.
alignment란 '단어 혹은 문구의 대응관계를 나타내는 정보'를 뜻하는 용어로 쓰인다.


정리하면,
hs의 각 벡터와 h를 내적하여 구한 각각의 alignment score를
softmax 함수에 통과시켜 normalize를 해준 결과를 
Attention weight라고 한다.

위 예시를 보면
hs는 5 x 4 행렬이다.  hs의 각 벡터와 h를 내적한 결과는 5 x 1 벡터가 된다
(계산의 편의를 위해 hhs와 같은 shape가 되도록 hr 행렬로 reshape 해준다음 원소별 곱(아다마르곱)을 해주었다.)

즉 5 x 1 벡터의 각 원소가 alignment score가 된다.


이 alignment scores를 softmax 함수로 normalize해주는 이유는
그렇게 하여 구한 ahs와 행렬곱 하는 연산이
확률분포에 따라 hs에서 특정 벡터(decoder가 필요하다고 판단한 벡터)
'선택'하는 것을 의미하게 되기 때문이다.


이것이 Attention 구조를 이루는 두번째 요소인  Attention weight 다.

정리해보자.


Attention 작동 과정은 다음과 같다.


1. Encoder가 매 시점에 출력하는 hidden state 를 모은
   hs에서 어떤 은닏상태에 '주목'해야 하는지 알기 위해,
   다시 말해, 각 단어의 가중치 a를 구하기 위해

   Decoder에 전달된 최종 은닉상태벡터 h
   hs의 각 단어 벡터를 내적하여 

   hhs의 각 벡터간의 유사도를 구한다.

2. 이어서 hsa를 행렬곱 연산하여
   hs에서 특정 단어 벡터,
   즉 추론에 가장 기여할 만한 벡터
를 선택한다.
   이렇게 선택된 벡터를 맥락벡터 c라고 한다.

 

3. ch와 연결되어 각 시점 디코더의 출력 y가 되고

   이렇게 y는 매번 다음 시점의 입력값이 되어
   h로 출력되고  
   hs와 함께 attention 계층에서 다시 c를 만들어 
   다음 시점의 y를 출력한다.

 

 

 

 


이를 위해 hs를 모든 Attention 계층에 입력한다.

어텐션의 위치는 다르게 배치될 수도 있다.

위 구조에선 어텐션의 출력인 맥락벡터가 해당 시점에서 추론을 하는 데 쓰이지 않고
다음 시점의 은닉층에 연결되고 있다. 
이 구조는 앞서 살펴본 구조와 달리 어텐션 계층과 decoder의 RNN 계층의 전후위치가 바뀌었다고 볼 수 있다.
바뀐 구조를 통해 어텐션의 역할을 다시 한번 복습해보자.

attention weight를 얻기 위해 필요한 alignment score는
Encoder의 최종 은닉 상태 ht와
매 시점에서의 은닉상태인 h1, h2, ... ht-1의 유사도를 구한 값이었다.

위 그림에선 alignment score를 연산하는 함수를 alignment function(fatt)라 하여
더 일반화해 표현하고 있다. 
(즉 fatt는 h와 hs의 각 벡터를 내적하는 함수일 수도 있고, 유사도를 구하는 작은 신경망 또는
그 밖의 여러 방법이 될 수 있다.)

alignment function이 하는 일은
2 encoder 의 최종 은닉상태S0로 주어졌을 때, 
encoder에서 매 시각 출력된 hidden state들을 각각 얼마나 주목해서 봐야하는 지를 연산하는 것이다.
3 그 연산 결과가 alignment score e1, e2... 이다. 

4 이 score들을 확률 분포로 바꾼 다음
5 encoder의 각 hidden state들의 가중합(weighted sum)을 구한다.
  그렇게 하여 얻은 context vector c1와  입력값 y0 ( 첫 시각에선 start token) 으로 
6 decoder의 새로운 hidden state S1이 계산된다. (여기서 attention 계층과 decoder의 RNN계층 순서가 바뀌었다)
7 S1에서 예측값 y1이 출력된다.
8 S1은 다음 어텐션 계층을 통과해 c2를 만드는데 쓰이고 
9 c2는 다시 입력값 y1과 함께 S2가 계산되는데 쓰인다.


이제 어텐션에 대해 좀 더 깊이 들어가보자.

애초에 seq2seq에 어텐션을 접목시킨 이유가 뭘까?

seq2seq 모델이 예측을 수행할 때 필요한 정보에만 '주목'하게 하기 위해서였다.

그런데 왜 그렇게 해야 했던 것일까?


그것은 seq2seq 의 인코더에서 얻어낸 고정길이벡터가 가진 한계 때문이다.

위 모델은 seq2seq를 약간 개량한 peeky decoder 모델이다.
기존 seq2seq에선 마지막 은닉상태가 decoder의 initial state로만 쓰였다면,
peeky version에선 마지막 hidden state (context vector)를
decoder의 initial hidden state로 쓸 뿐만 아니라
그 이후에도 매 시각마다 LSTM(또는 RNN)계층에 입력되도록 한다. 
이 모델은 시퀀스의 길이가 짧은 경우엔 잘 작동한다.

그러나 일반적인 seq2seq 모델에서 encoder의 final hidden state 에서든
위와 같은 peeky version encoder의 context vector든

decoder가 전달 받는 은닉상태는
어찌됐든 고정된 길이의 맥락벡터이기 때문에

인코더에 입력되는 시퀀스가 아주 길다면
 (짧은 문장 하나가 아니라 paragraph, page, whole book 사이즈의 시퀀스라면)

매 시각을 지날 때마다 
앞 시점의 정보가 제대로 반영되어 축적 되지 않을 가능성은 점점 높아진다.
바꿔 말해 최근 정보만 잘 반영되어 있게 된다.
(위 그림에선 이를 bottleneck problem이라고 표현했다.)


이제 Attention을 쓰게 되면 이 문제가 어떻게, 왜 해결되는지 답 해보자
encoder에 입력된 전체 시퀀스 정보가 응축된 단 하나의 고정길이벡터에 의존할 필요 없이

매 시점마다 decoder에서 새로운 context vector를 생성해냄으로써
encoder에서 생성된 매 시점마다의 hidden state로부터

매 시각 예측에 필요한 정보만 뽑아낼 수 있기 때문이다.

이로써 encoder에 입력되는 시퀀스의 길이도 아무리 길어져도
상관 없게 된다.



이제 어텐션의 작동 과정을 다시 한 번 살펴 보자.




1. 어떤 정보들이 축적돼 있는 데이터 hs가 있다.
   여기서 어떤 정보h
   주의를 기울여야 하는 정보인지 알아내고 a

2. 그 정보를 다시 hs중에서
   선택하게 하는 게 a × hs 

어텐션의 구조였다.

그런데 hhs의 각 벡터들의 정보가 축적된
인코더의 마지막 계층의 은닉상태 벡터이다.
물론 h에는 디코더의 입력 시퀀스 정보도 함께 들어 있다.


그러나 어찌됐든 h hs의 각 벡터의 유사도를 구한다는 건 
어떤 면에선, 그리고 어느 정도는 
자기와 자기 자신의 유사도를 구하는 것처럼 보이기도 한다!

Q1: self attention이라는 개념이 여기서 확장, 발전된걸까?

그리고 더 중요한 사실이 하나 있다.
여기서 어텐션 계층은 input이 'sequence'라는 사실을 전혀 고려하지 않으면서 연산을 수행하고 있다는 점이다.
this attention mechanism does not actually care about the fact the input is a sequence!

hs는 단지 행렬이고, h는 그 행렬의 각 단어벡터와 함께  alignment function의 피 연산항이 될 뿐이다.
(최소한 어텐션 계층에 국한시켜 보면 그렇다)

그렇다면 non-sequential data 를 다뤄야 하는 문제에서도 어텐션을 써도 되지 않을까?

Q2: 더불어 sequential 데이터의 병렬처리 문제도 어텐션 메커니즘에서 해답을 찾을 있지 않을까?


2. Image captioning with Attention

그렇다면 image 같은 non-sequential data를
convolutional layer를 거쳐 출력데이터인 feature map으로 변환시켰을 때, 
우리는 feature map을
encoder의 hs로 간주해볼 수 있다 .
이 때 각 hidden state는 원래 이미지의 형상을 유지한 3차원 데이터로 공간 정보를 담고 있을 것이다.

첫 시점에서 decoder는
grid of features 와 initial hidden state(S0) 를 비교해 alignment score를 만들고 
softmax를 거쳐 attention weights를 계산한 다음
이를 다시 grid of features와 행렬곱 연산하여 맥락 벡터(c1)를 얻어낸다.
c1과 start token(y0) 그리고 S0을 입력 받아
decoder의 새로운 은닉상태 S1을 계산하고
S1은 해당 시점의 output을 내는 한편 다음 시점의 맥락벡터를 얻기 위해 다시 어텐션 계층에 입력된다.

즉 St는
생성하고자 하는 caption에 상응하는 공간정보가
grid of features에서 구체적으로 어느 위치에 있는지를 알려주는 ct를 참고해 매 시각 적절한 output yt를 출력한다.

다시 말해, decoder의 매 시각 hidden state
이전 시점의 은닉상태와 출력값 y 그리고 매 시각 새로 생성되는 context vector를 입력받아 만들어진다. 

즉 어텐션 메커니즘은
전체 sequence 혹은 전체 이미지의 정보가 응축된 행렬에서
매 시각 매번 행렬의 다른 부분(벡터)에 주의를 집중해
원하는 결과를 출력해내는 기능을 한다.

여기서 핵심은 입력되는 input data의 sequential 여부가 
그 이후의 ouput을 내는 처리 과정에서 지속적으로 유지될 필요가 없다는 사실이다 

이것은 인간의 인지과정과 매우 유사하다.
처음에 어떤 정보를 입력받을 땐 sequential한 process를 거쳐야 한다.
하지만 일단 전체 data를 입력받아 응축된 형태의 정보로 변환이 되고나면
그것을 가지고 하게 되는 정신활동들은
처음에 정보가 입력된 순서와 무관하게 이뤄진다.
예컨대 영화를 볼 때는 촬영된 장면들을 순서대로 처음부터 끝까지 일단 봐야 하지만,
그 영화에 대해 평론을 할 땐
특정 장면에 가중치를 두어 순서에 상관없이 선택적으로 조합하는 것처럼 말이다. 

이것이 가능해지려면 
첫째,
전체 데이터의 정보가 최대한 유실되지 않도록 잘 응축하여
확실히 기억해 놓는 것이 중요하다. 
seq2seq 에선 hs가
CNN 에선 feature map이 그 최종 기억에 해당한다. 

지금까지 살펴본 attention mechanism을 보다 일반화하여 표현해보자.

seq2seq에 쓰인 attention을 일반화한다는 건
attention을 통해 seq2seq with attention을 이해할 수 있게 만든다는 것이다.


3. Attention layer

최종기억, 다시 말해 Input의 hidden state들의 집합을 input vectors : X
그리고 현재 시점에서부터 매시각 출력되는 hidden state에 입력되는 바로 이전 시점의 hidden state를 query vector : q
          (구조에 따라선 현재시점에서부터 매시각 출력되는 hidden state일 수도 있다)
'q'와 'X의 각 input vector(Xi)'간의 alignment를 구하는 함수를 similarity function : fatt 
이 함수에 의해 구해진 alignment score를 similarities : e
그리고 이 e에 softmax 함수를 씌워 확률분포로 만든 것을 attention weights : a
이 a와 X를 행렬곱한 결과를 output vector : y 라고 하자.

여기까지는 seq2seq with attention 에서 설명했던 attention 구조와 같다.

여기서 더 일반화 된 attention 을 만들기 위해 첫번째로 할 일은


fatt를 기존의 내적연산(dot product)에서
scaled dot product 연산을 수행하는 함수로 바꾸는 것이다. 
scaled dot product는
qXi를 내적한 값을
q의 차원(Xi의 차원)의 제곱근 : sqrt(Dq)으로
나눠주는 연산이다.



그냥 내적한 값을 바로 쓰지 않고 sqrt(Dq)으로 나눠준 값을 쓰는 이유는 
softmax 함수의 특정 입력값(유사도 ei)이 너무 클 경우 
기울기 소실문제가 발생하기 때문이다. (sigmoid 함수에서 같은 원인으로 기울기 소실이 일어나는 것과 같은 맥락이다)
유사도가 커지는 경우는 qxi의 차원이 매우 클 때일 것이다
따라서 sqrt(Dq)로 나눠주게 되면 내적의 결과를 scale해주는 효과를 얻을 수 있다.

두 번째로 할 일은

기존의 single query vector : q를, q들의 집합 query vectors : Q로 바꾸는 것이다.
다시 말해, 매 시각 출력되는 은닉상태에 해당하는 q들을 하나의 행렬로 모아
X의 전치행렬과 행렬곱을 해버리는 것으로 (행렬곱을 하려면 X를 transpose 해줘야 계산이 맞아진다)
기존에 매 시각 이루어졌던 attention weight연산을 한번에 끝낼 수 있게 하는 것이다.


이게 가능해지는 건 왜일까?

이렇게 한다는게 뭘 의미하는가?

병렬 처리가 가능해진다는 것이다
output도 더 이상 sequential할 필요가 없다는 걸까?

 

세 번째로 할 일은

input vectors Xkeyvalue 두  행렬로 쪼개는 것이다.

attention에서 Input vectors는 두 번 쓰인다.
  한 번은 attention weights를 구할 때 
  다른 한번은 outputs(context vectors)를 구할 때.

learnable weight matrix 두 개를 새로 만든 다음
   learnable key matrix Wk
   learnable value matrix Wv
input vector의 집합 X를 각각 곱해서 
key vectors K와  value vectors V를 만든다.

모델이 찾고자 하는 답을 얻기 위해 Q가 입력 됐을 때
key에서 가장 주의 깊게 봐야하는 정보가 무엇인지 찾기 위해
key의 각 벡터와  Q의 각 벡터를 비교해서 similarities를 구하고
이를 softmax함수에 통과시켜 attention weights를 구한 다음 
이 attention weights 중에 무엇을 선택할지 결정하기 위해
value와 attention weights를 연산한다.

이것이 일반화된 어텐션 레이어다.

왜 X를 그대로 두번 쓰지 않고
X를 사용해 K와 V 두개로 나눴을까?

모델이 input data를 보다 유연하게 사용할 수 있도록 해준 것이다 
모델이 query에 대한 답을 만드는 데 필요한 정보를 찾는 파트와
찾은 정보들에 가중치를 달리 두어 출력하는 파트를 나눈 것이다. 

output이 query에 너무 얽매이지 않게 하기 위해?
query에 대한 답을 하되, 좀 더 다양한 답, 표현력이 더 높은 답을 얻기 위해?


그럼 query를 자기 자신한테 날리는 attention도 가능할까?

input data 안에 있는 벡터들 간의 similarities를 구할 수 있을까?

이게 의미하는 게 뭘까?

이를 위해 세번째 learnable weight matrix Wq가 필요하다
먼저 Input vectors에 이 Wq를 곱해서 query vectors로 만든다.
(바꿔 말해 Q를 X로 만드는 것이다.)
그 다음 과정은 attention layer와 같다.

self-attention layer와 FC layer의 공통점 

 

이제부터 살펴볼 두 가지 기법은
seq2seq with RNN에서 시작된 attention이 일반화 되어
독자적인 새로운 Neural network로서의 self - attention layer가 가능해짐으로써
오히려 주객이 전도됨에 따라 벌어지는 상황을 되돌리기 위한 기법이라고 볼 수 있다.

하나는 positional encoding
다른 하나는 masking이다.

1. positional encoding
self attention이 하는 일은 
어떤 벡터의 집합을 주면
벡터끼리 서로서로 전부 비교한 다음
그 결과를 벡터의 집합으로 돌려 주는 것이다. 
따라서 벡터가 어떤 순서로 주어지는 지는
self attention의 연산에서 별로 중요한 게 아니다. 

따라서 입출력의 순서를 따져야 하는 task에 self attention 모델을 써야할 경우엔
각각의 input data에 순서를 알려줄 수 있다.

2. Masked self-attention layer

Attention layer에서 Multiple query vectors 가 가능해짐으로써
모든 hidden vector를 동시에 사용하고 
그에 따라 output 또한 한번에 모두 낼 수 있게 되었다. 
따라서 이 Attention layer를
이전시점까지의 정보를 바탕으로 다음시점의 output을 추론하거나 생성해야 하는 language model에 사용할 때
미래 시점의 hidden state에 해당하는 것들을 masking 해줘야 할 필요가 생긴다.
(모델이 과거의 정보만 볼 수 있게 하는 것이다)

보지 못하게 할 정보의 alignment score를 마이너스 무한대로 주면
softamax 결과 해당 부분의 attention weight는 0이 된다.

 

 

이어지는 내용은 self-attention, Transformer 에서 계속  questionet.tistory.com/47


'Deep learning > NLP 모델 설명' 카테고리의 다른 글

RWKV,  (0) 2024.01.31
Recurrent Neural Network (RNN)  (0) 2021.03.13
WordNet  (0) 2021.02.25
LSTM, GRU  (0) 2021.02.25
Comments