簡體   English   中英

使用 numpy vectorize 或 map 來加速循環 - Python NumPy 3D 矩陣“擺脫循環”Python 問題,蒙特卡洛

[英]Use numpy vectorize or map to speed up a loop - Python NumPy 3D matrix "get rid of a loop" Python question, Monte Carlo

現在我有 1 個循環填充 3D NumPy 矩陣。 我並不是最擅長理解 3D 數組結構,盡管我知道它實際上只是我習慣於在(2D)中思考的正常 XxY 的 XxYxZ 表示。 因此,如果您想知道這是什么,它是用於解決金融問題的蒙特卡羅模擬中使用的布朗橋 (BB) 結構。 歸功於原始代碼(源自作者 Kenta Oono 位於此處的修復原始帖子的評論): https ://gist.github.com/delta2323/6bb572d9473f3b523e6e。 你真的不需要了解它背后的數學知識; 它只是基本上切斷了一條步驟路徑(在本例中為 21),從 0 開始,應用了正態分布的沖擊(因此 np.random.randn),直到它到達終點,這也是 0。每條路徑都應用於一個模擬價格隨着時間的推移隨機“震驚”,從而生成資產在到期的過程中可以遵循的潛在路徑。 雖然這些是完全不相關的,所以我想我也會傳入一個 V 矩陣來關聯路徑是否正確,但是,讓我們保持簡單:

import numpy as np
from matplotlib import pyplot
import timeit

steps = 21
underlyings = 3
sims = 131072

seed = 0 # fix the seed for replicating results
np.random.seed(seed)

def sample_path_batches(underlyings, steps, sims):
    dt = 1.0 / (steps-1)
    dt_sqrt = np.sqrt(dt)
    B = np.empty((underlyings, steps, sims), dtype=float)
    B[:,0, :] = 0 # set first step to 0
    for n in range(steps - 2):
        t = n * dt
        xi = np.random.randn(underlyings, sims) * dt_sqrt
        B[:, n + 1, :] = B[:, n, :] * (1 - dt / (1 - t)) + xi
        B[:, -1, :] = 0 # set last step to 0
    return B

start_time = timeit.default_timer()
B = sample_path_batches(underlyings, steps, sims)
print('\n' + 'Run time for ', sims, ' simulation steps * underlyings: ', 
np.round((timeit.default_timer() - start_time),3), ' seconds')

pyplot.plot(B[:,:,np.random.randint(0,sims)].T); # plot a random simulation set of paths
pyplot.show()

131072 個模擬步驟的運行時間 * 底層代碼:2.014 秒

所以無論如何,這對我的應用程序來說太慢了,盡管我的帶有第二個內循環的原始版本大約是 15 秒。 所以我已經看到人們通過 np.vectorize 對 NumPy 進行矢量化或使用地圖來“展平”循環,但我無法想象如何自己實際做到這一點。 我正在尋找將產生相同數字的最佳“原生 Python”實現。 B 是 3D NumPy 數組。 如果需要,您可以復制並粘貼它並在線運行它: https : //mybinder.org/v2/gh/jupyterlab/jupyterlab-demo/HEAD?urlpath=lab/tree/demo

任何建議表示贊賞!!! 即使它只是“像這樣重新構建循環,然后應用 np.vectorize”或其他什么,我也很擅長接受建議並使其從簡單的“新視圖”中工作到如何可視化問題。 我通常只會在 Cython (nogil / OpenMP / prange) 中做這種類型的事情,但我想知道一般來說“展平”一個循環,使用 NumPy 或 Pandas 中內置的普通數學庫或任何有效的方法。

加速此代碼的一種簡單解決方案是使用 Numba 對其進行並行化。 您只需要為函數sample_path_batches使用裝飾器@nb.njit('float64[:,:,::1](int64, int64, int64)', parallel=True) (其中nb是 Numba 模塊)。 請注意,函數中的 dtype dtype=float必須替換dtype=np.float64 ,以便 Numba 可以正確編譯代碼。 請注意, parallel=True應該自動並行化np.random.randn調用以及循環中的基本后續操作。 在 10 核機器上,這要快 7 倍(Numpy 需要 0.253 秒,Numba 的並行實現需要 0.036 秒)。 如果您沒有看到任何改進,您也可以嘗試使用prange手動並行化它。

此外,您可以使用np.float32類型來顯着提高性能(理論上最多可提高 2 倍)。 但是,Numpy 目前不支持np.random.randn此類類型。 相反, np.random.default_rng().random(size=underlyings*sims, dtype=np.float32).reshape(underlyings, sims)應使用 不幸的是,Numba 可能還不支持它,因為 Numpy 最近添加了這個......

如果您有 Nvidia GPU ,另一種解決方案是使用 CUDA 在 GPU 上執行該功能。 這應該快得多。 請注意,Numba 具有特定的優化函數,可以使用 CUDA 在 GPU 上生成隨機np.float32值(請參閱此處)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM