블루아카이브

Visual Transformer

두원공대88학번뚜뚜 2022. 8. 22. 23:42

https://kmhana.tistory.com/28?category=838584 

 

[논문요약] Transformer 등장 - Attention Is All You Need(2017) ①

*크롬으로 보시는 걸 추천드립니다* https://arxiv.org/pdf/1706.03762.pdf 종합 : ⭐⭐⭐⭐⭐ 1. 논문 중요도 : 평가 불가 2. 실용성 : 평가 불가 설명 : 게임 체인저(Game Changer), Transformer 구조의 제안..

kmhana.tistory.com

https://aistudy9314.tistory.com/63

 

[논문리뷰] Attention is All you need

Transformer는 최근 들어 자연어 처리와 비전 분야 모두에서 월등한 성능을 보이면서 발전하고 있다. 이러한 Transformer를 처음으로 제안한 논문이 바로 "Attention is all you need"이 되시겠다 ㅎㅎ. 자연

aistudy9314.tistory.com

https://jalammar.github.io/illustrated-transformer/

 

The Illustrated Transformer

Discussions: Hacker News (65 points, 4 comments), Reddit r/MachineLearning (29 points, 3 comments) Translations: Arabic, Chinese (Simplified) 1, Chinese (Simplified) 2, French 1, French 2, Japanese, Korean, Russian, Spanish, Vietnamese Watch: MIT’s Deep

jalammar.github.io

https://velog.io/@idj7183/Attention-TransformerSelf-Attention

 

Attention, Transformer(Self-Attention)

Attention, Transformer(Self-Attention)

velog.io

https://cpm0722.github.io/pytorch-implementation/transformer

 

[NLP 논문 구현] pytorch로 구현하는 Transformer (Attention is All You Need)

Paper Link

cpm0722.github.io

세 번째 링크의 글을 알아보기 쉽도록 개조했다.

 

트랜스포머의 대략적 구조는 아래와 같다.

참조그림1

더 안으로 들어가면, 인코더와 디코더 구조로 나뉘어있음을 알 수 있다.

부호화(인코딩) 컴포넌트는 인코더의 스택이다. 총 6개로 쌓여있는 걸 볼 수 있는데, 이는 실험결과를 통해 임의로 나타낸 것이다. 따라서 굳이 숫자에 연연할 필요는 없다. 디코딩 컴포넌트의 스택 역시 개수는 같은 것을 볼 수 있다.

 

Encoder Block N개가 쌓여 Encoder을 이루는데, 이는 다음과 같은 의미를 갖는다.

각 Encoder Block은 Input으로부터 들어오는 vector에 대해 더 높은 차원에서의 context(더 추상적인 정보)를 담는다. 이게 무엇이냐, Encoder Block은 한 번 아래의 인코딩 과정을 거치면 원본 문장에 대한 낮은 수준의 Context를 갖게 되는데, 이 output을 기반으로 다시 한 번 context의 context, 또 그 context... 등 점차 높은 차원의 context를 담게 됨으로써 더더욱 추상적 정보를 담게 된다.

참조그림 3

인코더의 구조는 모두 동일하지만, 가중치(W)를 공유하지는 않는다. 인코더는 2개의 서브레이어로 나뉘게 된다(따라서, 위의 인코더는 총 12개의 서브레이어로 구성된다).

인코더의 입력은, 먼저 Self-Attention layer를 통과한다. 이 레이어는 인코더가 특정 단어를 인코딩할 때 입력문 내의 다른 단어를 참조할 수 있도록 도와주는 레이어다. 이것이 정확히 무슨 뜻인진, 후술할 것이다.

 

Self-Attention layer의 출력은, 위에서 볼 수 있듯 Feed-Forward 신경망에 Input으로 들어간다. 이 각 인코더 내의 Feed-Forward 신경망은, 각자의 위치에서 독립적으로 활동한다.

 

디코더에는 이러한 레이어에 더불어, 그 가운데에 디코더가 입력 문장의 관련 부분에 집중할 수 있도록 도와주는 Encoder-Decoder Attention 이라는 레이어가 존재한다(seq2seq 모델에서 주의 레이어와 유사).

텐서 도입

이렇게 모델의 주요 컴포넌트를 확인했으니, 이 컴포넌트 사이를 흐르는 다양한 벡터/텐서의 그 흐르는 방식에 대해 알아보겠다. 이를 통해 트레이닝된 모델의 입력이 출력으로 바뀌게 된다.

일반적으로 NLP 어플리케이션의 경우와 마찬가지로, 임베디드 알고리즘을 사용하여 각 입력 워드를 벡터로 변환하는 것으로 시작한다.

 


맨 위의 참조그림1에 들어가는 단어를 이렇게 조각내었다. 각 단어는 크기 512의 벡터(이 사이즈는 하이퍼파라미터이다)에 삽입된다. 이 간단한 상자가 벡터를 나타낸다고 하자.

