当前位置: 首页 > news >正文

别再死记硬背公式了!用Python手撸LDA,从随机数据降维到分类实战

用Python从零实现LDA:可视化降维与分类实战

当你第一次接触线性判别分析(LDA)时,是否曾被那些数学公式和理论概念弄得头晕目眩?本文将以一种全新的方式带你理解LDA——不是通过死记硬背公式,而是通过动手实现每一行代码,并亲眼见证数据如何被神奇地降维。我们将从随机生成的数据开始,一步步构建完整的LDA实现,最终将其应用于一个简单的分类任务。

1. 理解LDA的核心思想

LDA的核心目标可以用一个简单的比喻来理解:想象你在一个拥挤的派对上,想要把不同群体的客人分开。LDA就像是一个聪明的派对策划者,它会找到最佳的角度来安排座位,使得同一群体的客人坐得尽可能近,而不同群体的客人则尽可能远离。

从数学角度看,LDA追求两个关键目标:

  1. 最小化类内散度:同一类别数据点在投影后的方差要小
  2. 最大化类间散度:不同类别数据点投影后的均值差异要大

这两个目标可以量化为以下数学表达式:

J(w) = (wᵀS_B w) / (wᵀS_W w)

其中:

  • S_B是类间散度矩阵
  • S_W是类内散度矩阵
  • w是我们寻找的投影方向

2. 准备实验数据

让我们从生成一些随机数据开始。我们将创建两个类别的二维数据点,每个类别有100个样本:

import numpy as np import matplotlib.pyplot as plt # 设置随机种子保证结果可复现 np.random.seed(42) # 生成类别1的数据(均值为[1,1],协方差矩阵为[[2,0.5],[0.5,1]]) class1 = np.random.multivariate_normal(mean=[1,1], cov=[[2,0.5],[0.5,1]], size=100) # 生成类别2的数据(均值为[5,5],协方差矩阵为[[1,-0.5],[-0.5,2]]) class2 = np.random.multivariate_normal(mean=[5,5], cov=[[1,-0.5],[-0.5,2]], size=100) # 合并数据并创建标签 X = np.vstack((class1, class2)) y = np.array([0]*100 + [1]*100) # 可视化原始数据 plt.figure(figsize=(10,6)) plt.scatter(class1[:,0], class1[:,1], color='blue', label='Class 0') plt.scatter(class2[:,0], class2[:,1], color='red', label='Class 1') plt.title('Original Data Distribution') plt.xlabel('Feature 1') plt.ylabel('Feature 2') plt.legend() plt.grid(True) plt.show()

运行这段代码,你会看到两个类别的数据点在二维空间中的分布情况。这是我们LDA算法将要处理的"原材料"。

3. 实现LDA算法

现在,让我们一步步实现LDA算法。我们将把整个过程分解为几个清晰的步骤:

  1. 计算各类别均值中心
  2. 计算类内散度矩阵
  3. 计算类间散度矩阵
  4. 求解最优投影方向
  5. 将数据投影到新空间

以下是完整的LDA实现代码:

from numpy.linalg import inv def lda_implementation(X, y): """ 实现线性判别分析(LDA) 参数: X -- 输入数据矩阵 (n_samples, n_features) y -- 类别标签 (n_samples,) 返回: X_new -- 降维后的数据 (n_samples, 1) w -- 投影方向向量 (n_features, 1) """ # 将数据按类别分开 class1 = X[y == 0] class2 = X[y == 1] # 计算各类别均值 mean1 = np.mean(class1, axis=0) mean2 = np.mean(class2, axis=0) # 计算类内散度矩阵Sw # 类1的协方差矩阵 cov1 = np.cov(class1, rowvar=False) # 类2的协方差矩阵 cov2 = np.cov(class2, rowvar=False) # 类内散度矩阵 Sw = cov1 + cov2 # 计算类间散度矩阵Sb mean_diff = (mean1 - mean2).reshape(-1, 1) Sb = np.dot(mean_diff, mean_diff.T) # 计算最优投影方向w # 求解广义特征值问题:Sb w = λ Sw w # 等价于求解 Sw^{-1} Sb w = λ w # 由于Sb是外积矩阵,rank=1,所以唯一有意义的解是w = Sw^{-1} (mean1 - mean2) w = np.dot(inv(Sw), (mean1 - mean2)) w = w.reshape(-1, 1) # 对数据进行投影 X_new = np.dot(X, w) return X_new, w

注意:在实际应用中,我们通常会先对Sw进行正则化处理(如添加一个小的对角矩阵)以避免数值不稳定的情况。

4. 应用LDA并可视化结果

现在,让我们应用我们实现的LDA算法,并可视化降维前后的数据分布:

