机器学习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

相关推荐
蓝狐社1 小时前
AI这艘船,谁在划桨,谁在凿洞?
人工智能
GlobalInfo1 小时前
AI与另类数据融合,市场研究正在从“经验驱动”走向“数据智能”
大数据·网络·人工智能·ai
AImoon11.11 小时前
MiniMax H3开源引发视频赛道变局,客易云关注AI模型从生成工具迈向生产力工具
人工智能·音视频
XMAIPC_Robot1 小时前
RK3588+STM32:高性能机器人运动控制解决方案,兼顾实时性与AI算力
人工智能·stm32·嵌入式硬件·算法·fpga开发·机器人·arm+fpga
vivo互联网技术1 小时前
MagicBokeh:单步统一生成式框架实现真实感长焦虚化渲染 | CVPR 2026 Oral
人工智能·算法
新点一讯1 小时前
聚焦流程数智升级,九科信息企业自动化智能体赋能高效运营
大数据·人工智能
Dawson Zhu2 小时前
元宇宙对世界模型发展的启发与借鉴意义——以半导体晶圆厂为例
人工智能
武子康2 小时前
Pi Agent 为什么不内置 MCP:工具发现与上下文成本的真实争议
人工智能·llm·agent
樊小肆2 小时前
# 你还在等DeepSeek官方 agent Harness‌? 来试试 DeepSeeker-Code吧
前端·人工智能·后端