稀疏矩阵上的余弦相似度

机器算法验证 聚类
2022-03-13 11:10:58

我正在尝试实现基于项目的过滤,具有代表购买(1)或未购买(0)特定产品的消费者的大特征空间。

我有一个长尾分布,所以矩阵非常稀疏。R 处理得不好。我可以做些什么来简化余弦相似度的测量?

4个回答

使用Matrix包存储矩阵,使用skmeans_xdist函数计算余弦距离。

/edit: 看来这个skmeans_xdist功能效率不是很高。这是一个简单的示例,说明如何计算 R 中 netflix 大小的矩阵的余弦相似度。

首先,构建矩阵:

library(Matrix)
set.seed(42)
non_zero <- 99000000
i <- sample(1:17770, non_zero, replace=TRUE)
j <- sample(1:480189, non_zero, replace=TRUE)
x <- sample(1:5, non_zero, replace=TRUE)
m <- sparseMatrix(i=i,j=j,x=x) #Rows are movies, columns are users
m <- drop0(m)

接下来对每一行进行归一化,使其矢量距离为 1。这在我的机器上需要 85 秒。

row_norms <- sqrt(rowSums(m^2))
row_norms <- t(crossprod(sign(m), Diagonal(x=row_norms)))
row_norms@x <- 1/row_norms@x
m_norm <- m * row_norms

最后,我们可以找到余弦相似度,这需要我 155 秒

system.time(sim <- tcrossprod(m_norm))

另外,请注意余弦相似度矩阵非常稀疏,因为许多电影不共享任何共同的用户。您可以使用 转换为余弦距离1-sim,但这可能需要一段时间(我没有计时)。

/edit 几年后:这是一个更快的行规范化函数:

row_normalize <- function(m){
  row_norms <- sqrt(rowSums(m^2))
  row_norms <- t(crossprod(sign(m), Diagonal(x=row_norms)))
  row_norms@x <- 1/row_norms@x
  m_norm <- m * row_norms
  return(m_norm)
}

fast_row_normalize <- function(m){
    d <- Diagonal(x=1/sqrt(rowSums(m^2)))
    return(t(crossprod(m, d)))
}

library(microbenchmark)
microbenchmark(
  a = row_normalize(m),
  b = fast_row_normalize(m),
  times=1
)

新功能只需要 25 秒(而另一个需要 89 秒——我猜我的电脑变慢了 =/):

Unit: seconds
 expr      min       lq     mean   median       uq      max neval
    a 89.68086 89.68086 89.68086 89.68086 89.68086 89.68086     1
    b 24.09879 24.09879 24.09879 24.09879 24.09879 24.09879     1

R 通常不能很好地扩展到大数据。您可能需要转向更高效的实施。周围有很多选择。但是,当然,可能还有各种 R 包可以帮助您更进一步。

此外,停止思考矩阵也是值得的您正在使用的是一个图表1 是边,0 不是。在这里加速计算相似性的一种简单方法是巧妙地利用相似性。这个顺便说一句。例如,通过在 Hadoop 中以“列形式”处理数据所获得的好处几乎是一样的。

当你意识到余弦相似度由三个分量组成:A 和 B 的乘积、A 的长度和 B 的长度时,你会注意到两部分独立于另一个向量,而第三部分具有平方稀疏性,这将大大减少余弦相似度“矩阵”所需的计算(再次,不要将其视为矩阵)

那么精简是:

  1. 计算每个单个向量的长度,以进行归一化(即计算|A|
  2. 对于每个属性 (!) 向每对非零条目发送一条消息。如果个非零值,则这将只是消息。0.01cO(0.0001cn2)
  3. 计算每对收到的消息数,这是,除以.AB|A||B|

或者:

  1. 将每个向量标准化为长度(您的矩阵将不再是二进制的!)|A|=1
  2. 对于每个属性 (!),将带有两个值的乘积的消息发送到每对非零条目。如果个非零值,则这将只是消息。0.01cO(0.0001cn2)
  3. 对每对收到的消息求和,这就是余弦相似度。(不需要除法,因为|A|=|B|=1

并且一定要考虑如何在内存中存储组织数据。在这里,快速数据访问和操作是成本的 90%,实际计算是微不足道的。不要让 R 自动执行它,因为这可能意味着它做错了……

mcl 软件 ( http://micans.org/mcl ) 的一部分是 mcxarray 程序,专门用于快速进行所有与所有比较,包括余弦相似度。它利用稀疏表示,并且可以在不同的 CPU(带有线程)和不同的机器(带有作业)上并行化,并且可以轻松地重新集成计算部分。免责声明 - 它是我写的。

我正在使用gensim,它非常适用于通常是高维和稀疏的文本数据