首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Python SciPy 实现最小二乘法

Python SciPy 实现最小二乘法

作者头像
为为为什么
发布2023-04-08 10:59:45
发布2023-04-08 10:59:45
2.3K0
举报
文章被收录于专栏:又见苍岚又见苍岚

最小二乘法则是一种统计学习优化技术,它的目标是最小化误差平方之和来作为目标,从而找到最优模型。

简介

,从而找到最优模型的方法,该误差目标定义为:

J(\theta)=\min \sum_{i=1}{m}\left(f\left(x_{i}\right)-y_{i}\right){2}

Scipy 对优化最小二乘 Loss 的方法做了一些封装,主要有 scipy.linalg.lstsq 和 scipy.optimize.leastsq 两种,此外还有 scipy.optimize.curve_fit 也可以用于拟合最小二乘参数。

scipy.linalg.lstsq

SciPy 的 linalg 下的 lstsq 着重解决传统、标准的最小二乘拟合问题,该方法限制了模型 f(x_i) 的形式必须为 f\left(x_{i}\right)=a_{0}+a_{1} x^{1}+a_{2} x{2}+\cdots+a{n} x^{n} ,对于此类型的模型,给定模型和足够多的观测值 y_{i} 即可进行求解。

  • 求解时需要将模型 f(x_i) 改写成矩阵形式,矩阵用字母 A 表示,则只需给出方程 f\left(x_{i}\right) 的模型即 A 及样本 y_{i} 便可求得方程的各个系数。
  • 函数调用方法:
代码语言:javascript
复制
scipy.linalg.lstsq(A, y)

使用示例
例一

假设真实的模型是 y=2x+1 ,我们有一组数据 (x_i,y_i) 共 100 个,看能否基于这 100 个数据找出 x_i y_{i} 的线性关系方程 y=2 x+1 ,我们可以通过以下几步来完成。

  1. 序构造出100个 (x_i,y_i) 数据。
代码语言:javascript
复制
xi = x + np.random.normal(0, 0.05, 100)
yi = 1 + 2 * xi + np.random.normal(0, 0.05, 100)

  1. 给出模型 f(x)=a+b x 的矩阵A。由于有100个观测 \left(x_{i}, y_{i}\right) 的数据,那么就有:
\begin{aligned} a+b x_{0} & =y_{0} \ a+b x_{1} & =y_{1} \ a+b x_{2} & =y_{2} \ \cdots & \ a+b x_{99} & =y_{99}\end{aligned}

将以上式子写成如下矩阵的形式:

\left|\begin{array}{cc}1 & x_{0} \ 1 & x_{1} \ \vdots & \vdots \ 1 & x_{99}\end{array}\right| \times\left|\begin{array}{l}a \ b\end{array}\right|=\left|\begin{array}{c}y_{0} \ y_{1} \ \vdots \ y_{99}\end{array}\right|
代码语言:javascript
复制
A = np.vstack([xi**0, xi**1])

  1. 调用 scipy.linalg.lstsq 传入 A^{T} 和观测值里的 y_{i} 即程序里的yi变量即可求得 f(x)=a+b x 里的 abab 记录在 Istsq 函数的第一个返回值里。
代码语言:javascript
复制
sol, r, rank, s = la.lstsq(A.T, yi)

  1. scipy.linalg.Istsq 的第一个返回值 sol 共有两个值, sol0 即是估计出来的 f(x)=a+b x 里的 a \operatorname{sol}[1] 代表 f(x)=a+b x 里的 b 。因此 f(x) 为:
代码语言:javascript
复制
y_fit = sol[0] + sol[1] * x 

示例代码

代码语言:javascript
复制
import numpy
import scipy.linalg as la
import numpy as np
import matplotlib.pyplot as plt
m = 100
x = np.linspace(-1, 1, m)
y_exact = 1 + 2 * x
xi = x + np.random.normal(0, 0.05, 100)
yi = 1 + 2 * xi + np.random.normal(0, 0.05, 100)
print (xi,"#xi")
print (yi,"#yi")
A = np.vstack([xi**0, xi**1])
sol, r, rank, s = la.lstsq(A.T, yi)
y_fit = sol[0] + sol[1] * x 
print (sol,r ,rank,s )
fig, ax = plt.subplots(figsize=(12, 4))
ax.plot(xi, yi, 'go', alpha=0.5, label='Simulated data')
ax.plot(x, y_exact, 'k', lw=2, label='True value $y = 1 + 2x$')
ax.plot(x, y_fit, 'b', lw=2, label='Least square fit')
ax.set_xlabel(r"$x$", fontsize=18)
ax.set_ylabel(r"$y$", fontsize=18)
ax.legend(loc=2)
plt.show()

例二

考虑模型为 f\left(x_{i}\right)=a+b x+c x^{2} 的情况:

示例代码

代码语言:javascript
复制
import numpy
import scipy.linalg as la
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-1, 1, 100)
a, b, c = 1, 2, 3
y_exact = a + b * x + c * x**2
m = 100
xi=1 - 2 * np.random.rand(m)
print ("xi.shape", xi.shape,xi**1,xi)
yi=a + b * xi + c * xi**2 + np.random.randn(m) * 0.2
A = np.vstack([xi**0, xi**1, xi**2])
print (A.shape, A.T.shape)
sol, r, rank, s = la.lstsq(A.T, yi)
y_fit = sol[0] + sol[1] * x + sol[2] * x**2
fig, ax = plt.subplots(figsize=(12, 4))
ax.plot(xi, yi, 'go', alpha=0.5, label='Simulated data')
ax.plot(x, y_exact, 'k', lw=2, label='True value $y = 1 + 2x + 3x^2$')
ax.plot(x, y_fit, 'b', lw=2, label='Least square fit')
ax.set_xlabel(r"$x$", fontsize=18)
ax.set_ylabel(r"$y$", fontsize=18)
ax.legend(loc=2)
plt.show()

