如何在不重复计算目标函数的情况下跟踪differential_evolution的优化进度
如何在不重复计算目标函数的情况下跟踪differential_evolution的优化进度
你遇到的这个问题真的很戳痛点——scipy的differential_evolution回调设计确实有点“抠门”,只给当前最优解xk,却不附带已经算过的目标函数值,逼得你不得不重复调用opt(xk),要是目标函数计算成本高,那速度下降可不是一星半点。
其实我们可以换个思路:让目标函数自己“记住”最近计算出的最优结果,这样回调函数直接读取这个“记忆”就行,完全不用重复计算。下面是具体的实现方案,直接替换你的代码即可:
import numpy as np from scipy.optimize import differential_evolution, LinearConstraint # 先假设你的目标函数opt是这样的(示例) def opt(xk): # 这里是你的目标函数逻辑,比如简单的求和 return sum(xk) def de_optimise(): # 创建一个共享缓存,用来存储当前最优的目标值和对应的解 progress_cache = {'best_obj': float('inf'), 'best_xk': None} # 包装原始目标函数,添加缓存逻辑 def cached_opt(xk): obj_val = opt(xk) # 只有当当前结果比缓存的最优值更好时,才更新缓存 if obj_val < progress_cache['best_obj']: progress_cache['best_obj'] = obj_val progress_cache['best_xk'] = xk.copy() return obj_val def build_show_de(progress_f): def fn(xk, convergence): # 直接从缓存拿已经计算好的目标值,不用再跑opt(xk) current_best_obj = progress_cache['best_obj'] if current_best_obj < progress_f[-1]: print("DE", [round(x, 2) for x in xk], current_best_obj) progress_f.append(current_best_obj) # 不需要提前终止的话返回False/None即可 return False return fn bounds = [(0,1)]*3 # 定义线性约束 A = [[-1, 1, 0], [0, -1, 1]] lb = [0.3, 0.4] ub = [np.inf, np.inf] constraints = LinearConstraint(A, lb, ub) # 初始值设为无穷大,确保第一个有效最优值能被记录 progress_f = [float('inf')] c = build_show_de(progress_f) print("Optimizing using differential evolution") res = differential_evolution( cached_opt, # 使用带缓存的目标函数替代原始opt bounds=bounds, constraints=constraints, callback=c, disp=True ) # 打印时去掉初始的inf值 print(f"external way of keeping track of MINF: {progress_f[1:]}") de_optimise()
方案说明
- 缓存包装函数:我们给原始目标函数
opt套了一层cached_opt,每次计算目标值后,会检查是否是当前最优结果,如果是就更新progress_cache。这样算法每次算出新的最优解时,缓存里已经存好了对应的目标值。 - 回调直接读缓存:回调函数
fn现在不需要再调用opt(xk),直接从progress_cache里取结果就行,彻底避免了重复计算。 - 初始值处理:把
progress_f的初始值设为无穷大,确保第一个有效的最优值肯定能被加入跟踪列表。
如果你的问题是最大化目标函数,只需要把缓存更新的条件改成if obj_val > progress_cache['best_obj'],同时progress_f的初始值设为负无穷即可。
这个方法几乎没有额外性能开销,只是在目标函数计算后多了一次简单的比较和赋值,完全不会拖慢优化进程。
备注:内容来源于stack exchange,提问作者Simd
相关产品推荐
相关产品推荐

