本文根据最近学习机器学习书籍网络文章的情况,特将一些学习心得做了总结,详情如下.如有不当之处,请各位大拿多多指点,在此谢过。
一、概述
1、相关背景
在日常生活中,我们经常接触到的餐馆可以划分为很多类别,比如美式、中式、日式、牛排馆、素食店,等等。是否考虑过这些类别真的够用吗?或许有人喜欢这些混合类别,或者类似中式素食店那样的子类别。如何才知道到底有多少类餐馆呢?当然,可以咨询相关专家。但如果某个专家说应该按照调料分类,另外一个专家却说应该按照配料分类,那么我们该怎么办呢?这时,我们可以自己从数据着手,可以对记录用户关于餐馆观点的数据进行处理,且从中提取出背后的看法。
这些因素可能会与餐馆的类别、烹饪时所使用的某个特定配料,或其他任意对象一致。然后,就可以利用这些因素来估计人们对没有去过的餐馆的看法。
提取这些信息的方法称为奇异值分解(Singular Value Decomposition,SVD)。从生物信息学到金融学等在内的很多应用中,SVD都是提取信息的强大工具。
2、相关概念
2.1、 特征值
如果说一个向量 v 是方阵 A 的特征向量, 则一定可以表示成下面的形式:
这时候 λ 就被称为特征向量 v 对应的特征值, 一个矩阵的一组特征向量是一组正交向量.
2.2、 特征值分解
特征值分解将一个矩阵分解成下面的形式:
其中Q是这个矩阵A的特征向量组成的矩阵,Σ是一个对角阵,每一个对角线上的元素就是一个特征值。我这里引用了一些参考文献中的内容来说明一下。首先,要明确的是,一个矩阵其实就是一个线性变换,因为一个矩阵乘以一个向量后得到的向量,其实就相当于将这个向量进行了线性变换。特征值分解的局限在于变换的矩阵必须是方阵。
2.3、 奇异值分解 (Singular Value Decomposition,SVD)
由于特征值分解只适用于方阵, 所以当我们需要描述 m*n 的矩阵的重要特征时, 可以使用奇异值分解, 它能适用于任意矩阵的分解:
假设A是一个N * M的矩阵,那么得到的U是一个N * N的方阵(里面的向量是正交的,U里面的向量称为左奇异向量),Σ是一个N * M的矩阵(除了对角线的元素都是0,对角线上的元素称为奇异值),V’(V的转置)是一个N * N的矩阵,里面的向量也是正交的,V里面的向量称为右奇异向量),从图片来反映几个相乘的矩阵的大小可得下面的图片。
2.4、 奇异值
将2. 3 中的矩阵 Σ 的对角元素就称之为奇异值; 与特征值一样, 这些奇异值标识了数据集中的重要特征. 奇异值和特征值的关系为: 奇异值是矩阵 Data * DataT 特征值的平方根。
2.5、相似度计算
第一种:使用欧式距离,相似度=1/(1+距离)
当距离为0的时候,相似度为1;当距离很大的时候,相似度趋近于0
第二种:皮尔逊相关系数
皮尔逊相关系数度量的是两个向量之间的相似度,相对于欧式距离的一个优势是,它对用户评级的量级并不敏感。
皮尔逊相关系数的取值范围在-1到+1之间,在NumPy中由函数corrcoef()计算
第三种:余弦相似度
余弦相似度计算的是两个向量夹角的余弦值,如果夹角为90度,则相似度为0;如果两个向量的方向相同,则相似度为1
余弦相似度的取值范围在-1到+1之间,在NumPy中由函数linalg.norm()计算
3、奇异值分解优缺点
3.1、 优点: 简化数据,去除噪声提高算法的结果。
3.2、 缺点: 数据的转换可能难以理解。
3.3、 适用数据类型:数值型数据。
二、 应用场景
1、信息检索-隐性语义检索(Lstent Semantic Indexing, LSI)或 隐形语义分析(Latent Semantic Analysis, LSA)
隐性语义索引:矩阵 = 文档 + 词语
最早的 SVD 应用之一就是信息检索,我们称利用 SVD 的方法为隐性语义索引(LSI)或隐性语义分析(LSA)。
在LSA中,一个矩阵是由文档和词语组成的。当我们在该矩阵上应用SVD时,就会构建出多个奇异值。这些奇异值代表了文档中的概念或主题,这一特点可以用于更高效的文档搜索。
2、推荐系统
SVD的另一应用是推荐系统。简单版本的推荐系统能够计算项或者人之间的相似度。更先进的方法则先利用SVD从数据中构建一个主题空间,然后再在该空间下计算其相似度(从高维-低维空间的转化,在低维空间来计算相似度,SVD 提升了推荐系统的效率。)。考虑下图中给出的矩阵,它是由菜馆的菜和品菜师对这些菜的意见构成的。品菜师可以采用1到5之间的任意一个整数来对菜评级。若品菜师没有尝过某道菜,则其评级为0。
菜馆的菜及其评级的数据。对此矩阵进行SVD处理则可以将数据压缩到若干概念中去。在右边的矩阵中,标出了一个概念。
上图右边标注的为一组共同特征,表示美式 BBQ 空间;另一组在上图右边未标注的为日式食品 空间。
3、图像压缩
例如:32*32=1024 => 32*2+2*1+32*2=130
(2*1表示去掉了除对角线的0), 几乎获得了10倍的压缩比。
三、SVD工作原理
1、矩阵分解
1.1 矩阵分解是将数据矩阵分解为多个独立部分的过程。
1.2 矩阵分解可以将原始矩阵表示成新的易于处理的形式,这种新形式是两个或多个矩阵的乘积。(类似代数中的因数分解)
1.3 举例:如何将12分解成两个数的乘积?(1,12)、(2,6)、(3,4)都是合理的答案。
2、SVD 是矩阵分解的一种类型,也是矩阵分解最常见的技术
2.1 SVD 将原始的数据集矩阵 Data 分解成三个矩阵 U、∑、V
2.2 举例:如果原始矩阵 \(Data_{m*n}\) 是m行n列,
-
\(U_{m*n}\) 表示m行n列
-
\(∑_{m*k}\) 表示m行k列
-
\(V_{k*n}\) 表示k行n列。
\(Data_{m*n} = U_{m*k} * ∑_{k*k} * V_{k*n}\)
-
上述分解中会构建出一个矩阵∑,该矩阵只有对角元素,其他元素均为0(近似于0)。另一个惯例就是,∑的对角元素是从大到小排列的。这些对角元素称为奇异值。
-
奇异值与特征值(PCA 数据中重要特征)是有关系的。这里的奇异值就是矩阵 \(Data * Data^T\) 特征值的平方根。
-
普遍的事实:在某个奇异值的数目(r 个=>奇异值的平方和累加到总值的90%以上)之后,其他的奇异值都置为0(近似于0)。这意味着数据集中仅有 r 个重要特征,而其余特征则都是噪声或冗余特征。
四、推荐系统
1、概述
利用网络平台向访问者推送商品信息和提供建议,协助客户进行购物决策,模拟导购帮助顾客完成购买过程。
2、 应用场景
2.1 Amazon 会根据顾客的购买历史向他们推荐物品;
2.2 Netflix 会向其用户推荐电影;
2.3 新闻网站会对用户推荐新闻频道。
3、相关要素
3.1 基于协同过滤(collaborative filtering) 的推荐引擎
-
利用Python 实现 SVD(Numpy 有一个称为 linalg 的线性代数工具箱);
-
协同过滤:是通过将用户和其他用户的数据进行对比来实现推荐的;
-
当知道了两个用户或两个物品之间的相似度,我们就可以利用已有的数据来预测未知用户的喜好。
3.2 基于物品的相似度和基于用户的相似度:物品比较少则选择物品相似度,用户比较少则选择用户相似度。
-
基于物品的相似度:计算物品之间的距离。【耗时会随物品数量的增加而增加】
-
由于物品A和物品C 相似度(相关度)很高,所以给买A的人推荐C
-
基于用户的相似度:计算用户之间的距离。【耗时会随用户数量的增加而增加】
-
由于用户A和用户C 相似度(相关度)很高,所以A和C是兴趣相投的人,对于C买的物品就会推荐给A
3.3 计算相似度
3.3.1、inA, inB 对应的是 列向量
1)、欧氏距离
指在m维空间中两个点之间的真实距离,或者向量的自然长度(即该点到原点的距离)。二维或三维中的欧氏距离就是两点之间的实际距离。
-
相似度= 1/(1+欧式距离)
-
相似度= 1.0/(1.0 + la.norm(inA - inB))
-
物品对越相似,它们的相似度值就越大
2)、皮尔逊相关系数
度量的是两个向量之间的相似度。
-
相似度= 0.5 + 0.5*corrcoef() 【皮尔逊相关系数的取值范围从 -1 到 +1,通过函数0.5 + 0.5*corrcoef()这个函数计算,把值归一化到0到1之间】;
-
相似度= 0.5 + 0.5 * corrcoef(inA, inB, rowvar = 0)[0][1];
-
相对欧氏距离的优势:它对用户评级的量级并不敏感。
3)、余弦相似度
计算的是两个向量夹角的余弦值。
-
余弦值 = (A·B)/(||A||·||B||) 【余弦值的取值范围也在-1到+1之间】;
-
相似度= 0.5 + 0.5*余弦值;
-
相似度= 0.5 + 0.5*( float(inA.T*inB) / la.norm(inA)*la.norm(inB));
-
如果夹角为90度,则相似度为0;如果两个向量的方向相同,则相似度为1.0。
3.4 推荐系统评价
-
采用交叉测试的方法。【拆分数据为训练集和测试集】;
-
推荐引擎评价的指标: 最小均方根误差(Root mean squared error, RMSE),也称标准误差(Standard error),就是计算均方误差的平均值然后取其平方根。
3.5 基本原理
1)、推荐系统的工作过程:给定一个用户,系统会为此用户返回N个最好的推荐菜。
2)、实现流程大致如下:
-
寻找用户没有评级的菜肴,即在用户-物品矩阵中的0值。
-
在用户没有评级的所有物品中,对每个物品预计一个可能的评级分数。这就是说:我们认为用户可能会对物品的打分(这就是相似度计算的初衷)。
-
对这些物品的评分从高到低进行排序,返回前N个物品。
五、案例一:餐馆菜肴推荐引擎
1、项目概述
假如有一个人计划外出吃饭,但他并不知道去哪里吃饭以及该点什么菜。我们这个推荐系统可以帮他做到这两点。
2、收集数据
图14-4 给出了一个更真实的矩阵的例子,如下所示。
3、分析数据
不是重点,不做过多解读。
4、 训练算法
这里借助于recommend()函数进行推荐。
推荐系统的过程是: 给定一个用户,系统会为此用户返回N个最好的推荐菜。为了实现这一点,则需要做到:
-
寻找用户没有评级的菜肴,即在用户-物品矩阵中的0值;
-
在用户没有评级的所有物品中,对每个物品预计一个可能的评级分数。即:我们认为用户可能会对物品打分;
-
对这些物品的评分从高到低进行排序,返回前N个物品。
4.1 基于物品的相似度
基于物品相似度的推荐引擎:
def standEst(dataMat, user, simMeas, item):
n = shape(dataMat)[1]
simTotal = 0.0; ratSimTotal = 0.0
for j in range(n):
userRating = dataMat[user,j]
if userRating == 0: continue
overLap = nonzero(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
4.2 利用SVD提高推荐效果
基于SVD的评分估计
def svdEst(dataMat, user, simMeas, item):
n = shape(dataMat)[1]
simTotal = 0.0; ratSimTotal = 0.0
U,Sigma,VT = la.svd(dataMat)
Sig4 = mat(eye(4)*Sigma[:4]) #arrange Sig4 into a diagonal matrix
xformedItems = dataMat.T * U[:,:4] * Sig4.I #create transformed items
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
最后的推荐结果
def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst):
unratedItems = nonzero(dataMat[user,:].A==0)[1]#find unrated items
if len(unratedItems) == 0: return 'you rated everything'
itemScores = []
for item in unratedItems:
estimatedScore = estMethod(dataMat, user, simMeas, item)
itemScores.append((item, estimatedScore))
return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N]
5、应用模型算法输入
#菜馆菜肴推荐引擎
myMat = mat(loadExData()) #调入原始矩阵
#该矩阵对于展示SVD的作用非常好,但本身不是很有趣,因此对其中一些值进行修改
myMat[0,1] = myMat[0,0]=myMat[1,0]=myMat[2,0]=4
myMat[3,3] =2
#现在的矩阵是如下
myMat
输出
matrix([[4, 4, 0, 2, 2],
[4, 0, 0, 3, 3],
[4, 0, 0, 1, 1],
[1, 1, 1, 2, 0],
[2, 2, 2, 0, 0],
[5, 5, 5, 0, 0],
[1, 1, 1, 0, 0]])
输入
#现在可以做些推荐。先尝试默认推荐
recommend(myMat,2)
输出
the 1 and 0 similarity is: 1.000000
the 1 and 3 similarity is: 0.928746
the 1 and 4 similarity is: 1.000000
the 2 and 0 similarity is: 1.000000
the 2 and 3 similarity is: 1.000000
the 2 and 4 similarity is: 0.000000
[(2, 2.5), (1, 2.0243290220056256)]
这表明用户2(由于我们从0开始计数,因此这对应于矩阵的第3行)对物品2的预测评分
值为2.5,对物品1的预测评分值为2.05.下面利用其他的相似度计算方法来进行推荐。
recommend(myMat, 2,simMeas = ecludSim)
则
the 1 and 0 similarity is: 1.000000
the 1 and 3 similarity is: 0.309017
the 1 and 4 similarity is: 0.333333
the 2 and 0 similarity is: 1.000000
the 2 and 3 similarity is: 0.500000
the 2 and 4 similarity is: 0.000000
[(2, 3.0), (1, 2.8266504712098603)]
输入
#下面计算该矩阵的SVD来了解其到底需要多少维特征。
from numpy import linalg as la
U,Sigma, VT = la.svd(mat(loadExData2()))
Sigma
输出
array([ 15.77075346, 11.40670395, 11.03044558, 4.84639758,
3.09292055, 2.58097379, 1.00413543, 0.72817072,
0.43800353, 0.22082113, 0.07367823])
输入
#基于SVD的评分估计
recommend(myMat, 1 ,estMethod = svdEst)
输出
the 1 and 0 similarity is: 0.498142
the 1 and 3 similarity is: 0.498131
the 1 and 4 similarity is: 0.509974
the 2 and 0 similarity is: 0.552670
the 2 and 3 similarity is: 0.552976
the 2 and 4 similarity is: 0.217301
[(2, 3.4177569186592387), (1, 3.3307171545585645)]
输入
#尝试另外一种相似度计算方法
recommend(myMat , 1 , estMethod = svdEst, simMeas = pearsSim)
输出
the 1 and 0 similarity is: 0.280908
the 1 and 3 similarity is: 0.948940
the 1 and 4 similarity is: 0.606039
the 2 and 0 similarity is: 0.556905
the 2 and 3 similarity is: 0.552447
the 2 and 4 similarity is: 0.214974
[(2, 3.4205197987866813), (1, 3.1530093683701281)]
6、模型问题分析
上面展示了推荐引擎的工作流程及SVD将数据映射为重要特征的过程。在撰写代码时,这里尽量保证它们的可读性,但却无法保证代码的执行效率。
推荐引擎中还存在其他很多规模扩展性的挑战问题,比如矩阵的表示方法。上面给出的例子中有很多0,实际系统中0 的数目更多。当然,我们可以通过只存储非零元素来节约内存和计算开销。另一个潜在的计算资源浪费来自于相似度得分计算。
推荐系统还面临另外一个问题,那就是如何在缺失数据时给出好的推荐。这称为冷启动(cold-start)问题,处理起来十分困难。该问题换句话讲,用户不会喜欢一个无效物品,而用户不喜欢的物品又无效。
冷启动问题的解决方案就是将推荐看成是搜索问题。在上面菜肴推荐例子中,可以通过各自标签来标记菜肴;同时也可以将这些属性作为相似度计算所需要的数据,这被称为基于内容(content-based)的推荐。
六、案例二:基于SVD的图像压缩
1、项目背景
面对一个原始的图像大小是32*32×*=1024像素的图片,我们需要将使用更少的像素来表示这张图片,且保证图片不变形。
2、 样本数据格式
0_5.txt
00000000000000110000000000000000
00000000000011111100000000000000
00000000000111111110000000000000
00000000001111111111000000000000
00000000111111111111100000000000
00000001111111111111110000000000
00000000111111111111111000000000
00000000111111100001111100000000
00000001111111000001111100000000
00000011111100000000111100000000
00000011111100000000111110000000
00000011111100000000011110000000
00000011111100000000011110000000
00000001111110000000001111000000
00000011111110000000001111000000
00000011111100000000001111000000
00000001111100000000001111000000
00000011111100000000001111000000
00000001111100000000001111000000
00000001111100000000011111000000
00000000111110000000001111100000
00000000111110000000001111100000
00000000111110000000001111100000
00000000111110000000011111000000
00000000111110000000111111000000
00000000111111000001111110000000
00000000011111111111111110000000
00000000001111111111111110000000
00000000001111111111111110000000
00000000000111111111111000000000
00000000000011111111110000000000
00000000000000111111000000000000
3、实现过程
def imgCompress(numSV=3, thresh=0.8):
myl = []
for line in open('0_5.txt').readlines():
newRow = []
for i in range(32):
newRow.append(int(line[i]))
myl.append(newRow)
myMat = mat(myl)
print("****original matrix******")
printMat(myMat, thresh)
U,Sigma,VT = la.svd(myMat)
SigRecon = mat(zeros((numSV, numSV)))
for k in range(numSV):#construct diagonal matrix from vector
SigRecon[k,k] = Sigma[k]
reconMat = U[:,:numSV]*SigRecon*VT[:numSV,:]
print("****reconstructed matrix using %d singular values******" % numSV)
printMat(reconMat, thresh)
4、效果分析
由于输出结果长度较长,笔者这里不再贴出,感兴趣的读者可以自行输出下看看效果。从最终的输出结果来看,只需要两个奇异值就能相当精确地对图像实现重构。那么,最终我们需要多少个0-1的数字来重构图像呢?U和V^T都是32*2的矩阵,有两个奇异值,所以,总数字数目是64+64+2=130,和最初的1024相比,获得了将近10倍的压缩比。
七、小结
SVD是这一种强大的降维工具,可以利用SVD来逼近矩阵并从中提取重要特征。通过保留80%~90%的能量,就可以得到重要的特征并去掉噪声。SVD在推荐引擎应用的很成功,当然在其他领域也有更多的应用。
推荐引擎将物品推荐给用户,协同过滤则是一种基于用户喜好或行为数据的推荐实现方法。协同过滤的核心是相似度计算方法,有很多相似度计算方法都可以用于计算物品或用户之间的相似度。通过在低维空间下计算相似度,SVD提高了推荐系统的效果。
在大规模数据集上,SVD的计算和推荐可能是一个很困难的工程问题。但通过离线方式进行SVD分解和相似度计算,是一种减少冗余计算和推荐所需要时间的办法。
关于SVD简化数据方面的内容,笔者先撰写到此,后续有机会写下在大数据集上进行机器学习实现,如有不足之处,希望各位大拿多多指点,在此先行谢过。