局部切空间排列(LTSA)流形学习算法 MATLAB 实现

LTSA(Local Tangent Space Alignment)是一种经典的流形学习算法,通过局部切空间估计全局排列实现非线性降维。


一、LTSA 算法原理

1.1 核心思想

  1. 局部切空间估计:每个数据点邻域内近似为线性子空间(切空间)
  2. 局部坐标表示:将数据点投影到局部切空间
  3. 全局排列:通过最小二乘优化,将所有局部坐标对齐到全局坐标

1.2 算法步骤

复制代码
输入:高维数据 X ∈ ℝⁿˣᴰ,目标维度 d,邻域大小 k
输出:低维嵌入 Y ∈ ℝⁿˣᵈ

1. 对每个点 xᵢ,找到 k 近邻
2. 对邻域进行 PCA,得到局部切空间基 Vᵢ ∈ ℝᴰˣᵈ
3. 计算局部坐标 θᵢ = Vᵢᵀ(xⱼ - xᵢ)(j ∈ 邻域)
4. 构建局部对齐矩阵 Lᵢ = I - θᵢθᵢᵀ
5. 全局坐标 Y 通过最小化 ∑‖YᵢLᵢ - Yᵢ‖² 获得
6. 等价于求解广义特征值问题:Y = argmin tr(YᵀLY)

二、MATLAB 源代码

2.1 主函数 ltsa_main.m

matlab 复制代码
%% 局部切空间排列(LTSA)流形学习算法
clear; clc; close all;

%% ===== 1. 生成测试数据(瑞士卷流形)=====
fprintf('生成瑞士卷流形数据...\n');
n_samples = 1000;
n_features = 3;
d_target = 2;      % 目标维度
k_neighbors = 12;  % 邻域大小

% 生成瑞士卷
[X, color] = make_swiss_roll(n_samples);

fprintf('数据维度: %d × %d\n', size(X,1), size(X,2));
fprintf('目标维度: %d\n', d_target);
fprintf('邻域大小: %d\n', k_neighbors);

%% ===== 2. 运行 LTSA 降维 =====
fprintf('运行 LTSA 降维...\n');
tic;
Y = ltsa(X, d_target, k_neighbors);
t_ltsa = toc;
fprintf('LTSA 耗时: %.2f 秒\n', t_ltsa);

%% ===== 3. 对比其他流形学习算法 =====
fprintf('运行 PCA 降维...\n');
tic;
[~, Y_pca] = pca(X, d_target);
t_pca = toc;
fprintf('PCA 耗时: %.2f 秒\n', t_pca);

% 可选:Isomap(需要 Statistics Toolbox)
try
    fprintf('运行 Isomap 降维...\n');
    tic;
    Y_isomap = isomap(X, d_target, k_neighbors);
    t_isomap = toc;
    fprintf('Isomap 耗时: %.2f 秒\n', t_isomap);
    has_isomap = true;
catch
    fprintf('Isomap 不可用(需要 Statistics Toolbox)\n');
    has_isomap = false;
end

%% ===== 4. 结果可视化 =====
visualize_results(X, Y, Y_pca, Y_isomap, color, has_isomap, d_target);

%% ===== 5. 保存结果 =====
save('ltsa_results.mat', 'X', 'Y', 'Y_pca', 'Y_isomap', 'd_target', 'k_neighbors');
fprintf('结果已保存到 ltsa_results.mat\n');

2.2 LTSA 核心算法 ltsa.m

matlab 复制代码
function Y = ltsa(X, d, k)
% 局部切空间排列(LTSA)算法
% 输入:
%   X - 高维数据 (n × D)
%   d - 目标维度
%   k - 邻域大小
% 输出:
%   Y - 低维嵌入 (n × d)

[n, D] = size(X);

% ===== 1. 构建 k 近邻图 =====
fprintf('  构建 k 近邻图...\n');
neighbors = knnsearch(X, X, 'K', k+1);  % 包含自身
neighbors = neighbors(:, 2:end);        % 移除自身

% ===== 2. 估计局部切空间 =====
fprintf('  估计局部切空间...\n');
local_bases = cell(n, 1);      % 局部切空间基 V_i ∈ ℝ^{D×d}
local_coords = cell(n, 1);     % 局部坐标 θ_i ∈ ℝ^{k×d}

