You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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。这种重复定义会增加优化器的计算负担,且错误的约束写法会导致优化器无法正确识别更严格的上限限制。

修复建议

  1. 调整初始点到可行域内,比如x0 = [0.0001, 0.2, 0.0001, 0.7998](满足所有上下限和求和为1的约束)
  2. 替换错误的ineq1为向量式约束
  3. 可以移除重复的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 00:45:25