Python SciPy拟合高斯函数时出现sqrt无效值警告是否存在问题?
问题分析与解决办法
你的做法存在明显问题,完全达不到强制参数为正的效果,反而会引发警告:
你的方法为什么不行?
你通过np.sqrt(std)/np.sqrt(std)这类操作试图强制参数为正,但这本质上是无效的:
- 当参数为正时,这部分等于1,对函数结果没有任何影响;
- 当参数为负时,
np.sqrt(负数)会返回NaN(numpy实数运算下),直接触发invalid value encountered in sqrt警告,还会让拟合过程出现异常值,影响收敛。
而且这种操作根本没限制curve_fit迭代时尝试负参数,只是在参数为负时让函数输出无效值,完全起不到约束作用。
正确的参数约束方式
1. 用对数参数化强制正值
把需要为正的参数(振幅amp、标准差std,如果均值mean也需要正的话)转换为对数形式,利用指数函数恒正的特性来保证参数为正。重新定义拟合函数:
import numpy as np from scipy.optimize import curve_fit def gaussian_only(x, amp_log, mean, std_log): # 振幅和标准差通过指数转换为正值,均值如果允许为负可以直接用 amp = np.exp(amp_log) std = np.exp(std_log) return amp * np.exp(-(x - mean)**2 / (2 * std**2))
拟合时,初始猜测也要对应转换为对数形式:
def gaussian_only_fit(arr, curve_residual, initial_guess): amp0, mean0, std0 = initial_guess # 初始值必须为正,不然np.log会报错,所以初始猜测要给合理的正值 log_initial = [np.log(amp0), mean0, np.log(std0)] fit_params, fit_cov = curve_fit(gaussian_only, arr[0], curve_residual, log_initial) # 如果需要还原原始参数,再做指数转换 amp_fit = np.exp(fit_params[0]) mean_fit = fit_params[1] std_fit = np.exp(fit_params[2]) return (amp_fit, mean_fit, std_fit), fit_cov
这种方式从根源上保证了amp和std永远为正,不会出现负数参数的问题,也不会有sqrt警告。
2. 优化bounds参数的使用
你之前用bounds效果差,大概率是边界设置不合理或者初始猜测离最优解太远。比如如果mean允许为负,amp和std必须为正,可以这样设置边界:
# 下限:amp>=0,mean无限制,std>=0;上限都设为无穷大 bounds = ([0, -np.inf, 0], [np.inf, np.inf, np.inf]) # 使用原始无sqrt操作的高斯函数 def gaussian_only_original(x, amp, mean, std): return amp * np.exp(-(x - mean)**2 / (2 * std**2)) fit = curve_fit(gaussian_only_original, arr[0], curve_residual, initial_guess, bounds=bounds)
同时要调整初始猜测,尽量接近真实的最优值,避免拟合收敛到局部最优。
内容的提问来源于stack exchange,提问作者BatMandor
相关产品推荐
相关产品推荐

