機器學習理論與實戰(八)迴歸 相關公式詳解

機器學習理論與實戰(八)迴歸

       按照《機器學習實戰》的主線,結束有監督學習中關於分類的機器學習方法,進入迴歸部分。所謂迴歸就是數據進行曲線擬合,迴歸一般用來做預測,涵蓋線性迴歸(經典最小二乘法)、局部加權線性迴歸、嶺迴歸和逐步線性迴歸。先來看下線性迴歸,即經典最小二乘法,說到最小二乘法就不得說下線性代數,因爲一般說線性迴歸只通過計算一個公式就可以得到答案,如(公式一)所示:


(公式一)

       其中X是表示樣本特徵組成的矩陣,Y表示對應的值,比如房價,股票走勢等,(公式一)是直接通過對(公式二)求導得到的,因爲(公式二)是凸函數,導數等於零的點就是最小點。

(公式二)

         不過並不是所有的碼農能從(公式二)求導得到(公式一)的解,因此這裏給出另外一個直觀的解,直觀理解建立起來後,後續幾個迴歸就簡單類推咯。從初中的投影點說起,如(圖一)所示:

(圖一)

       在(圖一)中直線a上離點b最近的點是點b在其上的投影,即垂直於a的交點p。p是b在a上的投影點。試想一下,如果我們把WX看成多維的a,即空間中的一個超面來代替二維空間中的直線,而y看成b,那現在要使得(公式二)最小是不是就是尋找(圖一)中的e,即垂直於WX的垂線,因爲只有垂直時e才最小。下面來看看如何通過尋找垂線並最終得到W。要尋找垂線,先從(圖二)中的夾角theta 說起吧,因爲當cos(theta)=0時,他們也就垂直了。下面來分析下直線或者向量之間的夾角,如(圖二)所示:


(圖二)

       在(圖二)中,表示三角形的斜邊,那麼:

       角beta也可以得到同樣的計算公式,接着利用三角形和差公式得到(公式三):


(公式三)

        (公式三)表示的是兩直線或者兩向量之間的夾角公式,很多同學都學過。再仔細看下,發現分子其實是向量a,b之間的內積(點積),因此公式三變爲簡潔的(公式四)的樣子:

(公式四)

接下來繼續分析(圖一)中的投影,爲了方便觀看,增加了一些提示如(圖三)所示:

(圖三)

        在(圖三)中,假設向量b在向量a中的投影爲p(注意,這裏都上升爲向量空間,不再使用直線,因爲(公式四)是通用的)。投影p和a 在同一方向上(也可以反方向),因此我們可以用一個係數乘上a來表示p,比如(圖三)中的,有了投影向量p,那麼我們就可以表示向量e,因爲根據向量法則,,有因爲a和e垂直,因此,展開求得係數x,如(公式五)所示:


(公式五)

(公式五)是不是很像(公式一)?只不過(公式一)的分母寫成了另外的形式,不過別急,現在的係數只是一個標量數字,因爲a,b都是一個向量,我們要擴展一下,把a從向量擴展到子空間,因爲(公式一)中的X是樣本矩陣,矩陣有列空間和行空間,如(圖四)所示:

(圖四)

        (圖四)中的A表示樣本矩陣X,假設它有兩個列a1和a2,我們要找一些線性組合係數來找一個和(圖三)一樣的接受b 投影的向量,而這個向量通過矩陣列和係數的線性組合表示。求解的這個係數的思路和上面完全一樣,就是尋找投影所在的向量和垂線e的垂直關係,得到係數,如(公式六)所示:

(公式六)

       這下(公式六)和(公式一)完全一樣了,基於最小二乘法的線性迴歸也就推導完成了,而局部加權迴歸其實只是相當於對不同樣本之間的關係給出了一個權重,所以叫局部加權,如(公式七)所示:

(公式七)

       而權重的計算可通過高斯核(高斯公式)來完成,核的作用就是做權重衰減,很多地方都要用到,表示樣本的重要程度,一般離目標進的重要程度大些,高斯核可以很好的描述這種關係。如(公式八)所示,其中K是個超參數,根據情況靈活設置:

(公式八)

       (圖五)是當K分別爲1.0, 0.01,0.003時的局部加權線性迴歸的樣子,可以看出當K=1.0時,和線性迴歸沒區別:

(圖五)

  而嶺迴歸的樣子如(公式九)所示:

