Python Scipy最小化、fmin、leastsq类型问题(使用序列设置数组元素)、不匹配
我在使用scipy.optimize.fmin和scipy.optimize.minimize函数时遇到问题。我已经检查并确认传递给函数的所有参数都是numpy.array类型,以及error函数的返回值。此外,carreau函数返回一个标量值 一些额外参数(如大小)的原因是:我需要用给定的模型(Carreau)拟合数据。数据是在不同的温度下采集的,并用位移因子(也由模型拟合)进行校正,最后我得到了几组数据,这些数据都应用于计算相同的4个常数(参数p) 我读到无法向fmin函数传递数组列表,因此我必须将所有数据连接到x_data_lin中,用size参数跟踪不同的集合。t保持不同的测试温度,而t_0是保持参考温度的单元素阵列 我确信(经过三重检查)传递给函数的所有参数以及结果都是一维数组。以下是除此之外的代码:Python Scipy最小化、fmin、leastsq类型问题(使用序列设置数组元素)、不匹配,python,numpy,scipy,Python,Numpy,Scipy,我在使用scipy.optimize.fmin和scipy.optimize.minimize函数时遇到问题。我已经检查并确认传递给函数的所有参数都是numpy.array类型,以及error函数的返回值。此外,carreau函数返回一个标量值 一些额外参数(如大小)的原因是:我需要用给定的模型(Carreau)拟合数据。数据是在不同的温度下采集的,并用位移因子(也由模型拟合)进行校正,最后我得到了几组数据,这些数据都应用于计算相同的4个常数(参数p) 我读到无法向fmin函数传递数组列表,因此
import numpy as np
import scipy.optimize
from scipy.optimize import fmin as simplex
def err_func2(p, x, y, t, t_0, size):
result = array([])
temp = 0
for i in range(0, int(len(size)-1)):
for j in range(int(temp), int(temp+size[i])):
result = np.append(result, (carreau(p, x[j], t[i], t_0[0])-y[i]))
temp += size[i]
return result
p1 = simplex(err_func2, initial_guess,
args=(x_data_lin, y_data_lin, t_list, t_0, size), full_output=0)
以下是错误:
Traceback (most recent call last):
File "C:\Python27\Scripts\projects\Carreau - WLF\carreau_model_fit.py", line 146, in <module>
main()
File "C:\Python27\Scripts\projects\Carreau - WLF\carreau_model_fit.py", line 105, in main
args=(x_data_lin, y_data_lin, t_list, t_0, size), full_output=0)
File "C:\Python27\lib\site-packages\scipy\optimize\optimize.py", line 351, in fmin
res = _minimize_neldermead(func, x0, args, callback=callback, **opts)
File "C:\Python27\lib\site-packages\scipy\optimize\optimize.py", line 415, in _minimize_neldermead
fsim[0] = func(x0)
ValueError: setting an array element with a sequence.
:(我本来打算发布一张图表数据的图片,但我没有必要的10分
后期编辑:我现在已经让optimize.curvefit和optimize.leastsq开始工作了(这可能不是巧合地给出了相同的答案),但曲线很糟糕。我一直在尝试找出optimize.minimize,但这有点让人头疼。单纯形(fmin,Nelder\u Mead,随便你怎么称呼它)将运行,但产生了一个疯狂的答案。我以前从未处理过非线性优化问题,我真的不知道该往哪个方向走
以下是工作曲线拟合代码:
def temp_shift(t_s, t, t_0):
""" This function calculates the a_t temperature shift factor for polymer
viscosity curves. Variable is the standard temperature, t_s
"""
C_1 = 8.86
C_2 = 101.6
return(np.exp(
(C_1*(t_0-t_s) / (C_2+(t_0-t_s))) - (C_1*(t-t_s) / (C_2 + (t-t_s)))
))
def pass_data(t, t_0):
def carreau_2(x, p0, p1, p2, p3):
visc_0 = p0
m = p1
n = p2
t_s = p3
a_T = temp_shift(p3, t, t_0)
return (visc_0 * a_T / (1 + m * x * a_T)**n)
return carreau_2
initial_guess = array([20000, 3, 0.94, -20])
p1, conv = scipy.optimize.curve_fit(pass_data(t_all, t_0), x_data_lin,
y_data_lin, initial_guess)
以下是一些示例数据:
##def error_function(p, x, y, t, t_0):
## result = array([])
## for index in range(len(x)):
## result = np.append(result,(carreau(p, x[index],
## t[index], t_0) - y[index]))
## return result
## p1, success = scipy.optimize.leastsq(error_function, initial_guess,
## args=(x_list, y_list, t_list, t_0),
## maxfev=10000)
x_data_lin = array([0.01998, 0.04304, 0.2004, 0.43160, 0.92870, 2.0000, 4.30900,
9.28500, 15.51954, 21.94936, 37.52960, 90.41786, 204.35230,
331.58495, 811.92250, 1694.55309, 3464.27648, 8826.65738,
14008.00242])
y_data_lin = array([13520.00000, 13740.00000, 12540.00000, 9384.00000, 5201,
3232.00000, 2094.00000, 1484.00000, 999.00000, 1162.05088
942.56946, 705.62489, 429.47341, 254.15136, 185.22916,
122.07113, 76.46324, 47.85064, 25.74315, 18.84875])
t_all = array([190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190,
190, 190, 190, 190, 190, 190, 190])
t_0 = 80
下面是曲线拟合结果的图片(现在我有10个点,可以发布!)。注意,绘制了3条曲线,因为我使用了3组数据在3个不同的温度下优化曲线。聚合物的特性是剪切速率-粘度关系保持不变,只需通过温度系数a\T移动:
我非常感谢任何关于如何改进拟合的建议,或者如何定义函数以便优化.minimize工作,以及哪种方法(Nelder Mead,Powel,BFGS)可能工作的建议
要添加的另一个编辑:我使用了Nelder Mead(optimize.fmin和optimize.minimize的默认值)函数-我将在下面包含修改后的错误函数。之前,我只是简单地对结果数组求和并返回它。这导致了极端负值(显然,因为函数的目标是最小化)。求和前对结果进行平方运算解决了该问题。请注意,我还完全更改了函数,以利用numpy的阵列广播,正如JaminSore所建议的那样(谢谢Jamin!)
不幸的是,Nelder-Mead函数给出的结果与leastsq和curve_拟合的结果相同。从上图中可以看出,它不是最佳拟合;事实上,在这一点上,Microsoft Excel的solver函数在数据处理方面做得更好
至少,我希望这个线程在将来对初学者scipy.optimize有用,因为我花了很长时间才发现这一切。与
leastsq
不同,fmin
只能处理返回标量的错误函数,因此如果可能,您必须重写错误函数,使其返回标量。这里这是一个简单的工作示例
导入必要的库
import numpy as np
from scipy.optimize import fmin
定义一个助手函数(稍后将看到)
模拟一些数据
true_ = np.random.normal(size = 100) #parameters we're trying to recover
b = np.random.normal(size = 20)
exp_ = prob(true_[:, None], b) #expected
a_s, b_s = true_.shape[0], b.shape[0]
noise = np.random.uniform(size = (a_s, b_s))
response = (noise > (1 - exp_)).astype(int)
定义错误函数(我使用的是lambda
s,但在实践中不建议这样做)
如果我在错误函数定义的末尾删除
.sum()
,我会得到相同的错误。好的,现在我终于知道答案了!首先是最后一部分,然后是重述。拟合的问题不是curve\u fit、leastsq、Nelder\u Mead或Powell(我尝试过的方法)的错误。这与误差的相对权重有关。由于该数据是对数标度,因此高y值附近的拟合误差非常昂贵,而低y值附近的误差则无关紧要。为了纠正这一点,我通过除以数据的y值来确定误差的相对性,如下所示:
def err_func2(p, x, y, t, t_0):
return (((carreau(p, x, t, t_0)-y)/y)**2).sum()
现在,将每个相对误差平方,求和,然后最小化,得到以下拟合(使用Powell方法的optimize.minimize,尽管其他方法也应该如此)
现在,我们来回顾一下在这篇文章中找到的答案:
- 处理曲线拟合最简单的方法(或者至少对我来说,最简单的方法)是将所有数据收集到1D numpy.array中。然后,您可以依靠numpy的数组广播来执行所有操作。这意味着算术运算的处理方式与向量点积的处理方式相同。例如,array_1=[a,b],array_2=[c,d],然后是数组_1+array_2=[a+c,b+d]。这适用于加法、减法、乘法、除法和幂:数组+1array_2=[ac,b**d]
- 对于optimize.leastsq函数,您需要让目标函数返回一个数组;即,
,其中结果是一个数组。对于optimize.curve_fit,您还返回一个数组。在这种情况下,传递额外的参数(考虑其他常量)会稍微复杂一些,但您可以使用嵌套函数来实现,正如我在上面的返回结果
函数中所演示的那样pass\u data
- 对于optimize.minimize,您需要返回一个标量——也就是一个数字。我认为您也可以返回一个答案数组,但我通过将所有数据放入1D数组避免了这一点,正如我前面提到的。要获得这个标量,您只需将结果平方和即可(就像我在这篇文章中写的那样)
true_ = np.random.normal(size = 100) #parameters we're trying to recover b = np.random.normal(size = 20) exp_ = prob(true_[:, None], b) #expected a_s, b_s = true_.shape[0], b.shape[0] noise = np.random.uniform(size = (a_s, b_s)) response = (noise > (1 - exp_)).astype(int)
# sum of the squared residuals err_func = lambda a : ((prob(a[:, None], b) - response) ** 2).sum() result = fmin(err_func, np.zeros_like(true_)) #solve
def err_func2(p, x, y, t, t_0): return (((carreau(p, x, t, t_0)-y)/y)**2).sum()