기존의 attention은 LSTM, GRU 기반의 seq2seq 모델의 추가적인 add on 모듈로써 사용됬었다면
여기선 seq 데이터를 입력 혹은 출력으로 처리할때 필요로하는 LSTM, GRU, RNN 모델 자체를 attention 만으로 사용하여 모델을 대체했다
Bi-Directional RNNs
앞서 봤던 RNN의 경우는 항상 그 정보가 왼쪽에서 오른쪽으로 흘러가는 즉, 왼쪽에서 나타난 단어에서는 오른쪽에서 등장하는 단어에대한 정보를 참조해서 필요한 정보가 있다면 그 정보를 i 에 해당하는 encoding hidden vector에 담을 수는 없게 된다
이는 왼쪽에서 오른쪽으로 주어진 sequence를 encoding을하는 RNN 모델의 특성 때문이다
하지만 정보의 진행방향 혹은 sequence 방향을 왼쪽에서 오른쪽(forward RNN) 이라고 할때 주어진 동일한 sequence 대해서 오른쪽에서 왼쪽으로 (backward RNN) encoding 을 하는 방식도 생각할수 있다
이런 경우에서는 forward RNN에서 사용되는 RNN 모델의 파라미터와는 다른 별개의 파라미터를 독립적으로 가지는 또 다른 RNN을 구성하게 된다
아래 예시를 보면 'I'에 대한 hidden state vector는 오른쪽에서 부터 생성된 단어였던 'home', 'go' 에 해당하는 정보들을 담을수 있게 된다
그렇다면 가령 'go'의 경우 forward RNN에서는 'go'에 왼쪽에 등장했던 단어들의 정보까지를 encoding한 hidden state vector를 만들어낼수 있게 되고
backward RNN에서는 'go'에 오른쪽에 등장했던 단어들의 정보까지를 encoding한 hidden state vector를 만들어낸다
그렇다면 각 단어별로 encoding vector를 구할때 encoding vector가 항상 왼쪽에 있는 정보뿐만 아니라 오른쪽에 있는 정보도 같이 포함할 수 있도록 forward RNN과 backward RNN 두개의 모델을 병렬적으로 만들고 거기서 나타난 특정한 time step의 hidden state vector를 두 개의 모듈에서 가져와서 두 벡터를 concat 해주므로써 hidden state vector의 dimension에 두 배에 해당하는 sequence 전체의 정보가 반영된 벡터라고 볼수 있다
Transformer: Long-Term Dependency
그렇다면 주어진 sequence에 대해서 각 단어별로 전체 sequence내에 문맥을 잘 반영한 encoding vector를 만들어낸, RNN모델을 대체하는 용도로, transformer에서 소개된 attention 모듈의 구조와 작동방식을 알아보자
위에서 'I go home' 라는 sequence가 주어졌다고 할때 세개의 단어에 대한 input vector가 주어진다면 출력으로써 나오는 벡터들은 input sequence에서 주어진 각 단어별로 sequence 전체 내용을 잘 반영하고 encoding한 encoding vector가 output 으로 나오게 된다
이런 측면에서는 RNN에서 주어진 sequence내에 각 단어들이 sequence 주변에 발견되는 정보를 반영해서 encoding한 벡터를 단어별로 생성해주는것 처럼 attention 모델에서도 입력과 출력의 세팅은 동일하게 유지된다
구체적으로 어떤방식으로 attention을 적용해서 각각의 단어를 sequence 전체내용을 보고 encoding vector를 만들어내는지를 알아보자
attention이 적용되는 방식은 앞서 배운 seq2seq with attention에서 사용되는 방식과 거의 동일하다
seq2seq with attention 방식:
가령 'I' 라는 단어에 대한 encoding vector를 만든다고 하면 'I' 에대한 input vector가 seq2seq with attention에서 decoder 에서의 특정 time step에 hidden state vector처럼 역할을 해서 'I'의 input vector가 찾고자 하는 정보를 나타내고있는 벡터가 되고
encoder 에서 만들어진 hidden state vector들의 세트가 주어져있을때 각각의 벡터와 내적을 수행해서 유사도를 구한 후 softmax를 취해서 합이 1인 형태의 확률값으로 만들어준 후 다시 encoding vector의 가중치로 부여해서 가중평균을 내는 방식으로 최종적인 attention 모듈의 output vector 혹은 context vector를 계산했었다
Transformer의 attention 방식:
위 과정을 transformer의 attention에 대해서 적용을 해보면 'I'의 vector가 decoder의 hidden state vector라고 생각할때 encoder hidden state vector들의 세트는 3개 단어의 벡터가 동일하게 사용된다
그렇다면 결국 단어 'I'의 벡터가 자기자신과 내적을 한번하고 다른 단어의 벡터들과 내적이 되므로써 주어진 3개의 벡터와의 내적에 기반한 유사도를 구하게 된다
여기서 나타난 내적에 기반한 유사도를 softmax 를 취해서 얻어진 가중치, 가령 0.2, 0.1, 0.7로 구성됬다면, 를 얻어서 3개의 벡터의 가중평균 값이 'I'에 대한 encoding vector로 생가할수 있게된다
그렇게 되면 두 번째 단어인 'go'의 입력벡터를 decoder에서의 두번째 time step의 hidden state vector 처럼 사용한다
여기서도 마찬가지고 'go'의 벡터가 자기자신과 내적을 한번하고 나머지 두개의 벡터들과 내적을 수행 후 나온 유사도 값을 softmax를 통해서 새로운 가중치를 얻어서 3개의 벡터의 가중평균값이 'go'에 대한 encoding vector로 생각할수 있게 된다
기본적으로는 decoder hidden state vector와 벡터를 통해서 유사도를 구하게 되는 encoder hidden state vector들 간의 구별이 없이 동일한 세트에 벡터들 내에서 연산이 적용이 된다는 측면에서 이를 self-attention 모듈이라 부른다
이 과정을 자세히 살펴보면 'I'라는 벡터가 decoder hidden state vector로 쓰였을때 자기자신과 내적이 될 경우에는 보통 유사도가 서로다른 벡터와의 내적을 했을때 보다는 훨씬 더 큰값으로 도출된다
그럼 결국 자기자신에게 큰 가중치가 걸리는 형태로 attention vector의 양상이 나타날것이고 그렇게 encoding을 수행한 결과 output 벡터들도 자기자신의 원래 정보만을 주로 포함하고 있는 벡터들로 표현될것이다
자기자신의 원래 정보만을 주로 포함하고 있는 벡터의 문제를 개선하는 모델을 설명(query, key, values 벡터 설명):
attention이 수행되는 과정을 자세히 보면 주어진 입력벡터에서의 각 단어에 해당하는 벡터가 그때그때 서로 다른 역할을 한다
가령 'I'라는 단어에 대한 encoder hidden state vector를 얻어내기 위해서는 'I'가 가지는 입력벡터가 decoder hidden state vector 인것처럼 주어진 벡터 세트들의 유사도를 구하는 벡터로써 사용이 되었다
이 경우 주어진 벡터들 중에 어느 벡터를 선별적으로 가져올지에 대한 기준이 되는 벡터를 query 벡터라고 표현한다
query 벡터가 주어진 encoder hidden state vector의 각 벡터와 내적을 통해서 유사도가 구해지는데 이 query 벡터와 내적이 되는 각 벡터를 key 벡터라 부른다
결국엔 query 벡터를 통해서 주어진 여러개의 key벡터들 중에 어느 key벡터가 높은 유사도를 가지고 있고 어느 것을 가지고 올지에 대한 정보를 결정해주는 역할을 하는것이 key 벡터에 해당된다
마지막으로 각각의 key 벡터와의 유사도를 구한 후 softmax를 취한 후 나온 가중치를 실제로 적용해서 가중평균을 구하는데 사용되는 재료벡터는 key 벡터와 동일하다 즉, 한번은 유사도를 구하는 key 벡터로 사용되고 또 한번은 구해진 유사도를 바탕으로 가중평균이 구해지는 재료벡터로써 사용되는 이 재료 벡터를 value 벡터라 부른다
결국 한 sequence를 attention을 통해 encoding하는 과정에서 각 벡터들이 query, key, value 이 3가지 역할을 모두 다 한다
Transformer에서 제안된 self-attention 모듈은 동일한 세트에 벡터에서 출발했다 하더라도 각 역할에 따라 벡터가 서로 다른 형태로 변환할수 있도록 해주는 별도의 linear transformation matrix 가 있다
query로 변환될때 사용되는 linear transformation matrix, 그리고 주어진 벡터가 key로 변환될때 사용되는 linear transformation matrix, 그리고 마지막으로 value로 변환될때 사용되는 linear transformation가 각각 따로 정의되어있다
같은 벡터내에서 3가지 역할이 모두 공유가되는 제한적인 형태가 아니라 주어진 같은 벡터에서라도 query, key, value 로 쓰일때에 서로 다른 벡터로 변환될수 있는 확장된 형태가 만들어진다
query, key, value 로의 각각의 linear transformation matrix를 통해서 변환된 벡터를 생각하고 다시 attention 모듈을 적용해보자:
'I'라는 단어를 encoding 한다 생각하면 'I'에 해당하는 입력벡터가 WQ,WK,WV 라는 matrix들에 의해서 query, key, values 벡터들로 변환
key와 value 벡터가 주어졌을때 주의할점은 첫번째 key는 첫번째 value 벡터와 대응된다
그렇기 때문에 query 벡터는 하나로 고정되있지만 key와 value 벡터는 갯수는 동일해야 한다
query 벡터와 각각의 key벡터의 내적값이 구해지고 이 내적값에 softmax 를 취해서 합이 1인 형태인 확률값을 얻어낸다
위에 그림을 보게되면 비록 첫번째 단어로부터 만들어진q1,k1 임에도 불구하고 해당하는 내적값이 다른 key와의 내적값이 더 작을수 있게 된다
query와 key로의 서로다른 변환이 존재하기 때문에 같은 벡터를 가지고 query와 key를 만들었을때에도 유연한 유사도를 얻을수 있게된다
얻어진 가중치는 value 벡터에 부여되는 가중치가 되어서 가중평균에 결과로써 최종적인 벡터가 해당 단어의 output(encoding) 벡터이다
sequence의 길이가 길다 하더라도 self attention 모듈을 적용해서 sequence 내에 각각의 단어들을 encoding(output) 벡터로 만들게 되면 time step의 gap 이 먼 경우에도 동일한 key, value 벡터들로 변환이 되고 query벡터에 의한 내적에의한 유사도만 높다면 time step 차이가 멀었다 하더라도 멀리있는 정보를 손쉽게 가져올수 있다
결론적으로 self-attention 모듈은 long-term dependency 문제를 근본적으로 깔끔하게 해결한 sequence encoding 기법이다
Transformer: Scaled Dot-Product Attention
위 과정을 수식적으로 살펴보자
attention 모델의 입력 : query q, key와 value 벡터의 쌍으로 이루어진 다수의 (k,v) pair
query 벡터로 encoding한 output 벡터는 value 벡터에 대한 가중평균이 된다
가중평균에서 쓰이는 가중치는 query 벡터와 value 벡터에 해당하는 key 벡터와의 내적값을 통해서 구해진다
query 벡터와 key 벡터는 내적 연산이 가능해야하기 때문에 같은 차원의 벡터 dk 여야 한다
value 벡터의 경우는 query, key와 차원이 꼭 같지는 않아도 된다
상수배를 해서 가중평균을 내는것이기 때문에 애초에 query, key 벡터의 차원과는 같지 않아도 모듈이 실행되는데는 문제가 없다
dimensionality of value vector : dv
A(q,K,V)=∑i∑jexp(q⋅kj)exp(q⋅ki)vi
q : query 벡터 하나
K,V : key, value 벡터 pair들의 집합
대문자 : 각각 단어들의 벡터들을 합쳐놓은 행렬
최종적인 차원 : dv
attention 모델의 입력과 출력의 관계를 더 확장한 형태:
when we have multiple queries q, we can stack them in a matrix : Q
the equation above becomes:
A(Q,K,V)=softmax(QKT)V
query 벡터도 여러개가 주어져서 각각의 query에 대한 attention 모듈의 output 을 계산해야하는 상황에 query를 모아서 row 벡터로써 행렬을 구성한 후 위와같은 행렬연산을 하면 각각의 query 벡터에 대한 attention 모듈의 output 벡터를 한번에 계산할 수 있다
여러 query 벡터의 대한 attention 모듈의 계산을 행렬연산으로 바꾸면 행렬연산을 빠르게 병렬화해서 계산할 수 있는 GPU를 활용해서 효율적이고 빠르게 수행할 수 있다
병렬적인 행렬이 가능하다는 특성을 통해서 transformer 모델은 이전의 RNN 모델의 비해서 상대적으로 학습이 빠른 특성을 가직 된다
example from illustrated tranformer :
dk : scale
query 와 key 벡터의 내적을 계산할 때 추가적으로 붙는 scaling 과정인 dk (각각의 내적값을 나누어주는) 의 역할
기본적으로 QKT에서 나오는 각각의 element 들은 특정 query 벡터하나와 특정 key벡터 하나가 내적의 형태로 계산된다
내적에 참여하는 query 와 key 벡터의 dimension의 따라 내적값의 분산이 크게 좌지우지 될수 있고
또 이에 따라 softmax로 부터 나오는 확률분포가 어느 한 key에만 100%몰리는 극단적인 확률이 나올지 혹은 전반적으로 모든 key가 고른 확률값을 부여받은 형태로 나올지에 대한 softmax의 확률분포의 패턴이 의도치않게 query 와 key의 dimension때문에 영향을 받게된다
그렇게 때문에 내적값의 분산을 일정하게 유지시켜주므로써 학습을 안정화시켜주기 위해서 계산된 내적값의 해당dimension 만큼으로 값을 나누어주는 연산을 추가적으로 넣게 된다
나누어주게 되면 분산의 측면에서는 분산이 줄어들기 때문에 최종적으로 QKT의 각 element가 실제로 query 와 key 벡터의 dimension과 상관없이 분산이 일정하게 1인 형태로 유지되는 값이 나오게된다
softmax 의 output인 확률분포값이 한쪽으로 몰리는 정도가 의도치않게 dimension의 영향을 받지 않도록 scaling을 추가해줘야한다
결과적으로 softmax의 값이 한쪽으로 몰리게 되는 경우에 학습을 진행할때 backpropagation때 gradient vanishing이 많이 발생해 학습이 전혀 되지않는 상황이 발생할 위험이 있다