我正在尝试使用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时如何处理这些未知分数?
我没有任何代码,因为我还没有真正理解如何实现它。
感谢任何帮助!
答案 0 :(得分:9)
您没有“未知功能”,您的数据点不完整。
这实际上是kNN中众所周知的问题,并且有一个彻底验证的模式来处理它。
虽然问题实际上是一个“不完整的数据”问题,但在kNN上下文中,它常常(通常?)被称为稀疏性问题。
在实践中,构建knn模型中的稀疏性问题可能除了有效存储/检索构成模型的数据之外,kNN的关键。
例如,考虑Amazon.com的推荐引擎,其中产品评级为包含列的用户功能和包含行的用户,为了使这个矩阵100%完整,每个亚马逊客户都必须购买和审查亚马逊销售的每一个产品。该矩阵的实际稀疏度必须> 95%。
最常见的技术(据我所知仍然是最先进的技术)被称为 NNMA ,或非负矩阵近似。这种技术通常也被称为错误地为NNMF,其中F代表因子分解。 (NNMA基于分解技术,但结果不是原始数据矩阵的因素。)我提到这一点,因为这个替代术语虽然不正确但被广泛使用,所以我将其包含在我的搜索引擎查询中。
从本质上讲,这种技术可用于从矩阵中删除稀疏性,或换句话说,填充缺失的单元格(即,R行的客户尚未复制C列的产品)。
您可以找到nnma的完整实现,包括Albert Au Yeung Ching-man's blog中的附带教程(在python + numpy中)。
或者,有几个python包(可通过PyPI获得)包含NNMA的打包代码。我只使用了其中一个PyMF,您可以在Google代码中找到它。
这样你就可以看到NNMA如何发挥其魔力,这里是我在python + NumPy中简单但完整的NNMA实现:
import numpy as NP
def 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函数, 只需为每个丢失的单元格传入一个“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。 / p>
答案 1 :(得分:4)
您缺少的是测量距离的方法。 Pearson相关性是最广泛使用的方法之一。余弦距离是另一个。 L1距离(差异的绝对值之和)通常不会给出好的结果。
如果你谷歌,你会发现根据你使用的相似距离处理缺失值的推荐方法是什么。例如,在Pearson中,仅使用由两个用户共同评定的书籍来测量相关性,因此简单地忽略缺失值。这是有道理的,好像两个用户阅读的一小部分书是共同的,很可能意味着有不同的口味。在余弦距离中,缺失值可以假设为零。
另一种常用方法是估算缺失值。例如,您可以首先使用Pearson查找书籍之间的相似性,然后为每个人预测缺失的评级。
答案 2 :(得分:2)
KNN通常对#features敏感。在现实生活中,我希望你会有更多的书。
我会尝试更改功能空间:不是每个文档都有一个功能,也许值得调查使用书籍列表作为功能。
Feature1 = { books with score 1 }
Feature2 = { books with score 2 }
...
现在,您可以为每个功能定义距离 - 可以在两个用户的每两个列表之间使用recall and precision。
这种方法的另一个优点是你可以很容易地为功能赋予权重 - 也许排名为5的书籍列表比用3位排名的书籍更具信息量?
缺点很明显,如果用户A,B用4,5排名一本书,你将无法获得任何提升 - 但是也可以通过添加另一个功能来解决,比较两个用户之间的这些列表..
免责声明:我从来没有测试过这种方法,我也不知道它会如何表现 - 但我认为这是一种值得研究的方法。我认为没有好的方法来确定这个建议是否会产生良好的结果,除了经验测试,可以使用训练集中的cross-validation来完成。
答案 3 :(得分:0)
我的建议是您使用奇异值分解(SVD)对数据集执行降级。这将通过有效减少每本书提供的功能数量来填补这些缺失值。这与潜在语义分析非常相似,请查阅。