【问题标题】:How do I terminate an async scipy.optimize based on time?如何根据时间终止异步 scipy.optimize?
【发布时间】:2023-03-20 15:14:01
【问题描述】:

这个问题真的很挣扎...原谅这个冗长的帖子。

我有一个实验,每次试验都会显示一些刺激,收集反应,然后继续下一个试验。

我想合并一个在两次试验之间运行的优化器,因此必须有一个由我指定的特定时间窗口来运行,否则它应该被终止。如果它被终止,我想返回它尝试的最后一组参数,以便我以后可以使用它。

一般来说,这是我希望发生的事件的顺序:

在试验之间:

  1. 显示刺激(“+”)几秒钟。
  2. 发生这种情况时,运行优化器。
  3. 如果显示“+”的时间已过且优化器已 未完成,终止优化器,返回它尝试过的最新参数集,然后继续。

这是我目前正在使用的一些相关代码:

do_bns() 是目标函数。其中我包括NLL['par'] = parq.put(par)

from scipy.optimize import minimize
from multiprocessing import Process, Manager, Queue
from psychopy import core #for clock, and other functionality

clock = core.Clock()

def optim(par, NLL, q)::
    a = minimize(do_bns, (par), method='L-BFGS-B', args=(NLL, q),
        bounds=[(0.2, 1.5), (0.01, 0.8), (0.001, 0.3), (0.1, 0.4), (0.1, 1), (0.001, 0.1)],
        options={"disp": False, 'maxiter': 1, 'maxfun': 1, "eps": 0.0002}, tol=0.00002)

if __name__ == '__main__':
    print('starting optim')
    max_time = 1.57
    with Manager() as manager:
        par = manager.list([1, 0.1, 0.1, 0.1, 0.1, 0.1])
        NLL = manager.dict()
        q = Queue()
        p = Process(target=optim, args=(par, NLL, q))
        p.start()
        start = clock.getTime()

        while clock.getTime() - start < max_time:
            p.join(timeout=0)
            if not p.is_alive():
                break

        if p.is_alive():
            res = q.get()
            p.terminate()
            stop = clock.getTime()
            print(NLL['a'])
            print('killed after: ' + str(stop - start))

        else:
            res = q.get()
            stop = clock.getTime()
            print('terminated successfully after: ' + str(stop - start))
            print(NLL)
            print(res)

这段代码,就其本身而言,似乎可以做我想做的事。例如,p.terminate() 正上方的res = q.get() 实际上需要大约 200 毫秒,因此如果 max_time ,它不会恰好在 max_time 终止

如果我将此代码包装在一个 while 循环中,以检查是否该停止呈现刺激:

stim_start = clock.getTime()
stim_end = 5
print('showing stim')
textStim.setAutoDraw(True)
win.flip()
while clock.getTime() - stim_start < stim_end:

    # insert the code above

print('out of loop')

我得到了奇怪的行为,例如从一开始就对整个代码进行多次迭代......

showing stim
starting optim
showing stim
out of loop
showing stim
out of loop
[1.0, 0.10000000000000001, 0.10000000000000001, 0.10000000000000001, 0.10000000000000001, 0.10000000000000001]
killed after: 2.81303440395

注意多个“显示刺激”和“循环外”。

我愿意接受任何实现我目标的解决方案:|

帮助,谢谢! 本

【问题讨论】:

    标签: python-2.7 scipy mathematical-optimization


    【解决方案1】:

    一般说明

    你的解决方案会让我做噩梦!我没有看到在这里使用多处理的理由,我什至不确定您如何在终止之前获取这些更新的结果。也许您有这种方法的理由,但我强烈推荐其他方法(有限制)。

    基于回调的方法

    我将追求的总体思路如下:

    • 使用一些额外的时间限制信息和一些执行此操作的 回调 启动优化器
      • 在此优化器的每次迭代中都会调用回调
        • 如果达到时间限制:引发自定义异常

    限制:

    • 由于回调在每次迭代中仅调用一次,因此优化器可能会停止的时间点序列有限
      • 潜在差异在很大程度上取决于您的问题的迭代时间! (数值微分、大数据、缓慢的函数评估;所有这些都很重要)
      • 如果不超过某个给定时间是最高优先级,则此方法可能不正确,或者您需要某种受保护的插值来推断是否可以及时进行更多迭代
      • 或者:将你这种杀死工人的方式与我通过一些回调更新中间结果的方法结合起来

    示例代码(有点hacky):

    import time
    import numpy as np
    import scipy.sparse as sp
    import scipy.optimize as opt
    np.random.seed(1)
    
    """ Fake task: sparse NNLS """
    M, N, D = 2500, 2500, 0.1
    A = sp.random(M, N, D)
    b = np.random.random(size=M)
    
    """ Optimization-setup """
    class TimeOut(Exception):
        """Raise for my specific kind of exception"""
    
    def opt_func(x, A, b):
        return 0.5 * np.linalg.norm(A.dot(x) - b)**2
    
    def opt_grad(x, A, b):
        Ax = A.dot(x) - b
        grad = A.T.dot(Ax)
        return grad
    
    def callback(x):
        time_now = time.time()             # probably not the right tool in general!
        callback.result = [np.copy(x)]     # better safe than sorry -> copy
    
        if time_now - callback.time_start >= callback.time_max:
            raise TimeOut("Time out")
    
    def optimize(x0, A, b, time_max):
        result = [np.copy(x0)]                                   # hack: mutable type
        time_start = time.time()
        try:
            """ Add additional info to callback (only takes x as param!) """
            callback.time_start = time_start
            callback.time_max = time_max
            callback.result = result
    
            res = opt.minimize(opt_func, x0, jac=opt_grad,
                         bounds=[(0, np.inf) for i in range(len(x0))],  # NNLS
                         args=(A, b), callback=callback, options={'disp': False})
    
        except TimeOut:
            print('time out')
            return result[0], opt_func(result[0], A, b)
    
        return res.x, res.fun
    
    print('experiment 1')
    start_time = time.perf_counter()
    x, res = optimize(np.zeros(len(b)), A, b, 0.1)  # 0.1 seconds max!
    end_time = time.perf_counter()
    print(res)
    print('used secs: ', end_time - start_time)
    
    print('experiment 2')
    start_time = time.perf_counter()
    x_, res_ = optimize(np.zeros(len(b)), A, b, 5)  # 5 seconds max!
    end_time = time.perf_counter()
    print(res_)
    print('used secs: ', end_time - start_time)
    

    示例输出:

    experiment 1
    time out
    422.392771467
    used secs:  0.10226839151517493
    
    experiment 2
    72.8470708728
    used secs:  0.3943936788825996
    

    【讨论】:

    • 嗨,萨沙,谢谢您的回复。除了噩梦,我很欣赏周到的回答。使用回调方法(我已经尝试过)的问题是即使设置 maxitr = 1 也需要太长时间。由于毫秒计时在此应用程序中非常重要,这不是一个可行的解决方案,因为由于目标函数的复杂性,回调发生的频率太低。这就是为什么我不得不考虑这些复杂的方法。
    • 如果 1 太慢了,你无能为力,因为永远不会及时改进到 x0。所以相当于根本不优化。另外:这种行为在很大程度上取决于具体情况!有趣,毕业生,哪个优化器...
    猜你喜欢
    • 1970-01-01
    • 2012-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-15
    • 2015-09-23
    • 1970-01-01
    • 2017-07-22
    相关资源
    最近更新 更多