一、算法原理与数学推导
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。
目标函数 :
minW∑i,j(yi−yj)2Sij=minWtr(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; % 白化处理