← 학습 경로

공통 · 2026-09-09

Attention과 MLP: 토큰 사이의 정보와 토큰 내부의 변환

Attention과 MLP의 역할을 비교하고, 행렬 곱부터 기본 MLP와 Gated MLP의 내부 계산까지 살펴봅니다.

지난 글에서는 디코더 블록을 Residual stream과 그 옆의 변환 경로로 나누어 살펴봤습니다. 옆 경로에서는 RMSNorm을 거쳐 Attention이나 MLP의 변환 결과를 계산하고, 그 결과를 Residual stream에 더했습니다. 이번 글에서는 옅은 상자로 두었던 Attention과 MLP가 각각 어떤 역할을 하는지 비교하고, MLP 내부에서 어떤 계산이 이루어지는지 살펴보겠습니다.

Attention과 MLP는 어느 토큰을 참조할까?

Attention과 MLP는 모두 토큰의 벡터를 변환하지만, 다른 토큰의 벡터를 참조하는지에서 차이가 있습니다. Attention은 다른 위치에 있는 토큰의 정보를 가져와 현재 위치의 벡터에 반영합니다. 반면 MLP는 각 토큰의 벡터에 독립적으로 적용됩니다. 아래 그림에서는 세 번째 토큰의 출력에 어떤 입력이 사용되는지 비교해 보겠습니다.

Attention은 토큰 1·2·3의 벡터를 함께 참조해 토큰 3의 출력을 계산하고, MLP는 토큰 3의 벡터만 사용합니다. 두 출력 모두 토큰 3의 위치에 놓이며 d차원입니다.

왼쪽의 Attention은 토큰 1과 2의 벡터, 그리고 토큰 3 자신의 벡터를 사용해 토큰 3의 출력 벡터를 계산합니다. 이를 통해 토큰 3의 벡터에 주변 문맥이 반영됩니다. 오른쪽의 MLP는 토큰 3의 벡터만 받아 같은 위치의 출력 벡터를 계산합니다. 그림에서는 토큰 3의 출력만 표시했지만, 두 변환 모두 전체 입출력은 T × d 형태입니다.

지금 살펴보는 기본 Transformer 디코더의 순전파에서 서로 다른 토큰 위치의 정보를 결합하는 역할은 Attention이 담당합니다. RMSNorm은 한 토큰의 d개 성분으로 정규화하고, Residual 덧셈은 같은 위치의 벡터끼리 더하며, MLP도 한 토큰의 벡터를 변환합니다. 같은 MLP의 가중치가 모든 토큰에 동일하게 적용됩니다.

행렬 곱: 입력 성분들을 조합해 출력 만들기

MLP 내부를 보기 전에, 그 안에서 사용하는 행렬 곱부터 살펴보겠습니다. 토큰 하나의 벡터를 가로로 놓고 학습된 가중치 행렬을 곱하면 새로운 벡터가 나옵니다. 입력이 d차원이고 가중치 행렬이 d × m 형태라면 출력은 m차원입니다. 여기서 m은 MLP 내부에서 사용하는 중간 차원입니다. 그림에서는 d = 4, m = 8인 작은 예시를 사용했습니다.

4차원 입력 벡터와 4행 8열 가중치 행렬을 곱해 8차원 출력을 만듭니다. 가중치 행렬의 세 번째 열과 출력의 세 번째 성분을 강조하고, 네 쌍의 곱을 더하는 계산을 보여줍니다.

출력의 각 성분은 입력 벡터와 가중치 행렬의 열 하나를 사용해 계산합니다. 같은 위치의 값끼리 곱한 뒤 모두 더하는 방식입니다. 예를 들어 입력이 [1, 2, −1, 0]이고 세 번째 열이 [2, −1, −0.5, 1]이면, 세 번째 출력은 1 × 2 + 2 × (−1) + (−1) × (−0.5) + 0 × 1 = 0.5입니다. 여덟 개의 열에 대해 같은 계산을 하면 여덟 개의 출력 성분이 만들어집니다. 각 열은 입력 성분들을 서로 다른 가중치로 조합합니다.

