部分空間とは?機械学習に必要な線形代数の基礎を解説

主成分分析(PCA)や特異値分解(SVD)など、機械学習の多くの手法では線形代数が基盤となっています。その中でも「部分空間」は、次元削減やデータの射影を理解するうえで避けて通れない概念です。

本記事では、部分空間の定義と性質を丁寧に解説し、Pythonでの具体的な確認を通じて直感的な理解を目指します。

本記事の内容

  • 部分空間の定義と3つの条件
  • 部分空間の具体例と非例
  • 列空間・零空間と機械学習への応用
  • Pythonによる部分空間の可視化

前提知識

この記事を読む前に、以下の概念を押さえておくと理解が深まります。

  • ベクトルの加法とスカラー倍
  • 行列とベクトルの積
  • 線形独立と基底の概念

部分空間とは

部分空間(subspace)とは、ある大きなベクトル空間の「一部」であり、ベクトル空間としての性質を保っている集合のことです。

イメージとしては、3次元空間 $\mathbb{R}^3$ の中にある原点を通る平面や直線が部分空間です。原点を通らない平面は部分空間にはなりません。

部分空間の数学的定義

$d$ 次元のベクトル空間 $V$ に対し、部分集合 $W \subseteq V$ が 部分空間 であるとは、以下の3つの条件を全て満たすことを言います。

$$ \text{(1) 零ベクトルを含む:} \quad \bm{0} \in W $$

$$ \text{(2) 加法について閉じている:} \quad \bm{u}, \bm{v} \in W \Rightarrow \bm{u} + \bm{v} \in W $$

$$ \text{(3) スカラー倍について閉じている:} \quad \bm{u} \in W, \; c \in \mathbb{R} \Rightarrow c\bm{u} \in W $$

条件(2)と(3)をまとめて「線形結合について閉じている」と表現することもできます。すなわち、任意の $\bm{u}, \bm{v} \in W$ と任意のスカラー $\alpha, \beta \in \mathbb{R}$ に対して、

$$ \alpha \bm{u} + \beta \bm{v} \in W $$

が成り立つことと同値です。

具体例で理解する部分空間

例1: $\mathbb{R}^2$ における原点を通る直線

ベクトル $\bm{a} = (1, 2)^T$ のスカラー倍全体の集合を考えます。

$$ W_1 = \{ t \bm{a} \mid t \in \mathbb{R} \} = \{ (t, 2t)^T \mid t \in \mathbb{R} \} $$

これは $\mathbb{R}^2$ の部分空間です。確認してみましょう。

  • $t=0$ とすると $\bm{0} \in W_1$ (条件1)
  • $t_1 \bm{a} + t_2 \bm{a} = (t_1 + t_2)\bm{a} \in W_1$ (条件2)
  • $c(t \bm{a}) = (ct)\bm{a} \in W_1$ (条件3)

例2: 部分空間でない例

$\mathbb{R}^2$ における集合 $S = \{ (x, y)^T \mid x + y = 1 \}$ は部分空間ではありません。

$\bm{0} = (0, 0)^T$ について $0 + 0 = 0 \neq 1$ なので、零ベクトルが $S$ に含まれず、条件1を満たしません。

例3: 列空間(Column Space)

行列 $\bm{A} \in \mathbb{R}^{m \times n}$ の 列空間 $\text{Col}(\bm{A})$ は、$\bm{A}$ の列ベクトルの全ての線形結合からなる集合です。

$$ \text{Col}(\bm{A}) = \{ \bm{A}\bm{x} \mid \bm{x} \in \mathbb{R}^n \} $$

列空間は $\mathbb{R}^m$ の部分空間です。機械学習では、データ行列の列空間を求めることが主成分分析の本質です。

例4: 零空間(Null Space)

行列 $\bm{A}$ の 零空間 $\text{Null}(\bm{A})$ は、$\bm{A}\bm{x} = \bm{0}$ を満たす全ての $\bm{x}$ の集合です。

$$ \text{Null}(\bm{A}) = \{ \bm{x} \in \mathbb{R}^n \mid \bm{A}\bm{x} = \bm{0} \} $$

零空間も $\mathbb{R}^n$ の部分空間であり、列空間と合わせて行列の構造を完全に特徴づけます。

部分空間の次元と基底

部分空間 $W$ の 基底 とは、$W$ を張る線形独立なベクトルの集合です。基底に含まれるベクトルの数を $W$ の 次元 $\dim(W)$ と呼びます。

例えば、$\mathbb{R}^3$ の部分空間として原点を通る平面を考えると、その次元は2です。この平面を張る2つの線形独立なベクトルが基底となります。

$$ W = \text{span}(\bm{v}_1, \bm{v}_2) = \{ \alpha \bm{v}_1 + \beta \bm{v}_2 \mid \alpha, \beta \in \mathbb{R} \} $$

機械学習における部分空間

主成分分析(PCA)では、データの分散が最大となる方向を見つけ、その方向が張る部分空間にデータを射影します。$d$ 次元のデータを $k$ 次元の部分空間に射影することで、次元削減を行います。

データの共分散行列 $\bm{\Sigma}$ の固有値分解を行い、上位 $k$ 個の固有ベクトル $\bm{v}_1, \dots, \bm{v}_k$ を取得すると、

$$ W_k = \text{span}(\bm{v}_1, \bm{v}_2, \dots, \bm{v}_k) $$

が求める $k$ 次元の部分空間です。データ点 $\bm{x}$ のこの部分空間への射影は、

$$ \hat{\bm{x}} = \bm{V}_k \bm{V}_k^T \bm{x} $$

で計算されます。ここで $\bm{V}_k = [\bm{v}_1, \bm{v}_2, \dots, \bm{v}_k]$ です。

Pythonでの実装

部分空間の概念を可視化してみましょう。3次元空間における2次元部分空間(平面)へのデータの射影を実装します。

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# 乱数シードの固定
np.random.seed(42)

# 3次元のデータを生成(主に2次元の部分空間に分布)
n_samples = 100
v1 = np.array([1, 1, 0]) / np.sqrt(2)  # 部分空間の基底1
v2 = np.array([0, 1, 1]) / np.sqrt(2)  # 部分空間の基底2

# 部分空間上のデータ + ノイズ
coeffs = np.random.randn(n_samples, 2)
noise = np.random.randn(n_samples, 3) * 0.1
data = coeffs @ np.array([v1, v2]) + noise

# 共分散行列の固有値分解でPCAを実施
cov_matrix = np.cov(data.T)
eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)

# 固有値の大きい順にソート
idx = np.argsort(eigenvalues)[::-1]
eigenvalues = eigenvalues[idx]
eigenvectors = eigenvectors[:, idx]

# 上位2つの固有ベクトルで部分空間を構成
V_k = eigenvectors[:, :2]

# 部分空間への射影
projected = data @ V_k @ V_k.T

# 可視化
fig = plt.figure(figsize=(12, 5))

# 元のデータ
ax1 = fig.add_subplot(121, projection='3d')
ax1.scatter(data[:, 0], data[:, 1], data[:, 2], alpha=0.6, s=20)
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.set_zlabel('z')
ax1.set_title('Original 3D Data')

# 射影後のデータ
ax2 = fig.add_subplot(122, projection='3d')
ax2.scatter(projected[:, 0], projected[:, 1], projected[:, 2], alpha=0.6, s=20, color='orange')
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.set_zlabel('z')
ax2.set_title('Projected onto 2D Subspace')

plt.tight_layout()
plt.show()

# 固有値の表示
print("固有値:", eigenvalues)
print("各主成分の寄与率:", eigenvalues / eigenvalues.sum())
print("部分空間の次元: 2")

このコードでは、3次元空間上のデータを共分散行列の固有値分解によって2次元の部分空間に射影しています。固有値が大きい方向ほどデータの分散が大きく、情報量が多い方向に対応します。

部分空間の判定をPythonで確認

部分空間の3条件が成り立つことをプログラムで確認してみましょう。

import numpy as np

def check_subspace(vectors, basis, tol=1e-10):
    """
    ベクトルが部分空間に属するかを確認する
    basis: 部分空間の基底ベクトル (列ベクトルとして並べた行列)
    """
    # 射影行列を計算
    P = basis @ np.linalg.pinv(basis)

    results = []
    for v in vectors:
        projected = P @ v
        residual = np.linalg.norm(v - projected)
        in_subspace = residual < tol
        results.append(in_subspace)
    return results

# 基底ベクトル: xy平面を張る
basis = np.array([[1, 0],
                  [0, 1],
                  [0, 0]], dtype=float)

# テストベクトル
test_vectors = [
    np.array([3.0, 2.0, 0.0]),   # xy平面上 → 部分空間に属する
    np.array([1.0, 0.0, 0.0]),   # x軸上 → 部分空間に属する
    np.array([0.0, 0.0, 0.0]),   # 零ベクトル → 部分空間に属する
    np.array([1.0, 1.0, 1.0]),   # z成分あり → 属さない
]

results = check_subspace(test_vectors, basis)

for v, r in zip(test_vectors, results):
    status = "部分空間に属する" if r else "部分空間に属さない"
    print(f"ベクトル {v} → {status}")

まとめ

本記事では、部分空間の定義と性質について解説しました。

  • 部分空間は「零ベクトルを含む」「加法で閉じている」「スカラー倍で閉じている」の3条件を満たす集合
  • 列空間と零空間は代表的な部分空間であり、行列の性質を特徴づける
  • 主成分分析(PCA)は、データの分散が最大となる部分空間を求める手法
  • 部分空間への射影は射影行列 $\bm{V}_k \bm{V}_k^T$ で計算できる

次のステップとして、以下の記事も参考にしてください。