复杂MINLP收敛异常及Gekko优化DNN目标函数问题咨询
Gekko优化含DNN目标函数的数值错误与更新异常问题
问题背景
runcfspop是模拟电网连锁故障的函数,变量x代表待新增的电网线路,返回相对参考电网的连锁风险改进值。该函数基于Matlab和训练好的深度神经网络(DNN)实现,逻辑复杂,我在将其作为目标函数做优化时遇到了问题。
当前问题
- 目标函数评估结果正确,但返回值非整数时会触发数值错误;
- 用
int(round())对目标函数值取整后,Gekko能求解出满足线路数量、成本约束的解,但目标函数值始终停留在首次评估值,不会随最终解更新。
已确认初始文件加载、目录配置及初始变量评估均正确,无法提供全部关联文件,提出两个疑问:
① 为何目标函数值非整数会触发错误?
② Gekko是否支持优化含DNN的任意函数?
代码示例
from gekko import GEKKO import numpy as np import scipy.io from gekko import GEKKO import numpy as np import matlab.engine import torch import utils from runCFSsurrpop_gekko import runcfspop import pandas as pd import os # load cost and branch characteristics device = torch.device("cuda" if torch.cuda.is_available() else "cpu") branch, cost, eng, model, ccdf_ref = utils.data_init_ccdf_ref(device) root_dir = "C:\Users\Asus\Desktop\semester_project\TEP_Metaheuristics" root_dir_mat = "C:\Users\Asus\Desktop\semester_project\TEP_Metaheuristics\CascadesTEPforpy" system = scipy.io.loadmat(root_dir_mat + "\13_Power_system\IEEE118\sys.mat") branch = system["sys"]["branch"][0][0] cost = np.multiply(branch[:, 21], branch[:, 22]) system = scipy.io.loadmat(root_dir_mat + "\13_Power_system\IEEE118\sys.mat") branch = system["sys"]["branch"][0][0] # Define model m = GEKKO() m.options.SOLVER = 1 # APOPT is an MINLP solver m.options.DIAGLEVEL = 0 m.options.IMODE = 3 # Steady state optimization m.solver_options = ['minlp_maximum_iterations 5000', \ # minlp iterations with integer solution 'minlp_max_iter_with_int_sol 10', \ # treat minlp as nlp 'minlp_as_nlp 0', \ # nlp sub-problem max iterations 'nlp_maximum_iterations 100', \ # 1 = depth first, 2 = breadth first 'minlp_branch_method 1', \ # maximum deviation from whole number 'minlp_integer_tol 0.00001', \ # covergence tolerance 'minlp_gap_tol 0.0001'] # Define variables x = m.Array(m.Var, 186, value=1, lb=0, ub=1, integer=True) # Define paramters lower_bound_lines = m.Param(value=1) upper_bound_lines = m.Param(value=8) lower_bound_cost = m.Param(value= 0) upper_bound_cost = m.Param(value= 0.2e+8) # Define equations m.Equation(m.sum(x) <= upper_bound_lines) m.Equation(m.sum(x) >= lower_bound_lines) m.Equation(m.sum(cost*x) >= lower_bound_cost) m.Equation(m.sum(cost*x) <= upper_bound_cost) # Define objective m.Obj((runcfspop(x, eng, branch, device, model, ccdf_ref))) # Solve m.solve(disp=True) print('') print('Results') print(x) # print contraint print('Objective: ' + str(m.options.objfcnval))
已尝试方案
对目标函数值取整解决了数值错误,但目标函数不更新的异常仍未解决。
解答
① 目标函数非整数触发错误的原因
你使用的APOPT是MINLP求解器,它要求目标函数和约束具备连续可微性与数值稳定性。如果runcfspop返回的非整数结果伴随数值噪声、不连续跳变,或者求解器在分支定界迭代中因浮点数精度问题误判结果不符合整数变量的优化逻辑,就会触发数值错误。另外,若优化问题本质是整数规划,但目标函数是非平滑的浮点数输出,求解器的分支定界机制无法处理这种非平滑性,也会报错。
② Gekko对含DNN函数的优化支持
Gekko不支持直接优化任意黑盒DNN函数,核心原因:
- Gekko依赖梯度信息(自动微分或数值微分)完成迭代优化,而外部DNN(如你通过Matlab引擎、PyTorch调用的模型)属于黑盒,Gekko无法自动获取其梯度,只能用数值微分。但数值微分在处理高维变量(如186维的
x)时效率极低,且易因DNN的非平滑性出现梯度估计错误。 - 你当前的写法
m.Obj(runcfspop(x, ...))存在致命问题:Gekko的Var对象是符号变量,直接传入外部函数时,函数只会在初始化时用x的初始值(全1)执行一次,后续迭代中不会用更新后的x值重新计算,这就是目标函数值始终停留在首次评估值的根本原因。
修复建议
1. 解决目标函数不更新问题
需要将runcfspop包装为Gekko可识别的回调函数,确保每次迭代都用当前x值重新计算目标函数。示例思路如下:
# 包装目标函数,接收numpy数组形式的x值 def calc_objective(x_val): return runcfspop(x_val, eng, branch, device, model, ccdf_ref) # 初始化目标值参数 initial_obj = calc_objective(np.ones(186)) obj_val = m.Param(value=initial_obj) m.Obj(obj_val) # 定义回调函数,迭代时更新目标值 def solver_callback(inputs): x_current = inputs[0] return [calc_objective(x_current)] # 启用CSV读写以支持回调 m.options.CSV_READ = 1 m.options.CSV_WRITE = 1 # 绑定回调:输入为x,输出为obj_val m.user_defined(solver_callback, inputs=[x], outputs=[obj_val])
注意:需确保runcfspop能正确处理numpy数组类型的输入,而非Gekko的Var对象。
2. 处理数值错误
不要用int(round())直接取整(会破坏目标函数连续性),可尝试:
- 检查
runcfspop的返回值,添加平滑处理以减少数值噪声; - 调整APOPT参数:比如增大
minlp_integer_tol(放宽整数变量的精度要求),或临时设置minlp_as_nlp=1先按NLP求解,再验证整数解; - 若目标函数的非整数输出是合理的,可切换为NLP求解器(如
m.options.SOLVER=3,IPOPT),避免MINLP求解器对数值类型的严格限制。
内容的提问来源于stack exchange,提问作者Federico Sartore
相关产品推荐
相关产品推荐