(公式九)

        嶺迴歸主要是解決的問題就是當XX’無法求逆時,比如當特徵很多,樣本很少,矩陣X不是滿秩矩陣,此時求逆會出錯,但是通過加上一個對角爲常量lambda的矩陣,就可以很巧妙的避免這個計算問題,因此會多一個參數lambda,lambda的最優選擇由交叉驗證(cross-validation)來決定,加上一個對角不爲0的矩陣很形象的在對角上擡高了,因此稱爲嶺。不同的lambda會使得係數縮減,如(圖六)所示:

(圖六)

         說到係數縮減大家可能會覺得有奇怪,感覺有點類似於正則,但是這裏只是相當於在(公式六)中增大分母,進而縮小系數,另外還有一些係數縮減的方法,比如直接增加一些約束,如(公式十)和(公式十一)所示:

(公式十)

(公式十一)

        當線性迴歸增加了(公式十)的約束變得和橋迴歸差不多,係數縮減了,而如果增加了(公式十一)的約束時就是稀疏迴歸咯,(我自己造的名詞,sorry),係數有一些0。

有了約束後,求解起來就不像上面那樣直接計算個矩陣運算就行了,回顧第五節說中支持向量機原理,需要使用二次規劃求解,不過仍然有一些像SMO算法一樣的簡化求解算法,比如前向逐步迴歸方法:

前向逐步迴歸的僞代碼如(圖七)所示,也不難,仔細閱讀代碼就可以理解:

(圖七)

 

     下面直接給出上面四種迴歸的代碼:

     