각 단어가 벡터(상자)에 삽입되는 과정은,  맨 아래 인코더에서만 이루어진다. 모든 인코더에 공통되는 추상화(Abstraction, 해당 대상이 가져야 하는 핵심 기능을 모델로 만드는 것)은 512 사이즈 벡터 리스트의 수신이다. 이 때, 이 512사이즈 벡터리스트는 가장 맨밑 인코더(Self-Attention)에서는 일종의 워드 임베딩이며, 위의 인코더에서는 바로 아래의 인코더의 출력이 된다(참조그림 3을 보라. 맨 아래의 인코더 입장에서는 연속적 숫자로 수치화된 것이며, 그 위의 입장에서는 아랫것의 출력이 된다).

임베딩이란 범주형(Categorical) 자료를 연속형 벡터로 치환하는 것. 예를 들어 A, B, C, D라는 카테고리가 있다고 할 때, 이를 
A -> [0.1, 0.0, 0.0]
B -> [0.0, 0.0, 1.0]
C -> [0.1, 0.2, 1.0]
D -> [0.2, 0.2, 0.8] 처럼 변환하는 것.
https://blog.linewalks.com/archives/6408#:~:text=%EC%9E%84%EB%B2%A0%EB%94%A9%EC%9D%B4%EB%9E%80%20%EB%B2%94%EC%A3%BC%ED%98%95(Categorical,%EA%B0%99%EC%9D%B4%20%EB%B3%80%ED%99%98%ED%95%98%EB%8A%94%20%EA%B2%83%EC%9E%85%EB%8B%88%EB%8B%A4.

입력 시퀀스에 단어를 삽입한 후 각각 인코더의 두 레이어를 통과시켰다.

여기에서 트랜스포머의 주요 속성 중 하나, 즉 각 위치의 워드가 인코더 내의 자체 경로를 통해 흐른다는 것을 볼 수 있다. Self-Attention 레이어 내의 이 경로 사이에는 의존관계가 있습니다. 그러나 Feed Forward 레이어에는 이러한 의존관계가 없기 때문에, SA와는 달리 FF 내에서는 흐르는 동안 다양한 경로를 병렬로 실행할 수 있다(그래서 SA와 FF가 뭐고, 이렇게 해서 얻는 효과가 뭔데? 하는 건 후술하겠다).

 

한번 예를 더 짧은 문장으로 전환하고, 인코더의 각 서브레이어에서 어떤 일이 일어나는지 살펴보자.

인코딩 중!

이미 언급했듯이, 인코더는 벡터 리스트를 입력으로 받아, 'Self-Attention(이하 SA)' 계층으로 전달하고 Feed-Forward(이하 FF) 신경망에 전달하여 이 목록을 처리한 다음 출력을 다음 인코더로 위쪽으로 보낸다.

각 위치의 단어는 SA과정을 거친다.그런 다음 각각은 FF신경망을 통과한다. 위 그림을 보면 알 수 있듯, 각 벡터가 흐르는 FF신경망은 각각 다르지만, 이는 확실하게 '대승적으로' 보면, 같은 FF 신경망의 독립된 일부들이다.


높은 수준의 자기 주의(SA)

다음 문장을 번역하고 싶은 입력문이라고 하자.

”The animal didn't cross the street because it was too tired”

이 문장에서 "그것(it)"은 무엇을 의미할까? 거리, 아니면 동물?

 

모델은 "it"라는 단어를 처리할 때, SA를 통해 "it"를 "animal"과 연관시킬 수 있다.

모델이 각 워드(입력 시퀀스의 각 위치)를 처리할 때, SA를 통해 입력 시퀀스의 다른 위치를 살펴보고 이 워드의 인코딩(암호화)를 개선할 수 있는 단서를 찾을 수 있다.

 

RNN에 익숙한(familiar) 경우 이해하기 쉬운데, hidden state를 유지하는 것이 RNN으로 하여금 그 자신의 현재 처리하고 있는 단어/벡터와 이전의 처리한 것들을 통합하는 데에 얼마나 도움이 되는지 생각해보라. SA는 트랜스포머가 다른 관련 단어에 대한 "이해"를 현재 처리 중인 단어로 만들기 위해 사용하는 방법이다.


인코더 #5(스택 내 최상위 인코더)에서 "it"라는 단어를 인코딩할 때 주의 메커니즘의 일부는 "The Animal"에 초점을 맞추고 표현 중 일부를 "it"의 인코딩으로 변환했다.
트랜스포머2 모델의 load가 가능하고, 이러한 시각화를 직접 실험해보고 싶다면 Tensor2를 확인하면 좋다고 한다...

 

아무튼, 같은 문장 내의 두 token 사이의 Attention(얼마나 그 단어를 주의깊게 살펴봐야 하는지, 주의도(度))를 평가하는 것이기에 Self-Attention이라 한다.


SA 상세

아니, 대체 SA가 뭔데? 이제 상세히 살펴보겠다. 먼저 벡터를 사용하여 SA를 계산하는 방법을 살펴본 후, 매트릭스를 사용하여 실제로 어떻게 구현되는지 살펴보겠다.

SA를 계산하는  번째 단계인코더의 각 입력 벡터(각 단어의 임베딩)로부터 3개의 벡터, 즉 쿼리벡터, 키벡터, 값벡터를 생성하는 것이다/ 이러한 벡터는 임베딩의 훈련 과정 중에 학습한 3개의 매트릭스를 곱하여 생성된다.

 

