콘텐츠로 이동

P5-13.3 보충학습: QKV와 multi-head attention

Section ID: P5-13.3 Version: v2026.07.31

P5-13.1과 P5-13.2에서는 어텐션(Attention)과 셀프 어텐션(self-attention)의 직관을 먼저 잡았습니다. 그런데 여기까지 읽으면 자연스럽게 다음 질문이 생깁니다.

그렇다면 어텐션이 실제 계산에서는 쿼리(query), 키(key), 값(value)으로 왜 설명되며, 멀티헤드 어텐션(multi-head attention)은 왜 따로 이름이 붙는가?

용어가 다시 흩어져 보일 때는 개념사전의 쿼리-키-값(query-key-value, QKV)과 멀티헤드 어텐션(multi-head attention) 항목을 함께 다시 봅니다.

QKV와 멀티헤드가 필요한 질문

  • 쿼리(query), 키(key), 값(value)은 무엇을 뜻하나?
  • 왜 셀프 어텐션 계산을 이 세 이름으로 나누어 설명하나?
  • 멀티헤드 어텐션은 무엇을 여러 번 본다는 뜻인가?
  • 트랜스포머를 읽을 때 이 개념을 어느 정도까지 이해하면 충분한가?

이 보충학습에서는 왜 이런 이름을 쓰는가, 한 head와 여러 head의 차이를 어떻게 직관적으로 읽는가를 붙잡는 데 집중합니다. 이 보충학습의 핵심은 새 수식을 더 외우는가가 아니라, 이미 잡은 attention 직관을 QKV와 multi-head라는 반복 이름으로 다시 읽게 만드는 데 있습니다.

역할 이름과 여러 관점의 구분

  • query, key, value를 입문 수준에서 설명할 수 있습니다.
  • self-attention을 질문하고, 맞는 위치를 찾고, 그 정보를 가져오는 계산으로 읽을 수 있습니다.
  • multi-head attention을 관계를 한 종류로만 보지 않고 여러 관점으로 나누어 본다는 뜻으로 설명할 수 있습니다.
  • Transformer 절을 다시 읽을 때 QKV와 multi-head가 어디에 놓이는지 떠올릴 수 있습니다.

먼저 아주 짧은 비유로 보면

query, key, value는 다음처럼 비유할 수 있습니다.

이름 입문용 직관
query 지금 무엇을 찾고 싶은가
key 각 위치가 어떤 정보인지 붙어 있는 표지
value 실제로 가져올 내용

도서관 비유로 다시 쓰면 다음과 같습니다.

  • query: 나는 지금 역사 책을 찾고 싶다
  • key: 각 책 카드에는 역사, 과학, 소설 같은 표지가 붙어 있다
  • value: 실제로 읽어 가져갈 책 내용

즉, attention은 지금 필요한 질문(query)에 맞춰 어울리는 표지(key)를 가진 위치를 찾고, 거기서 실제 내용(value)을 더 많이 가져오는 계산입니다.

운영 메모 문맥으로 바로 바꿔 읽으면 더 분명해집니다.

같은 운영 메모 장면 query처럼 읽는 것 key처럼 읽는 것 value처럼 읽는 것
정지 해제는 요청되었다. 그러나 압력 복귀는 아직 완료되지 않았다. 현재 표현이 무엇이 아직 미완료인가를 다시 확인하려는 질문 정지 해제, 압력 복귀, 완료되지 않았다처럼 각 위치가 어떤 종류 정보인지 드러내는 표지 현재 표현에 실제로 다시 섞여 들어와야 하는 정지 요청과 복귀 미완료의 의미 내용
교대 인수인계 요약 지금 요약 표현이 최종 결정인가, 근거인가, 조건인가를 가르는 질문 각 문장이 결론, 이상 징후, 안전 조건 가운데 무엇에 가까운지 보여 주는 표지 최종 요약에 실제로 남겨야 하는 결정, 근거, 조건의 내용
정비 코드 읽기 현재 줄이 이 값이 어디서 왔는가를 다시 묻는 질문 변수 정의, 조건문, 함수 호출이 어떤 역할인지 보여 주는 표지 현재 줄 해석에 실제로 다시 섞여 들어와야 하는 정의-사용 관계와 호출 맥락

