【问题标题】:Python scipy.optimise.curve_fit gives linear fitPython scipy.optimise.curve_fit 提供线性拟合
【发布时间】:2020-04-10 23:02:58
【问题描述】:

我在使用 scipy 中的 curve_fit 参数时遇到了一个问题。我最初复制了文档建议的代码。然后我稍微改变了方程,这很好,但是增加了 np.linspace,整个预测最终变成了一条直线。有什么想法吗?

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


def f(x, a, b, c):
    # This works fine on smaller numbers
    return (a - c) * np.exp(-x / b) + c


xdata = np.linspace(60, 3060, 200)
ydata = f(xdata, 100, 400, 20)

# noise
np.random.seed(1729)
ydata = ydata + np.random.normal(size=xdata.size) * 0.2

# graph
fig, ax = plt.subplots()
plt.plot(xdata, ydata, marker="o")
pred, covar = curve_fit(f, xdata, ydata)
plt.plot(xdata, f(xdata, *pred), label="prediciton")
plt.show()

【问题讨论】:

    标签: python scipy curve-fitting scipy-optimize


    【解决方案1】:

    这是由于Levenberg–Marquardt algorithm 默认使用curve_fit 的限制。使用它的好方法是在优化之前为参数提供一些不错的初始猜测。根据我的经验,在优化像您的示例这样的指数函数时,这一点尤为重要。使用像 LM 这样的迭代算法,起点的质量决定了结果的收敛点。您拥有的参数越多,最终结果就越有可能收敛到完全不需要的曲线。总体而言,解决方案以某种方式找到了一个很好的初始猜测,就像其他答案一样。

    【讨论】:

      【解决方案2】:

      这是使用您的数据和方程的示例代码,初始参数估计由 scipy 的差分进化遗传算法模块给出。该模块使用拉丁超立方体算法来确保对参数空间的彻底搜索,这需要搜索范围。在此示例中,这些界限取自数据最大值和最小值。为初始参数估计提供范围比提供具体值要容易得多。

      import numpy, scipy, matplotlib
      import matplotlib.pyplot as plt
      from scipy.optimize import curve_fit
      from scipy.optimize import differential_evolution
      import warnings
      
      
      def func(x, a, b, c):
          return (a - c) * numpy.exp(-x / b) + c
      
      
      xData = numpy.linspace(60, 3060, 200)
      yData = func(xData, 100, 400, 20)
      
      # noise
      numpy.random.seed(1729)
      yData = yData + numpy.random.normal(size=xData.size) * 0.2
      
      
      # function for genetic algorithm to minimize (sum of squared error)
      def sumOfSquaredError(parameterTuple):
          warnings.filterwarnings("ignore") # do not print warnings by genetic algorithm
          val = func(xData, *parameterTuple)
          return numpy.sum((yData - val) ** 2.0)
      
      
      def generate_Initial_Parameters():
          # min and max used for bounds
          maxX = max(xData)
          minX = min(xData)
          maxY = max(yData)
          minY = min(yData)
      
          parameterBounds = []
          parameterBounds.append([minY, maxY]) # search bounds for a
          parameterBounds.append([minX, maxX]) # search bounds for b
          parameterBounds.append([minY, maxY]) # search bounds for c
      
          # "seed" the numpy random number generator for repeatable results
          result = differential_evolution(sumOfSquaredError, parameterBounds, seed=3)
          return result.x
      
      # by default, differential_evolution completes by calling curve_fit() using parameter bounds
      geneticParameters = generate_Initial_Parameters()
      
      # now call curve_fit without passing bounds from the genetic algorithm,
      # just in case the best fit parameters are aoutside those bounds
      fittedParameters, pcov = curve_fit(func, xData, yData, geneticParameters)
      print('Fitted parameters:', fittedParameters)
      print()
      
      modelPredictions = func(xData, *fittedParameters) 
      
      absError = modelPredictions - yData
      
      SE = numpy.square(absError) # squared errors
      MSE = numpy.mean(SE) # mean squared errors
      RMSE = numpy.sqrt(MSE) # Root Mean Squared Error, RMSE
      Rsquared = 1.0 - (numpy.var(absError) / numpy.var(yData))
      
      print()
      print('RMSE:', RMSE)
      print('R-squared:', Rsquared)
      
      print()
      
      
      ##########################################################
      # graphics output section
      def ModelAndScatterPlot(graphWidth, graphHeight):
          f = plt.figure(figsize=(graphWidth/100.0, graphHeight/100.0), dpi=100)
          axes = f.add_subplot(111)
      
          # first the raw data as a scatter plot
          axes.plot(xData, yData,  'D')
      
          # create data for the fitted equation plot
          xModel = numpy.linspace(min(xData), max(xData))
          yModel = func(xModel, *fittedParameters)
      
          # now the model as a line plot
          axes.plot(xModel, yModel)
      
          axes.set_xlabel('X Data') # X axis data label
          axes.set_ylabel('Y Data') # Y axis data label
      
          plt.show()
          plt.close('all') # clean up after using pyplot
      
      graphWidth = 800
      graphHeight = 600
      ModelAndScatterPlot(graphWidth, graphHeight)
      

      【讨论】:

      • differential_evolution 本身就是一个优化例程。您正在使用优化例程来寻找另一个优化例程 (curve_fit) 的初始猜测,这很好,但不是有点多余吗?最后,您仍然需要对differential_evolution 进行某种初始化,通过设置边界(differential_evolution 在内部将使用它来设置生成猜测),所以为什么不只是根据到底有没有界限?
      • 因为界限可以很大。许多数据和方程的组合对特定的初始参数估计非常敏感,因此很难手动找到这些初始值。为初始参数估计找到大的范围通常要容易得多,没有冗余。
      • 我认为您的陈述非常笼统,在实践中的许多情况下都站不住脚。一个慷慨的界限,尤其是像遗传这样的无导数例程,可以在定义不明确、收敛缓慢甚至发散的区域中找到猜测,只是因为它看起来不错。大范围的边界也可能导致浪费的搜索。全局优化是关于探索和局部搜索之间的平衡。我根据手头的问题将如何启动猜测的问题留给 OP。我避免对什么应该是启动猜测的最佳方式做出广泛的陈述
      • 在您对这个问题的回答中,您提供了初始参数估计值。您是如何确定这些估算值的?
      【解决方案3】:

      您可能需要从更好的猜测开始,默认初始猜测(1.0, 1.0, 1.0) 似乎在发散区域。

      我使用最初的猜测 p0 = (50,200,100) 并且它有效

      fig, ax = plt.subplots()
      plt.plot(xdata, ydata, marker="o")
      pred, covar = curve_fit(f, xdata, ydata, p0 = (50,200,100))
      plt.plot(xdata, f(xdata, *pred), label="prediciton")
      plt.show()
      

      【讨论】:

      • 在下面的回答中,我使用 scipy 的差分进化遗传算法实现来为 curve_fit() 提供初始参数估计。您如何确定您在答案中使用的初始参数估计值?
      猜你喜欢
      • 1970-01-01
      • 2021-07-23
      • 2015-03-14
      • 2021-08-03
      • 2017-12-17
      • 1970-01-01
      • 1970-01-01
      • 2013-11-08
      • 2012-07-29
      相关资源
      最近更新 更多