我有一个相当简单的函数,涉及基数10的对数(f1
如下所示)。我需要它尽可能快地运行,因为它作为更大代码的一部分被称为数百万次。
我尝试使用Taylor approximation(f2
以下),但即使进行大规模扩展,准确性也很差,甚至更糟,最终会花费更多时间。
我是否已达到numpy
可达到的效果限制?
import time
import numpy as np
def f1(m1, m2):
return m1 - 2.5 * np.log10(1. + 10 ** (-.4 * (m2 - m1)))
def f2(m1, m2):
"""
Taylor expansion of 'f1'.
"""
x = -.4 * (m2 - m1)
return m1 - 2.5 * (
0.30102999 + .5 * x + 0.2878231366 * x ** 2 -
0.0635837 * x ** 4 + 0.0224742887 * x ** 6 -
0.00904311879 * x ** 8 + 0.00388579 * x ** 10)
# The data I actually use has more or less this range.
N = 1000
m1 = np.random.uniform(5., 30., N)
m2 = np.random.uniform(.7 * m1, m1)
# Test both functions
M = 5000
s = time.clock()
for _ in range(M):
mc1 = f1(m1, m2)
t1 = time.clock() - s
s = time.clock()
for _ in range(M):
mc2 = f2(m1, m2)
t2 = time.clock() - s
print(t1, t2, np.allclose(mc1, mc2, 0.01))
答案 0 :(得分:3)
使用乘法替换f2
中的所有指数:
def f2(m1, m2):
"""
Taylor expansion of 'f1'.
"""
x = -0.4 * (m2 - m1)
x2 = x * x
x4 = x2 * x2
x6 = x4 * x2
return m1 - 2.5 * (
0.30102999 + .5 * x + 0.2878231366 * x2 -
0.0635837 * x4 + 0.0224742887 * x6 -
0.00904311879 * x4 * x4 + 0.00388579 * x4 * x6)
答案 1 :(得分:3)
使用此代码段,我不确定您是否应优化日志,但更多的是整个vector-expression本身。
你可以试试numexpr(Python的快速数值数组表达式求值器,NumPy,...),这可能对你有很大帮助。
尝试这个的想法来自Ignacio的评论,这让我想到了他的加速来自哪里(我敢肯定,它不是来自日志计算本身)。
在我对代码的简单修改中:
import numexpr as ne
def f1(m1, m2):
return ne.evaluate("m1 - 2.5 * log10( 1.0 + 10 ** (-0.4 * (m2-m1)))")
以上似乎(未经优化的)f2(近似值)<5> 6倍,同时仍然保持原始精度。
几乎是原始numpy-approach f1 的两倍。
这些数字可能会根据numexpr的设置而改变,因为例如也可以使用Intels MKL。由于我懒得检查我的基于anaconda的设置,我提供的这只是一个技术演示,每个人都可以尝试。
虽然我过去曾经使用过几次对于简单的东西,但我可以补充说,它也在pandas中使用,只是提到一个真实世界的项目,取决于它的正确运作。
免责声明:我使用您的基准作为模板(希望缓存和co不起作用)。