svd

SVD(Singular Value Decomposition),中文是奇异值分解,最近很感兴趣,下面谈谈我对svd的理解,没有线性代数基础的可以直接看应用部分或者记住关键结论就好了。

理论部分:

线性变化

奇异值分解不是一个凭空幻想出来的概念,而是解决一个数学问题的成果,这个数学问题可以概括成:对于∀矩阵A,是否∃一组正交基,在经过A变换之后还是正交的?

在研究一般矩阵之前,我们已经发现了一些特殊矩阵的结果,然后我们想着能否把这些特殊情况推广到一般情况。

特殊矩阵的结果如下:

:(对角矩阵是除了对角线上元素不全为零,其他位置全为零的方阵,)对角矩阵可以对标准基做变换之后得到的还是一组正交基(变换之后的不再是标准的了,他们在各个基上面可能做了伸缩,伸缩的系数用一个对角矩阵表示,对角矩阵的特殊性在于它能对一组最特殊的正交基(标准单位矩阵I:长度为1,且每个基向量只有一个元素为1)做变换。

对称矩阵是对角矩阵在对角线两侧对称位置加上相同元素得到的矩阵,这时候,对称矩阵无法对标准单位矩阵I做变换,使得得到的还是一组正交基,但是我们可以找到一组标准正交基Q,满足对称矩阵的需要

那么对于一般矩阵A,是否存在一组正交基,在进过A变换之后还是正交的?答案是肯定的,

下面给出证明:(A的转置用A'表示)补充一点,是特征值矩阵

给定A=m*n,A'A是对称矩阵,存在A'AQ=Q,也就是说存在一组标准正交基使得A'A满足要求;

取这组正交基的任意两个向量Vi,Vj,有Vi•Vj=0,

那么A'AVj=?jVj(?j是Vj对应的特征向量),两边左乘Vi',

Vi'A'AVj=Vi'?jVj=?jVi'Vj=?jVi•Vj=0,

即是:(AVi)'AVj=0->(AVi)•(AVj)=0

也就是说,∀A,都∃一组正交基使得经过A变换之后还是正交的

我觉得这就是矩阵的线性变化的理论基础,总结一下:

对于对角矩阵和对称矩阵,它可以对向量在自己的空间空做线性变换,使得变换的结果还是落在自己原来的坐标轴上

对于一般矩阵,它可以把任意向量从一个坐标系映射到另一个坐标系上,这就是我对矩阵变换的理解。

奇异值分解

有了上面的结论之后,我们可以做进一步的事情,∀A,都∃一组正交基V使得经过A变换之后还是正交的U,

取V中的任意一个向量Vi,有AVi•AVi=Vi'A'AVi=Vi'?iVi=?iVi'Vi=?iVi•Vi=?i

也就是|AVi|2=?i,=>|AVi|=√?i

取Ui=AVi/|AVi|=AVi/√?i

即:AVi=Ui√?i,这个就是我们问题:∀A,都∃一组正交基V使得经过A变换之后还是正交的U的  数学表示,

而这个就是我们要的奇异值分解的前一步。

令∂i=√?i,∂i是对角矩阵∑的对角位置为i的元素,那么AVi=Ui√?i

AV=U∑,同时右乘V',

AVV'=U∑V',由于V是一组正交基,所以VV'=I

即:A=U∑V'

这就是任意矩阵A的奇异值分解。

 任意矩阵的满秩分解

任意矩阵A=m*n的秩不可能都是n,假设rank(A)=k,那么存在A的满秩分解使得

A=XY,其中X=m*k,Y=k*n,下面盗取一张图

从∂i=√?i我们可以知道,∑中的对角元素是矩阵A'A的特征值的开根号,由于rank(A)=rank(A')=rank(A'A)=k,所以∑中只有k个有效值,如下如所示,对应的V,U也是有m-k和n-k个向量是由拓展而来

更具体一点,V中每个向量是n维,是行空间的基;U中每个向量有m维,是列空间的基;

更进一步,我们可以知道,Vk+1-Vn是行空间的补空间(也就是零空间,也成右零空间)的基,Uk+1-Um-k是列空间的补空间(左零空间)的基。

 实践部分

线性变换:对于∀矩阵A,∃一组正交基,在经过A变换之后还是正交的

为了直观,我们举二维的例子,二维向量的一组基为V1和V2

∀向量记为X=[x,y]'

A是对角矩阵:对角矩阵可以对标准基做变换之后得到的还是一组正交基,例子如下

A=[[1,0],

  [0,3]]

AX=[[1x],  

   [3y]],这个对称矩阵对任意向量的变换的结果仅仅是在y方向上拉伸了3个单位

下面证明对角矩阵可以对单位矩阵I的基做变换之后得到的还是在单位矩阵I的基上
列向量不好写,分别用转置表示,也就是V1=[1,0]'和V2[0,1]'来表示单位矩阵I的基

U1=AV1=[1,0],U2=AV2=[0,3],显然U1•U2=0,U1,U2还是一组正交基,下面的分解可以说明U1,U2这一组基还是在I的方向上

AV=[[1,0],   =[[1,0],   *  [[1,0],

    [0,3]]      [0,3]]        [0,1]]

如图所示:

A是对称矩阵:可以找到一组基使得经过A变换后还是在原来的坐标轴上

A=[[1,2],

  [2,2]]

AX=[[x+2y],  

   [2x+2y]]

我们先来看看A对单位矩阵I的变换结果,

U1=AV1=[1,2],U2=AV2=[2,2],显然U1•U2≠0,也就是说,对称矩阵是单位矩阵的变换的结果不是一组正交基,图解如下

但是我们能找找一组基,使得经过对称矩阵的变换还是一组正交基

对A做奇异值分解得:

A=[[-0.61541221 -0.78820544]  *    [[ 3.56155281 0] *  [[-0.61541221 -0.78820544] =U∑V'

     [-0.78820544 0.61541221]]          [0 0.56155281]]     [ 0.78820544 -0.61541221]]

所以V=[[-0.61541221 0.78820544]     V1=[-0.61541221,-0.78820544]',V2=[0.78820544,-0.61541221]',
           [ -0.78820544 -0.61541221]]

记a=0.61541221,b=0.78820544,V1=[-a,-b]',V2=[b,-a]',

A=[[1,2],

  [2,2]]

AX=[[x+2y],  

   [2x+2y]]

U1=AV1=[-a-2b,-2a-2b]',U2=AV2=[b-2a,2b-2a]',U1•U2=(-a-2b)*(b-2a)+(-2a-2b)*(2b-2a)=(-ab-2bb+2aa+4ab)+(-4ab-4bb+4aa+4ab)=6aa-6bb+3ab=3(2aa+ab-2bb)

=2*0.61541221*0.61541221+0.61541221*0.78820544-2*0.78820544*0.78820544=-3.09259662323e-09≈0

所以我们能找找一组基,使得经过对称矩阵的变换还是一组正交基,下面要证明这组正交基和原来的基是同一组:证明也是很简单的,因为A是对称矩阵,所以A=QɅQ',对比上面的A=U∑V',可以发现U=V,

或者这样说:对称矩阵的奇异值分解就是A=QɅQ',同理,对角矩阵的奇异值分解结果就是A=IɅI'

下面是上面这个对称矩阵变换的示例图

 一般矩阵

A=[[1,2],   显然这是一个奇异矩阵,我们来看一下变换结果。奇异值分解如下

  [1,2]]

A=[[-√0.5 -√0.5] *  [ 3.16227766e+00   0                        ]  *[[-√0.2 -2√0.2]

      [-√0.5 √0.5]]   [ 0                          4.24340278e-17]       [ 2√0.2 -√0.2 ]]

变换结果如下:可以看到奇异矩阵的变换结果在某个方向上被严重压缩了,这个结论我们下面可能会用到。

A=[[1,1],   奇异值分解如下

  [-1,1]]

A=[[-√0.5 √0.5] *  [ √2   0  ]  *[[-1 0]

      [√0.5 √0.5]]   [ 0      √2]     [ 0 1 ]]

变换结果如下,一般的满秩矩阵可以把一组基变换到另外一组基上面,并且维度不会有毁灭性的压缩

 

经过一轮实践之后,相信各位对矩阵的线性变化和奇异值分解已经了解的很透彻了,下面附上python求奇异值的代码

# svd.py
# coding:utf-8
import numpy
from numpy.linalg import svd
# import 

# svd返回的是一个奇异值的列表,需要把这个奇异值的列表转换成对角举证
def generatDiagMatrixFromList(arr):
    length=len(arr)
    result = numpy.zeros((length,length))
    for x in range(length):
        result[x][x]=arr[x]
    return result

data =[[1,1],
       [-1,1]]

U,sigma,VT = svd(data,full_matrices=False)
print U
print sigma
print VT

sigma = generatDiagMatrixFromList(sigma)

print "-----------"
print numpy.dot(U,sigma)

 

 奇异值分解的应用

例子一:直线拟合(降维)

首先来一个极简单的例子,点的直线拟合:下面是二维坐标的十个点(列表的前两行),直线拟合如左图所示:

然后我们把这十个点变成一个矩阵,进行奇异值分解,得到两个奇异值[ 6.03970879  0.21867278],明显第二个奇异值很小,我们认为是噪声数据,

我们看看令第二个奇异值为零,得到的结果如右图所示(点的数据在列表的后两行):

-1.030.74-0.020.51-1.310.990.69-0.12-0.721.11
-2.231.61-0.020.88-2.392.021.62-0.35-1.672.46
-1.057916110.76311312-0.011484620.43668239-1.172054920.968865280.7577245-0.15831222-0.782725731.16216187
-2.216676951.59896919-0.024063990.91499106-2.455834732.030086611.58767827-0.33171537-1.640063952.43510558

 

 奇异值分解在这个例子中直观上体现了直线拟合点的特性,或者从一个角度来说,我们期待这些采集到的点分布在一条直线上,而这些是二维的点,也就是我们希望在跟期望直线垂直的维度上进行压缩,回想实践部分的非对称奇异矩阵的变换效果,我们采集的这些点形成的数据矩阵本质上就期望是一个非对称奇异矩阵,变换的结果就是对跟期望直线垂直的维度上进行压缩

例子二:压缩或者去噪

假如有一组数据:6*5=30位

[[0,0,0,0,0],
[0,1,1,1,0],
[0,1,0,1,0],
[0,1,0,1,0],
[0,1,1,1,0],
[0,0,0,0,0]]

做奇异值分解得到五个奇异值:[ 3.02  0.94  0.    0.    0.  ]

6*5=A=U∑V'=(6*6)+(6*5)+(5*5)>6*5,但是我们可以发现,后三个奇异值是没有意义的,我们的∑可以取一个2*2的对角矩阵,那么A=(6*2)+(2*2)+(2*5)=20<30了,这就可以在存储上达到压缩的目的了。

压缩后的矩阵是:完全和源矩阵一致,没有任何损失,当然,这是数据很简单的情况,数据复杂时,可以根据实际的需求取前k个奇异值来达到压缩的目的

[[ 0. 0. 0. 0. 0.]
[ 0. 1. 1. 1. 0.]
[ 0. 1. 0. 1. 0.]
[ 0. 1. 0. 1. 0.]
[ 0. 1. 1. 1. 0.]
[ 0. 0. 0. 0. 0.]]

给这组数据加入一些噪声,比如把四个角的0变成0.2,看下结果如何:

做奇异值分解得到奇异值列表:[ 3.02  0.94  0.4   0.    0.  ],可以发现,噪声数据带来了一个最小的奇异值0.4,但我们一样取前两个奇异值,看下最终的结果如何:去噪效果非常好

[[ 0. 0. 0. 0. 0.]
[ 0. 1. 1. 1. 0.]
[ 0. 1. 0. 1. 0.]
[ 0. 1. 0. 1. 0.]
[ 0. 1. 1. 1. 0.]
[ 0. 0. 0. 0. 0.]]

我们把噪声放大点,四个角的0都变成0.5,看下结果如何:[ 3.02  1.    0.94  0.    0.  ],可以发现噪声带来的奇异值已经不容小觑了,看下结果如何:可以发现,噪声无法去除,并且已经影响到正常的数据了

[[ 0.5 -0. -0. -0. 0.5]
[ 0. 1.1 0.6 1.1 0. ]
[ 0. 0.9 0.5 0.9 0. ]
[ 0. 0.9 0.5 0.9 0. ]
[ 0. 1.1 0.6 1.1 0. ]
[ 0.5 0. 0. 0. 0.5]]

 下面附上代码:

# svd.py
# coding:utf-8
import numpy
from numpy.linalg import svd
# svd返回的是一个奇异值的列表,需要把这个奇异值的列表转换成对角举证
def generatDiagMatrixFromList(arr):
    length=len(arr)
    result = numpy.zeros((length,length))
    for x in range(length):
        result[x][x]=arr[x]
    return result

data =[[0.6,0,0,0,0.6],
       [0,1,1,1,0],
       [0,1,0,1,0],
       [0,1,0,1,0],
       [0,1,1,1,0],
       [0.6,0,0,0,0.6]]

U,sigma,VT = svd(data,full_matrices=False)
print U.round(2)
print sigma.round(2)
print VT.round(2)

# print 2*0.61541221*0.61541221+0.61541221*0.78820544-2*0.78820544*0.78820544
# 取前n个奇异值(把后面的奇异值都变为0)
sigma[2:]=0
sigma = generatDiagMatrixFromList(sigma)
print "-----------"
print sigma.round(2)
print "-----------"
print numpy.matrix( numpy.dot(numpy.dot(U[:,0:2],sigma[0:2,0:2]),VT[0:2,])).round(1)

 

例子三:推荐

接着上面的例子,现在假设每一行是每个用户对某些商品的评分,每一列是每个商品被每个用户的评分,那么我们该如何得到一些“猜测的评分”呢?

[[0,0,0,0,0],
[0,1,1,1,0],
[0,1,0,1,0],
[0,1,0,1,0],
[0,1,1,1,0],
[0,0,0,0,0]]

回顾我们上面讲到的矩阵的满秩分解,A=XY,如果我们能得到X和Y,使得XY 拟合数据中已有的评分的数据,那么那些没有评分的位置ij(用户i对商品j的评分)的猜测值。

这是用svd做推荐的关键思路,具体看参考 SVD在推荐系统中的应用详解以及算法推导

例子四:计算相关性

大多作者会引用吴军在数学之美 系列十八 - 矩阵运算和文本处理中的分类问题的例子:

首先,我们可以用一个大矩阵A来描述这一百万篇文章和五十万词的关联性。这个矩阵中,每一行对应一篇文章,每一列对应一个词。

这个矩阵A:M=1,000,000,N=500,000。第 i 行,第 j 列的元素,是字典中第 j 个词在第 i 篇文章中出现的加权词频(比如,TF/IDF)。读者可能已经注意到了,这个矩阵非常大,有一百万乘以五十万,即五千亿个元素。

奇异值分解可以把上面这样一个大矩阵,分解成三个小矩阵相乘,即分解成一个一百万乘以一百的矩阵X,一个一百乘以一百的矩阵B,和一个一百乘以五十万的矩阵Y。这三个矩阵的元素总数加起来也不过1.5亿,仅仅是原来的三千分之一。相应的存储量和计算量都会小三个数量级以上。

A=1000000*500000==>A=(1000000*100)(100*100)(100*500000)

这是一句非常经典的一句话:

这三个矩阵有非常清楚的物理含义。

  • 第一个矩阵X中的每一行表示意思相关的一类词,其中的每个非零元素表示这类词中每个词的重要性(或者说相关性),数值越大越相关。
  • 最后一个矩阵Y中的每一列表示同一主题一类文章,其中每个元素表示这类文章中每篇文章的相关性。
  • 中间的矩阵则表示类词和文章之间的相关性。

因此,我们只要对关联矩阵A进行一次奇异值分解,w 我们就可以同时完成了近义词分类和文章的分类。(同时得到每类文章和每类词的相关性)。

 svd本质思考

svd(奇异值分解),我个人觉得是线性代数的精髓所在,基本涵盖了线性代数的所有基本概念,是一个大一统的概念。所以,svd必须弄懂。

对于svd的本质思考,一开始听纠结的,就像我上面列举的例子一样,svd可以用来降维,可以用来压缩,也可以去噪,甚至可以用来推荐和求相似度,一开始很难从这些想明白奇异值分解的本质。

其实svd的本质很简单,就是一开始的问题:对于∀矩阵A,是否∃一组正交基,在经过A变换之后还是正交的?

svd的本质就是:对于任意矩阵都能使得从一组基变换到另外一组基,如果从几何角度来讲,就是任意一个m*n的矩阵,都能是使得一个n维的原空间映射到一个m维的像空间,并且不丢失任何信息。

还有一点是,每个维度的重要性是不一样的,这一点由∂体现,∂越大,对应的维度的越重要。

我们可以根据我们不用的需求,对这些维度做出自己的选择。

维度的丢失,意味着信息的丢失,如果我们确定,

1:那些不重要的维度不应该有值,我们认为那是有噪声引起的,去掉这些维度就可以达到降维或者去噪的功能。

2:如果我们认为,用某几个维度也能还原出原本所有的信息或者能接受的大部分信息,这就是压缩。

3:如果我们能赋予原空间和像空间恰当的定义,奇异值分解就会为我们打通他们之前的联系,比如文章和词的关系,但这一点我想的也不是很透彻。







 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/qwj-sysu/p/5527831.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值