scipy.optimize.curve_fit给出与初始值相同的值

wgeznvg7  于 2022-11-10  发布在  其他
关注(0)|答案(1)|浏览(156)

我正在尝试用曲线拟合我所拥有的数据。看起来很容易做,但是每当我拟合曲线时,优化似乎不起作用。curve_fit优化只是吐出我所给予的任何初始值。如果我不给参数任何初始值,它只是给出[1,1,1],这是curve_fit使用的默认初始猜测。有人能指出我正在犯的错误吗?
谢谢你,
下面是我正在使用的代码和数据:

import pandas as pd
import numpy as np
from scipy.optimize import curve_fit

x = [0.3, 0.6, 0.8, 1, 1.1, 1.5, 1.9, 2.4, 3.2, 12.6]
y = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]

df = pd.DataFrame({"X": x, "Y": y})

# Function

def sb_model(x, xmax, xc, b_val):
    return (1/(1+((np.log(xmax/x)/np.log(xmax/xc))**b_val)))*100

popt, pcov = curve_fit(f=sb_model, xdata=df["X"], ydata=df["Y"], p0 = [10.0, 0.9, 3.2])
print(popt)

输出量:

[10.   0.9  3.2]
bwleehnv

bwleehnv1#

我确实设法让你的模型拟合了你的数据。我相信你试图在不看结果图的情况下拟合它。这是做拟合时要做的第一件事。通常在非线性拟合中,你必须提供一组尽可能接近结果的初始点,这样算法就更容易达到它。
这样做,我注意到你的模型与数据相比太低了,我无法通过修改任何参数来使它“上升”。我最终添加了一个“scale”参数。接下来,我注意到你会因为日志中有负值而出错。我通过将参数的最小值设置为零来修复这个问题。我最终在lmfit中第一次这样做。然后用curve_fit测试,两个都能正常工作。

import pandas as pd
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt

x = [0.3, 0.6, 0.8, 1, 1.1, 1.5, 1.9, 2.4, 3.2, 12.6]
y = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]

df = pd.DataFrame({"X": x, "Y": y})

# Function

def sb_model(x, xmax, xc, b_val, scale):
    return scale/(1+((np.log(xmax/x)/np.log(xmax/xc))**b_val))

plt.plot(x, y)
p0 = np.array([100, 0.1, 1, 100])
model = sb_model(x, *p0)
plt.plot(x, model)

给了我这个模型和数据,它们比较接近:

我给它装上了

popt, pcov = curve_fit(f=sb_model, xdata=df["X"], ydata=df["Y"], p0 = p0, maxfev=800, bounds=(0, 1E5))

得到了这个:

popt原来是array([271.35537164, 1.21550569, 10.44652752, 100.36436418])
如果你对lmfit代码感兴趣,下面是它的代码,它的输出基本上是一样的,但是它还提供了一个标准误差(顺便说一句,xmax的标准误差是500%,b_瓦尔的标准误差是90%):

from lmfit import Parameters, minimize
p = Parameters()
p.add('xmax', value=100, min=0)
p.add('xc', value=1, min=0)
p.add('b_val', value=1)
p.add('scale', value=100, vary=True)

def residual(pars, x, data):
    xmax = pars['xmax']
    xc = pars['xc']
    b_val = pars['b_val']
    scale = pars['scale']
    model = sb_model(x, xmax, xc, b_val, scale)
    return model - data

out = minimize(residual, p, args=(x, y))

正在添加请求的约束:

from lmfit import Parameters, minimize

# xmax > xc

# xmax - xc > 0

# xmax - xc = delta; delta > 0

# xc = xmax - delta

p = Parameters()
acceptable_min = 1e-10 # Just so the values don't reach 0
p.add("xmax", value=100, min=acceptable_min, max=max(x))
p.add("delta", value=110, min=acceptable_min)
p.add("xc", min=acceptable_min, value=10, expr="delta - xmax")
p.add("b_val", value=1, min=acceptable_min)

def sb_model(x, xmax, xc, b_val):
    first_log = np.log(xmax / x)
    second_log = np.log(xmax / xc)              
    division = (first_log / second_log)
    # The suggestion by @Reinderen fixed the problem I was having here
    power =  np.sign(division) * np.abs(division)**b_val
    divisor = 1 + power
    val = 100 / divisor # Added the '100' factor here directly

    # I was having some problems with nans so I added this to monitor them.
    #print(f'{xmax.value=}, {xc.value=}, {b_val.value=}')
    #print(f'{first_log=}')
    #print(f'{second_log=}')
    #print(f'{division=}')
    #print(f'{power=}')
    #print(f'{divisor=}')
    #print(f'{val=}')
    #print()
    return val

def residual(pars, x, data):
    xmax = pars["xmax"]
    xc = pars["xc"]
    b_val = pars["b_val"]
    model = sb_model(x, xmax, xc, b_val)
    return model - data

out = minimize(residual, p, args=(x, y))

plt.plot(x, y)
plt.plot(x, y + out.residual)

如果我去掉xmax的最大值,我得到:

相关问题