Use numpy vectorize or map to speed an loop - Python NumPy 3D matrix "get rid of a loop" Python 问题,Monte Carlo

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 表示。因此,如果您想知道这是什么,它就是 Monte Carlo 金融问题模拟中使用的布朗桥 (BB) 结构。原始代码的信用(来自作者 Kenta Oono 修复原始 post 的评论,位于此处):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=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) should be used。不幸的是,Numba 可能还不支持它,因为 Numpy 最近添加了这个...

如果你有 Nvidia GPU,另一种解决方案是使用 CUDA 在 GPU 上执行函数。这应该快得多。请注意,Numba 具有特定的优化功能,可使用 CUDA 在 GPU 上生成随机 np.float32 值(请参阅 here)。