100次元のデータを人間が理解するにはどうすればよいでしょうか。100個の散布図を個別に見ても全体像は掴めません。そこで必要になるのが次元削減(dimensionality reduction)です。
次元削減は、高次元データの「本質的な構造」を保ちながら低次元(通常2-3次元)に圧縮する技術です。これにより、データの可視化、ノイズの除去、計算効率の向上が可能になります。
しかし、「本質的な構造」の定義は手法によって異なります。PCAは分散の方向を保ち、t-SNEは局所的な類似性を保ち、UMAPはトポロジカルな構造を保ちます。この違いを理解することが、適切な手法を選ぶ鍵です。
次元削減を理解すると、以下のような場面で活用できます。
- データ可視化: 高次元データの2次元プロットによる探索的分析
- 前処理: 次元の呪いへの対処と計算効率の向上
- ノイズ除去: 重要でない変動(ノイズ)を除去してモデルの性能を向上
- 特徴量抽出: 元の特徴量の線形/非線形組み合わせで新しい特徴量を作成
本記事の内容
- PCA(主成分分析)の理論と固有値分解
- t-SNEの確率的近傍埋め込み
- UMAPのトポロジカルデータ解析に基づくアプローチ
- 3手法の比較と使い分け
- Pythonでの実装と可視化
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
- 特徴量スケーリングの理論 — PCAの前処理
- 正規分布 — 分散の概念
PCA(主成分分析)
直感
PCAの直感は「データの分散が最大になる方向を見つける」ことです。2次元データが楕円形に散らばっているとき、楕円の長軸方向が第1主成分、短軸方向が第2主成分です。
数学的定式化
$n$ 個の $d$ 次元データ $\bm{X} \in \mathbb{R}^{n \times d}$(中心化済み: $\bar{\bm{x}} = \bm{0}$)に対して、共分散行列は
$$ \bm{C} = \frac{1}{n}\bm{X}^T\bm{X} $$
分散を最大化する方向 $\bm{u}$ は
$$ \max_{\bm{u}} \bm{u}^T\bm{C}\bm{u} \quad \text{subject to} \quad \|\bm{u}\| = 1 $$
ラグランジュの未定乗数法より、$\bm{C}\bm{u} = \lambda\bm{u}$ が得られます。つまり主成分方向は共分散行列の固有ベクトルであり、その方向の分散は固有値 $\lambda$ です。
固有値を大きい順に $\lambda_1 \geq \lambda_2 \geq \cdots \geq \lambda_d$ と並べ、対応する固有ベクトルを $\bm{u}_1, \bm{u}_2, \ldots, \bm{u}_d$ とすると、上位 $k$ 個の固有ベクトルで張られる空間への射影が $k$ 次元PCAです。
寄与率と次元の選択
第 $j$ 主成分の寄与率は $\lambda_j / \sum_k \lambda_k$ であり、上位 $k$ 個の累積寄与率が十分大きい(例: 95%以上)$k$ を選びます。
t-SNE
動機
PCAは線形的な構造しか捉えられません。非線形な多様体構造(例: スイスロール)を持つデータには不向きです。
t-SNE(t-distributed Stochastic Neighbor Embedding; van der Maaten & Hinton, 2008)は、高次元空間での近傍関係を低次元空間で保存する非線形手法です。
アルゴリズム
高次元空間でのデータ点 $\bm{x}_i, \bm{x}_j$ の類似度を条件付きガウス分布で定義します。
$$ p_{j|i} = \frac{\exp(-\|\bm{x}_i – \bm{x}_j\|^2 / 2\sigma_i^2)}{\sum_{k \neq i}\exp(-\|\bm{x}_i – \bm{x}_k\|^2 / 2\sigma_i^2)} $$
対称化して $p_{ij} = (p_{j|i} + p_{i|j}) / 2n$ とします。
低次元空間ではt分布(自由度1、コーシー分布)で類似度を定義します。
$$ q_{ij} = \frac{(1 + \|\bm{y}_i – \bm{y}_j\|^2)^{-1}}{\sum_{k \neq l}(1 + \|\bm{y}_k – \bm{y}_l\|^2)^{-1}} $$
t分布を使う理由は、裾が重いため遠い点をさらに遠くに配置でき、クラウディング問題(中間距離の点が混雑する問題)を緩和するためです。
$p$ と $q$ のKLダイバージェンスを最小化します。
$$ \text{KL}(P \| Q) = \sum_{i \neq j} p_{ij} \ln \frac{p_{ij}}{q_{ij}} $$
t-SNEの注意点
- 非確定的: ランダムな初期化により結果が毎回異なる
- perplexityパラメータ: 近傍の範囲を制御(通常5-50)
- 大域的構造の非保存: クラスター間の距離は意味を持たない
- 計算コスト: $O(n^2)$(Barnes-Hut近似で $O(n \log n)$)
UMAP
理論的背景
UMAP(Uniform Manifold Approximation and Projection; McInnes et al., 2018)はリーマン幾何学とトポロジカルデータ解析に基づく手法です。
UMAPは高次元データの位相的構造(ファジー単体集合)を構築し、低次元で同等の位相的構造を持つ埋め込みを見つけます。
t-SNEとの比較
- 速度: UMAPはt-SNEより大幅に高速($O(n)$ に近い)
- 大域構造: UMAPはクラスター間の相対的な位置関係をある程度保存
- パラメータ:
n_neighbors(局所/大域のバランス)とmin_dist(点の密集度)
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_swiss_roll, load_digits
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from sklearn.preprocessing import StandardScaler
np.random.seed(42)
# 手書き数字データ
digits = load_digits()
X_digits = digits.data
y_digits = digits.target
# 標準化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_digits)
fig, axes = plt.subplots(1, 3, figsize=(16, 5))
# (a) PCA
ax = axes[0]
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
scatter = ax.scatter(X_pca[:, 0], X_pca[:, 1], c=y_digits, cmap="tab10",
s=5, alpha=0.7)
ax.set_title(f"PCA (var explained={pca.explained_variance_ratio_.sum():.1%})", fontsize=13)
ax.set_xlabel("PC1", fontsize=12)
ax.set_ylabel("PC2", fontsize=12)
ax.grid(True, alpha=0.3)
# (b) t-SNE
ax = axes[1]
tsne = TSNE(n_components=2, perplexity=30, random_state=42, n_iter=1000)
X_tsne = tsne.fit_transform(X_scaled)
scatter = ax.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y_digits, cmap="tab10",
s=5, alpha=0.7)
ax.set_title("t-SNE (perplexity=30)", fontsize=13)
ax.set_xlabel("t-SNE 1", fontsize=12)
ax.set_ylabel("t-SNE 2", fontsize=12)
ax.grid(True, alpha=0.3)
# (c) UMAP(UMAPがインストールされていない場合のフォールバック)
ax = axes[2]
try:
import umap
reducer = umap.UMAP(n_components=2, n_neighbors=15, min_dist=0.1, random_state=42)
X_umap = reducer.fit_transform(X_scaled)
scatter = ax.scatter(X_umap[:, 0], X_umap[:, 1], c=y_digits, cmap="tab10",
s=5, alpha=0.7)
ax.set_title("UMAP (n_neighbors=15)", fontsize=13)
except ImportError:
# UMAPなしの場合はt-SNEの別パラメータで代替
tsne2 = TSNE(n_components=2, perplexity=50, random_state=0, n_iter=1000)
X_tsne2 = tsne2.fit_transform(X_scaled)
scatter = ax.scatter(X_tsne2[:, 0], X_tsne2[:, 1], c=y_digits, cmap="tab10",
s=5, alpha=0.7)
ax.set_title("t-SNE (perplexity=50, alt params)", fontsize=13)
ax.set_xlabel("Dim 1", fontsize=12)
ax.set_ylabel("Dim 2", fontsize=12)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("dimensionality_reduction.png", dpi=150, bbox_inches="tight")
plt.show()
このグラフから、3手法の特性の違いが読み取れます。
-
左図(PCA): 線形的な射影で、クラスター構造がある程度見えますが、クラス間の重なりが大きいです。累積寄与率は2次元で全分散の一部しか説明できていないことを示しています。大域的な分散の方向は保存されていますが、局所的な構造は失われています
-
中央図(t-SNE): 各数字のクラスターが明確に分離されており、局所的な構造が非常によく保存されています。ただし、クラスター間の距離は信頼できません。perplexityパラメータが結果に大きく影響します
-
右図(UMAP): t-SNEと同様に良好なクラスター分離を示しつつ、クラスター間の相対的な距離もある程度保存されています。計算速度もt-SNEより高速です
使い分けガイド
| 用途 | 推奨手法 | 理由 |
|---|---|---|
| 前処理(回帰/分類の前段) | PCA | 線形・高速・逆変換可能 |
| 可視化(探索的分析) | t-SNE / UMAP | 非線形構造の可視化に優れる |
| 大規模データの可視化 | UMAP | t-SNEより大幅に高速 |
| ノイズ除去 | PCA | 小さい固有値の成分を除去 |
| 特徴量抽出 | PCA / Kernel PCA | 新しい特徴量としてモデルに入力 |
まとめ
本記事では、主要な次元削減手法の理論と使い分けについて解説しました。
- PCAは共分散行列の固有値分解に基づく線形手法で、分散を最大化する方向に射影する
- t-SNEは高次元の近傍関係を低次元で保存する非線形手法で、可視化に優れるが計算コストが高い
- UMAPはトポロジカルデータ解析に基づく手法で、t-SNEの利点を保ちつつ高速で大域構造も保存する
- PCAは前処理とノイズ除去に、t-SNE/UMAPはデータの可視化と探索的分析に使い分ける
次のステップとして、以下の記事も参考にしてください。
- k近傍法の理論と実装 — 次元の呪いと次元削減
- DBSCANクラスタリングの理論と実装 — 低次元でのクラスタリング
- 特徴量スケーリングの理論 — PCAの前処理