【问题标题】:Scipy's optimization incompatible with Multiprocessing?Scipy 的优化与多处理不兼容?
【发布时间】:2016-04-21 05:49:17
【问题描述】:

在尝试使用 Scipy 的优化算法来最小化在子进程中计算其值的函数时,我发现基于梯度的算法(到目前为止的盆地跳跃和 L-BFGS-B)在优化的第 562 行遇到以下错误.py:

grad[k] = (f(*((xk + d,) + args)) - f0) / d[k]

TypeError: 不支持的操作数类型 -: 'NoneType' 和 'NoneType'

下面是一个产生此错误的简单代码示例:

import multiprocessing as mp
from scipy.optimize import basinhopping

def runEnvironment(x):
    return x**2

def func(x):
    if __name__ == '__main__':
        print "x:",x
        pool = mp.Pool(processes=1)

        results=pool.apply(runEnvironment,(x,))
        pool.close()
        return results

x0=5    
ret=basinhopping(func, x0, niter=100, T=1.0, stepsize=0.1, minimizer_kwargs=None, take_step=None, accept_test=None, callback=None, interval=50, disp=False, niter_success=None)

请注意,如果删除了多处理组件,或者使用了非基于梯度的算法(如 COBYLA),则此代码运行良好。谁能想到发生这种情况的原因?

【问题讨论】:

  • 为什么func 有一个if __name__ == '__main__' 测试?

标签: python optimization scipy multiprocessing


【解决方案1】:

您的 if __name__ == '__main__': 成语位置不正确 - 以这种方式重新排列可以:

import multiprocessing as mp
from scipy.optimize import basinhopping

def runEnvironment(x):
    return x**2

def func(x):

    print "x:",x
    pool = mp.Pool(processes=1)

    results=pool.apply(runEnvironment,(x,))
    pool.close()
    return results

if __name__ == '__main__':
    x0=5
    ret=basinhopping(func, x0, niter=100, T=1.0, stepsize=0.1, minimizer_kwargs=None, take_step=None, accept_test=None, callback=None, interval=50, disp=False, niter_success=None)

【讨论】:

  • 效果很好!虽然我不确定我是否理解为什么这是 if name == 'main': 语句的正确位置。
  • @Peter:由于 Windows 没有 os.fork,而是 multiprocessing 模块 starts a new python process and imports the calling module。如果您不使用if __name__ == '__main__' 保护对basinhopping 的调用,则子进程调用basinhopping,它将调用func。由于func内部有一个if __name__ == '__main__',所以这个func调用返回None,没有到达return语句时的默认值....
  • @Peter: (cont'd) basinhopping 期望 func 返回一个数值,因此当 func 返回 None 时会引发 TypeError
  • 啊,好吧,有道理。
  • @unutbu 这对类方法也有奇怪的行为,这是预期的吗?我的最新问题解释了它stackoverflow.com/questions/67291316/…
【解决方案2】:

创建许多小的mp.Pools 每个只有一个工人是低效的 过程。每次调用func 创建一个池也是低效的,因为 func 被多次调用。

相反,在程序开始时创建 一个 Pool,并将池传递给对 func 的每次调用:

if __name__ == '__main__':
    pool = mp.Pool()
    x0=5    
    ret = optimize.basinhopping(
        func, x0, niter=100, T=1.0, stepsize=0.1,
        minimizer_kwargs=dict(args=pool), 
        take_step=None, accept_test=None, callback=None,
        interval=50, disp=False, niter_success=None)
    pool.close()
    print(ret)

minimizer_kwargs=dict(args=pool) 告诉optimize.basinhoppingpool 作为附加参数传递给func


你也可以使用

logger = mp.log_to_stderr(logging.INFO)

获取显示函数在哪个进程中被调用的日志记录语句。例如,

import multiprocessing as mp
from scipy import optimize
import logging
logger = mp.log_to_stderr(logging.INFO)

def runEnvironment(x):
    logger.info('runEnvironment({}) called'.format(x))
    return x**2

def func(x, pool):
    logger.info('func({}) called'.format(x))
    results = pool.apply(runEnvironment,(x,))
    return results

if __name__ == '__main__':
    pool = mp.Pool()
    x0=5    
    ret = optimize.basinhopping(
        func, x0, niter=100, T=1.0, stepsize=0.1,
        minimizer_kwargs=dict(args=pool), 
        take_step=None, accept_test=None, callback=None,
        interval=50, disp=False, niter_success=None)
    pool.close()
    print(ret)

打印

[INFO/PoolWorker-1] child process calling self.run()
[INFO/PoolWorker-2] child process calling self.run()
[INFO/PoolWorker-3] child process calling self.run()
[INFO/PoolWorker-4] child process calling self.run()
[INFO/MainProcess] func([ 5.]) called
[INFO/PoolWorker-1] runEnvironment([ 5.]) called
[INFO/MainProcess] func([ 5.00000001]) called
[INFO/PoolWorker-2] runEnvironment([ 5.00000001]) called
[INFO/MainProcess] func([ 5.]) called
[INFO/PoolWorker-3] runEnvironment([ 5.]) called
[INFO/MainProcess] func([-5.]) called
[INFO/PoolWorker-4] runEnvironment([-5.]) called

这说明func总是被主进程调用,而 runEnvironment 由工作进程运行。

请注意,对func 的调用是按顺序进行的。从中获得任何好处 pool,每次调用 func,您都必须使用更多的处理器。

【讨论】:

  • 没错,但我会为每个函数调用创建一个池,以便在评估函数后立即释放内存。我正在使用一些似乎存在内存泄漏问题的第三方库,这是一种快速而肮脏的工作=/
  • 啊,我明白了。这是在func 中使用mp.Pool 的一个很好的理由。
猜你喜欢
  • 1970-01-01
  • 2019-05-12
  • 2020-11-21
  • 2019-02-27
  • 1970-01-01
  • 2017-02-08
  • 2019-11-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多