我一直在尝试使用 scipy.curve_fit 来拟合曲线,但它似乎坚持一些局部最小值(非常接近初始参数猜测)并且不尝试任何其他组合。通常在 100-200 次迭代后停止。 正如你所看到的,这种配合通常很糟糕。 预期的形状是:短恒线、斜线、(可选恒线)、斜线向下、短恒线
def piecewise_linear(X, x1, x2, x22, x3, y1, y2, y3):
return np.piecewise(X, [(X < x1), (x1 <= X) & (X < x2), (x2 <= X) & (X <= x22), (x22 < X) & (X < x3), X >= x3],
[lambda X: y1, lambda X: y1 + (y2 - y1) / (x2 - x1) * (X - x1),
lambda X: y2, lambda X: y2 + (y3 - y2) / (x3 - x22) * (X - x22), lambda X: y3])
for i in range(num of vectors):
bounds = ([0, global_min_left_x, global_max_x, global_max_x, -np.inf, -np.inf,-np.inf],
[global_max_x, global_max_x, global_min_right_x, vector_length, np.inf,np.inf, np.inf])
additional_kwargs = {'method': 'dogbox', 'ftol': 1e-15, 'xtol': 1e-15, 'maxfev': 100000}
params, _ = curve_fit(piecewise_linear, X, Z, bounds=bounds,
p0=(global_min_left_x, global_max_x,global_max_x, global_min_right_x,
global_min_left_z, global_max_z, global_min_right_z),
**additional_kwargs)
fitted_curve = [piecewise_linear(x, *params) for x in X]
也许解决方案是设置正确的 kwargs(说实话,我不确定我是否理解正确)。我也尝试过不同的p0,但问题是一样的——几乎没有拟合,基本上只是连接了p0点。
也许查看我想要拟合的数据可能有用,所以有一个例子 https://pastebin.com/f0xF9fsd
您遇到了缩放问题,您发布的数据的缩放比例为 1e-8,这混淆了优化算法。
您必须首先重新调整数据,然后由于此优化可能陷入局部最小值,因此您应该尝试从随机初始点重新启动它。我在您发布的数据上尝试了以下代码(使用您的符号,第一列是
X
,第二列是 Z
,函数 piecewise_linear
保留为您定义的):
ntrials = 1000
nparams = 7 # number of parameters to optimize
scale = max(max(abs(X)), max(abs(Z)))
print(f'scale: {scale}')
X = X / scale
Z = Z / scale
min_err = np.inf
best_params = None
for i in range(ntrials):
try:
params, stats = curve_fit(piecewise_linear, X, Z,
p0=np.random.randn(nparams))
except:
# Ignores RuntimeError: Optimal parameters not found:
# Number of calls to function has reached maxfev
continue
fitted_curve = [piecewise_linear(x, *params) for x in X]
err = np.mean(np.square(Z - fitted_curve))
# print(f'i: {i}: {err}')
if err < min_err:
min_err = err
best_params = params
打印
scale
显示大约 6e-8,如果需要,您可以将参数与此相乘以缩小它们。 1000 次试验的最佳参数给出了重新调整后的数据的均方误差约为 4e-6,并且曲线良好: