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

Scipy差分进化:如何通过回调获取每代无效种群能量占比?

在Scipy差分进化中获取每步无效种群能量占比

默认情况下,Scipy differential_evolution 提供的回调函数仅能接收当前最优解(xk)和收敛度(convergence)两个参数,无法直接获取种群及能量信息。要实现需求,无需修改依赖包源码,可通过以下两种可行方案:

方案一:手动迭代私有求解器类(推荐)

Scipy的差分进化内部通过_DifferentialEvolutionSolver类实现迭代逻辑,我们可以直接实例化该类并手动循环迭代,从而在每一步获取种群和对应能量值,再传入自定义回调计算占比。

示例代码:

import numpy as np
from scipy.optimize._differentialevolution import _DifferentialEvolutionSolver

# 自定义目标函数(根据你的实际问题替换)
def target_func(x):
    return x[0]**2 + x[1]**2 + x[2]**2

# 定义无效个体判断规则(根据你的约束条件替换)
def is_invalid_individual(x):
    # 示例:x[0] > 0 视为无效个体
    return x[0] > 0

# 自定义回调类,接收种群和能量参数
class IterationCallback:
    def __init__(self):
        self.iter_count = 0

    def __call__(self, xk, convergence, population, pop_energies):
        # 计算无效种群的能量占比
        invalid_mask = np.array([is_invalid_individual(ind) for ind in population])
        invalid_energy_sum = pop_energies[invalid_mask].sum()
        total_energy_sum = pop_energies.sum()
        
        invalid_ratio = invalid_energy_sum / total_energy_sum if total_energy_sum != 0 else 0.0
        print(f"迭代步数: {self.iter_count} | 无效种群能量占比: {invalid_ratio:.4f}")
        
        self.iter_count += 1
        return False  # 返回True会终止优化,按需调整

# 初始化求解器
bounds = [(-10, 10)] * 3  # 优化变量的边界
solver = _DifferentialEvolutionSolver(
    func=target_func,
    bounds=bounds,
    seed=42,  # 设置种子保证结果可复现
    maxiter=100  # 最大迭代步数
)

# 实例化回调
callback = IterationCallback()

# 手动执行迭代
for _ in range(solver.maxiter):
    try:
        next(solver)  # 执行一次迭代
    except StopIteration:
        break  # 提前收敛则终止
    
    # 获取当前迭代的种群和能量值
    current_pop = solver.population
    current_energies = solver.pop_energies
    
    # 调用回调并传入额外参数
    stop_signal = callback(solver.xk, solver.convergence, current_pop, current_energies)
    if stop_signal:
        break

# 获取最终优化结果
final_best_x = solver.x
final_best_energy = solver.fun

注意事项

  • _DifferentialEvolutionSolver是Scipy的私有类,虽然在1.12.0版本中稳定可用,但未来版本可能存在API变更风险。
  • 需根据你的实际问题替换target_func(目标函数)和is_invalid_individual(无效个体判断规则)。

方案二:猴子补丁扩展回调参数

若想保留differential_evolution的原生调用方式,可通过猴子补丁临时修改求解器的迭代逻辑,让回调函数能接收种群和能量参数。

示例代码:

import numpy as np
from scipy.optimize import differential_evolution
from scipy.optimize._differentialevolution import _DifferentialEvolutionSolver

# 保存原迭代方法
original_next = _DifferentialEvolutionSolver.__next__

# 重定义迭代方法,扩展回调参数
def patched_next(self):
    result = original_next(self)
    # 若存在回调,传入种群和能量信息
    if self.callback is not None:
        stop = self.callback(self.xk, self.convergence, self.population, self.pop_energies)
        if stop:
            raise StopIteration
    return result

# 应用猴子补丁
_DifferentialEvolutionSolver.__next__ = patched_next

# 自定义回调类
class PatchedCallback:
    def __init__(self):
        self.iter_count = 0

    def __call__(self, xk, convergence, population, pop_energies):
        # 计算无效能量占比(逻辑同方案一)
        invalid_mask = np.array([ind[0] > 0 for ind in population])
        invalid_ratio = pop_energies[invalid_mask].sum() / pop_energies.sum() if pop_energies.sum() !=0 else 0.0
        print(f"迭代步数: {self.iter_count} | 无效种群能量占比: {invalid_ratio:.4f}")
        self.iter_count += 1
        return False

# 原生调用differential_evolution
bounds = [(-10, 10)] * 3
result = differential_evolution(
    func=lambda x: x[0]**2 + x[1]**2 + x[2]**2,
    bounds=bounds,
    callback=PatchedCallback(),
    maxiter=100,
    seed=42
)

# 恢复原方法,避免影响其他代码
_DifferentialEvolutionSolver.__next__ = original_next

注意事项

  • 猴子补丁会全局修改Scipy的求解器行为,使用后建议恢复原方法。
  • 仅在单进程场景下可靠,若使用多进程(workers>1),该方法无法正常获取种群信息。

内容的提问来源于stack exchange,提问作者Bb92

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 00:03:26