Before Review
오랜만에 리뷰입니다. 논문은 평소에 읽어보고 싶었기도 하면서 , 비디오 과제에서 사용하고 있는 Timesformer를 이해하기 위해 Transformer의 원조 논문을 읽어보게 됐습니다. Transformer 자체가 자연어처리를 위해 등장했다보니 내용이 낯설기도 했고 , 논문의 내용 자체도 함축적인 내용들도 많아서 여러 블로그 리뷰나 유튜브 소스를 참조해서 내용을 이해하였습니다. 리뷰 시작하도록 하겠습니다.
그림 출처 : https://jalammar.github.io/illustrated-transformer/
Introduction
자연어 처리 분야에서 좋은 성능을 보여주던 기존의 방법들은 아마도 대부분 아시던 것처럼 Recurrent 계열의 방법론이 주를 이루고 있었습니다. Encoder-Decoder 구조의 RNN이나 LSTM , Seq2Seq 기반의 방법론들이 좋은 성능을 보여주었고 많이들 사용이 됐다고 합니다.
그런데 자연어처럼 순서를 가지는 Sequential한 data를 처리하는 데 있어서 기존의 방법론들의 문제점은 무엇이었을까요? 바로 병렬처리 입니다. 병렬처리가 힘든 이유는 Recurrent 계열의 방법론들은 순서대로 입력을 처리하기 때문입니다. 따라서 모든 연산을 동시다발적으로 하는 병렬처리를 할 수 없기 때문에 GPU자원을 효율적으로 사용하지 못했습니다.
이에 Google 연구팀은 CNN과 RNN 구조를 사용하지 않고 오직 Attention Mechanism만을 가지고 Sequential data를 처리하는 새로운 구조인 Transformer를 세상에 내놓게 됩니다. 이 Transformer는 위에서 얘기한 병렬처리가 가능하기 때문에 학습속도도 굉장히 빠르고 더불어 성능까지 SOTA를 달성하게 됩니다.
Transformer의 가장 큰 Contribution을 지금 한번 살짝 정리해보도록 하겠습니다.
- 기존의 인코더 디코더 구조의 RNN , LSTM , Seq2Seq 구조의 방법론은 입력은 순서대로 받아와야 한다는 한계점이 있어 , 모든 연산을 동시다발적으로 하는 병렬처리가 힘들었고 , 때문에 학습 속도 느렸다는 단점을 해결하여 병렬처리가 가능한 새로운 transduction model을 제안하였습니다.
- Attention Mechanism을 활용해서 단어들간의 Dependency를 해결하고 있으며 , Multi-Head Attention 구조를 채택하여 기계번역 분야에서 당시 SOTA의 성능을 기록하는 즉 , 성능적인 측면에서도 좋은 모습을 보여주었다는 점 입니다.
그렇다면 이제 Transformer가 어떻게 설계 되었는 지 하나씩 차근차근 살펴보도록 하겠습니다.
Model Architecture
Transformer의 자세한 구조는 생략하고 큰 그림을 보여주는 사진 입니다. 소스 문장을 넣어줬을 때 여러겹의 Encoder를 거쳐주게 되고 마지막 Encoder의 출력 값을 다시 여러겹의 Decoder가 참고하며 출력 문장을 만들어주고 있는 모습입니다. 이처럼 Transformer는 Encoder Decoder 구조를 사용하고 있으며 Encoder와 Decoder 끼리는 같은 구조를 가지고 있습니다. 하지만 같은 구조를 가진다고 같은 parameter를 가진다는 의미는 아닙니다.
그렇다면 이제 , Encoder에 들어가는 입력 부터 천천히 알아보도록 하겠습니다.
Embedding
다들 알다시피 단어 그대로를 네트워크의 입력으로 넣을 수는 없겠죠. 결국에는 어떤 벡터로 변환해서 네트워크의 입력으로 넣어줘야 하는 데 , 이 과정을 임베딩이라고 표현합니다. 단어를 벡터로 변환해주는 과정을 보통 Word Embedding이라 표현하며 반환되는 벡터를 Embedding Vector라고 우리는 부릅니다. 임베딩 시키는 알고리즘은 정말 많으니 본 리뷰에서는 다루지 않겠습니다.
그래서 보통 문장을 우선 , Token 별로 나눈다음 나누어진 Token에 임베딩 알고리즘을 적용해서 Token에 대한 Embedding Vector를 Encoder의 입력으로 넣어주게 됩니다. Transformer에서는 임베딩 벡터의 차원을 512로 고정을 해주고 있습니다. 이 차원의 수는 사용자가 직접 설정할 수 있는 값입니다.
Encoder
Ecoder 부터 살펴보도록 하겠습니다. Encoder는 두개의 SubLayer로 구성이 되어있으며 모든 Encoder는 같은 구조를 가지고 있습니다. 가장 밑단에 있는 Encoder는 Embedding Vector들을 입력으로 받게 되며 그 다음에 존재하는 Encoder들은 이제 이전 Encoder의 출력값을 입력으로 받는 구조라 생각하시면 됩니다.
사진을 살펴보면 Self-Attention Layer 그리고 Feed Forward Neural Network 두개의 Layer가 존재합니다. 우선 Self Attention Layer 부터 살펴보도록 하겠습니다. Attention을 말 그대로 주의를 집중하는 의미라 보시면 되는 데 좀 더 구체적으로 얘기하면 어느 부분을 집중해서 볼지 가중치를 구한다고 보시면 됩니다. Self Attention은 이제 소스문장에 있는 단어를 인코딩 할때 다른 위치에 있는 단어를 보고 거기서 힌트를 받아 내가 인코딩하고자 하는 단어를 보다 더 잘 인코딩 하기위해 적용한다고 보시면 됩니다.
예를 들어 “The animal didn’t cross the street because it was too tired” 라는 문장에서 it이 가리키는 것은 animal이라는 것을 사람은 단번에 알 수 있지만 이것을 기계도 알 수 있게끔 해준다고 생각하면 될 거 같습니다.
결국 “It” 이라는 단어를 인코딩 할때 The animal이라는 단어의 의미를 좀 더 부여해서 인코딩한다는 의미입니다.설명이 좀 추상적이니 좀 더 자세히 살펴보겠습니다. 가장 먼저 입력단의 임베딩 벡터로 부터 3가지의 벡터를 만들어주는 데 우리는 이 벡터들을 Query , Key , Value라고 정의합니다. 이 벡터들은 어떻게 구하냐 임베딩 벡터에 대해서 세개의 Learnable한 Matrix를 곱함으로 써 만들어집니다.
임베딩 벡터 X_{1} , X_{2}에 Matrix W^{Q} , W^{K} , W^{V}를 곱하면 벡터들이 반환되고 그 벡터들을 Query,Key,Value라고 보면 됩니다.
그리고 우리는 이제 Query와 Key를 가지고 어떠한 score를 계산할 것입니다. “Thinking”을 인코딩하는 상황에서 “Thinking”에 대한 key vector와 “Machine”에 대한 key vector를 가지고 “Thinking”의 쿼리 벡터와 내적을 해주면 어떤 스칼라 값이 반환될 것이고 이것을 우리는 score 혹은 energy라고 보시면 됩니다. 이 score를 key vector의 차원의 제곱근으로 나눠주는 scaling을 다음으로 진행해주는데 이 이유는 보다 더 안정적인 gradient를 얻기 위함이라고 합니다.
Scaled score에 이제 Softmax를 태워서 그 값들을 확률값으로 변환해주게 되는데 이 Softmax까지 거친 점수가 바로 현재 위치의 단어의 인코딩 과정에 있어서 얼마나 각 단어들의 표현이 반영되어야 하는지를 나타낸다고 보시면 됩니다. 그래서 보통은 현재 위치의 단어가 가장 높은 점수를 가지겠지만 가끔은 다른 위치에 있는 단어의 정보더 어느정도 반영이 되어 인코딩 과정에서 단어 맥락에 대한 고려를 해주고 있다 보시면 되겠습니다.
지금까지의 과정을 본 논문에서는 Scaled Dot-Product Attention이라 부릅니다. 하지만 여기서 Transformer는 이를 더 발전시켜 Multi-Head Attention이라는 구조를 정의합니다.
맞는 비유 인지는 조금 확신이 안서지만 이렇게 이해를 했습니다. 저희가 CNN을 가지고 Feature를 뽑을 때 하나의 Filter만 사용하는 게 아니라 여러개의 Filter를 가지고 좀 더 풍부한 Feature를 뽑을 수 있게 하였습니다. 마찬가지로 단어를 임베딩할 때 여러개의 헤드를 사용하여 단어를 임베딩하여 좀 더 Representation을 잘 해보겠다는 의미로 이해했습니다.
Multi-Head라는 것은 결국 Query,Key,Value를 만들어내는 Matrix가 독립적으로 Head의 갯수만큼 존재하는 상황이라 보시면 됩니다.
논문에서는 Head의 갯수를 8개로 사용을 해주고 있고 이를 그림으로 나타내면 다음과 같습니다.
각각의 헤드를 거쳐서 이제 우리는 Self Attention이 적용된 Output 벡터들을 받을 수 있고 이 벡터들을 Concat 해주게 됩니다.
여기서 Concat된 벡터를 그대로 Feed Forward Layer로 보내주면 안됩니다. 뒤에서 자세히 다루겠지만 Residual Learning의 구조가 들어가다 보니 입력벡터와 동일한 차원을 가져야만 합니다. 이를 위해 Concat된 벡터에 또다른 Matrix를 곱해서 최종적인 Multi Head Attention이 적용된 output vector를 얻을 수 있습니다.
자 얘기가 길어졌지만 Encoder 부분에서 일어나는 Self Attention에 대해서 알아봤습니다. 정리해보면 Self Attention을 해주는 이유는 단어들간의 맥락을 파악하여 인코딩할 때의 표현력을 높이기 위해서 였고 Transformer에서는 Multi-Head Attention이라는 구조를 활용해서 더욱 더 표현력을 높게 가져가는 구조를 취했습니다.
Encoder의 Attention Layer에 대해서 알아봤으니 , Encoder의 좀 더 자세한 구조를 살펴보도록 하겠습니다.
위의 사진을 보면 알 수 있지만 , Residual learning 방식을 채택하고 있습니다. Self Attention Layer에 들어가는 Input과 거쳐서 나온 Output을 더해준다음에 Layer Normalization을 해주고 있습니다. Residual Learning을 채택한 이유를 논문에서 직접적으로 언급하지는 않지만 통상적으로 Residual Learning을 사용하면 전반적인 학습 난이도가 낮아지고 , 초기의 모델 수렴속도가 높게 되고 결국 Global Optima를 찾을 가능성이 올라가는 장점들을 목표하여 사용한게 아닌가 싶습니다.
여기까지가 이제 Encoder의 구조인데 하나 빠진게 있습니다.
입력 데이터들의 순서를 고려하지 않았다는 것 입니다. 분명히 Transformer 역시 Sequential한 data를 처리하기 때문에 순서를 부여하는 과정이 필요하게 되는데 바로 그 과정을 Positional Encoding을 통하여 해결하고 있습니다.
간단하게 설명해보자면 입력 Token을 임베딩한 벡터와 Positional Encoding된 벡터를 더해줌으로써 모델에게 순서에 대한 정보를 전달하게 됩니다. 여기까지 살펴봤을 때 최종적인 Encoder 구조는 아래와 같습니다.
Positional Encoding 과정과 단어를 Decoding 하는 과정까지 본 리뷰에 다 써내려가면 리뷰가 너무 길어 질 것 같아 여기서 끊고 다음 리뷰에서 Transformer 논문 리뷰를 마무리 하도록 하겠습니다.
CNN, RNN 대비 Transformer의 장단점은 어떤게 있나요??
병렬처리를 통한 연산 속도 향상밖에 존재 하지 않나요?