Digital Reactor
機械学習

次元削減手法の比較検証:定量的・定性的評価によるPCA、t-SNE、UMAPの特徴分析

次元削減手法の比較検証:定量的・定性的評価によるPCA、t-SNE、UMAPの特徴分析

はじめに

この記事では、機械学習や統計分析において重要な、高次元データの可視化および分析のための次元削減手法について解説します。特に、PCA(主成分分析)、t-SNE、UMAPという代表的な3つの手法に焦点を当て、それぞれの理論的背景、性能評価、そして実践的な使い分けについて、詳細な比較と具体例を交えながら説明します。

対象読者:

  • 機械学習エンジニアやデータサイエンティスト
  • 次元削減の手法選択に悩んでいる方
  • 高次元データの可視化や分析に関心のある方

記事のポイント:

  • PCA、t-SNE、UMAPの理論的な違いを理解できる
  • 各手法の長所と短所を把握し、適切な使い分けができるようになる
  • 定量的な評価指標(トラストワーシネス、再構成誤差)と定性的な評価を比較できる

次元削減手法の理論的背景

PCA(主成分分析)

PCAは、最も基本的かつ広く利用されている次元削減手法です。

  • 仕組み: データの分散が最大となる方向(主成分)を順次見つけ出し、それらの主成分にデータを射影します。
  • 特徴:
    • 線形変換であり、変換後の空間から元の空間へ戻すこと(逆変換)が可能です。
    • 計算が高速で、大規模なデータセットにも適用可能です。
  • 最適化問題: 以下の最適化問題を解くことで主成分を求めます。
maxwwTXTXwsubject tow=1\max_{w} w^T X^T X w \quad \text{subject to} \quad \|w\| = 1

ここで、XX はデータ行列、ww は主成分ベクトルを表します。

t-SNE

t-SNEは、非線形な次元削減手法であり、特にデータの局所的な構造を保持することに優れています。

  • 仕組み: 高次元空間と低次元空間におけるデータ点間の類似度を確率分布で表現し、それらの分布が近づくように低次元空間でのデータ点配置を最適化します。
  • 特徴:
    • データの局所的な構造(クラスターなど)を可視化するのに適しています。
    • 計算コストが高く、大規模データセットへの適用には工夫が必要です。
  • 最適化問題: 以下のKLダイバージェンスを最小化します。
KL(PQ)=ijpijlogpijqijKL(P||Q) = \sum_{i}\sum_{j} p_{ij} \log\frac{p_{ij}}{q_{ij}}

ここで、PP は高次元空間での確率分布、QQ は低次元空間での確率分布、pijp_{ij}qijq_{ij} はそれぞれデータ点 iijj 間の類似度を表します。

UMAP

UMAPは、比較的新しい非線形次元削減手法で、t-SNEの改良版とも言えます。

  • 仕組み: リーマン幾何学とトポロジーの概念を応用し、高次元空間と低次元空間におけるデータ点間の関係性を保持するように低次元空間でのデータ点配置を最適化します。
  • 特徴:
    • t-SNEよりも高速で、グローバルな構造もある程度保持できます。
    • 新しいデータ点を既存の低次元空間に射影する機能(transform)を持っています。

評価指標

次元削減の性能を評価するため、以下の指標を用います。

トラストワーシネス(Trustworthiness)

トラストワーシネスは、局所的な構造の保持度を評価する指標です。

  • 定義: 元の高次元空間での近傍関係が、低次元空間でもどの程度保持されているかを測定します。
  • 値域: 0から1の値をとり、1に近いほど良好な保持度を示します。
  • 計算方法: 以下のPythonコードで計算できます。
from sklearn.manifold import trustworthiness
trust_score = trustworthiness(X_original, X_reduced)

再構成誤差(Reconstruction Error)

再構成誤差は、PCAに特有の評価指標で、情報の保持度を表します。

  • 定義: 低次元空間に射影したデータを元の高次元空間に戻した際に、元のデータとどの程度の差(二乗平均平方根誤差、RMSE)が生じるかを測定します。
  • : 値が小さいほど、元の情報が保持されていることを示します。
  • 計算方法: 以下のPythonコードで計算できます。
def compute_reconstruction_error(X, X_transformed, pca):
    X_reconstructed = pca.inverse_transform(X_transformed)
    return np.mean(np.square(X - X_reconstructed))

実験:3つのデータセットでの比較

実験設定

以下の3つのデータセットを用いて、PCA、t-SNE、UMAPの比較実験を行いました。

  1. Swiss Roll: 3次元の非線形多様体(人工データ)
  2. S-curve: 3次元の滑らかな曲面(人工データ)
  3. アヤメデータセット: 4次元の特徴量を持つ分類データ(実データ)
import numpy as np
from sklearn.datasets import make_swiss_roll, make_s_curve, load_iris
from sklearn.preprocessing import StandardScaler

# データセットの生成と前処理
X_swiss, color_swiss = make_swiss_roll(n_samples=1000, random_state=42)
X_s_curve, color_s_curve = make_s_curve(n_samples=1000, random_state=42)
iris = load_iris()
X_iris = StandardScaler().fit_transform(iris.data)

実験結果

定量的評価の比較

評価指標データセットPCAt-SNEUMAP
トラストワーシネスSwiss Roll0.9821.0000.999
S-curve0.9781.0000.999
Iris0.9740.9910.982
計算時間 (秒)Swiss Roll0.012.450.32
S-curve0.012.380.30
Iris0.011.120.15
再構成誤差Swiss Roll12.390--
S-curve0.113--
Iris0.042--

定性的特徴の比較

