奇异值分解,singular value decomposition(SVD)是线性代数中一种重要的矩阵分解
特征值
对于一个方阵,其特征值和特征向量满足:
Aν=λνAν=λν
求出所有的特征值和特征向量后,就得出了方阵A的特征值分解:
A=QΣQ−1
奇异值
现实世界里,为了实现类似特征值分解的计算,我们使用奇异值分解。奇异值分解适用于任何矩阵,如下所示,其中A是一个m*n的矩阵:
A=Um∗mΣm∗nVTn∗nA=Um∗mΣm∗nVn∗nT
其中
- UU 是一个m*m的正交矩阵,其向量被称为左奇异向量
- VV 也是一个n*n的正交矩阵,其向量被成为右奇异向量
- ΣΣ 是一个m*n的矩阵,其对角线上的元素为奇异值,其余元素皆为0
当选取top k个奇异值时,可以将矩阵降维成为:
Am∗n≈Um∗kΣk∗kVTk∗nAm∗n≈Um∗kΣk∗kVk∗nT
基于SVD的机器学习之Python代码如下:
import numpy as np
from numpy import linalg as la
def loadExData():
data=np.array([[0, 0, 0, 2, 2],
[0, 0, 0, 3, 3],
[0, 0, 0, 1, 1],
[1, 1, 1, 0, 0],
[2, 2, 2, 0, 0],
[5, 5, 5, 0, 0],
[1, 1, 1, 0, 0]])
return data
def loadExData2():
data2=np.array([[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]])
return data2
#计算相似度
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*np.corrcoef(inA, inB, rowvar = 0)[0][1]
'''
def cosSim(inA,inB): #利用余弦相似度计算
num=float(np.multiply(inA.T,inB)) #错误不知道为啥
#num = float(inA.T*inB)
denom = la.norm(inA)*la.norm(inB)
return 0.5+0.5*(num/denom)
'''
#基于物品相似度的推荐引擎
#1、
def standEst(dataMat, user, simMeas, item):
#输入:数据集、用户、相似性度量、物品编号
#输出:用户对物品的估计评分值
n = np.shape(dataMat)[1] #得到物品数
simTotal = 0.0; ratSimTotal = 0.0
for j in range(n):
userRating = dataMat[user,j]
if userRating == 0: continue
overLap =np. nonzero(np.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]) #计算两评级物品的相似度
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
#2、推荐引擎
def recommend(dataMat, user, N=3, simMeas=ecludSim, estMethod=standEst):
#输入:数据集、用户、返回个数(默认为3)、相似性度量、评估方法
#输出:返回相似度最高的N个推荐结果
unratedItems = np.nonzero(dataMat[user,:].A==0)[1] #建立未评分的物品列表
if len(unratedItems) == 0: return 'you rated everything'
itemScores = []
for item in unratedItems:
estimatedScore = estMethod(dataMat, user, simMeas, item) #调用standEst对物品进行评分
itemScores.append((item, estimatedScore)) #将物品编号与评分值放入列表中
return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N] #从大到小进行排序,返回前N个值
#3、基于SVD的评分估计
def svdEst(dataMat, user, simMeas, item):
n = np.shape(dataMat)[1]
simTotal = 0.0; ratSimTotal = 0.0
U,Sigma,VT = la.svd(dataMat)
Sig4 = np.mat(np.eye(4)*Sigma[:4]) #使用总能量达90%(使用前三个元素)的奇异值构建对角矩阵
xformedItems = dataMat.T * U[:,:4] * Sig4.I #利用矩阵U将数据转化到低维空间
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
if __name__ == "__main__":
#SVD分解
Data=loadExData()
U,Sigma,VT=np.linalg.svd(Data)
print(Sigma) #只返回对角线的值
#数据重构--只取前两个奇异值
Sig2=([Sigma[0],0],[0,Sigma[1]])
print(Sig2)
#reconData=U[:,:2]*Sig2*VT[:2,:]
#print(reconData)
#计算相似度
p1=ecludSim(Data[:,0],Data[:,4])
p11=ecludSim(Data[:,0],Data[:,0])
print(p1,p11)
print('-----------------------------')
p2=pearsSim(Data[:,0],Data[:,4])
p22=pearsSim(Data[:,0],Data[:,0])
print(p2,p22)
print('-----------------------------')
'''
p3=cosSim(Data[:,0],Data[:,4])
p33=cosSim(Data[:,0],Data[:,0])
print(p3,p33)
print('-----------------------------')
'''
#查看推荐引擎的效果
print(Data)
print('-----------------------------')
Data[0,0]=Data[0,1]=Data[1,0]=Data[2,0]=4
print(Data)
Data1=np.mat(Data) #数组转为矩阵,因为矩阵才可使用.A属性,即dataMat[user,:].A
print('-----------------------------')
pre=recommend(Data1,2) #用户2,即矩阵的第三行
print(pre)
print('-----------------------------')
#利用SVD提高推荐效果
Data2=loadExData2()
Data2=np.mat(Data2)
pre2=recommend(Data1,1,simMeas=pearsSim, estMethod=svdEst)
print(pre2)
'''
计算奇异值的能量
Sig2=Sigma**2
sum(Sig2) 总能量
sum(Sig2)*0.9 90%的能量
sum(Sig2[:2]) 前两个值能获得的能量
sum(Sig2[:3]) 前三个值能获得的能量
'''