이 계산은 토큰 하나의 벡터 안에 있는 성분들을 조합합니다. 토큰 T개를 행으로 쌓으면 X (T × d) × W (d × m) = Z (T × m)으로 함께 나타낼 수 있습니다. 이때도 각 출력 행은 해당 입력 행과 같은 가중치 행렬 W로 계산합니다. 여러 토큰을 하나의 행렬로 묶어 계산하더라도 토큰 사이의 정보가 섞이는 것은 아닙니다. 그림의 가중치와 차원은 설명용 예시이며, 여기서는 계산을 간단히 보기 위해 bias를 생략했습니다.

기본 MLP: 확장 → 활성화 → 축소

MLP는 Multi-Layer Perceptron의 약자이며, Transformer에서는 FFN(Feed-Forward Network)이라고도 부릅니다. 기본적인 형태는 두 개의 선형 변환 사이에 활성화 함수를 놓는 구조입니다. 먼저 행렬 곱으로 d차원 벡터를 더 큰 m차원으로 확장하고, 활성화 함수를 적용한 뒤, 두 번째 행렬 곱으로 다시 d차원으로 바꿉니다. 아래 그림의 첫 번째 선형 변환은 앞에서 살펴본 행렬 곱과 같은 수치를 사용합니다.

입력 [1, 2, −1, 0]을 4차원에서 8차원으로 확장하고, ReLU로 음수를 0으로 바꾼 뒤, 두 번째 선형 변환으로 다시 4차원 출력을 만듭니다.

첫 번째 변환의 출력은 [−2, 1, 0.5, −1, 3, 0, −0.5, 2]입니다. 여기에 ReLU를 적용하면 음수는 0이 되고, 0 이상의 값은 그대로 유지됩니다. 따라서 결과는 [0, 1, 0.5, 0, 3, 0, 0, 2]입니다. 활성화 함수는 이처럼 각 성분에 적용되며, 성분의 개수는 바꾸지 않습니다. 두 번째 선형 변환은 이렇게 달라진 여덟 성분을 다시 조합해 네 성분의 출력 벡터를 만듭니다.

두 선형 변환 사이에 활성화 함수를 넣는 이유는 비선형적인 변환을 만들기 위해서입니다. 활성화 함수 없이 행렬 곱만 연속으로 적용하면, 두 가중치 행렬을 미리 곱한 하나의 선형 변환으로 합칠 수 있습니다. ReLU처럼 입력 값에 따라 동작이 달라지는 함수를 사이에 넣으면 MLP가 그보다 다양한 변환을 학습할 수 있습니다. 내부에서는 차원을 확장하지만 마지막에는 d차원으로 돌아오므로, MLP의 결과를 같은 위치의 Residual stream에 더할 수 있습니다.

Gated MLP: 두 갈래로 계산한 뒤 곱하기

기본 MLP에서 한 단계 더 나아가면 Gated MLP가 있습니다. 여기서는 SiLU 활성화 함수를 사용하는 SwiGLU 구조를 살펴보겠습니다. 같은 입력 x를 두 경로로 나누어, 한쪽에서는 Up projection으로 중간 벡터 u를 만들고 다른 쪽에서는 Gate projection과 SiLU로 벡터 g를 만듭니다. 두 projection은 같은 입력을 받지만 서로 다른 가중치를 사용합니다.

같은 d차원 입력에서 Up projection과 Gate projection이 갈라집니다. Gate 쪽에는 SiLU를 적용하고, 두 m차원 벡터를 성분별로 곱한 뒤 Down projection으로 d차원 출력을 만듭니다.