# 应用LDA X_lda, w = lda_implementation(X, y) # 可视化投影方向 plt.figure(figsize=(10,6)) plt.scatter(class1[:,0], class1[:,1], color='blue', label='Class 0') plt.scatter(class2[:,0], class2[:,1], color='red', label='Class 1') # 绘制投影方向 origin = np.array([[0, 0],[0, 0]]) # origin point plt.quiver(*origin, w[0], w[1], color=['green'], scale=10, label='Projection Direction') plt.title('Original Data with LDA Projection Direction') plt.xlabel('Feature 1') plt.ylabel('Feature 2') plt.legend() plt.grid(True) plt.show() # 可视化降维后的数据 plt.figure(figsize=(8,6)) plt.scatter(X_lda[y==0], np.zeros_like(X_lda[y==0]), color='blue', label='Class 0') plt.scatter(X_lda[y==1], np.zeros_like(X_lda[y==1]), color='red', label='Class 1') plt.title('Data after LDA Projection') plt.xlabel('Projected Value') plt.yticks([]) plt.legend() plt.grid(True) plt.show()

从可视化结果中,你可以清晰地看到:

  1. 在原始数据图中,绿色箭头表示LDA找到的最佳投影方向
  2. 在降维后的图中,两个类别的数据点在一维空间上的分布明显分离

5. LDA在分类任务中的应用

LDA不仅可用于降维,还可以直接用于分类。让我们看看如何使用LDA投影后的特征构建一个简单的分类器:

from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 在训练集上计算LDA投影方向 X_train_lda, w = lda_implementation(X_train, y_train) # 计算两个类别在投影空间中的均值 class1_proj = X_train_lda[y_train == 0] class2_proj = X_train_lda[y_train == 1] mean1_proj = np.mean(class1_proj) mean2_proj = np.mean(class2_proj) # 计算分类阈值(两个类别均值的中间点) threshold = (mean1_proj + mean2_proj) / 2 # 在测试集上进行投影和分类 X_test_lda = np.dot(X_test, w) y_pred = (X_test_lda > threshold).astype(int).flatten() # 计算分类准确率 accuracy = accuracy_score(y_test, y_pred) print(f"Classification accuracy: {accuracy:.2f}")

这个简单的分类器基于一个直观的想法:在投影后的一维空间中,我们可以找到一个阈值(通常是两个类别均值的中间点)来区分两个类别。尽管简单,但这种方法的分类效果往往出人意料地好。

6. LDA与PCA的对比

为了更深入理解LDA的特性,让我们将其与主成分分析(PCA)进行对比。PCA是另一种常用的降维技术,但它与LDA有着本质的不同:

特性LDAPCA
目标最大化类别可分性最大化数据方差
监督性有监督(使用类别标签)无监督
适用场景分类任务的特征提取通用降维和可视化
数学基础类间和类内散度矩阵协方差矩阵的特征分解

让我们用代码实现PCA并比较两者的结果:

from sklearn.decomposition import PCA # 应用PCA pca = PCA(n_components=1) X_pca = pca.fit_transform(X) # 可视化PCA和LDA的结果对比 plt.figure(figsize=(12,5)) plt.subplot(1,2,1) plt.scatter(X_pca[y==0], np.zeros_like(X_pca[y==0]), color='blue', label='Class 0') plt.scatter(X_pca[y==1], np.zeros_like(X_pca[y==1]), color='red', label='Class 1') plt.title('Data after PCA Projection') plt.xlabel('Projected Value') plt.yticks([]) plt.legend() plt.grid(True) plt.subplot(1,2,2) plt.scatter(X_lda[y==0], np.zeros_like(X_lda[y==0]), color='blue', label='Class 0') plt.scatter(X_lda[y==1], np.zeros_like(X_lda[y==1]), color='red', label='Class 1') plt.title('Data after LDA Projection') plt.xlabel('Projected Value') plt.yticks([]) plt.legend() plt.grid(True) plt.tight_layout() plt.show()

从对比图中可以明显看出,LDA在分离两个类别方面做得更好,这正是因为它利用了类别标签信息来寻找最佳投影方向。

7. 处理多类别问题的LDA扩展

我们前面的实现是针对二分类问题的。LDA也可以扩展到多类别情况,主要区别在于类间散度矩阵的计算:

对于C个类别的情况,类间散度矩阵的计算公式为:

S_B = Σ n_i (μ_i - μ)(μ_i - μ)^T

其中:

  • n_i是第i类的样本数
  • μ_i是第i类的均值向量
  • μ是所有数据的全局均值向量

以下是多类别LDA的实现:

