我正在处理一个黑盒优化问题。由于黑盒的性质,向量(或向量集)
x
在优化期间无法以编程方式重新定义。也就是说,x
本身不会直接传递到黑匣子,而是传递数据库 JSON 文件中定义的一些 x
等效项。在我看来,我必须阅读数据库的内容,以编程方式调用 x
,然后在优化迭代后更新数据库。据我所知,scipy.optimize.minimize
无法使用以这种方式定义的目标函数。有没有解决方法,或者我必须从头开始定义自己的优化函数?
代码(编辑4)
好吧,这是一段代码,或多或少是我想要实现的目标。
# Sum of sum of squared differences function
def sum_ssd(y1, y2):
ssd_y1 = np.sum((y1-y1_data)**2)
ssd_y2 = np.sum((y2-y2_data)**2)
sum_that = ssd_y1 + ssd_y2
return sum_that
# Objective function
def objective(x12):
# Separate opt input into BB inputs
x1 = x12[:5]
x2 = x12[5:]
# Update BB inputs in database
json_obj[some_index]["x1"]=x1.tolist()
json_obj[some_index]["x2"]=x2.tolist()
with open(file_path, "w") as json_fp:
json.dump(json_obj, json_fp, indent = 4)
# Black Box - db_param changes with database changes
y1 = bb(db_param, param1)
y2 = bb(db_param, param2)
# Sum of SSDs
out = sum_ssd(y1, y2)
return out
# Callback function
def callback(x):
print(x)
print(objective(x))
# Set up x0 equivalent
with open(file_path, "r") as json_fp:
json_obj = json.load(json_fp)
x1 = json_obj[some_index]["x1"]
x2 = json_obj[some_index]["x2"]
x1 = np.asarray(x1)
x2 = np.asarray(x2)
x012 = np.append(x1, x2)
# Perform optimization and finish up
res = sp.optimize.minimize(objective, x012, method='Nelder-Mead', callback=callback, options={'disp':True})
因此,对于这个示例,我有
x1
和 x2
是 5 个元素的数组,并且为了欺骗 minimize
来处理多变量输入,我使用 x012
。至于为什么是Nelder-Mead,我不知道黑匣子是不是总是可微的,但它总是连续的。当我运行代码时,minimize
执行,但数据库中的x1
和x2
似乎没有发生任何事情。
编辑:我目前尝试过的...
x2.tolist()
更改为 [1, 2]
以查看数据库是否实际更新 - 确实如此。x
和 objective(x)
会发生什么。答案是没什么。此外,由于我没有使用可微函数,我认为这不应该是由“爆炸梯度”引起的(参见this)。所以,是的,在这一点上我不知所措,并对这个问题悬赏。我知道这是一个普遍问题 - 没有显示黑匣子的代码或如何定义
param1
和 param2
的定义,但我希望这里仍然有足够的代码可以为我提供一些帮助。再次感谢。
这个答案表明即使没有回调,与数据库交互也是可行的。
首先我们提供缺失的和平代码,主要是:
例如:
# Create DB:
data = {
"p": {
"x1": [1, 2, 3, 4, 5],
"x2": [6, 7, 8, 9, 0]
}
}
with open("db.json", "w") as handler:
json.dump(data, handler)
# Create some datasets:
n = 30
y1_data = np.linspace(0, 1, n)
y2_data = np.linspace(-1, 0, n)
# Whatever black box function:
def bb(db_params, params):
"""No idea of what this function does"""
return np.sum(db_params * params) * np.linspace(-1, 1, n)
现在我们调整您的代码以在每一步与 JSON 数据库交互:
# Sum of sum of squared differences function
def sum_ssd(y1, y2):
ssd_y1 = np.sum((y1-y1_data)**2)
ssd_y2 = np.sum((y2-y2_data)**2)
sum_that = ssd_y1 + ssd_y2
return sum_that
# Objective function
def objective(x12):
# Separate opt input into BB inputs
x1 = x12[:5]
x2 = x12[5:]
# Write to DB:
db = {
"p": {
"x1": x1.tolist(),
"x2": x2.tolist()
}
}
with open("db.json", "w") as handler:
json.dump(db, handler)
# Black Box - db_param changes with database changes
# Ensure function signature here!
y1 = bb(x1, x1)
y2 = bb(x2, x2)
# Sum of SSDs
out = sum_ssd(y1, y2)
return out
def callback(x):
with open("db.json", "r") as handler:
print(json.load(handler))
# Read from DB:
with open("db.json", "r") as handler:
db = json.load(handler)
x1 = db["p"]["x1"]
x2 = db["p"]["x2"]
x1 = np.asarray(x1)
x2 = np.asarray(x2)
x012 = np.append(x1, x2)
# Perform optimization and finish up
res = optimize.minimize(objective, x012, method='Nelder-Mead', callback=callback)
额外的回调不是必需的,但我们用它来检查 JSON DB 在每一步是否更新内容。可以通过运行当前代码来确认。
#...
#{'p': {'x1': [0.5635924679456159, 0.37744611037456866, 0.16889467850450193, 0.08056540458960171, 0.06987014247935036], 'x2': [-0.4420054776612644, 0.4365207767214855, -0.25828873817490783, 0.2155649422026483, 0.02998456765120435]}}
#{'p': {'x1': [0.5635974343191635, 0.37744623780370357, 0.16888311231741251, 0.0805657374136636, 0.06985568009797576], 'x2': [-0.44201141928424603, 0.43652230546899895, -0.2582863842239018, 0.21555487781815993, 0.029980534312484618]}}
#...
# message: Optimization terminated successfully.
# success: True
# status: 0
# fun: 15.000000000000522
# x: [ 5.636e-01 3.774e-01 1.689e-01 8.056e-02 6.985e-02
# -4.420e-01 4.365e-01 -2.583e-01 2.156e-01 2.997e-02]
# nit: 130
# nfev: 258
参数已更新,通过此设置,优化成功结束。如果您只有一步并且优化成功,您可能已经将正确的解决方案存储到数据库中。