LLM - Transformer

김태준·2024년 1월 6일
0

Deep-Learning

목록 보기
7/10

오늘은 LLM 즉, 거대 언어 모델의 근간이 되는 Transformer에 대해 살펴보고 각 용어, 모델이 의미하는 바에 대해 깊게 공부해보려 한다.

🎈 Transformer 개요

2017년 NIPS에서 등장한 'Attention Is All You Need'라는 논문을 통해 발표된 기법으로 병렬처리가 어려워 학습 속도 측면에서 문제가 있던 RNN의 한계를 극복해 NLP 분야에서의 높은 퍼포먼스를 보여주고 있다.

현재는 NLP를 넘어 Vision 등 다양한 분야에서 채택되어 활용되고 있고 트랜스포머 이전의 경우 LSTM, GRU와 같은 RNN 알고리즘이 최신 모델이었다. 이러한 트랜스포머를 기반으로 현재 많은 사람들이 알고있는 GPT, BERT 등과 같은 모델들이 생성되었다.

🎈 선행 연구 학습

트랜스포머라는 획기적인 기술 이전 선행 연구에 대한 이해를 위해 먼저 학습해보고자 한다.
학습할 내용은 다음과 같다.
1. RNN
2. LSTM
3. GRU

하나씩 차근차근 살펴보자.

✍️ 1. RNN

RNN의 경우 일반적으로 문장이 Input으로 들어와 문장으로 Output을 내놓는 many to many 기법이 기계번역이다. 각 박스는 단어를 의미하고 초록색 부분이 NN. NN에는 단어가 아닌 컴퓨터가 이해할 수 있는 벡터로 변환되어 입력된다. (단어를 벡터로 변환하는 작업이 바로 워드 임베딩.)

NN부분에서 현재 단어 벡터에 이전 값을 계속해서 가져와 업데이트하며 진행된다.

결과적으로 각 단어들에 원핫 인코딩을 취한 벡터로 변환한 다음, 각 벡터에 가중치를 곱해 은닉층으로 만들고 다시 가중치를 곱해 원래의 차원으로 되돌린 후 activation func을 사용해 확률을 구한다. 이후 output의 원핫벡터와 비교해 loss를 구한 다음, loss를 줄여나가는 방식으로 weight를 업데이트하며 학습을 이어나간다.

하지만, 이전 값을 계속해서 이어서 사용하게 되니 back propagation 과정에서 gradient vanishing 문제가 발생하게 된다. 따라서 문장이 긴 경우 성능이 급격히 떨어지게 된다.

✍️ 2. LSTM

앞서 문장이 길어질 때 성능이 떨어지는 경우를 보완한 케이스로 최신 셀에 더 가중치를 높게 두고 학습하는 방법이다.
tanh와 시그모이드라는 activation func을 연산하여 최근 기억한 것의 중요도를 결정하고 복잡한 연산을 거쳐 output을 산출한다.
LSTM의 경우 한 셀에 4개의 신경망이 존재하기에 컴퓨팅적으로 속도가 느린 단점이 존재한다. 이에 따라 신경망이 3개인 GRU가 등장하게 되었다.

✍️ 3. GRU

앞선 LSTM의 속도 측면을 개선한 것으로 4개가 아닌 3개의 신경망이 한 셀에 존재한다.

그러나 결국 이러한 seq2seq (문장~문장)의 모델들의 경우 어쩄든 고정된 크기의 context vector로 만들어진다는 문제가 존재한다. 이는 결국 context vector(하나의 벡터)에 문장의 모든 정보를 압축하는 것을 의미하고 bottle neck 현상이 발생해 성능 하락의 주요 원인이 된다. 따라서 입력 시퀀스 전체에서 정보를 추출하여 병렬 연산을 수행하는 방향으로 점차 발전하게 되었다.

🎈 Transformer 구조

