赞
踩
本文总结于Peter Harrington的《Machine Learning in Action》的第14章-利用SVD简化数据。
SVD(Singular Value Decomposition, SVD)奇异值分解。在说及SVD之前,我们需要了解一下特征值分解。学过线性代数都知道,如果一个方阵可以拆成如下的形式:
A
v
=
λ
v
Av = \lambda v
Av=λv
则
λ
\lambda
λ称为特征值,而v称为特征向量,如果这n个特征向量线性无关,那么矩阵就可以分解为如下的形式:
A
=
W
Σ
W
−
1
A = W \Sigma W^{-1}
A=WΣW−1
其中,W是由n个特征向量组成的矩阵,
Σ
\Sigma
Σ是由特征值组成的对角阵。通常我们会将W的n个向量进行标准化,即
∣
∣
W
i
∣
∣
2
=
1
或
者
W
T
W
=
1
||W_i||_2 = 1或者W^T W = 1
∣∣Wi∣∣2=1或者WTW=1,此时的n个特征向量为标准正交基,满足
W
T
W
=
I
W^T W = I
WTW=I, 于是矩阵A就可以分解为:
A
=
W
Σ
W
T
A = W \Sigma W^T
A=WΣWT
这就是矩阵的特征值分解。
这里我们注意到,特征值分解用于方阵,但是实际问题中,往往不是方阵,所以为了对非方阵的矩阵进行分解,就需要我们的SVD。这里我们假设矩阵A是一个m*n的矩阵。
于是,一个非方阵
A
m
∗
n
A_{m*n}
Am∗n,可以分解为:
A
m
∗
n
=
U
m
∗
m
Σ
m
∗
n
V
n
∗
n
T
A_{m*n} = U_{m*m}\Sigma_{m*n}V_{n*n}^T
Am∗n=Um∗mΣm∗nVn∗nT
这就是奇异值分解的原理。
假如我们要对某个用户喜欢的电影进行预测,推荐系统发现一部电影该用户还没有看过,它就会计算该电影与用户看过的电影之间的相似度,如果相似度高,那么系统就会认为该用户也喜欢这部电影。所以,基于SVD的推荐系统的第一件事就是定义相似度。在推荐系统的中,相似度的定义一般有三种方式:欧式距离、皮尔逊相关系数和余弦距离。
(1) 基于欧式距离的相似度:
S
i
m
i
l
a
r
i
t
y
=
1
1
+
d
i
s
t
a
n
c
e
Similarity = \frac{1}{1 + distance}
Similarity=1+distance1
如果距离越远,则相似度越小;距离越近,相似度越高。用python可以写为:
from numpy import linalg as la
def ecludSim(inA,inB):
# 计算基于欧式距离的相似度
return 1.0/(1.0 + la.norm(inA - inB))
其中la.norm()
计算范数,默认为2范数。
(2) 基于皮尔逊相关系数的相似度:
S
i
m
i
l
a
r
i
t
y
=
0.5
+
0.5
∗
c
o
r
r
c
o
e
f
(
)
Similarity = 0.5 + 0.5 *corrcoef()
Similarity=0.5+0.5∗corrcoef()
def pearsSim(inA,inB):
# 计算基于皮尔逊相关系数的相似度
if len(inA) < 3 : return 1.0
return 0.5+0.5*corrcoef(inA, inB, rowvar = 0)[0][1]
(3) 基于余弦距离的相似度“:
S
i
m
i
l
a
r
i
t
y
=
0.5
+
0.5
∗
c
o
s
(
θ
)
c
o
s
(
θ
)
=
A
∗
B
∣
∣
A
∣
∣
∣
∣
B
∣
∣
Similarity = 0.5 + 0.5 * cos(\theta) \\ cos(\theta) = \frac{A*B}{||A|| ||B||}
Similarity=0.5+0.5∗cos(θ)cos(θ)=∣∣A∣∣∣∣B∣∣A∗B
def cosSim(inA,inB):
# 计算基于余弦cos距离的相似度
num = float(inA.T*inB)
denom = la.norm(inA)*la.norm(inB)
return 0.5+0.5*(num/denom)
先看代码:
def svdEst(dataMat, user, simMeas, item): ''' 采用SVD奇异值分解的评价函数 :param dataMat: 数据矩阵 :param user: 用户【行数】 :param simMeas: 相似度计算函数 :param item: 待评价的物品 :return: ''' # 获取物品的数量 n = shape(dataMat)[1] simTotal = 0.0; ratSimTotal = 0.0 # 奇异值分解 U,Sigma,VT = la.svd(dataMat) # 选取包含90%能量的奇异值,在这里是前4个Sigma,构成对角矩阵 Sig4 = mat(eye(4)*Sigma[:4]) #arrange Sig4 into a diagonal matrix # 将原数据转化到低维空间中 xformedItems = dataMat.T * U[:,:5] * Sig4.I #create transformed items for j in range(n): # 取出user对物品j的打分 userRating = dataMat[user,j] # 如果打分为0或者j等于待打分物品 ,就跳过 if userRating == 0 or j==item: continue # 计算物品j与物品item在低维空间中的相似度 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 # 返回关于物品item的打分 else: return ratSimTotal/simTotal
(1)首先获取物品的数量,然后进行奇异值分解,选取包含90%能量以上的奇异值。即计算 Σ \Sigma Σ中前多少个奇异值之和能达到所有奇异值和的90%。这里选择的是5,这个5是基于测试数据:
def loadExData2(): return[[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]] # 对其进行奇异值分解有: dataMat = mat(loadExData2()) U, Sigma, VT = la.svd(dataMat) print('Sigma = ',Sigma) print('Ratio = ',sum( Sigma[:5]) / sum(Sigma))
Sigma = [15.77075346 11.40670395 11.03044558 4.84639758 3.09292055 2.58097379
1.00413543 0.72817072 0.43800353 0.22082113 0.07367823]
Ratio = 0.9014360861521987
(2)然后将原数据矩阵转化到低维空间中:
xformedItems = dataMat.T * U[:,:5] * Sig4.I
这句话很多人都不理解,为什么是这个样子的。我后来在机器学习实战的P264中代码对应的公式推导中找到了答案,其实就是一个很简单的线性变换:
A
m
∗
n
≃
U
m
∗
k
Σ
k
∗
n
V
n
∗
k
T
A_{m*n}\simeq U_{m*k}\Sigma_{k*n}V_{n*k}^T
Am∗n≃Um∗kΣk∗nVn∗kT
其中
V
n
∗
k
V_{n*k}
Vn∗k就是A在低维空间中的映射。现在只需要反解出来就可以了:
U
m
∗
k
−
1
A
m
∗
n
≃
U
m
∗
k
−
1
U
m
∗
k
Σ
k
∗
n
V
n
∗
k
T
U
k
∗
m
A
m
∗
n
≃
U
k
∗
m
U
m
∗
k
Σ
k
∗
n
V
n
∗
k
T
由
于
U
k
∗
m
是
正
交
阵
,
有
:
U
k
∗
m
A
m
∗
n
≃
E
k
∗
k
Σ
k
∗
n
V
n
∗
k
T
Σ
k
∗
n
−
1
U
k
∗
m
A
m
∗
n
≃
Σ
k
∗
n
−
1
Σ
k
∗
n
V
n
∗
k
T
于
是
:
V
n
∗
k
T
=
Σ
k
∗
n
−
1
U
k
∗
m
A
m
∗
n
两
边
同
时
转
置
:
V
n
∗
k
=
(
Σ
k
∗
n
−
1
U
k
∗
m
A
m
∗
n
)
T
=
A
m
∗
n
T
U
m
∗
k
Σ
k
∗
n
−
1
U_{m*k}^{-1}A_{m*n}\simeq U_{m*k}^{-1}U_{m*k}\Sigma_{k*n}V_{n*k}^T \\ U_{k*m}A_{m*n}\simeq U_{k*m}U_{m*k}\Sigma_{k*n}V_{n*k}^T \\ 由于U_{k*m}是正交阵,有:\\ U_{k*m}A_{m*n}\simeq E_{k*k}\Sigma_{k*n}V_{n*k}^T \\ \Sigma_{k*n}^{-1}U_{k*m}A_{m*n}\simeq \Sigma_{k*n}^{-1}\Sigma_{k*n}V_{n*k}^T \\ 于是: V_{n*k}^T = \Sigma_{k*n}^{-1}U_{k*m}A_{m*n} \\ 两边同时转置: V_{n*k} = (\Sigma_{k*n}^{-1}U_{k*m}A_{m*n})^T=A_{m*n}^TU_{m*k}\Sigma_{k*n}^{-1}
Um∗k−1Am∗n≃Um∗k−1Um∗kΣk∗nVn∗kTUk∗mAm∗n≃Uk∗mUm∗kΣk∗nVn∗kT由于Uk∗m是正交阵,有:Uk∗mAm∗n≃Ek∗kΣk∗nVn∗kTΣk∗n−1Uk∗mAm∗n≃Σk∗n−1Σk∗nVn∗kT于是:Vn∗kT=Σk∗n−1Uk∗mAm∗n两边同时转置:Vn∗k=(Σk∗n−1Uk∗mAm∗n)T=Am∗nTUm∗kΣk∗n−1
(3)对于每一个物品,计算与物品item在低维空间的中的相似度,遇到user打分为0或者j==item时就跳过。将这些相似度进行累加,对相似度与rating的乘积也进行累加,最后返回两者的比值:
R
a
t
i
o
p
r
e
d
=
r
a
t
S
i
m
T
o
t
a
l
s
i
m
T
o
t
a
l
=
∑
i
s
i
R
i
∑
i
s
i
Ratio_pred = \frac{ratSimTotal}{simTotal} \\ = \frac{\sum_i s_iR_i}{\sum_i s_i}
Ratiopred=simTotalratSimTotal=∑isi∑isiRi
这个比值其实就是预测用户user在物品item上的打分,这个结果就是将用户对于其他物品打分的分值进行加权平均。而这个权值就是相似度。
有了上述的打分函数,我们只需要遍历某个user没有打分的物品,使用打分函数 s v d E s t ( ) svdEst() svdEst()进行打分,最后返回得分最高的N个物品,推荐给用户user。
def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst): ''' 推荐函数 :param dataMat: 数据集 :param user: 待推荐用户 :param N: 推荐的个数 :param simMeas: 相似度函数 :param estMethod: 打分函数 :return: 返回N个推荐的物品及其预测的得分 ''' # 找到用户user没有打分的物品 unratedItems = nonzero(dataMat[user, :].A == 0)[1] #find unrated items # 如果没有未评价的物品,直接返回 if len(unratedItems) == 0: return 'you rated everything' itemScores = [] # 对于用户user没有评价的每个物品 for item in unratedItems: # 计算该物品的得分 estimatedScore = estMethod(dataMat, user, simMeas, item) # 把该物品及其得分加入到list中 itemScores.append((item, estimatedScore)) # 按照得分进行排序,并返回前N个物品 return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N]
SVD除了在推荐系统上有着很好的应用,也能应用在图像压缩上。将图像矩阵进行奇异值分解SVD,然后提取能使能量大于90%的前N个奇异值极其对应的左右奇异值矩阵。原来很大的图像,就可以用简单的左奇异矩阵U和右奇异矩阵V以及奇异值 Σ \Sigma Σ来表示,最后通过三者的乘积就可以复原图片。
优点: 简化数据,去除噪声点,提高算法的结果;
缺点: 数据的转换可能难以理解;
适用于数据类型:数值型。
通过SVD对数据的处理,我们可以使用小得多的数据集来表示原始数据集,这样做实际上是去除了噪声和冗余信息,以此达到了优化数据、提高结果的目的。
奇异值分解(SVD)原理与在降维中的应用
奇异值分解(SVD)原理详解
机器学习实战的P264中代码对应的公式推导
机器学习实战——SVD(奇异值分解)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。