ARTICLE DETAIL

资讯详情

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

MATLAB实现ISOMAP等距映射:流形降维方法详解

MATLAB实现ISOMAP等距映射:流形降维方法详解 简介面向高维数据降维与可视化需求等距映射算法是基于流形学习的经典非线性降维方法能有效挖掘嵌入在复杂数据中的低维流形结构弥补主成分分析等线性方法在非线性场景下的不足。这份资源提供了该算法的MATLAB实现适合机器学习、数据挖掘方向的初学者和科研人员使用可直接用于课程实验或项目验证。压缩包整体仅1KB共包含3个m源文件即主算法程序与距离矩阵计算辅助函数代码紧凑、结构清晰便于阅读与二次开发。目前该资源已有402人学习浏览体现了不错的实用性。通过精读这些代码读者可以完整掌握该算法的实现流程依据近邻关系构建邻接图基于最短路径计算测地距离矩阵再应用多维缩放得到低维嵌入坐标同时也能深入理解近邻数等关键参数对降维效果的影响方便后续替换数据、调整算法或扩展到其他流形学习技术。1. 为什么流形学习选 ISOMAP从线性降维到等距映射PCA、MDS 这类线性降维方法拿到一张瑞士卷形状的数据时会不分青红皂白地把两个卷层压在一起投影结果几乎看不出原本的结构。问题出在欧氏距离上三维空间里隔得很近的两个点沿着卷面走可能要绕过半圈这种沿流形量得的距离才是判断样本关系的正确标尺。ISOMAPIsometric Mapping等距映射正是为这类任务设计的流形学习方法。它先构造近邻图用图上的最短路径逼近测地距离再用经典 MDS 将这些距离映射到低维空间从而在降维的同时保持全局几何结构。对形状分析、姿态估计和高维数据可视化来说ISOMAP 是一个容易在 MATLAB 里落地、结果也足够直观的起点。2. ISOMAP 的算法拆解近邻图、测地距离与 MDS 谱分解2.1 等距映射的核心思想用图距离逼近流形测地距离ISOMAP 的思路可以分三步理解。第一步在原始高维空间里找出每个样本的近邻形成一张带权无向图第二步计算图上任意两点之间的最短路径作为测地距离的近似第三步把最短路径矩阵交给经典 MDS得到一组低维坐标使得低维空间里的欧氏距离尽量等于测地距离。整个过程本质上是用图来近似流形再用谱分解来保距。与局部线性嵌入 LLE 只保持局部邻域权重不同ISOMAP 着重保持所有样本对之间的全局距离因此它对展开型流形效果显著而在拓扑结构复杂或有大空洞的数据上容易失真。为什么要绕这么大一圈而不是直接对高维距离做 MDS因为高维空间里的直线距离会横穿流形而测地距离才是流形上的真实位移。比如球面上相距半个大圆的两点三维欧氏距离是直径但沿球面的最短路径是大圆弧二者含义完全不同。ISOMAP 用近邻图上的最短路径去近似这个大圆弧再进入 MDS等价于把流形“铺平”后再保持点间距离。2.2 构建邻域图的两种策略K 近邻与 ε 邻域构建近邻图是 ISOMAP 成败的第一步。常见做法有两种固定近邻数 K选择每个样本最近的 K 个点连边或者固定半径 ε两点距离小于 ε 就连边。两者的取舍可以放进一张表里。策略优点风险适用场景K 近邻每个点至少连接 K 条边局部密度变化不敏感K 太大出现“短路”把流形折叠处误连接K 太小图断裂采样密度不均匀推荐优先尝试ε 邻域几何意义直接边权重不受排序影响对密度差异很敏感稀疏区域容易孤立数据近均匀采样或点密度相近在 MATLAB 里构建 K 近邻图最简单的是用 pdist2 算出距离矩阵再对每一行排序取前 K1 个第一个是自身。也可以用 knnsearch 直接返回邻居索引。我更习惯先保留完整距离矩阵因为后面 MDS 和残差计算还要用到。下面这段代码演示了带权邻接矩阵的构建边权取两点间的欧氏距离随后用 max 做对称化避免两个点之间出现方向不同的两条边。D pdist2(X, X, euclidean); n size(X, 1); A sparse(n, n); for i 1:n [~, idx] sort(D(i, :)); nbrs idx(2:K1); % 去掉自身 A(i, nbrs) D(i, nbrs); end A max(A, A); % 对称化这段代码中X 是 N×D 的样本矩阵K 是近邻数。对每一行排序后取第 2 到第 K1 个索引因为第 1 个是自身距离为 0。用 max 做对称化而不是加法平均是为了防止 A(i,j) 和 A(j,i) 两条边同时存在时最短路径算法把权重算成两倍。如果改用 knnsearch构建会更快但需要额外保存邻居距离整体差别不大。2.3 测地距离矩阵用 graph 对象做最短路径图上最短路径的经典算法是 Floyd-Warshall直接在稠密距离矩阵上迭代但复杂度是 O(n^3)。在 MATLAB 中我一般会改用 graph 对象上的 distances 函数。graph 内部根据稀疏矩阵的密度选择 Dijkstra 或 Johnson 算法在近邻图这种边数远小于 n^2 的稀疏图上速度往往比 Floyd 快一到两个数量级。虽然 Floyd 的写法很优雅但实际数据上跑到一万个样本就会卡到无法忍受。G graph(A); % 从稀疏邻接矩阵创建无向图 Dgeo distances(G); % 返回所有点对最短路径Inf 表示不连通 Dgeo(isinf(Dgeo)) max(Dgeo(~isinf(Dgeo))) * 2;这里把 Inf 替换成一个比最大有效距离大很多的值是工程上的应急处理目的是不让后面 MDS 的双中心化产生 NaN。如果断掉的连通分量较大这种替换本身会引入严重偏差更严格的做法是先提取最大连通分量再在分量上运行 ISOMAP。关于连通分量的处理第 3 章会给出更严谨的替代方案。2.4 经典 MDS 降维与特征值分解拿到测地距离矩阵 Dgeo 后ISOMAP 的收尾工作是经典 MDS。先对平方距离矩阵做双中心化构造 Gram 矩阵 B -1/2 * J * D^2 * J其中 J I - 1/n * 11^T 是中心化矩阵。然后对 B 做特征值分解取前 d 个最大特征值对应的特征向量低维坐标就是特征向量乘以对应特征值的平方根。这样得到的低维空间中点间欧氏距离在最小二乘意义下最优地逼近测地距离。J eye(n) - ones(n) / n; B -0.5 * J * (Dgeo .^ 2) * J; [V, E] eig(B); ev real(diag(E)); [~, idx] sort(ev, descend); Y V(:, idx(1:d)) * diag(sqrt(max(ev(idx(1:d)), 0)));这里使用 eig 而不是 eigs是因为 B 通常都是满秩矩阵eig 能直接拿到全部特征向量当样本数上万时再考虑用 eigs 只求前几个。sqrt(max(...)) 是为了避免负特征值产生复数坐标。测地距离矩阵在噪声下不保证是欧氏距离矩阵所以 B 中会出现负特征值这并不影响前 d 个主要特征向量的有效性但需要截断。3. MATLAB 实现 ISOMAP从零写一个可复用的 isomap 函数3.1 输入输出设计与参数校验直接写一个函数而不是只贴散装代码。接口设计为 [Y, R] isomap(X, k, d)。X 是 N×D 矩阵每个样本一行k 是近邻数d 是目标维度。输出 Y 是 N×d 的低维坐标R 是保距残差用来判断降维质量。参数校验放在函数开头避免后面用到 k 或 d 时产生掩码式错误。n size(X, 1); if k 2 || k n error(k 必须在 [2, n-1] 之间); end if d 1 || d min(n, size(X, 2)) error(d 超出合法范围); end这里把 k 的下界定在 2是因为 k1 时近邻图只是一条条孤立边测地距离与欧氏距离几乎没有区别流形学习失去意义。d 的上界受限于样本数和原始维度毕竟低维坐标最多只能有 min(n, D)-1 个非零特征值。如果你的 MATLAB 版本比较新还可以把这段校验放到 arguments 代码块里但改写成函数后处理报错信息更直观。3.2 完整函数主体近邻图、最短路径与经典 MDS把第 2 章的散装步骤合成一个 isomap.m。完整代码不长核心就是 pdist2、graph/distances 和 eig 三句话。这里特意保留 for 循环构建近邻图是希望你能在断点处观察邻居索引如果追求性能可以把内层替换成 knnsearch。function [Y, R] isomap(X, k, d) n size(X, 1); D pdist2(X, X, euclidean); D(1:n1:end) Inf; % 排除自身 A zeros(n, n); for i 1:n [~, ord] sort(D(i, :)); nbrs ord(1:k); A(i, nbrs) D(i, nbrs); end A max(A, A); G graph(A); Dgeo distances(G); if any(isinf(Dgeo(:))) Dgeo(isinf(Dgeo)) max(Dgeo(isfinite(Dgeo))) * 2; end J eye(n) - ones(n) / n; B -0.5 * J * (Dgeo .^ 2) * J; [V, E] eig(B); ev real(diag(E)); [~, idx] sort(ev, descend); Y V(:, idx(1:d)) * diag(sqrt(max(ev(idx(1:d)), 0))); Ydist pdist(Y, euclidean); dvec Dgeo(tril(true(n), -1)); R 1 - corr(dvec(:), Ydist(:)); if R 0, R 0; end end参数说明X 必须是数值型矩阵缺失值需要提前处理不能带入 pdist2。k 的选择直接影响 A 的边数一般从 min(10, n-1) 附近开始扫描。d 是目标维度通常先设为 2 或 3 做可视化再根据残差曲线调整。corr 来自 Statistics Toolbox如果没有这个工具箱可以自己算皮尔逊相关系数公式是 (x-mean(x))*(y-mean(y)) 除以标准差乘积。用 tril(true(n),-1) 提取 Dgeo 下三角是为了和 pdist 输出的向量顺序对齐避免把矩阵上三角重复算进去。3.3 边界处理不连通图的替换策略与连通分量检查3.2 的代码用 max(finiteVals)*2 替换 Inf能在断图时保住输出不为 NaN但这属于“尽力而为”。更严谨的做法是先检查连通分量如果最大连通分量只覆盖了大部分样本就只在该分量上降维并把孤立样本的坐标置为 0 或 NaN。下面这段代码可以在进入 MDS 之前使用。G graph(A); bins conncomp(G); counts accumarray(bins(:), 1); [~, maxBin] max(counts); mainIdx find(bins maxBin);conncomp 返回每个节点所属分量的编号。max(counts) 定位包含节点最多的分量mainIdx 就是该分量内的样本索引。后续只需要把 isomap 的输入 X 替换成 X(mainIdx, :)算完后重新映射到原图位置。如果多个分量体量接近说明数据本身可以被切成多块独立流形强行用一个低维坐标表示会失真这时可以考虑分簇后分别降维。3.4 特征值分解与残差计算的实现细节经典 MDS 的特征值分解有一个容易被忽略的点B -0.5 * J * D^2 * J 是数值上对称的但由于浮点误差eig 返回的特征值可能有微小的虚部所以代码里用 real 取实部。排序用 sort(ev, descend)取前 d 个之后还要用 max(...,0) 做截断因为负特征值开根号会得到复数。若发现 Y 中出现大量全零列多半是 d 超过了正特征值个数这时需要减小 d。残差 R 1 - corr(测地距离, 低维距离) 是 ISOMAP 经典定义的一个变体。它衡量的是降维前后点对距离的单调相关性R 越接近 0 表示保距效果越好。需要注意corr 对尺度缩放不敏感ISOMAP 本身也只要求相对距离一致因此这个指标比直接算平均绝对误差更适合判断流形展开质量。4. 参数选择与调优K 近邻、特征维度与噪声数据的坑4.1 近邻数 K 对测地线失真的影响K 的选择是 ISOMAP 最敏感的参数。K 太小近邻图可能被拆成多个连通分量测地距离矩阵里出现大量 InfK 太大边缘处原本不相邻的两个卷层会被一条捷径连起来测地距离被严重低估展开结果出现重叠。用 MATLAB 调参时可以做一个扫描例如 K 从 5 递增到 20分别运行 isomap记录残差 R。ks 5:20; res zeros(size(ks)); for i 1:numel(ks) [~, rr] isomap(X, ks(i), 2); res(i) rr; end plot(ks, res, o-); xlabel(K); ylabel(Residual);通常残差随 K 先下降后上升选择平台区的左端点。如果所有 K 下残差都很大说明数据本身不是单一光滑流形或者距离度量不合适。另一个辅助指标是断边比例可以在 distances(G) 后统计 Inf 个数若 K 增大到某一值后断边比例突然归零通常说明图已经连通依然存在较多断边的 K 值不值得尝试。4.2 本征维度估计与残差曲线ISOMAP 的目标维度 d 也不是拍脑袋定的。常见方法是在 d 1:min(10, size(X,2)-1) 内循环计算残差画残差曲线。随着维度增加残差显著下降后进入平台拐点处可以看作本征维度。下面的代码沿用自写 isomap 的 R 输出不需要重新写距离计算。ds 1:10; res zeros(size(ds)); for i 1:numel(ds) [~, res(i)] isomap(X, 12, ds(i)); end plot(ds, res, o-); xlabel(d); ylabel(Residual);除了残差曲线还可以观察 B 的特征值衰减。特征值排序后前几个特征值明显大于其余时拐点同样指示本征维度。但特征值衰减受样本密度影响大残差曲线更接近“重构误差”语义。我一般两个图一起看残差曲线负责选 d特征图负责交叉验证。4.3 噪声数据与预处理为什么 ISOMAP 会“短路”ISOMAP 对噪声敏感的原因在于近邻图只看欧氏距离不区分“沿流形”和“横穿流形”。一个噪声点可能把两个不相邻的流形片层连接起来导致很多点对之间的测地距离被低估。处理噪声的常见做法有三类先降噪再跑 ISOMAP比如对局部邻域做 PCA 平滑增大 K让单个噪声点的影响被周围点稀释或者改用鲁棒距离如对距离矩阵做分位数截断。我一般在数据维度很高时先做一个 PCA 预降维保住 95% 方差再去跑 ISOMAP。这一步对 MATLAB 里的图像特征特别重要能避免噪声主导近邻排序。4.4 ISOMAP 与 PCA、LLE、t-SNE 的适用场景对比做降维选型时很多人问 ISOMAP 和 LLE、t-SNE 有什么区别。直接看这张表方法保持距离类型噪声敏感度是否适合大样本典型输出PCA全局欧氏距离较稳定适合线性主方向ISOMAP全局测地距离敏感中等O(N^2) 内存展开流形的低维坐标LLE局部线性重构权重较敏感中等低维嵌入t-SNE局部概率分布稳定大样本但慢可视化聚类结构ISOMAP 的目标是恢复低维坐标而不是像 t-SNE 那样把聚类结构按社区摊开。如果你的数据有明显的球面或圆柱结构ISOMAP 比 PCA 和 t-SNE 更接近真实内禀坐标。如果数据采样稀疏或者存在多个分量LLE 和 t-SNE 往往更稳。注意 ISOMAP 需要存储 N×N 的距离和最短路径矩阵样本数超过两万时内存会吃紧常见做法是先抽样跑参数再对全量数据用 Nyström 近似。5. 验证降维效果残差、保距误差与 MATLAB 可视化5.1 生成 Swiss Roll 并运行自写 isomapSwiss roll 是 ISOMAP 的标准试金石。在 MATLAB 中可以用下面的代码生成一个充分采样的三维瑞士卷然后调用第 3 章的 isomap 函数。n 1500; t (3 * pi / 2) * (1 2 * rand(n, 1)); h 30 * rand(n, 1); X [t .* cos(t), h, t .* sin(t)]; X (X - min(X)) ./ (max(X) - min(X)); [Y, R] isomap(X, 12, 2); scatter(Y(:,1), Y(:,2), 8, t, filled);这里的 t 变量本身对应瑞士卷展开后的角度方向用它给散点图着色能看出展开后的 Y 是否像一张被裁开的扇形。如果 ISOMAP 工作正常Y 的横轴应该大致沿着 t 的变化方向纵轴对应 h 的方向。如果图上出现明显的卷曲或叠层优先怀疑 K 太大。5.2 残差曲线与类可分性交叉验证残差是判别降维是否有效的一个定量指标但只有残差不够。如果降维后还要用于分类可以用 KNN 交叉验证的准确率来对比不同 K 下的嵌入。MATLAB 里可以用 fitcknn 和 crossval 快速完成但注意 fitcknn 属于 Statistics Toolbox。操作思路是对每个候选 K 运行 isomap 得到 Y把 [Y, labels] 交给 fitcknn然后 crossval 得到损失。这样能得到“降维后信息损失了多少”的实操答案比单纯看残差更贴近业务目标。5.3 常见错误与调试技巧最后放三个最容易踩的坑。第一pdist2 在样本数过万时单是矩阵就有几百 MB建议用分块欧氏距离或先采样一部分做参数探索。第二graph 对象要求邻接矩阵对称且无自环构建后可以用 issymmetric(A) 和 G.numedges 检查边数是否符合预期。第三特征分解后出现复数坐标基本是 Dgeo 里有 NaN 或负特征值没有截断回查 Inf 替换策略。调试时我习惯打印 min(Dgeo(:))、max(Dgeo(:)) 和 sum(isinf(Dgeo(:))) 三个量能快速定位断图和异常距离。本文还有配套的精品资源点击获取
返回列表