机器学习LDA

** **算法原理** **

LDA线性判别分析( Linear Discriminant Analysis ,一种有监督的降维/分类方法(不是主题模型那个 LDA)。

核心思想

把数据投影到一条(或几条)直线上,使得:

  • 类内 尽量紧(同类点靠近)
  • 类间 尽量远(不同类中心拉开)

二分类时,就是找投影方向 w ,让投影后两类最好分开。

关键公式

两类样本均值 ,类内散度 (notebook 里用两类协方差之和近似):

目标可写成(Fisher 准则):最大化

最优解满足:

与 notebook 代码对应

步骤 代码 含义
按类分组 X0=X[y==0], X1=X[y==1] 两类样本
类内散度 Sw = sigma0 + sigma1 Sw
均值差 mean_diff = u0 - u1 u 0 - u1
w self.w = Sw_.dot(mean_diff) w= S w -1 u 0 - u1 (SVD 求逆)
投影 h = sample.dot(self.w) h= w Tx
分类 y = 1 * (h < 0) 按投影符号判类

直观理解

  1. 算两类中心差:指明"类间该往哪拉开"
  2. S w -1 校正类内形状(方差大的方向少用力)
  3. 得到 w 后投影,再阈值阈值分类

和 PCA 的区别

PCA LDA
是否用标签 否(无监督) 是(有监督)
目标 方差最大 类间大、类内小
典型用途 降维、可视化 分类、有监督降维

Notebook 在鸢尾花两类子集上自实现准确率约 0.85,sklearn 的 LDA 为 1.0(实现细节与阈值阈值方式不同)。

一句话: LDA 用标签找"最能分开两类"的投影方向 w= S w -1 u 0 - u 1,再投影做分类。

** **代码** **

复制代码
import numpy as np

class LDA():
    def __init__(self):
        # 初始化权重矩阵
        self.w = None
        
    # 计算协方差矩阵
    def calc_cov(self, X, Y=None):
        m = X.shape[0]
        # 数据标准化
        X = (X - np.mean(X, axis=0))/np.std(X, axis=0)
        Y = X if Y == None else (Y - np.mean(Y, axis=0))/np.std(Y, axis=0)
        return 1 / m * np.matmul(X.T, Y)
    
    # 对数据进行投影
    def project(self, X, y):
        self.fit(X, y)
        X_projection = X.dot(self.w)
        return X_projection
    
    # LDA拟合过程
    def fit(self, X, y):
        # 按类分组
        X0 = X[y == 0]
        X1 = X[y == 1]

        # 分别计算两类数据自变量的协方差矩阵
        sigma0 = self.calc_cov(X0)
        sigma1 = self.calc_cov(X1)
        # 计算类内散度矩阵
        Sw = sigma0 + sigma1

        # 分别计算两类数据自变量的均值和差
        u0, u1 = np.mean(X0, axis=0), np.mean(X1, axis=0)
        mean_diff = np.atleast_1d(u0 - u1)

        # 对类内散度矩阵进行奇异值分解
        U, S, V = np.linalg.svd(Sw)
        # 计算类内散度矩阵的逆
        Sw_ = np.dot(np.dot(V.T, np.linalg.pinv(np.diag(S))), U.T)
        # 计算w
        self.w = Sw_.dot(mean_diff)

    
    # LDA分类预测
    def predict(self, X):
        y_pred = []
        for sample in X:
            h = sample.dot(self.w)
            y = 1 * (h < 0)
            y_pred.append(y)
        return y_pred

这段 fit 就是在求 最优投影方向 w ,对应 Fisher 准则下的解析解

w= S w -1 u 0 - u1

逐步对应

1. 按类分组

X0 = Xy == 0

X1 = Xy == 1

把有标签样本拆成两类,后面分别算"类内"与"类间"信息

2. 类内散度 Sw

sigma0 = self.calc_cov(X0)

sigma1 = self.calc_cov(X1)

Sw = sigma0 + sigma1

  • :两类各自的协方差(类内分散程度)
  • :总类内散度

大,说明同类点散得开;LDA 希望投影后这类方差尽量小。

3. 类间方向(均值差)

u0, u1 = np.mean(X0, axis=0), np.mean(X1, axis=0)

mean_diff = u0 - u1

指向两类中心连线,是"该把两类拉开"的方向。

(完整类间散度是 ,二分类时最优 同向。)

4.

U, S, V = np.linalg.svd(Sw)

Sw_ = V.T @ pinv(diag(S)) @ U.T

用 SVD 求伪逆,比直接 inv 更稳(Sw 可能接近奇异)。

得到 (代码里的 Sw_)。

5. 得到投影向量(下一段)

self.w = Sw_.dot(mean_diff) # w = S_w^{-1}(u0-u1)

"校正"均值差:

类内方差大的方向会被压低,从而选出 类间大、类内小 的投影方向。

直觉

作用
告诉你两类中心差在哪
按类内形状加权,避免往"本来就很散"的方向猛投
最终投影轴;预测时算 h= w ⊤x ,再按符号/阈值判类

一句话: 拟合 = 算两类协方差得 、算均值差,再解

** **代码** **

复制代码
from sklearn import datasets
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split

data = datasets.load_iris()
X = data.data
y = data.target

** **代码** **

复制代码
X = X[y != 2]
y = y[y != 2]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=41)
print(X_train.shape, X_test.shape, y_train.shape, y_test.shape)

