寻找最小化代价函数的 n 元组
Finding n-tuple that minimizes expensive cost function
假设有三个变量取离散整数值,比如w1 = {1,2,3,4,5,6,7,8,9,10,11,12},w2 = {1 ,2,3,4,5,6,7,8,9,10,11,12}, w3 = {1,2,3,4,5,6,7,8,9,10,11, 12}。任务是从每个集合中选择一个值,使得生成的三元组最小化一些(黑匣子,计算量大)成本函数。
我已经在 Matlab 中尝试过代理优化,但我不确定它是否合适。我也听说过模拟退火,但没有发现适用于此实例的实现。
除了穷举搜索,还有什么算法可以解决这个组合优化问题?
如有任何帮助,我们将不胜感激。
尝试三个值的所有可能组合,看看哪个成本最低。
Simulated Annealing(SA)的requirement/benefit,就是objective表面有些光滑,也就是我们可以接近一个解
- 对于一个完全随机的尖顶表面——你不妨做一个随机搜索
- 如果一切顺利,甚至有时,尝试 SA 是有意义的。
想法是(有时)仅更改 3 个值中的 1 个,我们对 blackbox 函数的影响很小。
这是一个使用模拟退火的基本示例,在 Python
中使用 frigidum
import numpy as np
w1 = np.array( [1,2,3,4,5,6,7,8,9,10,11,12] )
w2 = np.array( [1,2,3,4,5,6,7,8,9,10,11,12] )
w3 = np.array( [1,2,3,4,5,6,7,8,9,10,11,12] )
W = np.array([w1,w2,w3])
LENGTH = 12
我使用 Rastrigin 函数定义了一个黑盒。
def rastrigin_function_n( x ):
"""
N-dimensional Rastrigin
https://en.wikipedia.org/wiki/Rastrigin_function
x_i is in [-5.12, 5.12]
"""
A = 10
n = x.shape[0]
return A*n + np.sum( x**2- A*np.cos(2*np.pi * x) )
def black_box( x ):
"""
Transform from domain [1,12] to [-5,5]
to be able to push to rastrigin
"""
x = (x - 6.5) * (5/5.5)
return rastrigin_function_n(x)
模拟退火需要修改状态 X。我们跟踪索引而不是直接 taking/modifying 值。这简化了创建新提案的过程,因为索引始终是一个整数,我们可以简单地 add/subtract 1 modulo LENGTH.
def random_start():
"""
returns 3 random indices
"""
return np.random.randint(0, LENGTH, size=3)
def random_small_step(x):
"""
change only 1 index
"""
d = np.array( [1,0,0] )
if np.random.random() < .5:
d = np.array( [-1,0,0] )
np.random.shuffle(d)
return (x+d) % LENGTH
def random_big_step(x):
"""
change 2 indici
"""
d = np.array( [1,-1,0] )
np.random.shuffle(d)
return (x+d) % LENGTH
def obj(x):
"""
We have a triplet of indici,
1. Calculate corresponding values in W = [w1,w2,w3]
2. Push the values in out black-box function
"""
indices = x
values = W[np.array([0,1,2]), indices]
return black_box(values)
并向其抛出一个 SA 方案
import frigidum
local_opt = frigidum.sa(random_start=random_start,
neighbours=[random_small_step, random_big_step],
objective_function=obj,
T_start=10**4,
T_stop=0.000001,
repeats=10**3,
copy_state=frigidum.annealing.naked)
我不确定这个函数的最小值应该是多少,但它找到了一个 objective with 47.9095
with indicis np.array([9, 2, 2])
编辑:
对于frigidum改变冷却时间表,使用alpha=.9
。我的经验是,冷却方案效果最好的所有实验工作都不会超过重量,只需让它 运行 长一点。您提出的乘法(有时称为几何乘法)是标准乘法,也是在 frigidum 中实现的。所以要实现 Tn+1 = 0.9*Tn
你需要一个 alpha=.9
。请注意,此冷却步骤是在 N 次重复后完成的,因此如果重复次数=100,它将首先进行 100 次提议,然后再以因子 alpha
降低温度
当前状态的简单变化通常效果最好。由于最好的做法是将初始温度设置得足够高以使大多数提案 (>90%) 被接受,因此步长小并不重要。但如果您担心它太小,请尝试 2 或 3 个变体。 Frigidum接受proposal functions列表,组合可以相互强制。
我没有使用 MINLP 的经验。但即便如此,那么多次实验也能让我们大吃一惊。所以如果 time/cost 是小到给 table 带来另一个竞争对手,是的!
假设有三个变量取离散整数值,比如w1 = {1,2,3,4,5,6,7,8,9,10,11,12},w2 = {1 ,2,3,4,5,6,7,8,9,10,11,12}, w3 = {1,2,3,4,5,6,7,8,9,10,11, 12}。任务是从每个集合中选择一个值,使得生成的三元组最小化一些(黑匣子,计算量大)成本函数。
我已经在 Matlab 中尝试过代理优化,但我不确定它是否合适。我也听说过模拟退火,但没有发现适用于此实例的实现。
除了穷举搜索,还有什么算法可以解决这个组合优化问题?
如有任何帮助,我们将不胜感激。
尝试三个值的所有可能组合,看看哪个成本最低。
Simulated Annealing(SA)的requirement/benefit,就是objective表面有些光滑,也就是我们可以接近一个解
- 对于一个完全随机的尖顶表面——你不妨做一个随机搜索
- 如果一切顺利,甚至有时,尝试 SA 是有意义的。
想法是(有时)仅更改 3 个值中的 1 个,我们对 blackbox 函数的影响很小。
这是一个使用模拟退火的基本示例,在 Python
中使用 frigidumimport numpy as np
w1 = np.array( [1,2,3,4,5,6,7,8,9,10,11,12] )
w2 = np.array( [1,2,3,4,5,6,7,8,9,10,11,12] )
w3 = np.array( [1,2,3,4,5,6,7,8,9,10,11,12] )
W = np.array([w1,w2,w3])
LENGTH = 12
我使用 Rastrigin 函数定义了一个黑盒。
def rastrigin_function_n( x ):
"""
N-dimensional Rastrigin
https://en.wikipedia.org/wiki/Rastrigin_function
x_i is in [-5.12, 5.12]
"""
A = 10
n = x.shape[0]
return A*n + np.sum( x**2- A*np.cos(2*np.pi * x) )
def black_box( x ):
"""
Transform from domain [1,12] to [-5,5]
to be able to push to rastrigin
"""
x = (x - 6.5) * (5/5.5)
return rastrigin_function_n(x)
模拟退火需要修改状态 X。我们跟踪索引而不是直接 taking/modifying 值。这简化了创建新提案的过程,因为索引始终是一个整数,我们可以简单地 add/subtract 1 modulo LENGTH.
def random_start():
"""
returns 3 random indices
"""
return np.random.randint(0, LENGTH, size=3)
def random_small_step(x):
"""
change only 1 index
"""
d = np.array( [1,0,0] )
if np.random.random() < .5:
d = np.array( [-1,0,0] )
np.random.shuffle(d)
return (x+d) % LENGTH
def random_big_step(x):
"""
change 2 indici
"""
d = np.array( [1,-1,0] )
np.random.shuffle(d)
return (x+d) % LENGTH
def obj(x):
"""
We have a triplet of indici,
1. Calculate corresponding values in W = [w1,w2,w3]
2. Push the values in out black-box function
"""
indices = x
values = W[np.array([0,1,2]), indices]
return black_box(values)
并向其抛出一个 SA 方案
import frigidum
local_opt = frigidum.sa(random_start=random_start,
neighbours=[random_small_step, random_big_step],
objective_function=obj,
T_start=10**4,
T_stop=0.000001,
repeats=10**3,
copy_state=frigidum.annealing.naked)
我不确定这个函数的最小值应该是多少,但它找到了一个 objective with 47.9095
with indicis np.array([9, 2, 2])
编辑:
对于frigidum改变冷却时间表,使用
降低温度alpha=.9
。我的经验是,冷却方案效果最好的所有实验工作都不会超过重量,只需让它 运行 长一点。您提出的乘法(有时称为几何乘法)是标准乘法,也是在 frigidum 中实现的。所以要实现Tn+1 = 0.9*Tn
你需要一个alpha=.9
。请注意,此冷却步骤是在 N 次重复后完成的,因此如果重复次数=100,它将首先进行 100 次提议,然后再以因子alpha
当前状态的简单变化通常效果最好。由于最好的做法是将初始温度设置得足够高以使大多数提案 (>90%) 被接受,因此步长小并不重要。但如果您担心它太小,请尝试 2 或 3 个变体。 Frigidum接受proposal functions列表,组合可以相互强制。
我没有使用 MINLP 的经验。但即便如此,那么多次实验也能让我们大吃一惊。所以如果 time/cost 是小到给 table 带来另一个竞争对手,是的!