给定两个矩阵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。对不起我的英语,它不是我的第一语言,虽然我努力做到最好!
答案 0 :(得分:3)
Numpy只会让你的代码更快,如果你用它来渲染你的工作。在您的情况下,您可以使用数组广播来矢量化您的问题:比较您的两个数组并创建一个形状(N,M,K)
的辅助数组,您可以在其第三维上求和:
hamming_distance = (X[:,None,:] != X_train).sum(axis=-1)
我们在第一个数组中注入单个维度以使其形状为(N,1,K)
,第二个数组与形状(1,M,K)
隐式兼容,因此可以执行操作。
在评论中@ayhan指出,这将为大M
和N
创建一个巨大的辅助数组,这是非常正确的。这是矢量化的代价:你以内存为代价获得CPU时间。如果你有足够的内存使上面工作,它将非常快。如果您不这样做,则必须缩小矢量化的范围,并循环使用M
或N
(或两者;这将是您当前的方法)。但这并不涉及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