1 概念
-
最小二乘法:最小二乘法(又称最小平方法,Least squares)是一种数学优化技术。它通过最小化误差的平方和寻找数据的最佳函数匹配。利用最小二乘法可以简便地求得未知的数据,并使得这些求得的数据与实际数据之间误差的平方和为最小。最小二乘法还可用于曲线拟合。其他一些优化问题也可通过最小化能量或最大化熵用最小二乘法来表达。
-
梯度下降:梯度下降(Gradient Descent)是迭代法的一种,可以用于求解最小二乘问题(线性和非线性都可以)。
在求解损失函数的最小值时,可以通过梯度下降法来一步步的迭代求解,得到最小化的损失函数和模型参数值。反过来,如果我们需要求解损失函数的最大值,这时就需要用梯度上升法来迭代了。在机器学习中,基于基本的梯度下降法发展了两种梯度下降方法,分别为随机梯度下降法和批量梯度下降法。 -
在求解机器学习算法的模型参数,即无约束优化问题时,梯度下降是最常采用的方法之一,另一种常用的方法是最小二乘法。
2 区别
狭义的最小二乘法:指的是在线性回归下采用最小二乘准则(或者说叫做最小平方),进行线性拟合参数求解的、矩阵形式的公式方法。
这里,基于线性回归,有两个细节比较重要:
- 第一,线性回归的模型假设。这是最小二乘方法的优越性前提,否则不能推出最小二乘是最佳(即方差最小)的无偏估计,具体请参考高斯-马尔科夫定理。特别地,当随机噪声服从正态分布时,最小二乘与最大似然等价。
- 第二,由于是线性回归/拟合,因此可以很容易的求出**全局最优的闭式解(close form solution)**即:全局最优解,也即我们通常看到的那几个矩阵形式,给了(input data)可以一步到位计算出拟合参数,而不是像梯度下降法或者牛顿法那样一点点地迭代优化调参,最后到达极值点,即:局部最优解,二者得到的参数值可能会相同或特别接近。
广义的最小二乘法:是最小二乘准则,本质上是一种evaluation rule或者说objective funcion,这里的「最小二乘法」应叫做(最小二乘法则)或者(最小二乘准则),英文可呼为LSE(least square error)。(不确定是否正确)
综上:
- 狭义的最小二乘方法,是线性假设下的一种有闭式解的参数求解方法,最终结果为全局最优
- 梯度下降法,是假设条件更为广泛(无约束)的,一种通过迭代更新来逐步进行的参数优化方法,最终结果为局部最优;其实目标和最小二乘法相同,也是最小化误差的平方和,但是不能像最小二乘法一样写成矩阵的形式一步算出(并且算出的还是全局最优解),梯度下降做的是迭代求解,算出的也可能是局部最优解。
- 广义的最小二乘准则,是一种对于偏差程度的评估准则,与上两者不同。通俗来讲,就是最小化误差平方和的准则。(不确定是否正确)
3 scipy.optimize的使用
leastsq:最小化一组方程的平方和
S
(
p
)
=
∑
i
=
1
M
[
y
i
−
f
(
x
i
,
p
)
]
2
S(p) = \sum_{i=1}^{M}[y_{i} - f(x_{i}, p)]^2
S(p)=i=1∑M[yi−f(xi,p)]2
# 使用 leastsq 函数求参数,拟合的函数形式是:
# y=A*sin(2*pi*k*x + theta)
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import leastsq
def func(x, p):
""" 数据拟合所用的函数: A*sin(2*pi*k*x + theta) """
A, k, theta = p
return A*np.sin(2*np.pi*k*x+theta)
def residuals(p, y, x):
""" 实验数据x, y和拟合函数之间的差,p为拟合需要找到的系数 """
return y - func(x, p)
下面这些真实函数数据是用来评估我们的拟合结果:
#真实函数参数
A, k, theta = 10, 0.34, np.pi/6 # 真实数据的函数参数
# 真实函数数据
x = np.linspace(-2*np.pi, 0, 100) # x
y0 = func(x, [A, k, theta]) # 真实数据
在现实问题中,我们是根据目前手上已有的数据,和估计的方程,通过最小二乘法来估计方程的参数,下面是具体过程:
# 真实数据(加入噪声之后的实验数据)
y1 = y0 + 2 * np.random.randn(len(x))
# 需要求解的参数的初值,注意参数的顺序,要和func保持一致
p0 = [7, 0.2, 0]
# 调用leastsq进行数据拟合, residuals为计算误差的函数
# p0为拟合参数的初始值
# args为需要拟合的实验数据,也就是,residuals误差函数,除了P之外的其他参数都打包到args中
# 这里将 (y1, x)传递给args参数。Leastsq()会将这两个额外的参数传递给residuals()。
# 因此residuals()有三个参数,p是正弦函数的参数,y和x是表示实验数据的数组。
plsq = leastsq(residuals, p0, args=(y1, x))
# 拟合的参数
print("拟合参数", plsq[0])
# 拟合参数 [ 10.6359371 0.3397994 0.50520845]
# 发现跟实际值有差距,但是从下面的拟合图形来看还不错,说明结果还是堪用的。
拟合的结果每一次都会有一点不同。
[output]:
拟合参数 [10.39202763 0.33901741 0.53270437]
# 顺便画张图看看
plt.plot(x, y0, label = 'experiment data')
plt.plot(x, y1, label = 'real data')
plt.plot(x, func(x, plsq[0]), label = 'fitting data')
plt.legend()
plt.show()
巴斯模型的拟合:
Least-squares:求解带变量边界的非线性最小二乘问题
# 最小二乘法
from math import e # 引入自然数e
import numpy as np # 科学计算库
import matplotlib.pyplot as plt # 绘图库
from scipy.optimize import leastsq # 引入最小二乘法算法
# 样本数据(Xi,Yi),需要转换成数组(列表)形式
ti = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
yi = np.array([8, 11, 15, 19, 22, 23, 22, 19, 15, 11])
# 需要拟合的函数func :指定函数的形状,即n(t)的计算公式
def func(params, t):
m, p, q = params
fz = (p * (p + q) ** 2) * e ** (-(p + q) * t) # 分子的计算
fm = (p + q * e ** (-(p + q) * t)) ** 2 # 分母的计算
nt = m * fz / fm # nt值
return nt
# 误差函数函数:x,y都是列表:这里的x,y更上面的Xi,Yi中是一一对应的
# 一般第一个参数是需要求的参数组,另外两个是x,y
def error(params, t, y):
return func(params, t) - y
# k,b的初始值,可以任意设定, 一般需要根据具体场景确定一个初始值
p0 = [100, 0.3, 0.3]
# 把error函数中除了p0以外的参数打包到args中(使用要求)
params = leastsq(error, p0, args=(ti, yi))
params = params[0]
# 读取结果
m, p, q = params
print('m=', m)
print('p=', p)
print('q=', q)
# 有了参数后,就是计算不同t情况下的拟合值
y_hat = []
for t in ti:
y = func(params, t)
y_hat.append(y)
print(y_hat)
print(yi)
# 接下来我们绘制实际曲线和拟合曲线
# 由于模拟数据实在太好,两条曲线几乎重合了
fig = plt.figure()
plt.plot(yi, color='r', label='true')
plt.plot(y_hat, color='b', label='predict')
plt.title('BASS model')
plt.legend()
参考资料:
https://baike.baidu.com/item/%E6%9C%80%E5%B0%8F%E4%BA%8C%E4%B9%98%E6%B3%95/2522346?fr=aladdin
https://baike.baidu.com/item/%E6%A2%AF%E5%BA%A6%E4%B8%8B%E9%99%8D/4864937?fr=aladdin
https://blog.csdn.net/qq_27514297/article/details/53508149
https://blog.csdn.net/jiang425776024/article/details/87885969