机器学习降维技术深度解析:PCA、LDA与t-SNE原理及实战

机器学习降维技术深度解析:PCA、LDA与t-SNE原理及实战

一、降维技术概述

1.1 为什么需要降维?

在机器学习领域,维度灾难(Curse of Dimensionality) 是一个核心挑战。随着特征维度的增加,数据变得稀疏,模型容易过拟合,计算成本也急剧上升。

降维的核心价值:

  • 📦 数据压缩:减少存储空间,提高计算效率
  • 🎯 避免过拟合:降低模型复杂度,提升泛化能力
  • 📊 数据可视化:将高维数据映射到2D/3D空间
  • 🔍 特征提取:发现数据中的潜在结构

1.2 特征选择 vs 特征提取

对比维度 特征选择(Feature Selection) 特征提取(Feature Extraction)
核心思想 选择原始特征的子集 将数据转换到新的特征空间
特征来源 保留原始特征 创造新的特征组合
代表算法 SBS、L1正则化、随机森林 PCA、LDA、t-SNE
可解释性 高(保留原始特征名) 低(新特征是线性/非线性组合)

二、主成分分析(PCA)------无监督降维之王

2.1 PCA核心原理

PCA(Principal Component Analysis) 是一种无监督线性变换方法,目标是找到数据中方差最大的方向(主成分),并将数据投影到这些方向上实现降维。

关键洞察:方差最大的方向包含最多的信息。PCA通过保留最大方差的方向,实现信息损失最小的压缩。

2.2 PCA算法流程(七步法)

md 复制代码
┌─────────────────────────────────────────────────────────┐
│  Step 1: 标准化数据                                       │
│  ─────────────────                                       │
│  使用StandardScaler将特征缩放到零均值、单位方差              │
│  x_std = (x - μ) / σ                                     │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│  Step 2: 构造协方差矩阵                                   │
│  ─────────────────────                                   │
│  Σ = 1/(n-1) * X^T * X                                   │
│  协方差矩阵Σ[d×d]存储特征间的相关性                        │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│  Step 3: 特征分解                                         │
│  ─────────────                                           │
│  计算特征值和特征向量:Σv = λv                            │
│  使用np.linalg.eig或np.linalg.eigh                       │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│  Step 4: 特征值排序                                       │
│  ─────────────────                                       │
│  按特征值降序排列:λ₁ ≥ λ₂ ≥ ... ≥ λ_d                   │
│  特征值越大,对应主成分包含的方差越多                      │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│  Step 5: 选择前k个主成分                                  │
│  ─────────────────────                                   │
│  根据方差解释比选择k:                                    │
│  方差解释比 = λ_j / Σλ_i                                 │
│  累积方差解释比 = Σ(λ₁到λ_k) / Σλ_i                      │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│  Step 6: 构造投影矩阵W                                    │
│  ───────────────────                                     │
│  W = [v₁, v₂, ..., v_k]  (d×k矩阵)                       │
│  每列是一个主成分(特征向量)                              │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│  Step 7: 数据投影变换                                     │
│  ─────────────────                                       │
│  X' = X · W  (n×k矩阵)                                   │
│  原始d维数据 → 新的k维特征子空间                          │
└─────────────────────────────────────────────────────────┘

2.3 方差解释比可视化

PCA降维的关键是确定保留多少个主成分。通过方差解释比(Explained Variance Ratio) 可以量化信息保留程度:

python 复制代码
import numpy as np
import matplotlib.pyplot as plt

# 计算方差解释比
tot = sum(eigen_vals)
var_exp = [(i / tot) for i in sorted(eigen_vals, reverse=True)]
cum_var_exp = np.cumsum(var_exp)

# 可视化
plt.bar(range(1, 14), var_exp, align='center', 
        label='Individual explained variance')
plt.step(range(1, 14), cum_var_exp, where='mid',
         label='Cumulative explained variance')
plt.ylabel('Explained variance ratio')
plt.xlabel('Principal component index')
plt.legend(loc='best')
plt.show()

葡萄酒数据集示例结果:

  • 第1主成分:~40%方差
  • 前2主成分:~60%方差
  • 前3主成分:~70%方差

2.4 Scikit-Learn实战代码

python 复制代码
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

# 1. 数据准备
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, stratify=y, random_state=0
)

# 2. 标准化(PCA对尺度敏感!)
sc = StandardScaler()
X_train_std = sc.fit_transform(X_train)
X_test_std = sc.transform(X_test)