scipy.optimize.leastsq

scipy.optimize.leastsq 方法相比于 scipy.linalg.lstsq 更加灵活,开放了 f(x_i) 的模型形式。

leastsq() 函数传入误差计算函数和初始值,该初始值将作为误差计算函数的第一个参数传入。计算的结果是一个包含两个元素的元组,第一个元素是一个数组,表示拟合后的参数;第二个元素如果等于1、2、3、4中的其中一个整数,则拟合成功,否则将会返回 mesg。

调用示例
例一

首先仍以线性拟合为例,拟合 f(x)=a x+b 函数。

  • 示例代码
代码语言:javascript
复制
import numpy as np
from scipy.optimize import leastsq
def err(p, x, y):
    return p[0] * x + p[1] - y
p0 = [100, 20]
Xi=np.array([8.19,2.72,6.39,8.71,4.7,2.66,3.78])
Yi=np.array([7.01,2.78,6.47,6.71,4.1,4.23,4.05])
ret = leastsq(err, p0, args = (Xi, Yi))
print ret 
import matplotlib.pyplot as plt
k, b = ret[0]
plt.figure(figsize=(8,6))
plt.scatter(Xi,Yi,color="red",label="Sample Point",linewidth=3)
x = np.linspace(0,10,1000)
y = k * x + b
plt.plot(x,y,color="orange",label="Fitting Line",linewidth=2)
plt.legend()
plt.show()  

例二

这里我们展现一下 leastsq 的灵活之处,由于 leastsq 放开了对 f(x_i) 形式的严格限制,我们可以设置一些更加复杂的最小二乘的情况。

例如我现在就要拟合这么个函数:

f(x)=7e^x+3\frac{1}{\sqrt{x}}+12\sin x

相比于之前的多项式函数可以说有些丧心病狂了,但是也是在 leastsq 射程范围内:

代码语言:javascript
复制
import numpy as np
from scipy.optimize import leastsq

def f(p, x):
    return p[0] * (np.e ** x) + p[1] * (x ** - 0.5) + p[2] * np.sin(x)

def err(p, x, y):
    return f(p, x) - y

p0 = [1, 1, 1]
Xi = np.arange(1, 2, 0.03)

gt_p = [7, 3, 12]

Yi = f(gt_p, Xi) + (np.random.rand(len(Xi)) - 0.5)

ret = leastsq(err, p0, args = (Xi, Yi))
print (ret )
import matplotlib.pyplot as plt

plt.figure(figsize=(8,6))
plt.scatter(Xi,Yi,color="red",label="Sample Point",linewidth=3)

y = f(ret[0], Xi)

plt.plot(Xi,y,color="orange",label="Fitting Line",linewidth=2)
plt.legend()
plt.show()  

核心函数:

代码语言:javascript
复制
ret = leastsq(err, p0, args = (Xi, Yi))

  • 其中: err 为用于计算残差的 Callback 函数,p0 为初始解, args 为输入的数据。

输出结果:

代码语言:javascript
复制
array([ 7.02880266,  3.16343491, 11.73254754]), 1)

优化方法不是万能的,如果矩阵过于奇异,也是不利于准确求解模型参数的。

scipy.optimize.curve_fit

scipy.optimize.curve_fit 函数用于拟合曲线,给出模型和数据就可以拟合,相比于 leastsq 来说使用起来方便的地方在于不需要输入初始值。

代码语言:javascript
复制
scipy.optimize.curve_fit(fun, X, Y)

其中 fun 为输入参数为 x 和模型参数列表,输出 y 的 Callback 函数,XY 为数据

调用示例
例一

为了方便对比,将上文例二的示例代码修改成 curve_fit 函数的实现

  • 示例代码:
代码语言:javascript
复制
import numpy as np
from scipy.optimize import curve_fit

def f(x, p0, p1, p2):
    return p0 * (np.e ** x) + p1 * (x ** - 0.5) + p2 * np.sin(x)

Xi = np.arange(1, 2, 0.03)

gt_p = [7, 3, 12]

Yi = f(Xi, *gt_p) + (np.random.rand(len(Xi)) - 0.5)

para, pcov = curve_fit(f, Xi, Yi)
print (para)
import matplotlib.pyplot as plt

plt.figure(figsize=(8,6))
plt.scatter(Xi,Yi,color="red",label="Sample Point",linewidth=3)

y = f(Xi, *(para.tolist()))

plt.plot(Xi,y,color="orange",label="Fitting Line",linewidth=2)
plt.legend()
plt.show()  

输出结果:

代码语言:javascript
复制
[ 6.96284945  3.03529598 12.11638088]

绘制图像:

效果没有 leastsq 稳定,可能是没有初始值的缘故。

参考资料

文章链接:

https://cloud.tencent.com/developer/article/2260272

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023年4月6日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • scipy.linalg.lstsq
    • 使用示例
      • 例一
      • 例二
  • scipy.optimize.leastsq
    • 调用示例
      • 例一
      • 例二
  • scipy.optimize.curve_fit
    • 调用示例
      • 例一
  • 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档