机器学习实战(十四)——利用SVD简化数据
一、SVD的应用
SVD(奇异值分解),可以实现用小得多的数据集来表示原始数据集,达到去除噪声和冗余信息,以及压缩数据的目的。
SVD的主要应用场景有:
- 隐性语义索引:利用奇异值分解可以将文档中的概念或者主题提取出来
- 推荐系统:SVD可以从数据中构建一个主题空间,再在该主题空间下计算项的相似度达到推荐的目的
二、矩阵分解
在大多数情况下,原始数据集中很可能一小段就携带了大部分的信息,而其他的信息要么是噪声要么是毫不相关的信息,因此利用矩阵分解技术,可以将原始矩阵表示成新的易于处理的形式。在统计学习方法一书中对SVD方法的定义如下:
回顾上一章PCA中,得到的是矩阵的特征值,而此处得到的是矩阵的奇异值,实际上奇异值就是矩阵 A T A A^TA ATA 的特征值的平方根。并且,由于不要求矩阵 A A A是方阵,因此此处可能 m ! = n m!=n m!=n。另外,可能在某一个奇异值 σ r \sigma_r σr之后的奇异值都是0,也就是说数据集中仅有 r r r个重要特征。,而其余的特征都是噪声或者冗余特征。
三、利用Python实现SVD
在numpy库中,有现成的SVD函数,可以直接利用该函数来执行运行:
from numpy import *
if __name__ == "__main__":
U,Sigma,VT = linalg.svd([[1,1],[7,7]])
print("U = ",U)
print("Sigma = ",Sigma)
print("VT = ",VT)
返回值为:
U = [[-0.14142136 -0.98994949]
[-0.98994949 0.14142136]]
Sigma = [1.00000000e+01 2.82797782e-16]
VT = [[-0.70710678 -0.70710678]
[ 0.70710678 -0.70710678]]
可以看到 S i g m a Sigma Sigma原本应该是一个对角矩阵,但是其返回值为一个向量,这是因为对角矩阵除了其对角线元素外都为0,因此这样存储有利于节省空间。
下面对一个较大的数据集实行SVD:
def loadExData():
return [[1,1,1,0,0],
[2,2,2,0,0],
[1,1,1,0,0],
[5,5,5,0,0],
[1,1,0,2,2],
[0,0,0,3,3],
[0,0,0,1,1]]
if __name__ == "__main__":
Data = loadExData()
U, Sigma, VT = linalg.svd(Data)
print("Sigma = ",Sigma)
输出为:
Sigma = [9.72140007e+00 5.29397912e+00 6.84226362e-01 1.89194737e-15
1.35001244e-31]
可以看到前三个奇异值较大,后面两个奇异值接近于0,这说明可以将后面两个奇异值忽略,来恢复原来的矩阵:
即取明显不为0的奇异值来进行恢复,这称为紧奇异值分解,还有另外一种为截断奇异值分解,具体为:
- 紧奇异值分解:设 r ( D a t a ) = r ≤ m i n ( m , n ) r(Data)=r\leq min(m,n) r(Data)=r≤min(m,n),那么取 U r = U 1 ( m × r ) U_r=U_1(m\times r) Ur=U1(m×r), Σ r = Σ 1 ( r × r ) \Sigma_r=\Sigma_1(r\times r) Σr=Σ1(r×r), V r = V 1 ( n × r ) V_r=V_1(n\times r) Vr=V1(n×r)来进行恢复矩阵
- 截断奇异值分解:设 r ( D a t a ) = r ≤ m i n ( m , n ) r(Data)=r\leq min(m,n) r(Data)=r≤min(m,n),取 k ( k < r ) k(k<r) k(k<r)个最大的奇异值, 那么 U k = U 1 ( m × k ) U_k=U_1(m\times k) Uk=U1(m×k), Σ k = Σ 1 ( k × k ) \Sigma_k=\Sigma_1(k\times k) Σk=Σ1(k×k), V k = V 1 ( n × k ) V_k=V_1(n\times k) Vk=V1(n×k)来进行恢复矩阵, k k k为指定的压缩大小。
那就有疑问,重构出来的矩阵大小与原来的相同,为什么仍然称为压缩呢?
其实,SVD降维不是体现在矩阵的大小上,而是减少了矩阵的秩。
这里引用[知乎]((29 封私信 / 50 条消息) SVD 降维体现在什么地方? - 知乎 (zhihu.com))某位大佬的回答:
四、基于协同过滤的推荐引擎
协同过滤是通过将用户和其他用户的数据进行对比来实现推荐的。
4.1、相似度计算
在比较物品之间的相似程度时,如果我们采用的是“专家‘给出的描述物品的重要属性,例如对于食品来说有热量、配料等等,但这些属性当我们更换物品的时候就需要重新设置了,因此较为繁琐。
协同过滤中所使用的方法是:利用用户对于物品的意见来计算相似度,它不关心物品的描述属性,而是严格按照许多用户的观点来计算相似度。
计算相似度有以下几种常见方式:
- 用欧氏距离表征的相似度:取相似度为 相似度 = 1 / ( 1 + 距离) 相似度=1/(1+距离) 相似度=1/(1+距离),就可将距离转换到在0和1之间变化的相似度,且相似度越大代表距离越小,物品之间越接近。
- 用皮尔逊相关系数表征的相似度:皮尔逊相关系数的优势在于它对于用户评级的量级并不敏感,例如某个人对于每种物品评分都是5分,而另外一个人都是1分,在皮尔逊中它认为这两个向量是相等的。取相似度为 相似度 = 0.5 + 0.5 × c o r r c o e f ( ) 相似度=0.5+0.5\times corrcoef() 相似度=0.5+0.5×corrcoef(),将在-1和1之间变量的皮尔逊相关系数转换到0和1之间变换的相似度
- 余弦相似度:其计算的是两个向量夹角的余弦值,如果夹角为90度,那么相似度为0,如果两个向量方向相同那么相似度为1(因此也会忽略量级的影响)。余弦相似度的定义为: c o s Θ = A × B ∥ A ∥ ∥ B ∥ cos\Theta = \frac{A\times B}{\| A\| \| B \| } cosΘ=∥A∥∥B∥A×B。之后同样要取相似度为 相似度 = 0.5 + 0.5 × c o s Θ 相似度=0.5+0.5\times cos\Theta 相似度=0.5+0.5×cosΘ。
具体代码如下:
def ecludSim(inA,inB):
return 1.0 / (1.0 + la.norm(inA - inB))
def pearsSim(inA,inB):
if len(inA) < 3:return 1.0
return 0.5 + 0.5 * corrcoef(inA,inB,rowvar=False)[0][1]
def cosSim(inA,inB):
num = float(inA.T * inB)
denom = la.norm(inA) * la.norm(inB)
return 0.5 + 0.5 * (num / denom)
4.2、基于物品的相似度还是基于用户的相似度
在计算时如果取向量为列向量,那么就是计算物品之间的相似度;如果取为行向量那么就是计算用户之间的相似度。如何选择这两种方式就取决于数据中用户的数据多还是物品的数据多。如果用户的数目很多,那么会倾向于使用基于物品相似度的计算方法。
4.3、推荐引擎的评价
具体做法是将数据集划分为训练集和测试集,在测试集上预测已知的分数,查看偏差程度。
五、示例:餐馆菜肴推荐引擎
5.1、推荐未尝过的菜肴
具体的流程大致为:
- 寻找用户没有评级的菜肴,即用户-物品矩阵中的0值
- 在用户没有评级的所有物品中,对每个物品预计一个可能的评级分数,也就是打分
- 对这些物品的评分从高到低进行排序,返回前N个物品
具体代码为:
def standEst(dataMat,user,simMeas,item):
n = shape(dataMat)[1] # 有多少个物品
simTotal = 0.0
ratSimTotal = 0.0
for j in range(n):
userRating = dataMat[user,j] # 遍历该用户对每一个物品的评分
if userRating == 0: # 如果没有评分则跳过
continue
overLap = nonzero(logical_and(dataMat[:,item].A>0,dataMat[:,j].A>0))[0]
if len(overLap) == 0: # 没有都对这两个物品都评分过的用户
similarity = 0
else:
similarity = simMeas(dataMat[overLap,item],dataMat[overLap,j]) # 计算相似度
simTotal += similarity
ratSimTotal += similarity * userRating
if simTotal == 0:
return 0
else:
return ratSimTotal / simTotal
def recommend(dataMat,user,N = 3,simMeas = cosSim,estMethod = standEst):
unratedItems = nonzero(dataMat[user,:].A == 0)[1] # 寻找未评分的物品
print(nonzero(dataMat[user,:].A == 0))
if len(unratedItems) == 0: # 都评分过
return "you have rated everything"
itemScores = [] # 用来存储各个未评分物品的预测得分
for item in unratedItems: # item就是未评分的那些物品的索引,是列的索引
estimatedScore = estMethod(dataMat,user,simMeas,item)
itemScores.append((item,estimatedScore))
return sorted(itemScores,key=lambda jj:jj[1],reverse=True)[:N]
解释一下这行代码:
overLap = nonzero(logical_and(dataMat[:,item].A>0,dataMat[:,j].A>0))[0]
首先是 l o g i c a i _ a n d logicai\_and logicai_and,就是对两个向量按对应位置的元素进行逻辑与操作,例子如下:
A = np.mat([[1,0,1,0,1,1]])
B = np.mat([[0,0,1,1,0,1]])
np.logical_and(A,B)
Out[9]: matrix([[False, False, True, False, False, True]])
那么该行代码就是先找出 i t e m item item列(该用户未评分的物品那列)和当前循环的第j列,取出其中那些大于0的部分再按位与,再通过nonzero函数取出不为0,即两个物品都评分过的那些用户的索引。
再解释一下这行代码:
ratSimTotal += similarity * userRating
这里是因为该物品评分越高,且与未知物品越接近,那么用户对这个未知物品的评分也可能越高。另外分数是多个已评价的物品累计的,也就是:
- 如果有一个物品用户评价过分数很高( u s e r R a t i n g userRating userRating很大),且与待评价的物品很接近( s i m i l a r i t y similarity similarity很大),那么会对未评价物品的评分有较大的增加效果
- 如果有一个物品用户评价过分数很低( u s e r R a t i n g userRating userRating很小),且与待评价的物品不接近( s i m i l a r i t y similarity similarity很小),那么会就没什么贡献,没法使得预测分数增大。
再解释下这行代码为什么最后是[1]:
unratedItems = nonzero(dataMat[user,:].A == 0)[1] # 寻找未评分的物品
dataMat[user,:].A返回的是一个array类型的数据,可以看成一维的矩阵,那么对于矩阵,nonzero函数就会有两个参数:第一个参数为非零元素所在的行的索引,第二个为非零元素所在的列的索引,因为这里是一维矩阵,那么这里第一个元素就里面都是0。取出第二个元素也就是索引1才是我们想要的那些未评分的那些列的索引。
5.2、利用SVD提升推荐的效果
利用SVD可以先将数据进行降维,再在低维度上计算物品之间的相似度就更加方便:
def svdEst(dataMat,user,simMeas,item):
n = shape(dataMat)[1]
simTotal = 0.0
ratSimTotal = 0.0
U,Sigma,VT = la.svd(dataMat)
Sig4 = mat(eye(4) * Sigma[:4]) # 创建对角矩阵,维度为4维度
xformedItems = dataMat.T * U[:,:4] * Sig4.I
for j in range(n):
userRating = dataMat[user,j]
if userRating == 0 or j == item:
continue
similarity = simMeas(xformedItems[item,:].T,xformedItems[j,:].T)
print("the %d and %d similarity is: %f" %(item,j,similarity))
simTotal += similarity
ratSimTotal += similarity * userRating
if simTotal == 0:
return 0
else:
return ratSimTotal / simTotal
此部分代码与之前很相似,只不过用来计算的矩阵是重构之后维度更低的矩阵。
六、基于SVD的图像压缩
具体代码为:
def printMat(inMat,thresh = 0.8):
for i in range(32):
for j in range(32):
if float(inMat[i,j]) > thresh:
print("1 ",end = ' ') # 不加end的话会自动换行
else:
print("0 ",end = ' ')
print(" ")
def imgCompress(numSV = 3,thresh = 0.8):
myl = []
fr = open("0_5.txt")
for line in fr.readlines():
newRow = []
for i in range(32):
newRow.append(int(line[i]))
myl.append(newRow)
myMat = mat(myl)
print("*"*10,"original matrix","*"*10)
printMat(myMat,thresh)
U,Sigma,VT = la.svd(myMat)
SigRecon = mat(zeros((numSV,numSV)))
for k in range(numSV):
SigRecon[k,k] = Sigma[k] # 取前numSV个奇异值形成对角矩阵
reconMat = U[:,:numSV] * SigRecon * VT[:numSV,:]
print("*"*10,"reconstructed matrix using %d singular values" % numSV,"*"*10)
printMat(reconMat,thresh)
输出结果为:
********** original matrix **********
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
********** reconstructed matrix using 2 singular values **********
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
处理后还是很明显的看出手写的0的
并且经过SVD处理之后,我们的 U U U和 V T V^T VT都是 32 × 2 32\times 2 32×2的矩阵,另外还需要两个奇异值,则总共需要存储数字的数目为130。和原来图像需要存储1024个数字相比压缩效果显著。