在python/numpy中加速动态编程

在python/numpy中加速动态编程,python,numpy,dynamic-programming,Python,Numpy,Dynamic Programming,我有一个2D成本矩阵M,可能是400x400,我正试图计算通过它的最佳路径。因此,我有如下功能: M[i,j] = M[i,j] + min(M[i-1,j-1],M[i-1,j]+P1,M[i,j-1]+P1) 这显然是递归的。P1是一些加性常数。我的代码或多或少是有效的,它是: def最优成本(成本,P1=10): 宽度1,宽度2=成本.shape M=阵列(成本) 对于范围(0,宽度1)内的i: 对于范围(0,宽度2)内的j: 尝试: M[i,j]=M[i,j]+min(M[i-1,j-

我有一个2D成本矩阵
M
,可能是400x400,我正试图计算通过它的最佳路径。因此,我有如下功能:

M[i,j] = M[i,j] + min(M[i-1,j-1],M[i-1,j]+P1,M[i,j-1]+P1)
这显然是递归的。P1是一些加性常数。我的代码或多或少是有效的,它是:

def最优成本(成本,P1=10):
宽度1,宽度2=成本.shape
M=阵列(成本)
对于范围(0,宽度1)内的i:
对于范围(0,宽度2)内的j:
尝试:
M[i,j]=M[i,j]+min(M[i-1,j-1],M[i-1,j]+P1,M[i,j-1]+P1)
除:
M[i,j]=inf
返回M
现在我知道在Numpy中循环是一个糟糕的想法,对于初始成本矩阵的计算,我已经能够找到缩短时间的捷径。然而,由于我需要对整个矩阵进行潜在评估,因此我不确定如何进行评估。在我的机器上每次呼叫大约需要3秒钟,并且必须应用于大约300个成本矩阵。我不确定这段时间是从哪里来的,因为分析表明,对min的200000次调用只需要0.1秒——可能是内存访问

有没有一种方法可以同时做到这一点?我想可能有,但对我来说,似乎每个迭代都是依赖的,除非有更聪明的方法来记忆东西

这个问题有相似之处:

如果有必要,我很乐意切换到C,但我喜欢Python在快速测试方面的灵活性以及缺少文件IO的faff。在我脑海中,像下面这样的代码可能会更快吗

#定义P1 10
无效成本(双**成本加成,双**成本加成){
/* 
我们假设成本支出最初是
充满了科斯汀的价值观。
*/
浮动a、b、c、成本;

对于(i=0;i如果您切换到Python发行版并不困难,您可以尝试使用,对于这个特定的简单动态算法,它可能会提供大量的加速,而不会让您离开Python。

Numpy通常不太擅长迭代作业(虽然它确实有一些常用的迭代函数,如
np.cumsum
np.cumprod
np.linalg.*
等)。但是对于上面查找最短路径(或最低能量路径)这样的简单任务,您可以通过思考同时可以计算什么来将问题矢量化(同时尽量避免复印:

假设我们在“行”方向(即水平方向)找到最短路径,我们可以首先创建算法输入:

# The problem, 300 400*400 matrices
# Create infinitely high boundary so that we dont need to handle indexing "-1"
a = np.random.rand(300, 400, 402).astype('f')
a[:,:,::a.shape[2]-1] = np.inf
然后准备一些我们稍后将使用的实用程序数组(创建需要固定的时间):

最后进行计算并计时:

%%timeit
for i in np.arange(a.shape[1]-1):
    A[i].min(2, T)
    B[i] += T

在我的(超旧笔记本电脑)机器上的计时结果是1.78s,这已经比3分钟快多了。我相信通过优化内存布局和对齐(以某种方式),你可以提高更多(同时坚持numpy)。或者,您可以简单地使用
多处理.Pool
。它很容易使用,而且这个问题很容易分解为较小的问题(通过在批处理轴上分割)当你运行代码时,第一个看到的项目是
i=0
j=0
,所以你得到
M[0,0]=M[0,0]+min(M[-1,-1],M[-1,0]+P1,M[0,-1]+P1)
。在我看来,你的
try
试图捕捉超出范围的索引(顺便说一句,你应该明确你想要抓住的东西,即除了索引器之外,你要做
),但是索引中的
-1
s被认为是“该维度上的最后一个元素”所以没有任何东西被设置为
np.inf
。我不认为这是您想要的,但请确认。是的,无意的。实际上它似乎没有影响任何东西,这就是为什么我没有捕捉到它的原因!请注意,您实际上不需要扩展所有循环。在Numba中,无论您是为循环编写本机Python还是编写Numpy-b基于矢量化操作,Numba JIT将自动将任何一种情况转换为完全相同的优化C代码。如果您使用常规的装饰器而不是
autojit
,为其提供一些类型信息,则在这方面会更好。我发现这在总体上是好的,但我在Numpy中给出的单行代码的情况只有在我按下循环。+1用于
numba
的出色自包含应用。正是我所寻找的。我在“
numba
'd”中自由地更正了缩进和其他一些内容代码。感谢您的建议,看起来它减少了运行时间,因此非常易于管理。我认为要降低运行时间,需要在C中进行修补,但这就可以了!我没有为Anaconda烦恼,我已经通过brew/pip安装了它。有关详细信息,请参阅我的编辑。
abs(left[row,:][:,newaxis] - right[row,:])
# The problem, 300 400*400 matrices
# Create infinitely high boundary so that we dont need to handle indexing "-1"
a = np.random.rand(300, 400, 402).astype('f')
a[:,:,::a.shape[2]-1] = np.inf
# Create self-overlapping view for 3-way minimize
# This is the input in each iteration
# The shape is (400, 300, 400, 3), separately standing for row, batch, column, left-middle-right
A = np.lib.stride_tricks.as_strided(a, (a.shape[1],len(a),a.shape[2]-2,3), (a.strides[1],a.strides[0],a.strides[2],a.strides[2]))

# Create view for output, this is basically for convenience
# The shape is (399, 300, 400). 399 comes from the fact that first row is never modified
B = a[:,1:,1:-1].swapaxes(0, 1)

# Create a temporary array in advance (try to avoid cache miss)
T = np.empty((len(a), a.shape[2]-2), 'f')
%%timeit
for i in np.arange(a.shape[1]-1):
    A[i].min(2, T)
    B[i] += T