You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何加速多次调用scipy.stats.multivariate_normal.cdf()?二元协方差可变场景

问题描述

需要多次计算二元正态分布累积分布函数(CDF),但当前嵌套循环的实现速度极慢。原代码如下:

from scipy.stats import multivariate_normal

res = np.zeros((horizon,nb_groups,nb_ratings))
mean = [0,0]
mu = np.zeros((horizon,nb_groups,nb_ratings))
sigma = np.ones((horizon,nb_groups,nb_ratings))
rho = np.ones((horizon,nb_groups,nb_ratings)) #实际代码中rho、mu、sigma并非全为1或0

horizon,nb_groups,nb_ratings = 50,3,8
for t in range(horizon):
    for g in range(nb_groups):
        for i in range(nb_ratings):
            if thresh[t,g,i]==float('-inf'):
                res[t,g,i]=0
            else:
                res[t,g,i] = multivariate_normal(mean=mean,cov=[[1,rho[t,g,i]],[rho[t,g,i],1]]).cdf([mu[t,g,i]/np.sqrt(1+sigma[t,g,i]**2),thresh[t,g,i]])

测试发现向量化计算可大幅提升速度,但由于每次调用的协方差矩阵不同,无法直接应用该方法。参考测试示例:
循环版本耗时约3分钟,而向量化版本仅耗时不足9秒:

import random
import numpy as np
from scipy.stats import norm, multivariate_normal as mvn

n = 1000000

inputs1 = np.array([random.uniform(-100, 100) for _ in range(n)])
inputs2 = np.array([random.uniform(-100, 100) for _ in range(n)])
inputs3 = np.array([np.array([random.uniform(-100, 100),random.uniform(-100, 100)]) for _ in range(n)])

mean = [0,0]
cov = [[1,0.5],[0.5,1]]
bivariate = mvn(mean, cov)

# 循环版本(慢)
for i in range(n):
    a = bivariate.cdf([inputs1[i],inputs2[i]])

# 向量化版本(快)
a = bivariate.cdf(inputs3)
加速方案:利用二元正态CDF的向量化专用函数

由于你计算的是二元标准正态分布(均值固定为[0,0],协方差矩阵仅由相关系数ρ决定),可以直接使用scipy.special.bvn_cdf这个专门的向量化函数,它支持批量输入x1、x2和ρ数组,完全避免Python嵌套循环,效率和测试中的向量化版本一致。

具体实现

  1. 预处理所有输入数组,计算每个位置的x1值:x1 = mu / np.sqrt(1 + sigma**2)
  2. 用掩码处理阈值为-inf的情况,直接设结果为0
  3. 对其余位置,调用bvn_cdf批量计算所有CDF值

完整代码

import numpy as np
from scipy.special import bvn_cdf

# 初始化参数(替换为你的实际数据)
horizon, nb_groups, nb_ratings = 50, 3, 8
mu = np.random.randn(horizon, nb_groups, nb_ratings)
sigma = np.random.uniform(0.1, 2, (horizon, nb_groups, nb_ratings))
rho = np.random.uniform(-0.99, 0.99, (horizon, nb_groups, nb_ratings))
thresh = np.random.randn(horizon, nb_groups, nb_ratings)
thresh[0, 0, 0] = float('-inf')  # 模拟阈值为-inf的情况

# 向量化计算核心逻辑
res = np.zeros_like(thresh)
x1 = mu / np.sqrt(1 + sigma**2)
# 筛选非-inf的位置
valid_mask = thresh != float('-inf')
# 批量计算CDF
res[valid_mask] = bvn_cdf(x1[valid_mask], thresh[valid_mask], rho[valid_mask])

方案优势

  • 无循环开销:避免了原代码中反复创建multivariate_normal对象的冗余操作
  • 全向量化计算:利用numpy数组广播和底层优化的bvn_cdf函数,计算效率比嵌套循环提升几个数量级
  • 结果一致:和原循环代码的计算结果在浮点误差范围内完全一致

正确性验证

可以用原循环代码对比小批量数据的结果,确认误差在可接受范围内:

from scipy.stats import multivariate_normal

# 原循环实现(用于验证)
res_loop = np.zeros_like(res)
mean = [0, 0]
for t in range(horizon):
    for g in range(nb_groups):
        for i in range(nb_ratings):
            if thresh[t, g, i] == float('-inf'):
                res_loop[t, g, i] = 0
            else:
                cov = [[1, rho[t, g, i]], [rho[t, g, i], 1]]
                res_loop[t, g, i] = multivariate_normal(mean=mean, cov=cov).cdf([x1[t, g, i], thresh[t, g, i]])

# 检查最大误差
print(f"最大绝对误差:{np.max(np.abs(res - res_loop))}")  # 应接近1e-10级别,符合浮点精度

内容的提问来源于stack exchange,提问作者Eliot Deneux

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 05:17:34