Scipy Optimize(minimize)未输出正确结果,求问题排查
SciPy Optimize最小化结果与预期不符的原因分析
问题描述
尝试使用SciPy的SLSQP优化器执行最小化任务,目标是最小化np.std(np.dot(df, x.T)),预期结果接近x_expected = [0., 0., 0., 1],但实际输出与预期不符。代码如下:
import numpy as np import pandas as pd from scipy import optimize as sp x0 = [0.2, 0.2, 0.2, 0.4] x_expected = np.array([0., 0., 0., 1]) bounds = ((0, 1), (0, 1), (0, 1), (0, 1)) df = pd.DataFrame( {'t1': [1, 2, 3, 2, 1], 't2': [3, 4, 5, 6, 2], 't3': [2, 3, 2, 3, 5], 'c': [1, 1.1, 1.2, 1.3, 1.4]}).to_numpy() factors = pd.DataFrame( {"tk": ["t1", "t2", "t3", "c"], "class": ["x", "y", "x", "z"]}) min_max_class = pd.DataFrame( {"class": ["x", "y", "z"], "min_allocation": [0., 0., 0.], "max_allocation": [0.0001, 0.5, 1.0000]}) allocation = factors.join(min_max_class.set_index('class'), on='class') min_arr = allocation['min_allocation'].to_numpy() max_arr = allocation['max_allocation'].to_numpy() ineq1 = {'type': 'ineq', "fun": lambda x: np.greater_equal(x, min_arr).sum() + np.less_equal(x, max_arr).sum() - 2 * df.shape[1]} eq1 = {'type': 'eq', "fun": lambda x: 1 - np.sum(x)} result = sp.minimize(fun=lambda x: np.std(np.dot(df, x.T), ddof=1), x0=x0, bounds=bounds, method='SLSQP', options={'disp': True}, constraints=[eq1, ineq1]) print(f"final result : {np.round(result.x,2)}, objective value: {result.fun}") print(f" Manual : {x_expected}, objective value: {np.std(np.dot(df, x_expected.T))}")
核心原因分析
1. 初始点不满足约束条件
你的初始点x0 = [0.2, 0.2, 0.2, 0.4]违反了自定义的不等式约束:
- t1和t3对应的
max_allocation是0.0001,但x0中这两个变量的值是0.2,远大于上限 - 计算
ineq1的返回值:sum(x≥min_arr)=4(所有变量都≥0),sum(x≤max_arr)=2(仅t2和c满足上限),总和为6,减去2*4=8后得到-2 < 0,不满足ineq约束要求的fun≥0
SLSQP优化器要求初始点必须处于可行域内(或足够接近),否则优化过程可能无法收敛到正确的可行解,甚至直接在不可行区域内寻找局部最优。
2. 不等式约束定义错误
你试图通过单个标量函数表达所有变量的区间约束(min_arr[i] ≤ x[i] ≤ max_arr[i]),但这种写法逻辑错误:
- 该函数仅当所有变量同时满足上下限时返回0,只要有一个变量违反约束,返回值就会小于0,无法正确表达每个变量独立的区间限制
- 正确的做法是使用向量式约束,为每个变量的上下限分别定义不等式:
这样优化器能明确识别每个变量的独立约束,而非合并成一个模糊的全局条件。ineq_constraints = [ {'type': 'ineq', 'fun': lambda x: x - min_arr}, # 每个x[i] ≥ min_arr[i] {'type': 'ineq', 'fun': lambda x: max_arr - x} # 每个x[i] ≤ max_arr[i] ]
3. 约束与Bounds的重复定义
你同时设置了bounds=((0,1),...)和基于min_arr/max_arr的约束,其中min_arr全为0(与bounds下限一致),但max_arr中t1/t3的上限0.0001远小于bounds的1.0。这种重复定义会增加优化器的计算负担,且错误的约束写法会导致优化器无法正确识别更严格的上限限制。
修复建议
- 调整初始点到可行域内,比如
x0 = [0.0001, 0.2, 0.0001, 0.7998](满足所有上下限和求和为1的约束) - 替换错误的
ineq1为向量式约束 - 可以移除重复的
bounds设置,因为min_arr/max_arr已经覆盖了更严格的区间限制
修复后的关键代码片段:
# 正确的约束定义 ineq_constraints = [ {'type': 'ineq', 'fun': lambda x: x - min_arr}, {'type': 'ineq', 'fun': lambda x: max_arr - x} ] # 可行的初始点 x0 = [0.0001, 0.2, 0.0001, 0.7998] result = sp.minimize( fun=lambda x: np.std(np.dot(df, x.T), ddof=1), x0=x0, method='SLSQP', options={'disp': True}, constraints=[eq1] + ineq_constraints )
内容的提问来源于stack exchange,提问作者user19569348
相关产品推荐
相关产品推荐