즉, QKV를 굳이 따로 부르는 이유는 지금 무엇을 찾는가, 각 위치가 어떤 종류 정보인가, 실제로 무엇을 가져와 표현을 바꿀 것인가를 분리해 읽기 위해서입니다. 이 절은 이 역할 이름이 왜 필요한지 붙잡는 자리이지, 행렬 차원과 구현 최적화를 다루는 자리가 아닙니다.

self-attention 안에서는 무엇이 달라지나

P5-13.2에서는 self-attention을 같은 시퀀스 안 토큰들이 서로를 참고해 표현을 다시 계산하는 방식이라고 설명했습니다. 여기서 QKV를 붙이면 같은 말을 조금 더 계산 친화적으로 다시 쓰게 됩니다.

  • 현재 토큰은 나는 지금 무엇이 필요한가라는 query를 냅니다.
  • 모든 토큰은 나는 어떤 정보인가라는 key를 가집니다.
  • 모든 토큰은 내가 실제로 줄 수 있는 내용은 무엇인가라는 value를 가집니다.

즉, 현재 토큰은 자기 query와 다른 토큰들의 key를 비교해 누구를 더 참고할지를 정하고, 그다음 그 토큰들의 value를 가중 평균해 새 표현을 만듭니다.

이 흐름을 아주 단순하게 그리면 다음과 같습니다.

flowchart TD
  A["현재 토큰의 질문"]
  B["모든 키와 비교하기"]
  C["더 잘 맞는 위치에 더 큰 비중"]
  D["값들을 섞기"]
  E["새 토큰 표현"]

  A --> B
  B --> C
  C --> D
  D --> E

이 도식은 다음 순서의 계산을 압축합니다.

  1. 현재 토큰이 질문을 던집니다.
  2. 그 질문이 어떤 토큰 표지와 더 잘 맞는지 비교합니다.
  3. 잘 맞는 위치에 더 큰 비중을 줍니다.
  4. 그 위치들의 실제 내용(value)을 더 많이 섞어 새 표현을 만듭니다.

왜 굳이 key와 value를 나누나

입문 독자는 여기서 어차피 토큰 하나인데 왜 key와 value를 따로 부르지?라는 질문이 자연스럽습니다.

핵심은 찾는 기준과 가져올 내용의 역할이 다를 수 있기 때문입니다.

예를 들어 교대 인수인계 기록에서 이번 교대의 최종 결정을 찾는다고 해 보겠습니다.

  • 결정, 승인, 보류 같은 단어는 지금 찾는 기준에 가까운 표지 역할을 합니다.
  • 실제로 모델이 가져와야 하는 것은 그 문장 전체가 담고 있는 의미 표현입니다.

즉, key는 이 위치를 얼마나 참고할지 정하는 데 쓰이는 표지에 가깝고, value는 정말 섞어 와서 새 표현을 만들 내용에 가깝습니다.

이 구분을 한 문장으로 줄이면 다음과 같습니다.

key는 어디를 볼지 정하는 데 더 가깝고, value는 실제로 무엇을 가져올지에 더 가깝다.

작은 문장으로 다시 보면

정지 해제는 요청되었다. 그러나 압력 복귀는 아직 완료되지 않았다.

만약 현재 토큰이 그러나 뒤 문맥을 이해하려 한다면, query는 무엇이 지금 대비를 이루는가를 찾는 질문처럼 작동할 수 있습니다. 이때 정지 해제 요청과 압력 복귀 미완료라는 두 표현이 각각 다른 key를 내고, 현재 query와 더 잘 맞는 위치가 더 큰 비중을 받습니다. 그리고 그 위치의 value가 더 많이 섞여 현재 표현이 업데이트됩니다.

즉, self-attention에서 토큰 하나는 문장 전체를 다시 훑으며 지금 내 해석에 필요한 단서를 골라 섞는 방식입니다.

multi-head attention은 무엇을 여러 번 본다는 뜻인가

