本文主要是介绍梯度下降法求解线性回归,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 线性回归
- 损失函数
- 平均绝对误差(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))/nreturn 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))/nreturn 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 * yreturn 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))/nreturn msedef gradient_w(X,y,z):# 梯度计算gradient = 2*np.dot(X.T, (z- y)) / y.shape[0]return gradientdef gradient_b(X,y,z):# 梯度计算one = np.ones(y.shape[0])gradient = 2*np.dot(one, (z- y)) / y.shape[0]return gradientdef gradient_descent():w = 0 # 初始参数为 0b = 0 # 初始参数为 0lr = 0.000000001 # 设置合理学习率num_iter = 300 # 设置合理迭代次数l_list = [] # 保存损失函数值for i in range(num_iter): # 梯度下降迭代z=x*w+bw=w-gradient_w(x,y,z)*lrb=b-gradient_b(x,y,z)*lrl = mse_value(y,z) # 计算损失函数值l_list.append(l)return w, b
#w, b,l_y=gradient_descent()
绘制损失函数如下,发现曲线在迭代次数为300时趋于平缓,得合理迭代次数为300
这篇关于梯度下降法求解线性回归的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!