Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在python中加速非线性优化的多次迭代?_Python_Numpy_Optimization_Parallel Processing_Python Multiprocessing - Fatal编程技术网

如何在python中加速非线性优化的多次迭代?

如何在python中加速非线性优化的多次迭代?,python,numpy,optimization,parallel-processing,python-multiprocessing,Python,Numpy,Optimization,Parallel Processing,Python Multiprocessing,我正在尝试使用 优化类是单独定义的,然后通过单独的函数调用dyn\u optimGMO。该优化必须完成并保存,例如,由变量(节点)inits(或init_val) 使用timeit模块,我发现每次迭代大约需要17秒才能完成。这意味着1000次迭代大约需要5小时。这是一个非常长的时间 如果我必须对20个扰动节点重复此操作,那么总迭代次数将转到200000,这将需要上面计算的线性时间 我试图通过使用pythonmultiprocessingmodule来解决这个问题,为20个扰动节点中的每个节点并行

我正在尝试使用 优化类是单独定义的,然后通过单独的函数调用
dyn\u optimGMO
。该优化必须完成并保存,例如,由变量(节点)
inits(或init_val)

使用
timeit
模块,我发现每次迭代大约需要
17秒才能完成。这意味着
1000次迭代大约需要
5小时
。这是一个非常长的时间

如果我必须对20个扰动
节点重复此操作,那么总迭代次数将转到
200000
,这将需要上面计算的线性时间

我试图通过使用python
multiprocessing
module来解决这个问题,为20个扰动节点中的每个节点并行化每组1000次迭代。但这没用

我还尝试使用Numba jit函数,但它们无法识别pyGMO模块,因此失败

有没有什么方法可以并行化这段代码,并使它在任何迭代次数下都更快

请让我知道我的问题是否足够清楚,如果不清楚,我将根据需要添加详细信息

import numpy as np
import pygmo as pg

matL = np.random.rand(300,300) ; node_len = 300

inits = []; results = []


perturb = {25:0} #setting a random node, say, node 25 to 0

class my_constrained_udp:
    
    def __init__(self):
        pass
    
    def fitness(self, x):
        matA = np.matrix(x)
        obj1 = matA.dot(matL).dot(matA.T)[0,0] #this is int value
        ce1 = sum(init_val) - sum(x)                   
        return [obj1, ce1]
   
    def n_objs(self): # no of objectives
        return 1


    def get_nec(self): #no of equality constraints
        return 1   

 
    def get_nic(self): #no of in-equality constraints
        return 0                    


    def get_bounds(self): #lower and upper bounds: use this to perturb the node
        lowerB = np.array([0]*node_len); upperB = np.array([1]*node_len)
        if perturb:
            for k,v in perturb.items():
                lowerB[k] = v; upperB[k] = v
        return (lowerB,upperB)

  
    def gradient(self, x):
        return pg.estimate_gradient_h(lambda x: self.fitness(x), x)


def dyn_optimGMO(matL, node_len ,init):
        
    if perturb:
        for k,v in perturb.items(): init_val[k] = v  #setting perturbations in initial values
    
    inner_algo = pg.nlopt("slsqp"); inner_algo.maxeval = 5
    
    algo = pg.algorithm(uda = pg.mbh(inner_algo, stop = 2, perturb = .2))
    #algo.set_verbosity(10) # in this case this correspond to logs each 1 call to slsqp
    pop = pg.population(prob = my_constrained_udp(), size = 1000 , seed=123)
    pop.problem.c_tol = [1E-6] * 1 # get_nec + get_nic = 1, so multiplied by 1
    pop = algo.evolve(pop) 
   
    res = pop.champion_x   
    return res

# running above optimization code for 1000 random initializations

for i in range(1000):
    init_val = np.array([random.uniform(0, 1) for k in range(node_len)])
    
    if perturb:
        for k,v in perturb.items(): init_val[k] = v  #setting perturbations in initial values
    
    res = dyn_optimGMO(matL ,node_len ,init_val) # this function is defined here only
    
    inits.append(init_val); results.append(res)
编辑1:

正如@Ananda在下面所建议的,我对目标函数进行了修改,使运行时间减少了近7倍。我已经重写了代码,使用python
multiprocessing
模块在
1000次迭代中运行此代码。下面是我的新代码,我试图在其中生成并行处理迭代的进程。因为我的系统只有8个线程,所以我将池大小限制为5个,因为PyGMO也使用内部并行,并且它也需要一些线程

import numpy as np
import pygmo as pg


matL = np.random.rand(300,300) ; node_len = 300

perturb = {12:1} # assign your perturb ID here

def optimizationFN(var):

    results = []
    
    inits = var[0]; perturb = var[1]

    
    class my_constrained_udp:
        
        def fitness(self, x):
            obj1 = x[None,:] @ matL @ x[:,None] # @ is mat multiplication operator
            ce1 = np.sum(inits) - np.sum(x)                   
            return [obj1, ce1]
       
        def n_objs(self): # no of objectives
            return 1
        
        def get_nec(self): #no of equality constraints
            return 1    
        
        def get_nic(self): #no of in-equality constraints
            return 0                    
        
        def get_bounds(self): #lower and upper bounds: use this to perturb the node
            lowerB = np.array([0]*node_len); upperB = np.array([1]*node_len)
            if perturb:
                for k,v in perturb.items():
                    lowerB[k] = v; upperB[k] = v
            return (lowerB,upperB)
        
        def gradient(self, x):
            return pg.estimate_gradient_h(lambda x: self.fitness(x), x)
    
    def dyn_optimGMO(matL, node_len ,inits):
        '''
        perturb should be a dict of node index and value as 0 or 1. Type(node_index) = int
        '''  
        if perturb:
            for k,v in perturb.items(): inits[k] = v  #setting perturbations in initial values
            
        inner_algo = pg.nlopt("slsqp"); inner_algo.maxeval = 5
        
        algo = pg.algorithm(uda = pg.mbh(inner_algo, stop = 2, perturb = .2))
        
        #algo.set_verbosity(10) # in this case this correspond to logs each 1 call to slsqp
        
        pop = pg.population(prob = my_constrained_udp(), size = 100 , seed=123)
        
        pop.problem.c_tol = [1E-6] * 1 # get_nec + get_nic = 1, so multiplied by 1
        pop = algo.evolve(pop) 
       
        res = pop.champion_x   
        return res
    
    
    if perturb:
        for k,v in perturb.items(): inits[k] = v  #setting perturbations in initial values
    
    res = dyn_optimGMO(matL ,node_len ,inits) # this function is defined here only
    
    results.append(res)
    
    return results

