一、最小二乘法
1.一元线性拟合的最小二乘法
先选取最为简单的一元线性函数拟合助于我们理解最小二乘法的原理。

要让一条直接最好的拟合红色的数据点,那么我们希望每个点到直线的残差都最小。
设拟合直线为 yi=b+axi,那这些数据的所有误差和为:S=∑i=1n(y−b−axi)2
因此,为了能得到最小的误差所对应的b,a的值,需要分别对S关于b,a求导,并且令导数为0:
∂b∂S=−2i=1∑n(yi−b−axi)=0
∂a∂S=−2i=1∑n(yi−b−axi)xi=0
解得:
a=n∑xi2−(∑xi)2n∑xiyi−∑xi∑yi
b=y−ax
具体推导过程:
∂b∂S=−2i=1∑n(yi−b−axi)=0
∂a∂S=−2i=1∑n(yi−b−axi)xi=0
令∂a∂D=0,∂a∂D,求解a和b,令nxˉ=∑i=1nxi,nyˉ=∑i=1nyi
因此得:
nyˉ−na−bnxˉ=0
i=1∑nxiyi−ai=1∑nxi−bi=1∑nxi2=0
得:
b=yˉ−axˉ
i=1∑nxiyi−yˉi=1∑nxi+bxˉi=1∑nxi2=0
最终得
b=yˉ−axˉ
a=∑i=1nxi2−xˉ∑i=1nxi∑i=1nxiyi−yˉ∑i=1nxi=∑i=1n(xi−xˉ)2∑i=1n(xi−xˉ)(yi−yˉ)2 (求和性质)
求和性质:具体可以参考Introductory Econometrics A Modern Approach (Fourth Edition) 一书(计量经济学导论,第4版,杰弗里·M·伍德里奇 著)的附录A。
求和性质证明:
(1)i=1∑n(xi−xˉ)2=i=1∑n(xi2−2xixˉ+xˉ2)
=i=1∑nxi2−2xˉi=1∑nxi+i=1∑nxˉ2
=i=1∑n−2nxˉ2+nxˉ2
=i=1∑nxi2−nxˉ2=i=1∑nxi2−xˉi=1∑nxi (分母得证)
(2)i=1∑n(xi−xˉ)(yi−yˉ)
=i=1∑n(xiyi−xiyˉ−xˉyi+xˉyˉ)
=i=1∑nxiyi−i=1∑nxiyˉ−i=1∑nxˉyi+i=1∑nxˉyˉ
=i=1∑nxiyi−nxˉyˉ−nxˉyˉ+nxˉyˉ
=i=1∑nxiyi−yˉi=1∑nxi (分子得证)
将算法转化为python语言:
import numpy as np
import matplotlib.pyplot as plt
from pylab import mpl
"""
一元线性拟合
采用的拟合数据为xi=1,2,3,4,5,6,7
对应的相应函数值yi=0.5,2.5,2.4,3.5,6,5.5
"""
x=[1,2,3,4,5,6,7]
y=[0.5,2.5,2,4,3.5,6,5.5]
"""
离散点图像绘制
"""
plt.scatter(x,y)
plt.xlabel("x")
plt.ylabel("y")
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
plt.title("散点图")
plt.show()

"""
计算拟合曲线参数a,b
"""
size = len(x)
sum_x=0
sum_y=0
average_x=0
average_y=0
sum_xy=0
sum_sqare_x=0
b=0
for i in range(size):
sum_x+=x[i]
sum_y+=y[i]
sum_xy+=x[i]*y[i]
sum_sqare_x+=pow(x[i],2)
average_x=sum_x/size
average_y=sum_y/size
a= (sum_xy-average_y*sum_x)/(sum_sqare_x-average_x*sum_x)
b=(average_y-a*average_x)
print("a=",a)
print("b=",b)
a= 0.8392857142857143
b= 0.07142857142857117
"""
绘制拟合曲线
"""
data_new_y=[]
i = 0
plt.scatter(x,y,label='sin')
plt.title("一元线性拟合")
plt.xlabel("x")
plt.ylabel("y")
for i in range(size):
data_new_y.append(a*x[i]+b)
plt.plot(x,data_new_y,color='black')
plt.show()

