import numpy as np
class Simulator:
def __init__(self, function):
self.f = function # actual objective function
self.num_calls = 0 # how many times f has been called
self.callback_count = 0 # number of times callback has been called, also measures iteration count
self.list_calls_inp = [] # input of all calls
self.list_calls_res = [] # result of all calls
self.decreasing_list_calls_inp = [] # input of calls that resulted in decrease
self.decreasing_list_calls_res = [] # result of calls that resulted in decrease
self.list_callback_inp = [] # only appends inputs on callback, as such they correspond to the iterations
self.list_callback_res = [] # only appends results on callback, as such they correspond to the iterations
def simulate(self, x, *args):
"""Executes the actual simulation and returns the result, while
updating the lists too. Pass to optimizer without arguments or
parentheses."""
result = self.f(x, *args) # the actual evaluation of the function
if not self.num_calls: # first call is stored in all lists
self.decreasing_list_calls_inp.append(x)
self.decreasing_list_calls_res.append(result)
self.list_callback_inp.append(x)
self.list_callback_res.append(result)
elif result < self.decreasing_list_calls_res[-1]:
self.decreasing_list_calls_inp.append(x)
self.decreasing_list_calls_res.append(result)
self.list_calls_inp.append(x)
self.list_calls_res.append(result)
self.num_calls += 1
return result
def callback(self, xk, *_):
"""Callback function that can be used by optimizers of scipy.optimize.
The third argument "*_" makes sure that it still works when the
optimizer calls the callback function with more than one argument. Pass
to optimizer without arguments or parentheses."""
s1 = ""
xk = np.atleast_1d(xk)
# search backwards in input list for input corresponding to xk
for i, x in reversed(list(enumerate(self.list_calls_inp))):
x = np.atleast_1d(x)
if np.allclose(x, xk):
break
for comp in xk:
s1 += f"{comp:10.5e}\t"
s1 += f"{self.list_calls_res[i]:10.5e}"
self.list_callback_inp.append(xk)
self.list_callback_res.append(self.list_calls_res[i])
if not self.callback_count:
s0 = ""
for j, _ in enumerate(xk):
tmp = f"Comp-{j+1}"
s0 += f"{tmp:10s}\t"
s0 += "Objective"
print(s0)
print(s1)
self.callback_count += 1
可以定义简单的测试
from scipy.optimize import minimize, rosen
ros_sim = Simulator(rosen)
minimize(ros_sim.simulate, [0, 0], method='BFGS', callback=ros_sim.callback, options={"disp": True})
print(f"Number of calls to Simulator instance {ros_sim.num_calls}")
8条答案
按热度按时间eqfvzcg81#
正如mg007所建议的,一些scipy.optimize例程允许回调函数(不幸的是,leastsq目前不允许这样做)。下面是一个使用“fmin_bfgs”例程的示例,我使用回调函数来显示参数的当前值和每次迭代时目标函数的值。
输出如下所示:
至少通过这种方式,您可以看到优化器跟踪最小
d8tt03nd2#
下面的例子展示了如何去掉
global
变量、call_back
函数以及多次重新计算目标函数的值。这将生成如下输出
但是,没有免费启动,这里我使用
function evaluation times
而不是algorithmic iteration times
作为计数器。一些算法可能在一次迭代中多次计算目标函数。c9qzyr3d3#
请尝试使用:
以强制
scipy.optimize.minimize
打印中间结果。flseospp4#
scipy中的许多优化器确实缺乏详细的输出(
scipy.optimize.minimize
的'trust-constr'方法是个例外)。我遇到过类似的问题,并通过在目标函数周围创建一个 Package 器和使用回调函数解决了它。这里没有执行额外的函数求值,因此这应该是一个有效的解决方案。可以定义简单的测试
从而导致:
当然,这只是一个模板,它可以根据您的需要进行调整。它并不提供关于优化器状态的所有信息(例如,在MATLAB的优化工具箱中),但至少您对优化的进度有一些了解。
在这里可以找到一个类似的方法,但不使用回调函数。在我的方法中,回调函数用于在优化器完成一次迭代时打印输出,而不是在每次函数调用时打印输出。
z4iuyo4d5#
您使用的是哪个最小化函数?
大多数函数都构建了进度报告,包括通过使用
disp
标志(例如,请参见scipy.optimize.fmin_l_bfgs_b)准确显示所需数据的多级报告。vql8enpb6#
也可以在要最小化的函数中包含一个简单的print()语句。如果你导入了这个函数,你就可以创建一个wapper。
8xiog9wr7#
下面是一个适合我的解决方案:
且通过示例:
来源
omqzjyyz8#
好了!(当心:大多数情况下,全局变量是不好的做法。)
第一个