import time

st = time.time()
    
#1000 random initialisations
initial_vals = []
for i in range(1000): initial_vals.append(np.array([random.uniform(0, 1) for k in range(node_len)]))
initial_vals = np.array(initial_vals)

inp_val = []
for i in range(len(initial_vals)): inp_val.append([initial_vals[i],perturb])

#running evaluations
#eqVal = optimizationFN(initial_vals,perturb=perturb)
from multiprocessing import Pool


myPool = Pool(8)

data = myPool.map(optimizationFN,inp_val)

myPool.close(); myPool.join()


print('Total Time: ',round(time.time()-st,4))
这将在
1.13小时内执行整个1000次迭代


不过,是否还有其他可能使其更快?

在尝试并行化等之前,请尝试找出性能的瓶颈,并尝试解决这一问题

如果您使用

如您所见,大部分时间都花在
dot
sum
函数中,创建
matA
也花费了大量时间

我会像这样重写函数-

def fitness(self, x):

    obj1 = x[None, :] @ matL @ x[:, None]
    ce1 = np.sum(init_val) - np.sum(x)

    return [obj1, ce1]
如果你配置了这个函数,你可以看到

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    20                                               @profile
    21                                               def fitness(self, x):
    22                                           
    23     77084    3151649.0     40.9     48.9          obj1 = x[None, :] @ matL @ x[:, None]
    24     77084    3214012.0     41.7     49.9          ce1 = np.sum(init_val) - np.sum(x)
    25                                           
    26     77084      79439.0      1.0      1.2          return [obj1, ce1]
完整功能的每次点击总时间从380左右下降到80

np.建议不要再使用矩阵
方法,该方法将被弃用。使用本地python
sum
而不是
np.sum
可以大大降低性能

在我的机器上,它将性能从33秒/次提高到6秒/次。性能提高约5倍

Q:“是否有任何方法可以并行化此代码,并使其在任何迭代次数下都更快?”

对。如果试图
numba.jit()
代码“来自外部”(由于numba编译警告的原因失败),您可以尝试分发该批约1k+独立初始化的部分,并让它们并行计算,然后收集结果

这样做的好处是在一个快照中将性能提高1000倍,并且可以进一步扩展


如果您使用的是一个包含约1k+节点的大学集群,那么1k批计算可以在1k长序列的第一次单独运行的时间内产生结果(此处的通信成本可以忽略不计,请参阅).

CUDA与您的问题完全无关,我已经删除了标记。根据我的判断,代码有缺陷,请发布一个工作代码。对我来说,这是在扰动中k,v的
处抛出错误。items():init_val[k]=v
@Ananda完成了编辑。现在它应该在运行了,你要求的是代码检查——这并不是StackOverflow的真正目的。如果您对如何改进这一点有具体的想法,并且您已经尝试过,但遇到了麻烦,请描述具体的问题并征求建议。您是在要求其他人“找到一种方法使其快速运行或进一步并行化”——即代码审查?您需要解决哪些具体问题?你试了什么?你有什么问题?你使用的分析器是什么?@Naveen我使用的是line_profiler这实际上大大提高了运行时间,在我的机器上几乎快了7倍。LineProfiler是一个很好的工具。但是,你也能建议一种并行化上述执行以使它更快的方法吗?我怀疑,如果你想把它推到比这更大的位置,你可能会从Python移开,使用C++或者别的什么东西。据我所知,在python中,多处理+确保事情正确矢量化可能是最好的方法。单击“更多和更多向下投票”不会创建更强的反参数。在许多独立初始化的进化模型中,大规模并行过程流是最强大的性能助推器。以这种方式处理相同大小的批(数量)PyGMO作业要比以纯[串行]队列顺序处理它们快得多,这有利于加速模拟管道的某些部分。否决票在这方面一点也没有改变,也没有产生任何有意义的反论点——因此它们表现出纯粹的仇恨和愤怒行为,而不是我没有投票的任何好处,但我想知道否决票是否是因为人们告诉你一段时间的原因:这是一篇没有说太多的浮夸文章,并且过度使用了格式,这使得帖子更难阅读。你介意解释一下@halfer你从哪里得出了“不太多”的结论吗?是的。它a)说明了为什么numba相关的期望失败了,并将再次失败+b)将用户引导到唯一可行的方向,以真正提高E2E性能(使用分布式c
Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    20                                               @profile
    21                                               def fitness(self, x):
    22                                           
    23     77084    3151649.0     40.9     48.9          obj1 = x[None, :] @ matL @ x[:, None]
    24     77084    3214012.0     41.7     49.9          ce1 = np.sum(init_val) - np.sum(x)
    25                                           
    26     77084      79439.0      1.0      1.2          return [obj1, ce1]