[python] view plaincopy
  1. from numpy import *  
  2.   
  3. def loadDataSet(fileName):      #general function to parse tab -delimited floats  
  4.     numFeat = len(open(fileName).readline().split('\t')) - 1 #get number of fields   
  5.     dataMat = []; labelMat = []  
  6.     fr = open(fileName)  
  7.     for line in fr.readlines():  
  8.         lineArr =[]  
  9.         curLine = line.strip().split('\t')  
  10.         for i in range(numFeat):  
  11.             lineArr.append(float(curLine[i]))  
  12.         dataMat.append(lineArr)  
  13.         labelMat.append(float(curLine[-1]))  
  14.     return dataMat,labelMat  
  15.   
  16. def standRegres(xArr,yArr):  
  17.     xMat = mat(xArr); yMat = mat(yArr).T  
  18.     xTx = xMat.T*xMat  
  19.     if linalg.det(xTx) == 0.0:  
  20.         print "This matrix is singular, cannot do inverse"  
  21.         return  
  22.     ws = xTx.I * (xMat.T*yMat)  
  23.     return ws  
  24.   
  25. def lwlr(testPoint,xArr,yArr,k=1.0):  
  26.     xMat = mat(xArr); yMat = mat(yArr).T  
  27.     m = shape(xMat)[0]  
  28.     weights = mat(eye((m)))  
  29.     for j in range(m):                      #next 2 lines create weights matrix  
  30.         diffMat = testPoint - xMat[j,:]     #  
  31.         weights[j,j] = exp(diffMat*diffMat.T/(-2.0*k**2))  
  32.     xTx = xMat.T * (weights * xMat)  
  33.     if linalg.det(xTx) == 0.0:  
  34.         print "This matrix is singular, cannot do inverse"  
  35.         return  
  36.     ws = xTx.I * (xMat.T * (weights * yMat))  
  37.     return testPoint * ws  
  38.   
  39. def lwlrTest(testArr,xArr,yArr,k=1.0):  #loops over all the data points and applies lwlr to each one  
  40.     m = shape(testArr)[0]  
  41.     yHat = zeros(m)  
  42.     for i in range(m):  
  43.         yHat[i] = lwlr(testArr[i],xArr,yArr,k)  
  44.     return yHat  
  45.   
  46. def lwlrTestPlot(xArr,yArr,k=1.0):  #same thing as lwlrTest except it sorts X first  
  47.     yHat = zeros(shape(yArr))       #easier for plotting  
  48.     xCopy = mat(xArr)  
  49.     xCopy.sort(0)  
  50.     for i in range(shape(xArr)[0]):  
  51.         yHat[i] = lwlr(xCopy[i],xArr,yArr,k)  
  52.     return yHat,xCopy  
  53.   
  54. def rssError(yArr,yHatArr): #yArr and yHatArr both need to be arrays  
  55.     return ((yArr-yHatArr)**2).sum()  
  56.   
  57. def ridgeRegres(xMat,yMat,lam=0.2):  
  58.     xTx = xMat.T*xMat  
  59.     denom = xTx + eye(shape(xMat)[1])*lam  
  60.     if linalg.det(denom) == 0.0:  
  61.         print "This matrix is singular, cannot do inverse"  
  62.         return  
  63.     ws = denom.I * (xMat.T*yMat)  
  64.     return ws  
  65.       
  66. def ridgeTest(xArr,yArr):  
  67.     xMat = mat(xArr); yMat=mat(yArr).T  
  68.     yMean = mean(yMat,0)  
  69.     yMat = yMat - yMean     #to eliminate X0 take mean off of Y  
  70.     #regularize X's  
  71.     xMeans = mean(xMat,0)   #calc mean then subtract it off  
  72.     xVar = var(xMat,0)      #calc variance of Xi then divide by it  
  73.     xMat = (xMat - xMeans)/xVar  
  74.     numTestPts = 30  
  75.     wMat = zeros((numTestPts,shape(xMat)[1]))  
  76.     for i in range(numTestPts):  
  77.         ws = ridgeRegres(xMat,yMat,exp(i-10))  
  78.         wMat[i,:]=ws.T  
  79.     return wMat  
  80.   
  81. def regularize(xMat):#regularize by columns  
  82.     inMat = xMat.copy()  
  83.     inMeans = mean(inMat,0)   #calc mean then subtract it off  
  84.     inVar = var(inMat,0)      #calc variance of Xi then divide by it  
  85.     inMat = (inMat - inMeans)/inVar  
  86.     return inMat  
  87.   
  88. def stageWise(xArr,yArr,eps=0.01,numIt=100):  
  89.     xMat = mat(xArr); yMat=mat(yArr).T  
  90.     yMean = mean(yMat,0)  
  91.     yMat = yMat - yMean     #can also regularize ys but will get smaller coef  
  92.     xMat = regularize(xMat)  
  93.     m,n=shape(xMat)  
  94.     #returnMat = zeros((numIt,n)) #testing code remove  
  95.     ws = zeros((n,1)); wsTest = ws.copy(); wsMax = ws.copy()  
  96.     for i in range(numIt):  
  97.         print ws.T  
  98.         lowestError = inf;   
  99.         for j in range(n):  
  100.             for sign in [-1,1]:  
  101.                 wsTest = ws.copy()  
  102.                 wsTest[j] += eps*sign  
  103.                 yTest = xMat*wsTest  
  104.                 rssE = rssError(yMat.A,yTest.A)  
  105.                 if rssE < lowestError:  
  106.                     lowestError = rssE  
  107.                     wsMax = wsTest  
  108.         ws = wsMax.copy()  
  109.         #returnMat[i,:]=ws.T  
  110.     #return returnMat  
  111.   
  112. #def scrapePage(inFile,outFile,yr,numPce,origPrc):  
  113. #    from BeautifulSoup import BeautifulSoup  
  114. #    fr = open(inFile); fw=open(outFile,'a') #a is append mode writing  
  115. #    soup = BeautifulSoup(fr.read())  
  116. #    i=1  
  117. #    currentRow = soup.findAll('table', r="%d" % i)  
  118. #    while(len(currentRow)!=0):  
  119. #        title = currentRow[0].findAll('a')[1].text  
  120. #        lwrTitle = title.lower()  
  121. #        if (lwrTitle.find('new') > -1) or (lwrTitle.find('nisb') > -1):  
  122. #            newFlag = 1.0  
  123. #        else:  
  124. #            newFlag = 0.0  
  125. #        soldUnicde = currentRow[0].findAll('td')[3].findAll('span')  
  126. #        if len(soldUnicde)==0:  
  127. #            print "item #%d did not sell" % i  
  128. #        else:  
  129. #            soldPrice = currentRow[0].findAll('td')[4]  
  130. #            priceStr = soldPrice.text  
  131. #            priceStr = priceStr.replace('$','') #strips out $  
  132. #            priceStr = priceStr.replace(',','') #strips out ,  
  133. #            if len(soldPrice)>1:  
  134. #                priceStr = priceStr.replace('Free shipping', '') #strips out Free Shipping  
  135. #            print "%s\t%d\t%s" % (priceStr,newFlag,title)  
  136. #            fw.write("%d\t%d\t%d\t%f\t%s\n" % (yr,numPce,newFlag,origPrc,priceStr))  
  137. #        i += 1  
  138. #        currentRow = soup.findAll('table', r="%d" % i)  
  139. #    fw.close()  
  140.       
  141. from time import sleep  
  142. import json  
  143. import urllib2  
  144. def searchForSet(retX, retY, setNum, yr, numPce, origPrc):  
  145.     sleep(10)  
  146.     myAPIstr = 'AIzaSyD2cR2KFyx12hXu6PFU-wrWot3NXvko8vY'  
  147.     searchURL = 'https://www.googleapis.com/shopping/search/v1/public/products?key=%s&country=US&q=lego+%d&alt=json' % (myAPIstr, setNum)  
  148.     pg = urllib2.urlopen(searchURL)  
  149.     retDict = json.loads(pg.read())  
  150.     for i in range(len(retDict['items'])):  
  151.         try:  
  152.             currItem = retDict['items'][i]  
  153.             if currItem['product']['condition'] == 'new':  
  154.                 newFlag = 1  
  155.             else: newFlag = 0  
  156.             listOfInv = currItem['product']['inventories']  
  157.             for item in listOfInv:  
  158.                 sellingPrice = item['price']  
  159.                 if  sellingPrice > origPrc * 0.5:  
  160.                     print "%d\t%d\t%d\t%f\t%f" % (yr,numPce,newFlag,origPrc, sellingPrice)  
  161.                     retX.append([yr, numPce, newFlag, origPrc])  
  162.                     retY.append(sellingPrice)  
  163.         exceptprint 'problem with item %d' % i  
  164.       
  165. def setDataCollect(retX, retY):  
  166.     searchForSet(retX, retY, 8288200680049.99)  
  167.     searchForSet(retX, retY, 1003020023096269.99)  
  168.     searchForSet(retX, retY, 1017920075195499.99)  
  169.     searchForSet(retX, retY, 1018120073428199.99)  
  170.     searchForSet(retX, retY, 1018920085922299.99)  
  171.     searchForSet(retX, retY, 1019620093263249.99)  
  172.       
  173. def crossValidation(xArr,yArr,numVal=10):  
  174.     m = len(yArr)                             
  175.     indexList = range(m)  
  176.     errorMat = zeros((numVal,30))#create error mat 30columns numVal rows  
  177.     for i in range(numVal):  
  178.         trainX=[]; trainY=[]  
  179.         testX = []; testY = []  
  180.         random.shuffle(indexList)  
  181.         for j in range(m):#create training set based on first 90% of values in indexList  
  182.             if j < m*0.9:   
  183.                 trainX.append(xArr[indexList[j]])  
  184.                 trainY.append(yArr[indexList[j]])  
  185.             else:  
  186.                 testX.append(xArr[indexList[j]])  
  187.                 testY.append(yArr[indexList[j]])  
  188.         wMat = ridgeTest(trainX,trainY)    #get 30 weight vectors from ridge  
  189.         for k in range(30):#loop over all of the ridge estimates  
  190.             matTestX = mat(testX); matTrainX=mat(trainX)  
  191.             meanTrain = mean(matTrainX,0)  
  192.             varTrain = var(matTrainX,0)  
  193.             matTestX = (matTestX-meanTrain)/varTrain #regularize test with training params  
  194.             yEst = matTestX * mat(wMat[k,:]).T + mean(trainY)#test ridge results and store  
  195.             errorMat[i,k]=rssError(yEst.T.A,array(testY))  
  196.             #print errorMat[i,k]  
  197.     meanErrors = mean(errorMat,0)#calc avg performance of the different ridge weight vectors  
  198.     minMean = float(min(meanErrors))  
  199.     bestWeights = wMat[nonzero(meanErrors==minMean)]  
  200.     #can unregularize to get model  
  201.     #when we regularized we wrote Xreg = (x-meanX)/var(x)  
  202.     #we can now write in terms of x not Xreg:  x*w/var(x) - meanX/var(x) +meanY  
  203.     xMat = mat(xArr); yMat=mat(yArr).T  
  204.     meanX = mean(xMat,0); varX = var(xMat,0)  
  205.     unReg = bestWeights/varX  
  206.     print "the best model from Ridge Regression is:\n",unReg  
  207.     print "with constant term: ",-1*sum(multiply(meanX,unReg)) + mean(yMat)  


以上各種迴歸方法沒有考慮實際數據的噪聲,如果噪聲很多,直接用上述的迴歸不是太好,因此需要加上正則,然後迭代更新權重

參考文獻:

         [1] machine learning in action.Peter Harrington 

       [2]Linear Algebra and Its Applications_4ed.Gilbert_Strang


轉載請註明來源:http://blog.csdn.net/cuoqu/article/details/9387305

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章