콘텐츠로 이동

선형대수 연산

행렬 곱은 딥러닝에서 가장 빈번하게 사용되는 연산입니다. Linear 레이어, Attention 메커니즘 등 대부분이 행렬 곱으로 구성됩니다.

import torch
A = torch.tensor([[1.0, 2.0],
[3.0, 4.0]]) # shape: (2, 2)
B = torch.tensor([[5.0, 6.0],
[7.0, 8.0]]) # shape: (2, 2)
# 세 가지 방식 — 모두 동일한 결과
print(A @ B) # @ 연산자 (권장)
print(torch.matmul(A, B)) # 함수형 API
print(torch.mm(A, B)) # 2D 전용
# tensor([[19., 22.],
# [43., 50.]])
행렬 곱 (A @ B) — 결과 셀에 마우스를 올려보세요
A
1
2
3
4
 
@
B
5
6
7
8
 
=
결과
19
22
43
50
함수지원 차원배치 연산
torch.mm(A, B)2D 전용불가
torch.matmul(A, B) 또는 A @ B1D ~ ND가능
torch.bmm(A, B)3D 전용 (배치)전용
# matmul: 배치 행렬 곱 지원
batch_A = torch.randn(32, 3, 4) # 32개 배치, (3×4) 행렬
batch_B = torch.randn(32, 4, 5) # 32개 배치, (4×5) 행렬
result = torch.matmul(batch_A, batch_B)
print(result.shape) # torch.Size([32, 3, 5])
# bmm: 배치 행렬 곱 전용 (3D만 허용)
result = torch.bmm(batch_A, batch_B)
print(result.shape) # torch.Size([32, 3, 5])

torch.dot()1D 텐서(벡터) 의 내적(dot product)을 계산합니다.

a = torch.tensor([1.0, 2.0, 3.0])
b = torch.tensor([4.0, 5.0, 6.0])
# 내적: 1*4 + 2*5 + 3*6 = 32
print(torch.dot(a, b)) # tensor(32.)
# 내적은 두 벡터의 유사도 측정에 활용
# cos(θ) = dot(a, b) / (||a|| * ||b||)
norm_a = torch.norm(a)
norm_b = torch.norm(b)
cosine = torch.dot(a, b) / (norm_a * norm_b)
print(cosine) # tensor(0.9746) — 매우 유사한 방향
A = torch.tensor([[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0]]) # shape: (2, 3)
# .T: 모든 차원을 역순으로 뒤집음 (2D에서 전치와 동일)
print(A.T)
# tensor([[1., 4.],
# [2., 5.],
# [3., 6.]])
print(A.T.shape) # torch.Size([3, 2])
# torch.transpose(t, dim0, dim1): 두 차원을 교환
print(torch.transpose(A, 0, 1)) # A.T와 동일 (2D)
# 3D 텐서에서 transpose
t3 = torch.randn(2, 3, 4)
print(torch.transpose(t3, 1, 2).shape) # torch.Size([2, 4, 3])
A = torch.tensor([[1.0, 2.0],
[3.0, 4.0]])
# 역행렬
A_inv = torch.linalg.inv(A)
print(A_inv)
# tensor([[-2.0000, 1.0000],
# [ 1.5000, -0.5000]])
# 검증: A @ A_inv ≈ 단위행렬
print(A @ A_inv)
# tensor([[1.0000e+00, 0.0000e+00],
# [8.8818e-16, 1.0000e+00]]) — 부동소수점 오차 존재
# 행렬식 (determinant)
print(torch.linalg.det(A)) # tensor(-2.) — 1*4 - 2*3 = -2
# 행렬식이 0이면 역행렬이 존재하지 않음 (singular matrix)
singular = torch.tensor([[1.0, 2.0],
[2.0, 4.0]])
print(torch.linalg.det(singular)) # tensor(0.)

선형 시스템 풀기: torch.linalg.solve()

섹션 제목: “선형 시스템 풀기: torch.linalg.solve()”

역행렬을 직접 구하는 것보다 solve() 가 더 수치적으로 안정합니다.

# Ax = b 풀기
A = torch.tensor([[2.0, 1.0],
[5.0, 3.0]])
b = torch.tensor([4.0, 7.0])
x = torch.linalg.solve(A, b)
print(x) # tensor([5., -6.])
print(A @ x) # tensor([4., 7.]) — b와 일치

정방 행렬 A를 A = Q Λ Q⁻¹ 로 분해합니다.

A = torch.tensor([[4.0, 1.0],
[2.0, 3.0]])
# 고유값과 고유벡터
eigenvalues, eigenvectors = torch.linalg.eig(A)
print(eigenvalues) # tensor([5.+0.j, 2.+0.j]) — 복소수 형태
print(eigenvectors) # 각 열이 고유벡터
# 실수 대칭 행렬은 eigh() 사용 (더 효율적)
sym = torch.tensor([[3.0, 1.0],
[1.0, 3.0]])
eigenvalues, eigenvectors = torch.linalg.eigh(sym)
print(eigenvalues) # tensor([2., 4.]) — 실수 보장

SVD는 임의의 행렬 A를 A = U Σ Vᵀ 로 분해합니다. 차원 축소, 추천 시스템, 노이즈 제거 등에 활용합니다.

A = torch.tensor([[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0]]) # shape: (2, 3)
U, S, Vh = torch.linalg.svd(A, full_matrices=False)
print(U.shape) # torch.Size([2, 2])
print(S.shape) # torch.Size([2]) — 특이값 (내림차순)
print(Vh.shape) # torch.Size([2, 3])
print(S) # tensor([9.5080, 0.7729])
# 재구성 검증: U @ diag(S) @ Vh ≈ A
reconstructed = U @ torch.diag(S) @ Vh
print(torch.allclose(A, reconstructed, atol=1e-5)) # True
# 저랭크 근사 (rank-1 근사)
rank1 = S[0] * U[:, 0:1] @ Vh[0:1, :]
print(rank1)
# tensor([[1.0697, 1.8485, 2.6273],
# [3.9303, 6.7951, 9.6600]]) — 원본의 근사
함수설명
torch.linalg.inv(A)역행렬
torch.linalg.det(A)행렬식
torch.linalg.solve(A, b)선형 시스템 Ax=b 풀기
torch.linalg.eig(A)고유값/고유벡터 (복소수)
torch.linalg.eigh(A)고유값/고유벡터 (실수 대칭)
torch.linalg.svd(A)특이값 분해
torch.linalg.norm(A)행렬/벡터 노름
torch.linalg.matrix_rank(A)행렬 랭크
torch.linalg.pinv(A)의사역행렬 (pseudo-inverse)