机器学习实战(八)预测数值型数据:回归
文章目錄
- 第八章 預測數值型數據:回歸
- 8.1 用線性回歸找到最佳擬合直線
- 8.1.1 線性回歸
- 8.1.2數據可視化
- 8.1.3 求回歸系數向量,并根據系數繪制回歸曲線
- 8.2 局部加權線性回歸(LWLR)
- 8.3 預測鮑魚年齡
- 8.4 嶺回歸
- 8.5 前向逐步回歸
- 8.6 預測樂高玩具套件的價格
第八章 預測數值型數據:回歸
前面章節介紹了分類,分類的目標變量是標稱型數據,而本章將會對連續性數據做出預測。
8.1 用線性回歸找到最佳擬合直線
回歸的目的是預測數值型的目標值。最直接的辦法是依據輸入寫出一個目標值的計算公式。假如你想預測小姐姐男友汽車的功率,可能會這么計算:
HorsePower = 0.0015 * annualSalary - 0.99 * hoursListeningToPublicRadio
這就是所謂的回歸方程(regression equation),其中的0.0015和-0.99稱為回歸系數(regression weights),求這些回歸系數的過程就是回歸。一旦有了這些回歸系數,再給定輸入,做預測就非常容易了。具體的做法是用回歸系數乘以輸入值,再將結果全部加在一起,就得到了預測值。
說到回歸,一般都是指線性回歸(linear regression),所以本章里的回歸和線性回歸代表同一個意思。線性回歸意味著可以將輸入項分別乘以一些常量,再將結果加起來得到輸出。需要說明的是,存在另一種成為非線性回歸的回歸模型,該模型不認同上面的做法,比如認為輸出可能是輸入的乘積。這樣,上面的功率計算公式也可以寫做:
HorsePower=0.0015?annualSalaryhoursListeningToPublicRadioHorsePower = \frac{0.0015 * annualSalary }{ hoursListeningToPublicRadio}HorsePower=hoursListeningToPublicRadio0.0015?annualSalary?
8.1.1 線性回歸
如何利用線性回歸找到最佳擬合直線?
應該怎么從一大堆數據里求出回歸方程呢?假定輸入數據存放在矩陣X中,結果存放在向量y中:
而回歸系數存放在向量w中:
那么對于給定的數據x1,即矩陣X的第一列數據,預測結果u1將會通過如下公式給出:
現在的問題是,手里有數據矩陣X和對應的標簽向量y,怎么才能找到w呢?一個常用的方法就是找出使誤差最小的w。這里的誤差是指預測u值和真實y值之間的差值,使用該誤差的簡單累加將使得正差值和負差值相互抵消,所以我們采用平方誤差。
平方誤差和可以寫做:
用矩陣表示還可以寫作 (y?Xw)T(y?Xw)(y-Xw)^T(y-Xw)(y?Xw)T(y?Xw)
如果對www求導,得到XT(Y?Xw)X^T(Y-Xw)XT(Y?Xw),令其等于0,解得www:
w^=(XTX)?1XTy\hat{w}=(X^TX)^{-1}X^Tyw^=(XTX)?1XTy
其中,w^\hat{w}w^表示當前可以估計出的www的最優解,即最佳估計。
值得注意的是,上述公式中包含逆矩陣,也就是說,這個方程只在逆矩陣存在的時候使用,也即是這個矩陣是一個方陣,并且其行列式不為0。
述的最佳w求解是統計學中的常見問題,除了矩陣方法外還有很多其他方法可以解決。通過調用NumPy庫里的矩陣方法,我們可以僅使用幾行代碼就完成所需功能。該方法也稱作OLS, 意思是“普通小二乘法”(ordinary least squares)。
數據集為:ex0.txt
第一列:x0,都為1.0
第二列:x1,x軸的數據
第三列:x2,y軸的數據
8.1.2數據可視化
import numpy as np import matplotlib.pylab as pltdef loadDataSet(fileName):"""加載數據:param fileName: 文件名:return:xArr:x數據集yArr:y數據集"""numFeat=len(open(fileName).readline().split('\t'))-1xArr=[]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,yArrdef plotDataSet():"""繪制數據集:return:"""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)#繪制樣本點ax.scatter(xcord,ycord,s=20,c='blue',alpha=0.5)plt.title('DataSet')plt.xlabel('X')plt.show()if __name__=='__main__':plotDataSet()結果:
上圖即為數據分布情況
8.1.3 求回歸系數向量,并根據系數繪制回歸曲線
import numpy as np import matplotlib.pylab as pltdef loadDataSet(fileName):"""加載數據:param fileName: 文件名:return:xArr:x數據集yArr:y數據集"""numFeat=len(open(fileName).readline().split('\t'))-1xArr=[]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,yArrdef standRegres(xArr,yArr):"""計算回歸系數w:param xArr: x數據集:param yArr: y數據集:return: w:回歸系數"""#np.mat 將序列轉化為二維數組xMat=np.mat(xArr)yMat=np.mat(yArr).TxTx=xMat.T*xMat#np.linalg.inv():矩陣求逆#np.linalg.det():矩陣求行列式(標量)#如果行列式為0,則為奇異矩陣,不能求逆if np.linalg.det(xTx)==0:print("矩陣為奇異矩陣,不能求逆")return#回歸系數# .I為求逆ws=xTx.I*(xMat.T*yMat)return wsdef plotRegression():"""繪制回歸曲線和數據點:return:"""#加載數據集xArr,yArr=loadDataSet('ex0.txt')#計算回歸系數ws=standRegres(xArr,yArr)#創建矩陣xMat=np.mat(xArr)yMat=np.mat(yArr)#深拷貝xCopy=xMat.copy()#排序xCopy.sort(0)#計算對應的y值yHat=xCopy*wsfig=plt.figure()ax=fig.add_subplot(111)#繪制回歸曲線ax.plot(xCopy[:,1],yHat,c='red')#繪制樣本點ax.scatter(xMat[:,1].flatten().A[0],yMat.flatten().A[0],s=20,c='blue',alpha=0.5)plt.title('DataSet')plt.xlabel('X')plt.show()if __name__=='__main__':plotRegression()結果:
如何判斷擬合曲線的擬合效果:
根據經驗觀察,或者使用corrcoef方法,來比較預測值和真實值的相關性,代碼如下:
import numpy as np import matplotlib.pylab as pltdef loadDataSet(fileName):"""加載數據:param fileName: 文件名:return:xArr:x數據集yArr:y數據集"""numFeat=len(open(fileName).readline().split('\t'))-1xArr=[]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,yArrdef standRegres(xArr,yArr):"""計算回歸系數w:param xArr: x數據集:param yArr: y數據集:return: w:回歸系數"""#np.mat 將序列轉化為二維數組xMat=np.mat(xArr)yMat=np.mat(yArr).TxTx=xMat.T*xMat#np.linalg.inv():矩陣求逆#np.linalg.det():矩陣求行列式(標量)#如果行列式為0,則為奇異矩陣,不能求逆if np.linalg.det(xTx)==0:print("矩陣為奇異矩陣,不能求逆")return#回歸系數# .I為求逆ws=xTx.I*(xMat.T*yMat)return wsif __name__=='__main__':#加載數據集xArr,yArr=loadDataSet('ex0.txt')#計算回歸系數ws=standRegres(xArr,yArr)xMat=np.mat(xArr)yMat=np.mat(yArr)yHat=xMat*ws#np.corrcoef 獲得相關系數矩陣print(np.corrcoef(yHat.T,yMat))結果:
[[ 1. 0.98647356][ 0.98647356 1. ]]可以看到,對角線上的數據是1.0,因為yMat和自己的匹配是完美的,而YHat和yMat的相關系數為0.98。
最佳擬合直線方法將數據視為直線進行建模,具有十分不錯的表現。數據當中似乎還存在其他的潛在模式。那么如何才能利用這些模式呢?我們可以根據數據來局部調整預測,下面就會介紹這種方法。
np.corrcoef介紹:
官網鏈接
8.2 局部加權線性回歸(LWLR)
線性回歸的一個問題是有可能出現欠擬合現象,因為它求的是具有小均方誤差的無偏估 計。顯而易見,如果模型欠擬合將不能取得好的預測效果。所以有些方法允許在估計中引入一 些偏差,從而降低預測的均方誤差。
其中的一個方法是局部加權線性回歸(Locally Weighted Linear Regression,LWLR)。在該方法中,我們給待預測點附近的每個點賦予一定的權重。與kNN一樣,這種算法每次預測均需要事先選取出對應的數據子集。該算法解除回歸系數WWW的形式如下:
w^=(XTWX)?1XTWy\hat{w}=(X^TWX)^{-1}X^TWyw^=(XTWX)?1XTWy
其中,WWW是一個矩陣,這個公式跟我們上面推導的公式的區別就在于WWW,它用來給每個點賦予權重。
LWLR使用”核”(與支持向量機中的核類似)來對附近的點賦予更高的權重。核的類型可以自由選擇,最常用的核就是高斯核,高斯核對應的權重如下:
w(i,i)=exp(∣x(i)?x∣?2k2)w(i,i)=exp\left (\frac{|x^{(i)}-x|}{-2k^2}\right)w(i,i)=exp(?2k2∣x(i)?x∣?)
這樣我們就可以根據上述公式,編寫局部加權線性回歸,我們通過改變k的值,可以調節回歸效果,編寫代碼如下:
# -*- coding:utf-8 -*- from matplotlib.font_manager import FontProperties import matplotlib.pyplot as plt import numpy as npdef loadDataSet(fileName):"""函數說明:加載數據Parameters:fileName - 文件名Returns:xArr - x數據集yArr - y數據集Website:http://www.cuijiahua.com/Modify:2017-11-12"""numFeat = len(open(fileName).readline().split('\t')) - 1xArr = []; 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, yArrdef standRegres(xArr,yArr):"""函數說明:計算回歸系數wParameters:xArr - x數據集yArr - y數據集Returns:ws - 回歸系數Website:http://www.cuijiahua.com/Modify:2017-11-12"""xMat = np.mat(xArr); yMat = np.mat(yArr).TxTx = xMat.T * xMat #根據文中推導的公示計算回歸系數if np.linalg.det(xTx) == 0.0:print("矩陣為奇異矩陣,不能求逆")returnws = xTx.I * (xMat.T*yMat)return wsdef plotDataSet():"""函數說明:繪制數據集Parameters:無Returns:無Website:http://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) #添加subplotax.scatter(xcord, ycord, s = 20, c = 'blue',alpha = .5) #繪制樣本點plt.title('DataSet') #繪制titleplt.xlabel('X')plt.show()def plotRegression():"""函數說明:繪制回歸曲線和數據點Parameters:無Returns:無Website:http://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) #添加subplotax.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') #繪制titleplt.xlabel('X')plt.show()def plotlwlrRegression():"""函數說明:繪制多條局部加權回歸曲線Parameters:無Returns:無Website:http://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) #根據局部加權線性回歸計算yHatyHat_2 = lwlrTest(xArr, xArr, yArr, 0.01) #根據局部加權線性回歸計算yHatyHat_3 = lwlrTest(xArr, xArr, yArr, 0.003) #根據局部加權線性回歸計算yHatxMat = 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軸labelaxs0_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):"""函數說明:使用局部加權線性回歸計算回歸系數wParameters:testPoint - 測試樣本點xArr - x數據集yArr - y數據集k - 高斯核的k,自定義參數Returns:ws - 回歸系數Website:http://www.cuijiahua.com/Modify:2017-11-15"""xMat = np.mat(xArr); yMat = np.mat(yArr).Tm = 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("矩陣為奇異矩陣,不能求逆")returnws = xTx.I * (xMat.T * (weights * yMat)) #計算回歸系數return testPoint * wsdef lwlrTest(testArr, xArr, yArr, k=1.0): """函數說明:局部加權線性回歸測試Parameters:testArr - 測試數據集xArr - x數據集yArr - y數據集k - 高斯核的k,自定義參數Returns:ws - 回歸系數Website:http://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 yHatif __name__ == '__main__':plotlwlrRegression()結果:
8.3 預測鮑魚年齡
數據集:abalone.txt文件
數據集是多維的,雖然每個數據集的含義并未給出,但是只要知道最后一列數據是y值就可以了。最后一列代表鮑魚的真實年齡,前幾列是鮑魚的特征。
import matplotlib.pylab as plt from matplotlib.font_manager import FontProperties import numpy as npdef loadDataSet(fileName):"""加載數據:param fileName: 文件名:return:xArr:x數據集yArr:y數據集"""numFeat = len(open(fileName).readline().split('\t')) - 1xArr = []; 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, yArrdef lwlr(testPoint,xArr,yArr,k=1.0):"""使用局部加權線性回歸計算回歸系數w:param testPoint: 測試樣本點:param xArr: x數據集:param yArr: y數據集:param k: 高斯核的k,自定義參數:return: ws:回歸系數"""xMat=np.mat(xArr)yMat=np.mat(yArr).Tm=np.shape(xMat)[0]# m*m矩陣,創建權重對角陣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("矩陣為奇異矩陣,不能求逆")returnws=xTx.I*(xMat.T*(weights*yMat))return testPoint*wsdef lwlrTest(testArr,xArr,yArr,k=1.0):"""局部加權線性回歸測試:param testArr: 測試數據集,測試集:param xArr: x數據集,訓練集:param yArr: y數據集,訓練集:param k: 高斯核的k,自定義參數:return: ws:回歸系數"""m=np.shape(testArr)[0]yHat=np.zeros(m)for i in range(m):yHat[i]=lwlr(testArr[i],xArr,yArr,k)return yHatdef standRegres(xArr,yArr):"""函數說明:計算回歸系數wParameters:xArr - x數據集yArr - y數據集Returns:ws - 回歸系數"""xMat = np.mat(xArr); yMat = np.mat(yArr).TxTx = xMat.T * xMat #根據文中推導的公示計算回歸系數if np.linalg.det(xTx) == 0.0:print("矩陣為奇異矩陣,不能求逆")returnws = 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]) * wsprint('簡單的線性回歸誤差大小:', rssError(abY[100:199], yHat.T.A))結果:
k=1 時,誤差大小為: 429.89056187 k=10 時,誤差大小為: 549.118170883訓練集與測試集不同:局部加權線性回歸,核k的大小是越小越好嗎?更換數據集,測試結果如下: k=0.1時,誤差大小為: 25119.4591112 k=1 時,誤差大小為: 573.52614419 k=10 時,誤差大小為: 517.571190538訓練集與測試集不同:簡單的線性歸回與k=1時的局部加權線性回歸對比: k=1時,誤差大小為: 573.52614419 簡單的線性回歸誤差大小: 518.636315325結論:
- 當k=0.1時,訓練集誤差很小,但是應用到測試集時,誤差反而變大了,這就是過擬合現象。
- 當k=1時,加權回歸和簡單的線性回歸效果差不多
- 表明選取最佳模型必須要在未知數據集上比較才能得到
- 如何確定最佳效果:使用不同的10個樣本集做測試來比較
- 局部加權線性回歸中,過小的核可能導致過擬合現象,即訓練集表現良好,測試集表現很差。
局部加權線性回歸能夠取得比普通的更好的效果,但其問題在于每次必須在整個數據集上運行,也就是為了做出預測,必須保存所有的訓練數據。
8.4 嶺回歸
如果數據的特征比樣本點還多應該怎么辦?很顯然,此時我們不能再使用上文的方法進行計算了,因為矩陣X不是滿秩矩陣,非滿秩矩陣在求逆時會出現問題。為了解決這個問題,統計學家引入嶺回歸(ridge regression)的概念。
嶺回歸即我們所說的L2正則線性回歸,在一般的線性回歸最小化均方誤差的基礎上增加了一個參數w的L2范數的罰項,從而最小化罰項殘差平方和:
簡單說來,嶺回歸就是在普通線性回歸的基礎上引入單位矩陣。回歸系數的計算公式變形如下:
式中,矩陣I是一個mxm的單位矩陣,加上一個λI從而使得矩陣非奇異,進而能對矩陣求逆。
嶺回歸最先用來處理特征數多于樣本數的情況,現在也用于在估計中加入偏差,從而得到更好的估計。這里通過引入λ來限制了所有w之和,通過引入該懲罰項,能夠減少不重要的參數,這個技術在統計學中也可以叫做縮減(shrinkage)。
縮減方法可以去掉不重要的參數,因此能更好地裂解數據。此外,與簡單的線性回歸相比,縮減法能夠取得更好的預測效果。
代碼:
為了使用嶺回歸和縮減技術,首先需要對特征做標準化處理。因為,我們需要使每個維度特征具有相同的重要性。本文使用的標準化處理比較簡單,就是將所有特征都減去各自的均值并除以方差。
代碼很簡單,只需要稍做修改,其中,λ為模型的參數。我們先繪制一個回歸系數與log(λ)的曲線圖,看下它們的規律,編寫代碼如下:
# -*-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數據集"""numFeat = len(open(fileName).readline().split('\t')) - 1xArr = []; 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 ridgeRegres(xMat, yMat, lam = 0.2):"""函數說明:嶺回歸Parameters:xMat - x數據集yMat - y數據集lam - 縮減系數Returns:ws - 回歸系數"""xTx = xMat.T * xMatdenom = xTx + np.eye(np.shape(xMat)[1]) * lamif np.linalg.det(denom) == 0.0:print("矩陣為奇異矩陣,不能轉置")returnws = denom.I * (xMat.T * yMat)return ws def ridgeTest(xArr, yArr):"""函數說明:嶺回歸測試Parameters:xMat - x數據集yMat - y數據集Returns:wMat - 回歸系數矩陣"""xMat = np.mat(xArr); yMat = np.mat(yArr).T#數據標準化yMean = np.mean(yMat, axis = 0) #行與行操作,求均值yMat = yMat - yMean #數據減去均值xMeans = np.mean(xMat, axis = 0) #行與行操作,求均值xVar = np.var(xMat, axis = 0) #行與行操作,求方差xMat = (xMat - xMeans) / xVar #數據減去均值除以方差實現標準化numTestPts = 30 #30個不同的lambda測試wMat = np.zeros((numTestPts, np.shape(xMat)[1])) #初始回歸系數矩陣for i in range(numTestPts): #改變lambda計算回歸系數ws = ridgeRegres(xMat, yMat, np.exp(i - 10)) #lambda以e的指數變化,最初是一個非常小的數,wMat[i, :] = ws.T #計算回歸系數矩陣return wMat def plotwMat():"""函數說明:繪制嶺回歸系數矩陣"""font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)abX, abY = loadDataSet('abalone.txt')redgeWeights = ridgeTest(abX, abY)fig = plt.figure()ax = fig.add_subplot(111)ax.plot(redgeWeights)ax_title_text = ax.set_title(u'log(lambada)與回歸系數的關系', FontProperties = font)ax_xlabel_text = ax.set_xlabel(u'log(lambada)', FontProperties = font)ax_ylabel_text = ax.set_ylabel(u'回歸系數', FontProperties = font)plt.setp(ax_title_text, size = 20, weight = 'bold', color = 'red')plt.setp(ax_xlabel_text, size = 10, weight = 'bold', color = 'black')plt.setp(ax_ylabel_text, size = 10, weight = 'bold', color = 'black')plt.show()if __name__ == '__main__':plotwMat()結果:
結果分析:
上圖繪制了回歸系數和log(λ)log(\lambda)log(λ)的關系,最左邊時即λ\lambdaλ最小時,可以的是所有系數的原始值(與線性回歸一致),最右邊系數全部縮減為0,中間的某個位置將會得到最好的預測結果。
如何得到最佳參數λ\lambdaλ:使用交叉驗證
lasso(普通最小二乘回歸)
8.5 前向逐步回歸
前向逐步線性回歸算法屬于一種貪心算法,即每一步都盡可能減少誤差。一開始,所有的權重都設置為1,然后每一步所做的決策是對某個權重增加或減少一個很小的值。
我們計算回歸系數,不再是通過公式計算,而是通過每次微調各個回歸系數,然后計算預測誤差。那個使誤差最小的一組回歸系數,就是我們需要的最佳回歸系數。
前向逐步線性回歸實現也很簡單。當然,還是先進行數據標準化,編寫代碼如下:
# -*-coding:utf-8 -*- from matplotlib.font_manager import FontProperties import matplotlib.pyplot as plt import numpy as npdef loadDataSet(fileName):"""函數說明:加載數據Parameters:fileName - 文件名Returns:xArr - x數據集yArr - y數據集"""numFeat = len(open(fileName).readline().split('\t')) - 1xArr = [];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, yArrdef regularize(xMat, yMat):"""函數說明:數據標準化Parameters:xMat - x數據集yMat - y數據集Returns:inxMat - 標準化后的x數據集inyMat - 標準化后的y數據集"""inxMat = xMat.copy() # 數據拷貝inyMat = yMat.copy()yMean = np.mean(yMat, 0) # 行與行操作,求均值inyMat = yMat - yMean # 數據減去均值inMeans = np.mean(inxMat, 0) # 行與行操作,求均值inVar = np.var(inxMat, 0) # 行與行操作,求方差inxMat = (inxMat - inMeans) / inVar # 數據減去均值除以方差實現標準化return inxMat, inyMatdef rssError(yArr, yHatArr):"""函數說明:計算平方誤差Parameters:yArr - 預測值yHatArr - 真實值Returns:"""return ((yArr - yHatArr) ** 2).sum()def stageWise(xArr, yArr, eps=0.01, numIt=100):"""函數說明:前向逐步線性回歸Parameters:xArr - x輸入數據yArr - y預測數據eps - 每次迭代需要調整的步長numIt - 迭代次數Returns:returnMat - numIt次迭代的回歸系數矩陣"""xMat = np.mat(xArr);yMat = np.mat(yArr).T # 數據集xMat, yMat = regularize(xMat, yMat) # 數據標準化m, n = np.shape(xMat)returnMat = np.zeros((numIt, n)) # 初始化numIt次迭代的回歸系數矩陣ws = np.zeros((n, 1)) # 初始化回歸系數矩陣wsTest = ws.copy()wsMax = ws.copy()for i in range(numIt): # 迭代numIt次# print(ws.T) #打印當前回歸系數矩陣lowestError = float('inf') # 正無窮for j in range(n): # 遍歷每個特征的回歸系數for sign in [-1, 1]:wsTest = ws.copy()wsTest[j] += eps * sign # 微調回歸系數yTest = xMat * wsTest # 計算預測值rssE = rssError(yMat.A, yTest.A) # 計算平方誤差if rssE < lowestError: # 如果誤差更小,則更新當前的最佳回歸系數lowestError = rssEwsMax = wsTestws = wsMax.copy()returnMat[i, :] = ws.T # 記錄numIt次迭代的回歸系數矩陣return returnMatdef plotstageWiseMat():"""函數說明:繪制嶺回歸系數矩陣"""font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)xArr, yArr = loadDataSet('abalone.txt')returnMat = stageWise(xArr, yArr, 0.005, 1000)fig = plt.figure()ax = fig.add_subplot(111)ax.plot(returnMat)ax_title_text = ax.set_title(u'前向逐步回歸:迭代次數與回歸系數的關系', FontProperties=font)ax_xlabel_text = ax.set_xlabel(u'迭代次數', FontProperties=font)ax_ylabel_text = ax.set_ylabel(u'回歸系數', FontProperties=font)plt.setp(ax_title_text, size=15, weight='bold', color='red')plt.setp(ax_xlabel_text, size=10, weight='bold', color='black')plt.setp(ax_ylabel_text, size=10, weight='bold', color='black')plt.show()if __name__ == '__main__':plotstageWiseMat()結果:
縮減方法(逐步線性回歸或嶺回歸),就是將一些系數縮減成很小的值或者直接縮減為0。這樣做,就增大了模型的偏差(減少了一些特征的權重),通過把一些特征的回歸系數縮減到0,同時也就減少了模型的復雜度。消除了多余的特征之后,模型更容易理解,同時也降低了預測誤差。但是當縮減過于嚴厲的時候,就會出現過擬合的現象,即用訓練集預測結果很好,用測試集預測就糟糕很多。
8.6 預測樂高玩具套件的價格
使用sklearn的linear_model
官網鏈接
class sklearn.linear_model.Ridge(alpha=1.0, fit_intercept=True, normalize=False, copy_X=True, max_iter=None, tol=0.001, solver=’auto’, random_state=None)參數說明如下:
- alpha:正則化系數,float類型,默認為1.0。正則化改善了問題的條件并減少了估計的方差。較大的值指定較強的正則化。
- fit_intercept:是否需要截距,bool類型,默認為True。也就是是否求解b。
- normalize:是否先進行歸一化,bool類型,默認為False。如果為真,則回歸X將在回歸之前被歸一化。 當fit_intercept設置為False時,將忽略此參數。 當回歸量歸一化時,注意到這使得超參數學習更加魯棒,并且幾乎不依賴于樣本的數量。 相同的屬性對標準化數據無效。然而,如果你想標準化,請在調用normalize = False訓練估計器之前,使用preprocessing.StandardScaler處理數據。
- copy_X:是否復制X數組,bool類型,默認為True,如果為True,將復制X數組; 否則,它覆蓋原數組X。
- max_iter:最大的迭代次數,int類型,默認為None,最大的迭代次數,對于sparse_cg和lsqr而言,默認次數取決于scipy.sparse.linalg,對于sag而言,則默認為1000次。
- tol:精度,float類型,默認為0.001。就是解的精度。
- solver:求解方法,str類型,默認為auto。可選參數為:auto、svd、cholesky、lsqr、sparse_cg、sag。
- auto根據數據類型自動選擇求解器。
- svd使用X的奇異值分解來計算Ridge系數。對于奇異矩陣比cholesky更穩定。
- cholesky使用標準的scipy.linalg.solve函數來獲得閉合形式的解。
- sparse_cg使用在scipy.sparse.linalg.cg中找到的共軛梯度求解器。作為迭代算法,這個求解器比大規模數據(設置tol和max_iter的可能性)的cholesky更合適。
- lsqr使用專用的正則化最小二乘常數scipy.sparse.linalg.lsqr。它是最快的,但可能在舊的scipy版本不可用。它是使用迭代過程。
- sag使用隨機平均梯度下降。它也使用迭代過程,并且當n_samples和n_feature都很大時,通常比其他求解器更快。注意,sag快速收斂僅在具有近似相同尺度的特征上被保證。您可以使用sklearn.preprocessing的縮放器預處理數據。
- random_state:sag的偽隨機種子。
以上就是所有的初始化參數,當然,初始化后還可以通過set_params方法重新進行設定。
結果:
總結:
與分類一樣,回歸也是預測目標值的過程。回歸與分類的不同點在于,前者預測連續類型變量,而后者預測離散類型變量。
嶺回歸是縮減法的一種,相當于對回歸系數的大小施加了限制。另一種很好的縮減法是lasso。lasso難以求解,但可以使用計算簡便的逐步線性回歸方法求的近似解。
縮減法還可以看做是對一個模型增加偏差的同時減少方法。
總結
以上是生活随笔為你收集整理的机器学习实战(八)预测数值型数据:回归的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 3 基础之字符串下标索引、
- 下一篇: python3(十一)内置模块和类型转换