我可以加速这个基本的线性代数代码吗?

时间:2014-03-10 16:13:34

标签: python performance math numpy

我想知道是否可以使用Numpy或数学技巧来优化以下内容。

def f1(g, b, dt, t1, t2):
  p = np.copy(g)
  for i in range(dt):
    p += t1*np.tanh(np.dot(p, b)) + t2*p
  return p

其中g是长度为n的向量,bn x n矩阵,dt是迭代次数,t1t2是标量。

我很快就没有关于如何进一步优化这一点的想法,因为p在循环中使用,在等式的所有三个术语中:当添加到自身时;在点积;并以标量乘法。

但也许有不同的方式来表示这个功能,或者有其他技巧可以提高效率。如果可能的话,我宁愿不使用Cython等,但如果速度提升很重要,我愿意使用它。如果问题超出范围,请提前致谢,并道歉。

更新

到目前为止提供的答案更侧重于输入/输出的值可以避免不必要的操作。我现在用适当的变量初始化值更新了MWE(我没想到优化思想来自那个方面 - 道歉)。 g将在[-1, 1]范围内,b将在[-infinity, infinity]范围内。近似输出不是一个选项,因为返回的向量后来被赋予评估函数 - 近似可能返回相同的向量以获得相似的输入,因此它不是一个选项。


MWE:

import numpy as np
import timeit

iterations = 10000

setup = """
import numpy as np
n  = 100
g  = np.random.uniform(-1, 1, (n,)) # Updated.
b  = np.random.uniform(-1, 1, (n,n)) # Updated.
dt = 10
t1 = 1
t2 = 1/2

def f1(g, b, dt, t1, t2):
  p = np.copy(g)
  for i in range(dt):
    p += t1*np.tanh(np.dot(p, b)) + t2*p
  return p
"""

functions = [
  """
    p = f1(g, b, dt, t1, t2)
  """
]

if __name__ == '__main__':
  for function in functions:
    print(function)
    print('Time = {}'.format(timeit.timeit(function, setup=setup,
                                           number=iterations)))

2 个答案:

答案 0 :(得分:4)

要在没有cythonjit的情况下更快地运行代码将会非常困难,一些数学技巧可能会更容易。在我看来,如果我们在正N中为k(g, b) = f1(g, b, n+1, t1, t2)/f1(g, b, n, t1, t2)定义nk函数的限制应为t1+t2(还没有可靠的证据) ,只是一种直觉;它可能是E(g)= 0& E(p)= 0的特殊情况。)。对于t1=1t2=0.5k()似乎很快就达到了极限,对于N>100,它几​​乎是1.5的常量。

所以我认为数值近似方法应该是最简单的方法。enter image description here

In [81]:

t2=0.5
data=[f1(g, b, i+2, t1, t2)/f1(g, b, i+1, t1, t2) for i in range(1000)]
In [82]:

plt.figure(figsize=(10,5))
plt.plot(data[0], '.-', label='1')
plt.plot(data[4], '.-', label='5')
plt.plot(data[9], '.-', label='10')
plt.plot(data[49], '.-', label='50')
plt.plot(data[99], '.-', label='100')
plt.plot(data[999], '.-', label='1000')
plt.xlim(xmax=120)
plt.legend()
plt.savefig('limit.png')

In [83]:

data[999]
Out[83]:
array([ 1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,
        1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,
        1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,
        1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,
        1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,
        1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,
        1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,
        1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,
        1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,  1.5,
        1.5])

答案 1 :(得分:4)

我毫不犹豫地将此作为答案,因为我认为它可能是您提供给我们的输入数据的工件。不过请注意tanh(x) ~ 1x>>1。您输入的数据在我运行时始终为x = np.dot(p,b) >> 1,因此我们可以将f1替换为f2

def f1(g, b, dt, t1, t2):
  p = np.copy(g)
  for i in range(dt):
      p += t1*np.tanh(np.dot(p, b)) + t2*p
  return p

def f2(g, b, dt, t1, t2):
  p = np.copy(g)
  for i in range(dt):
      p += t1 + t2*p
  return p

print np.allclose(f1(g,b,dt,t1,t2), f2(g,b,dt,t1,t2))

这确实表明这两个函数在数值上是等价的。请注意,f2是non-homogeneous linear recurrence relation,如果您选择这样做,可以一步完成。