本節(jié)主要介紹回歸的代碼實現(xiàn),包括線性回歸,局部加權(quán)線性回歸,嶺回歸和逐步線性回歸。
一、線性回歸
“回歸”一詞的來歷:由達爾文的表型第Galton發(fā)明。他注意到,如果雙親的高度比平均高度高,他的子女也傾向于比平均高度高,但尚不及雙親。孩子的高度向著平均高度回退(回歸)。Galton在多項研究上都注意到這個現(xiàn)象,所以盡管這個英文單詞跟數(shù)值預(yù)測沒有任何關(guān)系,但是這種研究方法仍被稱作回歸。
1、回歸系數(shù)w是怎么求得的?
本節(jié)求得回歸系數(shù)w的方法,即對(yi-xiw)2,其中的X是代表數(shù)據(jù)的矩陣,于是對回歸系數(shù)w求導(dǎo)之后令導(dǎo)數(shù)為零,得到
一定要注意的是,此處的XTX的逆矩陣一定要存在。所以必須要在代碼中對矩陣是否存在逆做出判斷。
2、標(biāo)準(zhǔn)回歸函數(shù)和數(shù)據(jù)導(dǎo)入函數(shù)代碼
(剛?cè)腴T,可能我覺得好的地方對別人來說很low,管他呢)
這個代碼分為兩段,一個是數(shù)據(jù)導(dǎo)入函數(shù)一個是標(biāo)準(zhǔn)回歸函數(shù)。
其中判斷矩陣是否可逆用行列式是否為零來判斷,numpy提供一個線性代數(shù)的庫linalg,其中包含很多有用的函數(shù)。
def loadDataSet(fileName):
#得到特征值的個數(shù)!把一行的個數(shù)減去標(biāo)簽數(shù)之后,就是一個數(shù)據(jù)的特征個數(shù)。方便下面的循環(huán)賦值
numFeat = len(open(fileName).readline().split('\t')) - 1
dataMat = []; labelMat = []
fr = open(fileName)
for line in fr.readlines():
lineArr =[]
curLine = line.strip().split('\t')
#將特征賦值,并且不會把標(biāo)簽添加進去
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):
xTx = xMat.T*xMat
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
二、局部加權(quán)線性回歸
1、為什么采用局部加權(quán)線性回歸?
線性回歸有一個比較嚴(yán)重的問題,就是有可能出現(xiàn)欠擬合現(xiàn)象,因為它求的是具有最小均方誤差的無偏估計。
比較形象的說,就是用一條滿足最小均方誤差直線,去擬合所有的數(shù)據(jù),誤差是比較大的。
于是,比較好的方法,就是用部分的數(shù)據(jù),去擬合你需要預(yù)測的點,這樣的話,誤差就不是很大。
局部加權(quán)線性回歸和KNN類似,該模型認(rèn)為樣本點距離越近,越可能符合同一個線性模型。
2、局部加權(quán)線性回歸的w和W
小w還是回歸系數(shù)。
W代表的是一個只含有對角元素的權(quán)重矩陣。二者關(guān)系如下
由公式可見,直接效果就是所有的樣本點數(shù)據(jù)都會有一個權(quán)值。
LWLR使用“核”來調(diào)節(jié)樣本點的權(quán)重,使得距離需要預(yù)測的點越近得到樣本點權(quán)重更大。核的類型可以自由選擇,最常用就是高斯核(高斯分布中的exp),其對應(yīng)的權(quán)重如下:
上述公式的目的,就是使得**預(yù)測點x與樣本點x(i)越近,權(quán)值w(i,i)就會越大。
該公式包含了一個 參數(shù)k它決定了對附近的點賦予多大的權(quán)重,也是LWLR唯一需要考慮的參數(shù)。k越小,權(quán)值就衰減的越快,則被用上的局部點就越少,但是相對的也可能會考慮太多的噪聲,導(dǎo)致過擬合現(xiàn)象。
如上圖,平滑參數(shù)k分別是1,0.01和0.003時的情況。可以看到,k=1時模型效果跟最小二乘法差不多,k=0.01時該模型可以挖掘數(shù)據(jù)的潛在規(guī)律,而k=0.003時,考慮了太多的噪聲,導(dǎo)致了過擬合。
3、代碼實現(xiàn)
此模型的代碼,和標(biāo)準(zhǔn)線性回歸模型的代碼相比,只是增加了一個W矩陣。因此,需要先給W賦初值(對角矩陣)以后,再計算矩陣中元素,再按照,模型計算即可。
#只對一個點進行測試
def lwlr(testPoint,xArr,yArr,k=1.0):
xMat = mat(xArr); yMat = mat(yArr).T
#用此方法計算出樣本數(shù)量
m = shape(xMat)[0]
#先給w矩陣賦初值,先弄明白矩陣w的shape
weights = mat(eye((m)))
#對每個樣本點計算一次權(quán)重
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)
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
#將所有需要測試的點,用testArr傳入,再用上面函數(shù)挨個測試,返回所有預(yù)測的值
def lwlrTest(testArr,xArr,yArr,k=1.0):
#得到所有預(yù)測點的總數(shù)以后再構(gòu)造一個相同數(shù)目的列表
m = shape(testArr)[0]
yHat = zeros(m)
for i in range(m):
yHat[i] = lwlr(testArr[i],xArr,yArr,k)
return yHat
上面代碼,比較好的是,整體的思想。一個功能就是一個函數(shù),只需要把數(shù)據(jù)整體傳入,得到整體需要預(yù)測的數(shù)據(jù)。
但是,局部加權(quán)線性回歸也存在一個問題:增加了計算量,因為它對每個點做預(yù)測時都必須使用整個數(shù)據(jù)集。然而,大多數(shù)據(jù)點的權(quán)重都接近與零,所以,如果避免這些計算,將可以減少程序運行時間,緩解計算量。
三、嶺回歸
首先需要注意的是,嶺回歸和局部加權(quán)線性回歸沒有聯(lián)系。二者都是和標(biāo)準(zhǔn)線性回歸進行比較的。
局部加權(quán)線性回歸是通過設(shè)置權(quán)值來利用局部樣本點預(yù)測的。
而嶺回歸是用在數(shù)據(jù)的特征比樣本點還多的情況,通過增加正則項,縮減不必要的特征參數(shù),使得我們可以挖掘大量數(shù)據(jù)的內(nèi)在規(guī)律。
優(yōu)點:
比如,經(jīng)過嶺回歸訓(xùn)練之后,返回回歸系數(shù),會發(fā)現(xiàn)有的特征比別的特征大很多倍。(書中最后測試,第4項特征比第2項系數(shù)大5倍,比第1項大57倍,所以如果只能選擇一個特征進行預(yù)測,我們應(yīng)該選擇第4個特征。)
再如,當(dāng)數(shù)據(jù)由100個以上的特征的時候,嶺回歸就會十分有效:它可以指出哪些特征是關(guān)鍵的,哪些特征是不重要的。
1、嶺回歸中的嶺是什么?
嶺回歸中是用單位矩陣乘以 常量lambda,其中的單位矩陣I,值1貫穿整個對角線,其余元素全是0,在0構(gòu)成的平面上有一條1組成的“嶺”,這就是嶺回歸中的“嶺”的由來。
2、怎么理解嶺回歸(最小二乘后面加上正則項)?
2.1 理論
如果數(shù)據(jù)的特征比樣本點還多怎么辦?此時X不是滿秩矩陣,求逆就會出現(xiàn)錯誤。比如shape(X)=(3,10),那么shape(XTX)=(10,10)。于是出現(xiàn)了一種方法:嶺回歸。
從最優(yōu)化理論上理解嶺回歸的作用,一般三個方面:矩陣滿秩,正則項,過擬合。
知乎上guosc的回答容易理解:guosc[嶺回歸與最小二乘的區(qū)別]
直觀上來說訓(xùn)練數(shù)據(jù)較少時容易發(fā)生過擬合,過擬合曲線會盡可能擬合所有數(shù)據(jù)點,包括噪音點,此時由于函數(shù)的導(dǎo)數(shù)較大,因此系數(shù)較大,為了避免過擬合需要減小系數(shù),正則化就是通過對系數(shù)進行懲罰以減小系數(shù)。這樣得到的會是一條光滑的曲線,會有較好的泛化性能。
以上說明了嶺回歸中加上正則項的作用:不會過擬合。
2.2 縮減系數(shù)可視化
和最優(yōu)化問題中連接起來,嶺回歸就是在最小二乘后面加上懲罰項,當(dāng)后面的lambda越大時,前面的回歸系數(shù)就會變小(用書中的話來說就是 縮減),達到縮減不必要的參數(shù)的目的。
從上圖可以看出,隨著lambda的不斷增大,各個特征值的回歸系數(shù)會不斷縮小,我們可以在中間的某處(15-20)找的使得預(yù)測結(jié)果最好的lambda值。或者說,此時我們可以看出哪個特征是重要的(比如藍線比較重要),哪個是不重要的(接近零的線)。從而判斷出“不必要的參數(shù)”,提取出重要的特征。
2.3 公式
在最小二乘后面加上二范數(shù)的正則項以后,同標(biāo)準(zhǔn)線性回歸一樣,用矩陣求解的方法,可以得到此時的回歸系數(shù):
3、嶺回歸的代碼實現(xiàn)(縮減系數(shù)可視化)
3.1、代碼敘述
- 1、嶺回歸中的權(quán)值w代碼,按照上面公式推算就行,其中對角矩陣的shape要通過數(shù)據(jù)矩陣xMat得到,行數(shù)為shape(xMat)[1]。即數(shù)據(jù)的特征個數(shù)。對角矩陣用eye()得到。
- 2 因為每個特征的屬性不一樣,為了使用嶺回歸和縮減技術(shù),首先需要對特征做標(biāo)準(zhǔn)化處理,是每維特征具有相同的重要性。個人覺得是嶺回歸中w的求解公式中加上了lambda對角陣,為了使lambda對每個特征的影響都相同,所以需要標(biāo)準(zhǔn)化每個特征。
下面代碼中數(shù)據(jù)標(biāo)準(zhǔn)化的過程,是所有特征都減去各自的均值并除以方差。(代碼中有的方法和書中所述有些出入)
3.2 代碼
def ridgeRegres(xMat,yMat,lam=0.2):
xTx = xMat.T*xMat
denom = xTx + eye(shape(xMat)[1])*lam
#仍然需要檢查行列式是否為零,因為如果lambda設(shè)定為0的時候,也會產(chǎn)生錯誤
if linalg.det(denom) == 0.0:
print ("This matrix is singular, cannot do inverse")
return
ws = denom.I * (xMat.T*yMat)
return ws
#在30個不同的lambda下調(diào)用ridgeRegres函數(shù),返回一個回歸系數(shù)構(gòu)成的矩陣
def ridgeTest(xArr,yArr):
xMat = mat(xArr); yMat=mat(yArr).T
yMean = mean(yMat,0)
yMat = yMat - yMean #to eliminate X0 take mean off of Y
#regularize X's
xMeans = mean(xMat,0) #calc mean then subtract it off
xVar = var(xMat,0) #calc variance of Xi then divide by it
xMat = (xMat - xMeans)/xVar
#第一步,給矩陣賦初值
numTestPts = 30
wMat = zeros((numTestPts,shape(xMat)[1]))
for i in range(numTestPts):
ws = ridgeRegres(xMat,yMat,exp(i-10))
#第二步,給矩陣的每一行,賦值。ws返回的是一個列向量,所以需要轉(zhuǎn)置
wMat[i,:]=ws.T
return wMat
四、前向逐步回歸
1、算法簡介
前向逐步回歸屬于一種貪心算法==> 每一步都盡可能減少誤差。
大致實現(xiàn)就是,每次增大或減小當(dāng)前的某一個特征,得到一個新的W,計算新W下的誤差,如果新W得到的誤差小于舊的W所得的誤差,那么就用新的W替換舊的W。不斷循環(huán),直至收斂。
2 、代碼實現(xiàn)
下面是代碼實現(xiàn)。其中需要注意的是,在替換W的步驟中,為了不原地改變列表,用了copy的方法。
def stageWise(xArr,yArr,eps=0.01,numIt=100):
xMat = mat(xArr); yMat=mat(yArr).T
yMean = mean(yMat,0)
#這地方并沒有進行標(biāo)準(zhǔn)化,右邊有解釋
yMat = yMat - yMean #can also regularize ys but will get smaller coef
xMat = regularize(xMat)
m,n=shape(xMat)
#returnMat = zeros((numIt,n)) #testing code remove
ws = zeros((n,1)); wsTest = ws.copy(); wsMax = ws.copy()
for i in range(numIt):
print (ws.T)
lowestError = inf; #設(shè)置誤差初始值為無窮
for j in range(n):
for sign in [-1,1]:
wsTest = ws.copy()
wsTest[j] += eps*sign #每次增加或減少第j個特征的eps倍
yTest = xMat*wsTest
rssE = rssError(yMat.A,yTest.A)
#如果新的誤差小于舊的誤差,就替換誤差和W
if rssE < lowestError:
lowestError = rssE
wsMax = wsTest
ws = wsMax.copy()
returnMat[i,:]=ws.T
return returnMat
3、算法優(yōu)點
逐步線性回歸算法的主要優(yōu)點,在于它可以幫助人們理解現(xiàn)有的模型并作出改進。當(dāng)構(gòu)建了一個模型后,可以運行該算法找出重要的特征。能夠及時停止對那些不重要特征的收集。(比如,原數(shù)據(jù)有6個特征,運行此算法之后發(fā)現(xiàn)w1和w6還是沒有變化,就說明這兩個特征不對目標(biāo)值造成任何影響,也就意味著這兩個特征很可能是不需要的。)
五、方差與偏差
1、定義
方差:指的是模型之間的差異。
偏差:指的是模型預(yù)測值和數(shù)據(jù)真實值之間的差異。
模型之間的差異:比如從數(shù)據(jù)中取一根隨機樣本集(隨機取100個數(shù)據(jù))并用線性模型擬合,將會得到一組回歸系數(shù)w1、再取出另一組隨機樣本集并擬合,得到另一組回歸系數(shù)w2。那么w1和w2之間的差異大小,就是模型方差大小的反映。
2、二者之間的權(quán)衡
若方差低(一般來說兩組線性模型的擬合系數(shù)差異相對小些,比如標(biāo)準(zhǔn)線性回歸不同的隨機樣本集,回歸系數(shù)差別不大),也就是說,模型復(fù)雜度較低(可能該模型干脆就是線性模型),比較不容易擬合數(shù)據(jù),預(yù)測數(shù)據(jù)不是很準(zhǔn)確,于是偏差高。
若方差高(非線性模型的擬合系數(shù)差異相對比較大,比如局部加權(quán)線性回歸,不同數(shù)據(jù),系數(shù)差異肯定比較大),也就是說,模型復(fù)雜度高(比如特征含有x6的這種模型),容易擬合數(shù)據(jù),數(shù)據(jù)預(yù)測相對準(zhǔn)確。但也容易過擬合,換了數(shù)據(jù)集之后,也會出現(xiàn)偏差高的情況。
應(yīng)該在二者之間權(quán)衡,找到最適合的方差和偏差。
其實也就是,模型不能太復(fù)雜,不然容易過擬合,也不能太簡單,容易欠擬合。