이제 다음 질문이 생깁니다.

한 번의 attention이면 충분한데, 왜 multi-head가 필요한가?

핵심은 한 종류의 관계만 보지 않고, 서로 다른 관점으로 토큰 관계를 나누어 읽는다는 점입니다.

한 종류의 관계만 보지 말고, 서로 다른 관점으로 여러 번 관계를 읽어 보자.

예를 들어 문장에서는 같은 토큰이라도 여러 관계가 함께 중요할 수 있습니다.

  • 어떤 head는 주어-동사 관계를 더 잘 볼 수 있습니다.
  • 어떤 head는 수식 대상이나 지시어 관계를 더 잘 볼 수 있습니다.
  • 어떤 head는 가까운 토큰 결합을, 다른 head는 더 먼 토큰 연결을 더 잘 볼 수 있습니다.

즉, multi-head attention은 하나의 정답 관계만 보는 대신, 여러 종류의 관련성을 나누어 읽는 장치입니다.

아래 표는 같은 장면에서 single-head와 multi-head가 남기는 관계 정보의 차이를 바로 비교합니다.

같은 장면 single-head처럼 한 번만 읽을 때 먼저 남는 것 multi-head처럼 여러 관점으로 읽을 때 먼저 남는 것
절차 문서 변환 가장 눈에 띄는 한 관계만 남고 수식 범위나 예외 조건이 약해질 수 있다 주체-행동, 수식 범위, 대비 관계를 나눠 함께 볼 수 있다
교대 인수인계 요약 결론 한 줄은 남아도 근거나 조건이 함께 약해질 수 있다 결론, 근거, 조건을 서로 다른 관련성으로 나눠 보존하기 쉽다
정비 코드 이해 변수명 반복 같은 한 종류 신호에 치우치기 쉽다 정의-사용, 조건-결과, 호출 흐름을 다른 관점으로 함께 읽기 쉽다

도식으로 보면

flowchart TD
  A["같은 입력 토큰"]
  B["헤드 1<br/>한 관계 패턴"]
  C["헤드 2<br/>다른 관계 패턴"]
  D["헤드 3<br/>또 다른 관계 패턴"]
  E["헤드 결과 합치기"]
  F["더 풍부한 토큰 표현"]

  A --> B
  A --> C
  A --> D
  B --> E
  C --> E
  D --> E
  E --> F

이 도식에서 핵심은 입력이 여러 개로 쪼개진다가 아니라, 같은 입력을 여러 관점으로 읽은 결과를 다시 합친다는 점입니다.

같은 판단 문장을 single-head와 multi-head 감각으로 더 짧게 병치하면 다음처럼 볼 수 있습니다.

flowchart TD
  A["같은 판단 문장"]

  subgraph S["싱글 헤드"]
    B["결정, 근거, 조건이 한 문맥으로 절충"]
  end

  subgraph M["멀티헤드"]
    C["헤드 1: 결정-근거 관계"]
    D["헤드 2: 결정-조건 관계"]
  end

  A --> B
  A --> C
  A --> D

이 비교 도식에서 먼저 붙잡아야 할 점은 다음과 같습니다.

  • single-head는 결정, 근거, 조건을 하나의 절충 문맥으로 접기 쉽습니다.
  • multi-head는 같은 입력을 보더라도 결정 쪽 관계, 조건 쪽 관계처럼 다른 관련성 패턴을 나눠 남길 수 있습니다.
  • 이 감각이 잡혀야 멀티헤드를 attention을 여러 번 반복한다가 아니라 관계 종류를 분리해 유지한다는 구조로 읽게 됩니다.

QKV와 multi-head attention: 확인할 판단 기준

이 사례에서는 query, key, value와 multi-head attention을 처음 읽는 최소 기준을 보충하는지 확인한다.

이 절의 사례는 먼 위치를 다시 본다 자체보다, 무슨 종류의 관계를 따로 들고 가야 하는가를 먼저 봐야 합니다. 즉, 같은 문장을 읽더라도 결정, 근거, 조건, 정의-사용 같은 관계를 하나의 평균 문맥으로 접을지, 서로 다른 관점으로 나눠 들고 갈지를 구분해서 읽어야 합니다.

