使用scipy.optimize.fmin_bfgs最小化多元函数不收敛报错如何解决
问题背景
尝试最小化如下多元函数:
其中α_i为可正可负的常数,n为5~10之间的随机整数,要求使用scipy.optimize.fmin_bfgs实现,满足以下约束:
- 随机起点所有值均保留4位小数,格式为m.dddd
- 迭代至连续迭代值的L∞范数绝对差小于2%时停止
- 系数α_i保留4位小数,至少40%为负、至少25%为正
错误根因
- 目标函数无下界,不存在极小值:你定义的目标函数为
sum(α_i * x_i²),由于存在负的α_i,对应维度的x_i绝对值越大,函数值越小,会趋向负无穷,无约束的BFGS算法不可能找到收敛点,这是精度损失报错的核心原因。 - 停止条件逻辑错误:要求的停止条件是连续两次迭代值的L∞范数差小于2%,但你现有回调仅判断函数值是否小于0.02,既没有保存上一轮迭代值,也没有计算范数差,完全不符合要求。
- 维度不匹配:生成的初始值
x0是(n,1)的二维数组,fmin_bfgs要求输入一维数组,会导致迭代计算异常。 - α生成规则不满足硬要求:现有加权采样逻辑无法100%保证生成的α序列满足至少40%负、25%正的要求,存在概率性不符合规则的情况。
- 返回值解包错误:
fmin_bfgs共返回7个值,你仅用3个变量接收会触发解包报错。
修正代码
要实现收敛需要先给x增加取值范围约束,这里我们补充x_i∈[-10,10]的约束,改用支持约束的L-BFGS-B算法实现,同时修正所有逻辑问题:
import numpy as np from scipy.optimize import fmin_l_bfgs_b # 1. 随机生成n:5~10之间的整数 n = np.random.randint(5, 11) # 2. 生成满足要求的α:至少40%负,至少25%正,保留4位小数 neg_cnt = int(np.ceil(n * 0.4)) # 负α最少数量 pos_cnt = int(np.ceil(n * 0.25)) # 正α最少数量 remain_cnt = n - neg_cnt - pos_cnt # 剩余随机符号的α数量 alpha = [] # 先加负α alpha.extend(np.round(np.random.uniform(-1, -0.01, size=neg_cnt), 4).tolist()) # 再加正α alpha.extend(np.round(np.random.uniform(0.01, 1, size=pos_cnt), 4).tolist()) # 剩余的随机正负 if remain_cnt > 0: remain_alpha = np.round(np.random.uniform(-1, 1, size=remain_cnt), 4) # 避免生成0 remain_alpha[remain_alpha == 0] = np.random.choice([-0.01, 0.01]) alpha.extend(remain_alpha.tolist()) # 打乱顺序 np.random.shuffle(alpha) alpha = np.array(alpha) print("n值:", n) print("α序列:", alpha) # 3. 生成初始点:保留4位小数,一维数组 x0 = np.round(np.random.uniform(-5, 5, size=n), 4) print("初始点x0:", x0) # 4. 定义目标函数 def func(x): return np.sum(alpha * (x ** 2)) # 5. 定义满足要求的回调函数:判断连续迭代的L∞范数差小于2%停止 class StopCallback: def __init__(self, thresh=0.02): self.prev_x = None self.thresh = thresh self.opt_x = None def __call__(self, xk): if self.prev_x is not None: l_inf_diff = np.max(np.abs(xk - self.prev_x)) if l_inf_diff < self.thresh: self.opt_x = xk # 抛出异常停止迭代 raise StopIteration self.prev_x = xk.copy() cb = StopCallback(thresh=0.02) # 6. 带约束优化:x的范围限制在[-10,10] bounds = [(-10, 10) for _ in range(n)] try: x_opt, f_opt, info = fmin_l_bfgs_b(func, x0, bounds=bounds, callback=cb) except StopIteration: x_opt = cb.opt_x f_opt = func(x_opt) print("最优解x:", np.round(x_opt, 4)) print("最优函数值:", f_opt)
补充说明
如果你必须使用无约束的fmin_bfgs,需要确认目标函数是否书写错误,比如是否应为sum(α_i² * x_i²)、sum(|α_i| * x_i²)这类所有系数为正的形式,只有当目标函数为凸函数且存在下界时,无约束BFGS才能正常收敛。
内容的提问来源于stack exchange,提问作者meroka5326
相关产品推荐
相关产品推荐

