预测数值型数据:回归 源码分析(2)
4. 縮減系數(shù)來“理解”數(shù)據(jù)
4.1 嶺回歸
如果數(shù)據(jù)的特征比樣本點還多,那么就不能使用線性回歸,因為在計算(XTX)?1的時候會出錯。也就是輸入數(shù)據(jù)的矩陣X不是滿秩矩陣,非滿秩矩陣在求逆時會出現(xiàn)問題,為此有了嶺回歸。
簡單說來,嶺回歸就是在矩陣XTX上加一個λI從而使得矩陣非奇異,進而能對XTX+λI求逆。λ是一個用戶定義的數(shù)值。在這種情況下,回歸系數(shù)的計算公式將變成: 
 
嶺回歸最先用來處理特征數(shù)多于樣本數(shù)的情況,現(xiàn)在也用于在估計中加人偏差,從而得到更好的估計。這里通過引入λ來限制了所有w之和,通過引人該懲罰項,能夠減少不重要的參數(shù),這就是縮減技術。
這段話就是嶺回歸最大的特點和用處。。。。
嶺回歸代碼的原理: 
這里也是通過預測誤差最小化得到λ數(shù)據(jù)獲取之后,首先抽一部分數(shù)據(jù)用于測試,剩余的作為訓練集用于訓練參數(shù)w訓練完畢后在測試集上測試預測性能。通過選取不同的λ來重復上述測試過程,最終得到一個使預測誤差最小的λ。
這里要注意的地方就是對特征進行標準化處理:使每維特征具有相同的重要性 
 具體的做法是所有特征都減去各自的均值并除以方差。
運行結果: 
 
這里想說下結果圖的橫縱坐標: 
 
機器學習實戰(zhàn)上面的橫坐標為log(λ),而此處由結果圖可以看出橫坐標是10+log(λ),因為橫坐標是0?30,也就是以上圖的縱坐標為刻度的,例如對應0點的每個值就是λ最小時的8個特征對應的系數(shù)值,而此時的λ=e(0?10)=e?10,也就是10+log(λ)=10+log(e?10)=0既是此時的橫坐標,結果圖的其他刻度值同樣也是這樣的一個對應關系。
4.2 前向逐步回歸(貪心算法)
當最小二乘法回歸加上約束條件時:∑nk=1W2k?λ就可以得到和嶺回歸一樣的公式。 
 為什么要這樣做? 
 因為在使用普通最小二乘法時,在當兩個或者更多的特征相關時,可能會得到一個很大的正系數(shù)和一個很大的負系數(shù),但限制了所有回歸系數(shù)的平方和不大于λ時,就可以解決這個問題。
再說下lasso: 
 與嶺回歸類似,lasso也是對回歸做了限定,對應的約束:∑nk=1|Wk|?λ,這里是用的絕對值,但效果卻和嶺回歸有很大的差別:
- 絕對值:在λ很小時,一些系數(shù)會被迫縮減到0,這樣就可以洗漱數(shù)據(jù),得到更有用的特征,也就是能更好的理解數(shù)據(jù),類似于正則化中的范數(shù)。
前向逐步回歸:可以得到和lasso差不多的效果,但更加簡單,它是一種貪心算法,即每一步都盡可能的減少誤差。
# -*- coding: utf-8 -*- """ Created on Wed Oct 25 16:49:50 2017 """ from numpy import * import matplotlib.pyplot as plt# 數(shù)據(jù)導入函數(shù) def loadDataSet(fileName): numFeat = len(open(fileName).readline().split('\t')) - 1 # 得到特征數(shù) 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# 用于計算回歸系數(shù),嶺回歸 def ridgeRegres(xMat,yMat,lam=0.2): # lambda是關鍵字,此處不能用xTx = xMat.T*xMat # 構建矩陣x`xdenom = xTx + eye(shape(xMat)[1])*lam # shape(xMat)[1]得到的是特征數(shù)if linalg.det(denom) == 0.0: # linalg.det()用來計算行列式,檢查其是否為0print "This matrix is singular, cannot do inverse"returnws = denom.I * (xMat.T*yMat) # 如果矩陣非奇異,計算回歸系數(shù)并返回return ws # 返回的是回歸系數(shù)# 用于在一組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參數(shù)下調用ridgeregre()ws = ridgeRegres(xMat,yMat,exp(i-10)) # 這里lambda應以指數(shù)級變化,以觀察到有效的結果對比#print 'shape ws:',shape(ws)wMat[i,:]=ws.T # 把系數(shù)轉置后成為行向量后賦給wmat[]return wMat # 返回最終得到的30次不同的lambda得到的系數(shù)# 標準化處理:方差為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都需要是數(shù)組return ((yArr-yHatArr)**2).sum()# 前向逐步線性回歸(貪心算法) def stageWise(xArr,yArr,eps=0.01,numIt=100): # eps 每次迭代的步長,numIt 迭代的次數(shù)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): # 在所有特征上循環(huán)for sign in [-1,1]: # 每個特征運行兩次wsTest = ws.copy() # 每次的ws都是上一次最優(yōu)后得到的wsTest[j] += eps*sign # 增加或者減少該特征的影響對誤差的影響yTest = xMat*wsTestrssE = rssError(yMat.A,yTest.A) # 返回的誤差(平方誤差)if rssE < lowestError:lowestError = rssEwsMax = wsTestws = wsMax.copy() # 得到的是最小誤差的回歸系數(shù)returnMat[i,:]=ws.T # 每一行存儲的是每次迭代的回歸系數(shù)return returnMat # 最后返回的是貪心算法得到的系數(shù)矩陣# 前向逐步回歸 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()運行結果:
其中返回的系數(shù)矩陣為: 
 
