localaiforum
로그인
가이드 › 기초

transformer 내부

로컬에서 돌릴 수 있는 모든 것은 transformer입니다. 그리고 이 아키텍처는 냅킨 위에 그려집니다. 토큰을 벡터로 바꾸고, 토큰들이 서로를 바라보는 동일한 N개의 층을 통과시키고, 마지막 벡터를 어휘 점수로 되사영합니다. 미적분은 필요 없습니다. 내적과 softmax가 쇼 전체를 담당합니다. 이 페이지는 그 경로를 한 번, 천천히 걷습니다.

잔차 스트림(residual stream)

모델을 관통하는 컨베이어 벨트를 그려보세요. 각 층에서 서브모듈은 벨트를 읽고 자기 기여를 다시 벨트 위에 더합니다. 대체는 없고 축적만 있습니다:

x = embed(token_ids) + positions      # 토큰이 벡터가 됨 (차원 ~4k)
for layer in range(N):                # 8B 모델은 N ≈ 32
    x = x + attention(x)              # 토큰 간 정보 교환
    x = x + ffn(x)                    # 각 토큰이 자기 자신을 변환
logits = unembed(x[-1])               # 마지막 위치 → 어휘 항목별 점수
p = softmax(logits)                   # 모든 다음 토큰에 대한 확률
  • 임베딩: 각 토큰 ID는 학습된 벡터 테이블을 가리킵니다. 위치 정보가 섞여 들어갑니다(현대 모델은 회전 임베딩). 그래서 "dog bites man"과 "man bites dog"은 달라집니다.
  • 층은 모양은 같고 내용은 다릅니다. 초기 층은 구문과 표면 패턴을 처리하고, 깊은 층은 의미, 사실, 과제 동작을 담습니다.
  • 언임베딩: 최종 벡터를 모든 어휘 항목과 비교하고, softmax가 점수를 확률로 바꿉니다. 샘플링이 하나를 고릅니다(샘플링).

네 토큰으로 보는 셀프 어텐션

정보 교환 메커니즘입니다. 각 토큰의 벡터는 세 개의 유도 벡터를 만듭니다. 쿼리(내가 찾는 것은 무엇인가), 키(내가 광고하는 것은 무엇인가), 값(선택되면 넘길 것은 무엇인가). 문장: "The cat sat on". "sat"의 위치는 무엇을 끌어와야 하는가?

token:          The     cat     sat     on
q·k scores:     1.2     4.1     3.0     0.4      (내적, 예시)
softmax →        0.05    0.60    0.31   0.04     (가중치 합은 1.0)

output("sat") = 0.05·v(The) + 0.60·v(cat) + 0.31·v(sat) + 0.04·v(on)

"sat"은 대부분 "cat"에 어텐션합니다. 자기가 필요한 주어입니다. 모든 위치가 이것을 동시에 수행합니다. 하나의 제약이 이것을 언어 모델로 만듭니다. 인과 마스킹(causal masking) — 각 토큰은 이전 토큰만 볼 수 있고 이후 토큰은 결코 볼 수 없어서, 한 번의 패스로 시퀀스의 모든 다음-토큰 예측을 동시에 학습할 수 있습니다.

이것이 수학적 도구의 전부입니다. 유사도 점수(내적)를 softmax로 정규화해 값 벡터들의 혼합으로 만드는 것.

레시피로 정리하면, 한 위치에서의 어텐션 헤드 하나:

  1. 모든 토큰에 대해 쿼리, 키, 값 벡터를 계산합니다.
  2. 쿼리를 각 이전 토큰의 키와 내적하고, √(head_dim)으로 나눠 스케일합니다.
  3. 점수를 softmax해 합이 1인 가중치로 만듭니다.
  4. 그 토큰들의 값의 가중합을 출력합니다.
  5. 모든 헤드의 출력을 이어 붙이고 사영합니다 — 이것이 그 층의 어텐션 기여입니다.

멀티 헤드: 병렬 패턴

헤드 하나 = 쿼리/키/값 삼중항 하나 = 표현할 수 있는 관계의 한 종류(주어↔동사, 형용사↔명사, 공지시). 층은 여러 헤드(예: 32)를 저차원 조각에서 병렬로 돌리고 결과를 이어 붙입니다. 헤드가 많다는 것은 층당 관계 유형이 더 많다는 뜻입니다. 패턴의 폭이지 사고의 깊이가 아닙니다.

  • 헤드는 학습 시점에 고정됩니다. 위치를 넘나드는 "사고"는 어텐션이 잔차 스트림에 되돌려 쓰는 것입니다.
  • 헤드 차원(예: 128) × 헤드 수(예: 32) = 은닉 차원(4096). 헤드는 같은 컨베이어 벨트의 병렬 조각이지 별개의 모델이 아닙니다.

