在Gekko中使用sos1与Lookup表求解优化最大值时遇局部最优问题
问题描述
优化器需要从离散选项(0-5)中选择合适值以最大化目标函数,选项与对应值通过Lookup表关联。以下是简化示例:
数据
x_data = np.array([0, 1, 2, 3, 4, 5]) y_data = np.array([1000,2000,9000,4500,5000,900]) z_data = np.array([15,13,12,17,11,10])
我尝试用cspline函数建立离散选择变量x(定义为sos1类型)与因变量y、z的关系,代码如下:
from gekko import GEKKO import numpy as np # 定义数据点 x_data = np.array([0, 1, 2, 3, 4, 5]) y_data = np.array([1000,2000,9000,4500,5000,900]) z_data = np.array([15,13,12,17,11,10]) # 创建模型与变量 m = GEKKO() x = m.sos1(x_data) y,z = m.Array(m.Var,2) m.cspline(x, y, x_data, y_data) m.cspline(x, z, x_data, z_data) # 定义目标函数并求解 m.Maximize(y*z) m.options.SOLVER = 1 m.solve(disp=True) # 打印结果 print(f'x: {x.value[0]}, y: {y.value[0]}, z: {z.value[0]}') print('Objective:', y.value[0]*z.value[0]) print(y_data*z_data)
但求解得到了错误结果(疑似局部最优):
x: 0.0, y: 1000.0, z: 15.0 Objective: 15000.0
推测是三次样条导致的局部最优问题,请问有什么方法可以找到该合成函数的全局最大值?
解决方案
针对这个离散选择优化问题,三次样条插值反而会引入不必要的连续插值区域,增加局部最优的概率。以下是几种更直接有效的解决方法:
方法1:直接映射离散值(推荐)
既然x是离散选项,直接用二进制变量建立x与y、z的映射关系,完全避免插值带来的问题:
from gekko import GEKKO import numpy as np x_data = np.array([0, 1, 2, 3, 4, 5]) y_data = np.array([1000,2000,9000,4500,5000,900]) z_data = np.array([15,13,12,17,11,10]) m = GEKKO() # 定义6个二进制选择变量,每个变量对应一个x选项 b = m.Array(m.Var, 6, lb=0, ub=1, integer=True) m.Equation(sum(b) == 1) # 约束:仅选择一个选项 # 通过二进制变量映射得到选中的x、y、z值 x = m.sum([b[i] * x_data[i] for i in range(6)]) y = m.sum([b[i] * y_data[i] for i in range(6)]) z = m.sum([b[i] * z_data[i] for i in range(6)]) # 最大化目标函数 m.Maximize(y * z) m.options.SOLVER = 1 # 使用APOPT整数规划求解器 m.solve(disp=True) # 输出最优结果 selected_idx = np.argmax([bi.value[0] for bi in b]) print(f'x: {x_data[selected_idx]}, y: {y_data[selected_idx]}, z: {z_data[selected_idx]}') print('Objective:', y_data[selected_idx] * z_data[selected_idx])
这种方法直接针对离散选项建模,求解结果准确,不会出现局部最优问题。
方法2:启用全局优化模式
如果坚持使用样条插值,可以通过配置APOPT求解器的全局优化选项来提升找到全局最优的概率:
# 在原代码基础上修改求解器设置 m.options.SOLVER = 1 # 配置APOPT全局优化参数 m.solver_options = ['minlp_gap_tol 1.0e-5', 'minlp_maximum_iterations 1000', 'minlp_max_iter_with_int_sol 100', 'minlp_branch_method 1', 'minlp_integer_tol 1.0e-5']
不过这种方法仍受插值函数形状影响,可靠性不如直接映射。
方法3:枚举所有离散选项
由于选项仅6个,最简单高效的方式是直接遍历所有可能的x值,计算对应目标值后取最大值:
# 计算所有选项的目标值 objective_values = y_data * z_data # 找到最大值对应的索引 max_idx = np.argmax(objective_values) # 输出结果 print(f'最优x: {x_data[max_idx]}, 目标值: {objective_values[max_idx]}')
这种方法适用于选项数量少的场景,结果绝对准确,无需调用优化求解器。
内容的提问来源于Stack Exchange,提问作者TexasEngineer
相关产品推荐
相关产品推荐

