Scipy SLSQP优化算法返回错误解的问题咨询
问题描述
刚接触Scipy优化模块,用SLSQP方法求解简单约束优化问题时,算法要么找不到约束区域内的全局最优,要么返回看似不可行的点。尝试过禁用部分约束、调整边界,都没解决问题。只有当初始点设为[5,4]时返回该点(附带Positive directional derivative for linesearch (Exit mode 8)提示),初始点[0,0]返回可行但非最优解,其他初始点返回[8.16230102 10.]。
优化问题代码
from scipy.optimize import minimize import numpy as np from scipy.optimize import Bounds def simpleFunction(x): x1 = float(x[0]) x2 = float(x[1]) return x2*(x1-x2) def g1(x): x1 = float(x[0]) x2 = float(x[1]) return (x1-5)**2 - x2 def g2(x): x1 = float(x[0]) x2 = float(x[1]) return (x2+x1) - 10 def g3(x): x1 = float(x[0]) x2 = float(x[1]) return x2-0.8*x1 g1_ineq = {'type': 'ineq', 'fun': g1} g2_ineq = {'type': 'ineq', 'fun': g2} g3_ineq = {'type': 'ineq', 'fun': g3} x0 = np.array([5 ,8]) bnds = Bounds([5, 3], [10, 10]) cons = ([g1_ineq, g2_ineq, g3_ineq]) res = minimize(simpleFunction, x0, method='SLSQP', constraints=cons, options={'disp': True}, bounds=bnds) print(res.x) print(simpleFunction(res.x)) print(simpleFunction([5, 4])) print(res.x) print(simpleFunction(res.x)) print(simpleFunction([5, 4])) # 你认为的真实最小值点 # 不同初始点的结果: # [0, 0] -> [5. 3.] -> 6.0 # [5, 4] -> [5, 4] -> 4.0 # [5, 5] -> [ 8.16230102 10.] -> -18.376 # [6, 6] -> [ 8.16230102 10.] -> -18.376 # [10, 10] -> [ 8.16230102 10.] -> -18.376 # [5 ,8] -> [ 8.16230102 10.] -> -18.376
图形化验证代码(原版本存在约束不一致问题)
from scipy.optimize import minimize import numpy as np import matplotlib.pyplot as plt from scipy.optimize import Bounds from typing import List, Set, Dict, Tuple def simpleFunction(x1, x2): return x2*(x1-x2) def g1(x1, x2): return (x1-5)**2 - x2 def g2(x1, x2): return -x1 +5 # 此处与优化代码的g2约束完全不一致 def g3(x1, x2): return x2-0.8*x1 def g4(x1, x2): return -x2 +3 def g5(x1, x2): return (x2+x1) - 10 Z1 = 0 functions = [ (lambda x1, x2: g1(x1,x2), Z1,'g1'), (lambda x1, x2: g2(x1,x2), Z1,'g2'), (lambda x1, x2: g3(x1,x2), Z1,'g3'), (lambda x1, x2: g4(x1,x2), Z1,'g1'), (lambda x1, x2: g3(x1,x2), Z1,'g1'), ] X, Y = np.meshgrid(np.linspace(0, 10, 1000), np.linspace(0, 10, 1000)) for f, z,name in functions: plt.contour(X, Y, f(X, Y), levels = [0], colors='black') plt.contourf(X, Y, f(X, Y), [0, 1000], colors='blue', alpha=0.1) plt.contour(X, Y, simpleFunction(X, Y), levels=100, linestyles='dashed', colors='black') plt.title('feasible region') plt.xlim(0, 10) plt.ylim(0, 10) plt.xlabel('x1') plt.ylabel('x2') plt.show()
可行区域示意图

问题核心原因
约束方向完全搞反:
Scipy的ineq约束要求fun(x) >= 0时才属于可行区域。你的优化代码中g2定义为x1+x2-10,意味着只有x1+x2≥10的点才是可行点,但你误以为[5,4]是可行点——代入g2计算得5+4-10=-1<0,明显不满足约束,属于不可行点。
同时你的图形化代码中g2定义为-x1+5,对应x1≤5的约束,和优化代码的约束完全冲突,导致你对可行区域的理解完全错误。对SLSQP算法的局限性认知不足:
SLSQP是局部优化算法,只能找到初始点附近的局部最优解。如果目标函数在可行区域内有多个局部最优,不同初始点会收敛到不同结果。最优解判断错误:
你认为[5,4]是全局最小值,但该点本身不可行;而算法返回的[8.16230102,10]是满足所有约束的:- g1=(8.1623-5)²-10≈10-10=0≥0
- g2=8.1623+10-10=8.1623≥0
- g3=10-0.8*8.1623≈3.47≥0
- 边界x1∈[5,10]、x2∈[3,10]也满足
该点的函数值-18.376远小于[5,4]的4.0,是当前约束下的更优解。
解决建议
修正约束定义:
如果你实际想要的约束是x1+x2 ≤10,需要把g2改为10 - x1 -x2,这样g2≥0就对应x1+x2≤10,符合Scipy的约束规则。同步图形化代码与优化代码的约束:
修改图形化代码中的g2,使其和优化代码一致,这样才能正确可视化可行区域,辅助判断最优解。处理局部最优问题:
若需要找到全局最优解,可尝试使用Scipy的全局优化算法,比如dual_annealing、shgo等,或者多设置几个不同的初始点,对比结果后选择最优的可行解。理解Exit mode 8的含义:
当初始点设为[5,4]时出现的Exit mode 8提示,是因为该点不可行,算法在搜索方向上无法找到可行的步长,所以直接返回初始点。
内容的提问来源于stack exchange,提问作者hmcdl

