はじめに
線形代数学(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)
ベクトルの定義と表現
ベクトルは**大きさ(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}$$
**内積(ドット積):**
$$\mathbf{u} \cdot \mathbf{v} = \sum_{i=1}^{n} u_i v_i = \|\mathbf{u}\| \|\mathbf{v}\| \cos\theta$$
内積が $0$ のとき、2つのベクトルは**直交(orthogonal)**しています。
**外積(クロス積)** — 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}$
**単位ベクトル(正規化):**
$$\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
2ベクトルのなす角
cos_theta = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
angle = np.degrees(np.arccos(np.clip(cos_theta, -1, 1)))
print("なす角 (度):", angle)
直交確認
v1, v2 = np.array([1, 0]), np.array([0, 1])
print("直交確認:", np.dot(v1, v2)) # 0
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) | $AI = IA = A$ |
| 対角行列 (Diagonal) | 対角以外はすべて0 | — |
| 対称行列 (Symmetric) | $A = A^T$ | 実数固有値を持つ |
| 直交行列 (Orthogonal) | $A^T A = I$ | $\det(A) = \pm 1$ |
行列演算
**行列の積(Matrix Multiplication):**
$(AB)_{ij} = \sum_{k=1}^{n} a_{ik} b_{kj}$
行列の積は一般に交換法則が成立しません:$AB \neq BA$
**転置行列(Transpose):** $(A^T)_{ij} = A_{ji}$
**逆行列(Inverse):** $A^{-1}$ は $AA^{-1} = A^{-1}A = I$ を満たします。$\det(A) \neq 0$ のときのみ存在します。
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
行列の和
print("A + B:\n", A + B)
行列の積
C = A @ B
print("A @ B:\n", C)
アダマール積(要素ごとの積)
print("A * B (Hadamard):\n", A * B)
転置行列
print("転置 A^T:\n", A.T)
逆行列
A_inv = np.linalg.inv(A)
print("逆行列 A^(-1):\n", A_inv)
print("A @ A^(-1) = I:\n", np.round(A @ A_inv))
単位行列
print("3x3単位行列:\n", np.eye(3))
対角行列
print("対角行列:\n", np.diag([1, 2, 3]))
対称行列の確認
S = A + A.T
print("対称行列か:", np.allclose(S, S.T))
フロベニウスノルム
print("フロベニウスノルム:", np.linalg.norm(A, 'fro'))
3. 行列式 (Determinant)
行列式の定義と計算
行列式(determinant)は正方行列に定義されるスカラー値で、行列の重要な性質をエンコードします。
**2×2行列式:**
$$\det\begin{pmatrix} a & b \\ c & d \end{pmatrix} = ad - bc$$
**3×3行列式(余因子展開):**
$$\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})$$
行列式の幾何学的意味
行列式の絶対値は、行ベクトルが作る**平行六面体の体積**に等しくなります。行列式が0の場合、行列は空間を「潰す」変換を表します。
行列式の重要な性質
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. $\det(A) = 0 \iff$ 行列は特異(逆行列なし)
クラメルの公式 (Cramer's Rule)
$A\mathbf{x} = \mathbf{b}$ に対して:
$$x_i = \frac{\det(A_i)}{\det(A)}$$
2x2行列式
A2 = np.array([[3, 1], [2, 4]])
print("det([[3,1],[2,4]]):", np.linalg.det(A2)) # 10.0
3x3行列式
A3 = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 10]])
print("det(A3):", np.linalg.det(A3)) # -3.0
性質の検証
B = np.array([[2, 0], [0, 3]])
print("det(A2)*det(B):", np.linalg.det(A2) * np.linalg.det(B))
print("det(A2@B):", np.linalg.det(A2 @ B))
特異行列
singular = np.array([[1, 2], [2, 4]]) # 2行目 = 2倍の1行目
print("特異行列 det:", np.linalg.det(singular)) # 約0
クラメルの公式
def cramers_rule(A, b):
det_A = np.linalg.det(A)
n = len(b)
x = np.zeros(n)
for i in range(n):
Ai = A.copy().astype(float)
Ai[:, i] = b
x[i] = np.linalg.det(Ai) / det_A
return x
A_sys = np.array([[2, 1], [5, 3]], dtype=float)
b_sys = np.array([1, 2])
print("\nクラメルの公式:", cramers_rule(A_sys, b_sys))
print("numpy solve:", np.linalg.solve(A_sys, b_sys))
4. 連立方程式とガウス消去法
線形連立方程式
$n$ 個の未知数に対する $m$ 個の線形方程式:$A\mathbf{x} = \mathbf{b}$
解の種類:
- **不能**(解なし):方程式系が矛盾する場合
- **唯一解**:$\det(A) \neq 0$ の場合
- **不定**(無限に多くの解):方程式が冗長な場合
ガウス消去法 (Gaussian Elimination)
行の基本変換で**行階段形(Row Echelon Form)**を作ります:
1. 行の交換:$R_i \leftrightarrow R_j$
2. 行のスカラー倍:$R_i \leftarrow c \cdot R_i$
3. 行の和:$R_i \leftarrow R_i + c \cdot R_j$
**ガウス-ジョルダン消去法**では**簡約行階段形(RREF)**まで変換します。
LU分解
$A = LU$:下三角行列 $L$ と上三角行列 $U$ の積への分解。
from scipy import linalg
連立方程式の解法
A = np.array([[2, 1, -1],
[-3, -1, 2],
[-2, 1, 2]], dtype=float)
b = np.array([8, -11, -3], dtype=float)
numpy solve
x = np.linalg.solve(A, b)
print("解:", x) # [2. 3. -1.]
print("検証 A@x:", A @ x)
ガウス-ジョルダン消去法の実装
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]
x_gj = gauss_jordan(A.copy(), b.copy())
print("ガウス-ジョルダン解:", x_gj)
LU分解
P, L, U = linalg.lu(A)
print("\nL行列:\n", np.round(L, 4))
print("U行列:\n", np.round(U, 4))
LU分解で連立方程式を解く
y = linalg.solve_triangular(L, P.T @ b, lower=True)
x_lu = linalg.solve_triangular(U, y)
print("LU分解解:", x_lu)
5. ベクトル空間 (Vector Spaces)
ベクトル空間の定義
集合 $V$ がベクトル空間であるためには、加算とスカラー倍に関する8つの公理を満たす必要があります:
1. 加算の閉包性:$\mathbf{u} + \mathbf{v} \in V$
2. 加算の交換法則:$\mathbf{u} + \mathbf{v} = \mathbf{v} + \mathbf{u}$
3. 加算の結合法則
4. 零ベクトルの存在
5. 加法逆元の存在
6. スカラー倍の閉包性:$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, \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$$
基底 (Basis) と次元 (Dimension)
**基底**とはベクトル空間を張る(span)線形独立なベクトルの集合です。基底の元の個数が**次元**です。
4つの基本部分空間
行列 $A \in \mathbb{R}^{m \times n}$, $r = \text{rank}(A)$ に対して:
| 部分空間 | 所属 | 次元 |
| ---------------------- | -------------- | ----- |
| 行空間(Row space) | $\mathbb{R}^n$ | $r$ |
| 列空間(Column space) | $\mathbb{R}^m$ | $r$ |
| 零空間(Null space) | $\mathbb{R}^n$ | $n-r$ |
| 左零空間 | $\mathbb{R}^m$ | $m-r$ |
**ランク・零化域定理:** $\text{rank}(A) + \text{nullity}(A) = n$
from scipy.linalg import null_space
A = np.array([[1, 2, 3],
[4, 5, 6],
[2, 4, 6]]) # 3行目は1行目の2倍(線形従属)
print("ランク:", np.linalg.matrix_rank(A)) # 2
零空間の基底
ns = null_space(A)
print("零空間の次元:", ns.shape[1]) # 1
print("零空間:\n", ns)
検証:A @ ns ≈ 0
print("A @ 零空間:\n", np.round(A @ ns, 10))
線形独立性の確認
v1 = np.array([1, 0, 0])
v2 = np.array([0, 1, 0])
v3 = np.array([1, 1, 0]) # v1 + v2 → 線形従属
M = np.column_stack([v1, v2, v3])
print("\n[v1, v2, v3]のランク:", np.linalg.matrix_rank(M)) # 2(従属)
v3_ind = np.array([0, 0, 1])
M2 = np.column_stack([v1, v2, v3_ind])
print("[v1, v2, v3_ind]のランク:", np.linalg.matrix_rank(M2)) # 3(独立)
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})$(斉次性)
核と像
**核(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}$$
**せん断変換(Shear):**
$$H = \begin{pmatrix} 1 & k \\ 0 & 1 \end{pmatrix}$$
回転行列
def rotation_2d(deg):
theta = np.radians(deg)
return np.array([[np.cos(theta), -np.sin(theta)],
[np.sin(theta), np.cos(theta)]])
R90 = rotation_2d(90)
v = np.array([1.0, 0.0])
print("90度回転:", R90 @ v) # [0, 1]
R45 = rotation_2d(45)
print("45度回転:", np.round(R45 @ v, 4)) # [0.7071, 0.7071]
変換の合成(回転してから反射)
Fx = np.array([[1, 0], [0, -1]])
T = Fx @ R90
print("回転+反射:", T @ v) # [0, -1]
直交行列の性質
print("R^T @ R = I:", np.allclose(R90.T @ R90, np.eye(2)))
print("det(R) =", np.linalg.det(R90)) # 1.0
射影行列
u = np.array([1, 1]) / np.sqrt(2) # y=x方向の単位ベクトル
P = np.outer(u, u)
w = np.array([3, 1])
print("y=xへの射影:", P @ w) # [2, 2]
7. 内積空間 (Inner Product Spaces)
内積の定義
**内積** $\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)
線形独立なベクトル $\mathbf{v}_1, \ldots, \mathbf{v}_k$ から正規直交基底を構築します:
$$\mathbf{u}_j = \mathbf{v}_j - \sum_{i=1}^{j-1} \frac{\langle \mathbf{v}_j, \mathbf{u}_i \rangle}{\langle \mathbf{u}_i, \mathbf{u}_i \rangle} \mathbf{u}_i, \quad \mathbf{e}_j = \frac{\mathbf{u}_j}{\|\mathbf{u}_j\|}$$
これはQR分解の基礎となります。
def gram_schmidt(vectors):
"""グラム-シュミット正規直交化"""
basis = []
for v in vectors:
w = np.array(v, dtype=float)
for b in basis:
w -= np.dot(w, 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])
Q = gram_schmidt([v1, v2, v3])
print("正規直交基底:")
for i, q in enumerate(Q):
print(f" e{i+1} = {np.round(q, 4)}")
直交性確認
print("\n直交性確認 (Q @ Q^T = I):\n", np.round(Q @ Q.T))
QR分解(グラム-シュミットと等価)
A = np.column_stack([v1, v2, v3])
Q_qr, R = np.linalg.qr(A)
print("QR分解Q:\n", np.round(Q_qr, 4))
print("R(上三角):\n", np.round(R, 4))
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$$
この多項式(特性多項式)の根が固有値です。
**例:**
$$A = \begin{pmatrix} 4 & 1 \\ 2 & 3 \end{pmatrix} \implies \lambda^2 - 7\lambda + 10 = (\lambda-2)(\lambda-5) = 0$$
固有値:$\lambda_1 = 2$, $\lambda_2 = 5$
固有値分解(対角化)
対角化可能な行列 $A$ は:
$$A = P \Lambda P^{-1}$$
ここで $\Lambda = \text{diag}(\lambda_1, \ldots, \lambda_n)$、$P$ の列は固有ベクトル。
**応用:** $A^k = P\Lambda^k P^{-1}$ — 行列の累乗が簡単に計算可能!
対称行列のスペクトル定理
実対称行列 $A = A^T$ は:
1. すべての固有値が**実数**
2. 異なる固有値の固有ベクトルは**直交**
3. $A = Q\Lambda Q^T$(直交対角化可能)
from scipy.linalg import eigh
固有値分解
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:.1f}:")
print(" A @ v:", np.round(A @ v, 4))
print(" lambda * v:", np.round(lam * v, 4))
print(" 一致:", np.allclose(A @ v, lam * v))
対角化: A = P @ Lambda @ P_inv
P = eigenvectors
Lambda = np.diag(eigenvalues)
A_diag = P @ Lambda @ np.linalg.inv(P)
print("\n対角化から再構成:\n", np.round(A_diag.real))
行列の累乗: A^10
k = 10
A_k = P @ np.diag(eigenvalues**k) @ np.linalg.inv(P)
print("A^10 (固有値分解):\n", np.round(A_k.real))
print("A^10 (直接計算):\n", np.linalg.matrix_power(A, k))
対称行列(eigh使用)
B = np.array([[4, 2, 0],
[2, 3, 1],
[0, 1, 5]])
evals, evecs = eigh(B)
print("\n対称行列の固有値:", evals)
print("直交性 Q^T @ Q = I:\n", np.round(evecs.T @ evecs))
トレース = 固有値の和、行列式 = 固有値の積
print("\ntrace(A) =", np.trace(A), "= 固有値の和:", np.sum(eigenvalues).real)
print("det(A) =", np.linalg.det(A), "= 固有値の積:", np.prod(eigenvalues).real)
9. 特異値分解 (SVD - Singular Value Decomposition)
SVDの定義
任意の $m \times n$ 実行列 $A$ は次のように分解されます:
$$A = U \Sigma V^T$$
- $U \in \mathbb{R}^{m \times m}$:**左特異ベクトル**($AA^T$ の固有ベクトル)
- $\Sigma \in \mathbb{R}^{m \times n}$:特異値の対角行列 $\sigma_1 \geq \sigma_2 \geq \cdots \geq 0$
- $V \in \mathbb{R}^{n \times n}$:**右特異ベクトル**($A^TA$ の固有ベクトル)
幾何学的意味
$A = U\Sigma V^T$ は3段階の変換の合成:
1. $V^T$:回転/反射
2. $\Sigma$:座標軸方向の伸縮
3. $U$:回転/反射
低ランク近似
最良ランク $k$ 近似:
$$A_k = \sum_{i=1}^{k} \sigma_i \mathbf{u}_i \mathbf{v}_i^T$$
これが**画像圧縮**や**協調フィルタリング**の基礎です。
PCAとの関係
平均中心化されたデータ行列 $X$ に SVD を適用すると、右特異ベクトル $V$ が**主成分(principal components)**になります。
from sklearn.decomposition import PCA
from sklearn.datasets import load_iris
基本SVD
A = np.array([[3, 2, 2],
[2, 3, -2]], dtype=float)
U, S, Vt = np.linalg.svd(A)
print("U:\n", np.round(U, 4))
print("特異値:", np.round(S, 4))
print("Vt:\n", np.round(Vt, 4))
再構成
m, n = A.shape
Sigma = np.zeros((m, n))
Sigma[:min(m,n), :min(m,n)] = np.diag(S)
A_rec = U @ Sigma @ Vt
print("再構成誤差:", np.linalg.norm(A - A_rec))
低ランク近似
def low_rank_approx(A, k):
U, S, Vt = np.linalg.svd(A, full_matrices=False)
return U[:, :k] @ np.diag(S[:k]) @ Vt[:k, :]
np.random.seed(42)
M = np.random.randn(50, 50)
print("\n低ランク近似(50x50行列):")
for k in [1, 5, 10, 25]:
M_k = low_rank_approx(M, k)
rel_err = np.linalg.norm(M - M_k, 'fro') / np.linalg.norm(M, 'fro')
storage = k * (50 + 50 + 1) / (50 * 50)
print(f" k={k:2d}: 相対誤差={rel_err:.4f}, 圧縮比={storage:.3f}")
SVDを使ったPCA
iris = load_iris()
X = iris.data - iris.data.mean(axis=0) # 中心化
U_d, S_d, Vt_d = np.linalg.svd(X, full_matrices=False)
explained_ratio = S_d**2 / (S_d**2).sum()
print("\nIrisデータセット SVD-PCA:")
print("説明分散比(上位4つ):", np.round(explained_ratio, 4))
sklearn PCAとの比較
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)
print("sklearn PCA説明分散比:", np.round(pca.explained_variance_ratio_, 4))
print("累積寄与率:", round(pca.explained_variance_ratio_.sum(), 4))
10. 行列分解 (Matrix Factorizations)
主要な行列分解の比較
| 分解 | 形式 | 条件 | 主な用途 |
| ---------- | --------------------- | ---------- | -------------- |
| LU分解 | $A = LU$ | 正方行列 | 連立方程式 |
| QR分解 | $A = QR$ | 任意行列 | 最小二乗法 |
| Cholesky | $A = LL^T$ | 対称正定値 | 最適化 |
| SVD | $A = U\Sigma V^T$ | 任意行列 | PCA、圧縮 |
| 固有値分解 | $A = P\Lambda P^{-1}$ | 対角化可能 | スペクトル解析 |
擬似逆行列(Moore-Penrose pseudo-inverse)
$A = U\Sigma V^T$ のとき:
$$A^+ = V\Sigma^+ U^T$$
ここで $\Sigma^+$ は $\Sigma$ の非零対角要素を逆数に置き換えたものです。
from scipy import linalg
np.random.seed(42)
QR分解と最小二乗法
過決定系(方程式 > 未知数)
A = np.random.randn(10, 3)
x_true = np.array([1.0, 2.0, 3.0])
b = A @ x_true + 0.05 * np.random.randn(10)
Q, R = np.linalg.qr(A)
x_qr = np.linalg.solve(R, Q.T @ b)
print("QR最小二乗解:", np.round(x_qr, 4))
print("真の値:", x_true)
numpy lstsq
x_lstsq, _, _, _ = np.linalg.lstsq(A, b, rcond=None)
print("lstsq解:", np.round(x_lstsq, 4))
Cholesky分解
B = np.array([[6, 3, 2],
[3, 5, 1],
[2, 1, 4]], dtype=float)
L = np.linalg.cholesky(B)
print("\nCholesky L:\n", np.round(L, 4))
print("L @ L^T = B:", np.allclose(L @ L.T, B))
擬似逆行列
A_rect = np.array([[1, 2], [3, 4], [5, 6]], dtype=float)
A_pinv = np.linalg.pinv(A_rect)
print("\n擬似逆行列:\n", np.round(A_pinv, 4))
print("A_pinv @ A = I_2:\n", np.round(A_pinv @ A_rect))
条件数(最大/最小特異値の比)
cond = np.linalg.cond(B)
print("\n条件数:", round(cond, 4))
print("数値的に安定?(条件数 < 1000):", cond < 1000)
11. AI/MLにおける線形代数
ニューラルネットワークと行列積
ニューラルネットワークの順伝播(forward pass)は連続する行列積で表現されます:
$$\mathbf{h}^{(l)} = f\!\left(W^{(l)} \mathbf{h}^{(l-1)} + \mathbf{b}^{(l)}\right)$$
バッチ処理では $n$ サンプルを行列 $X \in \mathbb{R}^{n \times d}$ として並列計算します。
Transformerのアテンション機構
スケールドドット積アテンション:
$$\text{Attention}(Q, K, V) = \text{softmax}\!\left(\frac{QK^T}{\sqrt{d_k}}\right) V$$
$Q$(クエリ)、$K$(キー)、$V$(バリュー)の3行列を使った純粋な線形代数演算です。
次元削減とPCA
PCAは共分散行列 $C = \frac{1}{n}X^TX$ の固有値分解によって分散が最大の方向を見つけます。
from sklearn.decomposition import PCA
from sklearn.datasets import load_iris
1. ニューラルネットワーク順伝播
np.random.seed(42)
batch_size, d_in, d_hidden, d_out = 32, 784, 128, 10
X_batch = np.random.randn(batch_size, d_in)
W1 = np.random.randn(d_in, d_hidden) * np.sqrt(2.0 / d_in)
b1 = np.zeros(d_hidden)
W2 = np.random.randn(d_hidden, d_out) * np.sqrt(2.0 / d_hidden)
b2 = np.zeros(d_out)
def relu(x): return np.maximum(0, x)
def softmax(x):
ex = np.exp(x - x.max(axis=1, keepdims=True))
return ex / ex.sum(axis=1, keepdims=True)
H1 = relu(X_batch @ W1 + b1)
logits = H1 @ W2 + b2
probs = softmax(logits)
print("順伝播形状:", X_batch.shape, "->", H1.shape, "->", probs.shape)
print("確率の和:", np.allclose(probs.sum(axis=1), 1))
2. スケールドドット積アテンション
def attention(Q, K, V):
d_k = Q.shape[-1]
scores = Q @ K.T / np.sqrt(d_k)
exp_s = np.exp(scores - scores.max(axis=-1, keepdims=True))
weights = exp_s / exp_s.sum(axis=-1, keepdims=True)
return weights @ V, weights
seq_len, d_model = 6, 32
Q = np.random.randn(seq_len, d_model)
K = np.random.randn(seq_len, d_model)
V = np.random.randn(seq_len, d_model)
out, w = attention(Q, K, V)
print("\nアテンション出力:", out.shape)
print("アテンション重みの和:", np.round(w.sum(axis=-1), 4))
3. PCAによる次元削減
iris = load_iris()
X_iris = iris.data
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_iris)
print("\nIrisデータPCA:")
print("元の次元:", X_iris.shape, "-> 削減後:", X_pca.shape)
print("説明分散比:", np.round(pca.explained_variance_ratio_, 4))
print("累積寄与率:", round(sum(pca.explained_variance_ratio_), 4))
4. 単語埋め込みの類似度
embeddings = {
"king": np.array([0.9, 0.1, 0.8, 0.2]),
"queen": np.array([0.8, 0.2, 0.7, 0.9]),
"man": np.array([0.8, 0.1, 0.2, 0.1]),
"woman": np.array([0.7, 0.2, 0.1, 0.8]),
}
def cos_sim(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
analogy = embeddings["king"] - embeddings["man"] + embeddings["woman"]
print("\n単語アナロジー(king - man + woman):")
for word, emb in embeddings.items():
print(f" {word}との類似度: {cos_sim(analogy, emb):.4f}")
12. クイズ
**答え**:2つのベクトルの内積が $0$ のとき、それらは直交します。
**説明**:内積の公式 $\mathbf{u} \cdot \mathbf{v} = \|\mathbf{u}\|\|\mathbf{v}\|\cos\theta$ より、$\theta = 90°$ のとき $\cos 90° = 0$ となるため内積が $0$ になります。正規直交基底の構築や射影の計算に活用されます。
**答え**:正方行列の行列式が $0$ でない($\det(A) \neq 0$)とき、逆行列が存在します。
**説明**:行列式が $0$ の行列は**特異行列(singular matrix)**と呼ばれ、逆行列は存在しません。これはランク(rank)が行列のサイズより小さいことを意味し、対応する連立方程式は唯一解を持ちません。
**答え**:固有ベクトルは線形変換によって方向が変わらないベクトル、固有値はそのベクトルが何倍に伸縮されるかを示します。
**説明**:$A\mathbf{v} = \lambda\mathbf{v}$ において、行列 $A$ による変換を施しても $\mathbf{v}$ の向きは保たれ、大きさだけが $\lambda$ 倍になります。$\lambda > 1$ なら伸び、$0 < \lambda < 1$ なら縮み、$\lambda < 0$ なら反転します。
**答え**:平均中心化されたデータ行列にSVDを適用すると、PCAと同じ結果が得られます。SVDの右特異ベクトル $V$ がPCAの主成分に対応します。
**説明**:データ行列 $X$ を中心化して SVD を行うと $X = U\Sigma V^T$ が得られます。$V$ の列ベクトルが主成分であり、特異値の二乗が分散の大きさに比例します。数値的安定性のため、scikit-learnの `PCA` も内部でSVDを使用しています。
**答え**:$m \times n$ 行列 $A$ に対して $\text{rank}(A) + \text{nullity}(A) = n$ が成立します。
**説明**:rank は列空間(column space)の次元、nullity は零空間(null space)の次元です。列の数 $n$ は常にこれら2つの次元の和に等しくなります。例えば、$3 \times 4$ 行列のランクが $3$ なら零空間の次元は $1$ です。この定理は連立方程式の解の構造理解に不可欠です。
まとめと学習ロードマップ
線形代数の主要概念は密接に関連しています:
ベクトル -> 行列 -> 行列式 -> 連立方程式
| |
ベクトル空間 -> 線形変換 -> 内積空間
|
固有値・固有ベクトル -> SVD -> AI/ML応用
**次のステップとして推奨する学習リソース:**
1. **Gilbert Strang, MIT OCW 18.06**:線形代数の最も体系的な講義。問題集も充実しています
2. **3Blue1Brown - Essence of Linear Algebra**:幾何学的直観を養うのに最適な動画シリーズ
3. **NumPy公式ドキュメント**:`numpy.linalg` モジュールで全概念を実践
4. **scikit-learn PCAドキュメント**:実際のMLパイプラインでの活用方法
5. **MIT 18.065(Matrix Methods in Data Analysis)**:データ科学・深層学習への応用
線形代数をマスターする秘訣は**幾何学的直観**を育てることです。行列演算が空間に対して何をしているのかを常に視覚化し、NumPyによる実装と組み合わせることで深い理解が得られます。
현재 단락 (1/452)
線形代数学(Linear Algebra)は現代数学と工学の根幹をなす分野です。データサイエンス、機械学習、コンピュータグラフィックス、量子力学まで、ほぼすべての技術分野で活用されています。本ガイドで...