NumPy:如何快速归一化多个向量?

25 投票
6 回答
45909 浏览
提问于 2025-04-15 22:48

如何优雅地对NumPy中的向量列表进行归一化处理?

这里有一个示例,但这个示例能正常工作:

from numpy import *

vectors = array([arange(10), arange(10)])  # All x's, then all y's
norms = apply_along_axis(linalg.norm, 0, vectors)

# Now, what I was expecting would work:
print vectors.T / norms  # vectors.T has 10 elements, as does norms, but this does not work

最后的操作会出现“形状不匹配:对象无法广播到单一形状”的错误。

那么,如何才能优雅地对vectors中的二维向量进行归一化呢?

编辑:为什么上面的代码不工作,而给norms添加一个维度的做法却能正常工作(根据我下面的回答)?

6 个回答

16

好的,NumPy的数组形状广播是把维度加到数组形状的左边,而不是右边。不过,我们可以告诉NumPy把一个维度加到norms数组的右边:

print vectors.T / norms[:, newaxis]

这样是可以工作的!

27

计算大小

我看到这个问题后,对你们的归一化方法产生了好奇。我用了一种不同的方法来计算大小。注意:我通常是在最后一个索引上计算范数(在这种情况下是行,而不是列)。

magnitudes = np.sqrt((vectors ** 2).sum(-1))[..., np.newaxis]

不过,通常我就是这样进行归一化的:

vectors /= np.sqrt((vectors ** 2).sum(-1))[..., np.newaxis]

时间比较

我做了一个测试来比较时间,发现我的方法快了不少,但Freddie Witherdon的建议更快。

import numpy as np    
vectors = np.random.rand(100, 25)

# OP's
%timeit np.apply_along_axis(np.linalg.norm, 1, vectors)
# Output: 100 loops, best of 3: 2.39 ms per loop

# Mine
%timeit np.sqrt((vectors ** 2).sum(-1))[..., np.newaxis]
# Output: 10000 loops, best of 3: 13.8 us per loop

# Freddie's (from comment below)
%timeit np.sqrt(np.einsum('...i,...i', vectors, vectors))
# Output: 10000 loops, best of 3: 6.45 us per loop

不过要小心,因为这个StackOverflow的回答提到,使用einsum时有一些安全检查没有进行,所以你要确保vectorsdtype足够好,能够准确存储大小的平方。

17

好吧,除非我错过了什么,这个确实可以用:

vectors / norms

你建议中的问题在于广播规则。

vectors  # shape 2, 10
norms  # shape 10

它们的形状长度不一样!所以规则是先在左边把小的形状扩展一下:

norms  # shape 1,10

你可以通过手动调用来做到这一点:

vectors / norms.reshape(1,-1)  # same as vectors/norms

如果你想计算vectors.T/norms,你需要手动调整形状,方法如下:

vectors.T / norms.reshape(-1,1)  # this works

撰写回答