** **输出** **

(80, 4) (20, 4) (80,) (20,)

** **代码** **

复制代码
lda = LDA()
lda.fit(X_train, y_train)
y_pred = lda.predict(X_test)

from sklearn.metrics import accuracy_score
accuracy = accuracy_score(y_test, y_pred)
print(accuracy)

** **输出** **

-0.38776207 1.27520386 -1.697222 -0.09784557

0.85

** **代码** **

复制代码
y_test

** **输出** **

array(0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0)

代码

复制代码
y_pred

** **输出** **

0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1

** **代码** **

复制代码
import matplotlib.pyplot as plt
import matplotlib.cm as cmx
import matplotlib.colors as colors

class Plot():
    def __init__(self): 
        self.cmap = plt.get_cmap('viridis')

    def _transform(self, X, dim):
        covariance = calculate_covariance_matrix(X)
        eigenvalues, eigenvectors = np.linalg.eig(covariance)
        # Sort eigenvalues and eigenvector by largest eigenvalues
        idx = eigenvalues.argsort()[::-1]
        eigenvalues = eigenvalues[idx][:dim]
        eigenvectors = np.atleast_1d(eigenvectors[:, idx])[:, :dim]
        # Project the data onto principal components
        X_transformed = X.dot(eigenvectors)

        return X_transformed


    def plot_regression(self, lines, title, axis_labels=None, mse=None, scatter=None, legend={"type": "lines", "loc": "lower right"}):
        
        if scatter:
            scatter_plots = scatter_labels = []
            for s in scatter:
                scatter_plots += [plt.scatter(s["x"], s["y"], color=s["color"], s=s["size"])]
                scatter_labels += [s["label"]]
            scatter_plots = tuple(scatter_plots)
            scatter_labels = tuple(scatter_labels)

        for l in lines:
            li = plt.plot(l["x"], l["y"], color=s["color"], linewidth=l["width"], label=l["label"])

        if mse:
            plt.suptitle(title)
            plt.title("MSE: %.2f" % mse, fontsize=10)
        else:
            plt.title(title)

        if axis_labels:
            plt.xlabel(axis_labels["x"])
            plt.ylabel(axis_labels["y"])

        if legend["type"] == "lines":
            plt.legend(loc="lower_left")
        elif legend["type"] == "scatter" and scatter:
            plt.legend(scatter_plots, scatter_labels, loc=legend["loc"])

        plt.show()



    # Plot the dataset X and the corresponding labels y in 2D using PCA.
    def plot_in_2d(self, X, y=None, title=None, accuracy=None, legend_labels=None):
        X_transformed = self._transform(X, dim=2)
        x1 = X_transformed[:, 0]
        x2 = X_transformed[:, 1]
        class_distr = []

        y = np.array(y).astype(int)

        colors = [self.cmap(i) for i in np.linspace(0, 1, len(np.unique(y)))]

        # Plot the different class distributions
        for i, l in enumerate(np.unique(y)):
            _x1 = x1[y == l]
            _x2 = x2[y == l]
            _y = y[y == l]
            class_distr.append(plt.scatter(_x1, _x2, color=colors[i]))

        # Plot legend
        if not legend_labels is None: 
            plt.legend(class_distr, legend_labels, loc=1)

        # Plot title
        if title:
            if accuracy:
                perc = 100 * accuracy
                plt.suptitle(title)
                plt.title("Accuracy: %.1f%%" % perc, fontsize=10)
            else:
                plt.title(title)

        # Axis labels
        plt.xlabel('class 1')
        plt.ylabel('class 2')

        plt.show()