- 其中的第一列和第六列都為0,即說明這兩個特征的系數(shù)為0,也就是該特征不是主要特征,從而起到降維的作用。
- 還有就是可以看出在參數(shù)步長為0.01時,一段是時間后飽和后,一些系數(shù)就在特征值之間來回震蕩,這是因為步長太大的緣故。由對比不同的步長和迭代次數(shù)可以看出系數(shù)逐步穩(wěn)定的過程,不再震蕩。
- 逐步線性回歸可以幫助理解現(xiàn)有的模型,當構建好一個模型后,可以運算該算法找出重要特征,這樣就可以停止不重要的特征的收集。如果用于測試的話,該算法可以通過迭代后構建很多的同類模型,可以使用類似于10折交叉驗證法比較這些模型,最終選擇使誤差最小的模型。
5. 方差和偏差的簡單理解
方差指的是模型之間的差異,而偏差指的是模型預測值和數(shù)據(jù)之間的差異!!!
當應用縮減方法時,模型增加了偏差,同時卻減少了模型的方差。
可以看出將一些系數(shù)縮減到很小的值或直接縮減為0,這是一個減少模型復雜度的過程,但同時也是一個增大模型偏差的過程。
方差是可以度量的。如果從鮑魚數(shù)據(jù)中取一個隨機樣本集(例如取其中100個數(shù)據(jù))并用線性模型擬合,將會得到一組回歸系數(shù)。同理,再取出另一組隨機樣本集并擬合,將會得到另一組回歸系數(shù)。這些系數(shù)間的差異大小也就是模型方差大小的反映
6. 預測樂高玩具套裝的價格
理論應用:首先從拍賣站點抽取一些數(shù)據(jù),再使用一些回歸法進行實驗來為數(shù)據(jù)找到最佳的嶺回歸模型。這樣就可以通過實際效果來看看偏差和方差間的折中效果。
算法流程:
1 收集數(shù)據(jù):用google shopping的api收集數(shù)據(jù) 
 2 準備數(shù)據(jù):從返回的json數(shù)據(jù)中抽取價格 
 3 分析數(shù)據(jù):可視化并觀察數(shù)據(jù) 
 4 訓練算法:構建不同的模型,采用嶺回歸和普通線性回歸訓練模型 
 5 測試算法:使用交叉驗證來測試不同的模型,選擇效果最好的模型