대표 사례. 절차 문서 변환

긴 절차 문서를 작업 지침으로 바꿀 때 사람은 보통 단어 뜻 하나만 맞추면 된다고 느끼기 쉽습니다. 하지만 실제로는 주체-행동 관계, 수식 범위, 부정 표현, 예외 조건이 동시에 중요할 수 있습니다. 예를 들어 한 표현이 무엇을 꾸미는가와 어디에 대비되는가를 동시에 봐야 제대로 지침이 만들어지는 장면이 있습니다. 이때 한 번의 attention만으로 모든 관계를 한 종류로만 읽으면 중요한 차이가 섞일 수 있습니다. multi-head attention은 서로 다른 관계를 나누어 읽는다는 직관을 주므로, 절차 문서에서 여러 문법 단서를 함께 반영하는 장면을 설명하기 좋습니다. 그래서 이 사례에서 확인해야 할 결과는 현재 지침 문구가 한 관계만 따라가지 않고, 주체-행동 관계와 예외 조건 범위를 서로 다른 관점으로 함께 남겨 실제 작업 순서를 덜 왜곡하는가입니다.

같은 관점은 교대 판단 문장 정리나 정비 코드 읽기에도 그대로 이어집니다. 다만 이 절에서 붙잡을 핵심은 도메인 이름이 아니라, 한 문맥으로 절충되기 쉬운 관계를 여러 head가 서로 다른 관점으로 나누어 유지하는가입니다.

flowchart TD
  A[같은 판단 문장] --> B[싱글 헤드: 결정, 근거, 조건이 함께 접힘]
  A --> C[멀티헤드: 결정-근거 관계가 따로 유지됨]
  A --> D[멀티헤드: 결정-조건 관계가 따로 유지됨]
  B --> E[하나의 절충 해석]
  C --> F[결정 근거가 계속 보임]
  D --> G[결정 조건이 계속 보임]

이 도식은 같은 판단 문장이 single-head에서는 절충되고 multi-head에서는 결정·근거·조건 관계로 다시 나뉘는 차이를 압축해 보여 줍니다.

사람이 먼저 보기 쉬운 기준 single-head 관점으로 다시 읽는 기준 multi-head 관점으로 다시 읽는 기준
절차 문서는 핵심 단어 몇 개만 맞으면 된다고 느끼기 쉽다 가장 강한 한 관계가 남고 수식 범위나 예외 조건은 한 문맥 안에 절충될 수 있다 주체-행동, 수식 범위, 대비 관계를 다른 head가 나눠 읽어 함께 유지할 수 있다
교대 판단 문장은 결론 단어만 잡으면 충분하다고 느끼기 쉽다 결론 중심 신호 하나가 평균적 문맥을 끌고 가며 근거나 조건은 약해질 수 있다 결론, 근거, 조건을 서로 다른 관련성 패턴으로 나눠 더 오래 보존할 수 있다
코드는 변수명만 이어지면 읽힌다고 느끼기 쉽다 정의-사용, 조건-결과, 호출 흐름이 하나의 절충 표현에 섞일 수 있다 서로 다른 head가 다른 연결 패턴을 나눠 읽어 한 종류 신호에만 치우치지 않게 한다

세 사례에서 공통으로 확인해야 할 결과는 관계를 한 줄로만 읽지 않고 여러 관점으로 나누어 본다는 점입니다. 절차 문서 변환에서는 주체-행동 관계와 예외 조건이 함께 유지되는지, 교대 판단 문장에서는 결론과 근거와 조건이 함께 남는지, 코드에서는 정의-사용과 조건-결과 관계가 함께 이어지는지를 보면 충분합니다.

이 사례들에서 최종적으로 확인해야 할 결과도 분명합니다. multi-head의 차이는 attention을 여러 번 돈다는 데 있지 않고, single-head에서는 한 문맥으로 절충되기 쉬운 여러 관계를 서로 다른 head에 나누어 더 오래 유지한다는 데 있습니다.

