局部保局投影(LPP)算法实现

一、算法原理与数学推导

1.1 核心思想

局部保局投影(Locality Preserving Projections, LPP) 是一种线性流形学习方法,旨在保持数据的局部邻域结构。与PCA追求全局方差最大化不同,LPP通过构建邻接图来捕捉数据的局部几何结构,使降维后相近的样本在低维空间中仍然相近。

1.2 数学原理

给定数据集 X=[x1,x2,...,xn]∈RD×nX = [x_1, x_2, ..., x_n] \in \mathbb{R}^{D \times n}X=[x1,x2,...,xn]∈RD×n,LPP寻找投影矩阵 W∈RD×dW \in \mathbb{R}^{D \times d}W∈RD×d,将高维数据映射到低维空间 Y=WTXY = W^T XY=WTX。

目标函数
min⁡W∑i,j(yi−yj)2Sij=min⁡Wtr(WTXLXTW)\min_W \sum_{i,j} (y_i - y_j)^2 S_{ij} = \min_W \text{tr}(W^T X L X^T W)Wmini,j∑(yi−yj)2Sij=Wmintr(WTXLXTW)

约束条件
WTXDXTW=IW^T X D X^T W = IWTXDXTW=I

其中:

  • SSS 是相似度矩阵(邻接矩阵)
  • DDD 是对角矩阵,Dii=∑jSijD_{ii} = \sum_j S_{ij}Dii=∑jSij
  • L=D−SL = D - SL=D−S 是拉普拉斯矩阵

求解 :转化为广义特征值问题 XLXTw=λXDXTwX L X^T w = \lambda X D X^T wXLXTw=λXDXTw,取前 ddd 个最小非零特征值对应的特征向量。


二、MATLAB实现代码

2.1 主程序:lpp_main.m

matlab 复制代码
%% 局部保局投影(LPP)算法完整实现
% 功能:非线性流形学习的线性近似降维

clear; clc; close all;

%% 1. 参数设置
params.D = 100;           % 原始数据维度
params.n = 500;           % 样本数量
params.d = 2;             % 降维后维度
params.k = 5;             % k近邻参数
params.sigma = 1.0;       % 高斯核带宽
params.dataset = 'swiss_roll'; % 数据集类型

fprintf('=== 局部保局投影(LPP)算法 ===\n');
fprintf('原始维度: %d\n', params.D);
fprintf('样本数量: %d\n', params.n);
fprintf('目标维度: %d\n', params.d);
fprintf('k近邻数: %d\n', params.k);
fprintf('高斯核带宽: %.2f\n\n', params.sigma);

%% 2. 生成测试数据
fprintf('生成测试数据...\n');
[X, labels] = generate_test_data(params);

%% 3. 数据预处理
fprintf('数据预处理...\n');
X_norm = normalize_data(X);

%% 4. 构建邻接图
fprintf('构建邻接图...\n');
tic;
[S, D, L] = construct_adjacency_graph(X_norm, params);
graph_time = toc;
fprintf('  邻接图构建完成!耗时: %.3f秒\n', graph_time);

%% 5. 计算LPP投影矩阵
fprintf('计算LPP投影矩阵...\n');
tic;
W = compute_lpp_projection(X_norm, L, D, params.d);
projection_time = toc;
fprintf('  投影矩阵计算完成!耗时: %.3f秒\n', projection_time);

%% 6. 执行降维
fprintf('执行降维...\n');
Y = W' * X_norm;
fprintf('  降维完成!新维度: %d x %d\n', size(Y, 1), size(Y, 2));

%% 7. 性能评估
fprintf('性能评估...\n');
evaluate_lpp_performance(X_norm, Y, S, labels, params);

%% 8. 可视化结果
visualize_lpp_results(X_norm, Y, S, labels, params);

2.2 测试数据生成:generate_test_data.m

