我正在尝试使用kNN创建一个简单的推荐系统。
假设我有一个表格:
User | Book1 | Book2 | Book3 | Book4 | Book5 | Book6 | Book7 |1 | 5 | ? | 3 | ? | 4 | 3 | 2 |2 | 3 | 4 | ? | 2 | 3 | 4 | 2 |3 | 4 | 2 | 1 | ? | ? | 3 | 3 |4 | 2 | 5 | 3 | ? | 4 | 1 | 1 |5 | 1 | 1 | 4 | 3 | 1 | ? | 1 |6 | 5 | 2 | 5 | 4 | 4 | 2 | ? |
所以,如果要找出用户1的可能得分,我考虑的是计算用户1阅读的书籍与其他用户的绝对差异。然后,我会使用这个差异来找出列表中与用户1最“接近”的用户。但在现实世界中,会有更多未知分数(?)。那么,在使用kNN时,我该如何处理这些未知分数呢?
我还没有任何代码,因为我还没有真正理解如何实现这个功能。
任何帮助都将不胜感激!
回答:
你没有“未知特征”,你有不完整的数据点。
这实际上是kNN中的一个众所周知的问题,并且有经过充分验证的方法来处理它。
虽然这个问题实际上是一个“不完整数据”问题,但在kNN的背景下,它通常(通常?)被称为稀疏性问题。
在实践中,构建kNN模型时的稀疏性问题,除了可能的有效存储/检索模型数据之外,是kNN的核心问题。
例如,考虑亚马逊的推荐引擎,其中产品评分为用户特征,构成列,用户构成行,要使这个矩阵100%完整,每个亚马逊客户都必须购买并评论亚马逊销售的每一件产品。这个矩阵的实际稀疏度必须超过95%。
最常见的方法(据我所知仍然是最先进的)被称为NNMA,或非负矩阵近似。这种技术也经常被错误地称为NNMF,其中F代表分解。(NNMA基于一种分解技术,但结果不是原始数据矩阵的因子。)我提到这一点是因为这个替代术语虽然不正确,但被广泛使用,所以我会在搜索引擎查询中包括它。
本质上,这种技术可以用来消除矩阵中的稀疏性,或者换句话说,填充缺失的单元格(即,第R行的顾客没有评论第C列的产品)。
你可以在Albert Au Yeung Ching-man的博客上找到一个完整的nnma实现,包括一个配套的教程(使用Python + NumPy)。
或者,有几个Python包(通过PyPI提供)包含了NNMA的打包代码。我只使用过其中一个,PyMF,你可以在Google Code上找到它。
为了让你了解NNMA是如何发挥作用的,这是我在Python + NumPy中实现的简单但完整的NNMA实现:
import numpy as NPdef cf(q, v): """ the cost function """ qv = (q - v)**2 return NP.sum(NP.sum(qv, axis=0))def nnma(d, max_iter=100): x, y = d.shape z = y w = NP.random.rand(x, y) h = NP.random.rand(y, z) for i in range(max_iter): wh = NP.dot(w, h) cost = cf(d, wh) if cost == 0: break hn = NP.dot(w.T, d) hd = NP.dot(NP.dot(w.T, w), h) h *= hn/hd wn = NP.dot(d, h.T) wd = NP.dot(NP.dot(w, h), h.T) w *= wn/wd return NP.dot(w, h)
要使用这个NNMA函数,只需传入一个2D数组(矩阵),对于每个缺失的单元格使用“0”(换句话说,你的数据矩阵,对于每个缺失的值插入一个“0”):
>>> d # the original (sparse) data matrix with missing cells denoted by "0"s array([[ 7., 0., 4., 7., 0., 1.], [ 3., 9., 7., 3., 1., 7.], [ 4., 4., 3., 7., 3., 9.], [ 4., 8., 0., 9., 2., 1.], [ 6., 3., 9., 5., 9., 3.], [ 6., 1., 4., 4., 1., 0.], [ 0., 4., 8., 6., 0., 5.], [ 9., 0., 6., 0., 5., 2.], [ 6., 8., 4., 6., 3., 7.], [ 3., 6., 3., 8., 7., 2.]])>>> d1 = nnma(d) # call nnma, passing in the original data matrix>>> d1 # the approximated data matrix with all missing values populated array([[ 6.998, 0.29 , 3.987, 7.008, 0.292, 0.796], [ 2.989, 8.92 , 6.994, 3.02 , 1.277, 7.053], [ 4.007, 4.496, 2.999, 7.01 , 3.107, 8.695], [ 4.005, 8.019, 0.254, 9.002, 1.917, 0.89 ], [ 5.998, 3.014, 9.001, 4.991, 8.983, 3.052], [ 5.992, 1.077, 4.007, 3.976, 0.753, 0.464], [ 0.346, 3.436, 7.993, 5.988, 0.194, 5.355], [ 9.001, 0.124, 5.997, 0.375, 5.02 , 1.867], [ 6. , 7.994, 3.998, 6. , 2.999, 7.009], [ 2.995, 6.022, 3.001, 7.987, 6.939, 2.185]])
正如你所见,结果还不错,特别是对于一个非常简单的实现。所有缺失的项目都被填充了,其余的值与原始数据矩阵中的对应值非常接近,例如,原始数据矩阵中第0列第0行的值为7.0,在近似矩阵中为6.998。