【问题标题】:Numpy array is much slower than listNumpy 数组比列表慢得多
【发布时间】:2017-10-31 11:30:45
【问题描述】:

给定两个矩阵 X1 (N,3136) 和 X2 (M,3136)(其中每行中的每个元素都是二进制数),我试图计算汉明距离,以便将 X1 中的每个元素与所有X2 中的行,结果矩阵为 (N,M)。

我已经为它写了两个函数(第一个在 numpy 的帮助下,另一个没有 numpy):

def hamming_distance(X, X_train):
    array = np.array([np.sum(np.logical_xor(x, X_train), axis=1) for x in X])

    return array



def hamming_distance2(X, X_train):
    a = len(X[:,0])
    b = len(X_train[:,0])

    hamming_distance = np.zeros(shape=(a, b))

    for i in range(0, a):
        for j in range(0, b):
            hamming_distance[i,j] = np.count_nonzero(X[i,:] != X_train[j,:])

    return hamming_distance

我的问题是上层函数比我使用两个 for 循环的下层函数慢得多。是否可以改进第一个功能,以便我只使用一个循环?

PS。对不起,我的英语不是我的母语,虽然我已经尽力了!

【问题讨论】:

  • So learn from it ((u != v).mean())。
  • (u != v).mean() 不会比当前版本更有效,因为循环仍然是必要的。 N和M有多大?也许计算所有与广播的成对比较,并仅对必要的部分进行切片。不过,它可能很容易填满你的记忆。
  • 这只是表明 numpy 不是魔法仙尘;如果您正确使用它,它只会有助于提高性能。你的第二个也使用 numpy。
  • 我失败的尝试是使用点积,因为这两个操作的相似之处。给定两个二进制矩阵,如果将其中一个与 (1-other) 相乘,它会给出不同条目的数量,其中第一个为 1,另一个为零。所以(1-arr1).dot(arr2.T) + arr1.dot(1-arr2.T) 实际上给出了汉明距离。它比循环快吗?很不幸的是,不行。 :)
  • @ayhan 为了获得最佳性能,我们需要利用具有浮点值的 BLAS。将基于 float32 转换的数组的点作为答案发布。它以很大的优势获胜。

标签: python numpy matrix vectorization hamming-distance


【解决方案1】:

如果您使用 Numpy 来矢量化您的工作,Numpy 只会让您的代码更快。在您的情况下,您可以使用数组广播来矢量化您的问题:比较您的两个数组并创建一个形状为 (N,M,K) 的辅助数组,您可以沿其第三维求和:

hamming_distance = (X[:,None,:] != X_train).sum(axis=-1)

我们在第一个数组中注入一个单维,使其形状为(N,1,K),第二个数组隐式兼容形状(1,M,K),因此可以执行操作。

在 cmets @ayhan 中指出,这将为大 MN 创建一个巨大的辅助数组,这是非常正确的。这就是矢量化的代价:以内存为代价获得 CPU 时间。如果你有足够的内存让上面的工作,它会非常快。如果不这样做,则必须缩小矢量化的范围,并在MN 中循环(或两者;这将是您当前的方法)。但这与 numpy 本身无关,这是关于在可用资源和性能之间取得平衡。

【讨论】:

  • 不过,这将产生约 470 亿条条目。
  • @ayhan 我想这是正确的:D 没有彻底阅读 cmets。好吧,这就是矢量化必须付出的代价。
  • @ayhan 现在我正确阅读了 cmets;我刚刚注意到您基本上提出了相同的解决方案。您想添加自己的答案吗?在这种情况下,我很乐意删除我的。
  • 是的,当我看到这些数字时,我退出了。 :) 不用担心,我正在研究另一种解决方案。
【解决方案2】:

您所做的与点积非常相似。考虑这两个二进制数组:

1 0 1 0 1 1 0 0
0 0 1 1 0 1 0 1

我们正在尝试找出不同对的数量。如果你直接取点积,它会给你 (1, 1) 对的数量。但是,如果您否定其中一个,它将计算不同的。例如,a1.dot(1-a2) 计数 (1, 0) 对。由于我们还需要 (0, 1) 对的数量,我们将在其中添加 a2.dot(1-a1)。点积的好处是它非常快。但是,您需要先将数组转换为浮点数,as Divakar pointed out

这是一个演示:

prng = np.random.RandomState(0)
arr1 = prng.binomial(1, 0.3, (1000, 3136))
arr2 = prng.binomial(1, 0.3, (2000, 3136))
res1 = hamming_distance2(arr1, arr2)
arr1 = arr1.astype('float32'); arr2 = arr2.astype('float32')
res2 = (1-arr1).dot(arr2.T) + arr1.dot(1-arr2.T)

np.allclose(res1, res2)
Out: True

时间安排:

%timeit hamming_distance(arr1, arr2)
1 loop, best of 3: 13.9 s per loop

%timeit hamming_distance2(arr1, arr2)
1 loop, best of 3: 5.01 s per loop

%timeit (1-arr1).dot(arr2.T) + arr1.dot(1-arr2.T)
10 loops, best of 3: 93.1 ms per loop

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-13
    • 2019-08-17
    • 2018-02-17
    • 1970-01-01
    • 2019-04-09
    • 2022-01-16
    • 2022-01-14
    • 1970-01-01
    相关资源
    最近更新 更多