- 收集數(shù)據(jù):使用Google 購物的API來獲取玩具套裝的相關信息和價格,可以通過urllib2發(fā)送http請求,API將以JSON格式返回需要的產品信息,python的JSON解析模塊可以幫助我們從JSON格式中解析出所需要的數(shù)據(jù)。收集數(shù)據(jù)的代碼如下:
由于對爬數(shù)據(jù)不太會和其他原因,這里沒有運行出來,但我覺得還是有必要對源碼好好分析下,這里僅給出代碼解析:
# -*- coding: utf-8 -*- from time import sleep import json import urllib2# 購物信息的獲取函數(shù) def searchForSet(retX, retY, setNum, yr, numPce, origPrc): # 調用API并數(shù)據(jù)抽取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 等待返回數(shù)據(jù)retDict = json.loads(pg.read()) # 利用json打開和解析url獲得的數(shù)據(jù),數(shù)據(jù)信息存入字典中print ('i am here!')# 遍歷數(shù)據(jù)的每一個條目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]) # 將符合條件套裝信息作為特征存入數(shù)據(jù)矩陣retY.append(sellingPrice) # 將對應套裝的出售價格存入矩陣except: print ('problem with item %d' % i)# 多次調用收集數(shù)據(jù)函數(shù),獲取多組不同年份,不同價格的數(shù)據(jù) 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))其中不完整的套裝的檢索是用的啟發(fā)式!!
- 訓練算法,建立模型
要達到的目的是構建的模型可以對售價做出預測,并幫助理解現(xiàn)有數(shù)據(jù)。
現(xiàn)用嶺回歸進行模型的建立,上一篇博客講過如何對系數(shù)進行縮減,下面的代碼將是如何用縮減法確定最佳回歸系數(shù)。
# 交叉驗證測試嶺回歸 # xArr:從網(wǎng)站中獲得的玩具套裝樣本數(shù)據(jù),yArr:樣本對應的出售價格,numVal:交叉驗證的次數(shù) def crossValidation(xArr,yArr,numVal=10): m = len(yArr) # 獲取樣本數(shù) indexList = range(m)errorMat = zeros((numVal,30)) # 10次測試 每次有30組回歸系數(shù) 可以得到誤差 for i in range(numVal): # 測試次數(shù),默認為10次trainX=[]; trainY=[] # 訓練數(shù)據(jù)集和標簽 testX = []; testY = [] # 測試數(shù)據(jù)集和標簽random.shuffle(indexList) # 混洗索引列表,以實現(xiàn)訓練集或測試集數(shù)據(jù)點的隨機選取for j in range(m): # 遍歷每個樣本if j < m*0.9: # 數(shù)據(jù)集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) # 得到嶺回歸的所有回歸系數(shù),得到了30組不同的回歸系數(shù)for k in range(30): # 對于30組不同的嶺回歸得到的回歸系數(shù)進行測試,計算誤差,選取最好的matTestX = mat(testX); matTrainX=mat(trainX) meanTrain = mean(matTrainX,0) # 訓練數(shù)據(jù) 均值varTrain = var(matTrainX,0) # 訓練數(shù)據(jù) 方差# 特征減去各自的均值并除以方差,就是標準化的過程,嶺回歸需要使用標準化的數(shù)據(jù),# 因此數(shù)據(jù)也需要使用與訓練集相同的參數(shù)來標準化matTestX = (matTestX-meanTrain)/varTrain # 用訓練集的參數(shù)將測試數(shù)據(jù)標準化 # 之所以加上訓練標簽的均值也是為了標準化一致,從而得到的測試集預測值是其真實預測值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)) # 計算誤差均值最小的額回歸系數(shù)bestWeights = wMat[nonzero(meanErrors==minMean)] # 最好的回歸系數(shù)# 將標準化后的數(shù)據(jù)還原用于可視化 #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)# 數(shù)據(jù)標準化還原操作unReg = bestWeights/varXprint "the best model from Ridge Regression is:\n",unRegprint "with constant term: ",-1*sum(multiply(meanX,unReg)) + mean(yMat) # 還原計算預測結果這里采用嶺回歸來訓練模型,并且采用交叉驗證的方法來求出每個λ對應的測試誤差的均值,最后分析選出預測誤差最小的回歸模型。
注意:
- 這里對于數(shù)據(jù)集采用隨機的方式(random.shffle())選取訓練集和測試集,訓練集占數(shù)據(jù)總數(shù)的90%,測試集剩余的10%。采取這種方式的原因是,便于我們進行多次交叉驗證,得到不同的訓練集和測試集.
- 我們知道嶺回歸中會選取多個不同的λ值,來找到預測誤差最小的模型;此外,算法中采用交叉驗證的方法,所以對于每一個λ對應著多個測試誤差值,所以在分析預測效果最好的λ之前,需要先對每個λ對應的多個誤差求取均值。
- 嶺回歸算法需要對訓練集數(shù)據(jù)的每一維特征進行標準化處理,那么為保證結果的準確性,也需要對測試集進行和訓練集相同的標準化操作,即測試集數(shù)據(jù)特征減去訓練集該維度特征均值,再除以訓練集該維度特征方差
- 因為采用嶺回歸算法時,對數(shù)據(jù)進行了標準化處理,而標準的回歸算法則沒有,所以在代碼最后我們還是需要將數(shù)據(jù)進行還原,這樣便于分析比較二者的真實數(shù)據(jù)的預測誤差。
以上是很重要的幾點要注意的地方!!!
從機器學習實戰(zhàn)的運行結果可以看出具體的縮減過程中系數(shù)的變化情況:最后得到的回歸系數(shù)是經(jīng)過不同程度的衰減得到的,大的特征系數(shù)可以看做是最重要特征,在預測時起最主要作用。特征對應的系數(shù)值越大,那么其對預測的決定作用也就越大。如果某一維度系數(shù)值為0,則表明該特征在預測結果中不起作用,可以被視為不重要特征。
所以,這種縮減的分析方法還是比較有用的,因為運算這些算法可以幫助我們充分理解和挖掘大量數(shù)據(jù)中的內在規(guī)律。當特征數(shù)較少時可能效果不夠明顯,而當特征數(shù)相當大時,我們就可以據(jù)此了解特征中哪些特征是關鍵的,哪些是不重要的,這就為我們節(jié)省不少成本和損耗。
總結:
(1) 回歸與分類的區(qū)別,前者預測連續(xù)型變量,后者預測離散型變量;回歸中求最佳系數(shù)的方法常用的是最小化誤差的平方和;如果xTx可逆,那么回歸算法可以使用;可以通過預測值和原始值的相關系數(shù)來度量回歸方程的好壞
(2) 當特征數(shù)大于樣本總數(shù)時,xTx不可逆,即便當樣本總數(shù)大于特征數(shù),xTx的逆仍有可能無法計算,因為特征可能高度相關,我們可以通過引入嶺回歸來保證能夠求得回歸系數(shù)。
(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