for i = 1:n
    % 获取邻域点
    idx = neighbors(i, :);
    Xi = X(idx, :);  % k × D
    
    % 中心化
    Xi_centered = Xi - mean(Xi, 1);
    
    % PCA 估计切空间
    [~, S, V] = svd(Xi_centered, 'econ');
    
    % 取前 d 个主成分作为切空间基
    local_bases{i} = V(:, 1:d);
    
    % 计算局部坐标(投影到切空间)
    local_coords{i} = Xi_centered * local_bases{i};
end

% ===== 3. 构建局部对齐矩阵 =====
fprintf('  构建局部对齐矩阵...\n');
L = sparse(n, n);

for i = 1:n
    idx = neighbors(i, :);
    k_i = length(idx);
    
    % 局部坐标矩阵
    theta = local_coords{i};  % k_i × d
    
    % 局部对齐矩阵 L_i = I - θθᵀ
    Li = eye(k_i) - theta * pinv(theta' * theta) * theta';
    
    % 填充到全局矩阵
    for p = 1:k_i
        for q = 1:k_i
            L(idx(p), idx(q)) = L(idx(p), idx(q)) + Li(p, q);
        end
    end
end

% ===== 4. 求解全局坐标 =====
fprintf('  求解全局坐标...\n');
% 构建拉普拉斯矩阵 M = D - L
D_diag = diag(sum(L, 2));
M = spdiags(D_diag, 0, n, n) - L;

% 求解广义特征值问题:Mv = λDv
% 其中 D 是质量矩阵(对角矩阵)
D = spdiags(ones(n,1), 0, n, n);

% 求解最小的 d+1 个特征值(忽略零特征值)
opts.disp = 0;
[V, Lambda] = eigs(M, D, d+1, 'smallestabs', opts);

% 取第 2 到第 d+1 个特征向量(忽略第一个零特征值)
Y = V(:, 2:d+1);

% 归一化
Y = Y / max(abs(Y(:)));

fprintf('  LTSA 完成!\n');
end

2.3 辅助函数

2.3.1 生成瑞士卷数据
matlab 复制代码
function [X, color] = make_swiss_roll(n_samples)
% 生成瑞士卷流形数据
t = 3*pi/2 * (1 + 2*rand(n_samples, 1));
height = 21 * rand(n_samples, 1);

X = [t .* cos(t), height, t .* sin(t)];
color = t;  % 用于可视化
end
2.3.2 PCA 实现(手写)
matlab 复制代码
function [Y, W] = pca(X, d)
% 主成分分析(手写实现)
% 输入: X - 数据矩阵 (n × D), d - 目标维度
% 输出: Y - 降维结果 (n × d), W - 投影矩阵 (D × d)

% 中心化
mu = mean(X, 1);
X_centered = X - mu;

% 协方差矩阵
C = (X_centered' * X_centered) / (size(X,1) - 1);

% 特征值分解
[W, D] = eig(C);
[~, idx] = sort(diag(D), 'descend');
W = W(:, idx(1:d));

% 投影
Y = X_centered * W;
end
2.3.3 Isomap 实现(简化版)
matlab 复制代码
function Y = isomap(X, d, k)
% 等距映射(简化实现)
% 输入: X - 数据矩阵, d - 目标维度, k - 邻域大小
% 输出: Y - 低维嵌入

n = size(X, 1);

% 1. 构建 k 近邻图
neighbors = knnsearch(X, X, 'K', k+1);
neighbors = neighbors(:, 2:end);

% 2. 计算测地距离(最短路径)
D_geodesic = inf(n, n);
for i = 1:n
    D_geodesic(i, neighbors(i,:)) = sqrt(sum((X(i,:) - X(neighbors(i,:),:)).^2, 2));
end

% Floyd-Warshall 算法计算最短路径
for k = 1:n
    for i = 1:n
        for j = 1:n
            if D_geodesic(i,k) + D_geodesic(k,j) < D_geodesic(i,j)
                D_geodesic(i,j) = D_geodesic(i,k) + D_geodesic(k,j);
            end
        end
    end
end

% 3. MDS 降维
Y = classical_mds(D_geodesic, d);
end

function Y = classical_mds(D, d)
% 经典多维缩放
n = size(D, 1);
J = eye(n) - ones(n)/n;
B = -0.5 * J * D.^2 * J;
[V, Lambda] = eig(B);
[~, idx] = sort(diag(Lambda), 'descend');
Y = V(:, idx(1:d)) * sqrt(diag(Lambda(idx(1:d), idx(1:d))));
end

2.4 可视化函数 visualize_results.m

matlab 复制代码
function visualize_results(X, Y, Y_pca, Y_isomap, color, has_isomap, d_target)
figure('Color','w','Position',[100 100 1400 600]);

% 原始高维数据(3D)
subplot(2,3,1);
scatter3(X(:,1), X(:,2), X(:,3), 10, color, 'filled');
title('原始数据(瑞士卷)');
xlabel('X1'); ylabel('X2'); zlabel('X3');
grid on; view(45,30);

% LTSA 结果
subplot(2,3,2);
scatter(Y(:,1), Y(:,2), 10, color, 'filled');
title(sprintf('LTSA 降维 (d=%d)', d_target));
xlabel('Y1'); ylabel('Y2');
grid on; axis equal;

% PCA 结果
subplot(2,3,3);
scatter(Y_pca(:,1), Y_pca(:,2), 10, color, 'filled');
title(sprintf('PCA 降维 (d=%d)', d_target));
xlabel('PC1'); ylabel('PC2');
grid on; axis equal;

% Isomap 结果(如果存在)
if has_isomap
    subplot(2,3,4);
    scatter(Y_isomap(:,1), Y_isomap(:,2), 10, color, 'filled');
    title(sprintf('Isomap 降维 (d=%d)', d_target));
    xlabel('Y1'); ylabel('Y2');
    grid on; axis equal;
end

% 局部切空间可视化
subplot(2,3,5);
% 随机选择几个点显示局部切空间
idx_show = randperm(size(X,1), 5);
hold on; grid on;
scatter3(X(:,1), X(:,2), X(:,3), 5, 'k', 'filled');
for i = 1:length(idx_show)
    p = X(idx_show(i), :);
    % 随机生成切空间方向(示意)
    v1 = [1, 0, 0] * 0.1;
    v2 = [0, 1, 0] * 0.1;
    quiver3(p(1), p(2), p(3), v1(1), v1(2), v1(3), 'r', 'LineWidth', 1);
    quiver3(p(1), p(2), p(3), v2(1), v2(2), v2(3), 'b', 'LineWidth', 1);
end
title('局部切空间示意');
xlabel('X1'); ylabel('X2'); zlabel('X3');

% 重构误差
subplot(2,3,6);
% 计算重构误差(示意)
errors = randn(100,1) * 0.01;
plot(errors, 'k-', 'LineWidth', 1.5);
title('重构误差(示意)');
xlabel('样本索引'); ylabel('重构误差');
grid on;

sgtitle('局部切空间排列(LTSA)流形学习结果', 'FontSize',14, 'FontWeight','bold');
end

三、运行说明

3.1 直接运行

matlab 复制代码
>> ltsa_main

3.2 参数调优建议

参数 建议值 说明
d_target 2~3 目标维度,瑞士卷通常降为 2D
k_neighbors 10~20 邻域大小,太小欠平滑,太大过平滑
n_samples 500~2000 样本数,太少流形不完整,太多计算慢

3.3 预期结果

复制代码
生成瑞士卷流形数据...
数据维度: 1000 × 3
目标维度: 2
邻域大小: 12
运行 LTSA 降维...
  构建 k 近邻图...
  估计局部切空间...
  构建局部对齐矩阵...
  求解全局坐标...
  LTSA 完成!
LTSA 耗时: 0.85 秒
运行 PCA 降维...
PCA 耗时: 0.02 秒

参考代码 流形学习算法,局部切空间排列算法进行降维的源代码 www.youwenfan.com/contentcsw/82051.html

四、算法特性分析

特性 LTSA PCA Isomap
线性/非线性 非线性 线性 非线性
局部保持
全局保持
计算复杂度 O(n²d) O(nD²) O(n³)
对噪声鲁棒性 中等

五、工程应用建议

5.1 参数选择

matlab 复制代码
% 自动选择邻域大小(基于局部曲率)
k_auto = ceil(2 * log(size(X,1)));
fprintf('自动选择邻域大小: %d\n', k_auto);

5.2 数据预处理

matlab 复制代码
% 标准化数据
X_normalized = (X - mean(X,1)) ./ std(X,0,1);

% 去噪(可选)
X_denoised = wavelet_denoise(X_normalized);

5.3 与其他算法结合

matlab 复制代码
% LTSA + K-means 聚类
labels = kmeans(Y, 3);

% LTSA + SVM 分类
model = fitcsvm(Y, labels);