이러한 새로운 벡터는 64차원으로, 임베디드 벡터 및 인코더 입출력 벡터의 차원인 512보다 작다. 이 벡터들은 input으로 들어오는 Token Embedding Vector을 FCL(Fully-Connected Layer)에 넣는 것으로 생성된다.

 

그러나 굳이 더 작을 필요는 없으며, 멀티스레드 Attentin 상수의 연산을 일정하게 하기 위한 아키테쳐적 선택이라 한다. (즉, 임의로 지정된 숫자라는 뜻인듯?)

 


임베딩된 x1 WQ 가중치 매트릭스를 곱하면 해당 단어와 관련된 "쿼리" 벡터인 q1이 생성된다(1x4 벡터에 4x3 벡터를 곱해, 1x3 벡터가 생성되었다). 입력 문장으로부터 각 단어의 "쿼리", "키" 및 "값" 투영(projection)을 작성하게 된다.
projection에 대해선 https://m.blog.naver.com/qio910/221783953115

"쿼리", "키" 및 "값" 벡터는 무엇인가? 이는 Attention을 사용하는 데 있어 주요한 추상화(Abstraction)이다. 아래의 Attention 계산 방법을 통해, 각 벡터가 수행하는 역할에 대해 무엇인지 알 수 있다.

 

대략적으로 The animal didn't cross the street because it was too tired을 기준으로 할 때,

Query : Attention 계산에서 기준이 되는 벡터. 가령 It.

Key : Query 벡터와 Compatibility(일치성)이 높은 단어를 찾기 위한 벡터. Street 또는 Animal

Value : 다른 단어와의 Attention이 반영, 최종 Representation을 얻기 위한 벡터. Key와 같은 값이 됨.

 

SA 계산의  번째 단계점수 계산이다. 이 예의 첫 번째 단어인 "Thinking"에 대한 SA를 계산하고 있다고 하자. 이 때, 이 단어에 대해 입력 문장의 각 단어에 점수를 매겨야 한다. 이 점수는 특정 위치에서 단어를 인코딩할 때, 입력 문장의 다른 부분에 얼마나 초점을 맞출지를 결정한다.

 

이 점수는 쿼리 벡터와, 우리가 점수를 매기고 있는 각 워드의 키 벡터의 dot product로 계산된다. 따라서 위치 #1의 단어에 대해 SA를 처리중인 경우, 첫 번째 점수는 q1 k1의 dot product가 될 것이며, 두 번째 점수는 q1 k2의 dot product가 될 것이다.

 

 

 번째 번째 단계는 점수를 8(논문에 사용된 주요 벡터 차원의 제곱근 64)로 나눔으로써, 보다 안정적인 Gradient(기울기) 이 8은 디폴트값으로, 결국 해보니 가장 나은 값이었다고 보면 된다. 그런 다음 softmax 연산을 통해 결과를 전달한다. Softmax는 점수를 정규화하여, 모두 양수이고 합계는 1이 되도록 한다.

 

이 softmax 점수는 각 단어가 이 위치에서 얼마나 표현될지를 결정한다. 이는 위치에 따른, 단어별 다른 주의(Attention)을 기울이도록 하는 효과가 있다. 이 위치에 있는 단어가 소프트맥스 점수가 가장 높은 것은 분명하지만, 어쩌면 현재 단어와 관련된 다른 단어에 주의를 기울이는 것이 유용할 수도 있을 것이다.

 

다섯 번째 단계는, 합산을 위해 각 값 벡터에 softmax 점수를 곱하는 것이다. 이 과정에서, 중요한 벡터는 온전히 유지시키고, 관련 없는 단어(그 값은 0.001같이 작은 수로 귀결되었을 것)는 지워지게 된다.

  1.  

여섯 번째 단계는 앞서 말했듯 가중치 벡터를 합산하는 것이다. Value vector을 곱해준 모든 벡터들을 더해서 나오는 Vector(=z)가, 해당 위치(첫 번쨰 단어)의 해당 언어가 SA를 거친 후 반환할 SA-Layer의 출력값이 된다. 해당 Positon에서의 단어의 Self-Attentinon Output을 출력한다. 

 

이것으로 SA계산은 종료되며, 결과 벡터는 FF 뉴럴 네트워크로 보낼 수 있는 벡터이다. 그러나 실제 구현에서는 보다 빠른 처리를 위해 이 계산이 매트릭스 형태로 이뤄진다. 이제 단어 수준에서 계산의 직관을 살펴보도록 하자.

 

 

 

 

 

 

 

 

 

 

 

SA 매트릭스 계산

첫 번째 단계는 쿼리, 키 및 값 행렬을 계산하는 것이다. 이를 위해 임베딩을 행렬 X에 넣고, 훈련시킨 가중치 행렬(WQ, WK, WV)을 곱한다.


X행렬의 각 행은 입력문 내의 단어에 대응한다. 내장 벡터(512 또는 그림 4 상자)와 q/k/v 벡터(64 또는 그림 3 상자)의 크기 차이를 다시 볼 수 있다.

 

마지막으로 행렬을 다루고 있기 때문에, 2단계부터 6단계까지를 하나의 공식으로 압축하여 SA레이어의 출력을 계산할 수 있다.

