Natural Language Processing Theory : 트랜스포머(Transformer)
트랜스포머(Transformer)
어텐션(Attention)은 처음에 순환 신경망 기반 번역 모델의 병목을 보완하는 장치로 등장했습니다. 인코더와 디코더는 여전히 순환 신경망이었고, 어텐션은 그 사이를 잇는 다리 역할만 맡았습니다. 순환 신경망이 남아 있는 한 토큰을 하나씩 순서대로 처리해야 하므로, GPU의 병렬 연산 능력을 제대로 활용할 수 없었습니다.
2017년 구글의 바스와니(Ashish Vaswani) 연구진은 “Attention Is All You Need”라는 제목의 논문에서 질문을 뒤집었습니다. 어텐션이 이미 문장 안의 관계를 계산할 수 있다면, 순환 구조 자체가 필요한가? 연구진은 순환 신경망을 완전히 걷어내고 셀프 어텐션과 단순한 피드포워드 신경망만으로 인코더와 디코더를 구성했으며, 이 모델을 트랜스포머(Transformer)라 이름 붙였습니다.
트랜스포머는 영어-독일어 번역에서 당시 최고 성능을 넘어섰을 뿐 아니라, 학습에 필요한 시간도 기존 모델의 일부에 불과했습니다. 이후 BERT, GPT를 비롯한 거의 모든 자연어 처리 모델이 트랜스포머를 기반으로 만들어졌고, 이미지와 음성, 단백질 구조 예측에까지 퍼져 나갔습니다.
전체 구조
트랜스포머는 시퀀스-투-시퀀스와 마찬가지로 인코더(Encoder)와 디코더(Decoder)로 이루어져 있습니다. 차이는 각 부분을 구성하는 블록입니다.
인코더(Encoder): 입력 문장 전체를 한꺼번에 받아, 각 토큰을 문맥이 반영된 벡터로 바꿉니다. 인코더 블록은멀티 헤드 셀프 어텐션과피드포워드 신경망두 개의 하위 층으로 구성됩니다.디코더(Decoder): 인코더의 출력을 참고하며 출력 문장을 한 토큰씩 생성합니다. 디코더 블록은마스크드 셀프 어텐션,인코더-디코더 어텐션,피드포워드 신경망세 개의 하위 층으로 구성됩니다.
원 논문의 기본 모델은 인코더 블록과 디코더 블록을 각각 6개씩 쌓았습니다. 모든 층이 주고받는 벡터의 차원을 \(d_{\text{model}} = 512\)로 통일했는데, 입력과 출력의 크기가 같으므로 블록을 몇 개든 그대로 이어 붙일 수 있습니다. 첫 인코더 블록이 만든 벡터는 두 번째 블록의 입력이 되고, 마지막 인코더 블록의 출력은 모든 디코더 블록에 전달됩니다.
| 구성 요소 | 기본 모델(Base) | 큰 모델(Big) |
|---|---|---|
| 인코더 · 디코더 블록 수 | 6 · 6 | 6 · 6 |
| 모델 차원 \(d_{\text{model}}\) | 512 | 1024 |
| 피드포워드 차원 \(d_{\text{ff}}\) | 2048 | 4096 |
| 어텐션 헤드 수 \(h\) | 8 | 16 |
| 매개변수 수 | 약 6,500만 개 | 약 2억 1,300만 개 |
입력 토큰은 먼저 단어 임베딩을 거쳐 \(d_{\text{model}}\) 차원의 벡터가 됩니다. 여기에 위치 정보를 더한 뒤 인코더 블록을 통과하고, 디코더의 마지막 출력은 선형 층과 소프트맥스를 거쳐 어휘 전체에 대한 다음 토큰의 확률이 됩니다.
위치 인코딩(Positional Encoding)
셀프 어텐션은 모든 토큰 쌍의 관계를 한꺼번에 계산하므로, 토큰의 순서를 알지 못합니다. 입력 토큰의 순서를 뒤섞어도 각 토큰이 받는 어텐션 결과는 같은 집합을 뒤섞은 것일 뿐입니다. “개가 사람을 물었다”와 “사람이 개를 물었다”를 구분하려면 각 토큰이 문장의 몇 번째 위치에 있는지 따로 알려 주어야 합니다.
트랜스포머는 각 위치마다 고유한 벡터를 만들어 토큰 임베딩에 더하는 방식을 사용하며, 이를 위치 인코딩(Positional Encoding)이라 합니다. 원 논문은 학습 없이 사인과 코사인 함수로 위치 벡터를 계산했습니다.
\(pos\)는 토큰의 위치이고 \(i\)는 벡터의 차원 번호입니다. 짝수 차원에는 사인을, 홀수 차원에는 코사인을 사용하며, 차원 번호가 커질수록 분모가 커져 주기가 길어집니다.
위 그래프는 \(d_{\text{model}} = 128\)일 때 세 개의 짝수 차원이 위치에 따라 어떻게 변하는지 보여 줍니다. 낮은 차원은 위치가 하나만 바뀌어도 값이 크게 출렁이고, 높은 차원은 수십 위치에 걸쳐 천천히 변합니다. 시계의 초침, 분침, 시침이 함께 시각을 나타내듯, 빠르게 도는 차원과 느리게 도는 차원의 조합이 위치마다 서로 다른 무늬를 만듭니다.
사인 함수를 사용하는 데는 이유가 있습니다. 삼각함수의 덧셈 정리에 의해, 위치 \(pos + k\)의 인코딩은 위치 \(pos\)의 인코딩에 \(k\)에만 의존하는 회전 행렬을 곱한 것으로 표현됩니다. 따라서 모델은 절대 위치뿐 아니라 “세 칸 뒤”와 같은 상대 위치를 선형 변환으로 쉽게 알아낼 수 있습니다. 또한 학습 때 보지 못한 긴 문장에도 위치 벡터를 계산할 수 있습니다.
- Tip : 이후의 모델은 위치 인코딩 방식을 다양하게 바꾸었습니다. BERT와 GPT는 위치마다 벡터를 학습하는 위치 임베딩을 사용했고, 최근의 대규모 언어 모델은 쿼리와 키 벡터를 위치에 따라 회전시켜 상대 위치를 직접 반영하는
회전 위치 임베딩(Rotary Position Embedding, RoPE)을 널리 사용합니다.
멀티 헤드 어텐션(Multi-Head Attention)
어텐션에서 다룬 스케일드 닷-프로덕트 어텐션은 쿼리마다 하나의 가중치 분포를 만듭니다. 그런데 한 단어는 문장 안의 여러 단어와 서로 다른 이유로 관계를 맺습니다. “그녀는 어제 산 책을 동생에게 주었다”에서 “주었다”는 누가 주었는지(그녀), 무엇을 주었는지(책), 누구에게 주었는지(동생)를 모두 알아야 합니다. 이 관계들을 하나의 분포에 섞으면 서로 희석됩니다.
멀티 헤드 어텐션(Multi-Head Attention)은 어텐션을 여러 개 병렬로 수행해 각기 다른 관계를 따로 포착하는 방법입니다. 각 어텐션을 헤드(Head)라 하며, 헤드마다 쿼리, 키, 값을 만드는 가중치 행렬을 따로 가집니다.
각 헤드는 \(d_{\text{model}}\) 차원의 입력을 더 작은 \(d_{k} = d_{\text{model}} / h\) 차원으로 사영한 뒤 어텐션을 계산합니다. 기본 모델에서는 \(512 / 8 = 64\) 차원입니다. 여덟 개 헤드의 출력을 이어 붙이면 다시 512 차원이 되고, 마지막으로 행렬 \(W^{O}\)를 곱해 헤드들의 정보를 섞습니다. 차원을 헤드 수만큼 나누어 쓰므로 전체 연산량은 512 차원 어텐션 하나와 거의 같습니다.
학습된 트랜스포머의 헤드를 들여다보면 실제로 역할이 나뉘는 모습이 관찰됩니다. 어떤 헤드는 바로 앞 단어에 집중하고, 어떤 헤드는 동사와 그 목적어를 잇고, 어떤 헤드는 대명사와 그것이 가리키는 명사를 연결합니다. 이러한 역할은 사람이 지정한 것이 아니라 학습 과정에서 저절로 생겨난 것입니다. 다만 어텐션 가중치의 해석에서 다룬 것처럼, 헤드의 역할을 단정적으로 해석하는 데는 주의가 필요합니다.
피드포워드 신경망(Feed-Forward Network)
어텐션 층의 출력은 각 위치마다 피드포워드 신경망(Feed-Forward Network)을 한 번 더 통과합니다. 두 개의 선형 변환 사이에 ReLU 활성화 함수를 둔 단순한 구조입니다.
입력은 512 차원에서 2048 차원으로 네 배 넓어졌다가 다시 512 차원으로 돌아옵니다. 이 신경망은 모든 위치에 같은 가중치를 적용하되, 각 위치를 서로 독립적으로 처리합니다. 즉, 토큰 사이의 정보 교환은 어텐션이 맡고, 피드포워드 신경망은 각 토큰이 모아 온 정보를 개별적으로 가공하는 역할을 맡습니다.
피드포워드 신경망은 단순해 보이지만 트랜스포머 매개변수의 약 3분의 2를 차지합니다. 학습된 언어 모델을 분석한 연구에서는 피드포워드 층이 “프랑스의 수도는 파리”와 같은 사실 지식을 저장하는 기억 장치처럼 동작한다는 결과가 보고되기도 했습니다.
잔차 연결과 층 정규화
트랜스포머는 블록을 깊게 쌓으므로 순환 신경망에서 다룬 기울기 소실 문제가 다시 나타날 수 있습니다. 이를 막기 위해 모든 하위 층에 두 가지 장치를 붙였습니다.
잔차 연결(Residual Connection): 하위 층의 출력에 입력을 그대로 더합니다. 하위 층은 입력 전체를 새로 만드는 대신 입력에 더할 변화량만 학습하면 되고, 역전파 때 기울기가 덧셈 경로를 따라 줄어들지 않고 아래층까지 전달됩니다.층 정규화(Layer Normalization): 각 토큰의 벡터를 그 벡터 자신의 평균과 분산으로 정규화합니다. 값의 분포가 층을 거치며 크게 흔들리지 않도록 잡아 주어 학습을 안정시킵니다.
배치 정규화(Batch Normalization)는 미니 배치 안의 여러 샘플에 걸쳐 같은 특징의 평균과 분산을 구했습니다. 그러나 문장은 길이가 제각각이고, 생성할 때는 한 문장씩 처리하므로 배치 통계가 안정적이지 않습니다. 층 정규화는 한 토큰 벡터의 차원들에 걸쳐 평균 \(\mu\)와 분산 \(\sigma^{2}\)을 구하므로, 배치 크기나 문장 길이와 무관하게 동작합니다.
원 논문은 하위 층을 통과한 뒤 잔차를 더하고 정규화하는 \(\text{LayerNorm}(\mathbf{x} + \text{Sublayer}(\mathbf{x}))\) 순서를 사용했으며, 이를 Post-LN이라 합니다. 이후 GPT-2를 비롯한 많은 모델은 정규화를 하위 층 앞에 두는 \(\mathbf{x} + \text{Sublayer}(\text{LayerNorm}(\mathbf{x}))\) 순서, 즉 Pre-LN을 사용합니다. Pre-LN은 잔차 경로가 정규화를 거치지 않고 맨 아래까지 곧게 이어지므로, 층이 깊어져도 학습이 안정적입니다.
디코더의 두 가지 어텐션
디코더는 인코더와 달리 어텐션을 두 번 사용합니다.
첫 번째는 마스크드 셀프 어텐션(Masked Self-Attention)입니다. 디코더는 출력 문장을 한 토큰씩 생성하므로, 세 번째 토큰을 예측할 때 네 번째 이후의 토큰은 아직 존재하지 않습니다. 그런데 학습할 때는 교사 강요를 위해 정답 문장 전체를 한꺼번에 넣으므로, 그대로 셀프 어텐션을 계산하면 각 위치가 미래의 정답을 훔쳐볼 수 있습니다. 이를 막기 위해 각 위치가 자신과 그 이전 위치만 볼 수 있도록 점수 행렬의 위쪽 삼각형을 가립니다.
가리는 방법은 간단합니다. 소프트맥스를 적용하기 전에, 보면 안 되는 위치의 점수를 \(-\infty\)로 바꿉니다. \(e^{-\infty} = 0\)이므로 해당 위치의 어텐션 가중치는 정확히 0이 됩니다. 세 토큰으로 된 문장에서 두 번째 토큰의 점수가 \([1.0, \ 2.0, \ 3.0]\)이라면, 세 번째 위치를 가려 다음과 같이 계산합니다.
\[[1.0, \ 2.0, \ -\infty] \ \xrightarrow{\text{softmax}} \ \left[ \frac{e^{1}}{e^{1} + e^{2}}, \ \frac{e^{2}}{e^{1} + e^{2}}, \ 0 \right] \approx [0.269, \ 0.731, \ 0]\]가장 높은 점수 3.0을 가진 미래 위치는 완전히 무시되고, 나머지 두 위치가 전체 주의를 나눠 가집니다. 이렇게 하면 문장 전체를 한 번에 넣어 모든 위치의 다음 토큰 예측을 병렬로 학습하면서도, 각 위치는 생성할 때와 똑같은 조건에서 학습됩니다.
두 번째는 인코더-디코더 어텐션(Encoder-Decoder Attention)입니다. 쿼리는 디코더에서, 키와 값은 인코더의 마지막 출력에서 가져옵니다. 어텐션에서 다룬 원래의 번역 어텐션과 같은 역할로, 디코더가 출력 토큰을 만들 때 원문의 어느 부분을 볼지 결정합니다. 서로 다른 두 문장 사이의 어텐션이므로 교차 어텐션(Cross-Attention)이라고도 부릅니다.
학습 방법
트랜스포머는 구조만큼이나 학습 방법에서도 몇 가지 중요한 기법을 사용했습니다.
먼저 학습률을 고정하지 않고, 학습 초반에는 0에서 시작해 서서히 키운 뒤 이후에는 천천히 줄이는 방식을 사용했습니다. 처음 4,000 단계 동안 학습률을 선형으로 증가시키는데, 이 구간을 웜업(Warmup)이라 합니다. 학습 초기에는 가중치가 무작위 상태라 기울기의 방향이 불안정하고, Adam과 같은 최적화 알고리즘의 이동 평균도 아직 제대로 쌓이지 않았습니다. 이때 큰 학습률로 출발하면 학습이 발산하기 쉬우므로, 작은 걸음으로 시작해 방향이 안정된 뒤에 속도를 올리는 것입니다.
또한 정답 토큰의 목표 확률을 1이 아닌 0.9로 두고, 나머지 0.1을 다른 토큰에 고르게 나누는 레이블 스무딩(Label Smoothing)을 적용했습니다. 모델이 지나치게 확신하지 않도록 막는 정칙화 기법으로, 번역에서 “학교”를 “school”로도 “the school”로도 옮길 수 있듯 정답이 하나로 정해지지 않는 언어의 특성과도 잘 맞습니다.
이렇게 학습한 큰 모델은 WMT 2014 영어-독일어 번역에서 BLEU 28.4점을 기록해 기존 최고 성능을 2점 이상 앞섰습니다. 학습에는 GPU 8개로 3.5일이 걸렸는데, 이는 당시 최고 수준 모델들이 사용한 계산량의 일부에 불과했습니다.
순환 신경망과의 비교
트랜스포머가 순환 신경망을 빠르게 대체한 이유는 층 하나의 계산 특성을 비교하면 분명해집니다. \(n\)은 문장 길이, \(d\)는 벡터 차원입니다.
| 층 종류 | 층당 연산량 | 순차 연산 횟수 | 가장 먼 두 토큰 사이 경로 |
|---|---|---|---|
| 셀프 어텐션 | \(O(n^{2} \cdot d)\) | \(O(1)\) | \(O(1)\) |
| 순환 신경망 | \(O(n \cdot d^{2})\) | \(O(n)\) | \(O(n)\) |
순차 연산 횟수는 앞의 계산이 끝나야 다음 계산을 할 수 있는 단계의 수입니다. 순환 신경망은 문장 길이만큼 차례를 기다려야 하지만, 셀프 어텐션은 모든 위치를 동시에 계산합니다. GPU는 수천 개의 연산을 병렬로 수행하는 장치이므로, 이 차이는 학습 속도의 차이로 직결됩니다. 학습이 빠르다는 것은 같은 시간에 더 큰 모델을 더 많은 데이터로 학습할 수 있다는 뜻이며, 이는 다음 강좌에서 다룰 사전 학습 언어 모델이 등장하는 바탕이 되었습니다.
경로 길이는 두 토큰의 정보가 서로 닿기까지 거쳐야 하는 단계 수입니다. 경로가 짧을수록 기울기가 잘 전달되어 먼 거리의 관계를 학습하기 쉽습니다. 반면 층당 연산량은 셀프 어텐션이 \(n^{2}\)에 비례하므로, 문장이 벡터 차원보다 훨씬 길어지면 순환 신경망보다 불리해집니다.
트랜스포머 정리
| 구성 요소 | 역할 | 해결한 문제 |
|---|---|---|
| 셀프 어텐션 | 문장 안의 모든 토큰 쌍의 관계를 계산 | 순차 처리, 먼 거리 의존성 |
| 멀티 헤드 어텐션 | 여러 관계를 서로 다른 헤드에서 따로 포착 | 하나의 분포에 여러 관계가 섞이는 문제 |
| 위치 인코딩 | 각 위치에 고유한 벡터를 더함 | 셀프 어텐션이 순서를 모르는 문제 |
| 피드포워드 신경망 | 각 위치의 벡터를 개별적으로 가공 | 어텐션만으로 부족한 비선형 변환 |
| 잔차 연결 · 층 정규화 | 입력을 더하고 벡터 단위로 정규화 | 깊은 층에서의 기울기 소실과 불안정 |
| 마스크드 셀프 어텐션 | 미래 위치의 점수를 \(-\infty\)로 가림 | 병렬 학습 중 정답을 훔쳐보는 문제 |
| 인코더-디코더 어텐션 | 디코더가 인코더 출력을 참고 | 출력 토큰과 원문의 대응 |
트랜스포머는 새로운 연산을 발명했다기보다, 이미 있던 부품을 가장 단순한 형태로 재조합한 모델입니다. 어텐션, 잔차 연결, 정규화는 모두 이전에도 쓰이던 기법이었지만, 순환 구조를 과감히 제거하고 병렬 처리에 최적화된 형태로 묶어 낸 것이 결정적이었습니다.
트랜스포머의 인코더와 디코더는 이후 따로 떨어져 각자의 길을 걷게 됩니다. 인코더만 사용해 문장을 이해하는 BERT, 디코더만 사용해 문장을 생성하는 GPT, 둘 다 사용하는 T5가 대표적입니다. 다음 강좌에서는 이 모델들이 대량의 텍스트로 미리 학습하는 방법을 다룹니다.
- Tip : PyTorch에서는
torch.nn.Transformer,torch.nn.TransformerEncoderLayer,torch.nn.MultiheadAttention으로 트랜스포머의 구성 요소를 바로 사용할 수 있습니다.TransformerEncoderLayer의norm_first=True인자를 지정하면 Pre-LN 구조가 됩니다.
- Writer by : 윤대희
댓글 남기기