如何解决“for x in vector: for y in vector:”的矩阵,没有两个for循环
可以在不使用两个 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] 应包含向量 [i] 和向量 [j] 之间的不同之处。
在这种情况下,vectors 是 NumPy 数组的列表,相异度计算公式为:
def dissimilarity(v1,v2):
return (1-(v1.dot(v2)/(np.linalg.norm(v1)*np.linalg.norm(v2))))
或者换句话说,相异是两个一维 NumPy 数组之间的余弦相异。
我的目标是找到一种方法来获得距离矩阵而无需双循环,但计算时间仍然非常小。
解决方法
一般情况下无法做到这一点。一般来说(但不是在这种情况下),computation time 不会改变,因为工作在物理上存在并且存在并且不管人们可能尝试重写它的会计技巧(单个 for 循环就像两个 for 循环,或递归实现),在一天结束时,无论您的顺序如何,工作仍然必须完成。
有没有办法摆脱 for 循环?即使它稍微减慢了运行时间。 – Cat_Smithyyyy03
你的情况很特别。虽然通常没有理由考虑加速是可能的,但您要求考虑张量积,或者在这种情况下是矩阵积。当您考虑一般矩阵乘法 AB=C 时,矩阵乘积 C 基本上是 A 的行空间中的所有向量 a 和 B 的列空间中的 b 的外积 {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 来说非常理想,并且可能是数千倍的加速因素。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。