매트릭스 형식의 SA계산

 

 

 

 

 

 

 

 

 

 

 

Q는 현재 시점의 token을, K V는 Attention을 구하고자 하는 대상 token을 의미했다. 우선은 빠른 이해를 돕기 위해 Q, K, V가 모두 구해졌다고 가정하자. 위의 예시 문장을 다시 가져와 ‘it’과 ‘animal’ 사이의 Attention을 구한다고 해보자. dk=3이라고 한다면, 아래와 같은 모양일 것이다.

이 때 Q K를 MatMul(행렬곱)하는데, 이는 어떤 스칼라 곲을 나오게 하며, 이것이 곧 둘의 Attention Score이다. 결과값은 어떤 scalar 값이 나오게 될 것이다. 이후 scaling을 수행하는데, 값의 크기가 너무 커지지 않도록 dk로 나눠준다. 값이 너무 클 경우 gradient vanishing이 발생할 수 있기 때문이다. scaling을 제외한 연산 과정은 아래와 같다(해당 과정은 상술함).

지금까지는 1:1 Attention을 구했다면, 이를 확장시켜 1:N Attention을 구해보자. 그 전에 Q, K, V에 대한 개념을 되짚어보면, Q는 고정된 token을 가리키고, Q가 가리키는 token과 가장 높은 Attention을 갖는 token을 찾기 위해 K, V를 문장의 첫 token부터 마지막 token까지 탐색시키게 된다.

 

즉, Attention을 구하는 연산이 Q 1개에 대해서 수행된다고 가정했을 때, K, V는 문장의 길이 n만큼 반복되게 된다. Q vector 1개에 대해서 Attention을 계산한다고 했을 때, K V는 각각 n개의 vector가 되는 것이다. 이 때 Q, K, V vector의 dimension은 모두 dk로 동일할 것이다. 위의 예시 문장을 다시 갖고 와 ‘it’에 대한 Attention을 구하고자 할 때에는 Q ‘it’, K, V는 문장 전체이다. K V를 각각 n개의 vector가 아닌 1개의 matrix로 표현한다고 하면 vector들을 concatenate해 n×dk의 matrix로 변환하면 된다. 그 결과 아래와 같은 shape가 된다.

It이라는 단어를 모든 단어(Key)와 일일이 곱하면서 Attention을 구하고 있다.

그렇다면 이들의 Attention Score는 아래와 같이 계산될 것이다.

그 결과 Attention Score는 1×n의 matrix가 되는데, 이는 Q의 token과 문장 내 모든 token들 사이의 Attention Score를 각각 계산한 뒤 concatenate한 것과 동일하다. 이를 행렬곱 1회로 수행한 것이다.

이렇게 구한 Attention Score는 softmax를 사용해 확률값으로 변환하게 된다. 그 결과 각 Attention Score는 모두 더하면 1인 확률값이 된다. 이 값들의 의미는 Q의 token과 해당 token이 얼마나 Attention을 갖는지(얼마나 연관성이 짙은지)에 대한 비율(확률값)이 된다.

 

이 확률값을 최종적으로 V와 곱하게 되는데, V(Attention을 구하고자 하는 대상 token, 다시 한 번 강조하지만 K V는 같은 token을 의미한다.)를 각각 확률값만큼만 반영하겠다는 의미이다. 연산은 다음과 같이 이루어진다.

이렇게 구해진 최종 result는 기존의 Q, K, V와 같은 dimension(dk)를 갖는 vector 1개임을 주목하자. 즉, input으로 Q vector 1개를 받았는데, 연산의 최종 output이 input과 같은 shape를 갖는 것이다. 따라서 Self-Attention 연산 역시 shape에 멱등(Idempotent)하다. (Attention을 함수라고 했을 때 syntax 측면에서 엄밀히 따지자면 input은 Q, K, V 총 3개이다. 하지만 개념 상으로는 Q에 대한 Attention을 의미하는 것이므로 semantic 측면에서 input은 Q라고 볼 수 있다)

지금까지의 Attention 연산은 ‘it’이라는 한 token에 대한 Attention을 구한 것이다. 그러나 우리는 모든 token에 대한 Attention을 구해내야만 한다. 따라서 Query 역시 1개의 vector가 아닌 모든 token에 대한 matrix로 확장시켜야 한다.

그렇다면 Attention을 구하는 연산은 아래와 같이 진행된다.

연산 그림 1
연산 그림 2

계속 반복되는 이야기이지만, Self-Attention에서 input(Q)의 shape에 대해 멱등(Idempotent)하다.

Q, K, V를 구하는 FC layer에 대해 자세히 살펴보자. Self-Attention 개념 이전에 설명했듯이, 각각 서로 다른 FC layer에 의해 구해진다. FC layer의 input은 word embedding vector들이고, output은 각각 Q, K, V이다. word embedding의 dimension이 d_embed라고 한다면, input의 shape는 n×d_embed이고, output의 shape는 n×dk이다. 각각의 FC layer는 서로 다른 weight matrix (d_embed×dk)를 갖고 있기 때문에 output의 shape는 모두 동일할지라도, Q, K, V의 실제 값들은 모두 다르다.


