原文連接:cuijiahua.com/blog/2017/1…php
前面的文章介紹了不少分類算法,分類的目標變量是標稱型數據,而本文將會對連續型的數據作出預測。主要講解簡單的線性迴歸和局部加權線性迴歸,並經過預測鮑魚年齡的實例進行實戰演練。html
迴歸的目的是預測數值型的目標值。最直接的辦法是依據輸入寫出一個目標值的計算公式。假如你想預測小姐姐男朋友汽車的功率,可能會這麼計算:python
HorsePower = 0.0015 * annualSalary - 0.99 * hoursListeningToPublicRadiogit
寫成中文就是:github
小姐姐男朋友汽車的功率 = 0.0015 * 小姐姐男朋友年薪 - 0.99 * 收聽公共廣播的時間算法
這就是所謂的迴歸方程(regression equation),其中的0.0015和-0.99稱爲迴歸係數(regression weights),求這些迴歸係數的過程就是迴歸。一旦有了這些迴歸係數,再給定輸入,作預測就很是容易了。具體的作法是用迴歸係數乘以輸入值,再將結果所有加在一塊兒,就獲得了預測值。windows
說到迴歸,通常都是指線性迴歸(linear regression),因此本文裏的迴歸和線性迴歸表明同一個意思。線性迴歸意味着能夠將輸入項分別乘以一些常量,再將結果加起來獲得輸出。須要說明的是,存在另外一種成爲非線性迴歸的迴歸模型,該模型不認同上面的作法,好比認爲輸出多是輸入的乘積。這樣,上面的功率計算公式也能夠寫作:bash
HorsePower = 0.0015 * annualSalary / hoursListeningToPublicRadioapp
這就是一個非線性迴歸的例子,本文對此不作深刻討論。機器學習
應該怎麼從一大堆數據裏求出迴歸方程呢?假定輸入數據存放在矩陣X中,結果存放在向量y中:
而回歸係數存放在向量w中:
那麼對於給定的數據x1,即矩陣X的第一列數據,預測結果u1將會經過以下公式給出:
如今的問題是,手裏有數據矩陣X和對應的標籤向量y,怎麼才能找到w呢?一個經常使用的方法就是找出使偏差最小的w。這裏的偏差是指預測u值和真實y值之間的差值,使用該偏差的簡單累加將使得正差值和負差值相互抵消,因此咱們採用平方偏差。
平方偏差和能夠寫作:
用矩陣表示還能夠寫作:
爲啥能這麼變化,記住一個前提:若x爲向量,則默認x爲列向量,x^T爲行向量。將上述提到的數據矩陣X和標籤向量y帶進去,就知道爲什麼這麼變化了。
在繼續推導以前,咱們要先明確一個目的:找到w,使平方偏差和最小。由於咱們認爲平方偏差和越小,說明線性迴歸擬合效果越好。
如今,咱們用矩陣表示的平方偏差和對w進行求導:
若是對於矩陣求不熟悉的,能夠移步這裏:點擊查看
令上述公式等於0,獲得:
w上方的小標記表示,這是當前能夠估計出的w的最優解。從現有數據上估計出的w可能並非數據中的真實w值,因此這裏使用了一個"帽"符號來表示它僅是w的一個最佳估計。
值得注意的是,上述公式中包含逆矩陣,也就是說,這個方程只在逆矩陣存在的時候使用,也便是這個矩陣是一個方陣,而且其行列式不爲0。
述的最佳w求解是統計學中的常見問題,除了矩陣方法外還有不少其餘方法能夠解決。經過調用NumPy庫裏的矩陣方法,咱們能夠僅使用幾行代碼就完成所需功能。該方法也稱做OLS, 意思是「普通小二乘法」(ordinary least squares)。
咱們先看下數據集,數據下載地址:數據集下載
第一列都爲1.0,即x0。第二列爲x1,即x軸數據。第三列爲x2,即y軸數據。首先繪製下數據,看下數據分佈。編寫代碼以下:
# -*- coding:utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np
def loadDataSet(fileName):
""" 函數說明:加載數據 Parameters: fileName - 文件名 Returns: xArr - x數據集 yArr - y數據集 Website: https://www.cuijiahua.com/ Modify: 2017-11-12 """
numFeat = len(open(fileName).readline().split('\t')) - 1
xArr = []; yArr = []
fr = open(fileName)
for line in fr.readlines():
lineArr =[]
curLine = line.strip().split('\t')
for i in range(numFeat):
lineArr.append(float(curLine[i]))
xArr.append(lineArr)
yArr.append(float(curLine[-1]))
return xArr, yArr
def plotDataSet():
""" 函數說明:繪製數據集 Parameters: 無 Returns: 無 Website: https://www.cuijiahua.com/ Modify: 2017-11-12 """
xArr, yArr = loadDataSet('ex0.txt') #加載數據集
n = len(xArr) #數據個數
xcord = []; ycord = [] #樣本點
for i in range(n):
xcord.append(xArr[i][1]); ycord.append(yArr[i]) #樣本點
fig = plt.figure()
ax = fig.add_subplot(111) #添加subplot
ax.scatter(xcord, ycord, s = 20, c = 'blue',alpha = .5) #繪製樣本點
plt.title('DataSet') #繪製title
plt.xlabel('X')
plt.show()
if __name__ == '__main__':
plotDataSet()
複製代碼
運行代碼以下:
經過可視化數據,咱們能夠看到數據的分佈狀況。接下來,讓咱們根據上文中推導的迴歸係數計算方法,求出迴歸係數向量,並根據迴歸係數向量繪製迴歸曲線,編寫代碼以下:
# -*- coding:utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np
def loadDataSet(fileName):
""" 函數說明:加載數據 Parameters: fileName - 文件名 Returns: xArr - x數據集 yArr - y數據集 Website: https://www.cuijiahua.com/ Modify: 2017-11-12 """
numFeat = len(open(fileName).readline().split('\t')) - 1
xArr = []; yArr = []
fr = open(fileName)
for line in fr.readlines():
lineArr =[]
curLine = line.strip().split('\t')
for i in range(numFeat):
lineArr.append(float(curLine[i]))
xArr.append(lineArr)
yArr.append(float(curLine[-1]))
return xArr, yArr
def standRegres(xArr,yArr):
""" 函數說明:計算迴歸係數w Parameters: xArr - x數據集 yArr - y數據集 Returns: ws - 迴歸係數 Website: https://www.cuijiahua.com/ Modify: 2017-11-12 """
xMat = np.mat(xArr); yMat = np.mat(yArr).T
xTx = xMat.T * xMat #根據文中推導的公示計算迴歸係數
if np.linalg.det(xTx) == 0.0:
print("矩陣爲奇異矩陣,不能求逆")
return
ws = xTx.I * (xMat.T*yMat)
return ws
def plotRegression():
""" 函數說明:繪製迴歸曲線和數據點 Parameters: 無 Returns: 無 Website: https://www.cuijiahua.com/ Modify: 2017-11-12 """
xArr, yArr = loadDataSet('ex0.txt') #加載數據集
ws = standRegres(xArr, yArr) #計算迴歸係數
xMat = np.mat(xArr) #建立xMat矩陣
yMat = np.mat(yArr) #建立yMat矩陣
xCopy = xMat.copy() #深拷貝xMat矩陣
xCopy.sort(0) #排序
yHat = xCopy * ws #計算對應的y值
fig = plt.figure()
ax = fig.add_subplot(111) #添加subplot
ax.plot(xCopy[:, 1], yHat, c = 'red') #繪製迴歸曲線
ax.scatter(xMat[:,1].flatten().A[0], yMat.flatten().A[0], s = 20, c = 'blue',alpha = .5) #繪製樣本點
plt.title('DataSet') #繪製title
plt.xlabel('X')
plt.show()
if __name__ == '__main__':
plotRegression()
複製代碼
運行代碼以下:
如何判斷擬合曲線的擬合效果的如何呢?固然,咱們能夠根據本身的經驗進行觀察,除此以外,咱們還可使用corrcoef方法,來比較預測值和真實值的相關性。編寫代碼以下:
# -*- coding:utf-8 -*-
import numpy as np
def loadDataSet(fileName):
""" 函數說明:加載數據 Parameters: fileName - 文件名 Returns: xArr - x數據集 yArr - y數據集 Website: https://www.cuijiahua.com/ Modify: 2017-11-12 """
numFeat = len(open(fileName).readline().split('\t')) - 1
xArr = []; yArr = []
fr = open(fileName)
for line in fr.readlines():
lineArr =[]
curLine = line.strip().split('\t')
for i in range(numFeat):
lineArr.append(float(curLine[i]))
xArr.append(lineArr)
yArr.append(float(curLine[-1]))
return xArr, yArr
def standRegres(xArr,yArr):
""" 函數說明:計算迴歸係數w Parameters: xArr - x數據集 yArr - y數據集 Returns: ws - 迴歸係數 Website: https://www.cuijiahua.com/ Modify: 2017-11-12 """
xMat = np.mat(xArr); yMat = np.mat(yArr).T
xTx = xMat.T * xMat #根據文中推導的公示計算迴歸係數
if np.linalg.det(xTx) == 0.0:
print("矩陣爲奇異矩陣,不能求逆")
return
ws = xTx.I * (xMat.T*yMat)
return ws
if __name__ == '__main__':
xArr, yArr = loadDataSet('ex0.txt') #加載數據集
ws = standRegres(xArr, yArr) #計算迴歸係數
xMat = np.mat(xArr) #建立xMat矩陣
yMat = np.mat(yArr) #建立yMat矩陣
yHat = xMat * ws
print(np.corrcoef(yHat.T, yMat))
複製代碼
運行結果以下:
能夠看到,對角線上的數據是1.0,由於yMat和本身的匹配是完美的,而YHat和yMat的相關係數爲0.98。
最佳擬合直線方法將數據視爲直線進行建模,具備十分不錯的表現。數據當中彷佛還存在其餘的潛在模式。那麼如何才能利用這些模式呢?咱們能夠根據數據來局部調整預測,下面就會介紹這種方法。
二、局部加權線性迴歸 線性迴歸的一個問題是有可能出現欠擬合現象,由於它求的是具備小均方偏差的無偏估 計。顯而易見,若是模型欠擬合將不能取得好的預測效果。因此有些方法容許在估計中引入一 些誤差,從而下降預測的均方偏差。
其中的一個方法是局部加權線性迴歸(Locally Weighted Linear Regression,LWLR)。在該方法中,咱們給待預測點附近的每一個點賦予必定的權重。與kNN同樣,這種算法每次預測均須要事先選取出對應的數據子集。該算法解除迴歸係數W的形式以下:
其中W是一個矩陣,這個公式跟咱們上面推導的公式的區別就在於W,它用來給每一個店賦予權重。
LWLR使用"核"(與支持向量機中的核相似)來對附近的點賦予更高的權重。核的類型能夠自由選擇,最經常使用的核就是高斯核,高斯覈對應的權重以下:
這樣咱們就能夠根據上述公式,編寫局部加權線性迴歸,咱們經過改變k的值,能夠調節迴歸效果,編寫代碼以下:
# -*- coding:utf-8 -*-
from matplotlib.font_manager import FontProperties
import matplotlib.pyplot as plt
import numpy as np
def loadDataSet(fileName):
""" 函數說明:加載數據 Parameters: fileName - 文件名 Returns: xArr - x數據集 yArr - y數據集 Website:https://www.cuijiahua.com/ Modify: 2017-11-12 """
numFeat = len(open(fileName).readline().split('\t')) - 1
xArr = []; yArr = []
fr = open(fileName)
for line in fr.readlines():
lineArr =[]
curLine = line.strip().split('\t')
for i in range(numFeat):
lineArr.append(float(curLine[i]))
xArr.append(lineArr)
yArr.append(float(curLine[-1]))
return xArr, yArr
def plotlwlrRegression():
""" 函數說明:繪製多條局部加權迴歸曲線 Parameters: 無 Returns: 無 Website:https://www.cuijiahua.com/ Modify: 2017-11-15 """
font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)
xArr, yArr = loadDataSet('ex0.txt') #加載數據集
yHat_1 = lwlrTest(xArr, xArr, yArr, 1.0) #根據局部加權線性迴歸計算yHat
yHat_2 = lwlrTest(xArr, xArr, yArr, 0.01) #根據局部加權線性迴歸計算yHat
yHat_3 = lwlrTest(xArr, xArr, yArr, 0.003) #根據局部加權線性迴歸計算yHat
xMat = np.mat(xArr) #建立xMat矩陣
yMat = np.mat(yArr) #建立yMat矩陣
srtInd = xMat[:, 1].argsort(0) #排序,返回索引值
xSort = xMat[srtInd][:,0,:]
fig, axs = plt.subplots(nrows=3, ncols=1,sharex=False, sharey=False, figsize=(10,8))
axs[0].plot(xSort[:, 1], yHat_1[srtInd], c = 'red') #繪製迴歸曲線
axs[1].plot(xSort[:, 1], yHat_2[srtInd], c = 'red') #繪製迴歸曲線
axs[2].plot(xSort[:, 1], yHat_3[srtInd], c = 'red') #繪製迴歸曲線
axs[0].scatter(xMat[:,1].flatten().A[0], yMat.flatten().A[0], s = 20, c = 'blue', alpha = .5) #繪製樣本點
axs[1].scatter(xMat[:,1].flatten().A[0], yMat.flatten().A[0], s = 20, c = 'blue', alpha = .5) #繪製樣本點
axs[2].scatter(xMat[:,1].flatten().A[0], yMat.flatten().A[0], s = 20, c = 'blue', alpha = .5) #繪製樣本點
#設置標題,x軸label,y軸label
axs0_title_text = axs[0].set_title(u'局部加權迴歸曲線,k=1.0',FontProperties=font)
axs1_title_text = axs[1].set_title(u'局部加權迴歸曲線,k=0.01',FontProperties=font)
axs2_title_text = axs[2].set_title(u'局部加權迴歸曲線,k=0.003',FontProperties=font)
plt.setp(axs0_title_text, size=8, weight='bold', color='red')
plt.setp(axs1_title_text, size=8, weight='bold', color='red')
plt.setp(axs2_title_text, size=8, weight='bold', color='red')
plt.xlabel('X')
plt.show()
def lwlr(testPoint, xArr, yArr, k = 1.0):
""" 函數說明:使用局部加權線性迴歸計算迴歸係數w Parameters: testPoint - 測試樣本點 xArr - x數據集 yArr - y數據集 k - 高斯核的k,自定義參數 Returns: ws - 迴歸係數 Website:https://www.cuijiahua.com/ Modify: 2017-11-15 """
xMat = np.mat(xArr); yMat = np.mat(yArr).T
m = np.shape(xMat)[0]
weights = np.mat(np.eye((m))) #建立權重對角矩陣
for j in range(m): #遍歷數據集計算每一個樣本的權重
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("矩陣爲奇異矩陣,不能求逆")
return
ws = xTx.I * (xMat.T * (weights * yMat)) #計算迴歸係數
return testPoint * ws
def lwlrTest(testArr, xArr, yArr, k=1.0):
""" 函數說明:局部加權線性迴歸測試 Parameters: testArr - 測試數據集 xArr - x數據集 yArr - y數據集 k - 高斯核的k,自定義參數 Returns: ws - 迴歸係數 Website: https://www.cuijiahua.com/ Modify: 2017-11-15 """
m = np.shape(testArr)[0] #計算測試數據集大小
yHat = np.zeros(m)
for i in range(m): #對每一個樣本點進行預測
yHat[i] = lwlr(testArr[i],xArr,yArr,k)
return yHat
if __name__ == '__main__':
plotlwlrRegression()
複製代碼
運行結果以下:
能夠看到,當k越小,擬合效果越好。可是當k太小,會出現過擬合的狀況,例如k等於0.003的時候。
接下來,咱們將回歸用於真實數據。在abalone.txt文件中記錄了鮑魚(一種水生物→__→)的年齡,這個數據來自UCI數據集合的數據。鮑魚年齡能夠從鮑魚殼的層數推算獲得。
數據集下載地址:[數據集下載](cuijiahua.com/wp-content/…
數據集的數據以下:
能夠看到,數據集是多維的,因此咱們很難畫出它的分佈狀況。每一個維度數據的表明的含義沒有給出,不過沒有關係,咱們只要知道最後一列的數據是y值就能夠了,最後一列表明的是鮑魚的真實年齡,前面幾列的數據是一些鮑魚的特徵,例如鮑魚殼的層數等。咱們不作數據清理,直接用上全部特徵,測試下咱們的局部加權迴歸。
新建abalone.py文件,添加rssError函數,用於評價最後迴歸結果。編寫代碼以下:
# -*- coding:utf-8 -*-
from matplotlib.font_manager import FontProperties
import matplotlib.pyplot as plt
import numpy as np
def loadDataSet(fileName):
""" 函數說明:加載數據 Parameters: fileName - 文件名 Returns: xArr - x數據集 yArr - y數據集 Website: https://www.cuijiahua.com/ Modify: 2017-11-19 """
numFeat = len(open(fileName).readline().split('\t')) - 1
xArr = []; yArr = []
fr = open(fileName)
for line in fr.readlines():
lineArr =[]
curLine = line.strip().split('\t')
for i in range(numFeat):
lineArr.append(float(curLine[i]))
xArr.append(lineArr)
yArr.append(float(curLine[-1]))
return xArr, yArr
def lwlr(testPoint, xArr, yArr, k = 1.0):
""" 函數說明:使用局部加權線性迴歸計算迴歸係數w Parameters: testPoint - 測試樣本點 xArr - x數據集 yArr - y數據集 k - 高斯核的k,自定義參數 Returns: ws - 迴歸係數 Website: https://www.cuijiahua.com/ Modify: 2017-11-19 """
xMat = np.mat(xArr); yMat = np.mat(yArr).T
m = np.shape(xMat)[0]
weights = np.mat(np.eye((m))) #建立權重對角矩陣
for j in range(m): #遍歷數據集計算每一個樣本的權重
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("矩陣爲奇異矩陣,不能求逆")
return
ws = xTx.I * (xMat.T * (weights * yMat)) #計算迴歸係數
return testPoint * ws
def lwlrTest(testArr, xArr, yArr, k=1.0):
""" 函數說明:局部加權線性迴歸測試 Parameters: testArr - 測試數據集,測試集 xArr - x數據集,訓練集 yArr - y數據集,訓練集 k - 高斯核的k,自定義參數 Returns: ws - 迴歸係數 Website: https://www.cuijiahua.com/ Modify: 2017-11-19 """
m = np.shape(testArr)[0] #計算測試數據集大小
yHat = np.zeros(m)
for i in range(m): #對每一個樣本點進行預測
yHat[i] = lwlr(testArr[i],xArr,yArr,k)
return yHat
def standRegres(xArr,yArr):
""" 函數說明:計算迴歸係數w Parameters: xArr - x數據集 yArr - y數據集 Returns: ws - 迴歸係數 Website: https://www.cuijiahua.com/ Modify: 2017-11-19 """
xMat = np.mat(xArr); yMat = np.mat(yArr).T
xTx = xMat.T * xMat #根據文中推導的公示計算迴歸係數
if np.linalg.det(xTx) == 0.0:
print("矩陣爲奇異矩陣,不能求逆")
return
ws = xTx.I * (xMat.T*yMat)
return ws
def rssError(yArr, yHatArr):
""" 偏差大小評價函數 Parameters: yArr - 真實數據 yHatArr - 預測數據 Returns: 偏差大小 """
return ((yArr - yHatArr) **2).sum()
if __name__ == '__main__':
abX, abY = loadDataSet('abalone.txt')
print('訓練集與測試集相同:局部加權線性迴歸,核k的大小對預測的影響:')
yHat01 = lwlrTest(abX[0:99], abX[0:99], abY[0:99], 0.1)
yHat1 = lwlrTest(abX[0:99], abX[0:99], abY[0:99], 1)
yHat10 = lwlrTest(abX[0:99], abX[0:99], abY[0:99], 10)
print('k=0.1時,偏差大小爲:',rssError(abY[0:99], yHat01.T))
print('k=1 時,偏差大小爲:',rssError(abY[0:99], yHat1.T))
print('k=10 時,偏差大小爲:',rssError(abY[0:99], yHat10.T))
print('')
print('訓練集與測試集不一樣:局部加權線性迴歸,核k的大小是越小越好嗎?更換數據集,測試結果以下:')
yHat01 = lwlrTest(abX[100:199], abX[0:99], abY[0:99], 0.1)
yHat1 = lwlrTest(abX[100:199], abX[0:99], abY[0:99], 1)
yHat10 = lwlrTest(abX[100:199], abX[0:99], abY[0:99], 10)
print('k=0.1時,偏差大小爲:',rssError(abY[100:199], yHat01.T))
print('k=1 時,偏差大小爲:',rssError(abY[100:199], yHat1.T))
print('k=10 時,偏差大小爲:',rssError(abY[100:199], yHat10.T))
print('')
print('訓練集與測試集不一樣:簡單的線性歸回與k=1時的局部加權線性迴歸對比:')
print('k=1時,偏差大小爲:', rssError(abY[100:199], yHat1.T))
ws = standRegres(abX[0:99], abY[0:99])
yHat = np.mat(abX[100:199]) * ws
print('簡單的線性迴歸偏差大小:', rssError(abY[100:199], yHat.T.A))
複製代碼
運行結果以下:
能夠看到,當k=0.1時,訓練集偏差小,可是應用於新的數據集以後,偏差反而變大了。這就是常常說道的過擬合現象。咱們訓練的模型,咱們要保證測試集準確率高,這樣訓練出的模型才能夠應用於新的數據,也就是要增強模型的普適性。能夠看到,當k=1時,局部加權線性迴歸和簡單的線性迴歸獲得的效果差很少。這也代表一點,必須在未知數據上比較效果才能選取到最佳模型。那麼最佳的核大小是10嗎?或許是,但若是想獲得更好的效果,應該用10個不一樣的樣本集作10次測試來比較結果。
本示例展現瞭如何使用局部加權線性迴歸來構建模型,能夠獲得比普通線性迴歸更好的效果。局部加權線性迴歸的問題在於,每次必須在整個數據集上運行。也就是說爲了作出預測,必須保存全部的訓練數據。
本文出現的全部代碼和數據集,都可在個人github上下載,歡迎Follow、Star:點擊查看
圓方圓學院聚集 Python + AI 名師,打造精品的 Python + AI 技術課程。 在各大平臺都長期有優質免費公開課及錄像,歡迎報名收看。 公開課地址:ke.qq.com/course/3627…
加入python學習討論羣 78486745 ,獲取資料,和廣大羣友一塊兒學習。