【问题标题】:Matrix of "for x in vectors: for y in vectors:", without two for loops“for x in vectors: for y in vectors:”的矩阵,没有两个 for 循环
【发布时间】:2021-08-24 03:47:36
【问题描述】:

可以在不使用两个 for 循环的情况下获得两组向量的笛卡尔外积吗?数据量大,速度慢。

[[f(x,y) for x in vectors] for y in vectors]

我正在尝试在 python 中做一个凝聚聚类项目,为此,我需要创建一个距离矩阵。 这是我必须为距离矩阵定义函数的代码:

 def distance_matrix(vectors):
    s = np.zeros((len(vectors), len(vectors)))
    for i in range(len(vectors)):
        for v in range(len(vectors)):
            s[i, v] = dissimilarity(vectors[i], vectors[v])
    return s 

它应该做的是获取一个 NumPy 数组列表并返回一个 2D NumPy 数组 d,其中条目 d[i,j] 应该包含vectors[i] 和vectors[j] 之间的差异。

在这种情况下,向量是 NumPy 数组的列表,相异度的计算公式为:

def dissimilarity(v1, v2):
    return (1-(v1.dot(v2)/(np.linalg.norm(v1)*np.linalg.norm(v2))))

或者换句话说,差异是两个 1D NumPy 数组之间的余弦差异。

我的目标是找到一种方法来获得距离矩阵,而无需双重 for 循环,但计算时间仍然非常小。

【问题讨论】:

    标签: python numpy matrix cluster-analysis distance-matrix


    【解决方案1】:

    一般来说,这是做不到的。一般来说(但不是在这种情况下),computation time 不会改变,因为工作在物理上存在并且存在并且不管会计技巧人们可能会尝试重写它(单个 for 循环,其作用类似于两个 for 循环,或递归实现),在一天结束时,无论您执行的顺序如何,工作仍然必须完成。

    有没有办法摆脱 for 循环?即使它会稍微减慢运行时间。 – Cat_Smithyyyy03

    你的情况很特别。虽然通常没有理由考虑加速是可能的,但您要求考虑张量积,或者在这种情况下是矩阵积。当您考虑一般矩阵乘法 AB=C 时,矩阵乘积 C 基本上是 A 的行空间中的所有向量 a 和 B 的 colspace 中的所有向量的外积 {a dot b}。

    因此,对于您的情况,首先对所有向量进行归一化(因此以后不需要进行归一化),然后将它们堆叠形成 A,然后让 B=transp(A),然后进行矩阵相乘。

      [[a0 a1 a2]     [[a0 [b0     [z0        [aa ab ac .. az
       [b0 b1 b2]       a1  b1  ... z1         ba bb bc .. bz
    (      .       )(   a2] b2]     z2]] ) = (    .     .     )
           .                                      .      .
           .                                      .       .
       [z0 z1 z2]]                             za zb zc    zz]
    

    有趣的是,现在您可以将其插入到快速矩阵乘法算法中,该算法实际上比 O(dim * #vecs^2) 更快。希望它还针对可生成对称矩阵的自转置矩阵进行了优化(这可能会节省 2 倍的工作量……也许它有一些标志,例如 matrixmult(a,b,outputWillBeSymmetric))。

    这比“O(N^2)”快,不直观:这种重写暴露了问题中的一个子结构,可以利用它来获得比 O(dim * #vecs^2) 更快的速度。可利用的子结构就是您正在计算相同向量的外积。快速矩阵乘法算法将利用这一点。


    编辑:我原来的答案是错误的

    您有一组大小为 N 的集合,您希望计算集合中所有 a 和 b 的 f(a,b)。

    除非你知道一些值是微不足道的,否则没有比这更容易成为asymptotically faster,因为你必须想象最坏的情况:每一对 f(a,b) 可能是唯一的......所以没有办法做少于大约 N^2 的工作。

    但是由于你的函数 f 是对称的,你可以做一半的工作,然后复制它:

    N = len(vectors)
    for i in range(N):
      for v in range(N):
        dissim = #...
        s[i,v] = dissim
        s[v,i] = dissim
    

    (您可以避免在反身情况 f(a,a) 中计算您的度量,因为它很简单,但这不会使事情渐近更快,因为随着 N 增加,此类工作的分数 N/N^2 趋于零,所以这不是一个很好的优化......只有在使用非常少量的向量时才合理。)

    是否应该进一步优化取决于您是否需要。这样的代码应该能够轻松处理数百万个小向量。下一步:

    • 是否有什么可疑的东西使我的代码变得很慢?会是什么呢?我们没有足够的信息发表评论。
    • 如果没有任何可疑之处,您可以尝试将其改写为矩阵运算,以便尽可能多地保留在 numpy 优化的 C 例程中,而不是在 python 中来回跳动。这很难看,我会避免这样做,因为您的代码可读性会降低。
    • 如果您正在处理数以亿计的向量,也许可以考虑一种对缓存更友好的方法,您可以这样做for blockI in range(N//10**6): for blockV in range(N//10**6): for i in range(blockI*10**6, (blockI+1)*10**6): for v in range(blockV*...):
    • 如果您要处理数十亿个向量,请考虑利用 gpgpu。这对于 gpu 来说是非常理想的,并且可能是数千倍加速的一个因素。

    【讨论】:

    • 有没有办法摆脱 for 循环?即使它会稍微减慢运行时间。
    • @Cat_Smithyyyy03:通常你可以考虑张量积。在这种情况下,当您考虑矩阵乘法 AB=C 时,矩阵乘积 C 基本上是 A 的行空间和 B 的 colspace 中所有向量的外积。因此,首先对所有向量进行归一化,然后将它们堆叠形成A,然后让B=transp(A),然后做一个矩阵乘法。 (这意味着我认为我的答案是错误的;我已对其进行了编辑,谢谢。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-14
    • 2022-12-02
    • 1970-01-01
    • 2011-07-12
    • 1970-01-01
    • 1970-01-01
    • 2018-01-14
    相关资源
    最近更新 更多