【发布时间】:2021-04-04 08:32:52
【问题描述】:
我正在尝试使用 PyGMO package 解决非线性优化
优化类是单独定义的,然后通过单独的函数dyn_optimGMO 调用。必须为变量(节点)inits ( or init_val)
使用timeit 模块我发现每次迭代大约需要17 seconds 才能完成。这意味着1000 iterations 大约需要5 hours 。这是一个非常大的时间。
如果我必须为 20 perturb 节点重复此操作,那么总迭代次数将转到 200000,这将花费线性时间,如上面计算的那样。
我尝试通过使用 python multiprocessing 模块为 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 iterations 上运行此代码。下面是我尝试生成进程以并行处理迭代的新代码。由于我的系统只有 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 hours 中执行全部 1000 次迭代。
不过,有没有其他可能让我可以让它更快?
【问题讨论】:
-
CUDA 与您的问题完全无关,我已删除标签
-
据我所知,该代码存在错误,请发布工作代码。对我来说,它在
for k, v in perturb.items(): init_val[k] = v处抛出错误 -
@Ananda 完成了编辑。现在它应该正在运行。
-
您要求进行代码审查 - 这并不是 StackOverflow 的真正用途。如果您对如何改进有具体的想法,并且尝试过但遇到了麻烦,请描述具体问题并寻求建议。
-
您要求其他人“找到一种方法让它运行得更快或进一步并行化” - 即代码审查?您需要解决什么具体问题?你尝试了什么?你遇到了什么问题?
标签: python numpy optimization parallel-processing python-multiprocessing