1장. 준비 수학과 Transformer 개요
실습 2 — 행렬곱, 내적, Softmax 계산하기
행렬곱의 shape 규칙과 오류, 내적의 부호, Softmax 의 세 단계를 차례로 확인합니다. 노트북 1일차의 셀 10, 12, 14입니다. 실습 1의 1단계(import numpy as np)를 먼저 실행해 둡니다.
코드가 세 셀로 나뉘어 있어, 먼저 실행 흐름을 한 장으로 봅니다. 위에서 아래로 화살표를 따라 읽습니다.
| 갈래 | 단계 | 반복과 조건 | 핵심 한 줄 |
|---|---|---|---|
| A. 행렬곱 | 1 | try 안의 np.matmul(A, A) 에서 "ValueError 가 났나?"를 묻고, 났으면 except 아래에서 오류 문장을 찍는다 | C = np.matmul(A, B) |
| B. 내적 | 2 | 없음 | np.dot(u, v) |
| C. Softmax | 3 | 없음. softmax 안에도 반복문이 없고, NumPy 가 세 칸을 한꺼번에 계산한다 | e / e.sum(axis=-1, keepdims=True) |
- 세 갈래: 짙은 청록(A), 살구(B), 숯 회색(C) 세 갈래는 서로의 변수를 쓰지 않는 따로 된 셀입니다. 셋 다 실습 1의
np만 있으면 어느 것부터 실행해도 됩니다. - A 의 마름모:
try ~ except는 조건문의 한 가지입니다. 오류가 나면 "예" 쪽으로 내려가print("오류:", e)를 하고, 나지 않으면 "아니요" 쪽으로 건너뜁니다. 이 실습은 일부러 shape 가 맞지 않게 곱하므로 언제나 "예" 쪽을 지납니다. - C 의 정의와 실행:
def softmax(z)는 요리법을 적어 두기만 하고, 실제 계산은p = softmax(z)를 부를 때 합니다. 결과[0.66 0.24 0.1 ]의 합이1.0인지 마지막 네모에서 확인합니다.
1단계. 행렬곱과 shape 규칙
2행 3열 A 와 3행 2열 B 를 곱합니다. 점수표에 가중치표를 곱해 총점을 내는 것과 같은 계산입니다. 안쪽 숫자가 다른 A @ A 는 일부러 오류를 내 봅니다.
# A = 2행 3열 행렬. 바깥 대괄호 안에 행이 두 개, 행마다 숫자 세 개
A = np.array([[1, 2, 3],
[4, 5, 6]]) # A는 2행 3열 행렬입니다.
# B = 3행 2열 행렬. 행이 세 개, 행마다 숫자 두 개
B = np.array([[1, 0],
[0, 1],
[1, 1]]) # B는 3행 2열 행렬입니다.
# np.matmul = matrix multiplication(행렬곱). A @ B 와 같다
# (2, 3) @ (3, 2): 안쪽 3이 같아 곱할 수 있고, 결과는 바깥 숫자를 따라 (2, 2)
C = np.matmul(A, B) # 안쪽 크기 3이 같으므로 곱할 수 있고, 결과는 2행 2열입니다.
# 결과: [[ 4 5]
# [10 11]] → 첫 칸은 [1, 2, 3]·[1, 0, 1] = 1 + 0 + 3 = 4
print("A @ B =\n", C)
# 결과: 결과 shape: (2, 2) → (2, 2)
print("결과 shape:", C.shape, "→ (2, 2)")
# try ~ except = 오류가 나도 멈추지 않고 except 아래를 실행한다
try: # 안쪽 크기가 다른 행렬을 곱하면 어떤 오류가 나는지 확인합니다.
# (2, 3) @ (2, 3): 앞의 열 수 3과 뒤의 행 수 2가 달라 곱할 수 없다
np.matmul(A, A) # A @ A는 안쪽 크기 3과 2가 달라 계산할 수 없습니다.
# ValueError = 값(여기서는 shape)이 맞지 않을 때 나는 오류. as e 로 오류 내용을 e 에 담는다
except ValueError as e:
# 결과: 오류: matmul: Input operand 1 has a mismatch in its core dimension 0, ... (size 2 is different from 3)
print("오류:", e)A @ B =
[[ 4 5]
[10 11]]
결과 shape: (2, 2) → (2, 2)
오류: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 3)2단계. 내적으로 비슷한 정도 재기
두 벡터의 같은 자리 숫자끼리 곱해 더합니다. 두 사람의 취향이 얼마나 같은 쪽인지 재는 점수입니다.
# u, v = 숫자 3개짜리 벡터 두 개
u = np.array([1, 2, 3])
v = np.array([4, 5, 6])
# np.dot = dot product(내적). 같은 자리끼리 곱해 모두 더한다
# 결과: u · v = 32 → 1×4 + 2×5 + 3×6 = 32
print("u · v =", np.dot(u, v), "→ 1×4 + 2×5 + 3×6 = 32")
# -u = u 의 모든 숫자에 -1 을 곱한 반대 방향 벡터 [-1, -2, -3]
# 결과: u와 반대 방향 벡터(-u)의 내적 = -14 → 음수가 나옵니다
print("u와 반대 방향 벡터(-u)의 내적 =", np.dot(u, -u), "→ 음수가 나옵니다")u · v = 32 → 1×4 + 2×5 + 3×6 = 32
u와 반대 방향 벡터(-u)의 내적 = -14 → 음수가 나옵니다3단계. Softmax 로 점수를 확률로
점수 세 개를 합이 1인 확률로 바꿉니다. 득표수를 투표 비율로 바꾸는 일입니다. 강사자료 그림 softmax_steps 의 세 단계(점수, e^z, 합으로 나누기)를 숫자로 찍습니다.
# def softmax(z) = 점수 배열 z 를 받아 확률 배열을 돌려주는 함수를 만든다
def softmax(z): # 마지막 축(행마다) 기준으로 확률을 만듭니다.
# np.exp = exponential. e^z 를 구한다(e ≈ 2.718)
# z.max(axis=-1, keepdims=True) = 마지막 축의 최댓값. keepdims=True 는 빼기를 할 수 있게 축을 남겨 둔다
e = np.exp(z - z.max(axis=-1, keepdims=True)) # 큰 값에서 넘치지 않도록 최댓값을 빼 줍니다.
# e.sum(axis=-1, keepdims=True) = 마지막 축으로 더한 합. 이것으로 나누면 합이 1
return e / e.sum(axis=-1, keepdims=True) # 합으로 나누면 합이 1인 확률이 됩니다.
# 클래스 A, B, C 의 점수(logit). 강사자료 그림의 2.0, 1.0, 0.1 과 같다
z = np.array([2.0, 1.0, 0.1]) # 클래스 3개에 대한 점수입니다.
p = softmax(z) # 세 점수를 각 클래스의 확률로 바꿉니다.
# 결과: 점수 z : [2. 1. 0.1]
print("점수 z :", z)
# np.round(배열, 2) = 소수 둘째 자리까지 반올림
# 결과: e^z : [7.39 2.72 1.11]
print("e^z :", np.round(np.exp(z), 2))
# 결과: 확률 p : [0.66 0.24 0.1 ] → 7.39 ÷ 11.22 ≈ 0.66
print("확률 p :", np.round(p, 2))
# p.sum() = 확률을 모두 더한 값
# 결과: 확률의 합 : 1.0
print("확률의 합 :", p.sum())점수 z : [2. 1. 0.1]
e^z : [7.39 2.72 1.11]
확률 p : [0.66 0.24 0.1 ]
확률의 합 : 1.0이 실습이 뜻하는 것
점수표에 가중치표를 곱하고(1단계), 두 사람 취향이 같은 쪽인지 재고(2단계), 득표 점수를 투표 비율로 바꾼(3단계) 일입니다. 셋을 이으면 Attention 의 뼈대가 됩니다. 내적으로 관련도 점수를 매기고, Softmax 로 비율을 정하고, 행렬곱으로 내용을 섞습니다. 여기서 점수표가 A, 가중치표가 B, 취향 줄이 u, v, 득표 점수가 z, 투표 비율이 p 입니다.
| 단계 | 출력 | 뜻 |
|---|---|---|
| 1. 행렬곱 | [[ 4 5] [10 11]], (2, 2), 오류 문장 | 안쪽 숫자가 같아야 곱해지고, 결과는 바깥 숫자를 따른다 |
| 2. 내적 | 32, -14 | 같은 쪽이면 양수, 반대 쪽이면 음수 |
| 3. Softmax | [0.66 0.24 0.1 ], 합 1.0 | 점수가 합 1인 확률이 되었다 |
한 줄로 줄이면, Transformer 안의 계산은 거의 모두 "곱하고 더하기(행렬곱, 내적)"와 "비율로 바꾸기(Softmax)"의 조합입니다.
코드 설명
- 1단계의 오류 문장
size 2 is different from 3은 "뒤 행렬의 행 수 2가 앞 행렬의 열 수 3과 다르다"는 뜻입니다. shape 오류가 나면 두 행렬의 안쪽 숫자부터 확인합니다. C의 칸 하나는 A 의 행 하나와 B 의 열 하나의 내적입니다. 둘째 행 첫 칸은 [4, 5, 6]·[1, 0, 1] = 4 + 0 + 6 = 10 입니다.- 2단계의 -14 는 1×(-1) + 2×(-2) + 3×(-3) = -1 - 4 - 9 입니다. 반대 방향 벡터와의 내적은 음수입니다.
- 3단계의 softmax 는
axis=-1로 마지막 축을 기준으로 계산하므로 점수가 표로 들어와도 행마다 합이 1이 됩니다. 3장의 Attention 표에서 이 성질을 씁니다. - 확률 출력의
0.1은 0.10 을 NumPy 가 줄여 찍은 것입니다. 확률의 합1.0은 반올림 전의 값을 더한 것입니다.