我想標準化一個向量。最簡單的方法是
import numpy as np
v = np.random.rand(3)
v /= np.linalg.norm(v)
但我擔心我的包的性能和平方求和(不可避免),取平方根,然后劃分所有向量不是一個好主意。
然后我提出了這個問題,使用哪種解決方案sklearn.preprocessing.normalize
來做到這一點。不幸的是,它為我的包添加了另一個要求/依賴項。
這是問題。
- 不應該有一個
numpy
功能來做到這一點嗎?其中使用快速反平方根演算法。還是它超出了numpy
's 的范圍,不應該有這樣的功能? cython
我應該在/中實作我自己的函式numba
嗎?- 或者如果我非常擔心性能,我應該放棄
python
并開始在C
/中撰寫代碼C
嗎?
uj5u.com熱心網友回復:
不應該有一個 numpy 函式來做到這一點嗎?它使用快速反平方根演算法。還是它超出了numpy的范圍,不應該有這樣的功能?
我不知道 Numpy 中有任何功能可以做到這一點。純 Numpy 中需要多個函式呼叫。sklearn.preprocessing.normalize
確實是一個不錯的選擇(AFAIK 并不是唯一提供該選項的軟體包)。
問題是Numpy 不是為有效計算小型陣列而設計的。Numpy 呼叫的開銷對于小型陣列(例如只有 3 個值)來說是巨大的。組合多個函式呼叫只會使情況變得更糟。開銷主要是由于型別/形狀/值檢查、內部函式呼叫、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 只能在包裝函式中進行最少量的檢查。回圈非常快,但如果您替換為實際大小(例如 3),它們可以變得更快,v.size
因為 JIT 可以展開回圈并生成近乎最佳的代碼。np.sqrt
將被行內,它應該生成一個快速的平方根 FP 指令。如果您使用 flag fastmath=True
,JIT 甚至可以使用 x86-64 平臺上的專用更快指令計算倒數平方根(請注意fastmath
如果您使用 NaN 等特殊值或關心 FP 關聯性,則不安全)。盡管如此,對于非常小的向量,在主流機器上呼叫此函式的開銷可能為 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 內部函式或匯編指令,使用模板生成專門的代碼)。
uj5u.com熱心網友回復:
您可以實作的最佳解決方案的時間復雜度為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)
但是,這似乎不需要高度的優化和效率。不過,我將仔細研究它,以便找到解決此問題的更好方法。也許其他編程資源(如遞回或高級資料結構)可以稍微減少其運行時間。
uj5u.com熱心網友回復:
正如@mkrieger1 所寫,首先撰寫您的代碼并測驗性能是否適合您的作業。如果沒有開始嘗試您提到的解決方案并比較您設備上的差異。通常,運行代碼的硬體會對性能差異產生影響。這意味著最終并非所有庫在所有設備上都優于其他庫。
如果你真的想全力以赴,我建議并行執行你的代碼。最終甚至在 GPU 上。您可以為此使用 pytorch 或使用 C 和 CUDA(或類似)撰寫代碼,以充分利用系統。然而重要的是,總結元素也必須并行完成。
最后,如果您堅持使用 python,請查看 python 11,因為此版本在某些方面改進了 python 的性能。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/493253.html