# 3. PCA降维到2维
pca = PCA(n_components=2)
X_train_pca = pca.fit_transform(X_train_std)
X_test_pca = pca.transform(X_test_std)

# 4. 查看方差解释比
print(pca.explained_variance_ratio_)
# 输出: [0.36951469, 0.18434927]  # 约55%方差

# 5. 降维后分类
lr = LogisticRegression(multi_class='ovr', random_state=1)
lr.fit(X_train_pca, y_train)
print(f'Accuracy: {lr.score(X_test_pca, y_test):.3f}')

2.5 PCA特征贡献度分析

理解原始特征如何贡献于主成分,可通过载荷(Loadings) 分析:

python 复制代码
# 获取PCA载荷(特征与主成分的相关性)
sklearn_loadings = pca.components_.T * np.sqrt(pca.explained_variance_)

# 可视化第1主成分的载荷
fig, ax = plt.subplots()
ax.bar(range(13), sklearn_loadings[:, 0], align='center')
ax.set_ylabel('Loadings for PC 1')
ax.set_xticks(range(13))
ax.set_xticklabels(feature_names, rotation=90)
plt.ylim([-1, 1])
plt.show()

关键发现:载荷绝对值越大,该特征对主成分的贡献越大。

三、线性判别分析(LDA)------监督降维利器

3.1 LDA vs PCA:核心区别

md 复制代码
┌────────────────────────────────────────────────────────────┐
│                    PCA(无监督)                            │
├────────────────────────────────────────────────────────────┤
│  目标:最大化投影后方差                                       │
│  输入:仅特征X                                               │
│  优化:寻找数据分布最分散的方向                                │
│  假设:方差大的方向信息多                                     │
│  适用:数据探索、压缩、去噪                                   │
└────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────┐
│                    LDA(监督)                              │
├────────────────────────────────────────────────────────────┤
│  目标:最大化类间可分性                                       │
│  输入:特征X + 标签y                                         │
│  优化:类间距离大,类内距离小                                  │
│  假设:数据服从正态分布,各类协方差相同                         │
│  适用:分类任务预处理、特征提取                                │
└────────────────────────────────────────────────────────────┘

3.2 LDA几何直观

md 复制代码
┌─────────────────────────────────────────────────────────┐
│  Step 1: 计算各类别均值向量                                 │
│  ─────────────────────────                               │
│  μ_c = 1/n_c * Σx_i  (c=1,2,...,C类别数)                  │
└─────────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────┐
│  Step 2: 计算类内散布矩阵S_W                                │
│  ─────────────────────────                               │
│  S_W = Σ_c Σ_{x∈D_c} (x - μ_c)(x - μ_c)^T               │
│  衡量每个类别内部的离散程度                                  │
└─────────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────┐
│  Step 3: 计算类间散布矩阵S_B                                │
│  ─────────────────────────                               │
│  S_B = Σ_c n_c(μ_c - μ)(μ_c - μ)^T                       │
│  衡量不同类别均值之间的离散程度                              │
│  (μ为全局均值)                                           │
└─────────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────┐
│  Step 4: 求解广义特征值问题                                  │
│  ─────────────────────────                               │
│  S_W^(-1) * S_B * w = λ * w                              │
│  等价于:找到使 J(w) = (w^T S_B w) / (w^T S_W w) 最大的w   │
└─────────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────┐
│  Step 5: 选择特征向量构造投影矩阵                            │
│  ───────────────────────────────                         │
│  最大特征值对应的特征向量 = 最佳判别方向                     │
│  最多有C-1个非零特征值(C为类别数)                          │
└─────────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────┐
│  Step 6: 数据投影                                          │
│  ─────────────                                           │
│  X' = X · W  (投影到C-1维判别空间)                         │
└─────────────────────────────────────────────────────────┘

3.4 Scikit-Learn实战

python 复制代码
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA

# LDA降维(注意:n_components ≤ min(n_features, n_classes-1))
lda = LDA(n_components=2)
X_train_lda = lda.fit_transform(X_train_std, y_train)  # 需要标签!
X_test_lda = lda.transform(X_test_std)

# 训练分类器
lr = LogisticRegression(multi_class='ovr', random_state=1)
lr.fit(X_train_lda, y_train)

# 在3分类葡萄酒数据集上,LDA通常比PCA分类效果更好
print(f'LDA+LR Accuracy: {lr.score(X_test_lda, y_test):.3f}')

