在MATLAB和數值計算的世界,for循環被剪掉,而向量為王。
我最喜歡的向量化例子是,一位同事在寫了一篇非常酷的論文,對其中所涉及的大量計算做了腳注,與我分享了他的lorenz96代碼。其中,内部的向量化速度比沒有向量化快了4倍。
現在,快速向量代碼使機器學習成為可能。例如QR分解,雖然我還沒做過,但我确信現在我可以用MATLAB或numpy或Julia編寫。
我在MassMutual做的很多工作基本上都是數值計算,一條耗時數小時與耗時數秒的管道之間的差異巨大。秒意味着我們可以叠代,嘗試更多的選項。不過,為了靈活性,很多數值代碼都是用純Python(沒有Cython,沒有Numba)編寫的。我要說這是個壞想法!下面是一封同事的轉述郵件:
在僞代碼中,這是幾個月前我遇到的“精算”編碼難題:
EOM = 0 for months in years: PREM = 50 BOM = EOM PREM WIT = 5 EOM = BOM – WIT
一個簡單的例子,但是我認為顯示了BOM/EOM的相互依賴性(還有一些其他變量具有相似的關系)。你不能在不知道EOM的情況下對BOM進行向量化,而且在知道BOM之前也不能對EOM進行向量化。如果WIT0,PREM=0。基本上會出現很多相互依賴的情況。現在很多函數都不容易出現向量化。
好吧,我可以向量化這個,我做到了。以下是Python中的非向量化版本:
import numpy as np years = 10 bom = np.zeros(years*12) eom = np.zeros(years*12) for month in range(1, years*12): prem = 50 bom[month] = eom[month-1] prem wit = 5 eom[month] = bom[month] - wit
這是向量化版本:
import numpy as np years = 10 prem = 50 wit = 5 eom = np.arange(years*12)*prem - np.arange(years*12)*wit # 如果你仍希望将bom表作為數組: bom = eom np.arange(years*12)*wit
我還通過使用一系列字典來編寫for循環:
years = 10 prem = 50 wit = 5 result = [{bom: 0, eom: 0}] for month in range(1, years*12): inner = {} inner.update({bom: result[month-1][eom] prem}) inner.update({eom: inner[bom] - wit}) result.append(inner)
上面的這個返回一個不同類型的東西,一個dict列表…而不是兩個數組。
我們還可以導入Pandas來填充上述三個結果的結果(因此它們是一緻的輸出,我們可以保存到excel中,等等)。如果加載了Pandas,則可以使用空數據幀進行叠代,因此還有一個選項:
import numpy as np import pandas as pd years = 10 prem = 50 wit = 5 df = pd.DataFrame(data={bom: np.zeros(years*12), eom: np.zeros(years*12)}) for i, row in df.iterrows(): if i 0: row.bom = df.loc[i-1, eom] row.eom = row.bom - wit
對于所有這些類型的叠代,以及返回數據幀作為結果的選項,我們得到的結果是:
Cython 和Numba
我還添加了一些Cython版本的代碼,說明使用C可以在不使用numpy的情況下獲得向量化的性能,這确實可能在可讀性還有速度之間達到最佳平衡(保持for循環!)。
Numba也可以加速(它可能和Cython/Vectorized Numpy一樣快)。在這兩種情況下(Cython/Numba),你必須小心使用哪些數據類型(因為沒有dicts或pandas!)。我認為,如果你對如何集成Cython Numpy循環更聰明的話,它将有可能使Cython Numpy循環與向量化Numpy一樣快。
所有代碼,包括Cython,都可以在這裡找到:htt
更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!