1장. 준비 수학과 Transformer 개요
행렬곱과 내적: 곱하고 더하기
성적에 비중을 두어 총점을 내 봅니다. 국어 50%, 수학 30%, 영어 20%라면 민지의 총점은 90×0.5 + 80×0.3 + 70×0.2 = 45 + 24 + 14 = 83점입니다. 점수 줄과 비중 줄을 같은 자리끼리 곱해 모두 더했습니다. 이 "곱하고 더하기"를 학생 여러 명, 비중표 여러 개에 한꺼번에 하는 것이 행렬곱입니다. 신경망의 층 하나는 거의 이 계산 하나입니다.
그림은 노트북의 예를 shape 만 남겨 그린 것입니다. A 는 2행 3열, B 는 3행 2열입니다. A 의 열 수와 B 의 행 수가 둘 다 3으로 같아서 곱할 수 있고, 결과 C 의 shape 는 바깥 두 숫자를 따라 (2, 2) 가 됩니다. 규칙을 글로 적으면 (m × n) · (n × p) = (m × p) 입니다.
내적은 행렬곱의 가장 작은 단위입니다. 두 벡터가 같은 쪽을 향하면 양수이고 클수록 비슷합니다. 직각이면 0, 반대 쪽이면 음수입니다. 노트북의 예 u = [1, 2, 3], v = [4, 5, 6] 은 1×4 + 2×5 + 3×6 = 4 + 10 + 18 = 32 입니다. u 와 -u 는 1×(-1) + 2×(-2) + 3×(-3) = -1 - 4 - 9 = -14 입니다.
행렬곱과 내적 견주기
| 계산 | 넣는 것 | 나오는 것 | 손으로 계산 | 쓰이는 곳 |
|---|---|---|---|---|
| 내적 | 길이가 같은 벡터 둘 | 숫자 하나 | 1×4 + 2×5 + 3×6 = 32 | Attention 에서 낱말끼리 관련도 점수 |
| 행렬곱 | (m, n) 과 (n, p) | (m, p) 행렬 | 결과의 칸 하나 = 행 하나와 열 하나의 내적 | 신경망의 층, Q·K·V 만들기 |
| 안 되는 곱 | (2, 3) 과 (2, 3) | 오류 | 안쪽 숫자 3과 2가 다름 | shape 를 잘못 맞춘 코드 |
짧은 예시
import numpy as np
# 학생 2명 × 과목 3개 점수표
table = np.array([[90, 80, 70],
[60, 75, 95]])
# 과목 3개 × 비중 1개 표: 국어 0.5, 수학 0.3, 영어 0.2
weights = np.array([[0.5],
[0.3],
[0.2]])
# @ 는 행렬곱 기호다. (2, 3) @ (3, 1) → 안쪽 3이 같아 곱할 수 있고, 결과는 (2, 1)
total = table @ weights
print(total.ravel()) # 결과: [83. 71.5] → 민지 83점, 준호 71.5점
print(total.shape) # 결과: (2, 1)
# 내적: 두 사람의 취향 점수(액션, 로맨스, 코미디)
me = np.array([1, 2, 3])
friend = np.array([4, 5, 6])
other = np.array([3, 0, -1])
print(np.dot(me, friend)) # 결과: 32 → 같은 쪽, 취향이 비슷하다
print(np.dot(me, other)) # 결과: 0 → 1×3 + 2×0 + 3×(-1) = 0, 직각이라 관계가 없다이 코드가 뜻하는 것
점수표에 비중표를 곱해 두 학생의 총점을 한 번에 냈고, 내적으로 두 사람 취향이 같은 쪽인지 재 보았습니다. 여기서 점수표가 table, 비중표가 weights, 취향 줄이 me, friend, other 입니다.
| 줄 | 하는 일 | 쉽게 말하면 |
|---|---|---|
table @ weights | (2, 3) 과 (3, 1) 을 행렬곱한다 | 학생마다 비중 총점 내기 |
total.ravel() | (2, 1) 을 한 줄로 펴서 찍는다 | 총점 두 개를 보기 좋게 |
np.dot(me, friend) | 같은 자리끼리 곱해 더한다 | 취향이 같은 쪽인지 점수 매기기 |
준호의 총점도 손으로 확인해 봅니다. 60×0.5 + 75×0.3 + 95×0.2 = 30 + 22.5 + 19 = 71.5 입니다. 행렬곱 결과의 칸 하나는 결국 행 하나와 열 하나의 내적입니다.
주의할 점
- 행렬곱은 순서를 바꾸면 결과가 달라지거나 아예 안 됩니다.
weights @ table은 (3, 1) 과 (2, 3) 이라 안쪽 숫자 1과 2가 달라 오류가 납니다. *는 행렬곱이 아니라 같은 자리끼리 곱하기입니다. 행렬곱은@또는np.matmul을 씁니다.- 내적은 벡터의 길이(숫자 크기)에도 영향을 받습니다. 숫자가 큰 벡터는 방향이 조금만 맞아도 내적이 커집니다. 3장에서 Attention 점수를 √d_k 로 나누는 까닭이 여기에 있습니다.