文章目录
- 线性回归
- 损失函数
- 平均绝对误差(MAE)
- 均方误差(MSE)
- 最小二乘法
- 最小二乘法代数推导
- 最小二乘法矩阵推导
- 线性回归 Python 实现
- 线性回归 scikit-learn 实现
- 梯度下降法
- 梯度下降法的原理
- 梯度下降法求解线性回归
线性回归
线性回归,就是已知一系列x和y对应的点,通过求出 y = w x + b y=wx+b y=wx+b(线性,所以是一条直线)去拟合数据点,预测某一个 x 0 x_0 x0对应的 y 0 y_0 y0是多少。
x = np.array([56, 72, 69, 88, 102, 86, 76, 79, 94, 74])
y = np.array([92, 102, 86, 110, 130, 99, 96, 102, 105, 92])
那么,如何求出这条直线?如何判断这条直线对数据的拟合程度好坏?
这里需要引入损失函数。
损失函数
平均绝对误差(MAE)
平均绝对误差(MAE)就是绝对误差的平均值,它的计算公式如下:
MAE
(
y
,
y
^
)
=
1
n
∑
i
=
1
n
∣
y
i
−
y
^
i
∣
(1)
\textrm{MAE}(y, \hat{y} ) = \frac{1}{n}\sum_{i=1}^{n}{|y_{i}-\hat y_{i}|}\tag{1}
MAE(y,y^)=n1i=1∑n∣yi−y^i∣(1)
其中,
y
i
y_{i}
yi 表示真实值,
y
^
i
\hat y_{i}
y^i 表示预测值,
n
n
n 则表示值的个数。MAE 的值越小,说明模型拥有更好的拟合程度。
def mae_value(y_true, y_pred):
n = len(y_true)
mae = sum(np.abs(y_true - y_pred))/n
return mae
均方误差(MSE)
均方误差(MSE)表示误差的平方的期望值,它的计算公式如下:
MSE
(
y
,
y
^
)
=
1
n
∑
i
=
1
n
(
y
i
−
y
i
^
)
2
(2)
\textrm{MSE}(y, \hat{y} ) = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y_i})^{2}\tag{2}
MSE(y,y^)=n1i=1∑n(yi−yi^)2(2)
其中,
y
i
y_{i}
yi 表示真实值,
y
^
i
\hat y_{i}
y^i 表示预测值,
n
n
n 则表示值的个数。MSE 的值越小,说明预测模型拥有更好的精确度。
def mse_value(y_true, y_pred):
n = len(y_true)
mse = sum(np.square(y_true - y_pred))/n
return mse
在这里,我们已经知道了如何求损失,但是如何才能让损失最小呢?
最小二乘法
最小二乘法是用于求解线性回归拟合参数 w w w 的一种常用方法。最小二乘法中的「二乘」代表上面的均方误差(MSE),即均方误差最小。
最小二乘法代数推导
均方误差函数为:
y
i
^
=
w
x
i
+
b
f
=
∑
i
=
1
n
(
y
i
−
(
w
x
i
+
b
)
)
2
(3)
\hat{y_i}=wx_i+b\\ f = \sum\limits_{i = 1}^n {{{(y_{i}-(wx_{i}+b))}}^2} \tag{3}
yi^=wxi+bf=i=1∑n(yi−(wxi+b))2(3)
这里要求f的最小值,故求偏导如下:
∂
f
∂
b
=
∂
∑
i
=
1
n
(
y
i
2
−
2
y
i
(
w
x
i
+
b
)
+
(
w
x
i
+
b
)
2
)
∂
b
=
∑
i
=
1
n
(
−
2
y
i
+
2
w
x
i
+
2
b
)
=
−
2
(
∑
i
=
1
n
y
i
−
n
b
−
w
∑
i
=
1
n
x
i
)
(4a)
\frac{\partial f}{\partial b}=\frac{\partial \sum_{i=1}^n(y_i^2-2y_i(wx_i+b)+(wx_i+b)^2)}{\partial b} \\=\sum_{i=1}^n(-2y_i+2wx_i+2b) \\=-2(\sum_{i=1}^{n}{y_i}-nb-w\sum_{i=1}^{n}{x_i}) \tag{4a}
∂b∂f=∂b∂∑i=1n(yi2−2yi(wxi+b)+(wxi+b)2)=i=1∑n(−2yi+2wxi+2b)=−2(i=1∑nyi−nb−wi=1∑nxi)(4a)
∂
f
∂
w
=
∂
∑
i
=
1
n
(
y
i
2
−
2
y
i
(
w
x
i
+
b
)
+
(
w
x
i
+
b
)
2
)
∂
w
=
∑
i
=
1
n
(
−
2
y
i
x
i
+
2
w
x
i
2
+
2
x
i
b
)
=
−
2
(
∑
i
=
1
n
x
i
y
i
−
b
∑
i
=
1
n
x
i
−
w
∑
i
=
1
n
x
i
2
)
(4b)
\frac{\partial f}{\partial w}=\frac{\partial \sum_{i=1}^n(y_i^2-2y_i(wx_i+b)+(wx_i+b)^2)}{\partial w} \\= \sum_{i=1}^n(-2y_ix_i+2wx_i^2+2x_ib) \\=-2(\sum_{i=1}^{n}{x_iy_i}-b\sum_{i=1}^{n}{x_i}-w\sum_{i=1}^{n}{x_i}^2) \tag{4b}
∂w∂f=∂w∂∑i=1n(yi2−2yi(wxi+b)+(wxi+b)2)=i=1∑n(−2yixi+2wxi2+2xib)=−2(i=1∑nxiyi−bi=1∑nxi−wi=1∑nxi2)(4b)
令
∂
f
∂
b
=
0
\frac{\partial f}{\partial b}=0
∂b∂f=0 以及
∂
f
∂
w
=
0
\frac{\partial f}{\partial w}=0
∂w∂f=0,解得:
w
=
n
∑
x
i
y
i
−
∑
x
i
∑
y
i
n
∑
x
i
2
−
(
∑
x
i
)
2
(5b)
w=\frac {n\sum_{}^{}{x_iy_i}-\sum_{}^{}{x_i}\sum_{}^{}{y_i}} {n\sum_{}^{}{x_i}^2-(\sum_{}^{}{x_i})^2} \tag{5b}
w=n∑xi2−(∑xi)2n∑xiyi−∑xi∑yi(5b)
b
=
∑
x
i
2
∑
y
i
−
∑
x
i
∑
x
i
y
i
n
∑
x
i
2
−
(
∑
x
i
)
2
(5b)
b=\frac {\sum_{}^{}{x_i}^2\sum_{}^{}{y_i}-\sum_{}^{}{x_i}\sum_{}^{}{x_iy_i}} {n\sum_{}^{}{x_i}^2-(\sum_{}^{}{x_i})^2} \tag{5b}
b=n∑xi2−(∑xi)2∑xi2∑yi−∑xi∑xiyi(5b)
已经求出了平方损失函数最小时对应的
w
w
w、
b
b
b 参数值,这也就是最佳拟合直线。
def w_calculator(x, y):
n = len(x)
w = (n*sum(x*y) - sum(x)*sum(y))/(n*sum(x*x) - sum(x)*sum(x))
b = (sum(x*x)*sum(y) - sum(x)*sum(x*y))/(n*sum(x*x)-sum(x)*sum(x))
return w,b
w_calculator(x,y)
# (0.7545842753077117, 41.33509168550616)
最小二乘法矩阵推导
一元线性函数的表达式为
y
(
x
,
w
)
=
w
0
+
w
1
x
y(x, w) = w_0 + w_1x
y(x,w)=w0+w1x(原式子里的w设为
w
1
w_1
w1,b设为
w
0
w_0
w0),表达成矩阵形式为:
[
1
,
x
1
1
,
x
2
⋯
1
,
x
9
1
,
x
10
]
[
w
0
w
1
]
=
[
y
1
y
2
⋯
y
9
y
10
]
⇒
[
1
,
56
1
,
72
⋯
1
,
94
1
,
74
]
[
w
0
w
1
]
=
[
92
102
⋯
105
92
]
\left[ \begin{array}{c}{1, x_{1}} \\ {1, x_{2}} \\ {\cdots} \\ {1, x_{9}} \\ {1, x_{10}}\end{array}\right] \left[ \begin{array}{c}{w_{0}} \\ {w_{1}}\end{array}\right] = \left[ \begin{array}{c}{y_{1}} \\ {y_{2}} \\ {\cdots} \\ {y_{9}} \\ {y_{10}}\end{array}\right] \Rightarrow \left[ \begin{array}{c}{1,56} \\ {1,72} \\ {\cdots} \\ {1,94} \\ {1,74}\end{array}\right] \left[ \begin{array}{c}{w_{0}} \\ {w_{1}}\end{array}\right]=\left[ \begin{array}{c}{92} \\ {102} \\ {\cdots} \\ {105} \\ {92}\end{array}\right]
1,x11,x2⋯1,x91,x10
[w0w1]=
y1y2⋯y9y10
⇒
1,561,72⋯1,941,74
[w0w1]=
92102⋯10592
y
(
x
,
w
)
=
X
W
(6)
y(x, w) = XW \tag{6}
y(x,w)=XW(6)
(
6
)
(6)
(6) 式中,
W
W
W 为
[
w
0
w
1
]
\begin{bmatrix}w_{0} \\ w_{1} \end{bmatrix}
[w0w1],而
X
X
X 则是
[
X
1
,
x
]
[X_1,x]
[X1,x]
X
1
=
[
1
1
⋯
1
1
,
]
X_1= \begin{bmatrix}1 \\ 1 \\ \cdots \\ 1 \\ 1, \end{bmatrix}
X1=
11⋯11,
,
x
=
[
x
1
x
2
⋯
x
9
x
10
]
x= \begin{bmatrix}x_{1} \\ x_{2} \\ \cdots \\ x_{9} \\ x_{10} \end{bmatrix}
x=
x1x2⋯x9x10
)矩阵。然后,平方损失函数为:
f
=
∑
i
=
1
n
(
y
i
−
(
w
0
+
w
1
x
i
)
)
2
=
(
y
−
X
W
)
T
(
y
−
X
W
)
(7)
f = \sum\limits_{i = 1}^n {{{(y_{i}-(w_0 + w_1x_{i}))}}}^2 =(y-XW)^T(y-XW)\tag{7}
f=i=1∑n(yi−(w0+w1xi))2=(y−XW)T(y−XW)(7)
f
=
y
T
y
−
y
T
(
X
W
)
−
(
X
W
)
T
y
+
(
X
W
)
T
(
X
W
)
(8)
f = y^{T}y - y^{T}(XW) - (XW)^{T}y + (XW)^{T}(XW) \tag{8}
f=yTy−yT(XW)−(XW)Ty+(XW)T(XW)(8)
在该公式中
y
y
y 与
X
W
XW
XW 皆为相同形式的
(
m
,
1
)
(m,1)
(m,1) 矩阵,由此两者相乘属于线性关系,所以等价转换如下:
f
=
y
T
y
−
(
X
W
)
T
y
−
(
X
W
)
T
y
+
(
X
W
)
T
(
X
W
)
=
y
T
y
−
2
(
X
W
)
T
y
+
(
X
W
)
T
(
X
W
)
(9)
f = y^{T}y - (XW)^{T}y - (XW)^{T}y + (XW)^{T}(XW)\\ = y^{T}y - 2 (XW)^{T}y + (XW)^{T}(XW) \tag{9}
f=yTy−(XW)Ty−(XW)Ty+(XW)T(XW)=yTy−2(XW)Ty+(XW)T(XW)(9)
W
T
的偏导数是
W
,
T
r
(
A
B
)
对
A
或
B
的偏导数是
B
或
A
W^T的偏导数是W,Tr(AB)对A或B的偏导数是B或A
WT的偏导数是W,Tr(AB)对A或B的偏导数是B或A
对矩阵求偏导得:
∂
f
∂
W
=
2
X
T
X
W
−
2
X
T
y
=
0
(10)
\frac{\partial f}{\partial W}=2X^TXW-2X^Ty=0 \tag{10}
∂W∂f=2XTXW−2XTy=0(10)
当矩阵
X
T
X
X^TX
XTX 满秩时,
(
X
T
X
)
−
1
X
T
X
=
E
(X^TX)^{-1}X^TX=E
(XTX)−1XTX=E,且
E
W
=
W
EW=W
EW=W。所以有
(
X
T
X
)
−
1
X
T
X
W
=
(
X
T
X
)
−
1
X
T
y
(X^TX)^{-1}X^TXW=(X^TX)^{-1}X^Ty
(XTX)−1XTXW=(XTX)−1XTy,并最终得到:
W
=
(
X
T
X
)
−
1
X
T
y
(11)
W=(X^TX)^{-1}X^Ty \tag{11}
W=(XTX)−1XTy(11)
def w_matrix(x, y):
w = (x.T * x).I * x.T * y
return w
x= [[1, i] for i in x]
x = np.matrix(x)
y = np.matrix(y)
w_matrix(x, y.reshape(-1, 1))
# matrix([[41.33509169],
# [ 0.75458428]])
线性回归 Python 实现
以最小二乘法代数方法为例
w,b=w_calculator(x, y)
# 求损失
loss = mae_value(y,wx+b)
在上述例子中,得到的loss为447.69153479025357
接下来,我们尝试将拟合得到的直线绘制到原图中:
x_temp = np.linspace(50, 120, 100) # 绘制直线生成的临时点
plt.scatter(x, y)
plt.plot(x_temp, x_temp*w + b, 'r')
此时,想要预估x为100对应的y只需要代入公式
y
=
w
x
+
b
y=wx+b
y=wx+b即可得到:116.79351921627732
线性回归 scikit-learn 实现
scikit-learn 把线性回归的过程整合到了LinearRegression() 类里,只需要填入需要的参数即可。
sklearn.linear_model.LinearRegression(fit_intercept=True, normalize=False, copy_X=True, n_jobs=1) - fit_intercept: 默认为 True,计算截距项。 - normalize: 默认为 False,不针对数据进行标准化处理。 - copy_X: 默认为 True,即使用数据的副本进行操作,防止影响原数据。 - n_jobs: 计算时的作业数量。默认为 1,若为 -1 则使用全部 CPU 参与运算。
from sklearn.linear_model import LinearRegression
# 定义线性回归模型
model = LinearRegression()
model.fit(x.reshape(len(x), 1), y) # 训练, reshape 操作把数据处理成 fit 能接受的形状
# 得到模型拟合参数
model.intercept_, model.coef_
得到的结果(41.33509168550615, array([0.75458428]))和上述python得到结果一致。
# 想要预测x=100对应的y
model.predict([[100]])
梯度下降法
为了求解 w w w的极小值还可以引入一种叫「梯度下降」的求解方法。梯度下降法是一种十分常用且经典的最优化算法,通过这种方法我们就能快速找到函数的最小值。
梯度下降法的原理
什么是「梯度」?梯度是一个向量,它表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为该梯度的模)。对于一元函数而言,梯度就是指在某一点的导数。而对于多元函数而言,梯度就是指在某一点的偏导数组成的向量。
函数在沿梯度方向变化最快,所以「梯度下降法」的核心就是,我们沿着梯度下降方向去寻找损失函数的极小值(梯度的反方向)。过程如下图所示。
这里的损失函数依然是均方误差MSE:
J
=
f
n
=
1
n
∑
i
=
1
n
(
y
i
−
y
i
^
)
2
(1)
J= \frac fn= \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y_i})^{2}\tag{1}
J=nf=n1i=1∑n(yi−yi^)2(1)
J
=
1
n
∑
i
=
1
n
(
y
i
−
(
w
x
i
+
b
)
)
2
(2)
J= \frac{1}{n} \sum_{i=1}^{n} (y_i -(wx_i+b))^{2}\tag{2}
J=n1i=1∑n(yi−(wxi+b))2(2)
求解参数和截距项对应的梯度:
∂
J
∂
w
=
−
2
n
∑
i
=
1
n
x
i
(
y
i
−
(
w
x
i
+
b
)
)
(3a)
\frac{\partial J}{\partial w}= -\frac 2n \sum_{i=1}^{n} x_i(y_i -(wx_i+b))\tag{3a}
∂w∂J=−n2i=1∑nxi(yi−(wxi+b))(3a)
∂
J
∂
b
=
−
2
n
∑
i
=
1
n
(
y
i
−
(
w
x
i
+
b
)
)
(3b)
\frac{\partial J}{\partial b}=-\frac 2n \sum_{i=1}^{n} (y_i -(wx_i+b))\tag{3b}
∂b∂J=−n2i=1∑n(yi−(wxi+b))(3b)
当我们得到梯度的方向,然后乘以一个常数
α
\alpha
α ,就可以得到每次梯度下降的步长(上图箭头的长度)。最后,通过多次迭代,找到梯度变化很小的点,也就对应着损失函数的极小值了。其中,常数
α
\alpha
α往往也被称之为学习率 Learning Rate。
在下文中用lr(Learning Rate)代表常数 α \alpha α。
每次迭代,用w和b的初始值去减掉梯度*lr
w
=
w
−
∂
J
∂
w
∗
l
r
(4a)
w=w-\frac{\partial J}{\partial w}*lr\tag{4a}
w=w−∂w∂J∗lr(4a)
b
=
b
−
∂
J
∂
b
∗
l
r
(4b)
b=b-\frac{\partial J}{\partial b}*lr\tag{4b}
b=b−∂b∂J∗lr(4b)
梯度下降法求解线性回归
这里的x、y为数据点。
import numpy as np
import pandas as pd
def mse_value(y_true, y_pred):
n = len(y_true)
mse = sum(np.square(y_true - y_pred))/n
return mse
def gradient_w(X,y,z):
# 梯度计算
gradient = 2*np.dot(X.T, (z- y)) / y.shape[0]
return gradient
def gradient_b(X,y,z):
# 梯度计算
one = np.ones(y.shape[0])
gradient = 2*np.dot(one, (z- y)) / y.shape[0]
return gradient
def gradient_descent():
w = 0 # 初始参数为 0
b = 0 # 初始参数为 0
lr = 0.000000001 # 设置合理学习率
num_iter = 300 # 设置合理迭代次数
l_list = [] # 保存损失函数值
for i in range(num_iter): # 梯度下降迭代
z=x*w+b
w=w-gradient_w(x,y,z)*lr
b=b-gradient_b(x,y,z)*lr
l = mse_value(y,z) # 计算损失函数值
l_list.append(l)
return w, b
#w, b,l_y=gradient_descent()
绘制损失函数如下,发现曲线在迭代次数为300时趋于平缓,得合理迭代次数为300