特徴PCAt-SNEUMAP
非線形性の扱い線形のみ優れている優れている
局所構造の保持中程度非常に優れている優れている
グローバル構造の保持良好弱い中程度
スケーラビリティ優れている低い良好
新規データの射影可能不可能可能
解釈のしやすさ容易難しいやや難しい

実践的な使い分け

用途別の推奨手法

用途推奨手法理由
次元の意味理解PCA主成分の解釈が容易で、データの変動の方向性が明確
クラスタリング可視化t-SNE/UMAP局所構造の保持に優れ、クラスター間の分離が明確
大規模データ処理UMAP計算効率が良く、比較的高品質な結果が得られる
リアルタイム処理PCA計算が非常に高速で、新規データの射影も容易
教師なし異常検知UMAP局所構造を保持しつつ、計算効率も比較的良好で、外れ値の検出に適している

データサイズによる選択指針

データサイズ推奨手法注意点
小規模(<1000)全て使用可まずPCAで試し、必要に応じてt-SNEやUMAPを使用
中規模(1000-10000)PCA/UMAPt-SNEは計算時間に注意
大規模(>10000)UMAPPCAも可能だが、非線形性の表現が限定的になる可能性がある。

まとめ

本記事では、PCA、t-SNE、UMAPという3つの代表的な次元削減手法について、理論的背景、定量的・定性的な評価、そして実践的な使い分けについて解説しました。

以下に重要なポイントをまとめます。

  • PCAは線形変換で高速だが、非線形なデータの構造を捉えられない場合がある。
  • t-SNEはデータの局所構造の保持に優れるが、計算コストが高く、大規模データには不向き。
  • UMAPはt-SNEの利点を持ちつつ、高速でグローバルな構造もある程度保持でき、新規データの射影も可能。

次元削減手法の選択は、データの性質や分析の目的に大きく依存します。本記事で紹介した情報を参考に、最適な手法を選択し、効果的なデータ分析を実現してください。

コード

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import make_swiss_roll, make_s_curve, load_iris
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE, trustworthiness
from sklearn.metrics.pairwise import euclidean_distances
import umap
import japanize_matplotlib
from time import time

def compute_reconstruction_error(X, X_transformed, pca):
    """PCAの再構成誤差を計算"""
    if not isinstance(pca, PCA):
        return None
    X_reconstructed = pca.inverse_transform(X_transformed)
    return np.mean(np.square(X - X_reconstructed))

def compute_metrics(X, X_transformed, method_name, pca=None):
    """各種評価指標を計算"""
    metrics = {}

    # トラストワーシネス(局所構造の保持度)
    metrics['trustworthiness'] = trustworthiness(X, X_transformed)

    # 再構成誤差(PCAのみ)
    if isinstance(pca, PCA):
        metrics['reconstruction_error'] = compute_reconstruction_error(X, X_transformed, pca)

    return metrics

def generate_datasets():
    # スイスロールデータセット
    X_swiss, color_swiss = make_swiss_roll(n_samples=1000, random_state=42)

    # S字カーブデータセット
    X_s_curve, color_s_curve = make_s_curve(n_samples=1000, random_state=42)

    # IRISデータセット
    iris = load_iris()
    X_iris = StandardScaler().fit_transform(iris.data)
    color_iris = iris.target

    return (X_swiss, color_swiss, "Swiss_Roll"), (X_s_curve, color_s_curve, "S_Curve"), (X_iris, color_iris, "Iris")

def apply_dimension_reduction(X, methods):
    results = {}
    times = {}
    metrics = {}

    for name, method in methods.items():
        start_time = time()
        results[name] = method.fit_transform(X)
        times[name] = time() - start_time

        # 評価指標の計算
        pca = method if isinstance(method, PCA) else None
        metrics[name] = compute_metrics(X, results[name], name, pca)

    return results, times, metrics

def plot_results(results, colors, dataset_name, times, metrics):
    fig = plt.figure(figsize=(15, 5))

    for idx, (name, embedding) in enumerate(results.items()):
        ax = fig.add_subplot(1, 3, idx + 1)
        scatter = ax.scatter(embedding[:, 0], embedding[:, 1], c=colors, cmap='viridis')

        # タイトルに評価指標を追加
        title = f'{name}\nTime: {times[name]:.2f}s\n'
        title += f'Trust: {metrics[name]["trustworthiness"]:.3f}'
        if "reconstruction_error" in metrics[name]:
            title += f'\nRecon Error: {metrics[name]["reconstruction_error"]:.3f}'
        ax.set_title(title)

        if dataset_name == "Iris":
            plt.colorbar(scatter, ticks=[0, 1, 2], label='Species')
        else:
            plt.colorbar(scatter)

    plt.suptitle(f'{dataset_name}の次元削減結果比較')
    plt.tight_layout()
    plt.savefig(f'{dataset_name}_comparison.png')
    plt.close()

def main():
    # 次元削減手法の定義
    methods = {
        'PCA': PCA(n_components=2),
        't-SNE': TSNE(n_components=2, random_state=42),
        'UMAP': umap.UMAP(random_state=42)
    }

    # データセットの生成
    datasets = generate_datasets()

    # 各データセットに対して次元削減を適用
    for X, colors, name in datasets:
        results, times, metrics = apply_dimension_reduction(X, methods)
        plot_results(results, colors, name, times, metrics)

        # 評価指標の詳細を出力
        print(f"\n{name}データセットの評価指標:")
        for method_name, method_metrics in metrics.items():
            print(f"\n{method_name}:")
            for metric_name, value in method_metrics.items():
                print(f"  {metric_name}: {value:.3f}")

if __name__ == '__main__':
    main()

関連記事

← 技術ブログ一覧へ