一元線性迴歸是數據挖掘的基礎模型,其中包含了非常重要的數學回歸的概念,是學習多元迴歸,廣義線性迴歸的基礎。本文主要講解1)基礎原理2)數學推導3)R語言演示,來介紹一元線性迴歸。
整體思路:
根據已知點求一條直線,希望直線與各個點距離之和爲最小,根據最小二乘法算出最小時直線的參數。
一、基礎原理
例1 假設你想計算匹薩的價格。雖然看看菜單就知道了,不過也可以用機器學習方法建一個線性迴歸模
型,通過分析匹薩的直徑與價格的數據的線性關係,來預測任意直徑匹薩的價格。
直徑(英寸) | 價格(美元) |
---|---|
6 | 7 |
8 | 9 |
10 | 13 |
14 | 17.5 |
18 | 18 |
畫出散點圖
import matplotlib.pyplot as plt
import numpy as np
x=np.array([6,8,10,14,18])
y=np.array([7,9,13,17.5,18])
plt.scatter(x,y)
plt.show()
二 數學推導1
求的最小值?,這裏稱爲損失函數.
求解過程:
$
\frac{\partial J(\theta)}{\partial a}=\frac{1}{2M}\sum\limits_{i=1}m2(y_i-a-bx_i)(-1)=-\frac{1}{M}\sum\limits_{i=1}m(y_i-a-bx_i)=-\frac{1}{M}(\sum\limits_{i=1}my_i-na-b\sum\limits_{i=1}mx_i)
$
$
\frac{\partial J(\theta)}{\partial b}=\frac{1}{2M}\sum\limits_{i=1}m2(y_i-a-bx_i)(-x_i)=-\frac{1}{M}\sum\limits_{i=1}m(y_i-a-bx_i)x_i
$
$
=-\frac{1}{M}\sum\limits_{i=1}m(x_iy_i-ax_i-bx_i2)=-\frac{1}{M}(\sum\limits_{i=1}mx_iy_i-a\sum\limits_{i=1}mx_i-b\sum\limits_{i=1}mx_i2)=0
$
即:
$
\left{\begin{array}{l}
\sum\limits_{i=1}my_i-ma-b\sum\limits_{i=1}mx_i=0\
\sum\limits_{i=1}mx_iy_i-a\sum\limits_{i=1}mx_i-b\sum\limits_{i=1}mx_i2=0
\end{array}\right.
$
解得:
$
a=\frac{\sum\limits_{i=1}my_i-b\sum\limits_{i=1}mx_i}{m}
$
$
b=\frac{m\sum\limits_{i=1}mx_iy_i-\sum\limits_{i=1}my_i\sum\limits_{i=1}mx_i}{m\sum\limits_{i=1}mx_i2-(\sum\limits_{i=1}mx_i)^2}
$
注: 上式可以寫成
$
\left{\begin{array}{l}
a=\bar{y}-b\bar{x}\
b=\frac{\sum\limits_{i=1}^m(x_i-\bar x)(y_i-\bar{y})}{\sum\limits_{i=1}m(x_i-\bar{x})2}
\end{array}\right.
$
由下面可知,a,b的值還可寫成
$
\left{\begin{array}{l}
a=\bar{y}-b\bar{x}\
b=\frac{cov(x,y)}{var(x)}
\end{array}\right.
$
利用公式
$
\left{\begin{array}{l}
a=\bar{y}-b\bar{x}\
b=\frac{cov(x,y)}{var(x)}
\end{array}\right.
$可得
xbar = (6+8+10+14+18)/5
ybar = (7 + 9 + 13 + 17.5 + 18) / 5
cov = ((6 - xbar) * (7 - ybar) + (8 - xbar) * (9 - ybar) + (10 - xbar) *(13 - ybar) +(14 - xbar) * (17.5 - ybar) + (18 - xbar) * (18 - ybar)) / 4
var_x=((6-xbar)**2+(8-xbar)**2+(10-xbar)**2+(14-xbar)**2+(18-xbar)**2)/4
print(cov,var_x)
結果爲22.65,23.2.
Numpy裏面有cov方法可以直接計算協方差
import numpy as np
xfc=np.cov([6, 8, 10, 14, 18], [7, 9, 13, 17.5, 18])
xfc
結果爲
array([[23.2 , 22.65],
[22.65, 24.3 ]])
最後得到,,畫出圖形如下:
import matplotlib.pyplot as plt
import numpy as np
x=np.array([6,8,10,14,18])
y=np.array([7,9,13,17.5,18])
a=1.965;b=0.976
plt.scatter(x,y)
x1=np.linspace(5,19,100)
y1=a+b*x1
plt.plot(x1,y1)
plt.show()
在看一個例子.
例2 數據如下:
x,y
8.8,7.55
9.9,7.95
10.75,8.55
12.3,9.45
15.65,13.25
16.55,12.0
13.6,11.9
11.05,11.35
9.6,9.0
8.3,9.05
8.1,10.7
10.5,10.25
14.5,12.55
16.35,13.15
17.45,14.7
19.0,13.7
19.6,14.4
20.9,16.6
21.5,17.75
22.4,18.1
23.65,18.75
24.9,19.6
25.8,20.3
26.45,20.7
28.15,21.55
28.55,21.4
29.3,21.95
29.15,21.0
28.35,19.95
26.9,19.0
26.05,18.9
25.05,17.95
23.6,16.8
22.05,15.55
21.85,16.1
23.0,17.8
19.0,16.6
18.8,15.55
19.3,15.1
15.15,11.9
12.05,10.8
12.75,12.7
13.8,10.65
6.5,5.85
9.2,6.4
10.9,7.25
12.35,8.55
13.85,9.0
16.6,10.15
17.4,10.85
18.25,12.15
16.45,14.55
20.85,15.75
21.25,15.15
22.7,15.35
24.45,16.45
26.75,16.95
28.2,19.15
24.85,20.8
20.45,13.5
29.95,20.35
31.45,23.2
31.1,21.4
30.75,22.3
29.65,23.45
28.9,23.35
27.8,22.3
求出a,b的代碼如下:
res=[]
with open('d:/shuju1.txt','r') as f:
lines=f.readlines()
for line in lines:
res.append(list(map(float,line.strip('\n').split(','))))
res=np.array(res)
xfc=np.cov(res[:,0],res[:,1])
x_mean=np.mean(res[:,0])
y_mean=np.mean(res[:,1])
b=xfc[0][1]/xfc[0][0]
a=y_mean-b*x_mean
畫出圖如下:
plt.scatter(res[:,0],res[:,1])
x1=np.linspace(np.min(res[:,0])+0.5,np.max(res[:,0])+0.5,100)
y1=a+b*x1
plt.plot(x1,y1)
plt.show()
三 數學推導2
令,
對求一階偏導得到梯度,
$
\begin{array}{lll}
\nabla_{\theta}J(\theta)&=&\nabla_{\theta}(\frac{1}{2}(y-x\theta)^T(y-x\theta))\
&=&\nabla_\theta(\frac{1}{2M}(yT-\thetaTx^T)(y-x\theta))\
&=&\frac{1}{2M}\nabla_\theta(yTy-yTx\theta-\thetaTxTy+\thetaTxTx\theta)\
&=&\frac{1}{2M}(-(yTx)T-xTy+2xTx\theta)\
&=&\frac{1}{M}(xTx\theta-xTy)=0
\end{array}
$
解得: ,這個結果對於多元線性迴歸也適用.
對於上面的例1,有
import matplotlib.pyplot as plt
import numpy as np
x=np.array([6,8,10,14,18])
y=np.array([7,9,13,17.5,18])
X=np.matrix([[1,6],[1,8],[1,10],[1,14],[1,18]])
Y=np.matrix([[7,9,13,17.5,18]]).T
theta=(X.T*X).I*(X.T*Y)
theta
結果爲:
matrix([[1.96551724],
[0.9762931 ]])
對於上面的例2,有
res=[]
with open('d:/shuju1.txt','r') as f:
lines=f.readlines()
for line in lines:
res.append(list(map(float,line.strip('\n').split(','))))
res=np.array(res)
t=np.matrix([[1]*67])
X=np.hstack((t.T.A,np.matrix(res[:,0]).T.A))
X=np.matrix(X)
Y=np.matrix(res[:,1]).T.A
Y=np.matrix(Y)
(X.T*X).I*(X.T*Y)
結果爲:
matrix([[2.10892056],
[0.65771558]])
3.1 梯度下降法
最小二乘法的弊端:最小二乘法可以一步到位,直接算出a和b,但他是有前提的,需要求,並且計算量大.
下面討論不適用最小二乘法,而是梯度下降法來實現線性迴歸.
討論損失函數.
$
\frac{\partial J(\theta)}{\partial a}=\frac{1}{2M}\sum\limits_{i=1}m2(y_i-a-bx_i)(-1)=-\frac{1}{M}\sum\limits_{i=1}m(y_i-a-bx_i)
$
$
\frac{\partial J(\theta)}{\partial b}=\frac{1}{2M}\sum\limits_{i=1}m2(y_i-a-bx_i)(-x_i)=-\frac{1}{M}\sum\limits_{i=1}m(y_i-a-bx_i)x_i
$
我們要去找這個方程的最小值,最小值怎麼求?按數學的求法就是最小二乘法唄,但是大家可以直觀的想一下,很多地方都會用一個碗來形容,那我也找個碗來解釋吧。
大家把這個Loss函數想象成這個碗,而我們要求的最小值就是碗底。假設我們現在不能用最小二乘法求極小值,但是我們的計算機的計算能量很強,我們可以用計算量換結果,不管我們位於這個碗的什麼位置,只要我們想去碗底,就要往下走。
往下走????????這個下不就是往梯度方向走嗎?
梯度不就是上面那兩個公式唄。現在梯度有了,那每次滑多遠呢,一滑劃過頭了不久白算半天了嗎,所以還得定義步長,用來表示每次滑多長。這樣我們就能每次向下走一點點,再定義一個迭代值用來表示滑多少次,這樣我們就能慢慢的一點點的靠近最小值了,不出意外還是能距離最優值很近的。
具體實現如下:
每次向下滑要慢慢滑,就是要個步長,我們定義爲learning_rate,往往很小的一個值。
向下滑動的次數,就是迭代的次數,我定義爲num_iter,相對learning_rate往往很大。
定義好這兩個,我們就可以一邊求梯度,一邊向下滑了。就是去更新a和b。
對於上面的例2,如下圖所示,過程如下:
#step 1 get train data
res=[]
with open('d:/shuju1.txt','r') as f:
lines=f.readlines()
for line in lines:
res.append(list(map(float,line.strip('\n').split(','))))
res=np.array(res)
data=res
#step 2 define hyperparamters
#learning_rate is used to update gradient
#define the number that will iterate
#define y=a+bx
learning_rate=0.001
initial_b=0
initial_a=0
num_iter=1000
#step 3 optimize
[a,b]=optimizer(data,initial_a,initial_b,learning_rate,num_iter)
結果爲:
(0.24852432182905326, 0.7411262595522877)
以下是用到的優化器函數,優化器就是去做梯度下降:
def optimizer(data,init_a,init_b,learning_rate,num_iter):
b=init_b
a=init_a
#gradient descent
for i in range(num_iter):
a,b=compute_gradient(a,b,data,learning_rate)
#if i%100==0:
#print('iter{0}:error={1}'.format(i,computer_error(a,b,data)))
return [a,b]
裏面的compute_gradient方法就是去計算梯度做參數更新.
def compute_gradient(a_current,b_current,data,learning_rate):
a_gradient=0
b_gradient=0
M=float(len(data))
for i in range(len(data)):
x=data[i,0]
y=data[i,1]
a_gradient+= -(1/M)*(y-(a_current+b_current*x))
b_gradient+= -(1/M)*x*(y-(a_current+b_current*x))
new_b=b_current-(learning_rate*b_gradient)
new_a=a_current-(learning_rate*a_gradient)
return [new_a,new_b]
3.2 局部加權線性迴歸
給待測點附近的每個點賦予一定的權重。
損失函數爲:,其中,表示第i個樣本的權重。
局部加權線性迴歸使用”核“來對附近的點賦予更高的權重。核的類型可以自由選擇,最常用的核就是高斯核,高斯覈對應的權重如下:
這樣就有一個只含對角元素的權重矩陣W, 並且點與x 越近,也會越大。這裏的參數k 決定了對附近的點賦予多大的權重,這也是唯一需要考慮的參數。
當k越大,有越多的點被用於訓練迴歸模型;
當k越小,有越少的點用於訓練迴歸模型。
下面給出局部加權線性迴歸的推導過程.這裏,我們換一種寫法.
目標函數定義爲:,這裏設.
我們的目標是最小化cost function:
,
這裏,我們省略中的係數.我們的目標是.
換成線性代數的表述方式:,其中
$
W=\begin{bmatrix}
w^{(1)} & 0 & 0\
0 & w^{(2)} & 0\
\vdots & \ddots & \vdots\
0 & 0 & w^{(m)}\
\end{bmatrix}
$
是mxm維的對角矩陣,
$
X=\begin{bmatrix}
1 & x_1^{(1)} & \cdots & x_{n-1}^{(1)} \
1 & x_1^{(2)} & \cdots & x_{n-1}^{(2)} \
\vdots & \vdots & \ddots & \vdots \
1 & x_1^{(m)} & \cdots & x_{n-1}^{(m)}\
\end{bmatrix}是mxn維的輸入矩陣,
$
,$
Y=\begin{bmatrix}
y{(1)}\y{(2)}\ \vdots \ y^{(m)}\
\end{bmatrix}
$是mx1維的結果,
是nx1維的參數向量.
下面對求偏導.
$
\begin{array}{lll}
\frac{\partial}{\partial \theta}J(\theta)&=&\frac{\partial}{\partial \theta}(X\theta-Y)^TW(X\theta-Y)\
&=&\frac{\partial}{\partial \theta}(\thetaTXTWX\theta-\thetaTXTWY-YTWX\theta+YTWY)\
&=&(XTWX\theta-XTWY)
\end{array}
$,
令,有,即.
上面的例2,代碼如下:
首先獲取數據如下:
import numpy as np
res=[]
with open('d:/shuju1.txt','r') as f:
lines=f.readlines()
for line in lines:
res.append(list(map(float,line.strip('\n').split(','))))
res=np.array(res)
t=np.matrix([[1]*67])
X=np.hstack((t.T.A,np.matrix(res[:,0]).T.A))
X=np.matrix(X)
Y=np.matrix(res[:,1]).T.A
Y=np.matrix(Y)
其次,相關的函數如下:
import copy
def lwlr(testPoint,xArr,yArr,k=1.0):
xMat = np.mat(xArr); yMat = np.mat(yArr)
m = np.shape(xMat)[0]
weights = np.mat(np.eye((m)))
for j in range(m): #next 2 lines create weights matrix
diffMat = testPoint - xMat[j,:] #
weights[j,j] = np.exp(diffMat*diffMat.T/(-2.0*k**2))
xTx = xMat.T * (weights * xMat)
if np.linalg.det(xTx) == 0.0:
print("This matrix is singular, cannot do inverse")
return
ws = xTx.I * (xMat.T * (weights * yMat))
return testPoint * ws
def lwlrTest(testArr,xArr,yArr,k=1.0): #loops over all the data points and applies lwlr to each one
test=copy.copy(testArr)
m = np.shape(testArr)[0]
yHat = np.zeros(m)
for i in range(m):
yHat[i] = lwlr(test[i],xArr,yArr,k)
return yHat
如果要畫出圖形的話,首先要將數據X排序,然後求出對應的預測值,再畫出圖像.
def lwlrTestPlot(xArr,yArr,k=1.0): #same thing as lwlrTest except it sorts X first
yHat = np.zeros(np.shape(yArr)) #easier for plotting
xCopy = copy.copy(np.mat(xArr))
xCopy.sort(0)
for i in range(np.shape(xArr)[0]):
yHat[i] = lwlr(xCopy[i],xArr,yArr,k)
return yHat,xCopy
#畫圖代碼如下
import matplotlib.pyplot as plt
fig=plt.figure(figsize=(15,5))
xs=[1,0.5,0.1]
for i in range(3):
y1,x1=lwlrTestPlot(X[:,1],Y,xs[i])
n="ax"+str(i)
pj=131+i;i+=1
n=fig.add_subplot(pj)
n.plot(x1,y1)
n.scatter(X[:,1].flatten().A[0],Y.flatten().A[0],s=2,c='red')
a=2.10892056;b=0.65771558
x=np.linspace(0,35,100)
y=a+b*x
n.plot(x,y)
plt.show()
附錄
設總體均值爲,總體方差爲,樣本均值爲,則樣本方差爲:
,這裏分母爲n-1是因爲:
,
而定義,則由上面的計算知道,即是的無偏估計.
同理,樣本協方差定義爲
,.