트랜스포머 구조를 보면 다음과 같이 크게 위치 인코딩, 멀티헤드 어텐션, FFN으로 구성되어 있다.
인코더 블록과 디코더 블록은 'Attention Is All You Need'논문에선 6개씩 사용되었지만 꼭 6개가 최적은 아니다.

  • 인코더 블록의 경우 셀프 어텐션과 뉴럴네트워크로 구성이 되어있다.
  • 디코더 블록의 경우 masked 셀프 어텐션과 encoder decoder 셀프 어텐션, 그리고 뉴럴네트워크로 구성되어 있다.

이를 자세히 살펴보면 위 사진인데, Input 벡터의 경우 각 워드가 임베딩되어 벡터로 들어가지만 전체 벡터의 dimension은 512로 구성되고 문장 끝에 end를 나타내는 토큰을 넣고 그 외 빈곳은 padding으로 채워준다.
그의 반면 디코더의 경우 빨간색 부분을 보면 치팅을 방지하고자 앞에 있는 임베딩 벡터만을 넣어주고 뒷부분은 넣어주지 않는다.

  • 인코더의 경우 각각 워드 임베딩을 거친 단어들이 벡터로 변환된 이후 self attention을 거쳐 output을 내보내고 각 벡터는 딥러닝에서 생성한 weight와 연산되고 activation을 거쳐 output으로 나오게 된다. 이때 인코더 내의 NN부분에서 각 벡터들이 weight를 share하고 서로 다른 인코더에서는 weight를 share하진 않는다.

이제 다시 앞서 보인 그림의 구조를 살펴볼 것인데 하나씩 살펴보면 다음 과정을 거친다.

1. 우선 단어들을 벡터로 변환하는 워드 임베딩 작업을 수행한다. (각 벡터들이 한꺼번에 들어가므로 위치정보가 없기에 위치 값을 저장하고자 positional 임베딩을 수행한다.)
2. 따라서 각각 임베딩을 거친 벡터에는 positional embedding vector를 더해 최종 입력값으로 들어갈 벡터를 생성해준다.
3. Positional Encoding을 통해서 각 벡터가 위치 정보를 잘 담고 있는지 숫자로 확인할 수 있다.
4. 임베딩된 벡터가 self attention으로 들어오면 output으로 같은 dimension을 가진 벡터를 출력한다. 이후 해당 벡터는 NN부분에 입력값으로 사용된다.
5. self attention과정은 주어진 입력 단어가 어떤 단어를 가리키는지 확인 시에 사용된다.
6. 워드 임베딩을 통해 단어가 들어오게 되면 Query weight, Key weight, Value weight와 연산되어 각각 QKV Vector를 구할 수 있게 된다.
6-1. 찾고자 하는 단어의 Query 벡터를 통해 해당 문장의 모든 Key와 전부 연산을 수행하고 가장 Query vector와 유사한 벡터가 큰 값을 갖게 된다. (벡터의 내적 원리)
6-2. 이러한 방식으로 생성된 Query vector가 모든 Key와 연산(내적)을 수행할 때 가장 큰 내적값을 보이는 것이 결국 유사한 단어이다.
6-3. 따라서 우선 Query와 Key vector를 내적해 score를 계산하고 해당하는 dimension의 루트값으로 나눠준다. (정규화 작업 수행) (정규화 작업을 수행하는 이유는 간혹 내적 값이 너무 큰 경우를 대비해 gradient 과정에서 좀 더 안정되게 하기 위함이다.)
6-4. 이후 모든 embedding에서 나와 softmax를 취한 값과 Value vector를 곱한 후 sumation 해준 값을 Z vector로 출력한다.
-> 정리를 하면, 워드 임베딩 된 벡터가 인풋으로 들어오면 각각 weight를 곱해 3개의 Query, Key, Value 벡터를 만든다. 그리고 Q와 K벡터를 내적해 Dimension의 루트를 씌워 정규화 작업을 수행한다. 그 후 softmax를 취하고 v벡터를 곱한 다음 Sumation을 통해 Z벡터로 최종 output을 제공한다.

