在风险管理模型的背景下计算VaR,我正在实现一个例程来创建具有用户定义边际的多元分布。
我将提供一个代码示例,使用高斯copula和gamma marginals:
# generate a multivariate normal distribution with a given input correlation:
mv = scipy.stats.multivariate_normal.rvs(mean = np.zeros(10), cov = correlation, size = (50000, 50, 10))
# transform the sample to a uniform with the above correlation:
unif = scipy.stats.norm.cdf(mv)
# apply the marginal:
mv_fin = scipy.stats.gamma.ppf(unif, a=1)
字符串
它运行良好,并产生预期的结果。关键问题是它非常慢,这三个步骤占用了整个模型运行时间的90%。我怎样才能加快速度?谢啦,谢啦
3条答案
按热度按时间wsxa1bj11#
我有一些想法,这当我帮助充分为我:
首先将计算并行化:
您正在生成大量的随机
variates
并转换它们,这是一个令人尴尬的 * 并行 * 问题。考虑使用joblib
或多处理库来并行化随机 * 变量 * 的生成和转换。第二个优化阵列操作:
我们使用
scipy.stats.norm.cdf(mv)
来转换整个 mv 数组。该操作在计算上可能是昂贵的,尤其是对于大型阵列。您可以尝试通过使用NumPy的ndarray方法将scipy.stats.norm.cdf
替换为矢量化操作来优化它。第三,使用更快的库生成随机数:
SciPy
* 不是生成随机数 * 的最快库。如果速度是一个关键因素,请考虑使用NumPy等库,甚至是GPU加速库,如 CuPy或JAX,以生成随机变量。forth分析你的代码:
使用像 cProfile 这样的分析工具或行分析器来确定代码的哪些部分占用的时间最多。这将帮助您将优化工作集中在代码中最耗时的部分。
这是一个示例代码
字符串
我认为
JIT
编译与Numba是帮助充分,但你应该文本和mesure他们jmo0nnb32#
对于
scipy.stats.norm.cdf
,numba-stats
包提供了一个函数,用于计算显然相同的事情。默认参数需要显式地提供给这个函数,而不是Scipy。您可以使用numba_stats.stats.norm_cdf(mv, 0, 1)
来调用它。这是2. 2倍快,在我的机器上与i5- 9600 KF。速度的提升主要来自于多线程的使用(与Scipy实现相比,Scipy实现目前为止是顺序的)。关于
scipy.stats.gamma.ppf
,事情更加复杂,因为Numba包似乎没有提供任何替代方案(即gamma
子模块似乎到目前为止还没有实现)。Scipy的实现是顺序的。慢的部分似乎来自于(scipy.special
模块的)gammaincinv
函数,该函数被调用用于unif
的所有值(1
用于第一个参数)。此函数调用igami
,该函数似乎来自Cephes库。来源可以在这里找到。此函数使用了许多昂贵的数学函数,如pow
、exp
、log
(以及用于计算幂/无症状级数的基本循环)。库(和Scipy函数)似乎经过优化,以提供相当高的准确性,但性能相对较差。有趣的是,在同一台机器上,使用相同版本的Scipy(1.10.1),在Linux上调用这个函数的速度比在Windows上快2.6倍。我认为这是由于在Linux上比在Windows上更有效地实现了默认的数学库。您可以尝试使用优化的数学库,例如英特尔的数学库,以加快计算速度。请注意,可能需要在目标计算机上重新生成Scipy(和Cephes)以提高性能。一个更简单的方法当然是尝试Intel Distribution for Python,假设您使用的是英特尔处理器(或者可能是AMD处理器,因为大多数英特尔工具都可以在上面工作,尽管性能可能不如英特尔处理器)。
另一种加速计算的可能方法是在计算函数
scipy.stats.gamma.ppf
时使用多线程。不幸的是,Scipy目前还不支持此功能。话虽如此,人们可以重新实现Scipy函数并调用gammaincinv
函数,尽管这肯定是一项重要的工作。一个更简单(丑陋)的替代解决方案是简单地挂接函数scipy.stats.gamma._ppf
,以便使用多个线程。实际上,这是一个纯Python函数,并且这样的函数可以重新赋值。在我的6核机器上,该策略将计算速度提高了3.75倍。这种优化可以与更好的数学库相结合。下面是最终脚本:
字符串
正如您所看到的,将多个线程与快速数学库相结合(或者只是为了简单起见而使用Linux)可以显著提高速度。优化后的实现时间从Windows上的181.78 s下降到Linux上的24.66 s。
这可是7.4倍的加速!
qkf9rpyu3#
通过将gamma概率点函数替换为-ln(1 - x),可以将计算时间减少4倍。
我在试图找到逆不完全伽马函数的快速近似值时偶然发现了这一点:
scipy.stats.gamma.ppf(unif, a=1)
非常接近等于-np.log(1 - unif)
。这是两个人的情节。x1c 0d1x的数据
这是两个函数之间的差异图。请注意,Y轴的单位为10-15。
的
换句话说,这通常精确到小数点后15位。
使用gamma PPF的这种快速近似值,您可以像这样编辑原始代码。
字符串
在我的测试中,这大约是整体速度的4倍。
其他答案也解释了如何并行运行计算。这个想法可以与那些想法结合起来--既可以使用成本较低的计算,也可以并行运行。