@Baileys
2018-03-14T09:38:23.000000Z
字数 5422
阅读 2238
数学
抛出:对任意M*N的矩阵A,能否找到一组正交基使得经过它变换后还是正交基?
(则一个矩形经过变换后还是矩形)
若存在,即有:
问题转化为:。
可得
原理
步骤:
[U,S,V] = svd(A); % A = U*S*V'T=S; T(find(S~=0)) = 1./S(find(S~=0));svdInvA = V * T' * U';SVD分解得到的三个矩阵分别称为:左奇异向量,奇异值矩阵,右奇异向量。左奇异向量用于压缩行,右奇异向量压缩列。压缩方法均是取奇异值较大的左奇异向量或者右奇异向量与原数据相乘。
SVD的奇异值大小的分布情况和累计分布比率:

% 读入图像RGB数据,并从Uint8转换成double类型方便之后的处理。p = imread('2.jpg');pr = p(:,:,1);pg = p(:,:,2);pb = p(:,:,3);pr = double(pr);pg = double(pg);pb = double(pb);% 可视化图像figure()subplot(2,2,1);imshow(p)title('原来的RGB图像')subplot(2,2,2);imshow(mat2gray(pr))title('R分量的灰度图像')subplot(2,2,3);imshow(mat2gray(pg))title('G分量的灰度图像')subplot(2,2,4);imshow(mat2gray(pb))title('B分量的灰度图像')% SVD分解[Ur,Sr,Vr] = svd(pr);[Ug,Sg,Vg] = svd(pg);[Ub,Sb,Vb] = svd(pb);% 分析SVD,计划选取1 3 5 10 30 50 100 150svdD = diag(Sr);cumsumD = cumsum(svdD);plot(svdD,'LineWidth',2)plot(cumsumD,'LineWidth',2)% 分解后按照singular value从大到小选择fr = @(n) Ur(:,1:n)*Sr(1:n,1:n)*Vr(:,1:n)';fg = @(n) Ug(:,1:n)*Sg(1:n,1:n)*Vg(:,1:n)';fb = @(n) Ub(:,1:n)*Sb(1:n,1:n)*Vb(:,1:n)';param = [1,3,5,10,30,50,100,150];figure()for i = 1:8subplot(2,4,i);n = param(i);pnew(:,:,1) = fr(n);pnew(:,:,2) = fg(n);pnew(:,:,3) = fb(n);pnew = uint8(pnew);imshow(pnew)title(strcat(num2str(param(i)),'个奇异值'))end
SVD总能找到标准化正交基后方差最大的维度,因此可以用它进行降维去噪等等。
下面分别用SVD和linear regression去拟合直线:

