【发布时间】:2014-05-13 11:40:22
【问题描述】:
我需要取两个包含对数概率的 NumPy 矩阵(或其他二维数组)的矩阵乘积。天真的方式np.log(np.dot(np.exp(a), np.exp(b))) 显然不是首选。
使用
from scipy.misc import logsumexp
res = np.zeros((a.shape[0], b.shape[1]))
for n in range(b.shape[1]):
# broadcast b[:,n] over rows of a, sum columns
res[:, n] = logsumexp(a + b[:, n].T, axis=1)
有效,但运行速度比 np.log(np.dot(np.exp(a), np.exp(b))) 慢约 100 倍
使用
logsumexp((tile(a, (b.shape[1],1)) + repeat(b.T, a.shape[0], axis=0)).reshape(b.shape[1],a.shape[0],a.shape[1]), 2).T
或其他 tile 和 reshape 组合也可以工作,但运行速度甚至比上面的循环还要慢,因为实际大小的输入矩阵需要大量的内存。
我目前正在考虑用 C 语言编写一个 NumPy 扩展来计算它,但我当然宁愿避免这种情况。有没有一种既定的方法可以做到这一点,或者有人知道执行这种计算的内存密集度较低的方法吗?
编辑: 感谢 larsmans 提供的解决方案(推导见下文):
def logdot(a, b):
max_a, max_b = np.max(a), np.max(b)
exp_a, exp_b = a - max_a, b - max_b
np.exp(exp_a, out=exp_a)
np.exp(exp_b, out=exp_b)
c = np.dot(exp_a, exp_b)
np.log(c, out=c)
c += max_a + max_b
return c
使用 iPython 的神奇 %timeit 函数将此方法与上面发布的方法 (logdot_old) 进行快速比较,得出以下结果:
In [1] a = np.log(np.random.rand(1000,2000))
In [2] b = np.log(np.random.rand(2000,1500))
In [3] x = logdot(a, b)
In [4] y = logdot_old(a, b) # this takes a while
In [5] np.any(np.abs(x-y) > 1e-14)
Out [5] False
In [6] %timeit logdot_old(a, b)
1 loops, best of 3: 1min 18s per loop
In [6] %timeit logdot(a, b)
1 loops, best of 3: 264 ms per loop
显然 larsmans 的方法抹杀了我的方法!
【问题讨论】:
-
如果你已经了解 C,你可以使用 scipy.weave.blitz 在你的 Python 代码中加入几行 C
-
唉,scipy.weave 不适用于 python3
-
在您的示例中,我不认为
scipy.misc.logsumexp正在做您认为的事情-根据the docsb=参数实际上是exp(a)的缩放因子,即@ 987654333@. -
@mart:你为什么将权重解释为概率?
-
Weave 处于弃用周期。任何新代码都应该使用 Cython。
标签: python numpy matrix matrix-multiplication logarithm