【Python】這個裝飾器竟讓 Python 提速了 30 倍!
Python是一種解釋語言,其代碼不是直接編譯成機(jī)器碼,而是由另一個叫做
解釋器
的程序?qū)崟r解釋的(一般是
cpython
)。因此,與其他編譯語言相比,Python靈活性高(動態(tài)類型,兼容性高,...)。但這也造成了Python非常慢的缺點。
加速 Python的方法
實際上,有多種解決方案可以解決python的緩慢問題。

-
使用
cython:一種編程語言,是python的超集。Cython是Python編程語言和擴(kuò)展 Cython 編程語言(基于Pyrex)的優(yōu)化靜態(tài)編譯器。它使得為 Python 編寫 C 擴(kuò)展就像 Python 本身一樣容易。 -
使用C/C++語言結(jié)合
ctypes,pybind11或CFFI來編寫Python的綁定程序 - 用C/C++擴(kuò)展Python
- 使用其他編譯過的語言,如rust[1]
而所有這些方法,都需要使用除Python外的另一種語言,并編譯代碼使之與Python一起工作。盡管這些方法都很不錯,但并不是最適合我們初學(xué)者的使python更快的方法,更別提他們通常比較難以設(shè)置了。
Numba & JIT 編譯器
Numba[2]是一個Python包,在兼具Python的便利的同時,可以使你的代碼更快。

numba使用Just-in-time (JIT)編譯(即在Python代碼執(zhí)行過程中的實時編譯的),使用起來非常方便,無需向其他工具一樣,還需安裝一個C/C++編譯器,它僅需用 pip/conda 安裝它即可。
pip?install?numba
接下來試一個例子:用蒙特卡洛模擬來計算π的估計值。
import?random
from?numba?import?njit
def?monte_carlo_pi_without_numba(nsamples):
????acc?=?0
????for?i?in?range(nsamples):
????????x?=?random.random()
????????y?=?random.random()
????????if?(x?**?2?+?y?**?2)?<?1.0:
????????????acc?+=?1
????return?4.0?*?acc?/?nsamples
#?添加numba的裝飾器,使該函數(shù)更快。
@njit
def?monte_carlo_pi_with_numba(nsamples):
????acc?=?0
????for?i?in?range(nsamples):
????????x?=?random.random()
????????y?=?random.random()
????????if?(x?**?2?+?y?**?2)?<?1.0:
????????????acc?+=?1
????return?4.0?*?acc?/?nsamples
在使用該方法時,我們只需要導(dǎo)入numba的一個裝飾器(njit),剩下的都由它自己完成即可,可以說是非常方便。
我們運行兩個版本的代碼,并進(jìn)行計時對比。顯示numba比普通python快30倍。
%timeit?monte_carlo_pi_with_numba(100_000)
#?1.24?ms?±?10.4?μs?per?loop?(mean?±?std.?dev.?of?7?runs,?1000?loops?each)
%timeit?monte_carlo_pi_without_numba(100_000)
#?40.6?ms?±?814?μs?per?loop?(mean?±?std.?dev.?of?7?runs,?10?loops?each)
一些注意事項

值得一提的是,numba確實有一些缺點:
-
在首次運行
numba裝飾的函數(shù)時,有一定的時間開銷。這是因為首次執(zhí)行時,numba會試圖找出參數(shù)的類型并編譯函數(shù),從而導(dǎo)致程序有一定的延遲。 -
不是所有的Python代碼都能用
numba編譯,例如,如果你對同一個變量或?qū)α斜碓厥褂没旌蠑?shù)據(jù)類型,此種情況將會拋出異常。
加速 Pandas
Numba是專門為numpy設(shè)計的,對numpy數(shù)組非常友好。而 pandas 是建立在 numpy 之上的,這使得在使用用戶定義的函數(shù)或甚至執(zhí)行不同的Dataframe操作時,可以進(jìn)行瘋狂優(yōu)化。