% 模拟线性数据,加上一定的高斯噪声X = 1:10;Xb = ones(1,10);Y = 2*X + random('Normal',0,1,1,10);% 进行SVD分解并选择原输入空间的一个奇异值比较大的基,实现了数据降维M = [X;Y];[U,S,V] = svd(M);U1 = U(:,1);u1 = U1(1);u2 = U1(2);k = (u2/u1);% 进行线性回归w = pinv([X;Xb])'*Y';% 分别是SVD和线性回归拟合的数据Y1 = k*X;Y2 = w(1)*X+w(2);% 画图并比较figure()% 注释蛮方便的函数ezplot('y-2*x-1')% refline(u2/u1,0)hold onplot(X,Y,'ko','LineWidth',2)plot(X,Y1,'r-','LineWidth',1)plot(X,Y2,'b-','LineWidth',1)legend('数据点','SVD拟合','线性回归拟合')
数据集中行代表用户user,列代表物品item,其中的值代表用户对物品的打分。
基于SVD的优势在于:用户的评分数据是稀疏矩阵,可以用SVD将原始数据映射到低维空间中,然后计算物品item之间的相似度,可以节省计算资源。
整体思路:先找到用户没有评分的物品,然后再经过SVD“压缩”后的低维空间中,计算未评分物品与其他物品的相似性,得到一个预测打分,再对这些物品的评分从高到低进行排序,返回前N个物品推荐给用户。
具体代码如下,主要分为5部分:
第1部分:加载测试数据集;
第2部分:定义三种计算相似度的方法;
第3部分:通过计算奇异值平方和的百分比来确定将数据降到多少维才合适,返回需要降到的维度;
第4部分:在已经降维的数据中,基于SVD对用户未打分的物品进行评分预测,返回未打分物品的预测评分值;
第5部分:产生前N个评分值高的物品,返回物品编号以及预测评分值。
优势在于:用户的评分数据是稀疏矩阵,可以用SVD将数据映射到低维空间,然后计算低维空间中的item之间的相似度,对用户未评分的item进行评分预测,最后将预测评分高的item推荐给用户。
#coding=utf-8from numpy import *from numpy import linalg as la'''加载测试数据集'''def loadExData():return mat([[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5],[0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 3],[0, 0, 0, 0, 4, 0, 0, 1, 0, 4, 0],[3, 3, 4, 0, 0, 0, 0, 2, 2, 0, 0],[5, 4, 5, 0, 0, 0, 0, 5, 5, 0, 0],[0, 0, 0, 0, 5, 0, 1, 0, 0, 5, 0],[4, 3, 4, 0, 0, 0, 0, 5, 5, 0, 1],[0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4],[0, 0, 0, 2, 0, 2, 5, 0, 0, 1, 2],[0, 0, 0, 0, 5, 0, 0, 0, 0, 4, 0],[1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0]])'''以下是三种计算相似度的算法,分别是欧式距离、皮尔逊相关系数和余弦相似度,注意三种计算方式的参数inA和inB都是列向量'''def ecludSim(inA,inB):return 1.0/(1.0+la.norm(inA-inB)) #范数的计算方法linalg.norm(),这里的1/(1+距离)表示将相似度的范围放在0与1之间def pearsSim(inA,inB):if len(inA)<3: return 1.0return 0.5+0.5*corrcoef(inA,inB,rowvar=0)[0][1] #皮尔逊相关系数的计算方法corrcoef(),参数rowvar=0表示对列求相似度,这里的0.5+0.5*corrcoef()是为了将范围归一化放到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) #将相似度归一到0与1之间'''按照前k个奇异值的平方和占总奇异值的平方和的百分比percentage来确定k的值,后续计算SVD时需要将原始矩阵转换到k维空间'''def sigmaPct(sigma,percentage):sigma2=sigma**2 #对sigma求平方sumsgm2=sum(sigma2) #求所有奇异值sigma的平方和sumsgm3=0 #sumsgm3是前k个奇异值的平方和k=0for i in sigma:sumsgm3+=i**2k+=1if sumsgm3>=sumsgm2*percentage:return k'''函数svdEst()的参数包含:数据矩阵、用户编号、物品编号和奇异值占比的阈值,数据矩阵的行对应用户,列对应物品,函数的作用是基于item的相似性对用户未评过分的物品进行预测评分'''def svdEst(dataMat,user,simMeas,item,percentage):n=shape(dataMat)[1]simTotal=0.0;ratSimTotal=0.0u,sigma,vt=la.svd(dataMat)k=sigmaPct(sigma,percentage) #确定了k的值sigmaK=mat(eye(k)*sigma[:k]) #构建对角矩阵xformedItems=dataMat.T*u[:,:k]*sigmaK.I #根据k的值将原始数据转换到k维空间(低维),xformedItems表示物品(item)在k维空间转换后的值for j in range(n):userRating=dataMat[user,j]if userRating==0 or j==item:continuesimilarity=simMeas(xformedItems[item,:].T,xformedItems[j,:].T) #计算物品item与物品j之间的相似度simTotal+=similarity #对所有相似度求和ratSimTotal+=similarity*userRating #用"物品item和物品j的相似度"乘以"用户对物品j的评分",并求和if simTotal==0:return 0else:return ratSimTotal/simTotal #得到对物品item的预测评分'''函数recommend()产生预测评分最高的N个推荐结果,默认返回5个;参数包括:数据矩阵、用户编号、相似度衡量的方法、预测评分的方法、以及奇异值占比的阈值;数据矩阵的行对应用户,列对应物品,函数的作用是基于item的相似性对用户未评过分的物品进行预测评分;相似度衡量的方法默认用余弦相似度'''def recommend(dataMat,user,N=5,simMeas=cosSim,estMethod=svdEst,percentage=0.9):unratedItems=nonzero(dataMat[user,:].A==0)[1] #建立一个用户未评分item的列表if len(unratedItems)==0:return 'you rated everything' #如果都已经评过分,则退出itemScores=[]for item in unratedItems: #对于每个未评分的item,都计算其预测评分estimatedScore=estMethod(dataMat,user,simMeas,item,percentage)itemScores.append((item,estimatedScore))itemScores=sorted(itemScores,key=lambda x:x[1],reverse=True)#按照item的得分进行从大到小排序return itemScores[:N] #返回前N大评分值的item名,及其预测评分值
将文件命名为svd2.py,在python提示符下输入:
>>>import svd2>>>testdata=svd2.loadExData()>>>svd2.recommend(testdata,1,N=3,percentage=0.8)#对编号为1的用户推荐评分较高的3件商品