添加workers参数后scipy.differential_evolution报错的解决咨询
添加workers参数后scipy.differential_evolution报错的解决咨询
我的问题
我在使用scipy的differential_evolution为曲线拟合生成初始参数时,只要给DE加上workers参数(比如设为2)就会触发RuntimeError,不加的话代码完全正常运行。我已经把最小复现代码和报错信息整理出来了,想请教怎么修改才能解决这个问题,希望尽量沿用scipy的工具链。
最小复现代码
#### Python 3.9 #### #### Windows 10 #### import numpy as np import matplotlib.pyplot as plt from scipy.optimize import curve_fit import warnings from scipy.optimize import differential_evolution as DE def LCR(f, L, C, R): TPF = 2*np.pi*f return np.sqrt(R**2 + (TPF*L - 1/(TPF*C))**2) def sumOfSquaredError(parameterTuple): warnings.filterwarnings("ignore") return np.sum((ydata - LCR(xdata, *parameterTuple))**2) def generateParameterBounds(): parameterBounds = [] parameterBounds.append([1,5]) # parameter bounds for L parameterBounds.append([1,5]) # parameter bounds for C parameterBounds.append([1,5]) # parameter bounds for R # 正常运行的版本 # result = DE(sumOfSquaredError, parameterBounds, popsize=30, init='sobol', polish=False, seed=3) # 报错的版本(启用workers) result = DE(sumOfSquaredError, parameterBounds, popsize=30, init='sobol', polish=False, workers=2, seed=3) return result.x xdata = np.linspace(1e-3,1,1000) ydata = LCR(xdata,1.5,2,2.5) + np.random.randn(len(xdata)) plt.plot(xdata, ydata, 'b-', label='Measured') #### REGULAR CURVE FIT #### popt, pcov = curve_fit(LCR, xdata, ydata, bounds=([1, 1, 1], [5, 5, 5])) print(*popt) fit = LCR(xdata, *popt) plt.plot(xdata, fit, 'g-') #### DIFFERENTIAL EVOLUTION CURVE FIT #### ParameterRanges = generateParameterBounds() geneticParameters, pcov = curve_fit(LCR, xdata, ydata, ParameterRanges, maxfev=1000000) print(*geneticParameters) g_fit = LCR(xdata, *geneticParameters) plt.plot(xdata, g_fit, 'r-') plt.show()
报错信息
RuntimeError: An attempt has been made to start a new process before the current process has finished its bootstrapping phase. This probably means that you are not using fork to start your child processes and you have forgotten to use the proper idiom in the main module: if __name__ == '__main__': freeze_support() ... The "freeze_support()" line can be omitted if the program is not going to be frozen to produce an executable.
解决方法
这个错误是Windows系统下Python多进程的典型问题:Windows用spawn而非fork创建子进程,子进程会完整导入你的脚本文件。如果没有主入口保护,顶层的可执行代码(比如生成数据、调用拟合的逻辑)会被子进程重复执行,导致递归启动进程,触发这个RuntimeError。
具体修改步骤
- 把所有可执行主逻辑放进
if __name__ == '__main__':块:只有当脚本被直接运行时才会执行这部分代码,子进程导入脚本时会自动跳过,彻底避免递归创建进程的问题。 - 修正
curve_fit的参数错误:你原来把初始参数直接传给curve_fit的位置参数,这会被误判为bounds参数,应该显式用p0=ParameterRanges指定初始拟合值。
修改后的完整代码
#### Python 3.9 #### #### Windows 10 #### import numpy as np import matplotlib.pyplot as plt from scipy.optimize import curve_fit import warnings from scipy.optimize import differential_evolution as DE def LCR(f, L, C, R): TPF = 2*np.pi*f return np.sqrt(R**2 + (TPF*L - 1/(TPF*C))**2) def sumOfSquaredError(parameterTuple): warnings.filterwarnings("ignore") return np.sum((ydata - LCR(xdata, *parameterTuple))**2) def generateParameterBounds(): parameterBounds = [] parameterBounds.append([1,5]) # parameter bounds for L parameterBounds.append([1,5]) # parameter bounds for C parameterBounds.append([1,5]) # parameter bounds for R # 现在可以安全启用workers参数了 result = DE(sumOfSquaredError, parameterBounds, popsize=30, init='sobol', polish=False, workers=2, seed=3) return result.x # 所有实际运行的业务逻辑都放在这个主入口块里 if __name__ == '__main__': # 生成实验数据 xdata = np.linspace(1e-3,1,1000) ydata = LCR(xdata,1.5,2,2.5) + np.random.randn(len(xdata)) plt.plot(xdata, ydata, 'b-', label='Measured') # 常规曲线拟合 popt, pcov = curve_fit(LCR, xdata, ydata, bounds=([1, 1, 1], [5, 5, 5])) print("常规拟合参数:", *popt) fit = LCR(xdata, *popt) plt.plot(xdata, fit, 'g-', label='常规拟合') # 差分进化初始化的曲线拟合 ParameterRanges = generateParameterBounds() # 显式指定p0参数传递初始拟合值 geneticParameters, pcov = curve_fit(LCR, xdata, ydata, p0=ParameterRanges, maxfev=1000000) print("差分进化初始化拟合参数:", *geneticParameters) g_fit = LCR(xdata, *geneticParameters) plt.plot(xdata, g_fit, 'r-', label='差分进化初始化拟合') plt.legend() plt.show()
进阶优化建议
你的sumOfSquaredError函数目前依赖全局变量xdata和ydata,虽然现在能正常运行,但为了让代码更健壮、更符合模块化规范,可以用functools.partial把数据绑定到误差函数上,避免依赖全局变量:
def sumOfSquaredError(parameterTuple, xdata, ydata): warnings.filterwarnings("ignore") return np.sum((ydata - LCR(xdata, *parameterTuple))**2) def generateParameterBounds(xdata, ydata): from functools import partial parameterBounds = [] parameterBounds.append([1,5]) # parameter bounds for L parameterBounds.append([1,5]) # parameter bounds for C parameterBounds.append([1,5]) # parameter bounds for R # 绑定xdata和ydata到误差函数 error_func = partial(sumOfSquaredError, xdata=xdata, ydata=ydata) result = DE(error_func, parameterBounds, popsize=30, init='sobol', polish=False, workers=2, seed=3) return result.x
然后在主入口块里调用时改为:
ParameterRanges = generateParameterBounds(xdata, ydata)
备注:内容来源于stack exchange,提问作者onix
相关产品推荐
相关产品推荐

