两个大小不同的数组的求和

时间:2019-03-01 05:50:29

标签: python numpy

我对可视化两个变量的对数似然函数很感兴趣。

这里有一些代码可以做到这一点:

import numpy as np
from scipy.stats import norm
import matplotlib.pyplot as plt

#Generate fake data
x = np.random.normal(0,1,size = 100)
y = 2*x+1 + np.random.normal(size = x.size)

#Create a grid to visualize the log-likelihood
grid = np.linspace(-5,5,101)
B0,B1 = np.meshgrid(grid,grid)

#Compute the log likelihood
LogLik = 0
for xs,ys in zip(x,y):
    LogLik+= norm.logpdf(ys, loc = B0+B1*xs)

plt.contourf(B0,B1,LogLik)

这小段代码中的瓶颈是对数似然的计算,即

for xs,ys in zip(x,y):
    LogLik+= norm.logpdf(ys, loc = B0+B1*xs)

如果x或y的长度很大,那么这将花费比我需要的时间更长的时间。是否有一种方法可以向量化分布均值(即B0+B1*xs)的创建以及对logpdf的求值?

1 个答案:

答案 0 :(得分:1)

可以很容易地通过将newaxis广播到数组来向量化它。结果,瓶颈norm.logpdf将只执行一次:

log_lh = norm.logpdf(y, loc=B0[..., None] + B1[..., None] * x[None, None, :]).sum(axis=2)

# comparison with LogLik:
np.allclose(LogLik, log_lh)
# Out: True

将其重构为函数将允许定时执行:

def loglik(x, y, B0, B1):
    return norm.logpdf(y, loc=B0[..., None] + B1[..., None] * x[None, None, :]).sum(axis=2)

def loglik_loop(x, y, B0, B1):
    LogLik = 0
    for xs, ys in zip(x, y):
        LogLik+= norm.logpdf(ys, loc=B0+B1*xs)

%timeit loglik(x, y, B0, B1)
# Out: 94.1 ms ± 1.51 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit loglik_loop(x, y, B0, B1)
# Out: 54 ms ± 4.25 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

如您所见,这似乎是极少数情况,其中矢量化代码不能提高性能。 scipy的norm模块中似乎还存在另一个瓶颈,这会影响在多维数组上进行操作时的性能。

因此,提高代码性能的唯一可能性就是实现循环的并行执行(用分配给固定数组并随后求和的方式来代替+=运算符)。