深度学习导论知识——最小二乘法(Ordinary Least Squares, OLS)
1. 知识点简介
最小二乘法(Ordinary Least Squares, OLS)是常见的估计模型参数的方法。
早在19世纪,勒让德就认为按照 “误差的平方和最小” 这个规则估计出来的模型是最接近真实情形的。于是就有:
其中,yi 是观测值或真实值,即样本数据,即采集得到的数据。f(xi) 是把数据带入假设的模型中得到的理论值。即这个式子表示:真实值和理论值距离的平方和最小的时候,对假设的模型估计出来的参数是最接近真实情形的。这个准则被称为最小二乘准则。这里的 “二乘” 就是平方的意思。
关于最小二乘法内容更加详细的介绍,请参看以下两篇文章:
https://blog.csdn.net/MoreAction_/article/details/106443383
https://zhuanlan.zhihu.com/p/38128785
2. 公式推导
首先定义线性回归的目标函数(Objective Function)或称损失函数(Loss Function)为均方误差(Mean Squared Error, MSE),公式为误差平方和(sum of squared errors, SSE)除以样本数(n),其中
MSE越小越小,所以是一个最小化的问题。
(1)目标函数 。
1. ,这里可以忽略常数n,只考虑SSE,有:
2. 以矩阵表示线性回归。其中 ,故将 b 也可视为 w 的一个项目。
3. 以矩阵表示SSE:
注意:这里的 wx 已经包含了 b*x0 这一项,即 wx 是个大矩阵,横向拼接了特征变量矩阵 wx 以及偏移矩阵 b,这部分可在之后的范例1中的代码 X = np.hstack((X, b)) 这句看出。之外,矩阵计算乘法要遵循第一个矩阵的列等于第二个矩阵的行这个规则,所以 (y-wx)2 变换到矩阵形式为 (y-wx)T(y-wx) 。
(2)对 w 偏微分得
结合矩阵、微分,我们就能够轻松求出线性回归的系数,此原理是神经网络优化求解的基石。
3. 程序代码
范例1:以普通最小二乘法建立线性回归模型,预测波士顿(Boston)房价。
(1)依据以上公式推导的结论(即最后一个公式)计算出回归系数。
# 载入套件
import numpy as np
from sklearn import datasets
# 载入 sklearn 内建数据集
ds = datasets.load_boston()
# 特征变数,即用于预测房价的变量值,shape=(506, 13)
X = ds.data
# b = b * x^0, shape=(506, 1)
b = np.ones((X.shape[0], 1))
# 通过横向拼接将 b 并入 w,shape=(506, 14)
X = np.hstack((X, b))
# 目标变数,即最终预测出来的房价,shape=(506,)
y = ds.target
# 根据以上推导的最后一个公式求解出w
W = np.linalg.inv(X.T @ X) @ X.T @ y
print(f'W={W}')
代码运行结果:
(2)计算相关效果衡量指标。
1 # 计算效能衡量指标
2 # 计算误差平方和SSE(sum of squared errors, SSE),SSE越接近于0,说明模型选择和拟合更好,数据预测也越成功
3 SSE = ((X @ W - y) ** 2).sum()
4 # 计算均方误差MSE(Mean Squared Error, MSE)
5 MSE = SSE / y.shape[0]
6 # 计算均方根误差RMSE(Root Mean Squared Error,RMSE)
7 RMSE = MSE ** (1/2)
8 print(f'MSE={MSE}')
9 print(f'RMSE={RMSE}')
10
11 # 计算判别系数(R^2),该统计量越接近1,表明变量对y的解释能力越强,即模型对数据拟合的越好
12 # ravel可以让多维数据变成一维
13 y_mean = y.ravel().mean()
14 SST = ((y - y_mean) ** 2).sum()
15 R2 = 1 - (SSE / SST)
16 print(f'R2={R2}')
代码运行结果:
(3)以 Sklearn 库自带的求解线性回归模型的函数和衡量指标的函数验证以上推导过程。
1 from sklearn.linear_model import LinearRegression
2 from sklearn.metrics import r2_score, mean_squared_error
3
4 # 模型训练
5 lr = LinearRegression()
6 lr.fit(X, y)
7
8 # 预测
9 y_pred = lr.predict(X)
10
11 # 回归系数
12 print(f'W={lr.coef_},{lr.intercept_}\n')
13
14 # 计算效能衡量指标
15 print(f'MSE={mean_squared_error(y, y_pred)}')
16 print(f'RMSE={mean_squared_error(y, y_pred) ** .5}')
17 print(f'R2={r2_score(y, y_pred)}')
代码运行结果:
从代码运行结果可知,以上公式推导的结果和函数库计算出来的结果一致。
范例2:使用 Scipy 以普通最小二乘法计算函数 x2+5 的最小值。
先对函数绘图,再调用 scipy.optimize 模块的 leastsq() 函数进行优化求解。
1 # 函数绘图
2 import numpy as np
3 import matplotlib.pyplot as plt
4 from scipy.optimize import leastsq
5 plt.ion()
6
7 x = np.linspace(-5, 5, 11)
8
9
10 # x^2+5
11 def f(x):
12 return x**2+5
13
14
15 # 绘座标轴
16 plt.axhline()
17 plt.axvline()
18 # 绘图
19 plt.plot(x, f(x), 'g')
20 plt.scatter([0], [5], color='r')
21
22 import numpy as np
23 from scipy.optimize import leastsq
24
25
26 # x^2+5
27 def f(x):
28 return x**2+5
29
30
31 # 使用最小平方法求解
32 # leastsq():Minimize the sum of squares of a set of equations.
33 print(leastsq(f, 5, full_output=1)) # full_output=1 ==> 显示详尽的结果
代码运行结果:
在 leastsq() 函数中,第一个参数是求解的函数;第二个参数是起始点;leastsq() 是采用逼近法,而非纯数学公式求解,nfev 显示它经过 22 次执行周期,才找到最小值 5(fvec),当时 x = 1.72892379e-05 ≈ 0 。
leastsq() 第二个参数起始点可设置为任意值,通常采用随机数或是直接给0。指定值设定不佳的话,仍然可以找到最佳解,不过,需要较多次的执行周期,也就是所谓的较慢收敛(Convergence)。
这里的 leastsq() 函数的用法并没有给出样本点数据求模型的未知参数,而是只利用 leastsq() 函数可以返回最小值的功能,如果需要使用 leastsq() 这个最小二乘法函数计算参数,那么就要给出样本数据,并且设置 leastsq() 函数中的 args 参数,注意下,args 参数中变量的位置要按照定义函数的时候参数的顺序设置。具体的应用可以参照:https://www.cnblogs.com/jaszzz/p/15136309.html 以及官方文档。
当面对较复杂的函数或较多的变量时,我们很难单纯运用数学去求解,因此,逼近法会是一个比较实用的方法,深度学习的梯度下降法就是一个典形的例子。
备注:以上 2.公式推导 和 3. 程序代码 均来自《深度学习全书 公式+推导+代码+TensorFlow全程案例》—— 洪锦魁主编 清华大学出版社 ISBN 978-7-302-61030-4 这本书。