두 경로에서 나온 벡터는 모두 m차원입니다. 이 둘을 같은 위치의 성분끼리 곱한 뒤, Down projection으로 다시 d차원으로 바꿉니다. 그림의 ⊙는 이 성분별 곱을 뜻합니다. Gate, Up, Down이라는 세 개의 선형 변환이 있지만, 세 층을 순서대로 통과하는 구조는 아닙니다. Gate와 Up은 같은 입력에서 갈라지고, 두 결과를 곱한 다음 Down으로 이어집니다. 이 구조와 SwiGLU의 정의는 GLU Variants Improve Transformer에 제시되어 있습니다.

SiLU는 각 입력 값에 그 값의 sigmoid를 곱하는 활성화 함수입니다. 이 글에서는 세부적인 곡선보다 Gate 경로에도 성분별 활성화가 있고, 그 결과가 Up 경로의 값과 곱해진다는 점에 집중하겠습니다. 기본 MLP와 마찬가지로 전체 과정은 한 토큰 안에서 이루어지고, 입출력 차원도 d로 같습니다.

Gate: 입력에 따라 성분별 값 조절하기

Gate의 역할은 Up 경로에서 만든 값을 얼마나 반영할지 성분별로 조절하는 것으로 이해할 수 있습니다. 같은 입력에서 Up은 조절할 값을 만들고, Gate는 그 값에 곱할 배율을 만듭니다. 배율은 고정된 상수가 아니라 현재 입력으로부터 계산되므로, 입력이 달라지면 성분별 조절 값도 달라집니다. 아래 그림에서는 중간 벡터 중 네 성분을 확대했습니다.

Up 값 [2, −1, 3, 0.5]에 SiLU 적용 후 Gate 값 [0.1, 1.5, 0, 2]를 성분별로 곱하면 [0.2, −1.5, 0, 1]이 됩니다. 같은 눈금의 막대로 각 성분의 크기 변화를 비교합니다.

첫 번째 성분은 2 × 0.1 = 0.2로 작아지고, 두 번째 성분은 −1 × 1.5 = −1.5로 절댓값이 커집니다. 세 번째 성분은 0을 곱해 사라지고, 네 번째 성분은 0.5 × 2 = 1로 커집니다. 그림의 Gate 값은 SiLU를 적용한 뒤의 값입니다. SwiGLU의 Gate는 0과 1 사이로 제한되지 않습니다. 1보다 큰 값이나 음수도 나올 수 있으므로, 성분을 켜고 끄는 스위치보다는 값을 줄이거나 키우고 부호도 바꿀 수 있는 배율로 보는 편이 정확합니다.

기본 MLP에서도 활성화 함수가 각 성분의 값을 조절합니다. Gated MLP에서는 여기에 별도로 학습하는 경로를 두어, 조절할 값과 그 값에 곱할 배율을 각각 계산한다는 차이가 있습니다. 이는 Gate가 추가되면서 가능한 계산을 설명하는 직관입니다. 실제 품질 개선은 실험으로 확인해야 하며, SwiGLU 논문에서는 파라미터 수와 연산량을 맞춘 비교에서 기존 ReLU 기반 FFN보다 낮은 perplexity를 보고했습니다. 그림에서는 구조 비교를 위해 같은 중간 차원을 사용했지만, 실제 모델의 중간 차원은 설계에 따라 달라집니다.

이제 디코더 블록의 두 중심 변환을 구분할 수 있습니다. Attention은 토큰 사이의 정보를 결합하고, MLP는 각 토큰 안의 성분들을 변환합니다. 기본 MLP는 확장, 활성화, 축소로 이어지고, Gated MLP는 같은 입력에서 계산한 두 벡터를 성분별로 곱하는 단계를 포함합니다. 다음 글에서는 Attention 내부를 살펴보겠습니다. 먼저 입력 벡터가 Q, K, V로 바뀌는 projection과, Attention 결과를 다시 모델 차원으로 바꾸는 출력 projection을 설명하겠습니다.