최근 chatgpt가 나오며 떠들썩하던데 그런 chatgpt의 model architecture인 transformer에 대해 알아봅시다.
먼저 andrew ng님의 연구 논문 읽는 법에 대해 알아보고 갑시다.
- 한 글자 한 글자 다 읽는 것은 최악의 전략이다.
- 제목, 초록, 도표를 읽어라. 특히 딥러닝 분야에선 대부분 도표에 논문이 요약되어 있는 경우가 많다.
- 그다음 서론과 결론 도표를 다시 봐라.
- 그다음 논문을 위부터 아래로 쭉 읽어라 하지만 수식은 패스, 또 중요하지 않은 것 같은 부분도 패스
- 저자가 뭘 하려는지 생각해라
- 해당 논문의 접근법의 핵심이 뭔지 생각해라
- 너가 해당 논문에 나온 걸 어떻게 사용할 수 있는지 생각해라
- 다른 어떤 논문을 더 읽어볼지 생각해라
이제 본격적으로 논문을 읽어봅시다.
transformer가 나오기 이전 대부분의 모델들은 encdoer-decoder로 이루어진 recurrent 또는 convolutional 한 모델이었습니다.
sota모델 또한 해당 구조에 attention(디코더의 출력과 인코더의 입력들의 유사도를 구하는 방식)을 추가한 구조였죠.
transformer모델은 거기서 attention만 가져온 구조의 모델입니다.
transformer는 WMT 2014 English-to-French translation task에서 BLEU점수 41.8로 sota를 달성했습니다.
또 transformer의 장점은 학습시간이 짧다는 것입니다. 성능은 좋은데 비용은 적은, 가히 혁신이라 할 수 있는 모델인 것이죠.
모델의 전체적인 구조는 위와 같습니다.
대부분의 성능 좋은 문장 변환 모델이 인코더-디코더의 구조를 가지고 있는 만큼 transformer 또한 인코더-디코더의 구조를 가지고 있습니다.
인코더에선 sparse 한 representation vector(일반적으로 one hot vector)의 sequence를(x) 연속적인(continuous, dense) representation의 sequence로(z) 매핑합니다.
z가 주어졌을 때 디코더는 한 time당 한 단어를 생성합니다. 디코더는 auto-regressive 한 방식이고(teacher forcing방식이 들어감을 유추할 수 있습니다) 다음 time에서 단어를 생성할 때 이전 time들에서 생성된 단어들을 추가적인 인풋으로써 사용합니다.
자세한 것은 아래서 알아보겠습니다.
Embedding layer
다른 문장 변환 모델과 비슷하게 transformer 또한 학습된 embedding layer를 사용했습니다. transformer의 embedding layer의 특징은 poisitional encoding layer가 있단 것인데, transformer이전의 LSTM 등 의 모델은 인풋이 각 time step마다 들어가고 또 backpropagation시 그 순서대로 미분되니 문장의 순서 정보가 들어갔지만 transformer에선 인풋이 한 번에 들어가므로 transformer는 "monitor on the desk"와 "desk on the monitor"를 같은 문장이라 이해하게 됩니다.
이를 방지하기 위해 positional encoding이란 것을 사용하는데 임베딩된 인풋 문장, 예시로 [[0.1, 0.5, -0.4, 0.9], [0.3, 0.3, 0.4, 0.1], [-0.9, 0.4, -0.2, 0.6]]가 있을 때 여기에 positional encoding을 더하는 방식입니다.
그럼 positional encoding이란 뭐냐? 논문에 나와있는 수식은 아래와 같습니다.
여기서 pos는 각 단어의 위치이고 d_model은 embedding size입니다. 논문에서는 512로 정의됩니다.
sin함수는 pos가 짝수(0,2,4,6..)인 것에 대한 positional encoding이고 cos함수는 pos가 홀수(1,3,5,7...)인 단어에 대한 positional encoding입니다.
Encoder and Decoder Stacks
transformer는 각 인코더와 디코더를 쌓는 식으로 이루어져 있습니다. 위의 transformer의 아키텍처 사진은 인코더와 디코더의 스택이 각각 1,1 인 것으로 볼 수 있는 것이죠.
실제 논문에선 base모델은 각각 6,6만큼 쌓여있습니다. 물론 인코더와 디코더의 스택 높이가 달라도 문제없습니다.
Attention
이제부터 transformer의 핵심이라 할 수 있는 부분입니다. 사실 기존 lstm이나 gru에서의 attention은 디코더의 출력과 인코더의 입력의 유사도를 구하는 방식입니다. 그렇다면 transformer에서는 attention만으로 이루어져 있는데 어떻게 attention 연산을 했을까요?
먼저 인코더에서의 attention을 설명하겠습니다.
인코더에선 attention연산이 이루어지는 곳이 하나만 있습니다.(디코더는 사진에서 보이듯이 두 곳)
해당 layer에선 입력을 Q, K, V로 복제합니다. 이후 Q, K, V에 대해 각각 해당 layer의 weight인 W_Q, W_K, W_V와 내적 합니다.
이후 Q와 W_Q를 내적 한 행렬 Q를 K와 W_K를 내적한 K의 전치와 내적 합니다. 이후 이를 d_k(d_model/ num_heads, head에 대해선 이후 설명하겠습니다)의 제곱근으로 나누고 softmax연산을 한 뒤 V와 W_V를 내적 한 V와 내적 합니다. 봐보면 들어온 입력 하나만으로 attention연산을 진행했습니다. 이것이 transformer의 attention이 self-attention이라 불리는 이유입니다. 위 과정은 논문에 나온 수식과 정확히 일치합니다.
하지만 말로만으로 이해하기는 어려운 법이죠.
이에 대해 한 단어의 시점에서 봐보겠습니다.
한 단어에 대해 내적 한 모습입니다.
각 16, 4, 4, 16이
I와 I의 정보의 압축 값
I와 am의 정보의 압축 값
I와 a의 정보의 압축 값
I와 student의 정보의 압축 값 입니다.
나온 값들에 softmax연산을 하고 W_V와 내적 된 V에 곱해줍니다.
한 단어 기준으로 봤을때 행해지는 연산들을 한번에 하면 처음의 행렬 연산이 되는 것 입니다.
하지만 transformer에서의 attention의 특징은 하나 더 있습니다 바로 multi-head인데요.
multi-head란 attention연산에 들어가는 인풋의 차원을(d_model) 그대로 넣지 않고 multi-head의 값만큼 나눠서 넣는 것인데요.
논문에서 정한 값인 8로 예시를 들면 기존 512차원이었던 인풋을 d_model / multi-head = 64차원으로 나눠 각각 attention연산을 병렬적으로 진행합니다. 이후 값들을 다시 concat 해줍니다. 그리고 concat 된 결과를 한 번 더 linear연산을 해줍니다.
single-head에 비해 multi-head로 얻을 수 있는 이득은 문장들이 서로 좀 더 구석구석 비교할 수 있게 해 줘서 성능향상이 있었다 합니다.
Position-wise Feed Forward Networks
각 레이어에서 attention연산 이후엔 FFN레이어가 있는데 이 레이어는 원래 LSTM과 같은 이전 모델에서도 있던 full connected layer가 위치해 있습니다. 일반적인 fc layer가 같은 차원으로 linear연산을 하는 것과 달리 transformer에선 기존 차원 * 4의 크기만큼 늘린 다음 다시 기존 차원수만큼 줄입니다. 즉 fc layer가 두 개 있는 거죠. 또한 그 사이엔 ReLU가 들어갑니다. 코드로보면 아래와 같은 거죠.
fc = nn.Sequential(
nn.Linear(d_model, d_model * 4),
nn.ReLU(),
nn.Linear(d_model * 4, d_model),
)
Add & Norm
transformer에는 성능향상을 위한 부분이 또 있는데 바로 잔차연결이라 불리는 구조입니다.
논문의 나온 식만으로도 이해가 되실 거라 생각되는데 어떤 레이어의 인풋의 입력과 출력을 더한 후 normalization 하는 방법입니다.
만약 레이어를 attention layer라 한다면, 이를 시각화 할시 아래와 같습니다.
이는 원래 있던 기법인데 transformer논문에서는 Kaiming He, Xiangyu Zhang, Shaoqing Ren, and Jian Sun. Deep residual learning for image recognition. In Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition, pages 770–778, 2016. 논문을 참조하고 있다고 나와있습니다.
Attention in Decoder
디코더에서의 attention연산은 인코더에서 하던 것과 정말 조금 다른 점이 있습니다. 간단히 생각해도 디코더는 인코더의 출력을 반영해야 하는데 self-attention만 한다면 인코더의 출력을 반영할 수 없겠죠.
먼저 디코더의 attention layer는 두 개가 있는데 masked multi-head attention과 multi-head attention입니다.
먼저 masked multi-head attention layer에 대해 먼저 알아보죠.
저희가 학창 시절 공부할 때 선생님들이 문제 풀 때 답보지 말라했듯 모델도 Output을 예측할 때 input을 전부 알고 하면 안 되겠죠.
그럼 디코더는 아마 들어온 입력에 대해 해당 정답을 출력하기만 하는 모델이 되겠네요. 즉 인코더의 정보는 필요하지 않게 되죠.
이를 위해 저희는 디코더의 인풋을 가려줘야 하는데(masked) 사진으로 먼저 보면 아래와 같습니다.
masked multi-head attention layer에서는 인코더에서의 attention과 똑같이 self-attention을 해주고 나온 출력인 attention score를 위와 같이 masking 해줍니다.
위 사진에서 가로축에 있는 문자들이 K 세로축에 있는 문자들이 Q라고 보시면 됩니다. 무슨 말이냐면 위에 attention연산 설명할 때
attention score의 맨 윗줄은 Q의 첫 번째 단어와 K의 각 문자들의 attention score라고 했습니다.
<sos>가 주어졌을 때 <sos> je suis étudiant와의 각 attention score가 맨 윗줄에 위치한단 소리죠.
하지만 실제 inference시 <sos>가 주어졌을 때는 je suis étudiant라는 단어는 알지 못하니 위 사진과 같이 masking 하는 것이죠.
다음은 디코더의 Multi-head attention입니다. 사실 이 부분은 인코더의 attention과 크게 차이가 있지는 않습니다.
다른 점은 그저 해당 layer의 입력인 Q, K, V에서 K, V를 인코더의 최종출력으로부터 가져온다는 점입니다.
papaer link : https://arxiv.org/pdf/1706.03762.pdf
'deep learning' 카테고리의 다른 글
SeqGan 논문 겉햝기 (1) | 2023.09.26 |
---|---|
loss landscape에 대해 내가 이해한것 (0) | 2023.08.17 |