def multiclass_lda(X, y, n_components=None): """ 多类别LDA实现 参数: X -- 输入数据矩阵 (n_samples, n_features) y -- 类别标签 (n_samples,) n_components -- 要保留的维度数 返回: X_new -- 降维后的数据 (n_samples, n_components) """ n_samples, n_features = X.shape classes = np.unique(y) n_classes = len(classes) if n_components is None: n_components = min(n_classes - 1, n_features) # 计算全局均值 overall_mean = np.mean(X, axis=0) # 计算类内散度矩阵Sw Sw = np.zeros((n_features, n_features)) for c in classes: X_c = X[y == c] mean_c = np.mean(X_c, axis=0) cov_c = np.cov(X_c, rowvar=False) Sw += cov_c # 计算类间散度矩阵Sb Sb = np.zeros((n_features, n_features)) for c in classes: X_c = X[y == c] n_c = X_c.shape[0] mean_c = np.mean(X_c, axis=0) mean_diff = (mean_c - overall_mean).reshape(-1, 1) Sb += n_c * np.dot(mean_diff, mean_diff.T) # 求解广义特征值问题:Sb w = λ Sw w eigenvalues, eigenvectors = np.linalg.eig(np.dot(inv(Sw), Sb)) # 选择前n_components个最大的特征值对应的特征向量 sorted_indices = np.argsort(eigenvalues)[::-1] W = eigenvectors[:, sorted_indices[:n_components]] # 对数据进行投影 X_new = np.dot(X, W) return X_new

这个实现可以处理任意数量的类别,并允许指定要保留的维度数(最多为类别数减一)。

8. 实际应用中的注意事项

在实际项目中使用LDA时,有几个关键点需要注意:

  1. 数据预处理

    • LDA对特征的尺度敏感,建议先进行标准化
    • 处理异常值,因为它们会影响均值和散度矩阵的计算
  2. 小样本问题

    • 当特征维度高于样本数时,Sw可能不可逆
    • 解决方案包括使用伪逆或添加正则化项
  3. 模型评估

    • 总是使用交叉验证来评估LDA的性能
    • 可视化降维结果可以提供直观的洞察
  4. 与其它技术的结合

    • 可以先使用PCA降维,再用LDA
    • LDA常作为分类器(如朴素贝叶斯)的预处理步骤

以下是一个完整的数据预处理和LDA应用流程示例:

from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline # 创建数据处理和LDA的pipeline lda_pipeline = make_pipeline( StandardScaler(), PCA(n_components=5), # 先降维以避免小样本问题 # 这里可以使用我们实现的multiclass_lda函数 # 但为了示例,我们使用sklearn的LDA # 注意:实际项目中可以直接使用sklearn的LDA实现 ) # 应用pipeline X_processed = lda_pipeline.fit_transform(X, y)
http://www.jsqmd.com/news/875786/

相关文章:

  • 告别Win11桌面图标乱跑或锁死:深入‘任务计划程序’与注册表,一劳永逸设置指南
  • 机器学习力场加速热力学积分:双路径计算离子真实电势
  • 因果中介分析:双机器学习与非参数估计框架解析
  • DFT计算揭示稀土掺杂与异质结协同提升光催化材料性能的微观机制
  • 别再只盯着深度学习!用OpenCV+Python实战传统分水岭算法,5分钟搞定细胞图像分割
  • 量子机器学习安全:NISQ时代数据投毒攻击QUID的威胁与防御
  • 基于SpringBoot的工业设备远程运维台账毕业设计
  • 机器学习势与势能面描述符:高通量筛选固态电解质的新范式
  • 基于情感计算与网络分析:在线健身社区性别化情感表达研究
  • OpenLS-DGF:开源逻辑综合数据集生成框架,赋能EDA机器学习研究
  • 【无人机控制】基于强化学习在无人机中调整PID参数附Matlab代码
  • 信息检索模型在社会科学文献结构化提取中的应用与评估
  • 基于KDTree的机器学习壁面函数:提升CFD复杂流动模拟精度与效率
  • 接口测试的本质是验证系统契约而非连通性
  • 机器学习赋能量子软件测试:基于词袋模型与树模型的不稳定测试检测实践
  • 射电天文数据处理:致密源扣除与系统误差量化实战指南
  • 基于Q-learning算法的机器人迷宫路径规划研究附Matlab代码
  • 从ODE到SDE:随机微分方程建模、时间反转与边界值问题求解
  • 从Python课设到CTF利器:JWT_GUI工具开发复盘与使用避坑全指南
  • 基于特征建模的机器学习算法自适应选择方法与实践
  • 基于柯西-施瓦茨不等式的数据融合边界推断:半参数高效方法
  • 机器学习模型虚假相关性识别与应对:四大评估框架与实战指南
  • 双重稳健估计与渐近置信序列:在线实验中的因果推断与序贯监测
  • MATLAB基于3D FDTD的微带线馈矩形天线分析[用于模拟超宽带脉冲通过线馈矩形天线的传播,以计算微带结构的回波损耗参数]附Matlab代码
  • 使用C#代码在Excel中插入行和列的操作指南
  • OpenRA中稳定获取应用程序目录的C#实践
  • SHAP模型可解释性实战:从博弈论到金融风控应用
  • 纵向数据缺失处理:FIML、TSRE与机器学习方法对比与选择指南
  • 基于SVD/HOSVD与DLinear的流体场高分辨率预测模型解析
  • 算法稳定性与PAC-Bayesian理论:理解机器学习泛化能力的核心工具