降维技术详解
降维技术详解
降维是将高维数据映射到低维空间的技术,用于数据可视化、特征提取和计算效率提升。
降维原理
降维的必要性
- 维数灾难:高维空间中数据稀疏
- 计算效率:减少计算时间和内存需求
- 可视化:将高维数据映射到2D/3D空间
- 噪声过滤:去除不重要的特征
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA, KernelPCA, TruncatedSVD
from sklearn.manifold import TSNE, Isomap, LocallyLinearEmbedding
from sklearn.datasets import load_digits, make_swiss_roll
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
import time
# 加载手写数字数据集
digits = load_digits()
X, y = digits.data, digits.target
# 标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
print(f"原始数据维度: {X_scaled.shape}")
print(f"类别数量: {len(np.unique(y))}")
线性降维方法
主成分分析(PCA)
# PCA降维
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
print(f"PCA降维后维度: {X_pca.shape}")
print(f"解释方差比例: {pca.explained_variance_ratio_}")
print(f"累计解释方差: {sum(pca.explained_variance_ratio_):.4f}")
# 可视化
plt.figure(figsize=(10, 8))
scatter = plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y, cmap='tab10',
s=50, alpha=0.7, edgecolors='black')
plt.xlabel(f'主成分1 ({pca.explained_variance_ratio_[0]:.2%})')
plt.ylabel(f'主成分2 ({pca.explained_variance_ratio_[1]:.2%})')
plt.title('PCA降维后的手写数字数据')
plt.colorbar(scatter, label='数字类别')
plt.grid(True, alpha=0.3)
plt.show()
奇异值分解(SVD)
# 截断SVD
svd = TruncatedSVD(n_components=2, random_state=42)
X_svd = svd.fit_transform(X_scaled)
print(f"SVD降维后维度: {X_svd.shape}")
print(f"解释方差比例: {svd.explained_variance_ratio_}")
# 可视化
plt.figure(figsize=(10, 8))
scatter = plt.scatter(X_svd[:, 0], X_svd[:, 1], c=y, cmap='tab10',
s=50, alpha=0.7, edgecolors='black')
plt.xlabel(f'成分1 ({svd.explained_variance_ratio_[0]:.2%})')
plt.ylabel(f'成分2 ({svd.explained_variance_ratio_[1]:.2%})')
plt.title('SVD降维后的手写数字数据')
plt.colorbar(scatter, label='数字类别')
plt.grid(True, alpha=0.3)
plt.show()
非线性降维方法
t-SNE
# t-SNE降维
tsne = TSNE(n_components=2, random_state=42, perplexity=30)
X_tsne = tsne.fit_transform(X_scaled)
# 可视化
plt.figure(figsize=(10, 8))
scatter = plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y, cmap='tab10',
s=50, alpha=0.7, edgecolors='black')
plt.xlabel('t-SNE维度1')
plt.ylabel('t-SNE维度2')
plt.title('t-SNE降维后的手写数字数据')
plt.colorbar(scatter, label='数字类别')
plt.grid(True, alpha=0.3)
plt.show()
Isomap
# Isomap降维
isomap = Isomap(n_components=2, n_neighbors=10)
X_isomap = isomap.fit_transform(X_scaled)
# 可视化
plt.figure(figsize=(10, 8))
scatter = plt.scatter(X_isomap[:, 0], X_isomap[:, 1], c=y, cmap='tab10',
s=50, alpha=0.7, edgecolors='black')
plt.xlabel('Isomap维度1')
plt.ylabel('Isomap维度2')
plt.title('Isomap降维后的手写数字数据')
plt.colorbar(scatter, label='数字类别')
plt.grid(True, alpha=0.3)
plt.show()
局部线性嵌入(LLE)
# LLE降维
lle = LocallyLinearEmbedding(n_components=2, n_neighbors=10, random_state=42)
X_lle = lle.fit_transform(X_scaled)
# 可视化
plt.figure(figsize=(10, 8))
scatter = plt.scatter(X_lle[:, 0], X_lle[:, 1], c=y, cmap='tab10',
s=50, alpha=0.7, edgecolors='black')
plt.xlabel('LLE维度1')
plt.ylabel('LLE维度2')
plt.title('LLE降维后的手写数字数据')
plt.colorbar(scatter, label='数字类别')
plt.grid(True, alpha=0.3)
plt.show()
降维方法比较
性能比较
# 比较不同降维方法
methods = {
'PCA': PCA(n_components=2, random_state=42),
't-SNE': TSNE(n_components=2, random_state=42, perplexity=30),
'Isomap': Isomap(n_components=2, n_neighbors=10),
'LLE': LocallyLinearEmbedding(n_components=2, n_neighbors=10, random_state=42)
}
# 可视化比较
fig, axes = plt.subplots(2, 2, figsize=(14, 12))
axes = axes.ravel()
for idx, (name, method) in enumerate(methods.items()):
start_time = time.time()
X_reduced = method.fit_transform(X_scaled)
processing_time = time.time() - start_time
axes[idx].scatter(X_reduced[:, 0], X_reduced[:, 1], c=y, cmap='tab10',
s=50, alpha=0.7, edgecolors='black')
axes[idx].set_title(f'{name} (时间: {processing_time:.2f}秒)')
axes[idx].set_xlabel('维度1')
axes[idx].set_ylabel('维度2')
axes[idx].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
降维对分类性能的影响
# 测试不同维度对分类性能的影响
dimensions = [2, 5, 10, 20, 50, 64]
accuracies_pca = []
accuracies_tsne = []
processing_times_pca = []
processing_times_tsne = []
for n_dim in dimensions:
# PCA
start_time = time.time()
pca = PCA(n_components=n_dim)
X_pca = pca.fit_transform(X_scaled)
processing_times_pca.append(time.time() - start_time)
# 分类
X_train, X_test, y_train, y_test = train_test_split(
X_pca, y, test_size=0.2, random_state=42
)
clf = RandomForestClassifier(n_estimators=100, random_state=42)
clf.fit(X_train, y_train)
accuracies_pca.append(accuracy_score(y_test, clf.predict(X_test)))
# t-SNE(只测试低维,因为t-SNE计算量大)
if n_dim <= 10:
start_time = time.time()
tsne = TSNE(n_components=n_dim, random_state=42, perplexity=30)
X_tsne = tsne.fit_transform(X_scaled)
processing_times_tsne.append(time.time() - start_time)
X_train, X_test, y_train, y_test = train_test_split(
X_tsne, y, test_size=0.2, random_state=42
)
clf.fit(X_train, y_train)
accuracies_tsne.append(accuracy_score(y_test, clf.predict(X_test)))
else:
accuracies_tsne.append(None)
processing_times_tsne.append(None)
# 可视化
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
# 准确率 vs 维度
valid_indices = [i for i, acc in enumerate(accuracies_tsne) if acc is not None]
ax1.plot(dimensions, accuracies_pca, 'b-o', label='PCA', linewidth=2)
ax1.plot([dimensions[i] for i in valid_indices],
[accuracies_tsne[i] for i in valid_indices],
'r-o', label='t-SNE', linewidth=2)
ax1.set_xlabel('降维后的维度')
ax1.set_ylabel('分类准确率')
ax1.set_title('降维维度对分类性能的影响')
ax1.legend()
ax1.grid(True, alpha=0.3)
# 处理时间 vs 维度
valid_indices_time = [i for i, t in enumerate(processing_times_tsne) if t is not None]
ax2.plot(dimensions, processing_times_pca, 'b-o', label='PCA', linewidth=2)
ax2.plot([dimensions[i] for i in valid_indices_time],
[processing_times_tsne[i] for i in valid_indices_time],
'r-o', label='t-SNE', linewidth=2)
ax2.set_xlabel('降维后的维度')
ax2.set_ylabel('处理时间 (秒)')
ax2.set_title('降维维度对处理时间的影响')
ax2.legend()
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
特征选择 vs 特征提取
特征选择方法
from sklearn.feature_selection import SelectKBest, f_classif, RFE
from sklearn.ensemble import RandomForestClassifier
# 选择K个最佳特征
selector = SelectKBest(score_func=f_classif, k=10)
X_selected = selector.fit_transform(X_scaled, y)
# 获取选中的特征
selected_features = np.where(selector.get_support())[0]
print(f"选中的特征索引: {selected_features}")
# 可视化特征重要性
plt.figure(figsize=(12, 6))
scores = selector.scores_
plt.bar(range(len(scores)), scores, color='skyblue', edgecolor='black')
plt.scatter(selected_features, scores[selected_features], c='red', s=100, zorder=5)
plt.xlabel('特征索引')
plt.ylabel('F-score')
plt.title('特征选择:F-score')
plt.grid(True, alpha=0.3)
plt.show()
# 递归特征消除
rfe = RFE(RandomForestClassifier(n_estimators=100, random_state=42),
n_features_to_select=10, step=1)
X_rfe = rfe.fit_transform(X_scaled, y)
# 获取选中的特征
selected_features_rfe = np.where(rfe.get_support())[0]
print(f"RFE选中的特征索引: {selected_features_rfe}")
实际应用
数据可视化
# 使用PCA进行数据可视化
pca_viz = PCA(n_components=2)
X_viz = pca_viz.fit_transform(X_scaled)
# 创建子图比较不同类别的分布
fig, axes = plt.subplots(2, 5, figsize=(15, 6))
axes = axes.ravel()
for i in range(10):
mask = y == i
axes[i].scatter(X_viz[mask, 0], X_viz[mask, 1], alpha=0.6, label=f'数字 {i}')
axes[i].set_title(f'数字 {i}')
axes[i].set_xlabel('PC1')
axes[i].set_ylabel('PC2')
axes[i].grid(True, alpha=0.3)
plt.suptitle('PCA降维后各类别的分布', y=1.02)
plt.tight_layout()
plt.show()
降维最佳实践
- 理解数据:在降维前了解数据特点
- 选择合适方法:线性数据用PCA,非线性数据用t-SNE等
- 评估效果:使用交叉验证评估降维对模型性能的影响
- 可视化验证:通过可视化检查降维效果
- 计算效率:考虑算法的计算复杂度
降维是处理高维数据的重要技术,掌握降维方法对于数据可视化和机器学习建模至关重要。