一,引言
前面講到的基本都是分類問題,分類問題的目標變量是標稱型數據,或者離散型數據。而回歸的目標變量為連續型,也即是回歸對連續型變量做出預測,最直接的辦法是依據輸入寫出一個目標值的計算公式,這樣,對於給定的輸入,利用該公式可以計算出相應的預測輸出。這個公式稱為回歸方程,而求回歸方程顯然就是求該方程的回歸系數,而一旦有了這些回歸系數,再給定輸入,就可以將這些回歸系數乘以輸入值,就得到了預測值。
二,線性回歸
線性回歸,簡單而言,就是將輸入項分別乘以一些常量,再將結果加起來得到輸出。假設輸入數據存放在矩陣x中,而回歸系數存放在向量w中,那么對於給定的數據x1,預測結果將會通過y=xTw給出。那么,如何才能夠找出最佳的回歸系數向量w呢?
很容易想到使用最小化誤差的w,但是這里的誤差為預測y值和真實y值的差值,使用該誤差的簡單累加將會出現正差值和負差值的相互抵消,所以,我們可以采用平方誤差來進行度量。即:
Σ(yi-xiTW)2,i=1,2,3......N(N為樣本總數)
這樣,用矩陣表示可以寫成(y-Xw)T(y-Xw).因為要求函數的極小值,再對w求導得,xT(y-Xw),則令其等於0,即可得到w的最優解:
w*=(XTX)-1XTy
需要注意的是,公式中,出現的求逆運算,而對於任意一個矩陣而言,不一定可逆,所以,我們在實際寫代碼過程中,需要事先確定矩陣是否可逆,否則很可能會造成程序出現嚴重的錯誤。
有了回歸系數的求解公式,我們就可以寫代碼來根據樣本數據擬合出最佳的回歸系數向量w了:
form numpy import * #解析文件中的數據為適合機器處理的形式 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.extend(float(curLine[i])) dataMat.append(lineArr) labelMat.append(float(curLine[-1])) return dataMat,labelMat #標准線性回歸算法 #ws=(X.T*X).I*(X.T*Y) def standRegres(xArr,yArr): #將列表形式的數據轉為numpy矩陣形式 xMat=mat(xArr);yMat=mat(yArr).T #求矩陣的內積 xTx=xMat.T*xMat #numpy線性代數庫linalg #調用linalg.det()計算矩陣行列式 #計算矩陣行列式是否為0 if linalg.det(xTx)==0.0: print('This matrix is singular,cannot do inverse') return #如果可逆,根據公式計算回歸系數 ws=xTx.I*(xMat.T*yMat) #可以用yHat=xMat*ws計算實際值y的預測值 #返回歸系數 return ws
這里,需要說明的是,Numpy提供了一個線性代數的庫linalg,其中包含有很多有用的函數,其中就有代碼中用到的計算矩陣行列式值得函數linalg.det(),如果行列式值為0,則矩陣不可逆,如果不為0,那么就可以順利求出回歸系數。
此外,我們知道線性回歸的方程的一般形式為:y=wx+b;即存在一定的偏移量b,於是,我們可以將回歸系數和特征向量均增加一個維度,將函數的偏移值b也算作回歸系數的一部分,占據回歸系數向量中的一個維度,比如w=(w1,w2,...,wN,b)。相應地,將每條數據特征向量的第一個維度設置為1.0,即所有特征向量的第一個維度值均為1.0,這樣,最后得到的回歸函數形式為y=w[0]+w[1]*x1+...+w[N]*xN.
通過上面的數據我們可以得到一個擬合的線性回歸模型,但是我們還需要驗證該模型的好壞。如果將所有樣本的真實值y保存於一個數列,將所有的預測值yHat保存在另外一個數列,那么我們可以通過計算這兩個序列的相關系數,來度量預測值與真實值得匹配程度。我們可以通過NumPy庫中提供的corrcoef()函數計算兩個序列的相關性。比如如下代碼可以得到真實值和預測值序列的相關性,相關性最好為1,最差為0,表示不相關。
#計算預測值序列
yHat=xMat*ws corrcoef(yHat.T,yMat)
三,局部加權線性回歸
線性回歸一個比較容易出現的問題是有可能出現欠擬合現象,欠擬合顯然不能取得最好的預測效果。因為,我們求的是均方誤差最小的模型,所以可以在估計中引入一些偏差,從而降低預測的均方誤差,達到偏差和方差的折中,從而找到最佳的模型參數,即回歸系數。
解決上述問題的一個方法即是局部加權線性回歸(LWLR)。即在算法中,為每一個待預測的數據點附近的賦予一定的權重,越靠近預測點的數據點分配的權重越高。這里,我們采用高斯核函數為預測點附近的數據點分配權重,即:
w(i,i)=exp(|x(i)-x|/-2*k2),其中參數k可以由用戶自己定義。
顯然,有上面高斯核函數可知,矩陣W是一個只含對角元素的矩陣。這樣,回歸系數的解的形式變為:w*=(xTWx)-1(xTWy)。對上述代碼稍作修改之后得到如下局部加權線性回歸函數
#局部加權線性回歸 #每個測試點賦予權重系數 #@testPoint:測試點 #@xArr:樣本數據矩陣 #@yArr:樣本對應的原始值 #@k:用戶定義的參數,決定權重的大小,默認1.0 def lwlr(testPoint,xArr,yArr,k=1.0): #轉為矩陣形式 xMat=mat(xArr);yMat=mat(yArr).T #樣本數 m=shape(xMat)[0] #初始化權重矩陣為m*m的單位陣 weights=mat(eye((m))) #循環遍歷各個樣本 for j in range(m): #計算預測點與該樣本的偏差 diffMat=testPoint-xMat[j,:] #根據偏差利用高斯核函數賦予該樣本相應的權重 weights[j,j]=exp(diffMat*diffMat.T/(-2.0*k**2)) #將權重矩陣應用到公式中 xTx=xMat.T*(weights*xMat) #計算行列式值是否為0,即確定是否可逆 if linalg.det(xTx)==0.0 print('This matrix is singular,cannot do inverse') return #根據公式計算回歸系數 ws=xTx.I*(xMat.T*(weights*yMat)) #計算測試點的預測值 return testPoint*ws #測試集進行預測 def lwlrTest(testArr,xArr,yArr,k=1.0): #測試集樣本數 m=shape(testArr)[0] #測試集預測結果保存在yHat列表中 yHat=zeros(m) #遍歷每一個測試樣本 for i in range(m): #計算預測值 yHat[i]=lwlr(testArr[i],xArr,yArr,k) return yHat
需要說明的是,當為某一預測點附件的數據點分配權重時,由高斯核函數公式可知,與預測點約相近的點分配得到的權重越大,否則權重將以指數級衰減,與預測點足夠遠的數據點權重接近0,那么這些數據點將不會在該次預測中起作用。當然,用戶可以通過自己設定參數k來控制衰減的速度,如果k值較大,衰減速度較慢,這樣就會有更多的數據點共同參與決策;否則,如果參數k非常小,那么衰減速度極快,參與預測點決策的數據點就很少。所以,我們在實驗中,應該多選擇幾組不同的k值,得到不同的回歸模型,從而找到最優的回歸模型對應的k值。
下圖從上到下,依次是k取1.0,0.01及0.003情況下,對應的擬合模型。
圖中,當k=1.0(上圖)時,意味着所有的數據等權重,這樣,擬合結果與普通的線性回歸擬合結果一致,顯然很可能出現欠擬合情況。當k=0.01時(中圖),顯然得到了最好的擬合效果,因為此時掌握了數據的潛在模式,在預測時剔除了部分不重要數據的影響,增大了重要數據的權重。而k=0.003(下圖),可以看出預測時,納入了太多的噪聲點,擬合的直線與數據點過於貼近,出現了過擬合的現象。
此外,局部加權線性回歸也存在一定的問題,相對於普通的線性回歸,由於引入了權重,大大增加了計算量,雖然取得了不錯的擬合效果,但也相應地付出了計算量的代價。我們發現,在k=0.01時,大多的數據點的權重都接近0,所以,如果我們能避免這些計算,將一定程度上減少程序運行的時間,從而緩解計算量增加帶來的問題。
四,示例:預測鮑魚的年齡
接下來,就利用上述算法對真實的數據進行測試,並計算預測的誤差。鮑魚的年齡可以通過鮑魚殼的層數推算得到。在運行代碼前,我們還需要添加計算預測誤差的函數代碼:
#計算平方誤差的和 def rssError(yArr,yHatArr): #返回平方誤差和 return ((yArr-yHatArr)**2).sum()
當然,為了得到更好的效果,我們有必要采取交叉驗證的方法,選取多個樣本集來進行測試,找出預測誤差最小的回歸模型。
五,縮減系數"理解"數據的方法
試想,如果此時數據集樣本的特征維度大於樣本的數量,此時我們還能采取上面的線性回歸方法求出最佳擬合參數么?顯然不可能,因為當樣本特征維度大於樣本數時,數據矩陣顯然是非滿秩矩陣,那么對非滿秩矩陣求逆運算會出現錯誤。
為了解決這個問題,科學家提出了嶺回歸的概念,此外還有一種稱為"前向逐步回歸"的算法,該算法可以取得很好的效果且計算相對容易。
1 嶺回歸
簡單而言,嶺回歸即是在矩陣xTx上加入一個λI從而使得矩陣非奇異,進而能對矩陣xTx+λI求逆。其中矩陣I是一個單位矩陣,即對角線上元素皆為1,其他均為0。這樣,回歸系數的計算公式變為:
w*=(xTx+λI)-1(xTy)
公式中通過引入該懲罰項,從而減少不重要的參數,更好的理解和利用數據。此外,增加了相關約束:Σwi2<=λ,即回歸系數向量中每個參數的平方和不能大於λ,這就避免了當兩個或多個特征相關時,可能出現很大的正系數和很大的負系數。
上面的嶺回歸就是一種縮減方法,通過此方法可以去掉不重要的參數,更好的理解數據的重要性和非重要性,從而更好的預測數據。
在嶺回歸算法中,通過預測誤差最小化來得到最優的λ值。數據獲取之后,將數據隨機分成兩部分,一部分用於訓練模型,另一部分則用於測試預測誤差。為了找到最優的λ,可以選擇多個不同λ值重復上述測試過程,最終得到一個使預測誤差最小的λ。
#嶺回歸 #@xMat:樣本數據 #@yMat:樣本對應的原始值 #@lam:懲罰項系數lamda,默認值為0.2 def ridgeRegres(xMat,yMat,lam=0.2): #計算矩陣內積 xTx=xMat.T*xMat #添加懲罰項,使矩陣xTx變換后可逆 denom=xTx+eye(shape(xMat)[1])*lam #判斷行列式值是否為0,確定是否可逆 if linalg.det(denom)==0.0: print('This matrix is singular,cannot do inverse') return #計算回歸系數 ws=denom.I*(xMat.T*yMat) return ws #特征需要標准化處理,使所有特征具有相同重要性 def ridgeTest(xArr,yArr): xMat=mat(xArr);yMat=mat(yArr).T #計算均值 yMean=mean(yMat,0) yMat=yMat-yMean xMeans=mean(xMat,0) #計算各個特征的方差 xVar=var(xMat,0) #特征-均值/方差 xMat=(xMat-xMeans)/xVar #在30個不同的lamda下進行測試 numTestpts=30 #30次的結果保存在wMat中 wMat=zeros((numTestpts,shape(xMat)[1])) for i in range(numTestpts): #計算對應lamda回歸系數,lamda以指數形式變換 ws=ridgeRegres(xMat,yMat,exp(i-10)) wMat[i,:]=ws.T return wMat
需要說明的幾點如下:
(1)代碼中用到NumPy庫中的eye()方法來生成單位矩陣。
(2)代碼中仍保留了判斷行列式是否為0的代碼,原因是當λ取值為0時,又回到了普通的線性回歸,那么矩陣很可能出現不可逆的情況
(3)嶺回歸中數據需要進行標准化處理,即數據的每一維度特征減去相應的特征均值之后,再除以特征的方差。
(4)這里,選擇了30個不同的λ進行測試,並且這里的λ是按照指數級進行變化,從而可以看出當λ非常小和非常大的情況下對結果造成的影響
下圖示出了回歸系數與log(λ)之間的關系:
可以看出,當λ非常小時,系數與普通回歸一樣。而λ非常大時,所有回歸系數縮減為0。這樣,可以在中間的某處找到使得預測結果最好的λ。
2 逐步前向回歸
逐步前向回歸是一種貪心算法,即每一步都盡可能的減小誤差。從一開始,所有的權重都設為1,然后每一步所做的決策是對某個權重增加或減少一個較小的數值。算法的偽代碼為:
數據標准化,使其分布滿足均值為0,和方差為1
在每輪的迭代中:
設置當前最小的誤差為正無窮
對每個特征:
增大或減小:
改變一個系數得到一個新的w
計算新w下的誤差
如果誤差小於當前最小的誤差:設置最小誤差等於當前誤差
將當前的w設置為最優的w
將本次迭代得到的預測誤差最小的w存入矩陣中
返回多次迭代下的回歸系數組成的矩陣
#前向逐步回歸 #@eps:每次迭代需要調整的步長 def stageWise(xArr,yArr,eps=0.01,numIt=100): xMat=mat(xArr);yMat=mat(yArr) yMean=mean(yMat,0) yMat=yMat-yMean #將特征標准化處理為均值為0,方差為1 xMat=regularize(xMat) m,n=shape(xMat) #將每次迭代中得到的回歸系數存入矩陣 returnMat=zeros((numIt,m)) ws=zeros((n,1));wsTest=ws.copy();wsMat=ws.copy() for i in range(numIt): print ws.T #初始化最小誤差為正無窮 lowestError=inf; for j in range(n): #對每個特征的系數執行增加和減少eps*sign操作 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=rssE wsMat=wsTest ws=wsMat.copy() returnMat[i,:]=ws return returnMat
下面看一次迭代的算法執行過程,此時學習步長eps=0.01:
可以看到,上述結果中w1和w6都是0,這表明了這兩個維度的特征對預測結果不構成影響,即所謂的不重要特征。此外,在eps=0.01情況下,一段時間后系數就已經飽和並在特定值之間來回震盪,這顯然是步長太大的緣故,我們可以據此調整較小的步長。
前向逐步回歸算法也屬於縮減算法。它主要的優點是可以幫助我們更好地理解現有的模型並作出改進。當構建出一個模型時,可以運行該算法找出重要的特征,這樣就可能及時停止對不重要特征的收集。同樣,在算法測試的過程中,可以使用s折交叉驗證的方法,選擇誤差最小的模型。
此外,當我們不管是應用嶺回歸還是前向逐步回歸等縮減算法時,就相應的為模型增加了偏差,與此同時也就減小了模型的方差。而最優的模型往往是在模型偏差和方差的折中時獲得。否則,當模型方差很大偏差很小時模型復雜度很大而出現過擬合現象,而方差很小偏差很大時而容易出現欠擬合現象。因此,權衡模型的偏差和方差可以做出最好的預測。
六,嶺回歸應用示例:預測樂高玩具套裝的價格
我們知道樂高玩具是一種拼裝類玩具,由很多大小不同的塑料插件組成。一種樂高玩具套裝基本上在幾年后就會停產,但樂高收藏者之間仍會在停產后彼此交易。這樣,我們可以擬合一個回歸模型,從而對樂高套裝進行估價。顯然這樣做十分有意義。
算法流程:
1 收集數據:用google shopping的api收集數據
2 准備數據:從返回的json數據中抽取價格
3 分析數據:可視化並觀察數據
4 訓練算法:構建不同的模型,采用嶺回歸和普通線性回歸訓練模型
5 測試算法:使用交叉驗證來測試不同的模型,選擇效果最好的模型
1 收集數據:使用Google 購物的API來獲取玩具套裝的相關信息和價格,可以通過urllib2發送http請求,API將以JSON格式返回需要的產品信息,python的JSON解析模塊可以幫助我們從JSON格式中解析出所需要的數據。收集數據的代碼如下:
#收集數據 #添加時間函數庫 from time import sleep #添加json庫 import json #添加urllib2庫 import urllib2 #@retX:樣本玩具特征矩陣 #@retY:樣本玩具的真實價格 #@setNum:獲取樣本的數量 #@yr:樣本玩具的年份 #@numPce:玩具套裝的零件數 #@origPce:原始價格 def searchForSet(retX,retY,setNum,yr,numPce,origPrc): #睡眠十秒 sleep(10) #拼接查詢的url字符串 myAPIstr='get from code.google.com' searchURL='https://www.googleapis.com/shopping/search/v1/public/products?\ key=%s&country=US&q=lego+%d&alt=json' %(myAPIstr,setNum) #利用urllib2訪問url地址 pg=urllib2.urlopen(searchURL) #利用json打開和解析url獲得的數據,數據信息存入字典中 retDict=json.load(pg.read()) #遍歷數據的每一個條目 for i in range(len(retDict['items'])): try: #獲得當前條目 currItem=retDict['items'][i] #當前條目對應的產品為新產品 if currItem['product']['condition']=='new': #標記為新 newFlag=1 else:newFlag=0 #得到當前目錄產品的庫存列表 listOfInv=currItem['product']['inventories'] #遍歷庫存中的每一個條目 for item in listOfInv: #得到該條目玩具商品的價格 sellingPrice=item['price'] #價格低於原價的50%視為不完整套裝 if sellingPrice>origPrc*0.5: print('%d\t%d\t%d\t%f\t%f',%(yr,numPce,newFlag,\ origPrc,sellingPrice)) #將符合條件套裝信息作為特征存入數據矩陣 retX.append([yr,numPce,newFlag,origPce]) #將對應套裝的出售價格存入矩陣 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,49.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)
需要指出的是,我們在代碼中發送http請求,需要從NumPy導入urllib2模塊;使用json解析獲得的數據時需要導入json模塊;同時為避免多個函數同時訪問網站,在程序開始時先睡眠一定的時間,用於緩沖,即需要調用time模塊的sleep。
此外,由於套裝是由多個小插件組成,所以存在插件損失的情況,所以需要過濾掉這樣的玩具套裝,我們可以通過一定的過濾條件(一般套裝有缺失可能有標識,可通過關鍵詞篩選,或者通過貝葉斯估計估計套裝完整性),這里用的是價格來判斷,如果當前價格不到原始價格的一半,那么這樣的套裝必然有缺陷,損壞和缺失等。因為,一旦具有收藏價值的玩具套裝停產,必然價格相對上漲,所以出現這種價格反常的情況表明該產品套裝存在一定的缺陷,可以將其過濾掉。
2 訓練算法:建立模型
上面有了數據,那么我們就可以開始完成代碼進行模型訓練了,這里采用嶺回歸來訓練模型,並且采用交叉驗證的方法來求出每個λ對應的測試誤差的均值,最后分析選出預測誤差最小的回歸模型。
#訓練算法:建立模型 #交叉驗證測試嶺回歸 #@xArr:從網站中獲得的玩具套裝樣本數據 #@yArr:樣本對應的出售價格 #@numVal:交叉驗證次數 def crossValidation(xArr,yArr,numVal=10): #m,n=shape(xArr) #xArr1=mat(ones((m,n+1))) #xArr1[:,1:n+1]=mat(xArr) #獲取樣本數 m=len(yArr) indexList=range(m) #將每個回歸系數對應的誤差存入矩陣 errorMat=zeros((numVal,30)) #進行10折交叉驗證 for i in range(numVal): trainX=[];trainY=[] testX=[];testY=[] #混洗索引列表 random.shuffle(indexList) #遍歷每個樣本 for j in range(m): #數據集90%作為訓練集 if j<m*0.9: trainX.append(xArr1[indexList[j]]) trainY.append(yArr[indexList[j]]) #剩余10%作為測試集 else: testX.append(xArr1[indexList[j]]) testY.append(yArr[indexList[j]]) #利用訓練集計算嶺回歸系數 wMat=ridgeRegres(trainX,trainY) #對於每一個驗證模型的30組回歸系數 for k in range(30): #轉為矩陣形式 matTestX=mat(testX);matTrainX=mat(trainX) #求訓練集特征的均值 meanTrain=mean(matTrainX,0) #計算訓練集特征的方差 varTrain=val(matTrainX,0) #嶺回歸需要對數據特征進行標准化處理 #測試集用與訓練集相同的參數進行標准化 matTestX=(matTestX-meanTrain)/varTrain #對每組回歸系數計算測試集的預測值 yEst=matTestX*mat(wMat[k,:]).T+mean(trainY) #將原始值和預測值的誤差保存 errorMat[i,k]=rssError(yEst.T.A,array(testY)) #對誤差矩陣中每個lamda對應的10次交叉驗證的誤差結果求均值 meanErrors=mean(errorMat,0) #找到最小的均值誤差 minMean=float(min(meanErrors)) #將均值誤差最小的lamda對應的回歸系數作為最佳回歸系數 bestWeigths=wMat[nonzero(meanErrors==minMean)] xMat=mat(xArr);yMat=mat(yArr).T meanX=mean(xMat,0);valX=val(xMat,0) #數據標准化還原操作 unReg=bestWeigths/valX print('the best model from Ridge Regression is :\n',unReg) print('with constant term :',-1*sum(multiply(meanX,unReg))+mean(yMat))
同樣,這里需要說明的有以下幾點:
(1) 這里對於數據集采用隨機的方式(random.shffle())選取訓練集和測試集,訓練集占數據總數的90%,測試集剩余的10%。采取這種方式的原因是,便於我們進行多次交叉驗證,得到不同的訓練集和測試集.
(2) 我們知道嶺回歸中會選取多個不同的λ值,來找到預測誤差最小的模型;此外,算法中采用交叉驗證的方法,所以對於每一個λ對應着多個測試誤差值,所以在分析預測效果最好的λ之前,需要先對每個λ對應的多個誤差求取均值。
(3) 我們呢知道嶺回歸算法需要對訓練集數據的每一維特征進行標准化處理,那么為保證結果的准確性,也需要對測試集進行和訓練集相同的標准化操作,即測試集數據特征減去訓練集該維度特征均值,再除以訓練集該維度特征方差
(4) 因為采用嶺回歸算法時,對數據進行了標准化處理,而標准的回歸算法則沒有,所以在代碼最后我們還是需要將數據進行還原,這樣便於分析比較二者的真實數據的預測誤差。
由實驗結果得到回歸方程:
上面模型可能還是不易理解,再看一下具體的縮減過程中系數的變化情況:
最后得到的回歸系數是經過不同程度的衰減得到的。比如上圖中第一行第四項比第二項系數大5倍,比第一項大57倍。依次來看,第四特征可以看做是最重要特征,在預測時起最主要作用,其次就是第二特征。也即是,特征對應的系數值越大,那么其對預測的決定作用也就越大;如果某一維度系數值為0,則表明該特征在預測結果中不起作用,可以被視為不重要特征。
所以,這種縮減的分析方法還是比較有用的,因為運算這些算法可以幫助我們充分理解和挖掘大量數據中的內在規律。當特征數較少時可能效果不夠明顯,而當特征數相當大時,我們就可以據此了解特征中哪些特征是關鍵的,哪些是不重要的,這就為我們節省不少成本和損耗。
7,總結
(1) 回歸與分類的區別,前者預測連續型變量,后者預測離散型變量;回歸中求最佳系數的方法常用的是最小化誤差的平方和;如果xTx可逆,那么回歸算法可以使用;可以通過預測值和原始值的相關系數來度量回歸方程的好壞
(2) 當特征數大於樣本總數時,為解決xTx不可逆的問題,我們可以通過引入嶺回歸來保證能夠求得回歸系數
(3) 另外一種縮減算法是,前向逐步回歸算法,它是一種貪心算法,每一步通過修改某一維度特征方法來減小預測誤差,最后通過多次迭代的方法找到最小誤差對應的模型
(4) 縮減法可以看做是對一個模型增加偏差的同時減少方差,通過偏差方差折中的方法,可以幫助我們理解模型並進行改進,從而得到更好的預測結果