** **代码** **

复制代码
Plot().plot_in_2d(X_test, y_pred, title="LDA", accuracy=accuracy)

鸢尾花测试集是 4 维,没法直接画,所以函数里先用 PCA 压到 2 维,再按 LDA 的预测标签 y_pred 上色画散点。

图上元素 含义
点的位置 样本在 PCA 二维平面上的坐标(不是 LDA 投影轴)
点的颜色 LDA 预测 的类别(0 / 1)
标题 Accuracy 这次自实现 LDA 的准确率(约 85%)
轴标签 class 1 / class 2 实际是 PCA 的第 1、2 主成分(命名容易误解)

该怎么理解

  1. 用途:看预测结果在降维平面上是否大致成两团,方便直观检查分类效果。
  2. 不是 LDA 原理图:坐标轴是 PCA,不是 wx ;颜色是预测类,不是真实类。
  3. 和真实标签比:若某点颜色与真标签不符,就是误分类;图本身没标出真标签。

对应代码注释也写了:Plot the dataset X and the corresponding labels y in 2D using PCA.

** **代码** **

复制代码
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
clf = LinearDiscriminantAnalysis()
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(accuracy)

** **输出** **

1.0

相关推荐
程序员无隅8 分钟前
从 Vibe Coding 到可控交付:用 Spec-Driven Development 驾驭 AI 编程 Agent
人工智能·驱动开发
VIP_CQCRE13 分钟前
用 Ace Data Cloud 快速接入 Google Veo:让 AI 视频生成从 Demo 走向生产
人工智能·api·ai视频·acedatacloud·veo
geneculture14 分钟前
《融智学:终结信息定义纷争的时代范式》 拓展分析述评
大数据·人工智能·信息科学·融智学的重要应用·哲学与科学统一性·融智时代(杂志)·比较分析
空堂与归18 分钟前
迁移学习怎么落地?Transformers 库微调实战
人工智能·机器学习·自然语言处理·transformer·迁移学习
啥都想学点的研究生18 分钟前
一篇文章讲清楚:朴素贝叶斯
人工智能·机器学习·概率论
刘同学的 AI学习手记21 分钟前
每日 AI PM 情报 · 2026-08-31
大数据·人工智能
Talordata24 分钟前
從失控的價格爬蟲到可控的數據管線:一個跨境品牌數據團隊的重構手記
大数据·人工智能·ai·数据挖掘
chuntian_tester31 分钟前
测试中的提示词工程
人工智能·测试工具·ai
锋行天下1 小时前
LangChain / LangGraph 生产环境错误处理
人工智能
鹏哥带你干乡墅1 小时前
优选乡墅赋能培训平台如何帮助提升乡村建设?
大数据·人工智能·python