推荐系统的目的是为客户提供可能喜欢(购买)的产品,但从本质上来讲是一个聚类的过程(对客户聚类或者对商品聚类)。对于一个离线的推荐系统来讲,为某个客户推荐出的产品咱们能够经过某种方式知道是否为该客户喜欢的,也就是说咱们能够从该客户处获得标准值。正由于如此,咱们就能够借鉴检验分类模型的F值得分来作检验一个推荐系统的效能。
咱们能够经过将单个用户的准确率(或召回率)作累加,便可获得整个推荐系统的准确率(或召回率),以下:\[Precssion\left ( u \right )= \frac{\sum_{u\in U} R\left ( u \right )\bigcap T\left ( u \right )}{\sum _{u\in U}R\left ( u \right )}\] css
\[Recall\left ( u \right )= \frac{\sum_{u\in U} R\left ( u \right )\bigcap T\left ( u \right )}{\sum _{u\in U}T\left ( u \right )}\]
其中\(R(u)\)是为用户u作出的推荐列表,而\(T(u)\)是用户在测试集上的真正的行为列表,经过以下公式能够计算出整个推荐系统的效能:
\[F_{\beta }= \frac{\left ( 1+\beta \right )^{2}Recall\cdot Precision}{\beta ^{2}\cdot Recall+Precision}\] F得分取值范围为[0,1]之间,即权衡了准确率又兼顾了召回率,有了F得分就能够评价推荐系统的效能,尤为在多个推荐系统之间作比较时就能评判出推荐系统的优劣python
覆盖率是指推荐系统对全部的用户推荐的全部产品列表中,出现的产品与数据库中在销售的全部产品的占比(不考虑每一个商品被推荐的次数)。在一个在线购物网站,咱们但愿推荐系统推荐的商品列表尽量多地包含数据库中的在售商品,若是一个推荐系统的全部推荐商品列表都未出现某种商品,那么这种商品的销量将会愈来愈少,这是在线购物网站不肯意看到的。所以,咱们但愿提升覆盖率,覆盖率的计算公式以下:\[Coverage=\frac{\left |\cup _{u\in U} R\left ( u \right ) \right |}{\left | I \right |}\]其中I表示在售商品的总数
在考虑每一个商品推荐次数的状况下,咱们能够经过计算每一个商品出现的频率,用信息论中的熵或者基尼系数来度量一个推荐系统的覆盖率:\[H=-\sum _{i=1}^{n}p_{i}\ln p_{i}\]或
\[Gini=\sum _{i=1}^{n}p_{i}\left ( 1-p_{i} \right )\]数据库
多样性指一个推荐系统根据用户购买行为为用户推荐的商品的种类状况,若是一个用户购买了某种商品(青岛啤酒),推荐系统总是为该用户推荐其余厂商的啤酒,那么咱们说这个推荐系统推荐的产品不具备多样性(不是一个好的推荐系统),一个好的推荐系统可能会想到为用户推荐购买产品相近且不一样类别的产品,如能够推荐一个开瓶器等。
与多样性相对应的另一个指标是惊喜度,惊喜度来自于和客户喜欢的物品不类似,可是客户却以为满意(会购买)的推荐,好比上面说的为买啤酒的客户推荐一个尿布等。
上面几个指标是做为推荐系统效能的离线指标,通常指标值都达到最好时该推荐系统效果最好,可是通常状况下不可能都达到最好,所以在评价多个推荐系统效能时得综合考虑这几个指标,选择较好的部署。dom
假设已经有n我的对m个商品进行评价,其评价矩阵\(A_{mxn}\)反应了每一个人对每一个商品的喜爱程度(行为商品,列为用户)。通常来讲,一我的对商品或者某种事物的直观感觉会受到该商品或事物属性值的影响,好比一个单身狗最近打算找一个对象,在众多的女生中她选中了女生D,而他本人喜欢女生的类型具备漂亮的脸蛋、火辣的身材,白皙的皮肤等,这些特色就是他对美眉特征的一种偏好,而每一个女生在脸蛋、身材、皮肤等特征上都有必定值,对于一个理性的单身狗来讲,他选择的D美眉必定是一个在他欣赏的属性中具备较高值的女生,而在商品推荐中也是如此。虽然咱们从矩阵A中只看到了每一个用户对每一个商品的评价,可是咱们能够认为这个评价值是用户对商品的属性值的偏心程度与每一个商品在这些属性上的表现综合结果,这里的属性就是咱们所说的隐变量。基于这样的思想,咱们能够将矩阵A分解成为“商品—商品属性”的评价矩阵\(U_{mxk}\)与“商品属性—客户喜爱”矩阵\(V_{kxn}\)的乘积,及:\[A_{m\times n}=U_{m\times k} V_{K\times n}\]
这里的k能够看作是分解的商品的隐属性个数,举个栗子,假设有Ben、Tom、John、Fred对6种商品的评价状况以下,评分越高表明对该商品越喜欢(0表示未评价)函数
Ben | Tom | John | Fred | |
product1 | 5 | 5 | 0 | 5 |
product2 | 5 | 0 | 3 | 4 |
product3 | 3 | 4 | 0 | 3 |
product4 | 0 | 0 | 5 | 3 |
product5 | 5 | 4 | 4 | 5 |
product6 | 5 | 4 | 5 | 5 |
转化为矩阵A为:\[A=\begin{bmatrix} 5 &5 &0 &5 \\ 5 &0 &3 &4 \\ 3 &4 &0 &3 \\ 0 &0 &5 &3 \\ 5 &4 &4 &5 \\ 5 &4 &5 &5 \end{bmatrix}\]测试
如今,咱们的目的是要将矩阵A分解成为两个矩阵的乘积。在整个过程当中有三个问题须要考虑。第一,采用什么样的分解方法,怎样使得分解后的矩阵乘积尽可能逼进矩阵A;第二,用几个因变量来分解A矩阵合适,即分解后的矩阵U的列是多少合适;第三,对于一个客户,怎么经过矩阵分解后的矩阵为他提供推荐。
下面先来讲说第一个问题,要使得矩阵A能分解成U与V的乘积,也就是说对于A矩阵中的全部元素,要与矩阵U与矩阵V的乘积对应元素差距尽量小,这种想法咱们能够借鉴一下作回归分析时求回归系数的训练方法,及构造一个损失函数,对损失函数采用梯度降低的方式求得分解矩阵元素值。对于K个因变量构造一个损失函数:\[J\left ( U,V;A \right )=\sum _{i=1}^{m}\sum _{j=1}^{n}\left ( a_{ij}-\sum _{r=1}^{k}u_{ir}\cdot v_{rj} \right )^{2}+\lambda \left ( \sum _{i=1}^{m} \sum _{r=1}^{k}u_{ir}^{2}+\sum _{j=1}^{n}\sum _{r=1}^{k}v_{r=1}^{2}\right )\]损失函数的右边部分是L2正则化项(对应于redge回归的正则化),能够下降解决过拟合问题致使分解后的矩阵元素太大,对损失函数求梯度:
\[\left\{\begin{matrix} \frac{\partial J\left ( U,V;A \right )}{\partial u_{ir}}=-2\left ( a_{ij}-\sum _{r=1}^{k}u_{ir}v_{rj}\right )\cdot v_{rj}+2\lambda u_{ir}\\ \frac{\partial J\left ( U,V;A \right )}{\partial v_{rj}}=-2\left ( a_{ij}-\sum _{r=1}^{k}u_{ir}v_{rj}\right )\cdot u_{ir}+2\lambda v_{rj} \end{matrix}\right.,1\leq r\leqslant k\]
有了梯度,咱们就能够先随机给定“商品—商品属性”矩阵U和“商品属性—客户喜爱”矩阵V一些初始值,而后对损失函数经过SGD方式获得,代码以下:网站
import numpy as np import math def lfm(a,k): ''' 参数a:表示须要分解的评价矩阵 参数k:分解的属性(隐变量)个数 ''' assert type(a) == np.ndarray m, n = a.shape alpha = 0.01 lambda_ = 0.01 u = np.random.rand(m,k) v = np.random.randn(k,n) for t in range(1000): for i in range(m): for j in range(n): if math.fabs(a[i][j]) > 1e-4: err = a[i][j] - np.dot(u[i],v[:,j]) for r in range(k): gu = err * v[r][j] - lambda_ * u[i][r] gv = err * u[i][r] - lambda_ * v[r][j] u[i][r] += alpha * gu v[r][j] += alpha * gv return u,v
#对前面提到的评价矩阵A做分解,先选择三个隐变量(k=3) A = 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]]) b,c = lfm(A,3) #查看“商品—商品属性”矩阵b和“商品属性—客户喜爱”矩阵c b,c
(array([[-0.2972852 , 2.01461188, 1.0310134 ], [-0.41028699, 0.88314041, 1.70740435], [-0.69015017, 1.4846616 , 0.22246443], [ 1.58422492, 1.15064457, 0.98881608], [-0.13167643, 1.63106585, 1.39457599], [ 0.36301204, 1.78414273, 1.44277207]]), array([[-0.81521492, -0.8423817 , 1.25938865, -0.23881637], [ 1.32574586, 2.24986472, 1.49232807, 1.71803 ], [ 2.00802709, 0.18698412, 1.27586124, 1.42864871]]))
#查看b与c乘积 np.dot(b,c)
array([[ 4.98351751, 4.97581494, 3.94749428, 5.00511619], [ 4.933806 , 2.65182221, 2.97963547, 4.054526 ], [ 2.97761928, 3.96325495, 1.63026864, 3.03333586], [ 2.21954795, 1.43916544, 4.97388618, 3.01117386], [ 5.07006975, 4.0413629 , 4.047539 , 4.82602576], [ 4.9665124 , 3.97806056, 4.96047648, 5.03973199]])
咱们看到分解后的两个矩阵乘积能够大体还原矩阵A,并且还原后的矩阵对原来用户没有评价的商品已经有了评价值,咱们或许能够相信,该值即为用户对该种商品的评价值的预测值(Tom对产品2的评价可能为2.7,John对产品1的评价可能为5.9),这就回答了上面提到的第三个问题。
上面的第一个问题与第三个问题已经解决,并且真的按照咱们的思路将矩阵A分解成了两个矩阵的乘积。咱们再看看第二个问题,咱们分解出几个商品属性(隐变量)合适呢?这个问题我确实没有在文献上的看到有较完整的说明,或许能够从实验的角度多取几个K值来人为地检测其效果,好比本例,若是知道这几我的的喜爱,而后取不一样的K值来检验结果。
LFM仅矩阵分解中的一种方式,下一篇咱们再讨论另外一种特别有名气的分解—SVD分解。spa