연습 및 예제

이번 예제의 목표는 같은 토큰열을 보더라도 head마다 서로 다른 관계를 읽고, 그 차이가 head 가중치 변화에 따라 얼마나 커지거나 줄어드는지 직접 실험해 보는 것입니다.

이번에는 짧은 운영 보고서 조각을 CSV로 분리해 읽어 보겠습니다. 정지결정, 압력이상, 복귀조건 같은 조각이 있을 때, single-head는 이 셋을 하나의 절충 문맥으로 접기 쉽고, multi-head는 결정 쪽, 조건 쪽 같은 관점을 나눠 유지할 수 있는지를 보는 것이 핵심입니다.

입력:

  • qkv-multihead-report-scenarios.csv
  • 4개 운영 보고서, 3개 head 시나리오, 36개 토큰 행
  • 토큰별 의미 축 decision_axis, evidence_axis, condition_axis
  • 비교 기준 single_weight와 두 head의 head1_weight, head2_weight

출력:

  • 각 시나리오의 single-head 문맥
  • 각 시나리오의 head 1, head 2 문맥
  • single-head 대비 head별 차이
  • 두 head가 얼마나 다른 관계를 읽었는지 보여 주는 분리 정도

문제 상황:

  • multi-head attention은 말로만 들으면 추상적이므로, head를 얼마나 다르게 두었을 때 관계 분리가 실제로 커지는가를 직접 비교해 볼 필요가 있다

확인할 개념:

  • 서로 다른 head는 같은 토큰열에서도 다른 관계를 강조할 수 있다
  • head 가중치가 비슷하면 multi-head도 절충에 가까워지고, 멀어지면 관계 분리가 더 커진다
  • 여러 head 결과를 합치면 single-head보다 더 풍부한 표현을 만들 수 있다

CSV의 한 행은 특정 운영 보고서의 특정 시나리오에서 한 토큰 조각이 single-head와 두 head에 얼마나 반영되는가를 뜻합니다. 본문 코드는 그중 ops_pressure_return 보고서만 골라 세 시나리오를 비교합니다.

CSV 일부를 먼저 보면 다음과 같습니다.

report_id scenario token relation_role decision_axis evidence_axis condition_axis single_weight head1_weight head2_weight
ops_pressure_return balanced_heads 정지결정 decision 1.0 0.0 0.0 0.4 0.45 0.30
ops_pressure_return balanced_heads 압력이상 evidence 0.0 2.0 0.0 0.3 0.30 0.30
ops_pressure_return balanced_heads 복귀조건 condition 3.0 0.0 1.0 0.3 0.25 0.40
ops_pressure_return decision_vs_condition_split 정지결정 decision 1.0 0.0 0.0 0.4 0.70 0.10
ops_pressure_return decision_vs_condition_split 복귀조건 condition 3.0 0.0 1.0 0.3 0.10 0.60
ops_pressure_return condition_heavy_both_heads 복귀조건 condition 3.0 0.0 1.0 0.3 0.55 0.65

입력(input):

위 CSV를 읽어 ops_pressure_return 보고서의 세 가지 head 가중치 시나리오를 비교합니다.

코드를 보기 전에 먼저 각 시나리오가 어떤 관계 분리 정도를 남길지 예상해 보면, 절충된 한 문맥과 나뉜 여러 관계의 차이가 더 잘 보입니다.

비교 항목 먼저 예상해 볼 출력 예상 이유
balanced_heads head1_context와 head2_context 차이가 비교적 작을 가능성이 큼 두 head 모두 정지 결정, 압력 이상 근거, 복귀 조건을 비슷한 비율로 섞기 때문입니다.
decision_vs_condition_split head1_context와 head2_context 차이가 가장 크게 벌어질 가능성이 큼 한 head는 결정 쪽을, 다른 head는 조건 쪽을 강하게 밀어 single-head에서 접히는 차이를 크게 다시 벌립니다.
condition_heavy_both_heads 두 head가 모두 뒤쪽으로 기울어 single-head와 큰 차이가 적을 가능성이 큼 두 head가 서로 다른 관점이라기보다 같은 조건 쪽 관계를 함께 강조하기 때문입니다.
head_separation decision_vs_condition_split이 가장 클 가능성이 큼 서로 다른 head가 실제로 다른 종류의 관계를 읽을 때 분리 정도가 커집니다.