FFN: 파라미터가 사는 곳

각 층의 후반부는 모든 토큰에 독립적으로 적용되는 두-행렬 네트워크입니다. 벡터를 확장(~4배)하고, 비선형을 적용하고, 되사영합니다. 전형적인 덴스 모델에서 전체 파라미터의 약 3분의 2가 여기 있고, 어텐션에 있지 않습니다. 유용한 직관: 키-값 메모리. 첫 행렬은 탐지할 패턴을, 둘째 행렬은 패턴이 발동했을 때 벨트에 더할 것을 담습니다. 사람들이 "모델의 사실"이라 부르는 것의 상당수가 FFN 가중치에 저장되어 있습니다(파라미터 참고).

구성 요소 파라미터 비중 (전형적 7–9B 덴스)
어텐션 (Q, K, V, O) ~10–15%
FFN ~60–70%
임베딩 + 출력 헤드 ~10–25% (어휘 크기에 비례해 증가)

직접 파라미터 세어보기

모델 카드는 다섯 숫자를 공개합니다. 곱하면 파라미터 수에 도착합니다. 실제 8B급 오픈 모델 기준(32층, 은닉 차원 4096, KV 헤드 8개를 가진 32 헤드, FFN 폭 14336, 어휘 ~128k):

attention/layer:  q: 4096×4096 + k,v: 2×(4096×1024) + o: 4096×4096
                  ≈ 42M per layer × 32 layers            ≈ 1.3B
FFN/layer:        3 × (4096×14336), gated ≈ 176M × 32     ≈ 5.6B
embeddings:       2 × (128k × 4096), input + output head  ≈ 1.1B
total                                                     ≈ 8.0B

이 산술은 모델 카드의 "8B"와 거의 정확히 일치합니다. 교훈이 두 개 나옵니다. FFN이 파라미터 수(따라서 메모리 예산)를 지배합니다. 그리고 큰 어휘는 첫 층이 돌기도 전에 약 10억 파라미터를 소모합니다 — 대형 어휘 다국어 모델이 동급 덴스 형제들이 예상하는 것보다 무거운 이유의 일부입니다.

GQA: 어텐션 비용의 해결책

생성 중 각 새 토큰은 이전 모든 토큰의 키와 값을 읽어야 합니다. 이들은 KV 캐시에 저장됩니다. 원래의 멀티 헤드 어텐션은 토큰당 헤드당 K 하나, V 하나를 저장합니다. GQA(그룹드 쿼리 어텐션)는 소수의 KV 헤드를 여러 쿼리 헤드가 공유합니다(예: 쿼리 헤드 32개, KV 헤드 8개). 품질 손실은 무시할 만하면서 캐시를 4배 줄입니다. MQA(멀티 쿼리 어텐션)는 KV 헤드 하나만 공유합니다. 사실상 모든 현대 오픈 모델이 GQA를 씁니다. 이 절감이 롱 컨텍스트가 애초에 사용 가능한 큰 이유입니다(KV 캐시).

컨텍스트 길이 = 어텐션 폭

어텐션은 모든 토큰이 이전 모든 토큰을 보게 하므로, 프리필 연산량은 프롬프트 길이에 제곱으로 자라고 캐시는 생성된 토큰마다 선형으로 자랍니다. "128k 컨텍스트" 모델은 위치 트릭과 학습된 확장으로 그 지점에 도달하며, 창의 먼 끝자락 품질은 보통 시작 부근보다 약합니다. 긴 창은 꼭 필요할 때만 채우세요.

직접 테스트하세요. 창의 시작에 사실을 하나 두고, 중간을 채움물로 채우고, 그것에 대해 물어보세요. 대부분의 모델은 창의 시작과 끝에서는 통과하고 중간에서 주저앉습니다 — 유명한 "lost in the middle" 패턴입니다. 광고된 컨텍스트를 수용량으로 여기세요. 품질 보증이 아닙니다.

왜 이것으로 충분한가

이 구조만으로 실용적 사실들을 유도할 수 있습니다. 메모리 필요량 = 가중치 + KV 캐시(VRAM 계산). 디코드 속도 = 가중치를 다시 읽는 속도(프리필 대 디코드). 능력은 파라미터 수를 따라갑니다(파라미터). 컨텍스트 비용 = 어텐션 폭. 아키텍처는 단순합니다. 그 안의 수십억 학습된 가중치가 아무도 손으로 쓸 수 없었던 부분입니다.

관련: LLM이란 · KV 캐시 · 프리필 대 디코드 · 파라미터