논문 리뷰: 트랜스포머 (Attention is All You Need)
개요 및 요약
트랜스포머transformer는 Ashish Vaswani 외 7명이 2017년 논문 『Attention Is All You Need』1에서 제안한 신경망 구조다. 순환신경망(RNN)이나 합성곱신경망(CNN) 없이 어텐션만으로 인코더-디코더 구조를 구성한 것이 이 논문의 핵심이다. 최근에는(2026년 기준) 우수한 성능을 발휘하는 인공지능 모델은 분야를 막론하고(자연어처리, 이미지 분석 등) 트랜스포머의 구조를 기반으로 하고 있다.
본 리뷰는 트랜스포머의 구조를 수학적으로 설명하는 데에만 집중한다. 학습에 쓴 세팅이나 기계번역 벤치마크 점수, 실험 결과와 그에 대한 논의 등은 생략한다. 서술 순서 역시 논문의 목차를 따르지 않는다. 대신 입력 벡터가 인코더에 들어가서 디코더를 거쳐 출력 확률벡터가 되어 나오기까지, 계산이 실제로 이루어지는 과정을 따라 순서대로 설명한다.
표기법은 논문과 다르다. 논문은 데이터를 행에 쌓는 행벡터를 기준으로 되어있지만, 수학에서는 열벡터가 기본이므로 이 글의 벡터와 행렬 표기는 모두 열벡터를 기준으로 한다. 여기서 $\operatorname{Softmax}$는 입력된 행렬의 각 열에 소프트맥스를 취하는 함수를 말한다.
$$ \operatorname{Attention}(\mathbf{Q}, \mathbf{K}, \mathbf{V}) = \mathbf{V} \operatorname{Softmax} \left( \frac{\mathbf{K}^{\mathsf{T}} \mathbf{Q}}{\sqrt{d_{k}}} \right) \tag{column notation} $$
$$ \operatorname{Softmax}(\mathbf{X}) := \begin{bmatrix} \underset{\vert}{\overset{\vert}{\operatorname{softmax}(\mathbf{x}_{1})}} & \cdots & \underset{\vert}{\overset{\vert}{\operatorname{softmax}(\mathbf{x}_{\ell})}} \end{bmatrix}, \quad \mathbf{X} = \begin{bmatrix} \mathbf{x}_{1} & \cdots & \mathbf{x}_{\ell}\end{bmatrix} $$
논문에서는 행벡터를 기준으로 하기에 아래와 같은 표기를 따른다.
$$ \operatorname{Attention}(\mathbf{Q}, \mathbf{K}, \mathbf{V}) = \operatorname{softmax} \left( \frac{\mathbf{Q} \mathbf{K}^{\mathsf{T}}}{\sqrt{d_{k}}} \right) \mathbf{V} \tag{row notation} $$
트랜스포머의 구조를 요약하면 아래와 같다. 각 부분의 구체적인 의미는 아래의 각 절을 참고하라. $\operatorname{MultiHead}$를 멀티헤드 어텐션, $\operatorname{FF}$를 순방향신경망, $I$를 항등변환, $\operatorname{LN}$을 🔒(26/08/29)층 정규화라고 하자. $\operatorname{EncBlock}$을 다음과 같이 정의한다.
$$ \operatorname{EncBlock} = \operatorname{LN} \circ (I + \operatorname{FF}) \circ \operatorname{LN} \circ (I + \operatorname{MultiHead}) $$
$\operatorname{PE}$를 위치인코딩이라 하자. 트랜스포머의 인코더 $\operatorname{Encoder}$는 아래와 같은 합성함수이다.
$$ \operatorname{Encoder} = \overbrace{\operatorname{EncBlock}_{N} \circ \operatorname{EncBlock}_{N-1} \circ \cdots \circ \operatorname{EncBlock}_{1}}^{N} \circ \operatorname{PE} $$
$\operatorname{MaskMH}$를 마스크드 멀티헤드 어텐션, $\operatorname{CrossMH}$를 크로스 멀티헤드 어텐션이라 하자. $\operatorname{DecBlock}$을 다음과 같이 정의한다.
$$ \operatorname{DecBlock} = \operatorname{LN} \circ (I + \operatorname{FF}) \circ \operatorname{LN} \circ (I + \operatorname{CrossMH}) \circ \operatorname{LN} \circ (I + \operatorname{MaskMH}) $$
트랜스포머의 디코더 $\operatorname{Decoder}$는 아래와 같은 합성함수이다.
$$ \operatorname{Decoder} = \overbrace{\operatorname{DecBlock}_{N} \circ \operatorname{DecBlock}_{N-1} \circ \cdots \circ \operatorname{DecBlock}_{1}}^{N} \circ \operatorname{PE} $$
인코더와 디코더를 거쳐 얻은 출력 $\mathbf{S}_{N} \in \mathbb{R}^{d_{\text{model}} \times m}$에 학습되는 선형변환 $\mathbf{W}_{\text{voca}}$를 곱하고 소프트맥스를 취하면, 각 위치에서 다음에 올 토큰에 대한 확률을 얻는다. 이것이 트랜스포머가 다음 단어를 예측하는 최종적인 확률벡터이다.
$$ \mathbf{P} = \operatorname{Softmax}(\mathbf{W}_{\text{voca}} \mathbf{S}_{N}) \in \mathbb{R}^{|\mathcal{V}| \times m} $$
트랜스포머의 구조
인코더
트랜스포머의 인코더는 크게 세 부분으로 구성되어있다. 위치 인코딩을 담당하는 $\operatorname{PE}$, 어텐션을 수행하는 $\operatorname{MultiHead}$, 그리고 각 위치의 벡터를 위치마다 독립적으로 변환하는 순방향 신경망 $\operatorname{FF}$이다. $\operatorname{MultiHead}$와 $\operatorname{FF}$의 합성을 하나의 블럭 $\operatorname{EncBlock}$이라고 하면, 인코더는 아래와 같이 간단히 표현된다.
$$ \operatorname{Encoder} = \overbrace{\operatorname{EncBlock}_{N} \circ \operatorname{EncBlock}_{N-1} \circ \cdots \circ \operatorname{EncBlock}_{1}}^{N} \circ \operatorname{PE} $$
논문에서는 똑같은 구조의 블럭을 $N = 6$개 쌓았으며, 잔차 연결이 가능하도록 모든 서브층의 출력 차원을 $d_{\text{model}} = 512$로 통일했다고 한다. 정확하게 말하자면 $\operatorname{MultiHead}$와 $\operatorname{FF}$가 그대로 합성되는 것은 아니고, 각 서브층 마다 잔차 연결(스킵 커넥션)을 더한 뒤 🔒(26/08/29)층 정규화 $\operatorname{LayerNorm}$을 적용한다고 한다.
$$ \mathbf{x} \mapsto \operatorname{LayerNorm}\left( \mathbf{x} + \operatorname{Sublayer}(\mathbf{x}) \right) $$
따라서 항등함수를 $I$라 표기하면 $\operatorname{EncBlock}$은 구체적으로 다음과 같은 합성이다.
$$ \operatorname{EncBlock} = \operatorname{LayerNorm} \circ (I + \operatorname{FF}) \circ \operatorname{LayerNorm} \circ (I + \operatorname{MultiHead}) $$
각 부분이 어떻게 작동하는지 구체적으로 알아보자.
위치 인코딩
입력 데이터를 임베딩한 벡터들의 수열을 $\mathbf{X} = \begin{bmatrix} \mathbf{x}_{1} & \cdots & \mathbf{x}_{n} \end{bmatrix}$ $(\mathbf{x}_{i} \in \mathbb{R}^{d_{\text{model}}})$라 하자. 이하 입력 데이터라 함은 이렇게 임베딩된 벡터들을 가리키는 것으로 한다. $\mathbf{X}$가 어텐션에 입력되기 전에 $\mathbf{X}$로부터 쿼리/키/밸류 벡터를 만든다. 인코더는 이 셋을 모두 같은 $\mathbf{X}$로부터 만드는데 이를 셀프 어텐션이라 하며, 다음 절에서 자세히 소개한다. $\mathbf{x}_{i}$에 대응하는 쿼리 $\mathbf{q}_{i}$는 모든 키와 적합도를 재고 그 비중대로 모든 밸류를 섞으므로, 어텐션의 출력인 컨텍스트 벡터 $\mathbf{c}_{i}$는 아래와 같이 $\mathbf{x}_{1}$부터 $\mathbf{x}_{n}$까지 전체 데이터를 참조한 함수가 된다.
$$ \mathbf{c}_{i} = \mathbf{c}_{i}(\mathbf{x}_{1}, \dots, \mathbf{x}_{n}), \quad \forall i $$
이는 데이터를 순차적으로 참조하여 은닉벡터를 만드는 순환신경망, 양방향 순환신경망 등과 차이가 있다. 입력 데이터의 순차적 참조는 구조적인 성능 저하를 일으키며 병렬계산이 불가능하다는 단점이 있고, 트랜스포머 논문에서는 이를 해결하고자 했다. 트랜스포머는 아예 순차적 참조를 버리고, 위 식처럼 모든 위치를 동시에 참조하는 셀프 어텐션만으로 문맥을 파악한다.
문제는 이렇게 하면 각각의 출력 $\mathbf{c}_{i}$가 자신이 참조한 데이터들이 수열 내에서 몇번째에 위치했는지에 대한 정보를 갖지 못한다는 점이다. 순차적 참조는 피하면서도 $\mathbf{X}$는 순서가 있는 데이터라는 것을 표현하기 위해 사용하는 것이 위치 인코딩이다. 이 논문에서는 구체적으로 사인파 위치 인코딩을 도입하였다. $\text{pos}$번째 위치에 대응되는 벡터를 내놓는 함수 $\operatorname{pe} : \mathbb{N} \to \mathbb{R}^{d_{\text{model}}}$를 아래와 같이 정의하자. 성분의 인덱스는 $1$부터 세며, $i$는 $1 \le i \le d_{\text{model}}/2$의 범위를 움직인다. 논문에서는 성분의 인덱스를 $0$부터 세는 것에 주의하라.
$$ \operatorname{pe}(\text{pos}) = \begin{bmatrix} \sin \left( \text{pos} / 10000^{0/d_{\text{model}}} \right) \\ \cos \left( \text{pos} / 10000^{0/d_{\text{model}}} \right) \\ \sin \left( \text{pos} / 10000^{2/d_{\text{model}}} \right) \\ \cos \left( \text{pos} / 10000^{2/d_{\text{model}}} \right) \\ \vdots \\ \sin \left( \text{pos} / 10000^{(d_{\text{model}}-2)/d_{\text{model}}} \right) \\ \cos \left( \text{pos} / 10000^{(d_{\text{model}}-2)/d_{\text{model}}} \right) \\ \end{bmatrix} \in \mathbb{R}^{d_{\text{model}}} $$
$$ \begin{align*} \left[ \operatorname{pe}(\text{pos}) \right]_{2i-1} &:= \sin \left( \text{pos} / 10000^{2(i-1)/d_{\text{model}}} \right) \\ \left[ \operatorname{pe}(\text{pos}) \right]_{2i} &:= \cos \left( \text{pos} / 10000^{2(i-1)/d_{\text{model}}} \right) \end{align*}, \quad i = 1, 2, \cdots, d_{\text{model}}/2 $$
홀수번째 성분은 사인, 짝수번째 성분은 코사인으로, 성분의 쌍 $(2i-1, 2i)$마다 파장이 $2\pi \cdot 10000^{2(i-1)/d_{\text{model}}}$인 사인파가 하나씩 대응된다. 논문에서는 인덱스를 $0$부터 세어 이를 $PE_{(\text{pos}, 2i)}$, $PE_{(\text{pos}, 2i+1)}$로 표기하는데, 이 글에서는 아래에서 정의할 위치 인코딩 $\operatorname{PE}$와 구분하기 위해 소문자로 적었다. 아래와 같은 매핑을 위치 인코딩이라 하겠다.
$$ \operatorname{PE}(\mathbf{x}_{\text{pos}}) = \mathbf{x}_{\text{pos}} + \operatorname{pe}(\text{pos}) $$
$\mathbf{x}_{\text{pos}}$는 데이터의 추상화된 정보를 의미하고 $\operatorname{pe}(\text{pos})$는 이것이 데이터 수열에서 어디에 위치하는지에 대한 정보를 의미한다. 그러면 $\operatorname{pe}(\text{pos})$는 원래 벡터에 대한 섭동perturbation으로 이해할 수 있다. 원래 벡터가 갖는 의미를 등장 위치에 따라 서로 구분 가능하도록 만드는 것이다.
가령 $\mathbf{x}$가 "남자"라는 단어를 의미하는 벡터라고 하자. 위치 인코딩을 거치면 문장 처음에 나오는 "남자"는 $\mathbf{x}_{1} + \operatorname{pe}(1)$, 문장 $10$번째에 나오는 "남자"는 $\mathbf{x}_{10} + \operatorname{pe}(10)$이 되어, 같은 단어라도 등장하는 위치에 따라 서로 다른 벡터로 구분된다. 아래 그림은 $d_{\text{model}} = 2$일 때 벡터 $\mathbf{x} = (1, 1)$에 $\operatorname{pe}(1)$, $\operatorname{pe}(10)$, $\operatorname{pe}(100)$, $\operatorname{pe}(1000)$을 각각 더한 점들을 좌표평면에 나타낸 것이다.

