Skip to content

필사 모드: 선형대수학 완전정복: 벡터부터 SVD까지 제로투히어로 가이드

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

들어가며

선형대수학(Linear Algebra)은 현대 수학과 공학의 핵심 기초입니다. 데이터 과학, 머신러닝, 컴퓨터 그래픽스, 양자역학에 이르기까지 거의 모든 분야에서 활용됩니다. 이 가이드는 벡터와 행렬의 기초부터 SVD(특이값 분해)까지, 선형대수학의 핵심 개념을 체계적으로 다룹니다.

**참고 자료:**

- Gilbert Strang, MIT OCW 18.06 Linear Algebra

- 3Blue1Brown: Essence of Linear Algebra (YouTube)

- NumPy 공식 문서 (numpy.org)

- scikit-learn PCA 문서

1. 벡터 (Vectors)

벡터의 정의와 표현

벡터(vector)는 크기(magnitude)와 방향(direction)을 모두 가진 수학적 객체입니다. $n$차원 벡터 $\mathbf{v}$는 다음과 같이 표현합니다.

$$\mathbf{v} = \begin{pmatrix} v_1 \\ v_2 \\ \vdots \\ v_n \end{pmatrix} \in \mathbb{R}^n$$

벡터 연산

**덧셈과 스칼라 곱:**

$$\mathbf{u} + \mathbf{v} = \begin{pmatrix} u_1 + v_1 \\ u_2 + v_2 \end{pmatrix}, \quad c\mathbf{v} = \begin{pmatrix} cv_1 \\ cv_2 \end{pmatrix}$$

**내적(Dot Product):**

$$\mathbf{u} \cdot \mathbf{v} = \sum_{i=1}^{n} u_i v_i = \|\mathbf{u}\| \|\mathbf{v}\| \cos\theta$$

내적이 0이면 두 벡터는 서로 직교(orthogonal)합니다.

**외적(Cross Product)** — 3차원에서만 정의:

$$\mathbf{u} \times \mathbf{v} = \begin{vmatrix} \mathbf{i} & \mathbf{j} & \mathbf{k} \\ u_1 & u_2 & u_3 \\ v_1 & v_2 & v_3 \end{vmatrix}$$

노름 (Norm)

- **L1 노름:** $\|\mathbf{v}\|_1 = \sum_{i} |v_i|$

- **L2 노름 (유클리드):** $\|\mathbf{v}\|_2 = \sqrt{\sum_{i} v_i^2}$

- **Lp 노름:** $\|\mathbf{v}\|_p = \left(\sum_{i} |v_i|^p\right)^{1/p}$

**단위벡터 (Unit Vector):**

$$\hat{\mathbf{v}} = \frac{\mathbf{v}}{\|\mathbf{v}\|}$$

벡터 정의

a = np.array([1, 2, 3])

b = np.array([4, 5, 6])

벡터 덧셈

print("덧셈:", a + b) # [5 7 9]

스칼라 곱

print("스칼라 곱:", 3 * a) # [3 6 9]

내적

dot = np.dot(a, b)

print("내적:", dot) # 32

외적

cross = np.cross(a, b)

print("외적:", cross) # [-3 6 -3]

L2 노름

norm_l2 = np.linalg.norm(a)

print("L2 노름:", norm_l2) # 3.7416...

L1 노름

norm_l1 = np.linalg.norm(a, ord=1)

print("L1 노름:", norm_l1) # 6.0

단위벡터

unit = a / np.linalg.norm(a)

print("단위벡터:", unit)

print("크기 확인:", np.linalg.norm(unit)) # 1.0

두 벡터의 각도

cos_theta = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

angle = np.arccos(cos_theta)

print("각도 (라디안):", angle)

print("각도 (도):", np.degrees(angle))

2. 행렬 (Matrices)

행렬의 정의와 종류

$m \times n$ 행렬은 $m$개의 행과 $n$개의 열로 이루어진 수의 직사각형 배열입니다.

$$A = \begin{pmatrix} a_{11} & a_{12} & \cdots & a_{1n} \\ a_{21} & a_{22} & \cdots & a_{2n} \\ \vdots & \vdots & \ddots & \vdots \\ a_{m1} & a_{m2} & \cdots & a_{mn} \end{pmatrix}$$

**주요 행렬 종류:**

| 종류 | 정의 | 예시 |

| --------------------- | --------------------------- | -------------------------------------------- |

| 단위행렬 (Identity) | $I_{ij} = 1$ (i=j), 0 (i≠j) | $I_2 = \begin{pmatrix}1&0\\0&1\end{pmatrix}$ |

| 대각행렬 (Diagonal) | 대각선 외 모두 0 | $\text{diag}(d_1, d_2)$ |

| 대칭행렬 (Symmetric) | $A = A^T$ | — |