2.多元线性拟合的最小二乘法
在预测中,可能结果受到多个变量的影响,那么我们的拟合曲线就由y=a0+a1x转变为y=B0+B1x(1)+B2x(2)+...+Bmx(m)
(x的上标代表变量的编号而不是次方)
对于多变量,可以引入线性方程组和矩阵的概念得到最优解。
列出线性方程组:
B0+B1x11+...+Bmx1m=y1
B0+B1x21+...+Bmx2m=y2
B0+B1xi1+...+Bmxim=yi
B0+B1xn1+...+Bmxnm=yn
上式记为矩阵形式为:
⎣⎢⎢⎡11…1x11x12…xnm…………xn1xn1…xnm⎦⎥⎥⎤⎣⎢⎢⎡B0B1…Bm⎦⎥⎥⎤=⎣⎢⎢⎡y1y2…yn⎦⎥⎥⎤
可以简化为:
A⋅B=Y
最小二乘形式:
min∥AB−Y∥2
得到最终的最优解为:
B^=(ATA)−1ATY
具体推导过程:
多变量线性回归代价函数为:
J(θ0,θ1,…,θn)=2m1i=1∑m(hθ(x(i)−y(i)))2
其中:
hθ(x)=θTX=θ0x0+θ1x1+⋯+θnxn
正规方程式通过求解下面的方程来找出使得代价函数最小的参数:
∂θj∂j(θj)=0
设有m个训练实例,每个实例有n个特征,则训练实例集为:
X=⎣⎡x01…x0m………xn1…xnm⎦⎤
其中xj(i)表示第i个实例第j个特征
参数特征为:θ=[θ0,θ1,…,θn]T
故代价函数为:
J(θ0,θ1,…,θn)=2m1(X∗θ−Y)T(X∗θ−Y)=2m1(YTY−YTXθ−θTXTY+θTXTXθ)
进行求导,等价于如下的形式:
2m1(∂θ∂YTY−∂θ∂YTXθ−∂θ∂θTXTY+∂θ∂θTXTXθ)
-
其中第一项:
∂θ∂YTY=0
-
第二项
YTXθ=[y(1),y(2),…,y(m)]X=⎣⎡x01…x0m………xn1…xnm⎦⎤[θ0,θ1,…,θn]T=(x0(1)y(1)+⋯+x0(m)y(m))θ0+(x1(1)y(1)+⋯+x1(m)y(m))θ1+⋯+(xn(1)y(1)+⋯+xn(m)y(m))θn
该矩阵求导,有:
∂θ∂YTXθ=⎣⎢⎡∂θ0∂YTXθ…∂θn∂YTXθ⎦⎥⎤=⎣⎢⎡x0(1)y(1)+⋯+x0(m)y(m)…xn(1)y(1)+⋯+xn(m)y(m)⎦⎥⎤=XTY
- 第三项:
θTXTY=[θ0,θ1,…,θn]⎣⎢⎡x0(1)…x0(m)………xn(1)…xn(m)⎦⎥⎤[y(1),y(2),…,y(m)]T=(x0(1)θ0+⋯+xn(1)θn)y(1)+(x0(2)θ0+⋯+xn(2)θn)y(2)+⋯+(x0(m)θ0+⋯+xn(m)θn)y(m)
求导得:
∂θ∂θTXTY=⎣⎢⎡∂θ0∂θTXTY…∂θn∂θTXTY⎦⎥⎤=⎣⎢⎡x0(1)y(1)+⋯+x0(m)y(m)…xn(1)y(1)+⋯+xn(m)y(m)⎦⎥⎤=XTY
- 第四项:
θTXTX=(XTX)(θ0(2)+θ1(2)+⋯+θn(2))
其中θTXTX为标量,可看成一个常数
故求导得:
∂θ∂θTXTXθ=⎣⎢⎡∂θ0∂θTXTXθ…∂θn∂θTXTXθ⎦⎥⎤=2(XTX)⎣⎡θ0…θn⎦⎤=2XTXθ
综上,正规方程为:
2m1(−2XTY+2XTXθ)=0
最终可得特征参数的表示为:
θ=(XTX)−1XTY
算法转化为Python语言:
from numpy.linalg import inv
from numpy import dot,transpose
x=[[1,6,2],[1,8,1],[1,10,0],[1,14,2],[1,18,0]]
y=[[7],[9],[13],[17.5],[18]]
print(dot(inv(dot(transpose(x),x)),dot(transpose(x),y)))
[[1.1875 ]
[1.01041667]
[0.39583333]]
这样就得到了需要拟合多元直线的参数向量。
二、梯度下降算法
梯度下降算法(批量梯度下降)也适用于求线性回归的拟合曲线,我们设拟合的曲线为:
hθ(x)=θ0+θ1x1+θ2x2
那么我们的误差函数设为:$ J(\theta)= \frac{1}{2}\sum_{i=1}m(h_{\theta}(x{i})-y{i})2 $,要求误差函数的极小值,我们引入了梯度下降算法的概念。
梯度下降原理:将函数比作一座山,我们站在某个山坡上,往四周看,从哪个方向向下走一小步,能够下降的最快。

