All posts

📝笔记:SLAM常见问题(五):Singular Value Decomposition(SVD)分解

(Updated: )

SVD分解就是一种矩阵拆解术,它能够把任意矩阵拆解成3个矩阵的乘积形式,即:

其中,都是正交矩阵,即,即列向量是正交的单位向量,称为left single vectors称为right single vectors的对角阵(奇异值),奇异值且按照顺序降序排列。

MIT Gilbert Strang 教授对 SVD 讲解得很清晰,如下:

此外,Barry讲解的SVD也很精彩。

刚才说了矩阵的形式,视频中还提到了这三个矩阵的物理意义,即SVD分解可以理解为:任意矩阵都可以分解为**(rotation)*(Stretch)*(rotation)**的形式。接下来说明一下这三个矩阵是如何来的。

形式: skinny SVD 与 full SVD

一张图可以解释了,上面介绍的SVD形式为skinny SVD 或者 thin SVD,即仅保留了奇异值矩阵 的非零元素所在的部分,所以它的形状是个方阵。而full SVD保留了奇异值矩阵的非零部分,对应的 为方阵。

通常情况下,skinny SVD使用较多。

此外,还有向量表示形式,如上图最后一行的表示形式,此处不做赘述。

计算

可见,正是矩阵的特征向量,而为矩阵的特征值。

计算

可见,正是矩阵的特征向量,而为矩阵的特征值。

所以都可以通过上述方式来计算。

降维

实战

数字例子

有矩阵A,对其进行SVD分解,已知:

其中,将中主对角线为0的部分删去,同样的对应的部分删去,SVD分解就变成了下图的形式。

计算以及

对以上两式做特征值分解得到:

奇异值:

这与直接调用svd(A)结果是一致的(可能差个正负号)。

最小二乘 least square问题

MIT Gilbert Strang 教授介绍了4种方式来求解最小二乘问题。

问题:,求解

通常的解法如下:

V =
    0.4269    0.5222    0.1760   -0.5292   -0.4839
    0.4087   -0.1757   -0.0655   -0.5258    0.7221
    0.2100   -0.4474   -0.7536   -0.1512   -0.4062
    0.7389    0.1520   -0.0603    0.6481    0.0853
    0.2464   -0.6879    0.6271   -0.0258   -0.2687
U =
    0.5095    0.7999    0.3171
    0.4285    0.0838   -0.8997
    0.7462   -0.5942    0.3001

其中称为的伪逆,即

我们对A进行SVD分解,即 ,则

其中。上面使用到了矩阵求逆交换律:以及的正交矩阵的性质。

所以以上最小二乘的解为:

我们看一下最小二乘的error,定义 的估计值为 ,它的具体形式如下:

所以,表示投影矩阵,它将投影到了的列空间的线性组合,或者说是值域即。所以:

就是 上的正交投影

再看一下error:

由于所奇异矩阵的full形式为,则有:

进而最小二乘的误差为:

参考视频《Solving the Least-Squares Problem Using Geometry》

矩阵近似

祭上亲爱的Battle Angel Alita。

原始图像尺寸,我们可以对该图像做SVD分解,然后仅保留奇异值的前10,50,100重构图像,比较重构图像与原始图像的质量差异。可见仅仅保留其前10个奇异值时,图像质量遭到了极大破坏(此时仅保留原始图像信息的58.864%),随着奇异值数量的增多,图像质量也会逐渐提升,可以看到当奇异值个数为100时,基本上已经看不出与原图的差异(此时仅保留原始图像信息的87.37%)。由此,我们实现了图像压缩。

下图是保留的奇异值数量与图像质量的关系图,保留的奇异值越多,图像质量越高,图像压缩效果越不明显;反之,奇异值越少,图像质量越差,图像压缩效果越明显。这只是一种非常简单的图像压缩算法,仅作原理验证使用,在实际中用到的概率不是很大。

所以:

代码

%% simple test of using SVD decomposistion
clear all;
close all;
clc;

% A = [1 4 3 5 6;
%          2 3 4 5 0;
%          7 4 0 9 1]
% A'*A;
% A*A';
%
% [V,Dv] = eig(A'*A);
%
% lambda = wrev(diag(Dv));
% V = fliplr(V)
%
% [U,Du] = eig(A*A');
%
% lambda = wrev(diag(Du));
% U = fliplr(U)

%% LOADING IMAGE
img = imread('alita_origin.png');

ENERGE = 0;
for i = 1:3
    [U(:,:,i) D(:,:,i) V(:,:,i)] = svd(double(img(:,:,i)))  ;
    ENERGE = ENERGE +sum(diag(D(:,:,i)));
end

%% 10
DIM = 10;
ENERGE10 = 0;
for i = 1:3
    img_recons10(:,:,i) = U(:,1:DIM,i)*D(1:DIM,1:DIM,i)*V(:,1:DIM,i)';
     ENERGE10 = ENERGE10 +sum(diag(D(1:DIM,1:DIM,i)));
end

%% 50
DIM = 50;
ENERGE50 = 0;
for i = 1:3
    img_recons50(:,:,i) = U(:,1:DIM,i)*D(1:DIM,1:DIM,i)*V(:,1:DIM,i)';
     ENERGE50 = ENERGE50 +sum(diag(D(1:DIM,1:DIM,i)));
end

%% 100
DIM = 100;
ENERGE100 = 0;
for i = 1:3
    img_recons100(:,:,i) = U(:,1:DIM,i)*D(1:DIM,1:DIM,i)*V(:,1:DIM,i)';
    ENERGE100 = ENERGE100 +sum(diag(D(1:DIM,1:DIM,i)));
end

figure;
set(gcf,'pos',[ 986 414 1274 826])

FONTSIZE = 15;
h(1) = subplot(221);imshow(mat2gray(img));
xlabel('origin Alita');set(gca,'fontsize',FONTSIZE)

h(2) = subplot(222);imshow(mat2gray(img_recons10));
xlabel(['Using 10 singular values: ' num2str(ENERGE10/ENERGE)]);set(gca,'fontsize',FONTSIZE)

h(3) = subplot(223);imshow(mat2gray(img_recons50));
xlabel(['Using 50 singular values: ' num2str(ENERGE50/ENERGE)]);set(gca,'fontsize',FONTSIZE)

h(4) = subplot(224);imshow(mat2gray(img_recons100));
xlabel(['Using 100 singular values: ' num2str(ENERGE100/ENERGE)]);set(gca,'fontsize',FONTSIZE)
set(gcf,'color',[1 1 1])

%% SHOW ENERGY
ENERGY_tmp = zeros(size(img,1),1);
for DIM_ = 1:size(img,1)
   for i = 1:3
     ENERGY_tmp(DIM_,1) = ENERGY_tmp(DIM_,1) +sum(diag(D(1:DIM_,1:DIM_,i)));
   end
end

figure;
FONTSIZE = 30;
ratio = ENERGY_tmp/ENERGE;
X =   1:size(img,1);
plot(X,ratio,'linewidth',5,'color','r');
set(gcf,'color',[1 1 1])
xlabel('Number of Singular values');
ylabel('Image Quality');
set(gca,'fontsize',FONTSIZE)
set(gcf,'pos',[ 986 414 1274 826])

参考