이 표의 목적은 정확한 벡터 값을 미리 계산하는 데 있지 않습니다. multi-head가 단순 반복이 아니라, head를 어떻게 설계하느냐에 따라 관계 분리가 커지기도 하고 다시 절충에 가까워지기도 한다는 점을 코드 전에 붙잡는 데 있습니다.

from pathlib import Path
import csv
import math

DATA_PATH = Path("docs/assets/part-05/chapter-13/qkv-multihead-report-scenarios.csv")
FOCUS_REPORT_ID = "ops_pressure_return"
CONTEXT_AXES = [
    ("decision_axis", ["decision_axis"]),
    ("evidence_condition_axis", ["evidence_axis", "condition_axis"]),
]

with DATA_PATH.open(encoding="utf-8", newline="") as f:
    rows = list(csv.DictReader(f))

focus_rows = [row for row in rows if row["report_id"] == FOCUS_REPORT_ID]
scenario_names = []
for row in focus_rows:
    if row["scenario"] not in scenario_names:
        scenario_names.append(row["scenario"])


def weighted_context(scenario_rows, weight_column):
    return [
        sum(
            float(row[weight_column]) * sum(float(row[column]) for column in source_columns)
            for row in scenario_rows
        )
        for _, source_columns in CONTEXT_AXES
    ]


def vector_diff(left, right):
    return [left_value - right_value for left_value, right_value in zip(left, right)]


def l2_distance(left, right):
    return math.sqrt(sum((l - r) ** 2 for l, r in zip(left, right)))


def summarize_scenario(name):
    scenario_rows = [row for row in focus_rows if row["scenario"] == name]
    single_head_context = weighted_context(scenario_rows, "single_weight")
    head1_context = weighted_context(scenario_rows, "head1_weight")
    head2_context = weighted_context(scenario_rows, "head2_weight")
    difference_from_single = vector_diff(head1_context, single_head_context) + vector_diff(
        head2_context, single_head_context
    )
    head_separation = l2_distance(head1_context, head2_context)

    print(f"[{name}]")
    print("tokens =", [row["token"] for row in scenario_rows])
    print("single_head_context =", [round(value, 3) for value in single_head_context])
    print("head1_context       =", [round(value, 3) for value in head1_context])
    print("head2_context       =", [round(value, 3) for value in head2_context])
    print("difference_from_single =", [round(value, 3) for value in difference_from_single])
    print("head_separation =", round(head_separation, 3))
    print()


print("csv_rows =", len(rows))
print("focus_report_rows =", len(focus_rows))
print("context_axes =", [name for name, _ in CONTEXT_AXES])
print()

for scenario_name in scenario_names:
    summarize_scenario(scenario_name)

출력에서는 각 시나리오의 head_separation과 difference_from_single이 어떻게 달라지는지부터 보면 됩니다.

csv_rows = 36
focus_report_rows = 9
context_axes = ['decision_axis', 'evidence_condition_axis']

[balanced_heads]
tokens = ['정지결정', '압력이상', '복귀조건']
single_head_context = [1.3, 0.9]
head1_context       = [1.2, 0.85]
head2_context       = [1.5, 1.0]
difference_from_single = [-0.1, -0.05, 0.2, 0.1]
head_separation = 0.335

[decision_vs_condition_split]
tokens = ['정지결정', '압력이상', '복귀조건']
single_head_context = [1.3, 0.9]
head1_context       = [1.0, 0.5]
head2_context       = [1.9, 1.2]
difference_from_single = [-0.3, -0.4, 0.6, 0.3]
head_separation = 1.14

[condition_heavy_both_heads]
tokens = ['정지결정', '압력이상', '복귀조건']
single_head_context = [1.3, 0.9]
head1_context       = [1.85, 1.05]
head2_context       = [2.1, 1.05]
difference_from_single = [0.55, 0.15, 0.8, 0.15]
head_separation = 0.25