머리가 많은 야수(The Beast With Many Heads)

이 논문은 "다중 헤드(multi-headed)" Attention라고 불리는 메커니즘을 추가함으로써, SA계층을 두 가지 방법으로 더욱 개선했다.

  1. 다양한 위치에 집중할 수 있는 모델의 능력을 확장한다. 위의 예에서, z1에는 다른 모든 인코딩이 약간씩 포함되어 있지만, 동시에 자기 자신(Self) 역시 포함되어 있기에, 자신에 대한 Score가 너무 높아 다른 단어와의 연동이 필요한 상황에서 제대로 연결이 되지 않는 경우가 있을 수 있다. 만약 우리가 "동물이 너무 피곤해서 길을 건너지 않았다"와 같은 문장을 번역한다면, "그것"이 어떤 단어를 가리키는지 아는 것이 유용할 것이다.
  2. Attention의 레이어에 여러 "표현 하위 공간(Representation Subspaces)"을 제공한다. 다음에 설명하겠지만, 멀티 헤드 어텐션(이하 MHA)에서는 쿼리/키/값 가중치 매트릭스가 여러개 존재한다(트랜스포머는 8개의 어텐션 헤드를 사용하므로 인코더/디코더마다 8개의 세트로 끝난다).이들 세트는 각각 랜덤으로 초기화된다. 그런 다음, 훈련 후 각 세트를 사용하여 입력 임베딩(또는 하위 인코더/디코더로부터의 벡터)을 다른 표현 서브스페이스에 투영한다. 아래 그림처럼, 여러 개의 (Q, K, V) 가중치행렬을 가지기에, 학습 후 각 Vector을 목적에 따라 여러 개의 Representation Subspaces로 나타낼 수 있다.
멀티헤드 어텐션으로 각 헤드에 대해 별도의 Q/K/V 가중치 매트릭스를 유지 관리하여 서로 다른 Q/K/V 매트릭스를 생성한다.이전과 마찬가지로 X에 WQ/WK/WV 행렬을 곱하여 Q/K/V 행렬을 생성한다.

위에서 설명한 것과 동일한 SA계산을 서로 다른 무게 행렬로 8번만 수행하면 8개의 서로 다른 Z 행렬이 된다. 
앞서 설명한 Scaled Dot-Product Attention에서는 Q, K, V를 구하기 위해 FCL가 3개 필요했다. 따라서 여기에선 3x8개가 필요하다. 또한 각각의 최종 연산 output은 n * dk인데(연산그림 1 참조), 총 h개의 n * dk 행렬을 모두 concat해서 n x (dk*h)의 shape를 갖는 행렬을 만들어낼 것이다. 

이때 dk * h의 값을 d_model이라 하자. d_model은 d_embed와 같다고 봐도 무방하다. 기존 설명에서, Q, K, V를 구하기 위한 FC Layer은 d_embed를 dk로 변환했다. 본래라면 이렇게 구한 QKV로 각각의 A를 계산해 concat하는 방식은, 별개의 A 연산을 총 h회 수행해야 한다는 점에서 비효율적일 터이다.

 
따라서, QKV를 n * dk가 아니라, n * d_model로 생성해내서 한 번의 SA 계산으로 n x d_model의 output을 만들어내게 된다. 따라서, QKV 생성에 필요한 d_embed x dk의 가중치행렬을 갖는 FC Layer을 3*h개 갖는 것이 아니라, d_embed x d_model의 가중치 행렬을 갖는 FC Layer을 3개 운용하면 된다.

 

 

FF레이어에서는 8개의 행렬이 아니라, 단일 매트릭스(각 워드의 벡터)를 Input으로 받고자 한다. 따라서 이 8개를 하나의 매트릭스로 압축하는 방법이 필요하다. 이는 행렬을 합성한 다음, 추가 가중치 행렬 WO를 곱하는 방식으로 처리한다.

이를 한 눈에 보게 하면 아래와 같은 그림이 된다. 다중의 가중치행렬 세트를 활용하여 QKV값을 획득하고, 이를 이용해 Z 출력값을 얻어, W0과 곱함으로써 최종 Z를 획득.

 

 

이제 Attention Head에 대해 언급했으므로, 예문에서 "it"라는 단어를 부호화할 때 다른 주의 헤드가 어디에 초점을 맞추고 있는지 살펴보도록 하자.

우리가 "it"라는 단어를 부호화할 때, 한 Attention의 헤드는 "동물"에 가장 집중하는 반면, 다른 Attention의 헤드는 "지친"에 초점을 맞추고 있다. 각 Head가 서로 다른 단어에 집중(Attention)을 한다는 것을 보여준다. 어떤 의미에서는 "it"이라는 단어의 모델이 표현하는 것은 "동물"과 "지친" 모두를 표현한다.

그러나 모든 Attention Head를 사진에 추가하면 다음과 같이 해석하기가 어려워질 수 있다.

위치 부호화를 사용한 시퀀스 순서 표시(Positioning Encoding)

