* 2022 데이터청년캠퍼스 한국외대 자연어처리과정 논문세미나 진행하며 사용했던 발표 자료입니다. model architecture설명부분의 모든 이미지 자료들을 직접 제작했습니다.
자연어처리 분야에서 상당히 중요한 논문인 <Attention is all you need >에서 제안된 Transformer model 을 리뷰해보자
2017년 구글이 발표한 논문인 <Attention is all you need>에서 제안된 Transformer는 기존의 seq2seq구조의 인코더 디코더 포맷을 따르면서도 어텐션만으로 구현된 모델 구조를 갖는다. 기존에 널리 사용되던 RNN을 사용하지 않고 더 좋은 성능을 보여주었다는 점에서 NLP분야에서 일종의 game changer가 되었다.
<Attention is all you need>논문이 발표되기 전까지 기존의 언어모델들은 RNN기반의 혹은 CNN기반의 seq2seq모델을 채택해왔다. 이러한 모델은 sequence data에 대해 하나의 context vector로 압축하는 과정에서 여러가지 문제점이 발생한다. 입력으로 받은 모든 단어의 정보가 전달되지 못하는 것이 가장 큰 문제였다. 너무 오래전에 입력된 단어는 잊어버리거나 특정 단어 정보를 과도하게 반영하여 전체 정보를 왜곡하는 경우가 발생하는 것이다.
때문에 RNN은 긴 sequence를 가진 데이터를 처리하는 것에 대한 성능이 떨어졌다. 이를 RNN의 고질적인 장기의존성문제 (the problem of Long-Term dependencies)라고 부른다. 그리고 이를 극복하기 위해 다양한 시도들이 있어왔다.
Attention mechanism도 그러한 과정중에 등장했다. attention은 sequence 입력에 수행하는 기계학습 방법의 일종으로 seqeunce의 요소가운데 중요한 요소에 집중하고 그렇지 않은 요소는 무시하여 수행 능력을 끌어올리는 방식이라고 요약할 수 있다. 이는 디코더에서 출력을 예측하는 매 시점마다 인코더에서의 전체 입력 seqeunce를 다시한번 참고하게 하는 방식으로 수행된다. 그리고 참고 과정에서 가중치에 의해 결정된 집중정도(attention)가 높은 요소를 더욱 attention!하게 된다. 하지만 이 메커니즘을 처음 제안했던 논문에서도 여전히 RNN방식의 시퀀스 구조를 사용했다.
트랜스포머는 seq2seq의 encoder-decoder 구조를 활용하지만 RNN으로 부터 완전히 벗어나 attention만으로 설계되었다.
Transformer 를 이해하기 위해서는 self-attention의 작동원리에 대해서 먼저 이해할 필요가 있다. Attention 기법들과 transformer 구조에서 제안된 self-attention은 차이를 보인다.
기존에 제안되었던 attention기법은 일반적으로 decoder에서 연산이 시행될때 encoder로부터 넘어온 정보에 가중치를 주는 식으로만 작동되었다. 반면 self-attention은 입력으로 받은 텍스트 데이터 내에 존재하는 단어들 간의 관계를 파악하기 위해 사용한다. 그리고 이를 위해 입력데이터를 query, key, value라는 구체적인 정보들로 나누어 연산하는 과정을 포함한다.
여기서 key와 value는 파이썬 사전형 자료처럼 id와 해당 단어의 구체적인 정보들을 담고 있는 벡터들로 이해할 수 있고 query는 해당 단어가 어떤 단어와 가장 유사한가에 대한 질문이라고 이해할 수 있다.
논문을 보면 다음과 같은 그림으로 attention 에 대해 설명한다. Dot-product는 QK transpose ,즉 벡터 간 내적을 의미하고 sclaed는 값의 크기를 조절하기 위해 key의 차원으로 나눠주는 부분이다.
그림을 보면 쿼리와 키에 대해 내적을 하고 스케일링 해주고 여기서 마스킹은 뒤에 디코더 부분의 마스킹과는 조금 다른데 attention matrix 연산과정에서 행렬내의 벡터간 길이를 맞춰주기 위해 pad토큰을 설정해줄 경우 이 부분에 대해서 attention을 연산하지 않도록 masking을 해주는 부분이다. 그래서 뒤에 optional이라고 붙어있는거고, 여기에 소프트 맥스 취해서 확률값 형태로 출력한 가중치 행렬에 이번엔 value 행렬을 곱하여 최종적인 attention값 행렬을 구하게 된다.
논문에서는 dot-product를 additive attention과 비교하여 설명하는데 간단히 말하자면 벡터들이 행렬 형태로 입력되어 한번에 곱연산으로 수행되기에 dot-product가 더욱 공간효율적이고 빠르다. 하지만 key의 차원이 커지고 여러번의 softmax연산이 반복되면서 gradient vanishing문제가 발생하기 때문에 오히려 additive attention이 성능이 더 좋을 수 있는데, 이를 해결하기 위해 key 차원의 제곱근값을 나눠서 스케일을 해주는 방식을 취한다.
scaled-dot product가 어떤식으로 attention 연산이 수행되는지 예시를 통해 살펴보자.
Today is Friday라는 문장이 입력으로 들어와서 3*5행렬 형태로 임베딩이 이루어진 상태라고 가정한다.
우선 Query, key value 벡터들을 임베딩으로 부터 뽑아내야 한다. 오른쪽에 기입된 값들은 예시를 위해서 다 임의로 기입된 값들. 쿼리 가중치 행렬을 보면 5,3 행렬인데 임베딩 벡터의 차원이 5이기 때문에 3차원의 쿼리 벡터를 구한다고 가정했을때 도출되기 위한 값이라고 생각하면 될 것 같다. 쿼리 가중치 행렬의 초기값이 다음과 같이 입력된다면 이 값들은 학습과정에서 최적의 값들로 계속 update된다.
Key와 value에 대해서도 같은식의 연산을 반복한다. 똑같이 특정차원의 벡터를 얻기위해 가중치 행렬들을 벡터별로 곱해주는 것. 다만 행렬형태로 들어가니 실제로는 한번에 연산이 수행. 여기있는 가중치 행렬의 요소들 또한 학습과정에서 계속 최적값으로 update 된다.
Q,K,V행렬을 전부 구했다면 이제 어텐션을 구해야한다. 앞서 전체에 대한 그림에서 봤던것처럼 쿼리벡터로 이루어진 Q행렬과 키 벡터로 이루어진 K행렬에 대한 내적연산을 수행해주고 이 값을 key의 디멘션의 루트값으로 스케일링 해주고 소프트 맥스를 취해 확률값 형태로 계산한다. 여기서 쿼리와 키의 내적연산을 통해 나온 행렬을 attention score matrix라고 지칭(혹은 attention energy라고 부르기도 한다). 이 값은 각 query별 key와의 유사도를 나타낸다. 첫행의 Today를 예로 들면 today와 today자신, is, Friday에 대한 유사도를 나타낸 벡터인 것.
스케일과 소프트맥스까지 취한 행렬이 다음과 같이 가중치 형태로 값이 입력되어 나타나고 이 가중치 행렬을 이제 벨류 메트릭스에 곱해주면 각 단어의 attention값이 입력되어있는 행렬이 도출된다.
논문에서 dot product attention옆에 나와있는 그림이 이 그림이다. Multi head attention이라고 명명이 되어있는데 말 그대로 앞서 설명했던 attention을 여러개의 head에 걸쳐 나누어 놓은 형태이다. 똑같은 연산이 head의 수만큼 반복되는데 입력단계에서 조금 다른점은 임베딩에서 도출된 QKV 행렬을 linear projection을 거쳐서 각 헤드에 들어갈 벡터로 나누어주는 과정이 존재한다. 논문에서 설정한 헤드의 수는 8이고 쿼리와 키벨류 벡터의 차원이 512라면 linear projection을 통해 각각 64차원의 벡터를 가진 8개의 행렬들로 나눠져서 각각 어텐션을 수행한다. 헤드별로 어텐션을 수행하고 나면 concatenation을 통해 다시 일렬로 행렬들을 합하고 새로운 가중치 행렬을 곱해서 입력되었던 차원과 동일한 행렬을 도출한다.
이런식으로 헤드별로 나누어 언텐션을 수행하고 합치는과정을 거치는 이유를 논문에서는 모델의 성능을 끌어올려주기 때문이라고 설명한다. 멀티헤드 연산을 통해 attention해야할 단어가 무엇인지를 더 효과적으로 판단하고 각 단어가 갖는 문맥저긴 특성또한 더 잘 표현된다는 것. 계산적인 비용또한 차원을 분절시켜 여러번 연산을 수행하기 때문에 하나의 큰 차원으로 계산하는 것과 차이가 없게 된다.
멀티헤드 구조의 이해를 돕기위해 앞서 설명했던 this is Friday예시를 통해 전체 연산과정을 쭉 살펴보자. 다만 앞서 dot product attention에서 설명할떄와는 다르게 논문에서 설정한 그대로 512차원의 입력 임베딩과 8개의 헤드를 가지는 구조라고 가정한다. 우선 입력된 512차원의 벡터들이 각 쿼리 키 벨류 가중치 행렬과 곱해져 Q,K,V행렬을 만든다. 이때 만들어진 행렬들을 다시한번 헤드별로 가중치를 곱해서 8개로 나눠준다. 512의 쿼리 키 벨류 행렬이 512*64 가중치 행렬과 곱해져 각각 64차원의 키 쿼리 벨류 행렬들이 나오는것. 이 과정을 논문에서는 linear projection이라고 부른다.
헤드별로 새로운 쿼리 키 벨류 를 얻었다면 이제 각각 attention연산을 동일하게 수행해주면 된다. 내적연산하고 스케일하고 소프트맥스 취해서 가중치 형태로 만들어진 행렬을 벨류와 곱해주고 어텐션값이 나온다. 이걸 8번의 헤드만큼 반복하면 8개의 어텐션값 행렬이 나오는데. 이걸 그림처럼 일렬로 concatenate해준다. 그리고 다시한번 W가중치와 곱해지는 projection을 거쳐서 하나의 행렬값으로 도출. 이게 하나의 멀티헤드 어텐션의 결과값이고 이때 차원은 처음 어텐션이 입력받을때의 차원과 같은 512이다.
이제 어텐션 알고리즘에 대해서 이해 했으니 트랜스포머의 인코더와 디코더 구조를 좀 더 자세히 살펴보자. 인코더 디코더 내에서 앞서 설명했던 멀티헤드 어텐션이 총 세번 사용된다. 그리고 인코더에서는 앞부분과 완전히 동일한 셀프어텐션이 사용된다. 다만 이과정에서 어텐션외에도 다양한 서브레이어들을 거친다.
왼쪽 그림이 각 인코더 레이어들을 이어붙인 인코더 전체의 구조. 인코더는 N개의 동일한 레이어를 쌓아서 만들어진다. 이때 각 층의 마지막 출력값이 다음 층의 입력값으로 들어가는 형태로 동작하고 논문에서는 인코더와 디코더의 개수를 모두 6으로 설정했다.
각 인코더 레이어를 자세히보면 서브레이어들은 전부 add&norm을 거친다. 간략하게 설명하면 add&norm레이어에서 add는 잔차연결, norm은 레이어 normalization으로 각각 다양한 관점에서 블록계산을 통해 학습을 좀더 용이하게 해주고 정규화를 통해 학습속도를 높여준다.
다음은 디코더 부분. 디코더 부분또한 인코더 처럼 여러개의 동일한 레이어를 쌓아서 만들어진다.
다만 인코더와 어텐션을 수행하는 방식이 살짝 다르다. 여기에서 사용되는 attention은 masking 기법을 포함한 attention과 encoder의 입력과 decoder의 입력을 모두 활용하는 attention 두가지.
디코더는 출력해야할 문장행렬을 정답의 형태로 한번에 입력받는다. 입력받은 행렬을 이용해 디코더는 각 시점의 단어를 예측하도록 훈련된다. 하지만 이 과정에서 문제가 발생. RNN계열과 다르게 transformer는 현재시점에서 예측해야할 단어의 정답이 포함되어 있는 정보또한 입력으로 받아버리기 때문에 현재시점보다 미래의 단어에 대한 답을 참고하지 못하도록 설정할 필요가 있다. 그래서 답을 알지 못하고 제대로 학습이 되도록 미래시점의 단어들을 마스킹해주는 기법을 사용한다.
트랜스포머를 통한 기계번역을 가정한다면 maksed attention의 자세한 동작원리는 다음과 같다. 앞서 Today is Friday라는 문장이 인코더에 입력되었음을 가정했던 예시를 그대로 사용한다면 디코더에 정답행렬의 형태로 한번에 주어지는 행렬은 ‘오늘은 금요일 입니다’ 이다. 다만 시작 시퀀스에 대한 정보가 포함 된 SOS 라는 토큰이 포함되어 한칸식 오른쪽으로 밀린 임베딩 매트릭스가 만들어지게 될 것이다. 논문에서 제시한 디코더 그림에서 디코더입력부분에 shifted-right이라고 쓰여있는게 이러한 의미.
여기에 포지셔널 인코딩으로 시퀀스 정보를 넣어주고 어텐션에 입력으로 들어가면 똑같이 쿼리와 키에 대해서 내적연산을 수행하고 어텐션 스코어 매트릭스가 오른쪽 상단에 있는 그림처럼 만들어진다. 이때 time step별로 맞춰야 할 정보를 가려주는 마스킹이 적용된다. 구체적으로는 각 요소별 값에 음수무한의 값을 넣어 소프트맥스를 취했을때 0이 되도록 하는 것. 이렇게 하면 어텐션 가중치를 계산할때 미래에 맞춰야할 정답에 해당하는 마스킹된 값들은 어텐션을 계산하지 않게 된다. 이외에 나머지 연산들은 다른 멀티헤드 어텐션과 동일하게 수행한다.
mask attention과 add&norm층을 거쳐 올라오면 이제 encoder decoder attention층을 거친다. 여기서는 어텐션에 들어가는 키 쿼리 벨류의 출처가 이전과 다르다. 이전까지는 하나의 행렬에서 키쿼리벨류를 만들어 사용했다면 여기서는 인코더의 값을 키와 벨류로 사용하고 마스크층을 거쳐 올라온 행렬을 쿼리로 만들어 사용한다. 맞춰야 할 정답에 해당하는 행렬을 질문의 형태로 가져와 인코더에서 학습된 key와 value값에게 어떤 단어에 attention을 둬야하는지 물어본다고 비유할 수 있다.
동일하게 내적연산을 취하면 가운데 있는 것처럼 인코더의 정보와 디코더의 정보가 포함된 어텐션 스코어 행렬이 만들어진다.
인코더에서 입력받은 벡터들을 타임스텝별로 디코더에서 처리해 출력하는 과정 전체를 그림으로 표현하면 다음과 같다. 왼쪽처럼 인코더에서 today is friday를 받아서 키와 벨류의 형태로 디코더에 정보를 준다. 그럼 레이어들은 정답에 마스킹이 되어있는 아웃풋 벡터들을 쿼리로 활용해 각 레이어마다 인코더 디코더 연산을 이어가고 타임스텝별로 소프트맥스 엑티베이션 펑션을 거쳐 <오늘은 금요일 입니다> 라는 아웃풋이 도출된다. 이때 디코더는 종료 심볼인 <eos>가 나올때 까지 타임스텝별로 연산을 진행한다.
* 학습때 도움을 받았던 강의들은 다음과 같습니다.
: Lee's Data Science , <Transformer 1,2,3 >
: 동빈나,<Transformer: Attention Is All You Need (꼼꼼한 딥러닝 논문 리뷰와 코드 실습)>
: 허민석, <트랜스포머 (어텐션 이즈 올 유 니드)>
: Michael Phi, < Illustrated Guide to Transformers Neural Network: A step by step explanation >
https://jalammar.github.io/illustrated-transformer/
The Illustrated Transformer
Discussions: Hacker News (65 points, 4 comments), Reddit r/MachineLearning (29 points, 3 comments) Translations: Chinese (Simplified), French 1, French 2, Japanese, Korean, Russian, Spanish, Vietnamese Watch: MIT’s Deep Learning State of the Art lecture
jalammar.github.io
: Jalammar Github, < The illustrated Transformer >
https://wikidocs.net/book/2155
딥 러닝을 이용한 자연어 처리 입문
많은 분들의 피드백으로 수년간 보완되어 온 딥 러닝 자연어 처리 무료 eBook입니다. 기본적인 텍스트 전처리부터 BERT와 같은 PLM의 다양한 다운스트림 태스크를 ...
wikidocs.net
: Wikidocs , < 딥러닝을 이용한 자연어처리 입문 _ 16.1 트랜스포머>
* 오개념이 있거나 잘못된 설명이 있다면 알려주시면 감사하겠습니다