由之前所述,求θ系数的问题演变成了求J(θ)的极小值问题,这里使用梯度下降法。而梯度下降法中的梯度方向由J(θ)对θ的偏导数确定,由于求的是极小值,因此梯度方向是偏导数的反方向。
∂θi∂J(θ)=∂θi∂21(hθ(x)−y)2
=2∗21(hθ(x)−y)∂θi∂(hθ(x)−y)
=(hθ(x)−y)∂θi∂(θ0+θ1x1+θ2x2+⋯+θnxn−y)
(除了θixi项,其余项均为无关项)
所以,上式为:
=(hθ(x)−y)∂θi∂θixi
=(hθ(x)−y)xi
代入公式,最后可以得出关于θ的表达式:
θi=θi−α(hθ(x)−y)xi
=θi−αi=1∑m(hθ(x(i))−y(i))xi(i)
使用梯度下降算法的步骤:
1)对θ赋初始值,这个值可以是随机的,也可以让θ是一个全零的向量。
2)改变θ的值,使得目标损失函数J(θ)按梯度下降的方向进行减少。$\theta=\theta-\alpha\frac{\partial J(\theta)}{\partial \theta} $
其中α为学习率或步长,需要人为指定,若过大会导致震荡即不收敛,若过小收敛速度会很慢。
3)当下降的高度小于某个定义的值,则停止下降。
python语言实现:
import numpy as np
import matplotlib.pyplot as plt
from pylab import mpl
# 构造训练数据
x = np.arange(0,10,0.2)
m=len(x)
x0 = np.full(m,1)
input_data = np.vstack([x0,x]).T
y= 2*x+5+np.random.randn(m)
plt.scatter(x,y)
plt.xlabel("x")
plt.ylabel("y")
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
plt.title("散点图")
plt.show()

#设置终止条件
loop_max = 10000
epsilon = 1e-3
count = 0 #循环次数
finish = 0 #终止次数
#初始权值theta
theta = np.random.randn(2)
error = np.zeros(2)
alpha = 0.001 # 学习速率
while count < loop_max:
count +=1
sum_m = np.zeros(2)
for i in range(m):
dif = (np.dot(theta,input_data[i])-y[i])*input_data[i]
sum_m = sum_m + dif # 当alpha取值过大时,sum_m会在迭代过程中溢出
theta = theta - alpha * sum_m #当alpha取值过大时,会导致振荡
#判断是否已经收敛
if np.linalg.norm(theta-error) < epsilon:
finish = 1
break
else:
error = theta
plt.scatter(x,y)
plt.plot(x,theta[1]*x+theta[0],'red')
plt.xlabel("x")
plt.ylabel("y")
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
plt.title("拟合图")
plt.show()

三、最小二乘法和梯度下降的区别

总结:在数据量少时可以选择最小二乘法,在数据量大时,选择梯度下降拟合函数更加适宜。