Transformer 구조는 Recurrent Model을 사용하지 않고 Attention Mechanism만을 사용하기에, 두 단어 사이 거리가 멀면 Hidden State Vector에서의 값이 옅어지는 Seq2Seq과 달리, 모든 단어가 연산에 사용되기에 Sequence 정보를 담을 수 없다. 따라서, 별도로 이러한 Sequence 정보를 "Positioning Encoding"을 통해 삽입한다.

Positioning Encoding  수식

입력 시퀀스에서 단어의 순서는 어떻게 되는가? 트랜스포머는 각 입력 임베딩에 벡터를 추가한다.

이러한 벡터는 모델이 학습하는 특정 패턴을 따르며, 이는 각 단어의 위치 또는 시퀀스에서 서로 다른 단어 사이의 거리를 결정하는 데 도움이 된다. 여기서 직감적으로 이러한 값(추가할 벡터)을 임베딩에 추가하면, 임베딩 벡터가 Q/K/V 벡터에 투영(Projection)된 결과와, Dot product Attention 사이의 거리(distance)를 획득할 수 있다고 한다.

 

모델에 단어의 순서를 알기 위해서, 특정의 패턴을 따르는 위치 부호화 벡터를 추가한다.

 

임베딩의 치수가 4라고 가정하면 실제 위치 부호화는 다음과 같다.

내장 크기가 4인 위치 인코딩의 실제 예

이 패턴은 어떻게 생겼을까?

다음 그림에서 각 행은 벡터의 위치 부호화에 대응한다.첫 번째 행은 입력 시퀀스에 첫 번째 단어를 삽입할 때 추가하는 벡터다.각 행에는 512개의 값이 포함되어 있으며, 각 값은 1에서 -1 사이다. 패턴이 보이도록 색상으로 구분했음을 보자.

삽입 크기가 512(열)인 20개의 단어(행)에 대한 위치 인코딩의 실제 예.중앙에서 아래로 반으로 쪼개진 것을 볼 수 있다. 왼쪽 절반의 값은 한 함수(사인 사용)에 의해 생성되고 오른쪽 절반은 다른 함수(코사인 사용)에 의해 생성되기 때문이다. 그런 다음 연결해서 각각의 위치 부호화 벡터를 형성한다.

위치 부호화의 공식은 문서에 설명되어 있다(섹션 3.5).위치 부호화를 생성하기 위한 코드는 https://github.com/tensorflow/tensor2tensor/blob/23bd23b9830059fbc349381b70d9429b5c40a139/tensor2tensor/layers/common_attention.py에서 확인할 수 있다. 위치 부호화에 사용할 수 있는 방법은 이것뿐만이 아나자만, 이는 보이지 않는 길이의 시퀀스로 확장할 수 있는 이점을 제공한다(예: 우리의 훈련된 모델이 우리의 훈련 세트의 문장보다 더 긴 문장을 번역하도록 요청받는 경우).

 

2020년 7월 갱신:위의 위치 부호화는 트랜스포머의 Tranformer2Transformer 구현에 의한 것이다.이 문서에서 보여지는 방법은 직접 연결되지는 않지만 두 신호를 상호 연결한다는 점에서 약간 다르다. 다음 그림은 그 모양을 나타내고 있다.이 코드를 생성하기 위한 코드는 다음과 같다.

잔차(Residuals)

다음으로 넘어가기 전에 언급할 필요가 있는 인코더 아키텍처의 한 가지 세부 사항은 각 인코더 내의 각 서브레이어(self-attention, ffnn)가 그 주위에 잔존 접속을 가지며, 그 후에 레이어 정규화 스텝이 뒤따른다는 것이다.

셀프 어텐션과 관련된 벡터 및 레이어 노멀 연산을 시각화하면 다음과 같이 된다.

이것은 디코더의 서브레이어에도 해당된다. 2개의 스택형 인코더와 디코더의 트랜스포머를 생각해 보면 다음과 같다.

디코더 측

지금까지 인코더 측의 개념에 대해 설명했다. 이제 디코더들이 어떻게 함께 일하는지를 살펴보자.

인코더는 입력 시퀀스를 처리하는 것으로 시작한다. 다음으로 상위 인코더의 출력은 Attention 벡터 K와 V의 세트로 변환된다. 디코더가 입력 시퀀스의 적절한 장소에 초점을 맞추는 데 도움이 되는 「인코더-디코더 어텐션」레이어의 각 디코더가 사용하는 것은 다음과 같다.

부호화 단계가 끝나면 복호화 단계를 시작한다.복호화 단계의 각 스텝은 출력 시퀀스(이 경우 영어 번역문)에서 요소를 출력한다.


다음 단계는 변압기 디코더가 출력을 완료했음을 나타내는 특수 기호에 도달할 때까지 프로세스를 반복한다. 각 스텝의 출력은 다음 스텝에서 하부 디코더에 공급되며, 디코더는 인코더와 마찬가지로 디코딩 결과를 버블링한다. 인코더 입력과 마찬가지로 디코더 입력에 위치 부호화를 추가하여 각 워드의 위치를 나타낸다.

디코더의 SA레이어는 인코더와 약간 다른 방법으로 동작한다.

디코더에서 SA레이어는 출력 시퀀스 내의 이전 위치에만 대응할 수 있다. 이것은, SA 계산 안에 있는 Softmax로 나아가기 전 단계에서, 추후의 포지션(-inf로 설정)을 마스킹 하는 것에 의해서 행해진다.

