类内散度矩阵用于衡量每个类别内部样本的分布情况:
\[ S_w = \sum_{i=1}^c \sum_{x \in X_i} (x - \mu_i)(x - \mu_i)^T \]
类间散度矩阵则反映各个类别均值相对于整体均值的偏离程度:
\[ S_b = \sum_{i=1}^c n_i(\mu_i - \mu)(\mu_i - \mu)^T \]
其中,\( c \) 表示类别总数,\( X_i \) 是第 \( i \) 类的样本集合,\( \mu_i \) 为该类的均值向量,\( \mu \) 为所有样本的总体均值,\( n_i \) 是第 \( i \) 类的样本数量。
基于这两个矩阵,LDA定义了一个优化目标函数:
\[ J(W) = \frac{W^T S_b W}{W^T S_w W} \]
该函数旨在通过求解广义特征值问题,找到一组最优投影向量 \( W \),使投影后的数据具有最佳可分性。
function [W, projected_data] = lda(X, y, k)
% LDA线性判别分析
% 输入:
% X: 数据矩阵 (n×d),n个样本,d个特征
% y: 标签向量 (n×1)
% k: 目标维度
% 输出:
% W: 投影矩阵 (d×k)
% projected_data: 降维后的数据 (n×k)
[n, d] = size(X);
classes = unique(y);
c = length(classes);
% 计算总体均值
total_mean = mean(X);
% 初始化散度矩阵
S_w = zeros(d, d);
S_b = zeros(d, d);
% 遍历每一类计算类内与类间散度
for i = 1:c
class_idx = (y == classes(i));
X_class = X(class_idx, :);
n_i = sum(class_idx);
% 类内散度
class_mean = mean(X_class);
class_cov = (X_class - class_mean)' * (X_class - class_mean);
S_w = S_w + class_cov;
% 类间散度
mean_diff = (class_mean - total_mean)';
S_b = S_b + n_i * (mean_diff * mean_diff');
end
% 求解广义特征值问题:S_b * W = λ * S_w * W
[eigen_vectors, eigen_values] = eig(S_b, S_w);
% 按特征值降序排列
[~, sort_idx] = sort(diag(eigen_values), 'descend');
eigen_vectors_sorted = eigen_vectors(:, sort_idx);
% 选取前k个最大特征值对应的特征向量
W = eigen_vectors_sorted(:, 1:min(k, c-1));
% 对原始数据进行投影变换
projected_data = X * W;
end
function [W, projected_data, explained] = lda_improved(X, y, k)
% 改进的LDA实现,解决S_w可能奇异的问题
[n, d] = size(X);
classes = unique(y);
c = length(classes);
% 确定最大可行降维维度
max_k = min(d, c-1);
if k > max_k
k = max_k;
warning('目标维度调整为 %d', k);
end
total_mean = mean(X);
S_w = zeros(d, d);
S_b = zeros(d, d);
% 构建类内与类间散度矩阵
for i = 1:c
class_idx = (y == classes(i));
X_class = X(class_idx, :);
n_i = sum(class_idx);
% 使用无偏协方差估计
class_mean = mean(X_class);
class_cov = cov(X_class) * (n_i - 1);
S_w = S_w + class_cov;
% 更新类间散度
mean_diff = (class_mean - total_mean)';
S_b = S_b + n_i * (mean_diff * mean_diff');
end
% 添加微小正则化项防止矩阵奇异
regularization = 1e-6 * eye(d);
S_w = S_w + regularization;
% 利用SVD提高数值稳定性
[U, S, V] = svd(S_w);
% 计算S_w^{-1/2}
sqrt_inv_Sw = V * diag(1./sqrt(diag(S))) * U';
% 转换问题为标准特征值分解
transformed_Sb = sqrt_inv_Sw * S_b * sqrt_inv_Sw;
[eigen_vectors_trans, eigen_values] = eig(transformed_Sb);
% 按特征值从大到小排序
[~, idx] = sort(diag(eigen_values), 'descend');
eigen_vectors_trans = eigen_vectors_trans(:, idx);
% 还原原始空间中的投影方向
W_normalized = sqrt_inv_Sw * eigen_vectors_trans;
% 取前k维作为最终投影矩阵
W = W_normalized(:, 1:k);
% 投影数据
projected_data = X * W;
% 输出解释能力(可选)
explained = diag(eigen_values(idx(1:k)));
explained = explained / sum(explained) * 100;
end
min(特征数, 类别数-1)
[此处为图片2]
% 转换为标准特征值问题 S_b_transformed = S_inv_sqrt' * S_b * S_inv_sqrt; [eigen_vectors, eigen_values] = eig(S_b_transformed); % 对特征值进行降序排列 [eigen_values_sorted, sort_idx] = sort(diag(eigen_values), 'descend'); eigen_vectors_sorted = eigen_vectors(:, sort_idx); % 提取前k个特征向量 W_transformed = eigen_vectors_sorted(:, 1:k); % 将变换后的投影矩阵映射回原始空间 W = S_inv_sqrt * W_transformed; % 执行数据投影 projected_data = X * W; % 计算各分量的方差解释率 explained = eigen_values_sorted(1:k) / sum(eigen_values_sorted) * 100; end3. 可视化示例
function demo_lda() % 演示LDA的实际应用效果 rng(42); % 设定随机种子以确保结果可复现 n_per_class = 50; % 生成三类四维高斯分布数据 X1 = mvnrnd([2, 2, 1, 1], eye(4), n_per_class); X2 = mvnrnd([-1, -1, 2, 2], eye(4), n_per_class); X3 = mvnrnd([0, 3, -1, 0], eye(4), n_per_class); % 合并数据与标签 X = [X1; X2; X3]; y = [ones(n_per_class, 1); 2*ones(n_per_class, 1); 3*ones(n_per_class, 1)]; % 使用改进的LDA方法将数据降至2维 k = 2; [W, projected_data, explained] = lda_improved(X, y, k); % 创建图形窗口用于多子图展示 figure('Position', [100, 100, 1200, 400]); % 展示原始数据中前两个特征维度的分布情况 subplot(1, 3, 1); gscatter(X(:, 1), X(:, 2), y); title('原始数据(前两个维度)'); xlabel('特征1'); ylabel('特征2'); legend('类1', '类2', '类3');% 显示LDA降维后的二维可视化结果 subplot(1, 3, 2); gscatter(projected_data(:, 1), projected_data(:, 2), y); title('LDA降维结果(2维)'); xlabel(sprintf('LDA分量1 (%.1f%%)', explained(1))); ylabel(sprintf('LDA分量2 (%.1f%%)', explained(2))); legend('类1', '类2', '类3'); [此处为图片2] % 展示投影矩阵W的结构(热力图形式) subplot(1, 3, 3); imagesc(W); colorbar; title('投影矩阵 W'); xlabel('LDA分量'); ylabel('原始特征'); [此处为图片3] % 输出实验相关统计信息 fprintf('数据信息:\n'); fprintf(' 样本数: %d\n', size(X, 1)); fprintf(' 原始维度: %d\n', size(X, 2)); fprintf(' 降维后维度: %d\n', k); fprintf(' 类别数: %d\n', length(unique(y))); fprintf('方差解释率:\n'); for i = 1:k fprintf(' 分量%d: %.2f%%\n', i, explained(i)); end endmin(特征数, 类别数-1)4. LDA与PCA的对比分析
function compare_lda_pca() % 对比线性判别分析与主成分分析的降维性能差异 load fisheriris; % 加载鸢尾花数据集 X = meas; y = grp2idx(species); % 将类别转换为索引值 % 对输入数据进行标准化处理 X = zscore(X); % 应用LDA进行二维降维 k = 2; [W_lda, X_lda] = lda_improved(X, y, k); % 应用PCA进行相同维度的降维 [coeff, score, ~, ~, explained_pca] = pca(X); X_pca = score(:, 1:k); % 绘制对比图:左侧为LDA,右侧为PCA figure('Position', [100, 100, 1000, 400]); subplot(1, 2, 1);
function lda_classification_demo()
% LDA在分类问题中的应用演示
% 加载葡萄酒数据集
[wine_data, wine_labels] = load_wine_data();
if isempty(wine_data)
fprintf('无法加载葡萄酒数据集,使用鸢尾花数据集代替\n');
load fisheriris;
X = meas;
y = grp2idx(species);
feature_names = {'SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth'};
else
X = wine_data;
y = wine_labels;
feature_names = {};
end
% 数据标准化处理
X = zscore(X);
% 划分训练集与测试集
rng(123);
cv = cvpartition(y, 'HoldOut', 0.3);
X_train = X(cv.training, :);
y_train = y(cv.training);
X_test = X(cv.test, :);
y_test = y(cv.test);
% 方法一:基于原始特征直接分类
fprintf('方法1: 直接使用原始特征\n');
mdl_direct = fitcdiscr(X_train, y_train);
y_pred_direct = predict(mdl_direct, X_test);
accuracy_direct = sum(y_pred_direct == y_test) / length(y_test);
% 应用PCA进行降维
[X_pca, ~, explained_pca] = pca(X_train);
X_test_pca = (X_test - mean(X_train)) * pca_basis; % 使用训练集的主成分方向变换测试集
% 应用LDA进行降维
[lda_model, X_train_lda] = fitcdiscr(X_train, y_train, 'DiscriminantType', 'linear');
X_train_lda = X_train_lda(:, 1:end-1); % 取线性判别分量
Mu = lda_model.Mu;
W = cov(Mu); % 类均值的协方差作为类间散布估计
V = lda_model.Sigma; % 模型协方差矩阵作为类内散布
[eigvec, ~] = eig(W, V);
X_train_lda = (X_train - lda_model.Mean) * eigvec(:, end-numClasses+2:end); % 投影到LDA空间
X_test_lda = (X_test - lda_model.Mean) * eigvec(:, end-numClasses+2:end);
% 方法二:结合LDA降维后的特征进行分类
fprintf('方法2: 使用LDA降维后的特征\n');
mdl_lda = fitcdiscr(X_train_lda, y_train);
y_pred_lda = predict(mdl_lda, X_test_lda);
accuracy_lda = sum(y_pred_lda == y_test) / length(y_test);
% 方法三:使用PCA降维后分类
fprintf('方法3: 使用PCA降维后的特征\n');
mdl_pca = fitcdiscr(X_train_pca, y_train);
y_pred_pca = predict(mdl_pca, X_test_pca);
accuracy_pca = sum(y_pred_pca == y_test) / length(y_test);
% 输出准确率对比
fprintf('分类准确率比较:\n');
fprintf(' 原始特征: %.2f%%\n', accuracy_direct * 100);
fprintf(' PCA降维后: %.2f%%\n', accuracy_pca * 100);
fprintf(' LDA降维后: %.2f%%\n', accuracy_lda * 100);
% 可视化降维结果(仅适用于前两个分量)
figure;
subplot(1, 2, 1);
gscatter(X_pca(:, 1), X_pca(:, 2), y);
title('PCA降维结果');
xlabel(sprintf('PC1 (%.1f%%)', explained_pca(1)));
ylabel(sprintf('PC2 (%.1f%%)', explained_pca(2)));
legend('Setosa', 'Versicolor', 'Virginica');
min(特征数, 类别数-1)
subplot(1, 2, 2);
gscatter(X_lda(:, 1), X_lda(:, 2), y);
title('LDA降维结果');
xlabel('LDA分量1');
ylabel('LDA分量2');
legend('Setosa', 'Versicolor', 'Virginica');
[此处为图片2]
% 性能评估:计算类间与类内距离比率
fprintf('分类性能评估:\n');
% 计算LDA投影空间中的类间/类内距离比
[~, lda_ratio] = calculate_separation_ratio(X_lda, y);
fprintf('LDA 类间/类内距离比: %.4f\n', lda_ratio);
% 计算PCA投影空间中的对应比率
[~, pca_ratio] = calculate_separation_ratio(X_pca, y);
fprintf('PCA 类间/类内距离比: %.4f\n', pca_ratio);
end
function [separation_ratio, overall_ratio] = calculate_separation_ratio(X, y)
% 计算类间距离与类内距离的比率以评估分离度
classes = unique(y);
c = length(classes);
% 计算总体中心
total_mean = mean(X);
% 初始化类内散度
within_distance = 0;
for i = 1:c
class_idx = (y == classes(i));
X_class = X(class_idx, :);
class_mean = mean(X_class);
% 累加每一类的类内平方误差
within_distance = within_distance + sum(sum((X_class - class_mean).^2, 2));
end
% 初始化类间散度
between_distance = 0;
for i = 1:c
class_idx = (y == classes(i));
n_i = sum(class_idx);
class_mean = mean(X(class_idx, :));
% 累加加权的类间平方距离
between_distance = between_distance + n_i * sum((class_mean - total_mean).^2);
end
% 计算分离比
separation_ratio = between_distance / within_distance;
overall_ratio = separation_ratio;
end
% 方法2: 基于LDA降维的分类处理
k = min(2, length(unique(y_train)) - 1);
[W_lda, X_train_lda] = lda_improved(X_train, y_train, k);
X_test_lda = X_test * W_lda;
mdl_lda = fitcdiscr(X_train_lda, y_train);
y_pred_lda = predict(mdl_lda, X_test_lda);
accuracy_lda = sum(y_pred_lda == y_test) / length(y_test);
fprintf('LDA降维后分类准确率: %.2f%%\n', accuracy_lda * 100);
% 输出原始特征空间下的分类准确率
fprintf('直接分类准确率: %.2f%%\n', accuracy_direct * 100);
% 决策边界可视化(当降维至二维时)
if k == 2
figure('Position', [100, 100, 800, 400]);
subplot(1, 2, 1);
plot_decision_boundary(X_train, y_train, mdl_direct, '原始特征决策边界');
subplot(1, 2, 2);
plot_decision_boundary_lda(X_train_lda, y_train, mdl_lda, 'LDA特征决策边界');
end
end
function plot_decision_boundary_lda(X, y, model, title_str)
% 实现LDA空间中的决策边界绘制
h = 0.02;
x1_min = min(X(:, 1)) - 1; x1_max = max(X(:, 1)) + 1;
x2_min = min(X(:, 2)) - 1; x2_max = max(X(:, 2)) + 1;
[xx1, xx2] = meshgrid(x1_min:h:x1_max, x2_min:h:x2_max);
Z = predict(model, [xx1(:), xx2(:)]);
Z = reshape(Z, size(xx1));
contourf(xx1, xx2, Z, 'Alpha', 0.3);
hold on;
gscatter(X(:, 1), X(:, 2), y);
title(title_str);
xlabel('LDA分量1');
ylabel('LDA分量2');
hold off;
min(特征数, 类别数-1)
| 特性 | 描述 |
|---|---|
| 监督学习 | 依赖类别标签进行投影方向优化 |
| 最大降维数 | 不超过类别数量减一 |
| 优化目标 | 最大化类间散度,同时最小化类内散度 |
| 典型应用场景 | 适用于分类任务与判别性特征提取 |
| 主要优势 | 利用类别信息提升分类可分性 |
| 局限性 | 对数据分布敏感,小样本下可能不稳定 |
扫码加好友,拉您进群



收藏
