import torch
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
import seaborn as sns
torch.manual_seed(42)
np.random.seed(42)
class PCA:
def __init__(self, n_components=2):
self.n_components = n_components
self.components = None
self.mean = None
self.eigenvalues = None
def fit(self, X):
self.mean = torch.mean(X, dim=0)
X_centered = X - self.mean
n_samples = X.shape[0]
covariance_matrix = torch.matmul(X_centered.T, X_centered) / (n_samples - 1)
eigenvalues, eigenvectors = torch.linalg.eig(covariance_matrix)
self.eigenvalues = eigenvalues.real
eigenvectors = eigenvectors.real
sorted_indices = torch.argsort(self.eigenvalues, descending=True)
top_indices = sorted_indices[:self.n_components]
self.components = eigenvectors[:, top_indices]
def transform(self, X):
X_centered = X - self.mean
return torch.matmul(X_centered, self.components)
def fit_transform(self, X):
self.fit(X)
return self.transform(X)
n_samples = 300
n_features = 10
n_centers = 3
X_np, y_np = make_blobs(n_samples=n_samples, n_features=n_features,
centers=n_centers, random_state=42)
X = torch.tensor(X_np, dtype=torch.float32)
y = torch.tensor(y_np, dtype=torch.int64)
plt.figure(figsize=(15, 5))
plt.subplot(131)
feat1, feat2 = 0, 1
plt.scatter(X[:, feat1], X[:, feat2], c=y, cmap='viridis', alpha=0.7)
plt.title(f'原始数据(特征{feat1+1} vs 特征{feat2+1})', fontsize=12)
plt.xlabel(f'特征 {feat1+1}')
plt.ylabel(f'特征 {feat2+1}')
plt.grid(alpha=0.3)
X_centered = X - X.mean(dim=0)
plt.subplot(132)
plt.scatter(X_centered[:, feat1], X_centered[:, feat2], c=y, cmap='viridis', alpha=0.7)
plt.axhline(y=0, color='r', linestyle='--', alpha=0.5)
plt.axvline(x=0, color='r', linestyle='--', alpha=0.5)
plt.title(f'中心化后数据(特征{feat1+1} vs 特征{feat2+1})', fontsize=12)
plt.xlabel(f'中心化特征 {feat1+1}')
plt.ylabel(f'中心化特征 {feat2+1}')
plt.grid(alpha=0.3)
cov_matrix = torch.matmul(X_centered.T, X_centered) / (X.shape[0] - 1)
plt.subplot(133)
sns.heatmap(cov_matrix.numpy(), annot=False, cmap='coolwarm', fmt='.2f')
plt.title('特征协方差矩阵', fontsize=12)
plt.tight_layout()
plt.show()
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)
plt.figure(figsize=(12, 5))
plt.subplot(121)
sorted_eigenvalues = torch.sort(pca.eigenvalues, descending=True)[0]
plt.bar(range(1, len(sorted_eigenvalues)+1), sorted_eigenvalues.numpy())
plt.title('特征值排序(按降序)', fontsize=12)
plt.xlabel('特征值索引')
plt.ylabel('特征值大小')
plt.grid(axis='y', alpha=0.3)
plt.subplot(122)
explained_variance_ratio = sorted_eigenvalues / torch.sum(sorted_eigenvalues)
cumulative_ratio = torch.cumsum(explained_variance_ratio, dim=0)
plt.plot(range(1, len(cumulative_ratio)+1), cumulative_ratio.numpy(), 'o-', color='orange')
plt.axhline(y=0.9, color='r', linestyle='--', alpha=0.5, label='90%方差解释率')
plt.title('累积方差解释率', fontsize=12)
plt.xlabel('主成分数量')
plt.ylabel('累积解释率')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()
plt.figure(figsize=(10, 8))
scatter = plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y, cmap='viridis',
alpha=0.7, edgecolors='w', s=100)
scale = 5
plt.quiver([0, 0], [0, 0],
pca.components[feat1, 0]*scale, pca.components[feat2, 0]*scale,
color='red', width=0.003, label='第一主成分方向')
plt.quiver([0, 0], [0, 0],
pca.components[feat1, 1]*scale, pca.components[feat2, 1]*scale,
color='blue', width=0.003, label='第二主成分方向')
plt.title('PCA降维结果与主成分方向', fontsize=15)
plt.xlabel('主成分 1')
plt.ylabel('主成分 2')
plt.colorbar(scatter, label='类别')
plt.legend()
plt.grid(True, linestyle='--', alpha=0.7)
plt.show()