'먹었다'는 누가, 무엇을 먹었다는 걸까? — 단어가 문장 속 다른 단어를 얼마나 주목하는지 직접 확인해요. ChatGPT의 심장이 되는 계산이에요.
🖱️ 단어를 클릭하면 그 단어가 질문(Query)이 돼요. 곡선과 막대가 “누구를 얼마나 주목하는지”를 보여줘요.
읽는 법: 보라색 단어가 지금의 질문이에요. 곡선이 굵고 진할수록 그 단어를 많이 주목한다는 뜻. 자기 자신은 작은 고리로 표시돼요.
읽는 법: 막대 = ‘그’가 각 단어에 주는 어텐션 가중치(모두 더하면 100%). 테두리 있는 막대가 자기 자신이에요.
읽는 법: 행 = 질문하는 단어(Query), 열 = 주목받는 단어(Key). 칸이 파랗게 진할수록 많이 주목해요. 대각선(회색 테두리)은 자기 자신을 보는 정도, 보라 테두리 행이 지금 선택한 단어예요.
가중치는 softmax(q·k / (√d·T))로 계산해요 (d=4). 이 페이지는 투영 행렬 없이 Q = K = V = 임베딩으로 단순화한 셀프 어텐션이에요.
import torch
import torch.nn.functional as F
def scaled_dot_product_attention(Q, K, V):
d = Q.size(-1) # 임베딩 차원 (여기서는 4)
scores = Q @ K.transpose(-2, -1) # 모든 단어 쌍의 내적: (6, 6)
scores = scores / (d ** 0.5) # √d로 나눠 값이 튀지 않게
alpha = F.softmax(scores, dim=-1) # 행마다 합이 1인 가중치
return alpha @ V, alpha # 가중치로 V를 섞은 새 표현
# 문장 임베딩 (6 토큰 × 4차원) — 이 페이지와 같은 값
E = torch.tensor([
[0.7, 0.1, 0.0, 0.3], # 그
[1.6, 0.3, 0.6, 0.1], # 고양이는
[0.3, 1.6, 0.6, 0.0], # 생선을
[0.0, 0.1, 0.2, 1.0], # 아주
[0.4, 0.5, 1.2, 1.0], # 좋아해서
[0.8, 0.9, 0.7, 0.0], # 먹었다
])
out, alpha = scaled_dot_product_attention(E, E, E) # Q=K=V=E (단순화)
print(alpha[5].round(decimals=2)) # '먹었다'가 각 단어를 주목하는 정도