[关闭]
@Baileys 2018-03-14T09:38:23.000000Z 字数 5422 阅读 2238

SVD 奇异值分解的原理及应用

数学


什么是奇异值分解?

可得




其中为对称正定矩阵,对其进行特征值分解,两者的特征值一致。

奇异值分解步骤

  1. ,求.
  2. 的特征向量,即;特征值,即
  3. 标准化
  4. ,求.
  5. 的特征向量,即;特征值,即
  6. 标准化
  7. 组合成形式

推广

求任意矩阵的伪逆

原理

  1. SVD分解得到的矩阵:
  2. 正交阵的逆=转置
  3. 对角阵的逆=非零元素求倒

步骤:

  1. 求解A的SVD分解
    [U,S,V] = svd(A); % A = U*S*V'
  2. 将S中的非零元素求倒
    T=S;
    T(find(S~=0)) = 1./S(find(S~=0));
  3. 求invA
    svdInvA = V * T' * U';

应用

SVD分解得到的三个矩阵分别称为:左奇异向量,奇异值矩阵,右奇异向量。左奇异向量用于压缩行,右奇异向量压缩列。压缩方法均是取奇异值较大的左奇异向量或者右奇异向量与原数据相乘。

图像压缩

SVD的奇异值大小的分布情况和累计分布比率:


  1. % 读入图像RGB数据,并从Uint8转换成double类型方便之后的处理。
  2. p = imread('2.jpg');
  3. pr = p(:,:,1);
  4. pg = p(:,:,2);
  5. pb = p(:,:,3);
  6. pr = double(pr);
  7. pg = double(pg);
  8. pb = double(pb);
  9. % 可视化图像
  10. figure()
  11. subplot(2,2,1);
  12. imshow(p)
  13. title('原来的RGB图像')
  14. subplot(2,2,2);
  15. imshow(mat2gray(pr))
  16. title('R分量的灰度图像')
  17. subplot(2,2,3);
  18. imshow(mat2gray(pg))
  19. title('G分量的灰度图像')
  20. subplot(2,2,4);
  21. imshow(mat2gray(pb))
  22. title('B分量的灰度图像')
  23. % SVD分解
  24. [Ur,Sr,Vr] = svd(pr);
  25. [Ug,Sg,Vg] = svd(pg);
  26. [Ub,Sb,Vb] = svd(pb);
  27. % 分析SVD,计划选取1 3 5 10 30 50 100 150
  28. svdD = diag(Sr);
  29. cumsumD = cumsum(svdD);
  30. plot(svdD,'LineWidth',2)
  31. plot(cumsumD,'LineWidth',2)
  32. % 分解后按照singular value从大到小选择
  33. fr = @(n) Ur(:,1:n)*Sr(1:n,1:n)*Vr(:,1:n)';
  34. fg = @(n) Ug(:,1:n)*Sg(1:n,1:n)*Vg(:,1:n)';
  35. fb = @(n) Ub(:,1:n)*Sb(1:n,1:n)*Vb(:,1:n)';
  36. param = [1,3,5,10,30,50,100,150];
  37. figure()
  38. for i = 1:8
  39. subplot(2,4,i);
  40. n = param(i);
  41. pnew(:,:,1) = fr(n);
  42. pnew(:,:,2) = fg(n);
  43. pnew(:,:,3) = fb(n);
  44. pnew = uint8(pnew);
  45. imshow(pnew)
  46. title(strcat(num2str(param(i)),'个奇异值'))
  47. end

数据去噪

SVD总能找到标准化正交基后方差最大的维度,因此可以用它进行降维去噪等等。
下面分别用SVD和linear regression去拟合直线:

  1. % 模拟线性数据,加上一定的高斯噪声
  2. X = 1:10;
  3. Xb = ones(1,10);
  4. Y = 2*X + random('Normal',0,1,1,10);
  5. % 进行SVD分解并选择原输入空间的一个奇异值比较大的基,实现了数据降维
  6. M = [X;Y];
  7. [U,S,V] = svd(M);
  8. U1 = U(:,1);
  9. u1 = U1(1);
  10. u2 = U1(2);
  11. k = (u2/u1);
  12. % 进行线性回归
  13. w = pinv([X;Xb])'*Y';
  14. % 分别是SVD和线性回归拟合的数据
  15. Y1 = k*X;
  16. Y2 = w(1)*X+w(2);
  17. % 画图并比较
  18. figure()
  19. % 注释蛮方便的函数ezplot('y-2*x-1')
  20. % refline(u2/u1,0)
  21. hold on
  22. plot(X,Y,'ko','LineWidth',2)
  23. plot(X,Y1,'r-','LineWidth',1)
  24. plot(X,Y2,'b-','LineWidth',1)
  25. legend('数据点','SVD拟合','线性回归拟合')

推荐系统

数据集中行代表用户user,列代表物品item,其中的值代表用户对物品的打分。
基于SVD的优势在于:用户的评分数据是稀疏矩阵,可以用SVD将原始数据映射到低维空间中,然后计算物品item之间的相似度,可以节省计算资源。
整体思路:先找到用户没有评分的物品,然后再经过SVD“压缩”后的低维空间中,计算未评分物品与其他物品的相似性,得到一个预测打分,再对这些物品的评分从高到低进行排序,返回前N个物品推荐给用户。

具体代码如下,主要分为5部分:
第1部分:加载测试数据集;
第2部分:定义三种计算相似度的方法;
第3部分:通过计算奇异值平方和的百分比来确定将数据降到多少维才合适,返回需要降到的维度;
第4部分:在已经降维的数据中,基于SVD对用户未打分的物品进行评分预测,返回未打分物品的预测评分值;
第5部分:产生前N个评分值高的物品,返回物品编号以及预测评分值。

