페이지 불러오는 중
전처리·머신러닝에 필요한 만큼의 선형대수만 다룹니다.
핵심은 @(행렬곱)와 *(원소별 곱)의 구분, 그리고 np.linalg의 inv/norm/eig/svd입니다.
PCA·차원축소·정규방정식이 전부 이 함수들 위에 있습니다.
import numpy as np@ vs * 반드시 구분A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
A * B # 원소별 곱 (Hadamard)
# [[ 5 12]
# [21 32]]
A @ B # 행렬곱
# [[19 22]
# [43 50]]*는 원소별입니다. shape가 같거나 브로드캐스팅 가능해야 합니다.@는 행렬곱입니다. (m, k) @ (k, n) → (m, n). 안쪽 차원 k가 일치해야 합니다.np.dot(A, B)는 A @ B와 (2차원에서) 동일합니다. np.matmul도 마찬가지입니다.조용한 버그 1순위: 행렬곱을 하려다
*를 써도 shape가 우연히 맞으면 에러 없이 틀린 값이 나옵니다.
A = np.array([[1., 2.],
[3., 4.]])
v = np.array([1., 1.]) # (2,)
A @ v # array([3., 7.]) - (2,2) @ (2,) → (2,)
v @ A # array([4., 6.]) - (2,) @ (2,2) → (2,) (왼쪽 곱)
A.T @ A # (2,2) - "그람 행렬", 공분산·정규방정식에서 등장💡 이럴 때: 공분산 행렬이나 선형회귀 정규방정식의 그람 행렬이 필요할 때.
np.linalg 주요 함수A = np.array([[4., 2.],
[1., 3.]])
np.linalg.inv(A) # 역행렬 (정칙행렬일 때만; 아니면 LinAlgError)
np.linalg.det(A) # 행렬식
np.linalg.matrix_rank(A) # 계수(rank)
np.linalg.norm(v) # L2 노름 (벡터 길이) = sqrt(sum(v**2))
np.linalg.norm(A, ord='fro') # 프로베니우스 노름
# 선형 연립방정식 Ax = b → x
b = np.array([1., 2.])
x = np.linalg.solve(A, b) # inv(A) @ b 보다 빠르고 수치적으로 안정
solvevsinv:x = inv(A) @ b보다x = np.linalg.solve(A, b)가 더 정확하고 빠릅니다. 역행렬을 명시적으로 구할 이유는 거의 없습니다.
💡 이럴 때: 선형회귀 정규방정식 등 Ax = b를 풀 때 inv 대신 solve를 씁니다.
A = np.array([[2., 0.],
[0., 3.]])
eigvals, eigvecs = np.linalg.eig(A)
# eigvals: array([2., 3.])
# eigvecs: 각 열이 고유벡터
# 대칭행렬(공분산 등)이면 eigh가 더 빠르고 안정적
eigvals, eigvecs = np.linalg.eigh(A)💡 이럴 때: 공분산처럼 대칭행렬의 고윳값이 필요할 때는
eig가 아니라eigh를 씁니다.
X = np.array([[2., 1., 0.],
[0., 3., 1.],
[4., 0., 2.],
[1., 2., 5.]]) # 각 축으로 실제 분산이 있는 데이터
# 보통 중심화(평균 제거) 후 SVD
Xc = X - X.mean(axis=0)
U, S, Vt = np.linalg.svd(Xc, full_matrices=False)
# S: 특이값 (큰 값 = 분산이 큰 방향)
# Vt: 주성분 방향 (각 행이 하나의 주성분)
# 0에 가까운 특이값은 버린다 (S <= tol인 성분은 노이즈)
k = int(np.sum(S > 1e-10)) # 유효 성분 개수
X_reduced = Xc @ Vt[:k].T # (4, k)로 투영주의:
Xc의 계수(rank)보다 큰k를 쓰면 그 성분의 방향은 수치적으로 무의미한 노이즈입니다.k는 반드시S > tol인 개수 이하로 잡습니다.
💡 이럴 때: 차원축소(PCA)를 직접 구현하거나 노이즈 성분을 잘라낼 때. 중심화 후 SVD가 표준 절차입니다.
PCA를 직접 구현할 일은 드물지만(sklearn.decomposition.PCA), "PCA = 공분산행렬의 고유분해 = 중심화 데이터의 SVD"라는 연결은 알아두면 좋습니다.
댓글
GitHub 계정으로 의견이나 질문을 남길 수 있습니다.