首先創(chuàng)建一個DataFrame數(shù)據(jù)集。
import?numpy?as?np
import?pandas?as?pd
n?=?1_000_000
df?=?pd.DataFrame({
????'height':?1?+?1.3?*?np.random.random(n),
????'weight':?40?+?260?*?np.random.random(n),
????'hip_circumference':?94?+?14?*?np.random.random(n)
})
用戶定義的函數(shù)
numba 的另一個重要的方法是 vectorize,使用該方法可以很容易的創(chuàng)建numpy通用函數(shù)(ufuncs[3])
通用函數(shù)(或簡稱ufunc)是以ndarrays逐個元素的方式運行的函數(shù),支持?jǐn)?shù)組廣播、類型轉(zhuǎn)換和其他幾個標(biāo)準(zhǔn)特性。也就是說,ufunc 是一個函數(shù)的“矢量化”包裝器,它接受固定數(shù)量的特定輸入并產(chǎn)生固定數(shù)量的特定輸出。
下面是計算數(shù)據(jù)集中列height的平方。
from?numba?import?vectorize
def?get_squared_height_without_numba(height):
??return?height?**?2
@vectorize
def?get_squared_height_with_numba(height):
??return?height?**?2
%timeit?df['height'].apply(get_squared_height_without_numba)
#?279?ms?±?7.31?ms?per?loop?(mean?±?std.?dev.?of?7?runs,?1?loop?each)
%timeit?df['height']?**?2
#?2.04?ms?±?229?μs?per?loop?(mean?±?std.?dev.?of?7?runs,?1000?loops?each)
#?我們首先將列轉(zhuǎn)換為numpy數(shù)組,
#?因為numba與numpy兼容,與pandas并不兼容。
%timeit?get_squared_height_with_numba(df['height'].to_numpy())
#?1.6?ms?±?51.5?μs?per?loop?(mean?±?std.?dev.?of?7?runs,?1000?loops?each)
基本操作
使用njit,并計算 BMI(身體質(zhì)量指數(shù))。
from?numba?import?njit
@njit
def?get_bmi(weight_col,?height_col):
??n?=?len(weight_col)
??result?=?np.empty(n,?dtype="float64")
??#?與python循環(huán)相比,Numba的循環(huán)非???/span>
??for?i,?(weight,?height)?in?enumerate(zip(weight_col,?height_col)):
????result[i]?=?weight?/?(height?**?2)
??return?result
#?不要忘記將列轉(zhuǎn)換為?numpy?
%timeit?df['bmi']?=?get_bmi(df['weight'].to_numpy(),?
????????????????????????????df['height'].to_numpy())
#?6.77?ms?±?230?μs?per?loop?(mean?±?std.?dev.?of?7?runs,?100?loops?each)
%timeit?df['bmi']?=?df['weight']??/?(df['height']?**?2)
#?8.63?ms?±?316?μs?per?loop?(mean?±?std.?dev.?of?7?runs,?100?loops?each)
你可以看到,即使是基本的操作,numba仍然比原始 pandas 花費的時間更少(6.77ms vs 8.63ms)。
寫在最后
numba 是一種開箱即用的方法,可以輕而易舉地 讓你的 Python 代碼變得更快。當(dāng)然,在成功編譯代碼之前可能需要多幾次嘗試,你可以試試使用它。如果本文對你有用,那就點個贊和在看支持下云朵君吧!
拓展閱讀
rust: https://github.com/PyO3/pyo3
[2]Numba: https://numba.pydata.org/
[3]ufuncs: https://numpy.org/doc/stable/reference/ufuncs.html

往期
精彩
回顧
- 適合初學(xué)者入門人工智能的路線及資料下載
- (圖文+視頻)機(jī)器學(xué)習(xí)入門系列下載
- 機(jī)器學(xué)習(xí)及深度學(xué)習(xí)筆記等資料打印
- 《統(tǒng)計學(xué)習(xí)方法》的代碼復(fù)現(xiàn)專輯
- 機(jī)器學(xué)習(xí)交流qq群955171419,加入微信群請 掃碼