| 직교행렬 (Orthogonal) | $A^T A = I$ | 회전 행렬 |

행렬 연산

**행렬 곱 (Matrix Multiplication):**

$(AB)_{ij} = \sum_{k=1}^{n} a_{ik} b_{kj}$

행렬 곱은 교환 법칙이 성립하지 않습니다: $AB \neq BA$ (일반적으로)

**전치행렬 (Transpose):**

$(A^T)_{ij} = A_{ji}$

**역행렬 (Inverse):**

정방행렬 $A$의 역행렬 $A^{-1}$은 $AA^{-1} = A^{-1}A = I$를 만족합니다. 역행렬은 행렬식이 0이 아닐 때만 존재합니다.

행렬 정의

A = np.array([[1, 2], [3, 4]])

B = np.array([[5, 6], [7, 8]])

행렬 덧셈

print("행렬 덧셈:\n", A + B)

행렬 곱

C = A @ B

print("행렬 곱:\n", C)

원소별 곱 (Hadamard product)

print("원소별 곱:\n", A * B)

전치행렬

print("전치행렬:\n", A.T)

역행렬

A_inv = np.linalg.inv(A)

print("역행렬:\n", A_inv)

검증: A @ A_inv = I

print("검증 (A @ A_inv):\n", A @ A_inv)

단위행렬

I = np.eye(3)

print("3x3 단위행렬:\n", I)

대각행렬

D = np.diag([1, 2, 3])

print("대각행렬:\n", D)

대칭행렬 생성

S = A @ A.T

print("대칭행렬 (A @ A^T):\n", S)

print("대칭 확인:", np.allclose(S, S.T))

3. 행렬식 (Determinant)

행렬식의 정의와 계산

행렬식(determinant)은 정방행렬에서 정의되는 스칼라 값으로, 행렬의 다양한 특성을 담고 있습니다.

**2×2 행렬식:**

$$\det\begin{pmatrix} a & b \\ c & d \end{pmatrix} = ad - bc$$

