導(dǎo)語(yǔ)
回歸:從一組數(shù)據(jù)出發(fā),確定某些變量之間的定量關(guān)系式撵颊;即建立數(shù)學(xué)模型并估計(jì)未知參數(shù)宇攻。
回歸的目的是預(yù)測(cè)數(shù)值型的目標(biāo)值,它的目標(biāo)是接受連續(xù)數(shù)據(jù)倡勇,尋找最適合數(shù)據(jù)的方程逞刷,并能夠?qū)μ囟ㄖ颠M(jìn)行預(yù)測(cè)。這個(gè)方程稱(chēng)為回歸方程妻熊,而求回歸方程顯然就是求該方程的回歸系數(shù)夸浅,求這些回歸系數(shù)的過(guò)程就是回歸。
我們先來(lái)看看回歸跟分類(lèi)的區(qū)別:(引用 - 走刀口的回答 - 知乎
https://www.zhihu.com/question/21329754/answer/17901883)
分類(lèi)和回歸的區(qū)別在于輸出變量的類(lèi)型扔役。
定量輸出稱(chēng)為回歸帆喇,或者說(shuō)是連續(xù)變量預(yù)測(cè);
定性輸出稱(chēng)為分類(lèi)亿胸,或者說(shuō)是離散變量預(yù)測(cè)番枚。
舉個(gè)例子:
預(yù)測(cè)明天的氣溫是多少度,這是一個(gè)回歸任務(wù)损敷;
預(yù)測(cè)明天是陰葫笼、晴還是雨,就是一個(gè)分類(lèi)任務(wù)拗馒。
普通線性回歸
我們做線性回歸是為了得到一個(gè)最優(yōu)回歸系數(shù)向量w使得當(dāng)我們給定一個(gè)x能夠通過(guò)y=xw預(yù)測(cè)y的值。假定輸入數(shù)據(jù)存放在矩陣 X 中,而回歸系數(shù)存在在向量 w 中洋丐。那么對(duì)于給定的數(shù)據(jù)X1呈昔,預(yù)測(cè)結(jié)果將會(huì)通過(guò)Y1=XT1w給出。
那么怎樣的w才是最優(yōu)的呢友绝?在標(biāo)準(zhǔn)線性回歸中我們需要找到是誤差最小的w, 即預(yù)測(cè)的y值與真實(shí)的y值之間的差值堤尾,為了避免簡(jiǎn)單累加造成的正負(fù)差值相互抵消,這里采用了平方誤差:
用矩陣可表示為:(y-Xw)T(y-Xw)迁客,)因?yàn)橐蠛瘮?shù)的極小值郭宝,對(duì)w求導(dǎo)可得:
使其等于0,便可求出W的最優(yōu)解:
上述求w最優(yōu)解的方法也叫做最小二乘法掷漱。
需要注意的是粘室,上述公式中包含(XTX)-1,也就是需要對(duì)矩陣求逆卜范,因此這個(gè)方程只有在逆矩陣存在的時(shí)候有用衔统,所以我們寫(xiě)代碼時(shí)必須需要事先確定矩陣是否可逆。
此外海雪,我們知道線性回歸的方程的一般形式為:y=wx+b锦爵;即存在一定的偏移量b,于是奥裸,我們可以將回歸系數(shù)和特征向量均增加一個(gè)維度险掀,將函數(shù)的偏移值b也算作回歸系數(shù)的一部分,占據(jù)回歸系數(shù)向量中的一個(gè)維度刺彩,比如w=(w1,w2,...,wN,b)迷郑。相應(yīng)地,將每條數(shù)據(jù)特征向量的第一個(gè)維度設(shè)置為1.0创倔,即所有特征向量的第一個(gè)維度值均為1.0,這樣嗡害,最后得到的回歸函數(shù)形式為y=w[0]+w[1]x1+...+w[N]xN.
Python代碼實(shí)現(xiàn)
# 普通線性回歸
def standRegress(xArr, yArr):
# 用mat函數(shù)轉(zhuǎn)換為矩陣之后可以才進(jìn)行一些線性代數(shù)的操作
xMat = mat(xArr); yMat = mat(yArr).T
# XtX
xTx = xMat.T * xMat
# 判斷矩陣是否可逆
if linalg.det(xTx) == 0:
print("This matrix is singular, cannot do inverse")
return
# 根據(jù)公式計(jì)算w
w_hat = xTx.I * (xMat.T * yMat)
return w_hat
相關(guān)系數(shù)(Correlation Coefficient)計(jì)算:
幾乎任意數(shù)據(jù)集都可以用上述方法建立模型,怎么判斷這些模型的好壞呢畦攘?為了計(jì)算預(yù)測(cè)值序列和真實(shí)值序列的匹配程度霸妹,可以計(jì)算出這兩個(gè)序列的相關(guān)系數(shù)。
計(jì)算公式:
公式就是用X知押、Y的協(xié)方差除以X的標(biāo)準(zhǔn)差和Y的標(biāo)準(zhǔn)差叹螟。協(xié)方差是衡量?jī)蓚€(gè)變量變化趨勢(shì)是否相似的一種方法,是同向變化(同時(shí)變大或變小)還是反向變化(一個(gè)變大一個(gè)變小), 同向或者反向的程度如何台盯,計(jì)算公式如下:
在Python中罢绽,NumPy庫(kù)提供了相關(guān)系數(shù)的計(jì)算方法:
corrcoef(yEstimate, yActual)來(lái)計(jì)算相系數(shù),該方法返回一個(gè)矩陣静盅,該矩陣包所有兩兩組合的相關(guān)系數(shù)良价。
從上面的結(jié)果圖可以看出,線性回歸的一個(gè)問(wèn)題是可能會(huì)欠擬合,標(biāo)準(zhǔn)的線性回歸是一種最小均方差的無(wú)偏差估計(jì)明垢,在計(jì)算所有點(diǎn)的時(shí)候都是無(wú)偏差的計(jì)算誤差蚣常,如果針對(duì)不同的點(diǎn)能夠?qū)φ`差進(jìn)行調(diào)整便可以一定程度上避免標(biāo)準(zhǔn)線性回歸帶來(lái)的欠擬合現(xiàn)象。接下來(lái)介紹的局部加權(quán)線性回歸就是采取這種方法對(duì)值進(jìn)行預(yù)測(cè)痊银。
局部加權(quán)線性回歸
在該算法中抵蚊,我們給帶預(yù)測(cè)點(diǎn)附近的每個(gè)點(diǎn)賦予一定的權(quán)重,越靠近預(yù)測(cè)點(diǎn)的數(shù)據(jù)點(diǎn)分配的權(quán)重越高溯革,于分類(lèi)算法kNN一樣贞绳,此算法每次預(yù)測(cè)均需事先選取出對(duì)應(yīng)的數(shù)據(jù)子集,算法代價(jià)高鬓照。我們用θ表示回歸系數(shù)熔酷,w表示權(quán)重, 那么平方誤差的表達(dá)式就變成:
用矩陣可表示為:
對(duì)θ求導(dǎo)孤紧,令其等于0求極值得:
其中的W是一個(gè)矩陣豺裆,用來(lái)給每個(gè)數(shù)據(jù)點(diǎn)賦予權(quán)重。那我們?cè)趺从?jì)算這個(gè)W呢号显?
LWLR使用“核”(與支持向量機(jī)中的核類(lèi)似)來(lái)對(duì)附近的點(diǎn)賦予更高的權(quán)重臭猜。核的類(lèi)型可以自由選擇,最常用的是高斯核押蚤,高斯核對(duì)應(yīng)的權(quán)重如下:
通過(guò)公式可以看到如果xi距離x的距離越小蔑歌,W(i)就會(huì)越大,其中參數(shù)k決定了權(quán)重的大小揽碘。k越大權(quán)重的差距就越小次屠,k越小權(quán)重的差距就很大,僅有局部的點(diǎn)參與進(jìn)回歸系數(shù)的求取雳刺,其他距離較遠(yuǎn)的權(quán)重都趨近于零劫灶。如果k去進(jìn)入無(wú)窮大,所有的權(quán)重都趨近于1掖桦,W也就近似等于單位矩陣本昏,局部加權(quán)線性回歸變成標(biāo)準(zhǔn)的無(wú)偏差線性回歸,會(huì)造成欠擬合的現(xiàn)象枪汪;當(dāng)k很小的時(shí)候涌穆,距離較遠(yuǎn)的樣本點(diǎn)無(wú)法參與回歸參數(shù)的求取,會(huì)造成過(guò)擬合的現(xiàn)象雀久。
Python代碼實(shí)現(xiàn)
# 局部加權(quán)線性回歸
def lwlr(testPoint, xArr, yArr, k=1.0):
# 先把數(shù)組轉(zhuǎn)為矩陣宿稀,方便進(jìn)行線性代數(shù)操作
xMat = mat(xArr); yMat = mat(yArr).T
# shape():返回矩陣的維數(shù),是個(gè)數(shù)組
numLine = shape(xMat)[0]
# eye(num): 生成一個(gè)對(duì)角矩陣(對(duì)角線全為1赖捌,其它為0)
weights = mat(eye(numLine))
#計(jì)算權(quán)重
for i in range(numLine):
# x(i) - x
diffMat = testPoint - xMat[i, :]
weights[i, i] = exp(diffMat * diffMat.T / (-2 * k ** 2))
xTx = xMat.T * (weights * xMat)
if linalg.det(xTx == 0.0): #矩陣不可逆
print("This matrix ix singular, cannot do inverse")
return
# 根據(jù)公式求出回歸系數(shù)w
w = xTx.I * xMat.T * weights * yMat
return testPoint * w
def lwlrTest(testArr, xArr, yArr, k=1):
numLines = shape(testArr)[0]
# 初始化y值祝沸,全為0
yHat = zeros(numLines)
for i in range(numLines):
yHat[i] = lwlr(testArr[i], xArr, yArr, k)
return yHat
局部加權(quán)線性回歸也存在一個(gè)問(wèn)題奋隶,對(duì)于每一個(gè)要預(yù)測(cè)的點(diǎn)擂送,都要重新依據(jù)整個(gè)數(shù)據(jù)集計(jì)算一個(gè)線性回歸模型出來(lái),增加了很大的計(jì)算量唯欣,使得算法代價(jià)極高嘹吨。從上圖可以看出當(dāng)k=0.01時(shí)可以得到一個(gè)很好的預(yù)測(cè)。但是下圖展示了當(dāng)k=0.01時(shí)(假定我們預(yù)測(cè)的是x = 0.5這個(gè)點(diǎn))境氢,每個(gè)點(diǎn)所占的權(quán)重:
可以發(fā)現(xiàn)除了預(yù)測(cè)點(diǎn)附近蟀拷,大多數(shù)數(shù)據(jù)點(diǎn)的權(quán)重都接近于零,如果避免這些計(jì)算將可以減少程序運(yùn)行時(shí)間萍聊,從而緩解因計(jì)算量增加所帶來(lái)的問(wèn)題问芬。
嶺回歸
如果數(shù)據(jù)的特征比樣本點(diǎn)還多應(yīng)該怎么辦?或者說(shuō)輸入的數(shù)據(jù)矩陣不是滿(mǎn)秩矩陣寿桨,是否還能用前面介紹的普通回歸或者局部加權(quán)回歸來(lái)做預(yù)測(cè)此衅?我們?cè)谇蠡貧w系數(shù)的時(shí)候需要算出(XTX)-1,當(dāng)矩陣不是滿(mǎn)秩矩陣的話是不能進(jìn)行求逆運(yùn)算的亭螟,所以之前的回歸方法就不能用了挡鞍,為了解決這個(gè)問(wèn)題,我們需要對(duì)最初的標(biāo)準(zhǔn)線性回歸做一定的變化使原先無(wú)法求逆的矩陣變得非奇異预烙,使得問(wèn)題可以穩(wěn)定求解墨微。我們可以通過(guò)縮減系數(shù)的方式來(lái)處理這些問(wèn)題,例如嶺回歸和LASSO.
簡(jiǎn)單的說(shuō),嶺回歸就是在矩陣(XTX)上加上一個(gè)λI從而使得矩陣非奇異扁掸,進(jìn)而能對(duì)(XTX) + λI求逆翘县。其中矩陣I是一個(gè)單位矩陣,此時(shí)回歸系數(shù)的計(jì)算公式將變?yōu)椋?br>
Python代碼實(shí)現(xiàn)
# 嶺回歸
def ridgeRegress(xMat, yMat, lam=0.2):
xTx = xMat.T * xMat
# print(shape(xTx))
# print(shape(xMat))
temp = xTx + eye(shape(xTx)[0]) * lam
if linalg.det(temp) == 0.0:
print("This matrix is singular, cannot do reverse")
return
w = temp.I * (xMat.T * yMat)
return w
很明顯谴分,我們要找到使預(yù)測(cè)誤差最小的λ锈麸,不同的λ可以得到不同的參數(shù)w,因此我們可以改變?chǔ)说闹祦?lái)得到嶺回歸系數(shù)的變化狸剃,通過(guò)嶺跡圖我們可以觀察較佳的λ取值:
def ridgeTest(xArr, yArr):
xMat = mat(xArr)
yMat = mat(yArr).T
xMean = mean(xMat, 0)
yMean = mean(yMat, 0)
yMat = yMat - yMean
#數(shù)據(jù)標(biāo)注化:所有特征減去各自的均值并除以方差
xMat = (xMat - xMean) / var(xMat, 0)
numTestPts = 30
wMat = zeros((numTestPts, shape(xMat)[1]))
for i in range(numTestPts):
ws = ridgeRegress(xMat, yMat, exp(i-10))
wMat[i, :] = ws.T
return wMat
上述代碼可以生成一個(gè)30行的回歸系數(shù)掐隐,將其繪制出來(lái)可得到嶺跡圖:
該圖繪出了回歸系數(shù)與log(λ)的關(guān)系,在最左邊λ系數(shù)最小時(shí)钞馁,可以得到所有系數(shù)的原始值(與標(biāo)準(zhǔn)線性回歸相同); 而在右邊虑省,系數(shù)全部縮減為0, 從不穩(wěn)定趨于穩(wěn)定;為了定量的找到最佳參數(shù)值僧凰,還需要進(jìn)行交叉驗(yàn)證探颈。要判斷哪些變量對(duì)結(jié)果的預(yù)測(cè)最具影響力,可以觀察他們的系數(shù)大小即可训措。
中心化和標(biāo)準(zhǔn)化
在回歸問(wèn)題和一些機(jī)器學(xué)習(xí)算法中通常要對(duì)原始數(shù)據(jù)進(jìn)行中心化和標(biāo)準(zhǔn)化處理伪节,也就是需要將數(shù)據(jù)的均值調(diào)整到0光羞,標(biāo)準(zhǔn)差調(diào)整為1, 計(jì)算過(guò)程很簡(jiǎn)單就是將所有數(shù)據(jù)減去平均值后再除以標(biāo)準(zhǔn)差,上述代碼中就是先對(duì)數(shù)據(jù)進(jìn)行了標(biāo)準(zhǔn)化處理怀大,之所以需要進(jìn)行中心化其實(shí)就是個(gè)平移過(guò)程纱兑,將所有數(shù)據(jù)的中心平移到原點(diǎn)。而標(biāo)準(zhǔn)化則是使得所有數(shù)據(jù)的不同特征都有相同的尺度Scale, 這樣在使用梯度下降法以及其他方法優(yōu)化的時(shí)候不同特征參數(shù)的影響程度就會(huì)一致了化借。
交叉驗(yàn)證
為了定量的找到嶺回歸最佳參數(shù)值λ潜慎,還需要進(jìn)行交叉驗(yàn)證。一般地蓖康,交叉驗(yàn)證就是通過(guò)把數(shù)據(jù)集分成若干等份铐炫,每一份輪流作測(cè)試集,其余數(shù)據(jù)作訓(xùn)練集進(jìn)行輪流訓(xùn)練與測(cè)試蒜焊。如果把數(shù)據(jù)集分成n份倒信,就叫n-folds交叉驗(yàn)證,這樣會(huì)得到n個(gè)錯(cuò)誤率泳梆,然后取這n個(gè)的平均值作為最終的結(jié)果鳖悠。
# 交叉驗(yàn)證
def crossValidation(xArr, yArr, numSplit):
# 誤差矩陣,每行有30個(gè)lam得到的結(jié)果
errorMat = zeros((numSplit, 30))
data_len = len(yArr)
indexList = list(range(len(yArr)))
for i in range(numSplit):
trainX = []
trainY = []
testX = []
testY = []
# 打亂列表順序鸭丛,獲得隨機(jī)效果
random.shuffle(indexList)
# 劃分出訓(xùn)練集和測(cè)試集
for j in range(data_len):
if j < 0.9 * data_len:
trainX.append(xArr[indexList[j]])
trainY.append(yArr[indexList[j]])
else:
testX.append(xArr[indexList[j]])
testY.append(yArr[indexList[j]])
# 求出此次的回歸系數(shù) 30 * 8
wMat = ridgeTest(trainX, trainY)
# 求每一組回歸系數(shù)的誤差
for group in range(30):
testXMat = mat(testX)
trainXMat = mat(trainX)
trainXMean = mean(trainX, 0)
trainXVar = var(trainXMat, 0)
# 訓(xùn)練集做了標(biāo)準(zhǔn)化處理竞穷,測(cè)試集也要做相同處理
testXMat = (testXMat - trainXMean) / trainXVar
trainYMat = mat(trainY)
trainYMean = mean(trainYMat, 0)
yEst = testX * mat(wMat[group, :]).T + trainYMean
errorMat[i, group] = rssError(yEst.T.A, array(testY))
#計(jì)算每個(gè)lamda的平均誤差
meanErrors = mean(errorMat, 0)
minErrors = float(min(meanErrors))
bestWeightIndex = nonzero(meanErrors == minErrors)
print(bestWeightIndex[0])
LASSO回歸
LASSO(The Least Absolute Shrinkage and Selection Operator)是另一種縮減方法唐责,將回歸系數(shù)收縮在一定的區(qū)域內(nèi)鳞溉。LASSO的主要思想是構(gòu)造一個(gè)一階懲罰函數(shù)獲得一個(gè)精煉的模型, 通過(guò)最終確定一些變量的系數(shù)為0進(jìn)行特征篩選。
LASSO的懲罰項(xiàng)為:
而嶺回歸的懲罰項(xiàng):
我們可以看到鼠哥,它倆唯一的不同點(diǎn)在于熟菲,這個(gè)約束條件使用絕對(duì)值代替了平方和。雖然約束形式只是稍作變化朴恳,但是相比嶺回歸可以直接通過(guò)矩陣運(yùn)算得到回歸系數(shù)相比抄罕,這細(xì)微的變化卻極大的增加了計(jì)算復(fù)雜度。下面介紹更為簡(jiǎn)單的方法來(lái)得到與之差不多的結(jié)果于颖。
前向逐步回歸
前向逐步回歸是一種貪心算法呆贿,即每一步都盡可能的減少誤差。一開(kāi)始森渐,所有的權(quán)重都設(shè)為1做入,然后每一步所做的決策是對(duì)某個(gè)權(quán)重增加或減少一個(gè)很小的值。
偽代碼如下:
數(shù)據(jù)標(biāo)準(zhǔn)化同衣,使其分布滿(mǎn)足0均值和單位方差
在每輪迭代過(guò)程中:
設(shè)置當(dāng)前最小誤差lowestError為正無(wú)窮
對(duì)每個(gè)特征:
增大或縮小:
改變一個(gè)系數(shù)得到一個(gè)新的w
計(jì)算新W下的誤差
如果誤差Error小于當(dāng)前最小誤差lowestError:
設(shè)置Wbest等于當(dāng)前的W
將w設(shè)置為新的Wbest
python代碼實(shí)現(xiàn)
def stageWise(xArr, yArr, eps=0.01, numIt=100):
xMat = mat(xArr)
yMat = mat(yArr).T
# 標(biāo)準(zhǔn)化處理
yMat -= mean(yMat, 0)
xMat = (xMat - mean(xMat, 0)) / var(xMat, 0)
ws = zeros((shape(xMat)[1], 1))
returnMat = zeros((numIt, shape(xMat)[1]))
# 每輪迭代
for i in range(numIt):
lowestError = inf
# 對(duì)每個(gè)特征
for j in range(shape(xMat)[1]):
# 增大縮小
for sign in [-1, 1]:
wsTest = ws.copy()
wsTest[j] += eps * sign
yEst = xMat * wsTest
rssE = rssError(yMat.A, yEst.A)
if rssE < lowestError:
lowestError = rssE
wsMax = wsTest
ws = wsMax.copy()
returnMat[i, :] = ws.T
return returnMat
我們?nèi)〔介L(zhǎng)為0.01竟块,迭代200次來(lái)看看結(jié)果:
我們可以看到結(jié)果中w1和w6都是0耐齐,這說(shuō)明他們對(duì)目標(biāo)值的預(yù)測(cè)沒(méi)有任何影響浪秘,也就是說(shuō)這些特征可能是不需要的蒋情。另外,一段時(shí)間后系數(shù)就已經(jīng)飽和并在特征值之間來(lái)回震蕩耸携,這是應(yīng)為步長(zhǎng)太大的緣故棵癣。我們可以看到第一個(gè)權(quán)重在0.04和0.05之間來(lái)回震蕩。說(shuō)明最優(yōu)的權(quán)重應(yīng)該是在0.04-0.05之間夺衍,這時(shí)我們應(yīng)該把步長(zhǎng)調(diào)小浙巫。但如果迭代次數(shù)過(guò)多,逐步線性回歸算法就會(huì)與常規(guī)的最小二乘法效果類(lèi)似刷后。使用0.005的步長(zhǎng)并經(jīng)過(guò)1000次迭代后的結(jié)果圖:
逐步線性回歸的好處在于它可以幫助人們理解現(xiàn)有的模型并作出改進(jìn)的畴。當(dāng)構(gòu)建了一個(gè)模型后,可以運(yùn)行該算法找出重要的特征尝胆,這樣就有可能及時(shí)停止對(duì)那些不重要特征的收集丧裁。最后,如果用于測(cè)試含衔,該算法每100次迭代就可構(gòu)建出一個(gè)模型煎娇,可以使用上文所述的交叉驗(yàn)證法來(lái)比較這些模型,最終選擇使誤差最小的模型贪染。