이 예제에서 먼저 볼 산출물은 시나리오별 head_separation입니다. decision_vs_condition_split은 두 head가 결정 쪽과 조건 쪽으로 실제로 갈라지므로 분리 정도가 가장 크고, condition_heavy_both_heads는 head가 둘이어도 같은 조건 쪽을 함께 보므로 분리 정도가 작습니다.

시나리오별 헤드 분리 정도

두 번째 산출물은 single-head 문맥과 head 1, head 2 문맥이 좌표 위에서 어디로 갈라지는지입니다. 회색 점은 같은 single-head 기준이고, 파란 삼각형과 주황 사각형 사이의 거리가 커질수록 두 head가 서로 다른 관계를 더 분리해서 읽는다고 볼 수 있습니다.

싱글 헤드와 두 헤드의 문맥 위치

먼저 볼 출력 이 출력이 뜻하는 것 바꿔 보면 달라지는 것
head_separation이 크다 두 head가 실제로 다른 관계를 읽고 있다는 뜻 head 가중치를 더 비슷하게 만들면 분리 정도가 줄고, 더 다르게 만들면 커집니다
difference_from_single이 양쪽으로 벌어진다 single-head에서 평균내며 접힌 차이가 multi-head에서 다시 나뉜다는 뜻 CSV의 single_weight를 바꾸면 어떤 차이가 절충 결과에 이미 흡수되는지 비교할 수 있습니다
두 head가 모두 같은 방향으로 커진다 head 수가 둘이어도 실제로는 같은 관계를 중복해서 보고 있을 수 있다는 뜻 condition_heavy_both_heads처럼 두 head를 같은 쪽으로 몰면 multi-head의 분리 이점이 줄어듭니다
읽기 기준 single-head 출력만 보면 쉬운 판단 시나리오 비교까지 보고 바뀌는 판단
운전 보고서 요약 정지 결정, 압력 이상, 복귀 조건이 한 덩어리 평균으로만 남아, 무엇이 결론이고 무엇이 조건인지 약해질 수 있다 decision_vs_condition_split처럼 head를 갈라 두면 결론과 조건을 다른 문맥으로 오래 들고 갈 수 있다
절차 문서 해석 head가 여러 개면 무조건 관계가 다양해질 것처럼 느끼기 쉽다 condition_heavy_both_heads를 보면 head가 둘이어도 같은 조건만 함께 강조하면 관계 분리는 크지 않을 수 있다
실험 설계 multi-head는 값 하나만 보면 이해했다고 느끼기 쉽다 balanced_heads, decision_vs_condition_split, condition_heavy_both_heads를 나란히 봐야 head를 어떻게 다르게 두는가가 핵심이라는 점이 보인다

즉, single-head가 여러 관계를 하나의 절충 표현으로 접는다면, multi-head는 서로 다른 관계 읽기 결과를 나란히 유지해 단일 평균에서 사라질 차이를 더 오래 들고 갑니다. 다만 그 효과는 head 수 자체보다 head들이 실제로 얼마나 다른 관계를 읽는가에 더 크게 좌우됩니다.

이 숫자를 운영 보고서 읽기로 바꾸면, balanced_heads는 아직 절충에 가까운 요약이고, decision_vs_condition_split은 결정 쪽 문맥과 조건 쪽 문맥을 분리해 유지하는 읽기에 가깝습니다. 반대로 condition_heavy_both_heads는 head가 둘이어도 둘 다 조건 쪽으로 몰려, multi-head의 장점이 줄어든 장면으로 읽을 수 있습니다. 중요한 것은 벡터 값 자체보다, 서로 다른 head가 어떤 종류 판단을 따로 남겨 두는가입니다.

실제 multi-head attention은 이후 선형 변환까지 포함해 더 정교하게 결합되지만, 입문 단계에서는 서로 다른 관계 읽기 결과를 나란히 유지한다는 감각과 head를 어떻게 나누느냐가 중요하다는 감각까지 잡으면 충분합니다.

