在python中并行处理一个3D优化问题。

问题描述 投票:2回答:1

我正在研究一个优化问题,有 scipy.optimize 它的目的是计算一些3D地图。给定3个真实数据量 (vol0, vol1, vol2) 我的目标是估计3个参数的地图 (map_0, map_1, map_2) 通过体素强度的一些函数对其模型进行体素拟合。

到目前为止,这是我开始的想法。

import scipy
import numpy as np
from scipy.optimize import minimize

def objective (x,arg0,arg1):

    vol_model0 = someFun( arg0[0], arg1, ... ) # *model value for arg0 which needs arg0[0] and arg1*
    vol_model1 = someFun( arg0[1], arg1, ... ) # *model value for arg0 which needs arg0[1] and arg1*
    vol_model2 = someFun( arg0[2], arg1, ... ) # *model value for arg0 which needs arg0[2] and arg1*

    RSS = np.sum( [ np.power( ( arg0[0] - vol_model0 ), 2 )
                  + np.power( ( arg0[1] - vol_model1 ), 2 )
                  + np.power( ( arg0[2] - vol_model2 ), 2 ) ]
                  )
    return RSS


arg1 = [1, 2, 3, 4]

vol0 = 5* np.zeros([100,100,100])
vol1 = 3* np.zeros([100,100,100])
vol2 = 4* np.zeros([100,100,100])

map_0 = np.zeros([100,100,100])
map_1 = np.zeros([100,100,100])
map_2 = np.zeros([100,100,100])

x0    = [5, 5, 5] 
bnds  = ( (1,10), (1, 10), (1, 10) )

for i0 in range(0,100):
    for i1 in range(0,100):
        for i2 in range(0,100):
            arg0 = [ vol0[i0,i1,i2], \
                     vol1[i0,i1,i2],  \
                     vol2[i0,i1,i2]    \
                     ]
            res = minimize(objective, x0, args = (arg0,arg1), bounds = bnds)
            map_0[i0,i1,i2], \
            map_1[i0,i1,i2],   \
            map_2[i0,i1,i2] = res.x

我的问题是: 给定这个约束性优化问题,有没有办法让整个过程更快?嵌套的for循环需要很长的时间来完成.我想知道是否有办法将这个问题并行化或使其更快。

python multidimensional-array parallel-processing minimize
1个回答
1
投票

这个问题更多的是与 效率 的处理,比使用任何并发处理要好。

在进入任何进一步的步骤之前,让我提出几个要做的步骤。

1 ) 鉴于 1E6 调用将完成,优化所有的函数开销--因为每个 [us] 拯救的将会拯救另一个整体 [s] 在终点线上

2 ) 避免任何不相关的开销 (如果一个变量永远不会被重复使用,为什么要让python创建一个变量,维护一个变量名呢?你只是付出了成本,却没有得到任何回报)。)

3 ) 如果可能的话,numba-compile的代码,以提高性能&。objective() 函数,这里的每一个 [us] 削去 someFun( aFloat64, anInt64[], ... ) 运行时将进一步节省您的 ~ 3 [s]-的倍数。minimize()-在终点线上再重复一遍... ... 值得这样做,不是吗?


一个即时的附加功能:一个便宜的& 低悬的水果。矢量化& 利用Numpy工具的全部力量。

>>> aClk.start(); _ = np.sum( # _______________________________ AVOID I/O on printing here by assignment into _ var
                              [ np.power( ( arg0[0] - v0 ), 2 ),
                                np.power( ( arg0[1] - v1 ), 2 ),
                                np.power( ( arg0[2] - v2 ), 2 )
                                ] #___________________________ LIST-constructor overheads
                              ); aClk.stop() # _______________.stop() the [us]-clock
225 [us]
164 [us]
188
157
175
199
201
201
171 [us]

>>> RSS = _
>>> RSS
0.16506628505715615

交叉验证方法的正确性。

>>> arg0_dif     = arg0.copy()
>>> vV           = np.ones( 3 ) * 1.23456789
>>> arg0_dif[:] -= vV[:]
>>> np.dot( arg0_dif, arg0_dif.T )
0.16506628505715615

+5x ~ +7x FASTER CODE 。

>>> aClk.start(); _ = np.dot( arg0_dif, arg0_dif.T ); aClk.stop()
39
38
28
28
27 [us] ~ +5x ~ +7x FASTER just by using more efficient way to produce the RSS calculations

下一步。

"""
@numba.jit( signature =    'f8[:], f8[:], i8[:]', nogil = True, ... ) """
def aMoreEfficientObjectiveFUN( x,  arg0,  arg1 ):
    ###############################################################################################
    # BEST 2 VECTORISE THE someFun() into someFunVECTORISED(),
    #                      so as to work straight with arg0-vector and return vector of differences
    #rg0[:]   -= someFunVECTORISED( arg0,    arg1, ... ) ##########################################
    arg0[0]   -= someFun(           arg0[0], arg1, ... ) # *model value for arg0 which needs arg0[0] and arg1*
    arg0[1]   -= someFun(           arg0[1], arg1, ... ) # *model value for arg0 which needs arg0[0] and arg1*
    arg0[2]   -= someFun(           arg0[2], arg1, ... ) # *model value for arg0 which needs arg0[0] and arg1*

    return  np.dot( arg0, arg0.T ) # just this change gets ~ 5x ~ 7x FASTER processing ............

做好了这些,剩下的事情就简单了。

在一个案例中,所有修改后的附加间接费用。阿姆达赫定律 仍然可以证明支付所有的附加费用是合理的,即.XFER-process2main。 + SERDES (在发送参数前进行酸洗) + XFER-main2process + SERDES (在发送结果前进行酸洗) + XFER-process2main,即 multiprocessing 可以帮你分担 1E6呼叫,完全独立的呼叫,并将结果回溯到了 "我"。map_tensor[:,i0,i1,i2] ~ ( map_0[i0,i1,i2], map_1[i0,i1,i2], map_2[i0,i1,i2] )

在所有附加的开销成本都没有得到证明的情况下,保持工作流不在几个进程之间分配,如果确实好奇和热衷于实验,可以尝试在一个基于线程的并发处理内运行这个,作为GIL-free处理(逃避,在 numpy-的部件,不需要持有GIL锁,GIL驱动的成本重新。[SERIAL]-化)可能会在叠加的并发处理上表现出一些延迟屏蔽。体内测试将证明这一点或表明,在python原生态系统中这样做没有额外的优势。


尾声 。

Would be nice & fair to give us your feedback on runtime improvements, partly from cod-efficiency ( 从最初的 ~ 27 minutes (原状) -- -- 智能化之后 np.dot() 技巧 -- +在完全矢量化后的 objective( x, arg0, arg1 ),其中 someFun() 可以处理向量,并智能地返回了 np.dot() 单独 -- +在一个 @numba.jit( ... ) 装饰处理,如果这样的结果表明自己的效率进一步提高了

© www.soinside.com 2019 - 2024. All rights reserved.