预测数值型数据:回归 源码分析(2)
4. 縮減系數來“理解”數據
4.1 嶺回歸
如果數據的特征比樣本點還多,那么就不能使用線性回歸,因為在計算(XTX)?1的時候會出錯。也就是輸入數據的矩陣X不是滿秩矩陣,非滿秩矩陣在求逆時會出現問題,為此有了嶺回歸。
簡單說來,嶺回歸就是在矩陣XTX上加一個λI從而使得矩陣非奇異,進而能對XTX+λI求逆。λ是一個用戶定義的數值。在這種情況下,回歸系數的計算公式將變成:
嶺回歸最先用來處理特征數多于樣本數的情況,現在也用于在估計中加人偏差,從而得到更好的估計。這里通過引入λ來限制了所有w之和,通過引人該懲罰項,能夠減少不重要的參數,這就是縮減技術。
這段話就是嶺回歸最大的特點和用處。。。。
嶺回歸代碼的原理:
這里也是通過預測誤差最小化得到λ數據獲取之后,首先抽一部分數據用于測試,剩余的作為訓練集用于訓練參數w訓練完畢后在測試集上測試預測性能。通過選取不同的λ來重復上述測試過程,最終得到一個使預測誤差最小的λ。
這里要注意的地方就是對特征進行標準化處理:使每維特征具有相同的重要性
具體的做法是所有特征都減去各自的均值并除以方差。
運行結果:
這里想說下結果圖的橫縱坐標:
機器學習實戰上面的橫坐標為log(λ),而此處由結果圖可以看出橫坐標是10+log(λ),因為橫坐標是0?30,也就是以上圖的縱坐標為刻度的,例如對應0點的每個值就是λ最小時的8個特征對應的系數值,而此時的λ=e(0?10)=e?10,也就是10+log(λ)=10+log(e?10)=0既是此時的橫坐標,結果圖的其他刻度值同樣也是這樣的一個對應關系。
4.2 前向逐步回歸(貪心算法)
當最小二乘法回歸加上約束條件時:∑nk=1W2k?λ就可以得到和嶺回歸一樣的公式。
為什么要這樣做?
因為在使用普通最小二乘法時,在當兩個或者更多的特征相關時,可能會得到一個很大的正系數和一個很大的負系數,但限制了所有回歸系數的平方和不大于λ時,就可以解決這個問題。
再說下lasso:
與嶺回歸類似,lasso也是對回歸做了限定,對應的約束:∑nk=1|Wk|?λ,這里是用的絕對值,但效果卻和嶺回歸有很大的差別:
- 絕對值:在λ很小時,一些系數會被迫縮減到0,這樣就可以洗漱數據,得到更有用的特征,也就是能更好的理解數據,類似于正則化中的范數。
前向逐步回歸:可以得到和lasso差不多的效果,但更加簡單,它是一種貪心算法,即每一步都盡可能的減少誤差。
# -*- coding: utf-8 -*- """ Created on Wed Oct 25 16:49:50 2017 """ from numpy import * import matplotlib.pyplot as plt# 數據導入函數 def loadDataSet(fileName): numFeat = len(open(fileName).readline().split('\t')) - 1 # 得到特征數 dataMat = []; labelMat = []fr = open(fileName)for line in fr.readlines():lineArr =[]curLine = line.strip().split('\t')for i in range(numFeat):lineArr.append(float(curLine[i]))dataMat.append(lineArr)labelMat.append(float(curLine[-1])) # 得到最后一列目標值return dataMat,labelMat# 用于計算回歸系數,嶺回歸 def ridgeRegres(xMat,yMat,lam=0.2): # lambda是關鍵字,此處不能用xTx = xMat.T*xMat # 構建矩陣x`xdenom = xTx + eye(shape(xMat)[1])*lam # shape(xMat)[1]得到的是特征數if linalg.det(denom) == 0.0: # linalg.det()用來計算行列式,檢查其是否為0print "This matrix is singular, cannot do inverse"returnws = denom.I * (xMat.T*yMat) # 如果矩陣非奇異,計算回歸系數并返回return ws # 返回的是回歸系數# 用于在一組lambda上測試結果 def ridgeTest(xArr,yArr):xMat = mat(xArr); yMat=mat(yArr).TyMean = mean(yMat,0) # axis = 0 壓縮行,對各列求均值yMat = yMat - yMean # 減去均值xMeans = mean(xMat,0) # 得到每列特征的均值xVar = var(xMat,0) # 得到每列的方差,此處var()是求得方差xMat = (xMat - xMeans)/xVar # 特征減去各自的均值并除以方差,就是標準化的過程numTestPts = 30wMat = zeros((numTestPts,shape(xMat)[1]))for i in range(numTestPts): # 30個不同的lambda參數下調用ridgeregre()ws = ridgeRegres(xMat,yMat,exp(i-10)) # 這里lambda應以指數級變化,以觀察到有效的結果對比#print 'shape ws:',shape(ws)wMat[i,:]=ws.T # 把系數轉置后成為行向量后賦給wmat[]return wMat # 返回最終得到的30次不同的lambda得到的系數# 標準化處理:方差為0,方差為1 def regularize(xMat): inMat = xMat.copy()inMeans = mean(inMat,0) inVar = var(inMat,0) inMat = (inMat - inMeans)/inVarreturn inMat# 計算預測誤差的大小 def rssError(yArr,yHatArr): # yArr和yHatArr都需要是數組return ((yArr-yHatArr)**2).sum()# 前向逐步線性回歸(貪心算法) def stageWise(xArr,yArr,eps=0.01,numIt=100): # eps 每次迭代的步長,numIt 迭代的次數xMat = mat(xArr); yMat=mat(yArr).TyMean = mean(yMat,0) # axis = 0 壓縮行,對各列求均值yMat = yMat - yMean xMat = regularize(xMat) # 特征標準化為均值為0,方差為1m,n=shape(xMat) # 特征矩陣的行和列returnMat = zeros((numIt,n)) # 返回的矩陣100*nws = zeros((n,1)); wsTest = ws.copy(); wsMax = ws.copy() for i in range(numIt): # 迭代numIt次#print 'ws.T:',ws.T # 打印出來分析效果lowestError = inf; # 每一次迭代誤差初始值設為正無窮for j in range(n): # 在所有特征上循環for sign in [-1,1]: # 每個特征運行兩次wsTest = ws.copy() # 每次的ws都是上一次最優后得到的wsTest[j] += eps*sign # 增加或者減少該特征的影響對誤差的影響yTest = xMat*wsTestrssE = rssError(yMat.A,yTest.A) # 返回的誤差(平方誤差)if rssE < lowestError:lowestError = rssEwsMax = wsTestws = wsMax.copy() # 得到的是最小誤差的回歸系數returnMat[i,:]=ws.T # 每一行存儲的是每次迭代的回歸系數return returnMat # 最后返回的是貪心算法得到的系數矩陣# 前向逐步回歸 xArr,yArr=loadDataSet('abalone.txt') wsMat_1=stageWise(xArr,yArr,0.01,200) #print 'wsMat:',wsMat fig=plt.figure() ax=fig.add_subplot(111) ax.plot(wsMat_1) # 對于多維的畫圖,直接plt()即可 ax.set_title('Step length=0.01, Number of iterations:200') plt.show()wsMat_2=stageWise(xArr,yArr,0.001,5000) fig=plt.figure() ax=fig.add_subplot(111) ax.plot(wsMat_2) # 對于多維的畫圖,直接plt()即可 ax.set_title('Step length=0.001, Number of iterations:5000') plt.show()運行結果:
其中返回的系數矩陣為:
- 其中的第一列和第六列都為0,即說明這兩個特征的系數為0,也就是該特征不是主要特征,從而起到降維的作用。
- 還有就是可以看出在參數步長為0.01時,一段是時間后飽和后,一些系數就在特征值之間來回震蕩,這是因為步長太大的緣故。由對比不同的步長和迭代次數可以看出系數逐步穩定的過程,不再震蕩。
- 逐步線性回歸可以幫助理解現有的模型,當構建好一個模型后,可以運算該算法找出重要特征,這樣就可以停止不重要的特征的收集。如果用于測試的話,該算法可以通過迭代后構建很多的同類模型,可以使用類似于10折交叉驗證法比較這些模型,最終選擇使誤差最小的模型。
5. 方差和偏差的簡單理解
方差指的是模型之間的差異,而偏差指的是模型預測值和數據之間的差異!!!
當應用縮減方法時,模型增加了偏差,同時卻減少了模型的方差。
可以看出將一些系數縮減到很小的值或直接縮減為0,這是一個減少模型復雜度的過程,但同時也是一個增大模型偏差的過程。
方差是可以度量的。如果從鮑魚數據中取一個隨機樣本集(例如取其中100個數據)并用線性模型擬合,將會得到一組回歸系數。同理,再取出另一組隨機樣本集并擬合,將會得到另一組回歸系數。這些系數間的差異大小也就是模型方差大小的反映
6. 預測樂高玩具套裝的價格
理論應用:首先從拍賣站點抽取一些數據,再使用一些回歸法進行實驗來為數據找到最佳的嶺回歸模型。這樣就可以通過實際效果來看看偏差和方差間的折中效果。
算法流程:
1 收集數據:用google shopping的api收集數據
2 準備數據:從返回的json數據中抽取價格
3 分析數據:可視化并觀察數據
4 訓練算法:構建不同的模型,采用嶺回歸和普通線性回歸訓練模型
5 測試算法:使用交叉驗證來測試不同的模型,選擇效果最好的模型
- 收集數據:使用Google 購物的API來獲取玩具套裝的相關信息和價格,可以通過urllib2發送http請求,API將以JSON格式返回需要的產品信息,python的JSON解析模塊可以幫助我們從JSON格式中解析出所需要的數據。收集數據的代碼如下:
由于對爬數據不太會和其他原因,這里沒有運行出來,但我覺得還是有必要對源碼好好分析下,這里僅給出代碼解析:
# -*- coding: utf-8 -*- from time import sleep import json import urllib2# 購物信息的獲取函數 def searchForSet(retX, retY, setNum, yr, numPce, origPrc): # 調用API并數據抽取sleep(10) # 休息10秒鐘,防止短時間內有過多的API調用# 拼接查詢的url字符串,添加API的Key和待查詢的套裝信息searchURL = 'https://www.googleapis.com/shopping/search/v1/public/products?key=%s&country=US&q=lego+%d&alt=json' % (myAPIstr, setNum)pg = urllib2.urlopen(searchURL) # 打開 URL 等待返回數據retDict = json.loads(pg.read()) # 利用json打開和解析url獲得的數據,數據信息存入字典中print ('i am here!')# 遍歷數據的每一個條目for i in range(len(retDict['items'])): try:currItem = retDict['items'][i] # 獲得當前條目if currItem['product']['condition'] == 'new': # 當前條目對應的產品為新產品newFlag = 1else: newFlag = 0listOfInv = currItem['product']['inventories'] # 得到當前目錄產品的庫存列表for item in listOfInv: # 遍歷庫存中的每一個條目sellingPrice = item['price'] # 得到該條目玩具商品的價格if sellingPrice > origPrc * 0.5: # 價格低于原價的50%視為不完整套裝print ("%d\t%d\t%d\t%f\t%f" % (yr,numPce,newFlag,origPrc, sellingPrice))retX.append([yr, numPce, newFlag, origPrc]) # 將符合條件套裝信息作為特征存入數據矩陣retY.append(sellingPrice) # 將對應套裝的出售價格存入矩陣except: print ('problem with item %d' % i)# 多次調用收集數據函數,獲取多組不同年份,不同價格的數據 def setDataCollect(retX, retY):searchForSet(retX, retY, 8288, 2006, 800, 49.99)searchForSet(retX, retY, 10030, 2002, 3096, 269.99)searchForSet(retX, retY, 10179, 2007, 5195, 499.99)searchForSet(retX, retY, 10181, 2007, 3428, 199.99)searchForSet(retX, retY, 10189, 2008, 5922, 299.99)searchForSet(retX, retY, 10196, 2009, 3263, 249.99)lgx=[];lgy=[] print ('setDataCollect:',setDataCollect(lgx,lgy))其中不完整的套裝的檢索是用的啟發式!!
- 訓練算法,建立模型
要達到的目的是構建的模型可以對售價做出預測,并幫助理解現有數據。
現用嶺回歸進行模型的建立,上一篇博客講過如何對系數進行縮減,下面的代碼將是如何用縮減法確定最佳回歸系數。
# 交叉驗證測試嶺回歸 # xArr:從網站中獲得的玩具套裝樣本數據,yArr:樣本對應的出售價格,numVal:交叉驗證的次數 def crossValidation(xArr,yArr,numVal=10): m = len(yArr) # 獲取樣本數 indexList = range(m)errorMat = zeros((numVal,30)) # 10次測試 每次有30組回歸系數 可以得到誤差 for i in range(numVal): # 測試次數,默認為10次trainX=[]; trainY=[] # 訓練數據集和標簽 testX = []; testY = [] # 測試數據集和標簽random.shuffle(indexList) # 混洗索引列表,以實現訓練集或測試集數據點的隨機選取for j in range(m): # 遍歷每個樣本if j < m*0.9: # 數據集90%作為訓練集trainX.append(xArr[indexList[j]])trainY.append(yArr[indexList[j]])else: # 剩余10%作為測試集testX.append(xArr[indexList[j]])testY.append(yArr[indexList[j]])wMat = ridgeTest(trainX,trainY) # 得到嶺回歸的所有回歸系數,得到了30組不同的回歸系數for k in range(30): # 對于30組不同的嶺回歸得到的回歸系數進行測試,計算誤差,選取最好的matTestX = mat(testX); matTrainX=mat(trainX) meanTrain = mean(matTrainX,0) # 訓練數據 均值varTrain = var(matTrainX,0) # 訓練數據 方差# 特征減去各自的均值并除以方差,就是標準化的過程,嶺回歸需要使用標準化的數據,# 因此數據也需要使用與訓練集相同的參數來標準化matTestX = (matTestX-meanTrain)/varTrain # 用訓練集的參數將測試數據標準化 # 之所以加上訓練標簽的均值也是為了標準化一致,從而得到的測試集預測值是其真實預測值yEst = matTestX * mat(wMat[k,:]).T + mean(trainY) # yEst對應的是所有測試樣本在每個lambda下的預測值# 計算誤差,errorMat默認是10*30,保存的是10行交叉驗證,每行的30個元素分別是對應的lambda所有測試樣本預測值的誤差和errorMat[i,k]=rssError(yEst.T.A,array(testY)) meanErrors = mean(errorMat,0) # 計算誤差估計值的均值,此處為10折,每個lambda對應10個誤差minMean = float(min(meanErrors)) # 計算誤差均值最小的額回歸系數bestWeights = wMat[nonzero(meanErrors==minMean)] # 最好的回歸系數# 將標準化后的數據還原用于可視化 #can unregularize to get model#when we regularized we wrote Xreg = (x-meanX)/var(x)#we can now write in terms of x not Xreg: x*w/var(x) - meanX/var(x) +meanYxMat = mat(xArr); yMat=mat(yArr).TmeanX = mean(xMat,0); varX = var(xMat,0)# 數據標準化還原操作unReg = bestWeights/varXprint "the best model from Ridge Regression is:\n",unRegprint "with constant term: ",-1*sum(multiply(meanX,unReg)) + mean(yMat) # 還原計算預測結果這里采用嶺回歸來訓練模型,并且采用交叉驗證的方法來求出每個λ對應的測試誤差的均值,最后分析選出預測誤差最小的回歸模型。
注意:
- 這里對于數據集采用隨機的方式(random.shffle())選取訓練集和測試集,訓練集占數據總數的90%,測試集剩余的10%。采取這種方式的原因是,便于我們進行多次交叉驗證,得到不同的訓練集和測試集.
- 我們知道嶺回歸中會選取多個不同的λ值,來找到預測誤差最小的模型;此外,算法中采用交叉驗證的方法,所以對于每一個λ對應著多個測試誤差值,所以在分析預測效果最好的λ之前,需要先對每個λ對應的多個誤差求取均值。
- 嶺回歸算法需要對訓練集數據的每一維特征進行標準化處理,那么為保證結果的準確性,也需要對測試集進行和訓練集相同的標準化操作,即測試集數據特征減去訓練集該維度特征均值,再除以訓練集該維度特征方差
- 因為采用嶺回歸算法時,對數據進行了標準化處理,而標準的回歸算法則沒有,所以在代碼最后我們還是需要將數據進行還原,這樣便于分析比較二者的真實數據的預測誤差。
以上是很重要的幾點要注意的地方!!!
從機器學習實戰的運行結果可以看出具體的縮減過程中系數的變化情況:最后得到的回歸系數是經過不同程度的衰減得到的,大的特征系數可以看做是最重要特征,在預測時起最主要作用。特征對應的系數值越大,那么其對預測的決定作用也就越大。如果某一維度系數值為0,則表明該特征在預測結果中不起作用,可以被視為不重要特征。
所以,這種縮減的分析方法還是比較有用的,因為運算這些算法可以幫助我們充分理解和挖掘大量數據中的內在規律。當特征數較少時可能效果不夠明顯,而當特征數相當大時,我們就可以據此了解特征中哪些特征是關鍵的,哪些是不重要的,這就為我們節省不少成本和損耗。
總結:
(1) 回歸與分類的區別,前者預測連續型變量,后者預測離散型變量;回歸中求最佳系數的方法常用的是最小化誤差的平方和;如果xTx可逆,那么回歸算法可以使用;可以通過預測值和原始值的相關系數來度量回歸方程的好壞
(2) 當特征數大于樣本總數時,xTx不可逆,即便當樣本總數大于特征數,xTx的逆仍有可能無法計算,因為特征可能高度相關,我們可以通過引入嶺回歸來保證能夠求得回歸系數。
(3) 另外一種縮減算法是,前向逐步回歸算法,它是一種貪心算法,每一步通過修改某一維度特征方法來減小預測誤差,最后通過多次迭代的方法找到最小誤差對應的模型
(4) 縮減法可以看做是對一個模型增加偏差的同時減少方差,通過偏差方差折中的方法,可以幫助我們理解模型并進行改進,從而得到更好的預測結果
(5)當預測值和特征之間是非線性的關系時,這時線性的模型就難以擬合,但可以使用樹結構來預測。
shuffle()的示例:
In [2]: a=range(10)In [3]: random.shuffle(a)In [4]: a Out[4]: [0, 9, 1, 7, 3, 8, 2, 5, 4, 6]總結
以上是生活随笔為你收集整理的预测数值型数据:回归 源码分析(2)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 快速阅读训练法这本书有用吗(快速阅读训练
- 下一篇: numpy.cov()和numpy.va