matlab 复制代码
function [X, labels] = generate_test_data(params)
    % 生成测试数据集
    
    switch params.dataset
        case 'swiss_roll'
            % Swiss Roll数据集
            fprintf('  生成Swiss Roll数据集...\n');
            t = 3 * pi * rand(params.n, 1);
            height = 21 * rand(params.n, 1);
            X = [t .* cos(t), height, t .* sin(t)]';
            X = X + 0.1 * randn(size(X)); % 添加噪声
            labels = t; % 使用t作为标签
            
        case 'helix'
            % 螺旋数据集
            fprintf('  生成Helix数据集...\n');
            t = linspace(0, 4*pi, params.n)';
            X = [t .* cos(t), t .* sin(t), t]';
            X = X + 0.05 * randn(size(X));
            labels = t;
            
        case 'faces'
            % 人脸数据集(模拟)
            fprintf('  生成人脸数据集...\n');
            X = randn(params.D, params.n);
            % 添加人脸-like结构
            for i = 1:params.n
                angle = 2*pi*i/params.n;
                X(1:20, i) = X(1:20, i) + 0.5 * sin(angle);
                X(21:40, i) = X(21:40, i) + 0.5 * cos(angle);
            end
            labels = ceil(rand(params.n, 1) * 10);
            
        otherwise
            % 随机数据
            fprintf('  生成随机数据集...\n');
            X = randn(params.D, params.n);
            labels = randi(5, params.n, 1);
    end
    
    fprintf('  数据形状: %d x %d\n', size(X, 1), size(X, 2));
end

2.3 数据归一化:normalize_data.m

matlab 复制代码
function X_norm = normalize_data(X)
    % 数据归一化(Z-score标准化)
    
    % 计算均值和标准差
    mu = mean(X, 2);
    sigma = std(X, 0, 2);
    sigma(sigma == 0) = 1; % 避免除以零
    
    % Z-score标准化
    X_norm = (X - mu) ./ sigma;
end

2.4 邻接图构建:construct_adjacency_graph.m

matlab 复制代码
function [S, D, L] = construct_adjacency_graph(X, params)
    % 构建邻接图(k近邻 + 高斯核)
    
    [D_data, n] = size(X);
    
    % 计算欧氏距离矩阵
    fprintf('  计算距离矩阵...\n');
    dist_matrix = pdist2(X', X', 'euclidean');
    
    % 构建k近邻邻接矩阵
    fprintf('  构建k近邻邻接矩阵...\n');
    S = zeros(n, n);
    
    for i = 1:n
        % 找到第i个样本的k个最近邻
        distances = dist_matrix(i, :);
        [~, indices] = sort(distances, 'ascend');
        k_neighbors = indices(2:min(params.k+1, n)); % 排除自身
        
        % 设置邻接关系
        S(i, k_neighbors) = 1;
        S(k_neighbors, i) = 1; % 对称化
    end
    
    % 应用高斯核权重
    fprintf('  应用高斯核权重...\n');
    S = S .* exp(-dist_matrix.^2 / (2 * params.sigma^2));
    
    % 确保对角元素为0(不与自身相连)
    S = S - diag(diag(S));
    
    % 计算度矩阵D
    D = diag(sum(S, 2));
    
    % 计算拉普拉斯矩阵L = D - S
    L = D - S;
    
    fprintf('  邻接图统计:\n');
    fprintf('    非零元素: %d\n', nnz(S));
    fprintf('    稀疏度: %.2f%%\n', nnz(S) / (n*n) * 100);
    fprintf('    平均度数: %.2f\n', mean(sum(S, 2)));
end

2.5 LPP投影矩阵计算:compute_lpp_projection.m