3.5 判别能力分析

python 复制代码
# 计算线性判别式的判别能力比例
tot = sum(eigen_vals.real)
discr = [(i / tot) for i in sorted(eigen_vals.real, reverse=True)]
cum_discr = np.cumsum(discr)

plt.bar(range(1, 14), discr, align='center', label='Individual')
plt.step(range(1, 14), cum_discr, where='mid', label='Cumulative')
plt.ylabel('"Discriminability" ratio')
plt.xlabel('Linear Discriminants')
plt.legend()
plt.show()

关键特性:对于C类问题,LDA最多产生C-1个非零判别式。

四、t-SNE------非线性降维与可视化神器

4.1 为什么需要非线性降维?

python 复制代码
线性降维的局限:
┌────────────────────────────────────────┐
│     线性可分数据          非线性可分数据    │
│                                        │
│    ○ ○ ○ ○ ○           ○  ○  ○  ○  ○   │
│       \                    +++++++     │
│        \  +++++++        ○ +++++++ ○   │
│         \+++++++        ○  +++++++  ○  │
│    ○ ○ ○ ○ ○           ○   +++++++   ○ │
│                                        │
│   PCA/LDA: ✓ 完美        PCA/LDA: ✗ 失败 │
│   一条直线可分离          需要非线性边界    │
│                                        │
│   解决方案:t-SNE、UMAP等流形学习方法      │
└────────────────────────────────────────┘

4.2 t-SNE核心原理

t-SNE(t-Distributed Stochastic Neighbor Embedding) 的核心思想:

高维空间:计算样本间的相似度概率分布P

低维空间:构建对应的相似度概率分布Q

优化目标:最小化P与Q之间的KL散度

md 复制代码
高维空间(原始数据)                    低维空间(2D/3D可视化)
┌─────────────────┐                  ┌─────────────────┐
│                 │                  │    ●        ●   │
│    ●     ●      │    t-SNE优化      │  ●    ●  ●      │
│       ●         │  ───────────→    │        ●        │
│    ●     ●      │   保持局部结构      │  ●        ●    │
│                 │                  │      ●    ●     │
└─────────────────┘                  └─────────────────┘
   相似样本距离近                        相似样本在2D中仍靠近
   不相似样本距离远                      不相似样本在2D中远离

4.3 t-SNE关键特性

特性 说明
非线性 能捕捉复杂的非线性流形结构
局部性 主要保持局部邻域结构,全局结构可能失真
随机性 不同运行可能产生不同结果
不可投影 不能直接应用于新数据(需重新训练)
超参数敏感 perplexity、learning rate等影响大

4.4 实战:手写数字可视化

python 复制代码
from sklearn.manifold import TSNE
from sklearn.datasets import load_digits

# 加载手写数字数据集(64维:8×8像素)
digits = load_digits()
X_digits, y_digits = digits.data, digits.target  # (1797, 64)

# t-SNE降维到2D(关键参数设置)
tsne = TSNE(
    n_components=2,      # 降维到2维
    init='pca',          # 使用PCA初始化(推荐!)
    random_state=123,
    perplexity=30,       # 困惑度(通常5-50)
    learning_rate=200    # 学习率
)

# 执行降维(注意:t-SNE计算较慢)
X_digits_tsne = tsne.fit_transform(X_digits)

# 可视化结果
import matplotlib.patheffects as PathEffects

def plot_projection(X, colors):
    plt.figure(figsize=(8, 8))
    for i in range(10):  # 10个数字类别
        plt.scatter(X[colors == i, 0], X[colors == i, 1], 
                   label=f'Digit {i}')
        
        # 在类别中心添加数字标签
        xtext, ytext = np.median(X[colors == i, :], axis=0)
        txt = plt.text(xtext, ytext, str(i), fontsize=24)
        txt.set_path_effects([
            PathEffects.Stroke(linewidth=5, foreground="w"),
            PathEffects.Normal()
        ])
    plt.legend()
    plt.show()

plot_projection(X_digits_tsne, y_digits)

可视化效果:相同数字形成紧密的簇,不同数字明显分离,即使原始数据是64维!

4.5 t-SNE最佳实践

