1.SVD算法简介
SVD降维算法(Singular Value Decompositionm,SVD),即在矩阵论中常见的奇异值分解降维。通俗地讲,就是将一个线性变换分解为两个线性变换,一个线性变换代表旋转,一个线性变换代表拉伸。奇异值分解是在机器学习领域应用较为广泛的算法之一,也是学习机器学习算法绕不开的基石,可应用在特征分解、推荐系统、自然语言处理和计算机视觉等各个方面
2.枯燥又简洁的理论推导
SVD降维算法的理论推导让小编觉得很尴尬——学过《矩阵论》这门课的读者,可以直接把这一块儿的内容跳过,进入下一章节;没有学过《矩阵论》的读者,小编也很难像老师在课堂上那样把每一步的原理细细推导讲清楚。此处,还是再一次点亮标题——只作“简洁的理论推导”。
SVD的核心思想是将一个大矩阵转化为几个小矩阵的乘积,如下图所示。
其中,U、S和V分别为分解后的小矩阵,通常更关注S矩阵,S矩阵的每一个值都代表该位置的重要性指标,它与降维算法中的特征值和特征向量的关系类似。
其中n和m都是比较大的数值,代表原始数据;r是较小的数值,表示矩阵分解后的结果用较小的矩阵组合来近似代替。对于奇异值,与特征分解中的特征值类似,在奇异值矩阵中是按照从大到小排序,且奇异值减小得非常快,在大多数情况下,前10%甚至1%的奇异值的和就占了全部的奇异值之和的99%以上的比例。
由于r比n和m小很多,因此原本三个大矩阵(n×n)(n×m)(m×m)可以近似由三个较小的矩阵(n×r)(r×r)(r×m)表示,且原矩阵会在较大程度上保留信息。
由于这个重要的性质,SVD可以用于降维,来做数据压缩和去噪;也可以用于推荐算法,将用户和喜好对应的矩阵做特征分解,进而得到隐含的用户需求来做推荐。同时也可以用于NLP中的算法,比如潜在语义索引(LSI)。
3.SVD算法优缺点
优点:
1.原理和实现步骤都很简单;
2.可实现并行化。
缺点:
1.分解出的矩阵解释性不强,有“黑匣子”的味道;
2.每个基含有不同的鉴别信息和重构信息,且受原始数据的干扰较大。
4.python实战SVD(一)——用SVD算法降维
第一个实战项目,还是以降维开题——创建一个较小的矩阵,模拟用户对商品的评分降维。
import numpy as np
class CSVD(object):
'''
实现svd分解降维应用示例的Python代码
'''
def __init__(self, data):
self.data = data #用户数据
self.S = [] #用户数据矩阵的奇异值序列 singular values
self.U = [] #svd后的单位正交向量
self.VT = [] #svd后的单位正交向量
self.k = 0 #满足self.p的最小k值(k表示奇异值的个数)
self.SD = [] #对角矩阵,对角线上元素是奇异值 singular values diagonal matrix
def _svd(self):
'''
用户数据矩阵的svd奇异值分解
'''
self.U, self.S, self.VT = np.linalg.svd(self.data)
return self.U, self.S, self.VT
def _calc_k(self, percentge):
'''确定k值:前k个奇异值的平方和占比 >=percentage, 求满足此条件的最小k值
:param percentage, 奇异值平方和的占比的阈值
:return 满足阈值percentage的最小k值
'''
self.k = 0
#用户数据矩阵的奇异值序列的平方和
total = sum(np.square(self.S))
svss = 0 #奇异值平方和 singular values square sum
for i in range(np.shape(self.S)[0]):
svss += np.square(self.S[i])
if (svss/total) >= percentge:
self.k = i+1
break
return self.k
def _buildSD(self, k):
'''构建由奇异值组成的对角矩阵
:param k,根据奇异值开放和的占比阈值计算出来的k值
:return 由k个前奇异值组成的对角矩阵
'''
#方法1:用数组乘方法
self.SD = np.eye(self.k) * self.S[:self.k]
#方法2:用自定义方法
e = np.eye(self.k)
for i in range(self.k):
e[i,i] = self.S[i]
return self.SD
def DimReduce(self, percentage):
'''
SVD降维
:param percentage, 奇异值开方和的占比阈值
:return 降维后的用户数据矩阵
'''
#Step1:svd奇异值分解
self._svd()
#Step2:计算k值
self._calc_k(percentage)
print('\n按照奇异值开方和占比阈值percentage=%d, 求得降维的k=%d'%(percentage, self.k))
#Step3:构建由奇异值组成的对角矩阵singular values diagonal
self._buildSD(self.k)
k,U,SD,VT = self.k,self.U, self.SD, self.VT
#Step4:按照svd分解公式对用户数据矩阵进行降维,得到降维压缩后的数据矩阵
a = U[:len(U), :k]
b = np.dot(SD, VT[:k, :len(VT)])
newData = np.dot(a,b)
return newData
def CSVD_manual():
##训练数据集,用户对商品的评分矩阵,行为多个用户对单个商品的评分,列为用户对每个商品的评分
data = np.array([[5, 5, 0, 5],
[5, 0, 3, 4],
[3, 4, 0, 3],
[0, 0, 5, 3],
[5, 4, 4, 5],
[5, 4, 5, 5]])
percentage = 0.9
svdor = CSVD(data)
ret = svdor.DimReduce(percentage)
print('====================================================')
print('原始用户数据矩阵:\n', data)
print('降维后的数据矩阵:\n', ret)
print('====================================================')
if __name__=='__main__':
CSVD_manual()
运行程序,输出如下:
5.python实战SVD(二)——用SVD算法进行音乐推荐
近几年,随着各种短视频和音乐软件日渐火爆,以及各大厂之间的竞争日渐激烈。如何在短时间内让消费者对产品更加感兴趣,如何精准地分析用户心理和用户喜好度,一直是各个大厂孜孜不倦讨论和研究的热门话题。因此,在大厂算法工程师岗位中,日渐独立出一个新的岗位——推荐算法工程师。
借此机会,在这个降维的专题系列里,拓展降维算法的应用实例——应用SVD算法进行音乐推荐(选自《跟着迪哥学python》)。在程序中,首先构建评分矩阵,对其进行SVD分解,再选择待推荐用户,还原得到其对所有歌曲的估测评分值,最后排序,返回结果即可。
由于数据集比较大,且源代码较长,楼主双手奉上源程序和数据集的网盘,关注公众号“码点联盟”(文末有二维码),在后台回复“音乐推荐”即可自动获取网盘资源。
在SVD中所需的数据是用户对商品的打分,但在现在的数据集中,只有用户播放歌曲的情况,并没有实际的打分值,所以,在数据准备阶段,需要定义用户对每首歌曲的评分值。如果一个用户喜欢某首歌曲,他应该经常播放这首歌曲;相反,如果不喜欢某首歌曲,播放次数肯定比较少。
SVD的主体程序如下:
def compute_svd(urm, K):
U, s, Vt = svds(urm, K)
dim = (len(s), len(s))
S = np.zeros(dim, dtype=np.float32)
for i in range(0, len(s)):
S[i,i] = mt.sqrt(s[i])
U = csc_matrix(U, dtype=np.float32)
S = csc_matrix(S, dtype=np.float32)
Vt = csc_matrix(Vt, dtype=np.float32)
return U, S, Vt
def compute_estimated_matrix(urm, U, S, Vt, uTest, K, test):
rightTerm = S*Vt
max_recommendation = 250
estimatedRatings = np.zeros(shape=(MAX_UID, MAX_PID), dtype=np.float16)
recomendRatings = np.zeros(shape=(MAX_UID,max_recommendation ), dtype=np.float16)
for userTest in uTest:
prod = U[userTest, :]*rightTerm
estimatedRatings[userTest, :] = prod.todense()
recomendRatings[userTest, :] = (-estimatedRatings[userTest, :]).argsort()[:max_recommendation]
return recomendRatings
最终得到的结果如下:
6.下篇预告
下一篇,额,小编还没想好,不过,还是以降维为专题。
敬请期待!
欢迎关注公众号“码点联盟”,不定期分享机器学习、深度学习干货,及各种资料大礼包!
另外,这是一个由一线互联网大厂算法工程师及双985研究生创建的公众号,后续会经常分享各类实用机器学习算法技术文章,欢迎交流讨论!