7

我正在尝试使用 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“最接近”。但在现实世界中,会有更多的 ?/unknown 分数。那么在使用knn的时候如何处理那些未知的分数呢?

我没有任何代码,因为我还没有真正理解如何实现它。

任何帮助表示赞赏!

4

3 回答 3

10

您没有“未知功能”,您有不完整的数据点。

这实际上是 kNN 中众所周知的问题,并且有一个经过彻底验证的模式来处理它。

尽管该问题实际上是一个“不完整数据”问题,但在 kNN 上下文中,它通常(通常?)被称为稀疏问题。

在实践中,构建 kNN 模型的稀疏性问题是 kNN 的症结所在,除了对构成模型的数据进行有效存储/检索的可能例外。

例如,考虑 Amazon.com 的推荐引擎,其中产品评级是由组成的用户特征和由组成的用户,为了使这个矩阵 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 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。

于 2012-05-07T10:52:40.530 回答
4

您缺少的是测量距离的方法。Pearson 相关是应用最广泛的方法之一。余弦距离是另一个。L1 距离(差异的绝对值之和)通常不会给出好的结果。

如果你用谷歌搜索,你会发现根据你使用的相似距离处理缺失值的推荐方法是什么。例如,在 Pearson 中,仅使用两个用户共同评分的书籍来衡量相关性,因此忽略了缺失值。这是有道理的,就好像两个用户阅读的一小部分书籍是共同的,这很可能意味着他们有不同的品味。在余弦距离中,缺失值可以假设为零。

另一种常用的方法是估算缺失值。例如,您可以首先使用 Pearson 查找书籍之间的相似性,然后为每个人预测缺失的评分。

于 2012-05-07T08:57:30.100 回答
2

KNN 通常对#features 很敏感。在现实生活中,我希望你会有更多的书。

我会尝试更改功能空间:与其为每个文档都设置一个功能,不如使用书籍列表作为功能进行研究。

Feature1 = { books with score 1 }
Feature2 = { books with score 2 }
...

现在,您可以定义每个特征的距离 - 可能通过使用每两个 2 个用户列表之间的召回率和精度。

这种方法的另一个优点是您可以轻松地赋予特征权重-也许排名为 5 的书籍列表比排名为 3 的书籍列表信息量更大?

缺点很明显,如果用户 A、B 将一本书评为 4,5,您将不会获得任何提升 - 但是也可以通过添加另一个功能来解决,比较两个用户之间的这些列表。

免责声明:我从未测试过这种方法,也不知道它会如何表现 - 但我认为这是一种值得研究的方法。我认为除了经验测试之外,没有什么好的方法可以确定这个建议是否会产生好的结果,这可以使用训练集中的交叉验证来完成。

于 2012-05-06T17:45:34.383 回答