Numpy数组比列表慢得多

时间:2017-05-30 20:42:50

标签: python numpy matrix vectorization hamming-distance

给定两个矩阵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。对不起我的英语,它不是我的第一语言,虽然我努力做到最好!

2 个答案:

答案 0 :(得分:3)

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

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

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

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

答案 1 :(得分: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