"인코더-디코더 Attention" 레이어는 MSA와 동일하게 동작한다. 다만, 그 아래의 레이어에서 쿼리 매트릭스를 작성하고, 인코더 스택의 출력으로부터 키와 값 매트릭스를 가져온다.

최종 선형 레이어 및 Softmax 레이어

디코더 스택은 floats의 벡터를 출력합니다. Softmax 레이어에 이은 최종 선형 레이어는, 이를 단어로 바꾸는 작업을 한다.

선형 레이어는 디코더 스택에 의해 생성된 벡터를 로짓 벡터(logit vectors)라고 불리는 훨씬 더 큰 벡터에 투영하는 단순한 완전히 연결된 뉴럴 네트워크다.

 

모델이 교육 데이터 세트에서 학습한 10,000개의 고유한 영어 단어(모델의 "출력 어휘")를 알고 있다고 가정해본다. 이것은 로짓 벡터의 폭을 10,000 셀로 만들 것이다. 각 셀은 고유한 단어의 점수에 해당하며, 이것이 모델의 출력과 선형 레이어를 차례로 해석하는 방법이다.

 

그런 다음 softmax 계층은 이러한 점수를 확률로 변환한다(모두 양수, 모두 합산 1.0). 확률이 가장 높은 셀이 선택되고 관련 단어가 이 시간 단계의 출력으로 생성된다.


이 그림은 디코더 스택의 출력으로 생성된 벡터로 맨 아래에서 시작하며, 그런 다음 출력 워드로 변환된다.

트레이닝의 개요

모델을 훈련하는 방법에 대해 서술한다.

 

훈련 중에 훈련받지 않은 모델은 정확히 동일한 전진 패스(same forward pass)를 통과한다. 그러나 라벨이 붙은 트레이닝 데이터 세트에 대해 트레이닝을 실시하고 있기 때문에, 그 출력과 실제의 올바른 출력을 비교할 수 있다.