**3×3 행렬식 (사루스 법칙, Sarrus' Rule):**

$$\det(A) = a_{11}(a_{22}a_{33} - a_{23}a_{32}) - a_{12}(a_{21}a_{33} - a_{23}a_{31}) + a_{13}(a_{21}a_{32} - a_{22}a_{31})$$

행렬식의 성질

1. $\det(I) = 1$

2. $\det(AB) = \det(A)\det(B)$

3. $\det(A^T) = \det(A)$

4. $\det(A^{-1}) = 1/\det(A)$

5. 행렬식이 0이면 역행렬이 존재하지 않음 (특이행렬, singular matrix)

6. 기하학적으로: 행 벡터들이 이루는 평행육면체의 부피

크라머 공식 (Cramer's Rule)

연립방정식 $A\mathbf{x} = \mathbf{b}$의 해:

$$x_i = \frac{\det(A_i)}{\det(A)}$$

여기서 $A_i$는 $A$의 $i$번째 열을 $\mathbf{b}$로 대체한 행렬입니다.

A = np.array([[1, 2, 3],

[4, 5, 6],

[7, 8, 9]])

B = np.array([[2, -1, 0],

[-1, 2, -1],

[0, -1, 2]])

행렬식 계산

det_A = np.linalg.det(A)

det_B = np.linalg.det(B)

print("det(A):", det_A) # 거의 0 (특이행렬에 가까움)

print("det(B):", det_B) # 4.0

성질 검증: det(A@B) = det(A)*det(B)

print("det(A)*det(B):", det_A * det_B)

print("det(A@B):", np.linalg.det(A @ B))

크라머 공식으로 연립방정식 풀기

A_sys = np.array([[2, 1], [5, 3]], dtype=float)

b_sys = np.array([1, 2], dtype=float)

det_A_sys = np.linalg.det(A_sys)

x1 계산

A1 = A_sys.copy()

A1[:, 0] = b_sys

x1 = np.linalg.det(A1) / det_A_sys

x2 계산

A2 = A_sys.copy()

A2[:, 1] = b_sys

x2 = np.linalg.det(A2) / det_A_sys

print(f"x1 = {x1}, x2 = {x2}")

numpy linalg solve로 검증

x = np.linalg.solve(A_sys, b_sys)

print("numpy solve:", x)

4. 연립방정식과 가우스 소거법

선형 연립방정식

$n$개의 미지수에 대한 $m$개의 선형방정식:

$$a_{11}x_1 + a_{12}x_2 + \cdots + a_{1n}x_n = b_1$$

$$a_{21}x_1 + a_{22}x_2 + \cdots + a_{2n}x_n = b_2$$

$$\vdots$$

$$a_{m1}x_1 + a_{m2}x_2 + \cdots + a_{mn}x_n = b_m$$

행렬 형태: $A\mathbf{x} = \mathbf{b}$

첨가행렬 (Augmented Matrix)

$$[A|\mathbf{b}] = \left(\begin{array}{ccc|c} a_{11} & \cdots & a_{1n} & b_1 \\ \vdots & \ddots & \vdots & \vdots \\ a_{m1} & \cdots & a_{mn} & b_m \end{array}\right)$$

가우스 소거법 (Gaussian Elimination)

행 기본 연산(Elementary Row Operations):

1. 두 행 교환: $R_i \leftrightarrow R_j$

2. 행에 스칼라 곱: $R_i \leftarrow c \cdot R_i$

3. 행의 배수 더하기: $R_i \leftarrow R_i + c \cdot R_j$

이 연산으로 행사다리꼴(Row Echelon Form, REF) 또는 기약행사다리꼴(Reduced REF)을 만듭니다.

LU 분해

$A = LU$: 하삼각행렬 $L$과 상삼각행렬 $U$의 곱으로 분해

from scipy import linalg

연립방정식: 2x + y = 5, x + 3y = 10

A = np.array([[2, 1], [1, 3]], dtype=float)

b = np.array([5, 10], dtype=float)

numpy linalg.solve

x = np.linalg.solve(A, b)

print("해:", x) # [1. 3.]

가우스-조르단 소거법 구현

def gauss_jordan(A, b):

n = len(b)

첨가행렬 생성

aug = np.hstack([A.astype(float), b.reshape(-1, 1).astype(float)])

for col in range(n):

피벗 선택 (부분 피벗팅)

max_row = np.argmax(np.abs(aug[col:, col])) + col

aug[[col, max_row]] = aug[[max_row, col]]

피벗 행 정규화

aug[col] = aug[col] / aug[col, col]

다른 행에서 소거

for row in range(n):

if row != col:

aug[row] -= aug[row, col] * aug[col]

return aug[:, -1]

solution = gauss_jordan(A.copy(), b.copy())

print("가우스-조르단 해:", solution)

LU 분해

P, L, U = linalg.lu(A)

print("L 행렬:\n", L)

print("U 행렬:\n", U)

print("P 행렬:\n", P)

LU 분해로 연립방정식 풀기

x_lu = linalg.solve(A, b)

print("LU 분해 해:", x_lu)

5. 벡터 공간 (Vector Spaces)

벡터 공간의 정의

집합 $V$가 벡터 공간이 되려면 덧셈과 스칼라 곱에 대해 다음 8가지 공리를 만족해야 합니다:

1. **닫힘 (Closure):** $\mathbf{u}, \mathbf{v} \in V \Rightarrow \mathbf{u} + \mathbf{v} \in V$

2. **교환법칙:** $\mathbf{u} + \mathbf{v} = \mathbf{v} + \mathbf{u}$

3. **결합법칙:** $(\mathbf{u} + \mathbf{v}) + \mathbf{w} = \mathbf{u} + (\mathbf{v} + \mathbf{w})$

4. **영벡터 존재:** $\mathbf{v} + \mathbf{0} = \mathbf{v}$

5. **역원 존재:** $\mathbf{v} + (-\mathbf{v}) = \mathbf{0}$

6. **스칼라 닫힘:** $c \in \mathbb{R}, \mathbf{v} \in V \Rightarrow c\mathbf{v} \in V$

7. **분배법칙 1:** $c(\mathbf{u} + \mathbf{v}) = c\mathbf{u} + c\mathbf{v}$

8. **분배법칙 2:** $(c + d)\mathbf{v} = c\mathbf{v} + d\mathbf{v}$

선형독립 (Linear Independence)

벡터 집합 $\{\mathbf{v}_1, \mathbf{v}_2, \ldots, \mathbf{v}_k\}$이 **선형독립**이란:

$$c_1\mathbf{v}_1 + c_2\mathbf{v}_2 + \cdots + c_k\mathbf{v}_k = \mathbf{0} \implies c_1 = c_2 = \cdots = c_k = 0$$

즉, 자명한 해(trivial solution)만 존재해야 합니다.

기저 (Basis)와 차원 (Dimension)

벡터 공간 $V$의 **기저(basis)**는 $V$를 생성(span)하는 선형독립인 벡터들의 집합입니다.

- **차원(dimension):** 기저의 원소 개수

- $\mathbb{R}^n$의 표준기저: $\mathbf{e}_1, \mathbf{e}_2, \ldots, \mathbf{e}_n$

행공간, 열공간, 영공간

행렬 $A \in \mathbb{R}^{m \times n}$에 대해:

- **행공간 (Row Space):** $A$의 행 벡터들로 생성되는 공간, $\subseteq \mathbb{R}^n$

- **열공간 (Column Space):** $A$의 열 벡터들로 생성되는 공간, $\subseteq \mathbb{R}^m$

- **영공간 (Null Space):** $A\mathbf{x} = \mathbf{0}$의 해 집합, $\subseteq \mathbb{R}^n$

- **차원 정리:** $\text{rank}(A) + \text{nullity}(A) = n$

랭크 (Rank)

$\text{rank}(A) = \dim(\text{Row Space}) = \dim(\text{Column Space})$

A = np.array([[1, 2, 3],

[4, 5, 6],

[2, 4, 6]]) # 3행은 1행의 2배 (선형종속)

랭크 계산

rank = np.linalg.matrix_rank(A)

print("랭크:", rank) # 2

영공간 (null space) 계산

from scipy.linalg import null_space

ns = null_space(A)

print("영공간:\n", ns)

열공간의 기저 (QR 분해 활용)

Q, R = np.linalg.qr(A)

print("열공간의 기저 (Q의 처음 rank개 열):\n", Q[:, :rank])

선형독립 확인 예시

v1 = np.array([1, 0, 0])

v2 = np.array([0, 1, 0])

v3 = np.array([0, 0, 1])

M = np.column_stack([v1, v2, v3])

print("v1, v2, v3의 랭크:", np.linalg.matrix_rank(M)) # 3 = 선형독립

v4 = np.array([1, 2, 3])

v5 = np.array([2, 4, 6]) # v5 = 2*v4

M2 = np.column_stack([v4, v5])

print("v4, v5의 랭크:", np.linalg.matrix_rank(M2)) # 1 = 선형종속

6. 선형 변환 (Linear Transformations)

선형 변환의 정의

함수 $T: V \to W$가 **선형 변환**이 되려면:

1. $T(\mathbf{u} + \mathbf{v}) = T(\mathbf{u}) + T(\mathbf{v})$ (가산성)

2. $T(c\mathbf{v}) = cT(\mathbf{v})$ (동차성)

행렬 표현

모든 선형 변환은 행렬로 표현됩니다: $T(\mathbf{x}) = A\mathbf{x}$

**핵 (Kernel):** $\ker(T) = \{\mathbf{v} \in V : T(\mathbf{v}) = \mathbf{0}\}$

**상 (Image):** $\text{Im}(T) = \{T(\mathbf{v}) : \mathbf{v} \in V\}$

**차원 정리:** $\dim(\ker T) + \dim(\text{Im} T) = \dim V$

기하학적 변환

2차원에서의 주요 변환 행렬:

**회전 행렬 (각도 $\theta$):**

$$R(\theta) = \begin{pmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{pmatrix}$$

**x축 반사:**

$$F_x = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix}$$

**균등 확대/축소 (배율 s):**

$$S = \begin{pmatrix} s & 0 \\ 0 & s \end{pmatrix}$$

회전 변환

def rotation_matrix(theta):

return np.array([[np.cos(theta), -np.sin(theta)],

[np.sin(theta), np.cos(theta)]])

45도 회전

theta = np.pi / 4

R = rotation_matrix(theta)

print("45도 회전 행렬:\n", R)

벡터 변환

v = np.array([1, 0])

v_rotated = R @ v

print("회전된 벡터:", v_rotated) # [0.707, 0.707]

전단 변환 (shear)

shear = np.array([[1, 1],

[0, 1]])

단위 정사각형의 꼭짓점 변환

corners = np.array([[0, 1, 1, 0, 0],

[0, 0, 1, 1, 0]])

transformed = shear @ corners

print("전단 변환 후 꼭짓점:\n", transformed)

직교 행렬 검증

print("R^T @ R =\n", R.T @ R) # 단위행렬

print("det(R) =", np.linalg.det(R)) # 1

7. 내적 공간 (Inner Product Spaces)

내적의 정의

내적(inner product) $\langle \cdot, \cdot \rangle: V \times V \to \mathbb{R}$은 다음을 만족합니다:

1. **양정치성:** $\langle \mathbf{v}, \mathbf{v} \rangle \geq 0$, 등호는 $\mathbf{v} = \mathbf{0}$일 때만

2. **대칭성:** $\langle \mathbf{u}, \mathbf{v} \rangle = \langle \mathbf{v}, \mathbf{u} \rangle$

3. **선형성:** $\langle a\mathbf{u} + b\mathbf{v}, \mathbf{w} \rangle = a\langle \mathbf{u}, \mathbf{w} \rangle + b\langle \mathbf{v}, \mathbf{w} \rangle$

코시-슈바르츠 부등식

$$|\langle \mathbf{u}, \mathbf{v} \rangle| \leq \|\mathbf{u}\| \cdot \|\mathbf{v}\|$$

등호는 $\mathbf{u}$와 $\mathbf{v}$가 평행할 때 성립합니다.

그람-슈미트 과정 (Gram-Schmidt Process)

선형독립인 벡터들로부터 정규직교기저(orthonormal basis)를 구성합니다.

주어진 벡터 $\mathbf{v}_1, \mathbf{v}_2, \ldots, \mathbf{v}_k$에 대해:

$$\mathbf{u}_1 = \mathbf{v}_1$$

$$\mathbf{u}_2 = \mathbf{v}_2 - \frac{\langle \mathbf{v}_2, \mathbf{u}_1 \rangle}{\langle \mathbf{u}_1, \mathbf{u}_1 \rangle} \mathbf{u}_1$$

일반적으로:

$$\mathbf{u}_k = \mathbf{v}_k - \sum_{j=1}^{k-1} \frac{\langle \mathbf{v}_k, \mathbf{u}_j \rangle}{\langle \mathbf{u}_j, \mathbf{u}_j \rangle} \mathbf{u}_j$$

마지막으로 각 $\mathbf{u}_i$를 정규화합니다.

def gram_schmidt(vectors):

"""그람-슈미트 정규직교화"""

basis = []

for v in vectors:

w = v.copy().astype(float)

for b in basis:

w -= np.dot(v, b) * b

norm = np.linalg.norm(w)

if norm > 1e-10:

basis.append(w / norm)

return np.array(basis)

선형독립인 벡터들

v1 = np.array([1, 1, 0])

v2 = np.array([1, 0, 1])

v3 = np.array([0, 1, 1])

그람-슈미트 적용

basis = gram_schmidt([v1, v2, v3])

print("정규직교기저:\n", basis)

직교성 확인

print("내적 확인 (0이어야 함):")

print("e1 dot e2:", np.dot(basis[0], basis[1]))

print("e1 dot e3:", np.dot(basis[0], basis[2]))

print("e2 dot e3:", np.dot(basis[1], basis[2]))

단위벡터 확인

print("노름 확인 (1이어야 함):")

for i, b in enumerate(basis):

print(f" e{i+1} 노름:", np.linalg.norm(b))

scipy의 그람-슈미트 (QR 분해 활용)

A = np.column_stack([v1, v2, v3])

Q, R = np.linalg.qr(A)

print("QR 분해 Q 행렬:\n", Q)

8. 고유값과 고유벡터 (Eigenvalues & Eigenvectors)

정의

정방행렬 $A$에 대해 다음을 만족하는 스칼라 $\lambda$와 영이 아닌 벡터 $\mathbf{v}$:

$$A\mathbf{v} = \lambda\mathbf{v}$$

- $\lambda$: **고유값 (eigenvalue)**

- $\mathbf{v}$: $\lambda$에 대응하는 **고유벡터 (eigenvector)**

고유벡터는 $A$에 의해 변환될 때 방향은 유지되고 크기만 $\lambda$배 변합니다.

특성 방정식

$$\det(A - \lambda I) = 0$$

이 다항식(characteristic polynomial)의 근이 고유값입니다.

**2×2 예시:**

$$A = \begin{pmatrix} 4 & 1 \\ 2 & 3 \end{pmatrix}$$

$$\det(A - \lambda I) = (4-\lambda)(3-\lambda) - 2 = \lambda^2 - 7\lambda + 10 = (\lambda-2)(\lambda-5) = 0$$

따라서 $\lambda_1 = 2$, $\lambda_2 = 5$

고유값 분해 (Eigendecomposition)

대각화 가능한 행렬 $A$는 다음과 같이 분해됩니다:

$$A = P \Lambda P^{-1}$$

여기서 $\Lambda = \text{diag}(\lambda_1, \lambda_2, \ldots, \lambda_n)$이고 $P$의 열들은 고유벡터입니다.

대칭 행렬의 스펙트럼 정리

실수 대칭 행렬 $A = A^T$는:

1. 모든 고유값이 실수

2. 서로 다른 고유값에 대한 고유벡터는 직교

3. $A = Q \Lambda Q^T$ (직교 대각화 가능)

from scipy.linalg import eig

일반 행렬의 고유값/고유벡터

A = np.array([[4, 1],

[2, 3]])

eigenvalues, eigenvectors = np.linalg.eig(A)

print("고유값:", eigenvalues) # [5., 2.]

print("고유벡터 (열):\n", eigenvectors)

검증: A @ v = lambda * v

for i in range(len(eigenvalues)):

lam = eigenvalues[i]

v = eigenvectors[:, i]

print(f"\nlambda={lam:.2f}:")

print(" A @ v:", A @ v)

print(" lambda * v:", lam * v)

고유값 분해: A = P @ Lambda @ P^{-1}

P = eigenvectors

Lambda = np.diag(eigenvalues)

A_reconstructed = P @ Lambda @ np.linalg.inv(P)

print("\n재구성 행렬:\n", A_reconstructed)

print("원래 행렬과 동일:", np.allclose(A, A_reconstructed))

대칭 행렬 (실수 고유값 보장)

B = np.array([[2, 1, 0],

[1, 3, 1],

[0, 1, 2]])

eigenvalues_B, eigenvectors_B = np.linalg.eigh(B) # 대칭 행렬 전용

print("\n대칭 행렬 고유값:", eigenvalues_B)

print("직교성 확인 (Q^T @ Q = I):\n",

np.round(eigenvectors_B.T @ eigenvectors_B))

거듭제곱: A^n = P @ Lambda^n @ P^{-1}

n = 5

Lambda_n = np.diag(eigenvalues ** n)

A_power = P @ Lambda_n @ np.linalg.inv(P)

print(f"\nA^{n}:\n", A_power)

print("직접 계산과 비교:\n", np.linalg.matrix_power(A, n))

9. 특이값 분해 (SVD - Singular Value Decomposition)

SVD의 정의

임의의 $m \times n$ 행렬 $A$는 다음과 같이 분해됩니다:

$$A = U \Sigma V^T$$

- $U \in \mathbb{R}^{m \times m}$: 좌특이벡터(left singular vectors), 직교행렬

- $\Sigma \in \mathbb{R}^{m \times n}$: 특이값 대각행렬 ($\sigma_1 \geq \sigma_2 \geq \cdots \geq 0$)

- $V \in \mathbb{R}^{n \times n}$: 우특이벡터(right singular vectors), 직교행렬

기하학적 의미

$A$는 다음 세 변환의 합성으로 볼 수 있습니다:

1. $V^T$: 회전/반사

2. $\Sigma$: 좌표축 방향 확대/축소

3. $U$: 회전/반사

PCA와의 관계

데이터 행렬 $X$의 SVD를 수행하면 주성분 분석(PCA)을 수행한 것과 동일합니다.

$X = U \Sigma V^T$에서 $V$의 열들이 주성분(principal components)이 됩니다.

이미지 압축

특이값을 내림차순으로 정렬하면 상위 $k$개의 특이값으로 근사 행렬을 구성할 수 있습니다:

$$A_k = \sum_{i=1}^{k} \sigma_i \mathbf{u}_i \mathbf{v}_i^T$$

기본 SVD

A = np.array([[1, 2, 3],

[4, 5, 6],

[7, 8, 9],

[10, 11, 12]])

U, S, Vt = np.linalg.svd(A)

print("U 형태:", U.shape) # (4, 4)

print("S (특이값):", S) # 특이값

print("Vt 형태:", Vt.shape) # (3, 3)

재구성 검증

Sigma = np.zeros(A.shape)

Sigma[:min(A.shape), :min(A.shape)] = np.diag(S)

A_reconstructed = U @ Sigma @ Vt

print("재구성 오차:", np.linalg.norm(A - A_reconstructed))

이미지 압축 시뮬레이션

def svd_compress(matrix, k):

"""상위 k개 특이값으로 행렬 압축"""

U, S, Vt = np.linalg.svd(matrix)

k개 성분만 사용

U_k = U[:, :k]

S_k = S[:k]

Vt_k = Vt[:k, :]

return U_k @ np.diag(S_k) @ Vt_k

테스트 행렬 (이미지 시뮬레이션)

np.random.seed(42)

img = np.random.randn(100, 100)

다양한 압축 비율 테스트

for k in [5, 10, 20, 50]:

compressed = svd_compress(img, k)

error = np.linalg.norm(img - compressed, 'fro') / np.linalg.norm(img, 'fro')

compression_ratio = k * (100 + 100 + 1) / (100 * 100)

print(f"k={k}: 상대 오차={error:.4f}, 압축비={compression_ratio:.4f}")

PCA 구현 (SVD 활용)

def pca_svd(X, n_components):

"""SVD를 이용한 PCA"""

데이터 중심화

X_centered = X - X.mean(axis=0)

SVD 수행

U, S, Vt = np.linalg.svd(X_centered, full_matrices=False)

주성분으로 투영

X_pca = X_centered @ Vt[:n_components].T

설명 분산 비율

explained_variance_ratio = (S[:n_components]**2) / (S**2).sum()

return X_pca, Vt[:n_components], explained_variance_ratio

예시 데이터

np.random.seed(42)

X = np.random.randn(100, 5)

X_pca, components, evr = pca_svd(X, 2)

print("\nPCA 설명 분산 비율:", evr)

print("투영된 데이터 형태:", X_pca.shape)

10. 행렬 분해 (Matrix Factorizations)

주요 분해 방법 비교

| 분해 | 형태 | 적용 조건 | 주요 용도 |

| ----------- | ------------ | ----------- | ------------------ |

| LU 분해 | A = LU | 정방행렬 | 연립방정식 |

| QR 분해 | A = QR | 임의 행렬 | 최소제곱법, 고유값 |

| Cholesky | A = LL^T | 양정치 대칭 | 수치 최적화 |

| SVD | A = UΣV^T | 임의 행렬 | PCA, 이미지 압축 |

| 고유값 분해 | A = PΛP^{-1} | 정방행렬 | 스펙트럼 분석 |

Cholesky 분해

양정치(positive definite) 대칭 행렬에 대해: $A = LL^T$

from scipy import linalg

QR 분해

A = np.array([[1, 2, 3],

[4, 5, 6],

[7, 8, 10]], dtype=float)

Q, R = np.linalg.qr(A)

print("Q (직교 행렬):\n", Q)

print("R (상삼각 행렬):\n", R)

print("Q^T @ Q = I 확인:\n", np.round(Q.T @ Q))

Cholesky 분해

B = np.array([[4, 2, 2],

[2, 3, 1],

[2, 1, 3]], dtype=float)

L = np.linalg.cholesky(B)

print("\nCholesky L:\n", L)

print("L @ L^T = A 확인:\n", L @ L.T)

LU 분해 (scipy)

P, L_lu, U = linalg.lu(A)

print("\nLU 분해:")

print("P:\n", P)

print("L:\n", L_lu)

print("U:\n", U)

print("P @ L @ U = A 확인:\n", np.round(P @ L_lu @ U))

11. AI/ML에서의 선형대수학

신경망에서의 행렬 곱

신경망의 순전파(forward propagation)는 연속된 행렬 곱으로 표현됩니다:

$$\mathbf{h}^{(l)} = f(W^{(l)} \mathbf{h}^{(l-1)} + \mathbf{b}^{(l)})$$

- $W^{(l)}$: 가중치 행렬

- $\mathbf{b}^{(l)}$: 편향 벡터

- $f$: 활성화 함수

배치 처리 시 입력을 행렬 $X \in \mathbb{R}^{n \times d}$로 표현하여 병렬 계산이 가능합니다.

그래디언트와 야코비안

손실 함수 $L$에 대한 가중치 행렬의 그래디언트:

$$\frac{\partial L}{\partial W} = \frac{\partial L}{\partial \mathbf{h}} \mathbf{x}^T$$

PCA로 차원 축소

고차원 특성 공간에서 주요 분산 방향을 찾아 차원을 축소합니다.

Transformer의 Attention 메커니즘

Self-Attention은 세 행렬 $Q, K, V$ (Query, Key, Value)를 이용합니다:

$$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right) V$$

from sklearn.decomposition import PCA

from sklearn.datasets import load_iris

1. 간단한 신경망 순전파

np.random.seed(42)

n_samples, input_dim, hidden_dim, output_dim = 32, 784, 128, 10

X = np.random.randn(n_samples, input_dim)

W1 = np.random.randn(input_dim, hidden_dim) * 0.01

b1 = np.zeros(hidden_dim)

W2 = np.random.randn(hidden_dim, output_dim) * 0.01

b2 = np.zeros(output_dim)

순전파

H1 = np.maximum(0, X @ W1 + b1) # ReLU 활성화

logits = H1 @ W2 + b2

print("logits 형태:", logits.shape) # (32, 10)

2. PCA로 차원 축소

iris = load_iris()

X_iris = iris.data # (150, 4)

pca = PCA(n_components=2)

X_pca = pca.fit_transform(X_iris)

print("\nPCA 후 형태:", X_pca.shape) # (150, 2)

print("설명 분산 비율:", pca.explained_variance_ratio_)

print("누적 설명 분산:", pca.explained_variance_ratio_.sum())

3. Scaled Dot-Product Attention

def scaled_dot_product_attention(Q, K, V):

d_k = Q.shape[-1]

Q @ K^T / sqrt(d_k)

scores = Q @ K.T / np.sqrt(d_k)

Softmax

exp_scores = np.exp(scores - scores.max(axis=-1, keepdims=True))

attention_weights = exp_scores / exp_scores.sum(axis=-1, keepdims=True)

가중 합

output = attention_weights @ V

return output, attention_weights

예시

seq_len, d_model = 5, 8

Q = np.random.randn(seq_len, d_model)

K = np.random.randn(seq_len, d_model)

V = np.random.randn(seq_len, d_model)

output, weights = scaled_dot_product_attention(Q, K, V)

print("\nAttention 출력 형태:", output.shape) # (5, 8)

print("Attention 가중치 합:", weights.sum(axis=-1)) # 모두 1

4. Word Embedding 유사도

word_embeddings = {

"king": np.array([0.8, 0.3, 0.1, 0.9]),

"queen": np.array([0.7, 0.4, 0.2, 0.8]),

"man": np.array([0.6, 0.1, 0.7, 0.2]),

"woman": np.array([0.5, 0.2, 0.8, 0.1])

}

def cosine_similarity(a, b):

return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

print("\n단어 유사도:")

print("king-queen:", cosine_similarity(

word_embeddings["king"], word_embeddings["queen"]))

print("man-woman:", cosine_similarity(

word_embeddings["man"], word_embeddings["woman"]))

king - man + woman ≈ queen (analogy)

analogy = word_embeddings["king"] - word_embeddings["man"] + word_embeddings["woman"]

sim_queen = cosine_similarity(analogy, word_embeddings["queen"])

print("king - man + woman vs queen 유사도:", sim_queen)

12. 퀴즈

**정답**: 두 벡터의 내적이 0이면 두 벡터는 서로 직교(orthogonal)합니다.

**설명**: 내적 공식 $\mathbf{u} \cdot \mathbf{v} = \|\mathbf{u}\|\|\mathbf{v}\|\cos\theta$에서 $\cos 90° = 0$이므로, 두 벡터가 직각을 이룰 때 내적이 0이 됩니다. 이를 이용해 정규직교기저를 구성하거나 직교 투영을 계산합니다.

**정답**: 정방행렬의 행렬식(determinant)이 0이 아닐 때 역행렬이 존재합니다.

**설명**: $\det(A) \neq 0$이면 $A$는 가역행렬(invertible matrix)입니다. 행렬식이 0인 행렬은 특이행렬(singular matrix)이라 하며 역행렬이 존재하지 않습니다. 이는 행렬의 랭크(rank)가 행렬의 크기보다 작음을 의미하고, 해당 선형 연립방정식은 유일한 해를 가지지 않습니다.

**정답**: 고유벡터는 선형 변환에 의해 방향이 바뀌지 않는 벡터이고, 고유값은 그 벡터가 몇 배로 늘어나는지를 나타냅니다.

**설명**: $A\mathbf{v} = \lambda\mathbf{v}$에서 행렬 $A$로 변환해도 벡터 $\mathbf{v}$의 방향은 유지되고 크기만 $\lambda$배 변합니다. $\lambda > 1$이면 늘어남, $0 < \lambda < 1$이면 줄어듦, $\lambda < 0$이면 방향이 반전됩니다. 고유값 분해는 PCA, 행렬 거듭제곱 등에 활용됩니다.

**정답**: 중심화된 데이터 행렬에 SVD를 적용하면 PCA와 동일한 결과를 얻습니다. SVD의 우특이벡터(V)가 PCA의 주성분(principal components)에 해당합니다.

**설명**: 데이터 행렬 $X$를 중심화한 후 SVD를 수행하면 $X = U\Sigma V^T$가 됩니다. 이때 $V$의 열벡터들이 주성분이며, 특이값 $\sigma_i$의 제곱에 비례하는 분산을 설명합니다. 계산적으로 공분산 행렬의 고유값 분해보다 수치적으로 안정적이므로 sklearn의 PCA도 내부적으로 SVD를 사용합니다.

**정답**: $m \times n$ 행렬 $A$에 대해 $\text{rank}(A) + \text{nullity}(A) = n$이 성립합니다.

**설명**: rank(A)는 열공간(column space)의 차원이고, nullity(A)는 영공간(null space)의 차원입니다. 열의 수 $n$은 항상 이 두 차원의 합과 같습니다. 예를 들어 3×4 행렬의 랭크가 3이라면 영공간의 차원은 1입니다. 이 정리는 연립방정식의 해의 구조를 이해하는 데 핵심적입니다.

정리 및 학습 로드맵

선형대수학의 핵심 개념들은 서로 긴밀하게 연결되어 있습니다:

벡터 → 행렬 → 행렬식 → 연립방정식

↓ ↓

벡터 공간 → 선형 변환 → 내적 공간

고유값/고유벡터 → SVD → AI/ML 응용

**다음 단계로 추천하는 학습 자료:**

1. **Gilbert Strang의 Linear Algebra (MIT OCW 18.06)**: 가장 체계적인 선형대수학 강의

2. **3Blue1Brown - Essence of Linear Algebra**: 시각적 직관 이해에 탁월

3. **NumPy 공식 문서**: 선형대수 모듈 (`numpy.linalg`) 실습

4. **scikit-learn PCA 문서**: 실제 ML 파이프라인에서의 활용

5. **Matrix Methods in Data Analysis (MIT 18.065)**: 고급 응용 과정

선형대수학은 단순히 수식을 외우는 것이 아니라 행렬과 벡터의 기하학적 의미를 이해하는 것이 핵심입니다. 꾸준한 코드 실습과 시각화를 통해 깊은 이해를 쌓아가시길 바랍니다.

현재 단락 (1/466)

선형대수학(Linear Algebra)은 현대 수학과 공학의 핵심 기초입니다. 데이터 과학, 머신러닝, 컴퓨터 그래픽스, 양자역학에 이르기까지 거의 모든 분야에서 활용됩니다. 이 ...

작성 글자: 0원문 글자: 18,236작성 단락: 0/466