ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

高光谱数据降维:PCA-LDA原理与MATLAB源码解析

高光谱数据降维:PCA-LDA原理与MATLAB源码解析 简介针对高光谱图像波段冗余、分类困难的问题这份资源提供基于PCA-LDA组合策略的MATLAB降维实现。高光谱数据往往包含数百个连续波段直接处理计算开销大且易过拟合PCA负责无监督去噪与主特征提取LDA则利用类别标签寻找最优投影方向二者结合能显著减少光谱数据维度并提升分类精度适合数据分析、机器学习及遥感图像处理的工程师和研究者。资源包为RAR压缩格式体积仅6KB包含8个文件4个M脚本用于主程序、训练与测试环节4个ASV文件为MATLAB自动保存的备份版本便于对照修改和排查问题。已有2212人学习下载。通过运行源码可直观看到降维前后光谱特征的分布变化也能学习到PCA和LDA在高光谱分类中的完整衔接流程对快速开展实验和算法对比具有实用价值。1. 光谱数据降维PCA-LDA组合在高光谱图像里到底解决什么问题高光谱图像最劝退的不是分类模型难选而是数据本身——一条光谱曲线动辄几百个波段一个像元就是一条几百维的向量直接拿去跑分类不只慢还会被大量冗余波段带偏。基于PCA-LDA光谱数据的降维处理核心思路是先用PCA把几百维的光谱压到前十几个主成分上再用LDA在这些主成分里找类别可分性最强的投影方向。这套MATLAB源码包正好覆盖了从PCA降维到LDA训练、测试的完整流程适合做高光谱图像分类、光谱特征提取以及地物识别这类工作的工程师参考。与其自己在MATLAB里从零写特征值分解和判别投影不如先把它跑通再按自己的数据改。2. 理解PCA和LDA的数学边界先搞懂投影方向再调参数很多人拿到源码第一步就改参数但PCA和LDA各自在做什么、它们为什么能组合在一起这些才是调参的前提。2.1 PCA到底压掉的是什么方差、主成分与光谱冗余PCA是一种无监督方法它的输入只有光谱特征矩阵不需要类别标签。它的本质是找到数据方差最大的几个正交方向把原始特征映射到新坐标系上新坐标轴按照方差从大到小排列。方差大的方向保留方差小的方向舍弃这就是降维。在光谱数据里相邻波段往往高度相关——比如植被光谱在红光和红边区域的变化是连续的这些波段携带的信息大量重叠。PCA把这种重叠信息压缩成少数几个主成分第一个主成分通常对应光谱整体反射率的高低第二个主成分往往对应光谱曲线形状的主要差异。实际使用中我发现高光谱数据的前三个主成分常常能解释超过90%的总方差剩下那些维度基本是噪声和冗余。实现上PCA的核心操作是计算协方差矩阵并做特征值分解。MATLAB里通常写成% 假设 X 是 样本数 x 波段数 的光谱矩阵 % 第一步中心化每个波段减去该波段的均值 X_centered X - mean(X, 1); % 第二步计算协方差矩阵并做特征值分解 [coeff, score, latent, ~, explained] pca(X); % coeff : 主成分系数矩阵载荷矩阵每一列是一个主成分方向 % score : 样本在主成分空间的投影坐标行数样本数列数主成分数 % latent : 每个主成分对应的特征值方差贡献量 % explained: 每个主成分解释的方差百分比比如 85.23 表示解释了85.23%这里有一个关键参数保留多少个主成分。常见做法不是写死个数而是看explained的累积贡献率。我一般设个阈值比如保留累积贡献率达到95%的前K个主成分cumsum_explained cumsum(explained); K find(cumsum_explained 95, 1, first); % 找到达到95%贡献率的最小主成分数 X_pca score(:, 1:K); % 取前K个主成分作为降维结果参数说明explained是PCA函数返回的百分比向量cumsum_explained是它的累积和find函数找到第一个不小于95的位置。这里95%是一个经验值如果你的数据噪声大85%就够如果后续LDA分类效果不理想可以提到98%试试。注意PCA对量纲敏感——高光谱不同波段之间数值范围可能差距很大比如近红外波段反射率可能是红波段的几倍如果差异过大建议先做标准化X_std (X - mean(X, 1)) ./ std(X, 0, 1);2.2 LDA的分类本质类间散度与类内散度的博弈LDA和PCA最大的不同在于它是有监督的需要类别标签参与计算。PCA寻找的是数据整体方差最大的方向它不关心这些方向对分类有没有帮助LDA则直接冲着分类去目标是找到一个投影方向让不同类别的样本在这个方向上分得越开越好同类样本聚得越紧越好。数学上LDA要最大化的是类间散度矩阵Between-class scatter matrix和类内散度矩阵Within-class scatter matrix的比值。类间散度衡量各类别中心之间的距离类内散度衡量每个类别内部样本的离散程度。投影方向W要满足% 计算类内散度矩阵 Sw 和类间散度矩阵 Sb % 求解广义特征值问题Sb * W lambda * Sw * W % 取最大特征值对应的特征向量作为投影方向 [V, D] eig(Sb, Sw); % V是特征向量D是对角矩阵存放特征值 % 按特征值从大到小排序取前 C-1 个方向C是类别数 [~, idx] sort(diag(D), descend); W V(:, idx(1:C-1)); % LDA最多能降到类别数减一维LDA的降维结果维度有个硬性上限类别数减一。如果你只有三类地物比如植被、土壤、水体LDA最多只能把数据降到二维。这个边界很多人容易忽略——设降维到5维但总共只有3类程序要么报错要么返回无效结果。在光谱数据里LDA特别适合用来做分类前的特征提取。因为高光谱图像不同地物类别的光谱曲线常常有重叠区域直接拿原始维度做分类分类器容易把这些重叠区域误判LDA找到的方向是专门为区分这些类别设计的投影之后类别之间的分离度会明显提升。2.3 为什么先PCA再LDA降维顺序不是随便排的PCA和LDA的组合顺序是个值得琢磨的事。PCA先把几百维压缩到十几个维度LDA再在这些维度上找分类方向。好处有两点。第一PCA可以去除冗余和噪声让LDA面对的数据更干净。光谱数据里大量波段是线性相关的协方差矩阵可能接近奇异直接对原始数据做LDA会导致类内散度矩阵不可逆广义特征值分解失败或者数值不稳定。先PCA降维之后特征之间基本去相关LDA的数值稳定性会好很多。第二PCA降低了计算开销。几百乘几百的矩阵特征值分解和几十乘几十的矩阵分解耗时完全不是一个量级。特别是高光谱图像如果逐像元处理数据量动辄几十万条光谱计算效率会直接崩。先PCA压到20维以内再做LDA计算量小到可以忽略。不过顺序也有讲究如果先PCA再LDAPCA阶段是完全不看类别标签的这意味着PCA可能丢掉一些方差不大但对分类很关键的维度。在光谱分类场景里这种风险通常可控——因为光谱维度高度冗余方差小的维度大概率也是噪声。但如果你发现LDA分类效果很差可以检查一下被PCA丢弃的维度里是不是有异常的类别区分信息这种情况在波段数少、样本噪声大的数据集上偶尔会出现。3. 拆解MATLAB源码包从主流程到训练测试脚本的调用关系拿到压缩包之后第一件事不是双击运行而是先看文件结构。这份源码里既有.m脚本也有.asv文件搞清楚它们的关系再动手。3.1 文件结构与调用链条源码包里的文件清单如下文件角色说明PCALDAMain.m/PCALDAMain.asv主入口控制整个流程从加载数据开始顺序执行PCA、LDA训练和测试LDATraining.m/LDATraining.asvLDA训练脚本接收PCA降维后的数据计算类内/类间散度矩阵生成投影方向LDATesting.m/LDATesting.asvLDA测试脚本用训练得到的投影矩阵对测试数据做变换并评估分类效果LDA.m/LDA.asv核心算法文件LDA算法的底层实现被训练和测试脚本共同调用调用关系是PCALDAMain.m作为主控先加载光谱数据和标签调用MATLAB自带的pca函数完成降维然后把降维结果分别传给LDATraining.m和LDATesting.m。LDATraining.m内部会调用LDA.m求解投影矩阵LDATesting.m用这个投影矩阵把测试数据映射到LDA空间。这里有个细节.asv是MATLAB编辑器自动保存的备份文件它会在你编辑脚本时每隔几分钟自动生成一份历史版本。源码包里同时存在.m和.asv说明作者在开发过程中保留了自动备份。.asv文件可以直接用文本编辑器打开内容本质上是上一版脚本的文本。如果.m文件被误删或者损坏.asv可以拿来恢复但正常使用时不需要理会它直接运行.m文件即可。3.2 主流程脚本的结构逻辑结合源码文件名和常见的MATLAB工程习惯PCALDAMain.m的流程一般是这样的%% PCALDAMain.m 主流程骨架 % 1. 加载光谱数据 % 假设数据文件包含两个变量 % spectra: 维度 样本数 x 波段数 的光谱反射率矩阵 % labels: 维度 样本数 x 1 的类别标签取值1,2,3... load(spectra_data.mat, spectra, labels); % 2. PCA降维 % 对原始光谱矩阵做中心化并求解主成分 X_centered spectra - mean(spectra, 1); [coeff, score, latent, ~, explained] pca(X_centered); % 按累积贡献率选择主成分个数 K find(cumsum(explained) 95, 1, first); X_pca score(:, 1:K); % 3. 划分训练集和测试集 % 常见做法是按比例随机划分训练集占70% rng(42); % 固定随机种子保证结果可复现 train_ratio 0.7; cv cvpartition(labels, HoldOut, 1 - train_ratio); train_idx training(cv); test_idx test(cv); X_train X_pca(train_idx, :); y_train labels(train_idx); X_test X_pca(test_idx, :); y_test labels(test_idx); % 4. LDA训练得到投影矩阵 W W LDATraining(X_train, y_train); % 5. LDA测试投影并对测试样本分类 y_pred LDATesting(X_test, W, X_train, y_train); % 6. 计算并打印分类准确率 accuracy sum(y_pred y_test) / length(y_test) * 100; fprintf(PCA-LDA 分类准确率: %.2f%%\n, accuracy);逻辑说明主脚本的核心顺序是“PCA降维 → 数据划分 → LDA训练 → LDA测试”。第2步PCA输出的score是样本在新坐标系里的坐标取前K列就是降维后的特征。第4步的训练函数需要同时传入特征矩阵和标签因为LDA必须知道每个样本属于哪一类才能计算类间散度。第5步测试时除了测试数据还需要传入训练数据——这是因为LDA分类时要用训练集每个类别的中心点作为参考计算测试样本投影后离哪个类别中心最近。参数说明cvpartition的HoldOut参数控制测试集比例1-train_ratio是留出比例这里训练占70%。rng(42)这行很多人会删掉但我不建议删——随机种子固定后每次跑出来结果一致调试时能确认改动带来的影响而不是被随机划分的波动干扰。3.3 LDA训练脚本里需要关注的两个核心计算LDATraining.m的核心任务就是计算投影矩阵W。这里有两个关键步骤值得单独看。第一步是计算各类别的均值向量和全局均值向量。假设降维后有K个特征、C个类别function W LDATraining(X, labels) % X: 样本数 x 特征数labels: 样本数 x 1 classes unique(labels); % 获取类别列表 C length(classes); % 类别总数 [n_samples, n_features] size(X); % 计算全局均值向量 global_mean mean(X, 1); % 初始化类内散度矩阵和类间散度矩阵 Sw zeros(n_features, n_features); Sb zeros(n_features, n_features); for i 1:C X_class X(labels classes(i), :); % 取出第i类样本 class_mean mean(X_class, 1); % 第i类均值向量 n_class size(X_class, 1); % 第i类样本数 % 类内散度矩阵累加每个类内样本与类均值的协方差 X_centered_class X_class - class_mean; Sw Sw X_centered_class * X_centered_class; % 类间散度矩阵每个类均值与全局均值的差异按样本数加权 mean_diff class_mean - global_mean; Sb Sb n_class * (mean_diff * mean_diff); end % 求解广义特征值问题 Sb * v lambda * Sw * v % 取最大特征值对应的前 C-1 个特征向量 [V, D] eig(Sb, Sw); [~, idx] sort(diag(D), descend); W V(:, idx(1:C-1)); end逻辑说明Sw是类内散度矩阵衡量每个类别内部样本的离散程度它由每个类别内部样本与类均值的差累加得到。Sb是类间散度矩阵衡量各类别中心与全局中心的差异样本越多的类别权重越大。LDA的目标是让Sb方向上的方差大、Sw方向上的方差小广义特征值分解直接给出满足这个条件的投影方向。参数说明eig(Sb, Sw)在MATLAB里计算的是广义特征值问题要求Sw可逆。但如果PCA阶段没有充分降维或者样本量小于特征维度Sw可能奇异这时eig会返回NaN或者Inf你会在W里看到一堆非数值。这是一个非常典型的报错点后面避坑章节会专门讲。3.4 LDA测试脚本投影之后怎么做分类LDATesting.m的工作分两步先用训练得到的W把测试样本投影到LDA空间再用最近邻原则判断类别。最近邻的参照是训练集每个类别的中心在LDA空间的位置。function y_pred LDATesting(X_test, W, X_train, y_train) % 1. 投影测试数据和训练数据到LDA空间 Z_test X_test * W; % 测试数据投影 Z_train X_train * W; % 训练数据投影 % 2. 计算训练集每个类别的中心点 classes unique(y_train); C length(classes); class_centers zeros(C, size(Z_train, 2)); for i 1:C class_centers(i, :) mean(Z_train(y_train classes(i), :), 1); end % 3. 对每个测试样本找距离最近的类别中心作为预测类别 n_test size(Z_test, 1); y_pred zeros(n_test, 1); for i 1:n_test distances sqrt(sum((Z_test(i, :) - class_centers).^2, 2)); [~, nearest] min(distances); y_pred(i) classes(nearest); end end逻辑说明投影之后维度从K变成了C-1类别数减一所以Z_test的列数是类别数减一。最近邻分类里距离度量用的是欧氏距离这是LDA分类最简单的配套方法。如果你想更准确可以把最近邻换成二次判别分析或朴素贝叶斯但最近邻的好处是零参数、不需要额外训练作为baseline足够用。参数说明sqrt(sum((A-B).^2, 2))是对行做欧氏距离计算的MATLAB惯用写法sum(..., 2)表示按行求和结果是每行一个距离值。这个计算在样本量大时会有性能瓶颈一个优化方向是改用pdist2但当前规模下差别不大。4. 把光谱数据跑通PCA-LDA数据格式、参数调整与结果验证源码结构看明白了接下来就是把数据喂进去跑起来。这一章按我的实操经验给出从数据准备到参数调整的完整步骤。4.1 输入数据格式光谱矩阵和标签向量怎么组织这套脚本对数据格式有隐含要求不符合的话第一步就会报错。常见的坑是数据行列搞反。脚本里默认的光谱数据格式是每一行是一个样本一个像元的光谱曲线每一列是一个波段。也就是说如果你有N个像元、M个波段矩阵维度是N x M不是M x N。% 检查数据维度是否正确 % 假设加载了变量 spectra whos spectra % 如果显示 5000 x 200说明有5000个样本、200个波段格式正确 % 如果显示 200 x 5000需要转置spectra spectra;标签向量的格式是N x 1的列向量取值从1开始连续编号。高光谱图像分类里通常会用ENVI或ArcGIS导出样本标签导出的标签可能是0、1、2……注意脚本里unique(labels)会自动识别类别但如果标签里有0或者缺失类别会影响LDA的判断需要确认没有空类别。% 检查标签是否连续且没有缺失类别 classes unique(labels); fprintf(类别数量: %d\n, length(classes)); % 如果类别数量远小于预期检查是不是某些类别没有样本数据加载完成后建议先用size确认矩阵规模再用plot抽查几条光谱曲线确认数据不是全零或者包含异常尖峰figure; plot(spectra(1:10, :)); % 画出前10个样本的光谱曲线 xlabel(波段序号); ylabel(反射率); title(原始光谱数据抽查);这段代码的作用是快速定位数据问题。如果曲线全是直线大概率数据加载错了如果某条曲线出现极端尖峰那个样本可能是个坏像元需要在预处理阶段剔除。4.2 关键参数怎么调贡献率阈值和训练集比例跑通流程之后下一步是调参。PCA-LDA流程里真正需要调的参数就三个别想复杂了。第一个是PCA保留的累积贡献率阈值。我习惯从95%起步看分类效果再上下浮动。如果你发现LDA的投影结果里类别分不开、准确率在七成以下可以试试把阈值提到98%让PCA保留更多细节。反之如果训练集只有几百个样本而波段有上千个95%可能还会保留几十个维度这时候降到90%让LDA面对更小的矩阵反而更稳定。第二个是训练集与测试集的划分比例。源码示例里用的是73这个比例对大多数光谱数据集都适用。样本总量少于几百时可以考虑82甚至91因为测试集太少会导致准确率波动很大——测试集50个样本错5个就是10个百分点。样本量大到几千上万时73完全够用还可以直接用交叉验证替代固定划分后面第6章会展开。第三个是是否做标准化。这个参数新手经常忽略。高光谱原始数据的反射率范围基本在0到1之间不同波段量纲一致所以很多人直接跑PCA也没问题。但如果你用的是辐射亮度数据或者原始DN值数据不同波段的数值范围会差很多这时必须先做标准化再进PCA否则PCA会被数值大的波段主导。% 标准化示例 X_std (spectra - mean(spectra, 1)) ./ std(spectra, 0, 1); % 注意均值和标准差应该只用训练集的统计量计算再应用到测试集 % 严格做法是先划分训练测试再在训练集上算均值和标准差这里有个数据泄漏的细节值得单独提标准化时的均值和标准差应该在训练集上计算然后用同一组参数去标准化测试集。如果对全部数据统一标准化再做划分测试集的信息就泄漏到了训练过程里评估出来的准确率会偏乐观。4.3 运行完成后看什么准确率之外还要验证投影效果跑完PCALDAMain.m之后如果只盯着打印出来的准确率一个数字容易漏掉重要信息。我每次跑完至少要看三个东西。第一是PCA的累积贡献率曲线确认保留的主成分确实携带了足够信息figure; plot(1:length(explained), cumsum(explained), o-); xlabel(主成分数量); ylabel(累积贡献率 (%)); title(PCA 累积贡献率); grid on;第二是LDA投影后的散点图直观确认类别分离情况% 假设只有两类或三类投影后最多2维直接画散点 Z_train X_train * W; figure; gscatter(Z_train(:,1), Z_train(:,2), y_train); xlabel(LDA 维度 1); ylabel(LDA 维度 2); title(训练集 LDA 投影结果); legend(类别 1, 类别 2);如果散点图里不同类别像一团浆糊一样叠在一起准确率低就很好理解问题大概率出在PCA阶段保留了太多噪声维度。如果散点明显分开但准确率还是低那问题出在测试集上——可能是测试集分布和训练集不一致或者测试样本里混入了坏像元。第三是混淆矩阵它会告诉你错在哪两类之间confusionchart(y_test, y_pred); % 这个函数在较新的MATLAB版本里可用直接可视化混淆矩阵混淆矩阵的价值在于两个类别互混和所有错误集中在某个罕见类别上处理策略完全不同。互混说明两个类的光谱太接近需要更多特征错误集中在罕见类别上说明训练样本不平衡LDA的类间散度计算会被样本多的类别主导。5. 高光谱图像PCA-LDA实战排查五个高频翻车点和处置办法这一章全是实操里踩过的坑。每一条都是现场遇到过的不是纸面推演。5.1 现象eig返回的特征值全是NaN或Inf跑LDATraining.m的时候计算完eig(Sb, Sw)发现D矩阵里全是NaN或者InfW矩阵里也全是非数值。程序没报错但后面所有结果都废了。原因基本是两个一是Sw矩阵不可逆奇异eig(Sb, Sw)在MATLAB里要求Sw满秩二是输入数据里有NaNPCA阶段如果原始数据包含缺失值pca函数默认会按行删除可能导致样本数少于特征数。解决先用sum(isnan(spectra(:)))检查有没有缺失值有就填充或剔除。然后检查PCA降维后的维度K是否大于样本数——LDA要求的是特征数不能超过样本数减类别数。一个实用的处理是把PCA的累积贡献率阈值从95%降到90%把K压得更小让Sw满秩。% 如果Sw奇异一个最直接的兜底方案在Sw对角线加一个很小的正则项 Sw_reg Sw 1e-6 * eye(size(Sw)); [V, D] eig(Sb, Sw_reg);这个正则项是岭估计的常见做法1e-6是经验值加得太大投影方向会被带偏加得太小正则效果不明显。我一般从1e-6开始调如果还是奇异就逐步加到1e-4。5.2 现象PCA跑完score比自己写协方差矩阵分解的结果差得远有次我发现代码跑出来的分类准确率比论文里报告的低很多于是拿PCA投影后的数据跟前几个特征向量画图对比发现score和手动分解协方差矩阵得到的结果符号相反、顺序也不一致。原因在于MATLAB的pca函数默认做了内部的数据处理包括中心化和奇异值分解求解方式和教科书里直接eig(cov(X))的写法数值上会略有差异。更重要的是pca函数的输出score是原始数据去均值后与系数矩阵相乘的结果而手动分解时候漏掉了中心化步骤导致符号和量级都对不上。解决不要纠结哪个是“标准答案”。LDA训练需要的是投影后的相对坐标只要数据在PCA空间里有正确的可分性score直接就能用。真正需要检查的是中心化是否匹配——训练和测试阶段必须使用同一个均值向量不能训练时用了mean(X_train)测试时又用mean(X_test)去中心化。% 正确方式在训练集上计算均值测试集用同一均值中心化 mu_train mean(X_train, 1); X_train_centered X_train - mu_train; X_test_centered X_test - mu_train; % 注意用的是训练集的均值5.3 现象训练集准确率接近100%测试集准确率只有六成这种情况最常见也最迷惑人。刚跑通时我看到训练集准确率98%以为模型没有问题回头一测测试集直接掉到62%。当时第一反应是数据有问题反复检查才发现是流程顺序问题。原因在于如果源码在PCA和LDA之前就用全部数据包含测试集参与标准化或者PCA计算那么测试集的信息已经泄漏进了训练过程。PCA是一种无监督方法它根本不需要标签所以很多人会把所有数据一起交出去做PCA再划分训练测试集。这个顺序是有问题的——虽然PCA不依赖标签但它仍然从测试集里提取了方差结构这等于测试集的信息参与了特征构建。解决调整流程顺序先划分训练测试集再把训练集单独做PCA拟合然后用同一条PCA投影路径去映射测试集% 先划分后PCA [train_idx, test_idx] dividerand(size(spectra, 1), 0.7, 0.3); X_train_raw spectra(train_idx, :); X_test_raw spectra(test_idx, :); y_train labels(train_idx); y_test labels(test_idx); % 在训练集上拟合PCA得到投影系数 [coeff_train, score_train, ~, ~, explained_train] pca(X_train_raw - mean(X_train_raw, 1)); % 用训练集的系数矩阵直接投影测试集 mu_train mean(X_train_raw, 1); X_test_centered X_test_raw - mu_train; score_test X_test_centered * coeff_train;从这以后我每次跑新数据都强制走一遍“先划分、后PCA”的流程再也没出现过这种虚高准确率的情况。5.4 现象LDA投影后类别之间总是重叠无论怎么调参都分不开现象是LDA训练完成后画出投影散点图不同类别依然大面积重叠准确率徘徊在五成。调了PCA贡献率阈值、换了标准化方式都没有本质改善。原因我没头绪的时候排查到最后发现是LDA的类别数量和分析目标不匹配。具体来说我的高光谱图像里有5类地物样本但其中两类光谱曲线几乎一样比如两种含水量不同的土壤PCA阶段方差都很小LDA的类间散度矩阵里这两类的均值差异几乎为零。LDA要找的是区分所有类别的最佳方向但这两类本身就不可分投影方向自然会被它们“拖着走”。解决先做类别可分性分析再决定要不要合并类别。常用的工具是计算每个波段上不同类别的均值差异和标准差% 逐波段计算类别可分性指标类间均值差 / 类内标准差 for band 1:size(spectra, 2) band_data spectra(:, band); class_means zeros(length(classes), 1); class_stds zeros(length(classes), 1); for i 1:length(classes) idx labels classes(i); class_means(i) mean(band_data(idx)); class_stds(i) std(band_data(idx)); end separability(band) range(class_means) / mean(class_stds); end figure; plot(separability); xlabel(波段); ylabel(可分性指标);如果某两个类别的光谱曲线在所有波段上都极其接近最务实的做法是把它们合并成一个类别或者剔除不可分样本强行让LDA区分它们只会降低整体准确率。5.5 现象源码包里的.asv文件被直接当成.m文件运行有读者把LDATraining.asv改名成.m放进路径里结果整个工作区变量全乱了——.asv文件是MATLAB自动保存的备份文件内容可能是不完整的中间版本甚至没有完整的函数定义。直接在MATLAB里打开或运行它会报错或者产生不可预期的变量覆盖。原因.asv和.m虽然扩展名只差一个字母但生命周期不同。.asv是编辑过程中周期性生成的临时文件删除后不影响任何功能。解决源码包里.asv文件一律忽略不要改扩展名不要加到MATLAB路径里。如果你需要清理# 在项目根目录执行删除所有 .asv 备份文件 find . -name *.asv -delete在Windows上也可以用资源管理器搜索*.asv然后删掉它们不影响项目运行。6. 验证一套降维方案是否靠谱交叉验证与迁移新数据的检查习惯眨眼间这套PCA-LDA流程已经能稳定跑通了但离“靠谱”还差一步——怎么确认你的结果不是只在这份数据上有效我自己的习惯是每次新数据集至少做三重验证交叉验证代替单次划分、可视化确认投影结构、用小规模样本做敏感性测试。第一重验证是用交叉验证代替固定比例划分。固定73划分有个隐患某次划分恰好把难分类的样本全分到测试集准确率会异常难看或者反过来某次划分把难分类的样本全留在训练集准确率虚高。用5折交叉验证能消除这种波动% 5折交叉验证示例 rng(1); cv cvpartition(labels, KFold, 5); accuracies zeros(cv.NumTestSets, 1); for fold 1:cv.NumTestSets train_idx cv.training(fold); test_idx cv.test(fold); % 在训练集上做PCA X_train spectra(train_idx, :); y_train labels(train_idx); X_test spectra(test_idx, :); y_test labels(test_idx); mu mean(X_train, 1); [coeff, score_train] pca(X_train - mu); score_test (X_test - mu) * coeff; % LDA训练与测试 W LDATraining(score_train, y_train); y_pred LDATesting(score_test, W, score_train, y_train); accuracies(fold) sum(y_pred y_test) / length(y_test) * 100; end fprintf(5折交叉验证平均准确率: %.2f%% ± %.2f%%\n, mean(accuracies), std(accuracies));第二重验证是看投影结构的稳定性。交叉验证结束后把每一折得到的投影矩阵W放在一起对比——如果各折的W方向差异很大说明特征选择不稳定数据里可能存在严重的噪声或类别不平衡。% 把5折得到的投影方向第一列放在一起看 % W_collected 是一个 特征数 x 5 的矩阵每列是一折的投影方向 figure; for i 1:5 plot(W_collected(:, i)); hold on; end xlabel(特征维度); ylabel(投影系数); title(不同折之间的LDA投影方向对比);第三重验证是小样本敏感性测试。高光谱数据标注成本很高有时整个数据集只有几百个样本。我的检查习惯是把训练集从总量里抽取50%、60%、70%、80%分别跑一遍观察准确率变化曲线。如果样本减半导致准确率暴跌超过10个百分点说明数据量已经逼近下限公布结果时必须注明适用范围如果准确率变化平缓说明标注样本冗余后续收集数据可以更精简。这套源的局限也说清楚它解决的是光谱特征降维和分类问题不包含波段选择band selection、混合像元分解等上游处理也不包含各种分类器对比。如果你只是为了快速出个结果它够用如果要做严谨的学术对比建议把它输出的降维特征接到SVM或随机森林上再测一轮看看LDA最近邻的baseline到底处在什么水平。从那次训练测试划分顺序翻车以后我处理任何光谱数据集都强制走一遍“先划分、后PCA、再LDA、最后交叉验证”的流程一秒都不跳过。这套源码本身不算复杂但把流程顺序和边界条件搞清楚它就能从一份能跑的代码变成你手上可靠的工具。希望这篇拆解帮到你。本文还有配套的精品资源点击获取
返回列表