使用Python和Gekko拟合非线性模型

3

我有一个关于树木的数据集。在这个数据集中,我有地块的唯一编号"Plot"、采集数据时的顺序"Measurement"以及树木高度平均值和年龄平均值。类似这样:

数据头部

接下来,我定义了预测使用年龄来预测树木高度的模型:

Height = B0 * ((1 - exp(-B1 *Age))**B2)

我的目标是分别确定B0、B1和B2的值。为此,我使用gekko包来找到模型参数,使用以下代码:

num_p = data_gek.Plot.unique()
nmp = 5
number_p = (data_gek.Plot == num_p[nmp])

m = GEKKO()

xm = np.array(data_gek[number_p]['Age'])
x = m.Param(value=xm)

B0 = m.FV(value=38.2) #value=38.2
B0.STATUS = 1

B1 = m.FV(value=0.1) #value=0.1
B1.STATUS = 1

B2 = m.FV(value=2.08) #value=2.08
B2.STATUS = 1

ym =  np.array(data_gek[number_p]['Height'])
z = m.CV(value=ym)

y = m.Var()
m.Equation(y==B0 * ((1 - m.exp(-B1 *x))**B2))
m.Obj(((y-z)/z)**2)

m.options.IMODE = 2
m.options.SOLVER = 1
m.solve(disp=False)

print(B0.value[0],B1.value[0],B2.value[0])
#output 
27.787958561 0.0052435491089 0.21178326158

不过,我不确定我是否使用了正确的方法。在没有参数初始值的情况下,是否有可能完成这个过程?因为我从文献中使用了B0、B1和B2的先前值。

如果您想查看我的数据集和处理过程,可以访问Google Colab笔记本

1个回答

2
您的脚本只有一个问题。需要将z的定义更改为ParamMV类型,而不是CV,例如z = m.Param(value=ym),因为它是目标函数的输入。
如果您将y定义为CV而不是Var,则还可以使用内置目标。您只需要打开反馈状态FSTATUS=1,就可以使用最小化测量值和模型预测之间差异的目标函数。以下是修改后的脚本示例。

regression fit

from gekko import GEKKO
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
url = 'http://apmonitor.com/pdc/uploads/Main/data_2nd_order.txt'
data = pd.read_csv(url)
m  = GEKKO()
xm = np.array(data['time'])
x  = m.Param(value=xm)
B0 = m.FV(1); B1 = m.FV(1); B2 = m.FV(1)
B0.STATUS = 1; B1.STATUS = 1; B2.STATUS = 1
ym =  np.array(data['output (y)'])
y  = m.CV(value=ym)
y.FSTATUS = 1
yi = m.Intermediate(B0 * ((1 - m.exp(-B1 *x))**B2))
m.Equation(y==yi)
m.options.IMODE = 2
m.options.SOLVER = 1
m.solve(disp=True)
print(B0.value[0],B1.value[0],B2.value[0])
plt.plot(xm,ym,'ro')
plt.plot(xm,y.value,'b--')
plt.show()

我尝试使用您的代码,但它仍然无法工作。我得到了“异常:@error:未找到解决方案”的提示。即使我清理了我的数据,查找了图层和缺失的数据。您能否看一下我的数据?我认为它比您的示例更复杂。 - Juan Pablo Cuevas
2
尝试将 B0B1B2的初始值从1更改为您在文献中找到的初始值。例如:B0 = m.FV(38.2) 等等... - reyPanda
3
另一个可尝试的方法是对参数进行限制,特别是B1。一个很大的负值会导致m.exp()这一项有一个非常大的值,并使求解器难以收敛。您可以使用B0 = m.FV(lb=0,ub=100)添加这些限制。@reyPanda也提出了一个很好的建议,即使用更好的初始值。您可以将这些建议与B0 = m.FV(value=38.2,lb=0,ub=100)相结合。 - John Hedengren

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接