我想把向量归一化。最简单的方法就是
import numpy as np
v = np.random.rand(3)
v /= np.linalg.norm(v)
但是我担心我的包的性能和平方(不可避免的)之和,取平方根,然后除以所有向量不是一个好主意。
然后我进入了这个问题,哪个解决方案使用sklearn.preprocessing.normalize
来完成它。不幸的是,它为我的包增加了另一个需求/依赖。
这就是问题所在。
numpy
函数来这样做吗?它使用快速逆平方根算法。还是它超出了numpy
的范围,不应该有这样的函数?cython
/numba
中实现自己的函数吗?python
,开始用C
/C++
编写代码吗?发布于 2022-06-18 16:19:26
难道不应该有一个矮胖的函数来这样做吗?该算法采用快速逆平方根算法。或者是超出了numpy的范围,而不应该有这样的函数?
我不知道在Numpy中有什么功能能做到这一点。纯Numpy中需要多个函数调用。sklearn.preprocessing.normalize
确实是一个很好的选择(而且AFAIK并不是唯一提供该功能的包)。
问题是,Numpy的设计并不是为了高效地计算小数组。对于小型数组(比如只有3个值),Numpy调用的开销很大。将多个函数调用组合在一起只会使情况更糟。造成这种开销的主要原因是类型/形状/值检查、内部函数调用、CPython解释器以及新数组的分配。因此,即使Numpy提供了您想要的函数,对于一个只有3项的数组来说,它也会很慢。
我应该在cython/numba中实现我的函数吗?
,这是一个很好的主意,,因为Numba可以用更小的开销来完成这个任务。注意,Numba函数调用的开销仍然很小,但是从Numba上下文调用它们非常便宜(本机调用)。
例如,您可以使用:
# Note:
# - The signature cause an eager compilation
# - ::1 means the axis is contiguous (generate a faster code)
@nb.njit('(float64[::1],)')
def normalize(v):
s = 0.0
for i in range(v.size):
s += v[i] * v[i]
inv_norm = 1.0 / np.sqrt(s)
for i in range(v.size):
v[i] *= inv_norm
此函数不分配任何新数组,因为它可以就地工作。此外,Numba只能在包装功能中进行最小数量的检查。循环非常快,但是如果你用实际的大小代替v.size
,它们可以变得更快。3)因为JIT可以展开循环并生成几乎最优的代码。np.sqrt
将被内联,它应该生成一个快速平方根FP指令.如果使用标志fastmath=True
,JIT甚至可以使用x86-64平台上的专用快速指令计算倒数平方根(请注意,如果您使用NaN之类的特殊值或关心FP的结合性,则fastmath
是不安全的)。尽管如此,在主流机器上调用此函数的开销可能是100到300 ns,用于非常小的向量: CPython包装函数有很大的开销。删除它的唯一解决方案是在调用方函数中使用Numba/Cython。如果您需要在大多数项目中使用它们,那么直接编写C/C++代码当然更好。
或者,如果我如此担心性能,我应该放弃python,开始用C/C++编写代码吗?
--它取决于您的整个项目,但是您想要操作许多这样的小向量,直接使用C/C++要有效得多。另一种方法是将Numba或Cython用于当前速度较慢的内核。
优化良好的Numba代码或Cython代码的性能可以非常接近于本地编译的C/C++代码。例如,我成功地用Numba一次超过了经过高度优化的OpenBLAS代码(多亏了专门化)。Numba中开销的主要来源之一是数组绑定检查(通常可以针对循环进行优化)。C/C++是较低级别的,所以您不支付任何隐藏成本,但代码可能更难维护。此外,您还可以应用Numba/Cython中甚至不可能实现的低级优化(例如。直接使用SIMD内部指令或装配指令,生成带有模板的专用代码)。
发布于 2022-06-18 09:37:52
您可以获得的最佳解决方案是O(2n)
的时间复杂度,它不是非常有效,而是线性的。下一步的办法如下:
1-首先计算向量的大小及其数值逆,这不可避免地会导致线性时间复杂度,而不管编程语言的性能如何。在此之前,我们有一个O(n)
算法。
2-向量的所有分量与原向量的计算逆幅相乘,从而使其归一化。当我们再次遍历整个列表时,我们在算法的复杂性中添加了另一个O(n)
项。最后,我们将这些时间复杂度项加起来,得到了O(n)+O(n)=O(2n)
。
import numpy as np
v = np.random.rand(3)
mod = (sum([i**2 for i in v]))**-0.5
print(v, mod)
v *= mod
print(v)
然而,这似乎不需要高度的优化和效率。不过,我会再看一看,以找出一个更好的方法来处理这个问题。也许其他编程资源,如递归或高级数据结构,可以稍微减少其运行时间。
发布于 2022-06-18 09:37:36
正如@mkrieger1 1所写的,首先编写代码,然后测试性能是否适合您的工作。如果没有,请开始尝试上述解决方案,并比较设备上的差异。通常,在代码上运行的硬件可能会对性能差异产生影响。这意味着并非所有库最终都优于其他所有设备。
如果您真的想全部执行,我建议并行执行您的代码。最终甚至在GPU上。您可以为此使用pytorch,也可以用C和CUDA (或类似的)编写代码,以最大限度地利用系统。然而,重要的是,总结这些要素也必须并行进行。
最后,如果您坚持使用python,请看一下python 11,因为这个版本在某些方面提高了python的性能。
https://stackoverflow.com/questions/72671213
复制相似问题