셀프 어텐션
트랜스포머의 인코더에서는 각 위치에서 만든 쿼리가 모든 위치의 키와 비교되고, 그 결과에 따라 모든 위치의 밸류를 가중합한다. 따라서 셀프 어텐션의 출력 벡터는 입력 수열 전체를 참조할 수 있다. $\mathbf{X}$의 각 열에 위치 인코딩 $\operatorname{PE}$를 적용시킨 것을 $\operatorname{PE}(\mathbf{X})$와 같이 표기하자.
$$ \mathbf{X}_{0} = \operatorname{PE}(\mathbf{X}) = \begin{bmatrix} \operatorname{PE}(\mathbf{x}_{1}) & \cdots & \operatorname{PE}(\mathbf{x}_{n}) \end{bmatrix} $$
쿼리/키/밸류 행렬 $\mathbf{Q}$, $\mathbf{K}$, $\mathbf{V}$는 아래와 같이 모두 같은 입력 데이터 $\mathbf{X}_{0}$로부터 만든다. 이렇게 셋을 같은 데이터에서 얻는 방식을 셀프 어텐션이라 한다.
$$ \mathbf{Q} = \mathbf{W}^{Q} \mathbf{X}_{0}, \qquad \mathbf{K} = \mathbf{W}^{K} \mathbf{X}_{0}, \qquad \mathbf{V} = \mathbf{W}^{V} \mathbf{X}_{0} $$
여기서 $\mathbf{W}^{Q}$, $\mathbf{W}^{K}$, $\mathbf{W}^{V}$는 학습되는 가중치 행렬이며, 크기는 각각 다음과 같다.
$$ \mathbf{W}^{Q} \in \mathbb{R}^{d_{k} \times d_{\text{model}}}, \qquad \mathbf{W}^{K} \in \mathbb{R}^{d_{k} \times d_{\text{model}}}, \qquad \mathbf{W}^{V} \in \mathbb{R}^{d_{v} \times d_{\text{model}}} $$
그리고 어텐션의 형태는 스케일드 닷프로덕트 어텐션을 채택하였다. 스코어 함수로 쿼리와 키를 내적한 값에 스케일링 팩터 $1/\sqrt{d_{k}}$를 곱해주는 것이다. 이렇게하면 소프트맥스가 포화하는 것을 방지할 수 있는데, 자세한 것은 스케일드 닷프로덕트 어텐션 문서를 참고하라.
$$ \begin{align*} \operatorname{Attention}(\mathbf{X}_{0}) &:= \mathbf{V} \operatorname{Softmax} \left( \frac{\mathbf{K}^{\mathsf{T}} \mathbf{Q}}{\sqrt{d_{k}}} \right) \\ &= \left( \mathbf{W}^{V} \mathbf{X}_{0} \right) \operatorname{Softmax} \left( \frac{\left( \mathbf{W}^{K} \mathbf{X}_{0} \right)^{\mathsf{T}} \left( \mathbf{W}^{Q} \mathbf{X}_{0} \right)}{\sqrt{d_{k}}} \right) \tag{1} \end{align*} $$
여기서 $\operatorname{Softmax}$는 행렬의 각 열에 소프트맥스 $\operatorname{softmax}$를 취해서 각 열의 합이 $1$이 되게 하는 함수다. $(1)$의 번호는 논문의 수식 번호를 그대로 따른 것인데, 행벡터 기준인 논문에는 $\operatorname{Attention}(\mathbf{Q}, \mathbf{K}, \mathbf{V}) = \operatorname{softmax} \left( \mathbf{Q} \mathbf{K}^{\mathsf{T}} / \sqrt{d_{k}} \right) \mathbf{V}$로, 이 글과는 전치된 꼴로 적혀 있음에 주의하라.
멀티헤드 어텐션
$(1)$의 수식은 마치 합성곱 신경망(CNN)에서 채널의 수를 $1$로 두고 쓰는 것과 같다. 채널의 수가 적당히 크면 CNN의 성능이 좋아지는 것과 마찬가지로 트랜스포머에서도 어텐션을 병렬로 연결하여 다차원의 정보를 얻는다. 이를 멀티헤드 어텐션이라 한다. 즉 실제로 트랜스포머 내에서는 수식 $(1)$이 아니라 아래와 같이 작동한다. 각 헤드 $h$는 서로 다른 가중치 행렬 $\mathbf{W}_{h}^{Q}, \mathbf{W}_{h}^{K}, \mathbf{W}_{h}^{V}$로 각자 어텐션을 수행하여 서로 다른 부분공간에 담긴 정보를 뽑아낸다. $h$번째 헤드의 출력 $\operatorname{head}_{h}$는 다음과 같다.
$$ \begin{align*} \operatorname{head}_{h}(\mathbf{X}_{0}) &= \operatorname{Attention}_{h}(\mathbf{X}_{0}) \\ &= \left( \mathbf{W}_{h}^{V} \mathbf{X}_{0} \right) \operatorname{Softmax} \left( \frac{\left( \mathbf{W}_{h}^{K} \mathbf{X}_{0} \right)^{\mathsf{T}} \left( \mathbf{W}_{h}^{Q} \mathbf{X}_{0} \right)}{\sqrt{d_{k}}} \right) \\ \end{align*} $$
각 헤드의 출력을 하나로 합치면 아래와 같다. 논문에서는 헤드들을 세로로 이어붙인concatenate 뒤 하나의 사영 행렬 $\mathbf{W}^{O}$를 곱하지만, $\mathbf{W}^{O} = \begin{bmatrix} \mathbf{W}_{1}^{O} & \cdots & \mathbf{W}_{H}^{O} \end{bmatrix}$처럼 블록으로 나누면 이는 각 헤드에 $\mathbf{W}_{h}^{O}$를 곱해 더하는 아래 식과 같다. 자세한 것은 멀티헤드 어텐션 문서를 참고하라.
$$ \operatorname{MultiHead}(\mathbf{X}_{0}) = \sum_{h=1}^{H} \mathbf{W}_{h}^{O} \operatorname{head}_{h}(\mathbf{X}_{0}) $$
$\mathbf{W}_{h}^{Q}, \mathbf{W}_{h}^{K} \in \mathbb{R}^{d_{k} \times d_{\text{model}}}$와 $\mathbf{W}_{h}^{V} \in \mathbb{R}^{d_{v} \times d_{\text{model}}}$는 헤드마다 따로 학습되는 가중치 행렬이고, $\mathbf{W}_{h}^{O} \in \mathbb{R}^{d_{\text{model}} \times d_{v}}$는 헤드들을 다시 $d_{\text{model}}$차원으로 모으는 가중치 행렬이다. 논문에서는 헤드의 수를 $H = 8$로, 각 헤드의 차원을 $d_{k} = d_{v} = d_{\text{model}} / H = 64$로 두었다.
여기에 잔차층과 🔒(26/08/29)층 정규화를 거치면 다음과 같다. $\mathbf{X}_{0,1}$은 첫번째 인코더 블럭에서 첫번째 서브층까지 통과한 출력을 뜻하는 표기이다.
$$ \mathbf{X}_{0,1} = \operatorname{LayerNorm} \circ (I + \operatorname{MultiHead}) (\mathbf{X}_{0}) $$
$\mathbf{X}_{0,1}$의 각 열은 멀티헤드 셀프 어텐션을 통해 입력 수열 전체의 문맥을 반영하도록 갱신된 표현이다.
순방향 신경망
$\mathbf{X}_{0,1}$은 이제 순방향 신경망을 통과한다. 신경망 $\operatorname{F}: \mathbb{R}^{d_{\text{model}}} \to \mathbb{R}^{d_{\text{model}}}$을 아래와 같이 정의하자. 이때 $\operatorname{ReLU} : \mathbb{R}^{r} \to \mathbb{R}^{r}$는 임의의 차원 $r$에 대해서 입력 벡터/행렬의 각 성분에 렐루를 취하는 함수다.
$$ \left[ \operatorname{ReLU} \left( \mathbf{x} \right) \right]_{i} := \max\left\{ 0, x_{i} \right\} $$
$$ \operatorname{F}(\mathbf{x}) = \mathbf{W}_{2} \operatorname{ReLU} \left( \mathbf{W}_{1} \mathbf{x} + \mathbf{b}_{1} \right) + \mathbf{b}_{2}, \qquad \mathbf{x} \in \mathbb{R}^{d_{\text{model}}} \tag{2} $$
$$ \mathbf{W}_{1} \in \mathbb{R}^{d_{ff} \times d_{\text{model}}}, \quad \mathbf{W}_{2} \in \mathbb{R}^{d_{\text{model}} \times d_{ff}} $$
$$ \mathbf{b}_{1} \in \mathbb{R}^{d_{ff}}, \quad \mathbf{b}_{2} \in \mathbb{R}^{d_{\text{model}}} $$
논문에서는 입력과 출력의 차원이 $d_{\text{model}} = 512$, 은닉층의 차원이 $d_{ff} = 2048$이다. 이제 함수 $\operatorname{FF}: \mathbb{R}^{d_{\text{model}} \times n} \to \mathbb{R}^{d_{\text{model}} \times n}$를 입력된 행렬의 각 열벡터에 $\operatorname{F}$를 적용하는 것으로 정의하자.
$$ \begin{align*} \operatorname{FF}(\mathbf{X}) &= \begin{bmatrix} \operatorname{F}(\mathbf{x}_{1}) & \cdots & \operatorname{F}(\mathbf{x}_{n}) \end{bmatrix} \\ &= \mathbf{W}_{2} \operatorname{ReLU} \left( \mathbf{W}_{1} \mathbf{X} + \mathbf{b}_{1} \mathbf{1}^{\mathsf{T}} \right) + \mathbf{b}_{2} \mathbf{1}^{\mathsf{T}} \end{align*} $$
여기서 $\mathbf{1}$은 성분이 모두 $1$인 $n$차원 벡터이며, $\mathbf{b}_{1} \mathbf{1}^{\mathsf{T}}$와 $\mathbf{b}_{2} \mathbf{1}^{\mathsf{T}}$는 각각 편향 $\mathbf{b}_{1}$, $\mathbf{b}_{2}$를 $\mathbf{X}$의 모든 열에 더하는 브로드캐스트를 나타낸다.
셀프 어텐션이 위치들 사이에서 정보를 섞는 부분이라면, $\operatorname{FF}$는 각 위치의 벡터를 위치마다 독립적으로 변환하는 부분이다. $\operatorname{FF}$는 각 열에 동일한 $\operatorname{F}$를 따로따로 적용하므로, 어떤 위치의 출력도 다른 위치의 입력에 의존하지 않는다. 즉 이 서브층에서는 위치들 사이의 정보 혼합이 일어나지 않는다. 여기에도 마찬가지로 잔차 연결과 층 정규화가 적용되어, 첫번째 인코더 블럭의 최종 출력을 얻는다.
$$ \mathbf{X}_{1} = \operatorname{LayerNorm} \circ (I + \operatorname{FF}) (\mathbf{X}_{0,1}) $$
정리
지금까지의 과정을 정리하면 다음과 같다. 위치 인코딩을 거친 입력 $\mathbf{X}_{0}$가 멀티헤드 셀프 어텐션과 순방향 신경망이라는 두 서브층을 차례로 통과하며, 각 서브층에는 잔차 연결과 층 정규화가 따라붙는다.
$$ \begin{align*} \mathbf{X}_{0} &= \operatorname{PE}(\mathbf{X}) \\ \mathbf{X}_{0,1} &= \operatorname{LayerNorm} \circ (I + \operatorname{MultiHead}) (\mathbf{X}_{0}) \\ \mathbf{X}_{1} &= \operatorname{LayerNorm} \circ (I + \operatorname{FF}) (\mathbf{X}_{0,1}) = \operatorname{EncBlock}_{1}(\mathbf{X}_{0}) \end{align*} $$
논문에서는 이 블럭을 $N = 6$개 쌓았다고 한다. $k$번째 블럭의 출력을 $\mathbf{X}_{k} = \operatorname{EncBlock}_{k}(\mathbf{X}_{k-1})$라 하면 인코더 전체는 다음과 같은 합성함수이다.
$$ \operatorname{Encoder}(\mathbf{X}) = \overbrace{\operatorname{EncBlock}_{N} \circ \cdots \circ \operatorname{EncBlock}_{1}}^{N} \circ \operatorname{PE} (\mathbf{X}) = \mathbf{X}_{N} $$
인코더의 최종 출력 $\mathbf{X}_{N} \in \mathbb{R}^{d_{\text{model}} \times n}$은 아래에서 볼 디코더의 크로스 어텐션에서 키와 밸류로 참조된다.
디코더
디코더도 인코더와 마찬가지로 동일한 구조의 블럭 $\operatorname{DecBlock}$을 $N = 6$개 쌓은 구조이다. 다만 인코더 블럭이 $\operatorname{MultiHead}$와 $\operatorname{FF}$로 이루어진 두 개의 서브층을 가졌던 것과 달리, 디코더 블럭은 마스크드 어텐션층이 포함된 세 개의 서브층으로 이루어진다.
디코더는 학습단계와 추론단계에서 서로 다르게 작동하는데, 우선 학습할 때 어떻게 작동하는지 알아보자. 학습단계에서 디코더는 정답인 출력 전체를 한 번에 입력으로 사용한다. 입력 데이터 $\mathbf{X}$에 대한 정답을 $\mathbf{Y} = \begin{bmatrix} \mathbf{y}_{1} & \cdots & \mathbf{y}_{m} \end{bmatrix}$이라 하자. 인코더에서와 마찬가지로 위치 인코딩을 통과한다.
$$ \mathbf{S}_{0} = \operatorname{PE} (\mathbf{Y}) $$
효율적인 학습을 위해서 여기서도 셀프 어텐션을 사용하지만, $t$번째 위치의 쿼리가 자기보다 뒤에 있는 $\mathbf{y}_{t+1}, \dots, \mathbf{y}_{m}$을 참조하면 아직 맞혀야 할 정답을 미리 컨닝하는 셈이 된다. 그래서 미래 위치의 키에 대한 어텐션 확률이 $0$이 되게 하는 마스킹을 도입한다. 이렇게 마스킹된 셀프 어텐션이 디코더 블럭의 첫번째 서브층이다.
마스크드 어텐션
🔒(26/08/31)마스크드 어텐션은 특정한 키와 쿼리의 적합도를 $-\infty$로 강제하는 것을 말한다. 마스크 오퍼레이터 $\operatorname{Mask}$를 아래와 같이 정의하자.
$$ \begin{bmatrix} \operatorname{Mask}(\mathbf{K}^{\mathsf{T}} \mathbf{Q}/ \sqrt{d_{k}}) \end{bmatrix}_{ij} = \begin{cases} -\infty & i \gt j \\ \mathbf{k}_{i}^{\mathsf{T}} \mathbf{q}_{j} / \sqrt{d_{k}} & i \le j \end{cases} $$
여기서 행 인덱스 $i$는 키의 위치, 열 인덱스 $j$는 쿼리의 위치에 대응하므로, $i \gt j$인 성분을 $-\infty$로 두는 것은 각 위치의 쿼리가 자기보다 뒤에 있는 키를 보지 못하게 막는 것이다. 그러면 마스크 오퍼레이터가 적용된 멀티헤드 어텐션은 아래와 같다. $-\infty$는 소프트맥스를 거치면서 값이 $0$이 되어 대응하는 밸류 벡터를 전혀 참고하지 않게 된다.
$$ \begin{align*} \operatorname{Maskhead}_{h}(\mathbf{S}_{0}) &= \left( \mathbf{W}_{h}^{V} \mathbf{S}_{0} \right) \operatorname{Softmax} \circ \operatorname{Mask} \left( \frac{\left( \mathbf{W}_{h}^{K} \mathbf{S}_{0} \right)^{\mathsf{T}} \left( \mathbf{W}_{h}^{Q} \mathbf{S}_{0} \right)}{\sqrt{d_{k}}} \right) \\ \operatorname{MaskMH}(\mathbf{S}_{0}) &= \sum_{h=1}^{H} \mathbf{W}_{h}^{O} \operatorname{Maskhead}_{h}(\mathbf{S}_{0}) \end{align*} $$
여기에 잔차층과 층 정규화를 거치면 다음과 같다.
$$ \mathbf{S}_{0,1} = \operatorname{LayerNorm} \circ (I + \operatorname{MaskMH})(\mathbf{S}_{0}) $$
크로스 어텐션
두번째 서브층은 인코더의 최종 출력 $\mathbf{X}_{N}$을 참조하는 크로스 어텐션이다. 논문에서는 이를 인코더-디코더 어텐션이라 부른다. 첫번째 서브층의 출력 $\mathbf{S}_{0,1}$에서 쿼리를 만들고, 키와 밸류는 인코더의 출력 $\mathbf{X}_{N}$에서 만든다.
$$ \mathbf{Q} = \mathbf{W}^{Q} \mathbf{S}_{0,1}, \qquad \mathbf{K} = \mathbf{W}^{K} \mathbf{X}_{N}, \qquad \mathbf{V} = \mathbf{W}^{V} \mathbf{X}_{N} $$
쿼리와 키/밸류의 출처가 다를 뿐, 어텐션을 계산하는 방식은 셀프 어텐션과 같다. 여기서는 미래를 가릴 이유가 없으므로 마스크를 씌우지 않는다.
$$ \begin{align*} \operatorname{Crosshead}_{h}(\mathbf{S}_{0,1}; \mathbf{X}_{N}) &= \left( \mathbf{W}_{h}^{V} \mathbf{X}_{N} \right) \operatorname{Softmax} \left( \frac{\left( \mathbf{W}_{h}^{K} \mathbf{X}_{N} \right)^{\mathsf{T}} \left( \mathbf{W}_{h}^{Q} \mathbf{S}_{0,1} \right)}{\sqrt{d_{k}}} \right) \\ \operatorname{CrossMH}(\mathbf{S}_{0,1}; \mathbf{X}_{N}) &= \sum_{h=1}^{H} \mathbf{W}_{h}^{O} \operatorname{Crosshead}_{h}(\mathbf{S}_{0,1}; \mathbf{X}_{N}) \end{align*} $$
이 서브층 덕분에 디코더의 각 위치가 입력 수열 전체를 참조할 수 있다. 마찬가지로 잔차층과 층 정규화를 거친다.
$$ \mathbf{S}_{0,2} = \operatorname{LayerNorm} \circ (I + \operatorname{CrossMH})(\mathbf{S}_{0,1}) $$
순방향 신경망
세번째 서브층은 인코더와 완전히 동일한 순방향 신경망 $\operatorname{FF}$이다. 각 위치의 벡터를 위치마다 독립적으로 변환하며, 역시 잔차층과 층 정규화가 따라붙어 첫번째 디코더 블럭의 최종 출력을 얻는다.
$$ \mathbf{S}_{1} = \operatorname{LayerNorm} \circ (I + \operatorname{FF})(\mathbf{S}_{0,2}) $$
정리
지금까지의 세 서브층을 합성하면 디코더 블럭 $\operatorname{DecBlock}$이 된다. 층 정규화 $\operatorname{LayerNorm}$를 간단히 $\operatorname{LN}$이라 표기하면 다음과 같다.
$$ \begin{align*} &\operatorname{DecBlock}(\cdot\ ; \mathbf{X}_{N}) \\ &= \operatorname{LN} \circ (I + \operatorname{FF}) \circ \operatorname{LN} \circ (I + \operatorname{CrossMH}(\cdot\ ; \mathbf{X}_{N})) \circ \operatorname{LN} \circ (I + \operatorname{MaskMH}) \end{align*} $$
인코더와 마찬가지로 디코더도 이와 같은 구조의 블럭을 $N = 6$개 쌓는다. $k$번째 블럭의 출력을 $\mathbf{S}_{k} = \operatorname{DecBlock}_{k}(\mathbf{S}_{k-1}; \mathbf{X}_{N})$라 하면, 모든 크로스 어텐션 서브층이 같은 인코더 출력 $\mathbf{X}_{N}$을 키와 밸류로 참조하는 가운데 디코더 전체는 다음과 같은 합성이다.
$$ \begin{align*} &\operatorname{Decoder}(\mathbf{Y}; \mathbf{X}_{N}) \\ &= \overbrace{\operatorname{DecBlock}_{N}(\cdot\ ; \mathbf{X}_{N}) \circ \cdots \circ \operatorname{DecBlock}_{1}(\cdot\ ; \mathbf{X}_{N})}^{N} \circ \operatorname{PE}(\mathbf{Y}) = \mathbf{S}_{N} \end{align*} $$
마지막으로 디코더의 출력 $\mathbf{S}_{N} \in \mathbb{R}^{d_{\text{model}} \times m}$의 각 열을 어휘 집합에 대한 확률분포로 바꾼다. 어휘의 크기를 $|\mathcal{V}|$라 하면, 학습되는 선형변환 $\mathbf{W}_{\text{voca}} \in \mathbb{R}^{|\mathcal{V}| \times d_{\text{model}}}$을 곱한 뒤 각 열에 소프트맥스를 취한다.
$$ \mathbf{P} = \operatorname{Softmax}(\mathbf{W}_{\text{voca}} \mathbf{S}_{N}) \in \mathbb{R}^{|\mathcal{V}| \times m} $$
$\mathbf{P}$의 $t$번째 열은 $t$번째 위치까지 주어졌을 때 그 다음에 올 토큰에 대한 확률분포이다. 즉 위치 $t$의 출력이 정답의 그 다음 토큰을 가리키도록 학습한다. 정답의 첫 토큰을 예측하려면 그 앞에 무언가가 있어야 하므로, 실제로 디코더에 넣는 입력 $\mathbf{Y}$는 정답 수열을 한 칸 뒤로 밀고 맨 앞에 시작 토큰을 붙인 것이다.
손실함수
정답 토큰들의 인덱스를 $y_{1}, \dots, y_{m} \in \left\{ 1, \dots, |\mathcal{V}| \right\}$ 라 하자. $\mathbf{P}$의 $t$번째 열은 $t$번째 위치에 올 토큰의 확률벡터이므로, $\left[ \mathbf{P} \right]_{y_{t}, t}$는 모델이 정답 토큰 $y_{t}$ 에 매긴 확률이다. 손실함수는 이를 모든 위치에 대해 모은 교차 엔트로피이다.
$$ \mathcal{L} = - \frac{1}{m} \sum_{t=1}^{m} \log \left[ \mathbf{P} \right]_{y_{t}, t} $$
추론 과정
학습이 끝나 실제로 출력을 생성할 때는 정답을 알 수 없으므로 디코더를 앞서와 다르게 사용한다. 정답 수열 전체를 한꺼번에 넣는 대신, 시작 토큰에서 출발하여 자기회귀적으로 한 번에 한 토큰씩 생성한다. 인코더는 입력에 대해 한 번만 실행하고, 그 출력 $\mathbf{X}_{N}$을 모든 생성 단계의 크로스 어텐션에서 키와 밸류로 재사용한다. 인코더의 출력 $\mathbf{X}_{N}$이 주어졌다고 하자.
- 시작 토큰
<SOS>의 임베딩 벡터 $\mathbf{y}_{0}$ 하나만을 입력으로 하여 디코더에서 얻은 확률벡터를 $\mathbf{P}_{1} \in \mathbb{R}^{|\mathcal{V}| \times 1}$이라 하자. 여기서 확률이 가장 높은 성분의 인덱스를 고르면 그것이 첫번째 출력 토큰 $\hat{y}_{1}$이다. $$ \hat{y}_{1} = \argmax_{1 \le w \le |\mathcal{V}|} \left[ \mathbf{P}_{1} \right]_{w} $$ - 토큰 $\hat{y}_{1}$의 임베딩 벡터를 $\hat{\mathbf{y}}_{1}$이라 하자. 이를 시작 토큰 뒤에 이어붙인 $\begin{bmatrix} \mathbf{y}_{0} & \hat{\mathbf{y}}_{1} \end{bmatrix}$을 다시 디코더의 입력으로 하면 두 개의 열을 갖는 확률행렬 $\mathbf{P}_{2}$를 얻는다. $$ \mathbf{P}_{2} = \begin{bmatrix} \mathbf{p}_{2}^{1} & \mathbf{p}_{2}^{2} \end{bmatrix} \in \mathbb{R}^{|\mathcal{V}| \times 2} $$ 첫번째 열은 이미 알고 있는 $\hat{y}_{1}$에 대한 예측이므로 버리고, 마지막 열에서 다음 토큰을 얻는다. $$ \hat{y}_{2} = \argmax_{1 \le w \le |\mathcal{V}|} \left[ \mathbf{p}_{2}^{2} \right]_{w} $$
- 이를 반복하여 순차적으로 출력을 얻는다. 끝을 뜻하는 종료 토큰이 나오거나 미리 정한 최대 길이에 도달하면 멈춘다.
위에서는 설명의 편의를 위해 매번 확률이 가장 높은 토큰을 선택하는 그리디 서치를 사용했지만, 논문에서 실제로 채택한 것은 빔 서치와 같은 방법을 사용하여 여러 후보를 동시에 고려한다.
같이보기
Ashish Vaswani et al. Attention Is All You Need. Advances in Neural Information Processing Systems 30 (2017). https://arxiv.org/abs/1706.03762 ↩︎

저희들의 저서 「줄리아 프로그래밍」이 2024 세종도서 학술부문에 선정되었습니다!