优势在于:用户的评分数据是稀疏矩阵,可以用SVD将数据映射到低维空间,然后计算低维空间中的item之间的相似度,对用户未评分的item进行评分预测,最后将预测评分高的item推荐给用户。

  1. #coding=utf-8
  2. from numpy import *
  3. from numpy import linalg as la
  4. '''加载测试数据集'''
  5. def loadExData():
  6. return mat([[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5],
  7. [0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 3],
  8. [0, 0, 0, 0, 4, 0, 0, 1, 0, 4, 0],
  9. [3, 3, 4, 0, 0, 0, 0, 2, 2, 0, 0],
  10. [5, 4, 5, 0, 0, 0, 0, 5, 5, 0, 0],
  11. [0, 0, 0, 0, 5, 0, 1, 0, 0, 5, 0],
  12. [4, 3, 4, 0, 0, 0, 0, 5, 5, 0, 1],
  13. [0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4],
  14. [0, 0, 0, 2, 0, 2, 5, 0, 0, 1, 2],
  15. [0, 0, 0, 0, 5, 0, 0, 0, 0, 4, 0],
  16. [1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0]])
  17. '''以下是三种计算相似度的算法,分别是欧式距离、皮尔逊相关系数和余弦相似度,
  18. 注意三种计算方式的参数inA和inB都是列向量'''
  19. def ecludSim(inA,inB):
  20. return 1.0/(1.0+la.norm(inA-inB)) #范数的计算方法linalg.norm(),这里的1/(1+距离)表示将相似度的范围放在0与1之间
  21. def pearsSim(inA,inB):
  22. if len(inA)<3: return 1.0
  23. return 0.5+0.5*corrcoef(inA,inB,rowvar=0)[0][1] #皮尔逊相关系数的计算方法corrcoef(),参数rowvar=0表示对列求相似度,这里的0.5+0.5*corrcoef()是为了将范围归一化放到0和1之间
  24. def cosSim(inA,inB):
  25. num=float(inA.T*inB)
  26. denom=la.norm(inA)*la.norm(inB)
  27. return 0.5+0.5*(num/denom) #将相似度归一到0与1之间
  28. '''按照前k个奇异值的平方和占总奇异值的平方和的百分比percentage来确定k的值,
  29. 后续计算SVD时需要将原始矩阵转换到k维空间'''
  30. def sigmaPct(sigma,percentage):
  31. sigma2=sigma**2 #对sigma求平方
  32. sumsgm2=sum(sigma2) #求所有奇异值sigma的平方和
  33. sumsgm3=0 #sumsgm3是前k个奇异值的平方和
  34. k=0
  35. for i in sigma:
  36. sumsgm3+=i**2
  37. k+=1
  38. if sumsgm3>=sumsgm2*percentage:
  39. return k
  40. '''函数svdEst()的参数包含:数据矩阵、用户编号、物品编号和奇异值占比的阈值,
  41. 数据矩阵的行对应用户,列对应物品,函数的作用是基于item的相似性对用户未评过分的物品进行预测评分'''
  42. def svdEst(dataMat,user,simMeas,item,percentage):
  43. n=shape(dataMat)[1]
  44. simTotal=0.0;ratSimTotal=0.0
  45. u,sigma,vt=la.svd(dataMat)
  46. k=sigmaPct(sigma,percentage) #确定了k的值
  47. sigmaK=mat(eye(k)*sigma[:k]) #构建对角矩阵
  48. xformedItems=dataMat.T*u[:,:k]*sigmaK.I #根据k的值将原始数据转换到k维空间(低维),xformedItems表示物品(item)在k维空间转换后的值
  49. for j in range(n):
  50. userRating=dataMat[user,j]
  51. if userRating==0 or j==item:continue
  52. similarity=simMeas(xformedItems[item,:].T,xformedItems[j,:].T) #计算物品item与物品j之间的相似度
  53. simTotal+=similarity #对所有相似度求和
  54. ratSimTotal+=similarity*userRating #用"物品item和物品j的相似度"乘以"用户对物品j的评分",并求和
  55. if simTotal==0:return 0
  56. else:return ratSimTotal/simTotal #得到对物品item的预测评分
  57. '''函数recommend()产生预测评分最高的N个推荐结果,默认返回5个;
  58. 参数包括:数据矩阵、用户编号、相似度衡量的方法、预测评分的方法、以及奇异值占比的阈值;
  59. 数据矩阵的行对应用户,列对应物品,函数的作用是基于item的相似性对用户未评过分的物品进行预测评分;
  60. 相似度衡量的方法默认用余弦相似度'''
  61. def recommend(dataMat,user,N=5,simMeas=cosSim,estMethod=svdEst,percentage=0.9):
  62. unratedItems=nonzero(dataMat[user,:].A==0)[1] #建立一个用户未评分item的列表
  63. if len(unratedItems)==0:return 'you rated everything' #如果都已经评过分,则退出
  64. itemScores=[]
  65. for item in unratedItems: #对于每个未评分的item,都计算其预测评分
  66. estimatedScore=estMethod(dataMat,user,simMeas,item,percentage)
  67. itemScores.append((item,estimatedScore))
  68. itemScores=sorted(itemScores,key=lambda x:x[1],reverse=True)#按照item的得分进行从大到小排序
  69. return itemScores[:N] #返回前N大评分值的item名,及其预测评分值

将文件命名为svd2.py,在python提示符下输入:

  1. >>>import svd2
  2. >>>testdata=svd2.loadExData()
  3. >>>svd2.recommend(testdata,1,N=3,percentage=0.8)#对编号为1的用户推荐评分较高的3件商品
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注