< 앞서 정리한 Q,K,V 벡터 연산 방법.>
단, 인코더에선 Mask 수행 X
해당 연산은 single attention을 의미하고 그걸 동시에 여러 번 병렬처리한 것을 멀티헤드 어텐션이라고 한다.

이후 FFN에선 Z벡터가 입력값으로 들어오게 되고 ffn에 존재하는 한 개의 히든레이어의 weight와 연산 후 bias를 더한 뒤 ReLU 활성함수를 사용해 2048까지 dimension을 늘린다. 이후 다시 weight를 곱하고 bias를 더하는 방식으로 입력값과 동일한 dimension으로 되돌려 놓는다.
이 과정에서 주의할 점은 역시 마찬가지로 동일한 인코더에선 FF의 weight는 share되는 것이다.

  • 디코더에선 크게 3개의 모듈이 존재하는데, 인코더와 다른 Masked multi head attention부터 살펴보면 다음과 같다. (인코더 input - 독일 // 디코더 input - 영어)
  1. 디코딩 수행 시 주어진 단어 뒤에 존재하는 단어들은 무한대 값을 주어 추후 softmax를 취할때 0에 수렴하도록 한다.
  2. 디코더 역시 Q,K 내적해 SCORE를 계산하고 attention mask를 적용해 softmax를 취하면 주어진 단어의 뒷부분은 모두 0에 수렴한다. (이렇게 하는 이유는 번역 시 치팅을 방지하고 정상적으로 학습하기 위함이다.)
  3. 인코더-디코더 멀티 어텐션 네트워크의 경우 한 스텝 동안 인코딩의 input embedding vector가 들어오면 해당 결과인 z벡터로부터 K, V 벡터를 디코더에 제공한다. 그럼 디코더에 들어오는 라벨된 input 벡터가 Q벡터로 들어오고 인코더로부터 넘겨받은 k와 내적하여 내적 후 V벡터와 또 연산을 수행한다. 해당 결과인 output은 다음 디코더의 Q벡터로 입력되고 다음 디코더에도 존재하는 k와 내적해 v벡터와 또 연산을 수행하는 반복이 이루어진다. (논문에선 6회)

    -> 정리를 하면 다음과 같다.
    encoder self attention : 각 단어가 서로 어떤 연관성을 띄는지 (score계산해 유의어 파악)
    masked decoder self attention : 앞쪽에 있는 단어만 참고하기 (뒷 단어가 무엇인지 모르게 학습하는 방법)
    encoder-decoder attention : 디코더의 Q벡터가 인코더의 어떤 벡터에 더 많은 가중치를 두는지 계산하는 과정

-Output 즉 결과는 다음과 같다.

  • 출력된 확률값으로 다음 토큰인 단어를 예측.
    디코더에서 나온 output벡터는 NN을 거쳐 1차원 벡터로 나오게 되는데 softmax 활성함수를 거쳐 최종 확률이 높은 단어가 output으로 나온다.

전체 과정을 짧게 요약하면, 순서는 다음과 같다.
1. 문장 자체가 input으로 들어오면 각 단어는 워드 임베딩을 통해 벡터로 변환된다.
2. 워드 임베딩은 각각 Q,K,V로 바뀌고 각각의 attention을 거쳐 concat이후 weight와 연산되어 초기 Input dimension과 동일하게 output dimension을 맞춰준다.
3. 그 후 각 벡터(단어)들은 Fully-Connected Layer를 거친 후 다시 concat된다.
4. 해당 과정은 총 6번 반복되며 동일한 attention 내에선 weight를 share한다.
5. 그 후 인코더에서 생성된 벡터들이 각각 디코더의 K,V로 들어가고 디코더의 INPUT인 Q와 만나 연산을 수행하게 되어 번역할 단어를 출력해준다.

cf.
1. https://www.youtube.com/watch?v=URci3Eqz3hc
2. https://wikidocs.net/31379

profile
To be a DataScientist

0개의 댓글