我有两个稀疏的市场,A和B. A是120000 * 5000,B是30000 * 5000。我需要找到B中每一行与所有A行之间的欧氏距离,然后找到A中的5行,与B中所选行的距离最小。因为这是一个非常大的数据,我使用CSR,否则我得到记忆错误。很明显,对于A中的每一行,它计算(x_b - x_a)^ 2 5000次并对它们求和,然后得到一个sqrt。这个过程需要很长时间,比如11天!有什么方法可以更有效地做到这一点吗?我只需要与B中每行距离最短的5行。
我正在实施K-Nearest Neighbors,A是我的训练集,B是我的测试集。
答案 0 :(得分:1)
嗯 - 我不知道你是否可以“矢量化”。该代码,以便它将在本机代码而不是Python中运行。加速numpy和scipy的诀窍总是如此。
如果您可以在1GHz CPU中使用本机代码运行该代码,并使用1 FP指令进行时钟处理,那么您可以在不到10小时内完成该代码。 (5000 * 2 * 30000 * 120000)/ 1024 ** 3
将其提升为1.5Ghz x 2 CPU物理内核x 4路SIMD指令采用乘法+累加(英特尔AVX扩展,大多数CPU都可用),你可以将这个数字运算降低到1小时,2 x 100%一个适度的核心i5 machinne。但这需要在本机代码中进行完全SIMD优化 - 远非一项微不足道的任务(尽管如果你决定走这条道路,关于SO的进一步问题可以得到人们在SIMD编码中弄湿手的帮助:-)) - 接口例如,C with Scipy中的这段代码使用cython并不难(你只需要那部分就可以得到10小时以上的数字)
现在......至于算法优化,并保持Python :-)
事实上,您不需要完全计算A中行的所有距离 - 您只需要保留5个较低行的排序列表 - 并且任何时候累积的总和如果方块大于第5个最近的行(到目前为止),则只需中止该行的计算。
你可以使用Python' heapq操作:
import heapq
import math
def get_closer_rows(b_row, a):
result = [(float("+inf"), None) * 5]
for i, a_row in enumerate(a):
distance_sq = 0
count = 0
for element_a, element_b in zip(a_row, b_row):
distance_sq += element_a * element_b
if not count % 64 and distance_sq > result[4][0]:
break
count += 1
else:
heapq.heappush(result, (distance, i))
result[:] = result[:5]
return [math.sqrt(r) for r in result]
closer_rows_to_b = []
for row in b:
closer_rows_to_b.append(get_closer_rows(row, a))
注意辅助"计数"避免昂贵的检索和比较所有乘法的值。 现在,如果您可以使用pypy而不是常规Python来运行此代码,我相信它可以充分利用JITting,如果您在纯Python中运行代码,那么您可以获得明显的改进(即:非numpy / scipy矢量化代码)。