matlab 复制代码
function W = compute_lpp_projection(X, L, D, d)
    % 计算LPP投影矩阵
    
    [D_data, n] = size(X);
    
    % 计算矩阵 XLX^T 和 XDX^T
    fprintf('  计算矩阵乘积...\n');
    XLXT = X * L * X';
    XDXT = X * D * X';
    
    % 添加正则化项防止奇异
    reg_param = 1e-6;
    XDXT = XDXT + reg_param * eye(D_data);
    
    % 求解广义特征值问题: XLX^T w = λ XDX^T w
    fprintf('  求解广义特征值问题...\n');
    [eigenvectors, eigenvalues] = eig(XLXT, XDXT);
    
    % 取前d个最小非零特征值对应的特征向量
    % 注意:特征值可能包含零或负值,需要排序
    eigenvalues = diag(eigenvalues);
    
    % 排序(从小到大)
    [~, idx] = sort(real(eigenvalues));
    
    % 排除零特征值(通常第一个是零)
    non_zero_idx = idx(eigenvalues(idx) > 1e-10);
    
    % 取前d个最小非零特征值对应的特征向量
    if length(non_zero_idx) >= d
        selected_idx = non_zero_idx(1:d);
    else
        selected_idx = non_zero_idx;
        warning('可用特征向量不足,只取%d个', length(selected_idx));
    end
    
    W = eigenvectors(:, selected_idx);
    
    fprintf('  投影矩阵形状: %d x %d\n', size(W, 1), size(W, 2));
    fprintf('  特征值: %s\n', mat2str(eigenvalues(selected_idx)', 3));
end

2.6 性能评估:evaluate_lpp_performance.m

matlab 复制代码
function evaluate_lpp_performance(X, Y, S, labels, params)
    % 评估LPP性能
    
    % 1. 邻域保持率
    fprintf('\n=== LPP性能评估 ===\n');
    neighborhood_preservation = compute_neighborhood_preservation(X, Y, params.k);
    fprintf('邻域保持率: %.2f%%\n', neighborhood_preservation * 100);
    
    % 2. 重构误差
    reconstruction_error = compute_reconstruction_error(X, Y);
    fprintf('重构误差: %.4f\n', reconstruction_error);
    
    % 3. 分类性能(如果有标签)
    if ~isempty(labels)
        classification_accuracy = evaluate_classification_performance(Y', labels);
        fprintf('分类准确率: %.2f%%\n', classification_accuracy * 100);
    end
    
    % 4. 局部结构保持度量
    local_structure_metric = compute_local_structure_metric(X, Y, S);
    fprintf('局部结构保持度量: %.4f\n', local_structure_metric);
    
    % 5. 与PCA比较
    fprintf('\n与PCA比较:\n');
    compare_with_pca(X, Y, labels, params);
end

function preservation = compute_neighborhood_preservation(X, Y, k)
    % 计算邻域保持率
    n = size(X, 2);
    preserved = 0;
    
    for i = 1:n
        % 在高维空间找k近邻
        dist_X = sum((X - X(:, i)).^2, 1);
        [~, idx_X] = sort(dist_X, 'ascend');
        neighbors_X = idx_X(2:min(k+1, n)); % 排除自身
        
        % 在低维空间找k近邻
        dist_Y = sum((Y - Y(:, i)).^2, 1);
        [~, idx_Y] = sort(dist_Y, 'ascend');
        neighbors_Y = idx_Y(2:min(k+1, n));
        
        % 计算重叠率
        overlap = intersect(neighbors_X, neighbors_Y);
        preserved = preserved + length(overlap) / k;
    end
    
    preservation = preserved / n;
end

function error = compute_reconstruction_error(X, Y)
    % 计算重构误差
    % 使用伪逆重构
    W_pinv = pinv(Y);
    X_reconstructed = W_pinv' * Y;
    error = norm(X - X_reconstructed, 'fro') / norm(X, 'fro');
end

function accuracy = evaluate_classification_performance(Y, labels)
    % 评估分类性能
    % 使用简单的KNN分类器
    cv = cvpartition(labels, 'KFold', 5);
    accuracy_sum = 0;
    
    for fold = 1:cv.NumTestSets
        train_idx = training(cv, fold);
        test_idx = test(cv, fold);
        
        train_data = Y(train_idx, :)';
        train_labels = labels(train_idx);
        test_data = Y(test_idx, :)';
        test_labels = labels(test_idx);
        
        % 使用1NN分类器
        predicted_labels = knnclassify(test_data, train_data, train_labels, 1);
        accuracy_sum = accuracy_sum + sum(predicted_labels == test_labels) / length(test_labels);
    end
    
    accuracy = accuracy_sum / cv.NumTestSets;
end

function metric = compute_local_structure_metric(X, Y, S)
    % 计算局部结构保持度量
    n = size(X, 2);
    metric = 0;
    
    for i = 1:n
        neighbors = find(S(i, :) > 0);
        if ~isempty(neighbors)
            % 计算高维和低维空间中邻居间的距离变化
            for j = neighbors
                dist_X = norm(X(:, i) - X(:, j));
                dist_Y = norm(Y(:, i) - Y(:, j));
                metric = metric + abs(dist_X - dist_Y) / dist_X;
            end
        end
    end
    
    metric = metric / nnz(S);
end

function compare_with_pca(X, Y, labels, params)
    % 与PCA比较
    [coeff, score] = pca(X', 'NumComponents', params.d);
    
    % 计算PCA的邻域保持率
    pca_preservation = compute_neighborhood_preservation(X, score', params.k);
    lpp_preservation = compute_neighborhood_preservation(X, Y, params.k);
    
    fprintf('  PCA邻域保持率: %.2f%%\n', pca_preservation * 100);
    fprintf('  LPP邻域保持率: %.2f%%\n', lpp_preservation * 100);
    
    if lpp_preservation > pca_preservation
        fprintf('  LPP在保持局部结构上优于PCA\n');
    else
        fprintf('  PCA在保持局部结构上优于LPP\n');
    end
end

2.7 结果可视化:visualize_lpp_results.m

matlab 复制代码
function visualize_lpp_results(X, Y, S, labels, params)
    % 可视化LPP结果
    
    figure('Name', '局部保局投影(LPP)结果', 'Color', 'white', 'Position', [100, 100, 1400, 800]);
    
    % 1. 原始数据分布(前三维)
    subplot(2,4,1);
    if size(X, 1) >= 3
        scatter3(X(1,:), X(2,:), X(3,:), 10, labels, 'filled');
        xlabel('X1'); ylabel('X2'); zlabel('X3');
        title('原始数据分布(3D)');
        colorbar; grid on;
    else
        scatter(X(1,:), X(2,:), 10, labels, 'filled');
        xlabel('X1'); ylabel('X2');
        title('原始数据分布(2D)');
        colorbar; grid on;
    end
    
    % 2. 邻接图可视化
    subplot(2,4,2);
    % 只显示部分连接以减少混乱
    [i_idx, j_idx] = find(S > 0);
    if length(i_idx) > 1000
        idx = randperm(length(i_idx), 1000);
        i_idx = i_idx(idx);
        j_idx = j_idx(idx);
    end
    
    hold on;
    for k = 1:length(i_idx)
        plot([X(1, i_idx(k)), X(1, j_idx(k))], [X(2, i_idx(k)), X(2, j_idx(k))], 'k-', 'LineWidth', 0.1);
    end
    scatter(X(1,:), X(2,:), 10, labels, 'filled');
    xlabel('X1'); ylabel('X2');
    title('邻接图结构');
    colorbar; grid on;
    hold off;
    
    % 3. LPP降维结果
    subplot(2,4,3);
    if size(Y, 1) >= 2
        scatter(Y(1,:), Y(2,:), 10, labels, 'filled');
        xlabel('LPP1'); ylabel('LPP2');
        title('LPP降维结果(2D)');
        colorbar; grid on;
    else
        plot(Y(1,:), zeros(size(Y, 2), 1), 'o', 'MarkerSize', 6, 'MarkerFaceColor', 'b');
        xlabel('LPP1'); ylabel('');
        title('LPP降维结果(1D)');
        grid on;
    end
    
    % 4. 特征值谱
    subplot(2,4,4);
    % 计算特征值
    D_data = size(X, 1);
    XLXT = X * (diag(sum(S, 2)) - S) * X';
    XDXT = X * diag(sum(S, 2)) * X';
    XDXT = XDXT + 1e-6 * eye(D_data);
    [~, eigenvalues] = eig(XLXT, XDXT);
    eigenvalues = sort(real(diag(eigenvalues)));
    
    plot(1:min(20, length(eigenvalues)), eigenvalues(1:min(20, length(eigenvalues))), 'o-', 'LineWidth', 2);
    xlabel('特征值序号'); ylabel('特征值大小');
    title('LPP特征值谱');
    grid on;
    
    % 5. 与PCA对比
    subplot(2,4,5);
    [~, score] = pca(X', 'NumComponents', 2);
    subplot(1,2,1);
    scatter(score(:,1), score(:,2), 10, labels, 'filled');
    xlabel('PC1'); ylabel('PC2');
    title('PCA降维结果');
    colorbar; grid on;
    
    subplot(1,2,2);
    scatter(Y(1,:), Y(2,:), 10, labels, 'filled');
    xlabel('LPP1'); ylabel('LPP2');
    title('LPP降维结果');
    colorbar; grid on;
    
    % 6. 局部结构保持效果
    subplot(2,4,6);
    % 计算局部结构保持误差
    n = size(X, 2);
    errors = zeros(1, n);
    for i = 1:n
        neighbors = find(S(i, :) > 0);
        if ~isempty(neighbors)
            for j = neighbors
                dist_X = norm(X(:, i) - X(:, j));
                dist_Y = norm(Y(:, i) - Y(:, j));
                errors(i) = errors(i) + abs(dist_X - dist_Y) / dist_X;
            end
            errors(i) = errors(i) / length(neighbors);
        end
    end
    
    histogram(errors, 20, 'FaceColor', 'cyan', 'EdgeColor', 'black');
    xlabel('局部结构保持误差'); ylabel('频数');
    title('局部结构保持效果分布');
    grid on;
    
    % 7. 投影矩阵热力图
    subplot(2,4,7);
    % 计算投影矩阵
    L = diag(sum(S, 2)) - S;
    XLXT = X * L * X';
    XDXT = X * diag(sum(S, 2)) * X';
    XDXT = XDXT + 1e-6 * eye(size(X, 1));
    [W, eigenvalues] = eig(XLXT, XDXT);
    eigenvalues = sort(real(diag(eigenvalues)));
    W_selected = W(:, 1:min(2, size(W, 2)));
    
    imagesc(W_selected);
    colorbar;
    xlabel('投影方向'); ylabel('原始特征');
    title('LPP投影矩阵');
    colormap('jet');
    
    % 8. 算法参数信息
    subplot(2,4,8); axis off;
    param_text = {
        sprintf('原始维度: %d', params.D)
        sprintf('样本数量: %d', params.n)
        sprintf('目标维度: %d', params.d)
        sprintf('k近邻数: %d', params.k)
        sprintf('高斯带宽: %.2f', params.sigma)
        sprintf('数据集: %s', params.dataset)
        sprintf('邻接矩阵非零: %d', nnz(S))
        sprintf('稀疏度: %.2f%%', nnz(S)/(params.n^2)*100)
    };
    text(0.1, 0.9, 'LPP算法参数:', 'FontSize', 12, 'FontWeight', 'bold');
    for i = 1:length(param_text)
        text(0.1, 0.9 - i*0.1, param_text{i}, 'FontSize', 10);
    end
    title('算法配置信息');
end

三、扩展功能模块

3.1 核LPP(Kernel LPP)

matlab 复制代码
function [alpha, Y] = kernel_lpp(X, params)
    % 核局部保局投影(非线性扩展)
    
    % 计算核矩阵
    K = compute_kernel_matrix(X, params.kernel_type, params.sigma);
    
    % 构建核空间邻接图
    [S, D, L] = construct_adjacency_graph_kernel(K, params);
    
    % 求解核LPP
    KLK = K * L * K';
    KDK = K * D * K';
    
    % 添加正则化
    reg_param = 1e-6;
    KDK = KDK + reg_param * eye(size(KDK));
    
    % 广义特征值问题
    [alpha, eigenvalues] = eig(KLK, KDK);
    
    % 取前d个最小非零特征值对应的特征向量
    eigenvalues = sort(real(diag(eigenvalues)));
    non_zero_idx = find(eigenvalues > 1e-10, params.d, 'first');
    alpha = alpha(:, non_zero_idx);
    
    % 计算投影
    Y = K' * alpha;
end

function K = compute_kernel_matrix(X, kernel_type, sigma)
    % 计算核矩阵
    n = size(X, 2);
    K = zeros(n, n);
    
    for i = 1:n
        for j = 1:n
            diff = X(:, i) - X(:, j);
            switch kernel_type
                case 'rbf'
                    K(i, j) = exp(-norm(diff)^2 / (2 * sigma^2));
                case 'polynomial'
                    K(i, j) = (X(:, i)' * X(:, j) + 1)^3;
                otherwise
                    K(i, j) = X(:, i)' * X(:, j);
            end
        end
    end
end

3.2 监督LPP(Supervised LPP)

matlab 复制代码
function W = supervised_lpp(X, labels, params)
    % 监督局部保局投影(利用标签信息)
    
    % 构建有监督的邻接图
    n = size(X, 2);
    S = zeros(n, n);
    
    for i = 1:n
        for j = 1:n
            if labels(i) == labels(j)
                % 同类样本:基于距离构建邻接关系
                dist = norm(X(:, i) - X(:, j));
                S(i, j) = exp(-dist^2 / (2 * params.sigma^2));
            else
                % 异类样本:不连接或弱连接
                S(i, j) = 0.01;
            end
        end
    end
    
    % 对称化
    S = (S + S') / 2;
    
    % 计算度矩阵和拉普拉斯矩阵
    D = diag(sum(S, 2));
    L = D - S;
    
    % 计算LPP投影矩阵
    W = compute_lpp_projection(X, L, D, params.d);
end

参考代码 局部保局投影LPP算法 www.youwenfan.com/contentcsu/60128.html

四、实际应用建议

4.1 参数选择指南

参数 作用 推荐范围 选择方法
k(近邻数) 控制局部结构粒度 5-20 根据数据密度调整
σ(带宽) 控制相似度衰减 0.5-2.0 使用交叉验证
d(目标维度) 降维程度 2-50 根据特征值谱拐点
正则化参数 防止奇异 1e-6到1e-8 保持矩阵可逆

4.2 算法优缺点

优点

  • 线性方法,计算效率高
  • 保持局部流形结构
  • 适用于新样本投影
  • 对非线性结构有效

缺点

  • 对参数敏感
  • 需要构建邻接图(O(n²)复杂度)
  • 可能产生退化解
  • 不适合高度扭曲的流形

4.3 最佳实践

matlab 复制代码
% 1. 参数调优
best_accuracy = 0;
best_params = struct();

for k = 3:2:15
    for sigma = 0.5:0.5:3.0
        params.k = k;
        params.sigma = sigma;
        
        % 运行LPP
        W = compute_lpp_projection(X, L, D, params.d);
        Y = W' * X;
        
        % 评估
        accuracy = evaluate_classification_performance(Y', labels);
        
        if accuracy > best_accuracy
            best_accuracy = accuracy;
            best_params.k = k;
            best_params.sigma = sigma;
        end
    end
end

% 2. 数据预处理的重要性
X_normalized = (X - mean(X, 2)) ./ std(X, 0, 2);  % Z-score标准化
X_whitened = inv(sqrtm(cov(X'))) * X_normalized;   % 白化处理
相关推荐
ShoreKiten2 小时前
cpp考前急救
数据结构·c++·算法
纪伊路上盛名在3 小时前
机器学习中常见的距离度量函数 Distance metrics
人工智能·算法·机器学习·数据分析·统计
sheeta19983 小时前
LeetCode 每日一题笔记 日期:2026.05.07 题目:3660. 找到所有可以到达的最大值
笔记·算法·leetcode
经济元宇宙3 小时前
哪款工业仿真软件上手简单?企业常用款推荐
人工智能·算法
Hesionberger3 小时前
LeetCode79:单词搜索DFS回溯详解
java·开发语言·c++·python·算法·leetcode·c#
纪伊路上盛名在3 小时前
聊一聊关于gene的富集分析
算法·数据分析·统计分析·计算生物·gene
米粒14 小时前
力扣算法刷题 Day 62 最短路算法
算法·leetcode·职场和发展
时空自由民.4 小时前
三个按键的,短按1S,长按3S,单击,双击,三击的检测程序
大数据·数据库·计算机网络·算法
dog2504 小时前
圆锥曲线命题的定义和证明
网络·算法·php