이 예제는 한 번 실행하고 끝내기보다, 아래 세 가지 조작을 직접 해 보면서 관계 분리가 언제 커지고 언제 줄어드는지 확인하는 편이 좋습니다.

지금 바로 바꿔 볼 값 관찰할 출력 해석할 질문
CSV의 head1_weight, head2_weight를 더 비슷하게 바꾸기 head_separation 서로 다른 head가 사실상 같은 관계를 읽게 되면 multi-head의 장점이 얼마나 줄어드는가
CSV의 single_weight를 head1_weight 쪽이나 head2_weight 쪽으로 더 기울이기 difference_from_single single-head가 이미 특정 관계를 강하게 반영하면 multi-head와 차이가 얼마나 줄어드는가
CSV의 복귀조건 행에서 condition_axis를 더 크게 또는 더 작게 바꾸기 head2_context, head_separation 토큰 자체의 의미 강도가 바뀌면 어떤 head가 그 변화를 더 민감하게 끌어오는가

앞의 숫자는 실제 대규모 multi-head attention 전체를 구현한 것은 아니지만, single-head가 여러 관계를 한 번에 평균내며 하나의 절충된 문맥으로 남기는 반면 multi-head는 서로 다른 관계 읽기 결과를 나란히 유지한 뒤 함께 쓴다는 비교 기준과, head 설계가 그 차이를 키우거나 줄인다는 실험 기준은 충분히 드러납니다. 즉, multi-head attention은 단순히 attention을 여러 번 반복한다는 뜻이 아니라, 서로 다른 종류의 관련성 패턴을 동시에 잃지 않게 들고 갈 수 있도록 head를 나누는 구조에 더 가깝습니다.

이 절에서 독자가 최종적으로 붙잡아야 하는 것도 같습니다. QKV는 무엇을 찾는가, 어떤 표지와 맞는가, 무엇을 실제로 가져오는가를 나눠 읽게 하는 이름이고, multi-head는 그렇게 찾은 관계를 한 번에 평균내지 않고 결정, 근거, 조건처럼 다른 관점으로 더 오래 유지하게 하는 구조입니다.

Part 5 흐름에서 왜 중요한가

이 보충학습은 attention 절과 Transformer 절 사이에 끼어 있는 세부 구현 메모가 아닙니다. 오히려 본편에서 이미 잡은 직관을 왜 이런 이름과 구조가 붙는가로 연결해 주는 회수 위치입니다. attention과 self-attention의 핵심 직관은 이미 본문에서 닫았지만, QKV와 multi-head라는 이름이 Transformer 본문에서 반복 등장하므로, 이 절은 수식 메모가 아니라 이름을 다시 읽어 주는 입문 회수 위치로 남겨 두는 편이 더 적절합니다. 다음 장 P5-14.1에서는 이 이름들이 Transformer 블록 안에서 어디에 놓이는지를 이어서 설명합니다.

체크리스트

  • 쿼리-키-값(QKV)이 어텐션 계산을 설명하는 이름이라는 점을 설명할 수 있는가?
  • 멀티헤드 어텐션이 왜 여러 관점의 참조 계산으로 읽히는지 말할 수 있는가?
  • query, key, value를 질문, 표지, 가져올 내용의 역할 차이로 설명할 수 있는가?
  • multi-head attention은 한 종류의 관계만 보지 않고 여러 관점의 관련성을 함께 읽는 방식이라는 점을 말할 수 있는가?
  • single-head와 multi-head의 차이를 절충된 하나의 관계와 여러 관계 관점의 동시 유지로 나눠 말할 수 있는가?
  • attention 직관만으로는 query, key, value 이름이 갑자기 튀어 보일 때, QKV 회수 관점을 먼저 떠올릴 수 있는가?
  • head를 왜 여러 개 두는지 설명해야 할 때, single-head 절충과 multi-head 다관점 유지 차이를 다시 볼 수 있는가?
  • 다음 Transformer 절을 읽을 때도 먼저 이 이름들이 왜 블록 안 핵심 부품으로 반복되는가를 떠올릴 준비가 되어 있는가?

출처와 참고 자료