이를 시각화하기 위해 출력 어휘에는 6개의 단어("a", "am", "i", "thanks", "student", "<eos>"('문장 끝'의 줄임말)만 포함되어 있다고 가정해보자.


모델의 출력 어휘는 훈련을 시작하기도 전에 전처리 단계에서 생성된다.

출력 어휘를 정의하면 같은 폭의 벡터를 사용하여 어휘 내의 각 단어를 나타낼 수 있다. One-Hot Encoding이다. 예를 들어 다음 벡터를 사용하여 "am"이라는 단어를 나타낼 수 있다.

 

손실 함수

모델을 훈련하고 있다고 하자. 예를 들어, 트레이닝의 첫 단계이며, 「merci」를 「감사합니다」라고 하는 간단한 예에 따라서 트레이닝을 실시하고 있다. 이 때, 출력은 "감사합니다"라는 단어를 나타내는 확률 분포가 되어야 한다. 하지만 이 모델은 아직 훈련되지 않았기 때문에, 아직 그런 일은 일어나지 않을 것 같다.

모형의 모수(가중치)는 모두 랜덤하게 초기화되므로 (추적되지 않은) 모형은 각 셀/단어에 대해 임의의 값을 갖는 확률 분포를 생성한다. 실제 출력과 비교한 다음 역전파를 사용하여 모든 모델의 가중치를 조정하여 출력을 원하는 출력에 가깝게 만들 수 있다.

두 확률 분포를 비교하려면 어떻게 해야 할까? 우리는 단순히 하나를 다른 하나에서 뺄 수 있다.자세한 내용은 교차 엔트로피와 쿨백-라이블러 발산(Kullback-Leibler divergence)을 참조하라.

 

그러나 이는 지나치게 단순화된 예라는 점에 유의하라. 좀 더 현실적으로, 한 단어보다 긴 문장을 사용할 수 있다. 예를 들어, 입력:"je suis etudiant" 및 예상 출력:"저는 학생입니다". 즉, 다음과 같은 경우 모델이 확률 분포를 순차적으로 출력하기를 원한다.

  • 각 확률 분포는 너비 vocab_size의 벡터로 표현된다(Toy example에서는 6이지만 보다 현실적으로 30,000 또는 50,000과 같은 숫자).
  • 첫 번째 확률 분포는 단어 "i"와 연관된 셀에서 가장 높은 확률을 가진다.
  • 두 번째 확률 분포는 단어 "am"과 연관된 세포에서 가장 높은 확률을 가진다.
  • 마찬가지로 다섯 번째 출력 분포가 <end of Sentence>라는, 10000개의 기본 어휘와 연관이 있는 셀을 지닌 심볼을 나타낼 때까지 계속된다.
하나의 샘플 문장에 대한 교육 예제에서 모델을 교육할 목표 확률 분포

충분히 큰 데이터 세트에 대해 충분한 시간 동안 모델을 교육한 후 생성된 확률 분포는 다음과 같다.

트레이닝 후에, 이 모델이 우리가 기대하는 올바른 번역을 출력할 수 있기를 바란다. 물론 이 문구가 교육 데이터 세트의 일부인지 여부는 알 수 없다( 교차 검증 &nbsp; 참조).모든 포지션이 그 시간 스텝의 결과일 가능성이 낮더라도 약간의 가능성이 있다는 점에 유의하라. 이것은 훈련 프로세스에 도움이 되는 softmax의 매우 유용한 속성이다.

이제 모델은 출력을 한 번에 하나씩 생성하기 때문에, 모델이 확률 분포에서 확률이 가장 높은 단어를 선택하고 나머지는 버린다고 가정할 수 있다. 이것이 그것을 실현하는 한 가지 방법이다(그리디 디코딩이라고 불린다).

또 다른 방법은 예를 들어 상위 두 단어(예를 들어 'I'와 'a')를 유지한 후 다음 단계에서 모델을 두 번 실행하는 것이다. 한 번은 첫 번째 출력 위치가 단어 'I'라고 가정하고, 다른 하나는 첫 번째 출력 위치가 단어 'a'라고 가정하고, 다른 하나는 위치 #1과 #를 고려하여 오류가 적은 버전입니다.2는 보관되어 있습니다.포지션 #2 및 #3 등에 대해 이 절차를 반복합니다.이 방법을 "빔 검색"이라고 합니다.이 예에서는 beam_size가 2(항상 2개의 부분 가설(미완성 변환)이 메모리에 저장됨을 의미함), top_beams도 2(즉, 2개의 변환을 반환함)이다. 이 두 가지 모두 실험할 수 있는 하이퍼 파라미터다.


위의 것과 더불어, 2개의 블로그 글을 참고하여 알아낸 것에 대해 적어보고자 한다. 솔직히 위의 것만 보면 뭔 소린지 감이 안 잡힘.

 

Sequence Modeling은, 어떤 Sequence를 갖는 데이터로부터 또 다른 Sequence를 갖는 데이터를 생성한다.

예로는 LSTM, RNN등이 있다. 

이러한 모델은 어떠한 시점 t에서 구한 hidden state h_t가 그 전 Sequence(1, 2, ... , t-1)의 정보를 함축하고 있다. 

그러나 이 모델의 문제는, 모든 데이터를 순차적으로 입력에 넣어야 한다. 즉, I am ironman의 경우, ironman을 처리하기 위해서는 무조건 i를 거쳐야 한다. 이는, 데이터가 긴 경우, Memory와 Computation에서 큰 부담이 생긴다.

Attention은 이를 해결하기 위한 방법 중 하나로, Decoder에서 출력 단어를 예측하는 시점마다 Encoder에서의 전체 입력문장을 다시 한번 참고함으로써, 해당 단어와 연관이 있는 단어를 더 집중하여 보게 한다. input이나 output 데이터에서, Sequence Distance완 무관하게, 서로간의 dependencies를 모델링한다. 위는 Franch를 English로 번역하는 Sequence modeling에서 Attention을 사용할 때의 그 Correlation Matrix를 나타낸다.

 

그리고 Transformer는 이 Attention만을 이용하여 아키텍처를 구축한 사례이다.

한 시점의 hidden state는 그 전것들의 정보를 함축하고 있다 했다. 

Sequence-to-sequence 모델은 이 encoder의 최종 output을 일종의 embedded vector로 사용하여 Decoder에 넣는데, 이 떄 Memory와 계산량으로 인해 embedded vector의 maximum length를 제한해야 한다. 이렇게 되면, 제한된 크기의 vector로 모든 정보를 담아야 하기에, 정보의 손실이 커지고, 성능의 병목현상이 발생한다.

Transformer은 이를 해결하고자 한 사례이다. 내부는 SA와 FCN(Fully Connected Layer)로 구성되어있다.

Attention은 Input으로, Q(Query - 물어보는 존재, 어떤 단어를 나타내는 Vector), K(Key - Q에게 물어봐지는 존재, 문장의 모든 단어들에 대한 Vector의 Stack Matrix), V(Value - 데이터의 값)을 받는다. 또한 Q와 K는 dk차원, V는 dv차원을 갖는다(단, 논문에서는 dk=dv).

AI is awesome.이라는 문장에 Attention을 적용한다면, Q는 awesome, key는 모든 단어(AI, is, awesome)의 stack된 Matrix다. 여기서 QK^T는 한 단어(awesome)와 모든 단어의 Dot Product를 해줌으로써, 어떠한 relation vector을 생성한다. 이를, 모든 단어를 query로 사용할 때까지 반복한다.

 

이 dot-product를 실행한 다음엔, 1/sqrt(dk)로 scaling을 진행한다. 이를 통해, Softmax가 0 근처에서는 Gradient가 높고, Large Positive/Negative 값에서는 낮기때문에 학습이 잘 되지 않아서, 이 Scaling을 통해 모든 값이 0 근처에 오게 한다.

 

이렇게 Softmax를 진행하여 Query가 모든 단어와 어느정도의 Correlation이 있는지를 확률분포 형태로 만들고, 이를 Value matrix와 dot product를 해줌으로써, 기존 Vector에 Query와 Key 간의 correlation 정보를 더한 vector을 생성한다.

상세한 계산 방식은 상단의 SA상세를 살펴보라.