md 复制代码
┌─────────────────────────────────────────────────────────┐
│  ✅ DO(推荐做法)                                        │
├─────────────────────────────────────────────────────────┤
│  • 先用PCA降维到50维左右,再应用t-SNE(加速+去噪)          │
│  • 使用PCA初始化(init='pca')                            │
│  • 多次运行取最佳结果(因随机性)                           │
│  • 调整perplexity(5-50,数据集越大可越大)                 │
│  • 主要用于可视化,不用于特征工程                            │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│  ❌ DON'T(避免做法)                                     │
├─────────────────────────────────────────────────────────┤
│  • 直接在高维数据(>100维)上运行t-SNE                     │
│  • 用于训练机器学习模型(无法transform新数据)               │
│  • 过度解读全局距离(t-SNE主要保持局部结构)                 │
│  • 固定random_state后认为结果是确定的                       │
└─────────────────────────────────────────────────────────┘

五、算法对比与选择指南

5.1 综合对比表

维度 PCA LDA t-SNE
学习类型 无监督 监督 无监督
线性/非线性 线性 线性 非线性
主要目标 最大方差 最大类间可分性 保持局部邻域结构
输出维度限制 无(≤原维度) ≤C-1(C为类别数) 通常2或3(可视化)
新数据投影 ✅ 可以 ✅ 可以 ❌ 不可行
计算复杂度 O(d³)或O(nd²) O(d³) O(n²) ~ O(n log n)
主要用途 压缩、去噪、预处理 分类预处理、特征提取 可视化、探索性分析
对异常值敏感 中等

5.2 选择决策树

md 复制代码
开始
  │
  ▼
需要用于训练模型? ──Yes──→ 需要标签信息?
  │ No                      │
  ▼                      Yes ├─→ LDA(监督分类任务)
t-SNE/UMAP              No  │
(仅可视化)                ▼
                        PCA
                      (通用降维)

5.3 实际应用建议

场景 推荐算法 理由
高维数据预处理(>100维) PCA → 50维 去噪、加速后续处理
分类任务特征提取 LDA 利用标签信息,提升可分性
数据探索与展示 t-SNE/UMAP 直观展示数据结构
实时系统需要transform新数据 PCA/LDA t-SNE无法直接投影新数据
图像/文本非线性结构 UMAP 比t-SNE更快,可投影新数据

六、总结与进阶

6.1 核心要点回顾

  • PCA是降维的瑞士军刀,无监督、线性、可投影,适合预处理
  • LDA利用标签信息,最大化类间可分性,适合分类任务
  • t-SNE专注于可视化,非线性、保持局部结构,但不适合特征工程

6.2 进阶学习方向

  • UMAP:t-SNE的升级版,更快且可投影新数据
  • Kernel PCA:非线性PCA,使用核技巧
  • Autoencoder:基于神经网络的非线性降维
  • Factor Analysis:概率化的PCA变体

本文来自《智能系统与技术丛书 Python机器学习 基于PyTorch和Scikit-Learn_(美)塞巴斯蒂安・拉施卡》的学习与理解,仅供学习使用,请勿用于商业用途

相关推荐
九.九8 小时前
ops-transformer:AI 处理器上的高性能 Transformer 算子库
人工智能·深度学习·transformer
春日见8 小时前
拉取与合并:如何让个人分支既包含你昨天的修改,也包含 develop 最新更新
大数据·人工智能·深度学习·elasticsearch·搜索引擎
恋猫de小郭9 小时前
AI 在提高你工作效率的同时,也一直在增加你的疲惫和焦虑
前端·人工智能·ai编程
deephub9 小时前
Agent Lightning:微软开源的框架无关 Agent 训练方案,LangChain/AutoGen 都能用
人工智能·microsoft·langchain·大语言模型·agent·强化学习
大模型RAG和Agent技术实践9 小时前
从零构建本地AI合同审查系统:架构设计与流式交互实战(完整源代码)
人工智能·交互·智能合同审核
老邋遢9 小时前
第三章-AI知识扫盲看这一篇就够了
人工智能
互联网江湖9 小时前
Seedance2.0炸场:长短视频们“修坝”十年,不如AI放水一天?
人工智能
PythonPioneer9 小时前
在AI技术迅猛发展的今天,传统职业该如何“踏浪前行”?
人工智能
冬奇Lab10 小时前
一天一个开源项目(第20篇):NanoBot - 轻量级AI Agent框架,极简高效的智能体构建工具
人工智能·开源·agent
阿里巴巴淘系技术团队官网博客10 小时前
设计模式Trustworthy Generation:提升RAG信赖度
人工智能·设计模式