从教堂语料库中有效构造余弦相似矩阵

时间:2017-09-05 02:39:03

标签: sparse-matrix cosine-similarity chapel

我有一个V TF / IDF向量,因此它们非常稀疏 这是一个大约2,500乘150,000的阵列 我想计算语料库中每个文档之间的余弦相似度。

这几乎是我能想到的最天真的方式。我已经知道了三四个优化,但我不想假设答案。我想知道在这个计算中使用Chapel的计算效率最高的方法。目标是将X作为对称矩阵diag(X) = 0

use Norm,
    LinearAlgebra;

var ndocs = 2500,
    nftrs = 150000,
    docs = 1..ndocs,
    ftrs = 1..nftrs,
    V: [docs, ftrs] real,
    X: [docs, docs] real;

for i in docs {
  var n1 = norm(V[i,..]);
  for j in (i+1)..ndocs {
    var n2 = norm(V[j,..]);
    var c = dot(V[i,..], V[j,..]) / (n1*n2);
    X[i,j] = c;
    X[j,i] = c;
  }
}

使用

编译
chpl -I/usr/local/Cellar/openblas/0.2.20/include -L/usr/local/Cellar/openblas/0.2.20/lib -lblas cosim.chpl

==更新==

这实际上应该可以编译并运行。原始代码有错误,如@bradcray下面

所示

2 个答案:

答案 0 :(得分:1)

以下是对原始实现的一些改进:

  • 为所有dot(V[i, ..], V[i, ..])预先计算并缓存i到数组中以减少重复计算。
  • 使用1..V.sizeV.domain代替1..V.shape[1]
    • V.shape是根据域大小计算的,而不是存储为字段。
  • 通过并行计算X来利用此程序的令人尴尬的并行性质

有关详细信息,请参阅GitHub issue,了解这些变化及其对时间的影响。

答案 1 :(得分:1)

[Meta:这个问题一直在给我带来压力,因为它长期没有回答。由于使用了“计算效率最高”这个短语,我个人回避了它。实际上,鉴于从一台目标机器或数据集到下一台目标机器或数据集可能发生的变化,很难保证任何解决方案都符合该描述。但是,由于没有其他人加强,我会做一些评论,希望它们可能有用。]

您的代码中有一些突出的东西:

1)除非我遗漏了某些东西,否则你在计算过程中会多次多次计算norm(V[r, ..])。渐渐地说,这表明你正在进行二次加工,只需要线性工作。我建议为每一行计算一次规范并将其存储在一个数组中以避免这种冗余计算:

var normVrow: [docs] real = [r in docs] norm(V[r,..]);

然后,在内部循环中,您可以引用normVrow[i]normVrow[j]

2)由于这是Chapel,你的循环似乎没有交叉循环依赖,而不是使用串行for循环,你应该使用并行forall循环进行此计算。关于是否:

有一个问题

(a)将外部循环更改为forall(这将导致负载不平衡,因为整个迭代空间为三角形),

(b)将两个循环更改为forall循环(这会通过过度分解来帮助解决负载不平衡问题,但可能还会增加开销),或者

(c)将外部循环变为动态调度循环以解决负载不平衡问题。

我的直觉是使用Chapel的dynamic迭代器做选项c:

use DynamicIters;

forall i in dynamic(ndocs) {
  ...
}

3)最后要考虑的是避免三角形迭代空间,只是冗余地计算X[i,j]X[j,i],即使它们具有相同的值。这在共享内存运行中可能没有意义,但如果您在分布式阵列X上进行计算,则可能会减少通信,因为这些矩阵值将由不同的处理器存储。在这种方法中,您可以在forall上使用单个X.domain循环进行迭代,并且默认情况下结果将是良好负载平衡的,而无需动态迭代器。