第8章 預(yù)測(cè)數(shù)值型數(shù)據(jù):回歸
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=default"></script>
回歸(Regression) 概述
我們前邊提到的分類的目標(biāo)變量是標(biāo)稱型數(shù)據(jù),而回歸則是對(duì)連續(xù)型的數(shù)據(jù)做出處理,回歸的目的是預(yù)測(cè)數(shù)值型數(shù)據(jù)的目標(biāo)值。
回歸 場(chǎng)景
回歸的目的是預(yù)測(cè)數(shù)值型的目標(biāo)值。最直接的辦法是依據(jù)輸入寫(xiě)出一個(gè)目標(biāo)值的計(jì)算公式。
假如你想要預(yù)測(cè)蘭博基尼跑車的功率大小,可能會(huì)這樣計(jì)算:
HorsePower = 0.0015 * annualSalary - 0.99 * hoursListeningToPublicRadio
這就是所謂的 回歸方程(regression equation)
,其中的 0.0015 和 -0.99 稱作 回歸系數(shù)(regression weights)
,求這些回歸系數(shù)的過(guò)程就是回歸。一旦有了這些回歸系數(shù),再給定輸入,做預(yù)測(cè)就非常容易了。具體的做法是用回歸系數(shù)乘以輸入值,再將結(jié)果全部加在一起,就得到了預(yù)測(cè)值。我們這里所說(shuō)的,回歸系數(shù)是一個(gè)向量,輸入也是向量,這些運(yùn)算也就是求出二者的內(nèi)積。
說(shuō)到回歸,一般都是指 線性回歸(linear regression)
。線性回歸意味著可以將輸入項(xiàng)分別乘以一些常量,再將結(jié)果加起來(lái)得到輸出。
回歸 原理
1、線性回歸
我們應(yīng)該怎樣從一大堆數(shù)據(jù)里求出回歸方程呢? 假定輸入數(shù)據(jù)存放在矩陣 x 中,而回歸系數(shù)存放在向量 w 中。那么對(duì)于給定的數(shù)據(jù) X1,預(yù)測(cè)結(jié)果將會(huì)通過(guò) Y = X1^T w 給出。現(xiàn)在的問(wèn)題是,手里有一些 X 和對(duì)應(yīng)的 y,怎樣才能找到 w 呢?一個(gè)常用的方法就是找出使誤差最小的 w 。這里的誤差是指預(yù)測(cè) y 值和真實(shí) y 值之間的差值,使用該誤差的簡(jiǎn)單累加將使得正差值和負(fù)差值相互抵消,所以我們采用平方誤差。
平方誤差可以寫(xiě)做:
用矩陣表示還可以寫(xiě)做
。如果對(duì) w 求導(dǎo),得到
,令其等于零,解出 w 如下(具體求導(dǎo)過(guò)程為: http://blog.csdn.net/nomadlx53/article/details/50849941 ):
1.1、線性回歸 須知概念
1.1.1、矩陣求逆
因?yàn)槲覀冊(cè)谟?jì)算回歸方程的回歸系數(shù)時(shí),用到的計(jì)算公式如下:
需要對(duì)矩陣求逆,因此這個(gè)方程只在逆矩陣存在的時(shí)候適用,我們?cè)诔绦虼a中對(duì)此作出判斷。
判斷矩陣是否可逆的一個(gè)可選方案是:
判斷矩陣的行列式是否為 0,若為 0 ,矩陣就不存在逆矩陣,不為 0 的話,矩陣才存在逆矩陣。
1.1.2、最小二乘法
最小二乘法(又稱最小平方法)是一種數(shù)學(xué)優(yōu)化技術(shù)。它通過(guò)最小化誤差的平方和尋找數(shù)據(jù)的最佳函數(shù)匹配。
1.2、線性回歸 工作原理
讀入數(shù)據(jù),將數(shù)據(jù)特征x、特征標(biāo)簽y存儲(chǔ)在矩陣x、y中
驗(yàn)證 x^Tx 矩陣是否可逆
使用最小二乘法求得 回歸系數(shù) w 的最佳估計(jì)
1.3、線性回歸 開(kāi)發(fā)流程
收集數(shù)據(jù): 采用任意方法收集數(shù)據(jù)
準(zhǔn)備數(shù)據(jù): 回歸需要數(shù)值型數(shù)據(jù),標(biāo)稱型數(shù)據(jù)將被轉(zhuǎn)換成二值型數(shù)據(jù)
分析數(shù)據(jù): 繪出數(shù)據(jù)的可視化二維圖將有助于對(duì)數(shù)據(jù)做出理解和分析,在采用縮減法求得新回歸系數(shù)之后,可以將新擬合線繪在圖上作為對(duì)比
訓(xùn)練算法: 找到回歸系數(shù)
測(cè)試算法: 使用 R^2 或者預(yù)測(cè)值和數(shù)據(jù)的擬合度,來(lái)分析模型的效果
使用算法: 使用回歸,可以在給定輸入的時(shí)候預(yù)測(cè)出一個(gè)數(shù)值,這是對(duì)分類方法的提升,因?yàn)檫@樣可以預(yù)測(cè)連續(xù)型數(shù)據(jù)而不僅僅是離散的類別標(biāo)簽
1.4、線性回歸 算法特點(diǎn)
優(yōu)點(diǎn):結(jié)果易于理解,計(jì)算上不復(fù)雜。
缺點(diǎn):對(duì)非線性的數(shù)據(jù)擬合不好。
適用于數(shù)據(jù)類型:數(shù)值型和標(biāo)稱型數(shù)據(jù)。
1.5、線性回歸 項(xiàng)目案例
1.5.1、線性回歸 項(xiàng)目概述
根據(jù)下圖中的點(diǎn),找出該數(shù)據(jù)的最佳擬合直線。
數(shù)據(jù)格式為:
x0 x1 y
1.000000 0.067732 3.176513
1.000000 0.427810 3.816464
1.000000 0.995731 4.550095
1.000000 0.738336 4.256571
1.5.2、線性回歸 編寫(xiě)代碼
def loadDataSet(fileName):
""" 加載數(shù)據(jù)
解析以tab鍵分隔的文件中的浮點(diǎn)數(shù)
Returns:
dataMat : feature 對(duì)應(yīng)的數(shù)據(jù)集
labelMat : feature 對(duì)應(yīng)的分類標(biāo)簽,即類別標(biāo)簽
"""
# 獲取樣本特征的總數(shù),不算最后的目標(biāo)變量
numFeat = len(open(fileName).readline().split('\t')) - 1
dataMat = []
labelMat = []
fr = open(fileName)
for line in fr.readlines():
# 讀取每一行
lineArr =[]
# 刪除一行中以tab分隔的數(shù)據(jù)前后的空白符號(hào)
curLine = line.strip().split('\t')
# i 從0到2,不包括2
for i in range(numFeat):
# 將數(shù)據(jù)添加到lineArr List中,每一行數(shù)據(jù)測(cè)試數(shù)據(jù)組成一個(gè)行向量
lineArr.append(float(curLine[i]))
# 將測(cè)試數(shù)據(jù)的輸入數(shù)據(jù)部分存儲(chǔ)到dataMat 的List中
dataMat.append(lineArr)
# 將每一行的最后一個(gè)數(shù)據(jù),即類別,或者叫目標(biāo)變量存儲(chǔ)到labelMat List中
labelMat.append(float(curLine[-1]))
return dataMat,labelMat
def standRegres(xArr,yArr):
'''
Description:
線性回歸
Args:
xArr :輸入的樣本數(shù)據(jù),包含每個(gè)樣本數(shù)據(jù)的 feature
yArr :對(duì)應(yīng)于輸入數(shù)據(jù)的類別標(biāo)簽,也就是每個(gè)樣本對(duì)應(yīng)的目標(biāo)變量
Returns:
ws:回歸系數(shù)
'''
# mat()函數(shù)將xArr,yArr轉(zhuǎn)換為矩陣 mat().T 代表的是對(duì)矩陣進(jìn)行轉(zhuǎn)置操作
xMat = mat(xArr)
yMat = mat(yArr).T
# 矩陣乘法的條件是左矩陣的列數(shù)等于右矩陣的行數(shù)
xTx = xMat.T*xMat
# 因?yàn)橐玫絰Tx的逆矩陣,所以事先需要確定計(jì)算得到的xTx是否可逆,條件是矩陣的行列式不為0
# linalg.det() 函數(shù)是用來(lái)求得矩陣的行列式的,如果矩陣的行列式為0,則這個(gè)矩陣是不可逆的,就無(wú)法進(jìn)行接下來(lái)的運(yùn)算
if linalg.det(xTx) == 0.0:
print "This matrix is singular, cannot do inverse"
return
# 最小二乘法
# http://www.apache.wiki/pages/viewpage.action?pageId=5505133
# 書(shū)中的公式,求得w的最優(yōu)解
ws = xTx.I * (xMat.T*yMat)
return ws
def regression1():
xArr, yArr = loadDataSet("input/8.Regression/data.txt")
xMat = mat(xArr)
yMat = mat(yArr)
ws = standRegres(xArr, yArr)
fig = plt.figure()
ax = fig.add_subplot(111) #add_subplot(349)函數(shù)的參數(shù)的意思是,將畫(huà)布分成3行4列圖像畫(huà)在從左到右從上到下第9塊
ax.scatter(xMat[:, 1].flatten(), yMat.T[:, 0].flatten().A[0]) #scatter 的x是xMat中的第二列,y是yMat的第一列
xCopy = xMat.copy()
xCopy.sort(0)
yHat = xCopy * ws
ax.plot(xCopy[:, 1], yHat)
plt.show()
完整代碼地址: https://github.com/apachecn/MachineLearning/blob/master/src/python/8.PredictiveNumericalDataRegression/regression.py
1.5.3、線性回歸 擬合效果
2、局部加權(quán)線性回歸
線性回歸的一個(gè)問(wèn)題是有可能出現(xiàn)欠擬合現(xiàn)象,因?yàn)樗蟮氖蔷哂凶钚【讲畹臒o(wú)偏估計(jì)。顯而易見(jiàn),如果模型欠擬合將不能取得最好的預(yù)測(cè)效果。所以有些方法允許在估計(jì)中引入一些偏差,從而降低預(yù)測(cè)的均方誤差。
一個(gè)方法是局部加權(quán)線性回歸(Locally Weighted Linear Regression,LWLR)。在這個(gè)算法中,我們給預(yù)測(cè)點(diǎn)附近的每個(gè)點(diǎn)賦予一定的權(quán)重,然后與 線性回歸 類似,在這個(gè)子集上基于最小均方誤差來(lái)進(jìn)行普通的回歸。我們需要最小化的目標(biāo)函數(shù)大致為:
與 kNN 一樣,這種算法每次預(yù)測(cè)均需要事先選取出對(duì)應(yīng)的數(shù)據(jù)子集。該算法解出回歸系數(shù) w 的形式如下:
其中 w 是一個(gè)矩陣,用來(lái)給每個(gè)數(shù)據(jù)點(diǎn)賦予權(quán)重。
LWLR 使用 “核”(與支持向量機(jī)中的核類似)來(lái)對(duì)附近的點(diǎn)賦予更高的權(quán)重。核的類型可以自由選擇,最常用的核就是高斯核,高斯核對(duì)應(yīng)的權(quán)重如下:
這樣就構(gòu)建了一個(gè)只含對(duì)角元素的權(quán)重矩陣 w,并且點(diǎn) x 與 x(i) 越近,w(i, i) 將會(huì)越大。上述公式中包含一個(gè)需要用戶指定的參數(shù) k,它決定了對(duì)附近的點(diǎn)賦予多大的權(quán)重,這也是使用 LWLR 時(shí)唯一需要考慮的參數(shù),下面的圖給出了參數(shù) k 與權(quán)重的關(guān)系。
上面的圖是 每個(gè)點(diǎn)的權(quán)重圖(假定我們正預(yù)測(cè)的點(diǎn)是 x = 0.5),最上面的圖是原始數(shù)據(jù)集,第二個(gè)圖顯示了當(dāng) k = 0.5 時(shí),大部分的數(shù)據(jù)都用于訓(xùn)練回歸模型;而最下面的圖顯示當(dāng) k=0.01 時(shí),僅有很少的局部點(diǎn)被用于訓(xùn)練回歸模型。
2.1、局部加權(quán)線性回歸 工作原理
讀入數(shù)據(jù),將數(shù)據(jù)特征x、特征標(biāo)簽y存儲(chǔ)在矩陣x、y中
利用高斯核構(gòu)造一個(gè)權(quán)重矩陣 W,對(duì)預(yù)測(cè)點(diǎn)附近的點(diǎn)施加權(quán)重
驗(yàn)證 X^TWX 矩陣是否可逆
使用最小二乘法求得 回歸系數(shù) w 的最佳估計(jì)
2.2、局部加權(quán)線性回歸 項(xiàng)目案例
2.2.1、局部加權(quán)線性回歸 項(xiàng)目概述
我們?nèi)匀皇褂蒙厦?線性回歸 的數(shù)據(jù)集,對(duì)這些點(diǎn)進(jìn)行一個(gè) 局部加權(quán)線性回歸 的擬合。
數(shù)據(jù)格式為:
1.000000 0.067732 3.176513
1.000000 0.427810 3.816464
1.000000 0.995731 4.550095
1.000000 0.738336 4.256571
2.2.2、局部加權(quán)線性回歸 編寫(xiě)代碼
# 局部加權(quán)線性回歸
def lwlr(testPoint,xArr,yArr,k=1.0):
'''
Description:
局部加權(quán)線性回歸,在待預(yù)測(cè)點(diǎn)附近的每個(gè)點(diǎn)賦予一定的權(quán)重,在子集上基于最小均方差來(lái)進(jìn)行普通的回歸。
Args:
testPoint:樣本點(diǎn)
xArr:樣本的特征數(shù)據(jù),即 feature
yArr:每個(gè)樣本對(duì)應(yīng)的類別標(biāo)簽,即目標(biāo)變量
k:關(guān)于賦予權(quán)重矩陣的核的一個(gè)參數(shù),與權(quán)重的衰減速率有關(guān)
Returns:
testPoint * ws:數(shù)據(jù)點(diǎn)與具有權(quán)重的系數(shù)相乘得到的預(yù)測(cè)點(diǎn)
Notes:
這其中會(huì)用到計(jì)算權(quán)重的公式,w = e^((x^((i))-x) / -2k^2)
理解:x為某個(gè)預(yù)測(cè)點(diǎn),x^((i))為樣本點(diǎn),樣本點(diǎn)距離預(yù)測(cè)點(diǎn)越近,貢獻(xiàn)的誤差越大(權(quán)值越大),越遠(yuǎn)則貢獻(xiàn)的誤差越小(權(quán)值越小)。
關(guān)于預(yù)測(cè)點(diǎn)的選取,在我的代碼中取的是樣本點(diǎn)。其中k是帶寬參數(shù),控制w(鐘形函數(shù))的寬窄程度,類似于高斯函數(shù)的標(biāo)準(zhǔn)差。
算法思路:假設(shè)預(yù)測(cè)點(diǎn)取樣本點(diǎn)中的第i個(gè)樣本點(diǎn)(共m個(gè)樣本點(diǎn)),遍歷1到m個(gè)樣本點(diǎn)(含第i個(gè)),算出每一個(gè)樣本點(diǎn)與預(yù)測(cè)點(diǎn)的距離,
也就可以計(jì)算出每個(gè)樣本貢獻(xiàn)誤差的權(quán)值,可以看出w是一個(gè)有m個(gè)元素的向量(寫(xiě)成對(duì)角陣形式)。
'''
# mat() 函數(shù)是將array轉(zhuǎn)換為矩陣的函數(shù), mat().T 是轉(zhuǎn)換為矩陣之后,再進(jìn)行轉(zhuǎn)置操作
xMat = mat(xArr)
yMat = mat(yArr).T
# 獲得xMat矩陣的行數(shù)
m = shape(xMat)[0]
# eye()返回一個(gè)對(duì)角線元素為1,其他元素為0的二維數(shù)組,創(chuàng)建權(quán)重矩陣weights,該矩陣為每個(gè)樣本點(diǎn)初始化了一個(gè)權(quán)重
weights = mat(eye((m)))
for j in range(m):
# testPoint 的形式是 一個(gè)行向量的形式
# 計(jì)算 testPoint 與輸入樣本點(diǎn)之間的距離,然后下面計(jì)算出每個(gè)樣本貢獻(xiàn)誤差的權(quán)值
diffMat = testPoint - xMat[j,:]
# k控制衰減的速度
weights[j,j] = exp(diffMat*diffMat.T/(-2.0*k**2))
# 根據(jù)矩陣乘法計(jì)算 xTx ,其中的 weights 矩陣是樣本點(diǎn)對(duì)應(yīng)的權(quán)重矩陣
xTx = xMat.T * (weights * xMat)
if linalg.det(xTx) == 0.0:
print ("This matrix is singular, cannot do inverse")
return
# 計(jì)算出回歸系數(shù)的一個(gè)估計(jì)
ws = xTx.I * (xMat.T * (weights * yMat))
return testPoint * ws
def lwlrTest(testArr,xArr,yArr,k=1.0):
'''
Description:
測(cè)試局部加權(quán)線性回歸,對(duì)數(shù)據(jù)集中每個(gè)點(diǎn)調(diào)用 lwlr() 函數(shù)
Args:
testArr:測(cè)試所用的所有樣本點(diǎn)
xArr:樣本的特征數(shù)據(jù),即 feature
yArr:每個(gè)樣本對(duì)應(yīng)的類別標(biāo)簽,即目標(biāo)變量
k:控制核函數(shù)的衰減速率
Returns:
yHat:預(yù)測(cè)點(diǎn)的估計(jì)值
'''
# 得到樣本點(diǎn)的總數(shù)
m = shape(testArr)[0]
# 構(gòu)建一個(gè)全部都是 0 的 1 * m 的矩陣
yHat = zeros(m)
# 循環(huán)所有的數(shù)據(jù)點(diǎn),并將lwlr運(yùn)用于所有的數(shù)據(jù)點(diǎn)
for i in range(m):
yHat[i] = lwlr(testArr[i],xArr,yArr,k)
# 返回估計(jì)值
return yHat
def lwlrTestPlot(xArr,yArr,k=1.0):
'''
Description:
首先將 X 排序,其余的都與lwlrTest相同,這樣更容易繪圖
Args:
xArr:樣本的特征數(shù)據(jù),即 feature
yArr:每個(gè)樣本對(duì)應(yīng)的類別標(biāo)簽,即目標(biāo)變量,實(shí)際值
k:控制核函數(shù)的衰減速率的有關(guān)參數(shù),這里設(shè)定的是常量值 1
Return:
yHat:樣本點(diǎn)的估計(jì)值
xCopy:xArr的復(fù)制
'''
# 生成一個(gè)與目標(biāo)變量數(shù)目相同的 0 向量
yHat = zeros(shape(yArr))
# 將 xArr 轉(zhuǎn)換為 矩陣形式
xCopy = mat(xArr)
# 排序
xCopy.sort(0)
# 開(kāi)始循環(huán),為每個(gè)樣本點(diǎn)進(jìn)行局部加權(quán)線性回歸,得到最終的目標(biāo)變量估計(jì)值
for i in range(shape(xArr)[0]):
yHat[i] = lwlr(xCopy[i],xArr,yArr,k)
return yHat,xCopy
#test for LWLR
def regression2():
xArr, yArr = loadDataSet("input/8.Regression/data.txt")
yHat = lwlrTest(xArr, xArr, yArr, 0.003)
xMat = mat(xArr)
srtInd = xMat[:,1].argsort(0) #argsort()函數(shù)是將x中的元素從小到大排列,提取其對(duì)應(yīng)的index(索引),然后輸出
xSort=xMat[srtInd][:,0,:]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(xSort[:,1], yHat[srtInd])
ax.scatter(xMat[:,1].flatten().A[0], mat(yArr).T.flatten().A[0] , s=2, c='red')
plt.show()
完整代碼地址: https://github.com/apachecn/MachineLearning/blob/master/src/python/8.PredictiveNumericalDataRegression/regression.py
2.2.3、局部加權(quán)線性回歸 擬合效果
上圖使用了 3 種不同平滑值繪出的局部加權(quán)線性回歸的結(jié)果。上圖中的平滑系數(shù) k =1.0,中圖 k = 0.01,下圖 k = 0.003 。可以看到,k = 1.0 時(shí)的模型效果與最小二乘法差不多,k=0.01時(shí)該模型可以挖出數(shù)據(jù)的潛在規(guī)律,而 k=0.003時(shí)則考慮了太多的噪聲,進(jìn)而導(dǎo)致了過(guò)擬合現(xiàn)象。
2.3、局部加權(quán)線性回歸 注意事項(xiàng)
局部加權(quán)線性回歸也存在一個(gè)問(wèn)題,即增加了計(jì)算量,因?yàn)樗鼘?duì)每個(gè)點(diǎn)做預(yù)測(cè)時(shí)都必須使用整個(gè)數(shù)據(jù)集。
3、線性回歸 & 局部加權(quán)線性回歸 項(xiàng)目案例
到此為止,我們已經(jīng)介紹了找出最佳擬合直線的兩種方法,下面我們用這些技術(shù)來(lái)預(yù)測(cè)鮑魚(yú)的年齡。
3.1、項(xiàng)目概述
我們有一份來(lái)自 UCI 的數(shù)據(jù)集合的數(shù)據(jù),記錄了鮑魚(yú)(一種介殼類水生動(dòng)物)的年齡。鮑魚(yú)年齡可以從鮑魚(yú)殼的層數(shù)推算得到。
3.2、開(kāi)發(fā)流程
收集數(shù)據(jù): 采用任意方法收集數(shù)據(jù)
準(zhǔn)備數(shù)據(jù): 回歸需要數(shù)值型數(shù)據(jù),標(biāo)稱型數(shù)據(jù)將被轉(zhuǎn)換成二值型數(shù)據(jù)
分析數(shù)據(jù): 繪出數(shù)據(jù)的可視化二維圖將有助于對(duì)數(shù)據(jù)做出理解和分析,在采用縮減法求得新回歸系數(shù)之后,可以將新擬合線繪在圖上作為對(duì)比
訓(xùn)練算法: 找到回歸系數(shù)
測(cè)試算法: 使用 rssError()函數(shù) 計(jì)算預(yù)測(cè)誤差的大小,來(lái)分析模型的效果
使用算法: 使用回歸,可以在給定輸入的時(shí)候預(yù)測(cè)出一個(gè)數(shù)值,這是對(duì)分類方法的提升,因?yàn)檫@樣可以預(yù)測(cè)連續(xù)型數(shù)據(jù)而不僅僅是離散的類別標(biāo)簽
收集數(shù)據(jù): 采用任意方法收集數(shù)據(jù)
準(zhǔn)備數(shù)據(jù): 回歸需要數(shù)值型數(shù)據(jù),標(biāo)稱型數(shù)據(jù)將被轉(zhuǎn)換成二值型數(shù)據(jù)
數(shù)據(jù)存儲(chǔ)格式:
1 0.455 0.365 0.095 0.514 0.2245 0.101 0.15 15
1 0.35 0.265 0.09 0.2255 0.0995 0.0485 0.07 7
-1 0.53 0.42 0.135 0.677 0.2565 0.1415 0.21 9
1 0.44 0.365 0.125 0.516 0.2155 0.114 0.155 10
0 0.33 0.255 0.08 0.205 0.0895 0.0395 0.055 7
分析數(shù)據(jù): 繪出數(shù)據(jù)的可視化二維圖將有助于對(duì)數(shù)據(jù)做出理解和分析,在采用縮減法求得新回歸系數(shù)之后,可以將新擬合線繪在圖上作為對(duì)比
訓(xùn)練算法: 找到回歸系數(shù)
使用上面我們講到的 局部加權(quán)線性回歸 訓(xùn)練算法,求出回歸系數(shù)
測(cè)試算法: 使用 rssError()函數(shù) 計(jì)算預(yù)測(cè)誤差的大小,來(lái)分析模型的效果
# test for abloneDataSet
def abaloneTest():
'''
Desc:
預(yù)測(cè)鮑魚(yú)的年齡
Args:
None
Returns:
None
'''
# 加載數(shù)據(jù)
abX, abY = loadDataSet("input/8.Regression/abalone.txt")
# 使用不同的核進(jìn)行預(yù)測(cè)
oldyHat01 = lwlrTest(abX[0:99], abX[0:99], abY[0:99], 0.1)
oldyHat1 = lwlrTest(abX[0:99], abX[0:99], abY[0:99], 1)
oldyHat10 = lwlrTest(abX[0:99], abX[0:99], abY[0:99], 10)
# 打印出不同的核預(yù)測(cè)值與訓(xùn)練數(shù)據(jù)集上的真實(shí)值之間的誤差大小
print "old yHat01 error Size is :" , rssError(abY[0:99], oldyHat01.T)
print "old yHat1 error Size is :" , rssError(abY[0:99], oldyHat1.T)
print "old yHat10 error Size is :" , rssError(abY[0:99], oldyHat10.T)
# 打印出 不同的核預(yù)測(cè)值 與 新數(shù)據(jù)集(測(cè)試數(shù)據(jù)集)上的真實(shí)值之間的誤差大小
newyHat01 = lwlrTest(abX[100:199], abX[0:99], abY[0:99], 0.1)
print "new yHat01 error Size is :" , rssError(abY[0:99], newyHat01.T)
newyHat1 = lwlrTest(abX[100:199], abX[0:99], abY[0:99], 1)
print "new yHat1 error Size is :" , rssError(abY[0:99], newyHat1.T)
newyHat10 = lwlrTest(abX[100:199], abX[0:99], abY[0:99], 10)
print "new yHat10 error Size is :" , rssError(abY[0:99], newyHat10.T)
# 使用簡(jiǎn)單的 線性回歸 進(jìn)行預(yù)測(cè),與上面的計(jì)算進(jìn)行比較
standWs = standRegres(abX[0:99], abY[0:99])
standyHat = mat(abX[100:199]) * standWs
print "standRegress error Size is:", rssError(abY[100:199], standyHat.T.A)
完整代碼地址: https://github.com/apachecn/MachineLearning/blob/master/src/python/8.PredictiveNumericalDataRegression/regression.py
根據(jù)我們上邊的測(cè)試,可以看出:
簡(jiǎn)單線性回歸達(dá)到了與局部加權(quán)現(xiàn)行回歸類似的效果。這也說(shuō)明了一點(diǎn),必須在未知數(shù)據(jù)上比較效果才能選取到最佳模型。那么最佳的核大小是 10 嗎?或許是,但如果想得到更好的效果,應(yīng)該用 10 個(gè)不同的樣本集做 10 次測(cè)試來(lái)比較結(jié)果。
使用算法: 使用回歸,可以在給定輸入的時(shí)候預(yù)測(cè)出一個(gè)數(shù)值,這是對(duì)分類方法的提升,因?yàn)檫@樣可以預(yù)測(cè)連續(xù)型數(shù)據(jù)而不僅僅是離散的類別標(biāo)簽
4、縮減系數(shù)來(lái) “理解” 數(shù)據(jù)
如果數(shù)據(jù)的特征比樣本點(diǎn)還多應(yīng)該怎么辦?是否還可以使用線性回歸和之前的方法來(lái)做預(yù)測(cè)?答案是否定的,即我們不能再使用前面介紹的方法。這是因?yàn)樵谟?jì)算的時(shí)候會(huì)出錯(cuò)。
如果特征比樣本點(diǎn)還多(n > m),也就是說(shuō)輸入數(shù)據(jù)的矩陣 x 不是滿秩矩陣。非滿秩矩陣求逆時(shí)會(huì)出現(xiàn)問(wèn)題。
為了解決這個(gè)問(wèn)題,我們引入了 嶺回歸(ridge regression)
這種縮減方法。接著是 lasso法
,最后介紹 前向逐步回歸
。
4.1、嶺回歸
簡(jiǎn)單來(lái)說(shuō),嶺回歸就是在矩陣對(duì)角線上元素全為1,其他元素全為0。而λ是一個(gè)用戶定義的數(shù)值,后面會(huì)做介紹。在這種情況下,回歸系數(shù)的計(jì)算公式將變成:
嶺回歸最先用來(lái)處理特征數(shù)多于樣本數(shù)的情況,現(xiàn)在也用于在估計(jì)中加入偏差,從而得到更好的估計(jì)。這里通過(guò)引入 λ 來(lái)限制了所有 w 之和,通過(guò)引入該懲罰項(xiàng),能夠減少不重要的參數(shù),這個(gè)技術(shù)在統(tǒng)計(jì)學(xué)中也叫作 縮減(shrinkage)
。
縮減方法可以去掉不重要的參數(shù),因此能更好地理解數(shù)據(jù)。此外,與簡(jiǎn)單的線性回歸相比,縮減法能取得更好的預(yù)測(cè)效果。
這里通過(guò)預(yù)測(cè)誤差最小化得到 λ: 數(shù)據(jù)獲取之后,首先抽一部分?jǐn)?shù)據(jù)用于測(cè)試,剩余的作為訓(xùn)練集用于訓(xùn)練參數(shù) w。訓(xùn)練完畢后在測(cè)試集上測(cè)試預(yù)測(cè)性能。通過(guò)選取不同的 λ 來(lái)重復(fù)上述測(cè)試過(guò)程,最終得到一個(gè)使預(yù)測(cè)誤差最小的 λ 。
4.1.1、嶺回歸 原始代碼
def ridgeRegres(xMat,yMat,lam=0.2):
'''
Desc:
這個(gè)函數(shù)實(shí)現(xiàn)了給定 lambda 下的嶺回歸求解。
如果數(shù)據(jù)的特征比樣本點(diǎn)還多,就不能再使用上面介紹的的線性回歸和局部現(xiàn)行回歸了,因?yàn)橛?jì)算 (xTx)^(-1)會(huì)出現(xiàn)錯(cuò)誤。
如果特征比樣本點(diǎn)還多(n > m),也就是說(shuō),輸入數(shù)據(jù)的矩陣x不是滿秩矩陣。非滿秩矩陣在求逆時(shí)會(huì)出現(xiàn)問(wèn)題。
為了解決這個(gè)問(wèn)題,我們下邊講一下:嶺回歸,這是我們要講的第一種縮減方法。
Args:
xMat:樣本的特征數(shù)據(jù),即 feature
yMat:每個(gè)樣本對(duì)應(yīng)的類別標(biāo)簽,即目標(biāo)變量,實(shí)際值
lam:引入的一個(gè)λ值,使得矩陣非奇異
Returns:
經(jīng)過(guò)嶺回歸公式計(jì)算得到的回歸系數(shù)
'''
xTx = xMat.T*xMat
# 嶺回歸就是在矩陣 xTx 上加一個(gè) λI 從而使得矩陣非奇異,進(jìn)而能對(duì) xTx + λI 求逆
denom = xTx + eye(shape(xMat)[1])*lam
# 檢查行列式是否為零,即矩陣是否可逆,行列式為0的話就不可逆,不為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):
'''
Desc:
函數(shù) ridgeTest() 用于在一組 λ 上測(cè)試結(jié)果
Args:
xArr:樣本數(shù)據(jù)的特征,即 feature
yArr:樣本數(shù)據(jù)的類別標(biāo)簽,即真實(shí)數(shù)據(jù)
Returns:
wMat:將所有的回歸系數(shù)輸出到一個(gè)矩陣并返回
'''
xMat = mat(xArr)
yMat=mat(yArr).T
# 計(jì)算Y的均值
yMean = mean(yMat,0)
# Y的所有的特征減去均值
yMat = yMat - yMean
# 標(biāo)準(zhǔn)化 x,計(jì)算 xMat 平均值
xMeans = mean(xMat,0)
# 然后計(jì)算 X的方差
xVar = var(xMat,0)
# 所有特征都減去各自的均值并除以方差
xMat = (xMat - xMeans)/xVar
# 可以在 30 個(gè)不同的 lambda 下調(diào)用 ridgeRegres() 函數(shù)。
numTestPts = 30
# 創(chuàng)建30 * m 的全部數(shù)據(jù)為0 的矩陣
wMat = zeros((numTestPts,shape(xMat)[1]))
for i in range(numTestPts):
# exp() 返回 e^x
ws = ridgeRegres(xMat,yMat,exp(i-10))
wMat[i,:]=ws.T
return wMat
#test for ridgeRegression
def regression3():
abX,abY = loadDataSet("input/8.Regression/abalone.txt")
ridgeWeights = ridgeTest(abX, abY)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(ridgeWeights)
plt.show()
完整代碼地址: https://github.com/apachecn/MachineLearning/blob/master/src/python/8.PredictiveNumericalDataRegression/regression.py
4.1.2、嶺回歸在鮑魚(yú)數(shù)據(jù)集上的運(yùn)行效果
上圖繪制除了回歸系數(shù)與 log(λ) 的關(guān)系。在最左邊,即 λ 最小時(shí),可以得到所有系數(shù)的原始值(與線性回歸一致);而在右邊,系數(shù)全部縮減為0;在中間部分的某值將可以取得最好的預(yù)測(cè)效果。為了定量地找到最佳參數(shù)值,還需要進(jìn)行交叉驗(yàn)證。另外,要判斷哪些變量對(duì)結(jié)果預(yù)測(cè)最具有影響力,在上圖中觀察它們對(duì)應(yīng)的系數(shù)大小就可以了。
4.2、lasso
在增加如下約束時(shí),普通的最小二乘法回歸會(huì)得到與嶺回歸一樣的公式:
上式限定了所有回歸系數(shù)的平方和不能大于 λ 。使用普通的最小二乘法回歸在當(dāng)兩個(gè)或更多的特征相關(guān)時(shí),可能會(huì)得到一個(gè)很大的正系數(shù)和一個(gè)很大的負(fù)系數(shù)。正式因?yàn)樯鲜鱿拗茥l件的存在,使用嶺回歸可以避免這個(gè)問(wèn)題。
與嶺回歸類似,另一個(gè)縮減方法lasso也對(duì)回歸系數(shù)做了限定,對(duì)應(yīng)的約束條件如下:
唯一的不同點(diǎn)在于,這個(gè)約束條件使用絕對(duì)值取代了平方和。雖然約束形式只是稍作變化,結(jié)果卻大相徑庭: 在 λ 足夠小的時(shí)候,一些系數(shù)會(huì)因此被迫縮減到 0.這個(gè)特性可以幫助我們更好地理解數(shù)據(jù)。
4.3、前向逐步回歸
前向逐步回歸算法可以得到與 lasso 差不多的效果,但更加簡(jiǎn)單。它屬于一種貪心算法,即每一步都盡可能減少誤差。一開(kāi)始,所有權(quán)重都設(shè)置為 1,然后每一步所做的決策是對(duì)某個(gè)權(quán)重增加或減少一個(gè)很小的值。
偽代碼如下:
數(shù)據(jù)標(biāo)準(zhǔ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
4.3.1、前向逐步回歸 原始代碼
def stageWise(xArr,yArr,eps=0.01,numIt=100):
xMat = mat(xArr); yMat=mat(yArr).T
yMean = mean(yMat,0)
yMat = yMat - yMean # 也可以規(guī)則化ys但會(huì)得到更小的coef
xMat = regularize(xMat)
m,n=shape(xMat)
#returnMat = zeros((numIt,n)) # 測(cè)試代碼刪除
ws = zeros((n,1)); wsTest = ws.copy(); wsMax = ws.copy()
for i in range(numIt):
print (ws.T)
lowestError = inf;
for j in range(n):
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
wsMax = wsTest
ws = wsMax.copy()
returnMat[i,:]=ws.T
return returnMat
#test for stageWise
def regression4():
xArr,yArr=loadDataSet("input/8.Regression/abalone.txt")
stageWise(xArr,yArr,0.01,200)
xMat = mat(xArr)
yMat = mat(yArr).T
xMat = regularize(xMat)
yM = mean(yMat,0)
yMat = yMat - yM
weights = standRegres(xMat, yMat.T)
print (weights.T)
完整代碼地址: https://github.com/apachecn/MachineLearning/blob/master/src/python/8.PredictiveNumericalDataRegression/regression.py
4.3.2、逐步線性回歸在鮑魚(yú)數(shù)據(jù)集上的運(yùn)行效果
逐步線性回歸算法的主要優(yōu)點(diǎn)在于它可以幫助人們理解現(xiàn)有的模型并作出改進(jìn)。當(dāng)構(gòu)建了一個(gè)模型后,可以運(yùn)行該算法找出重要的特征,這樣就有可能及時(shí)停止對(duì)那些不重要特征的手機(jī)。最后,如果用于測(cè)試,該算法每100次迭代后就可以構(gòu)建出一個(gè)模型,可以使用類似于10折交叉驗(yàn)證的方法比較這些模型,最終選擇使誤差最小的模型。
4.4、小結(jié)
當(dāng)應(yīng)用縮減方法(如逐步線性回歸或嶺回歸)時(shí),模型也就增加了偏差(bias),與此同時(shí)卻減小了模型的方差。
5、回歸 項(xiàng)目案例
項(xiàng)目案例1: 預(yù)測(cè)樂(lè)高玩具套裝的價(jià)格
項(xiàng)目概述
Dangler 喜歡為樂(lè)高套裝估價(jià),我們用回歸技術(shù)來(lái)幫助他建立一個(gè)預(yù)測(cè)模型。
開(kāi)發(fā)流程
(1) 收集數(shù)據(jù):用 Google Shopping 的API收集數(shù)據(jù)。
(2) 準(zhǔn)備數(shù)據(jù):從返回的JSON數(shù)據(jù)中抽取價(jià)格。
(3) 分析數(shù)據(jù):可視化并觀察數(shù)據(jù)。
(4) 訓(xùn)練算法:構(gòu)建不同的模型,采用逐步線性回歸和直接的線性回歸模型。
(5) 測(cè)試算法:使用交叉驗(yàn)證來(lái)測(cè)試不同的模型,分析哪個(gè)效果最好。
(6) 使用算法:這次練習(xí)的目標(biāo)就是生成數(shù)據(jù)模型。
收集數(shù)據(jù): 使用 Google 購(gòu)物的 API
由于 Google 提供的 api 失效,我們只能自己下載咯,將數(shù)據(jù)存儲(chǔ)在了 input 文件夾下的 setHtml 文件夾下
準(zhǔn)備數(shù)據(jù): 從返回的 JSON 數(shù)據(jù)中抽取價(jià)格
因?yàn)槲覀冞@里不是在線的,就不再是 JSON 了,我們直接解析線下的網(wǎng)頁(yè),得到我們想要的數(shù)據(jù)。
分析數(shù)據(jù): 可視化并觀察數(shù)據(jù)
這里我們將解析得到的數(shù)據(jù)打印出來(lái),然后觀察數(shù)據(jù)。
訓(xùn)練算法: 構(gòu)建不同的模型
from numpy import *
from bs4 import BeautifulSoup
# 從頁(yè)面讀取數(shù)據(jù),生成retX和retY列表
def scrapePage(retX, retY, inFile, yr, numPce, origPrc):
# 打開(kāi)并讀取HTML文件
fr = open(inFile)
soup = BeautifulSoup(fr.read())
i=1
# 根據(jù)HTML頁(yè)面結(jié)構(gòu)進(jìn)行解析
currentRow = soup.findAll('table', r="%d" % i)
while(len(currentRow)!=0):
currentRow = soup.findAll('table', r="%d" % i)
title = currentRow[0].findAll('a')[1].text
lwrTitle = title.lower()
# 查找是否有全新標(biāo)簽
if (lwrTitle.find('new') > -1) or (lwrTitle.find('nisb') > -1):
newFlag = 1.0
else:
newFlag = 0.0
# 查找是否已經(jīng)標(biāo)志出售,我們只收集已出售的數(shù)據(jù)
soldUnicde = currentRow[0].findAll('td')[3].findAll('span')
if len(soldUnicde)==0:
print "item #%d did not sell" % i
else:
# 解析頁(yè)面獲取當(dāng)前價(jià)格
soldPrice = currentRow[0].findAll('td')[4]
priceStr = soldPrice.text
priceStr = priceStr.replace('$','') #strips out $
priceStr = priceStr.replace(',','') #strips out ,
if len(soldPrice)>1:
priceStr = priceStr.replace('Free shipping', '')
sellingPrice = float(priceStr)
# 去掉不完整的套裝價(jià)格
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, origPrc])
retY.append(sellingPrice)
i += 1
currentRow = soup.findAll('table', r="%d" % i)
# 依次讀取六種樂(lè)高套裝的數(shù)據(jù),并生成數(shù)據(jù)矩陣
def setDataCollect(retX, retY):
scrapePage(retX, retY, 'input/8.Regression/setHtml/lego8288.html', 2006, 800, 49.99)
scrapePage(retX, retY, 'input/8.Regression/setHtml/lego10030.html', 2002, 3096, 269.99)
scrapePage(retX, retY, 'input/8.Regression/setHtml/lego10179.html', 2007, 5195, 499.99)
scrapePage(retX, retY, 'input/8.Regression/setHtml/lego10181.html', 2007, 3428, 199.99)
scrapePage(retX, retY, 'input/8.Regression/setHtml/lego10189.html', 2008, 5922, 299.99)
scrapePage(retX, retY, 'input/8.Regression/setHtml/lego10196.html', 2009, 3263, 249.99)
測(cè)試算法:使用交叉驗(yàn)證來(lái)測(cè)試不同的模型,分析哪個(gè)效果最好
# 交叉驗(yàn)證測(cè)試嶺回歸
def crossValidation(xArr,yArr,numVal=10):
# 獲得數(shù)據(jù)點(diǎn)個(gè)數(shù),xArr和yArr具有相同長(zhǎng)度
m = len(yArr)
indexList = range(m)
errorMat = zeros((numVal,30))
# 主循環(huán) 交叉驗(yàn)證循環(huán)
for i in range(numVal):
# 隨機(jī)拆分?jǐn)?shù)據(jù),將數(shù)據(jù)分為訓(xùn)練集(90%)和測(cè)試集(10%)
trainX=[]; trainY=[]
testX = []; testY = []
# 對(duì)數(shù)據(jù)進(jìn)行混洗操作
random.shuffle(indexList)
# 切分訓(xùn)練集和測(cè)試集
for j in range(m):
if j < m*0.9:
trainX.append(xArr[indexList[j]])
trainY.append(yArr[indexList[j]])
else:
testX.append(xArr[indexList[j]])
testY.append(yArr[indexList[j]])
# 獲得回歸系數(shù)矩陣
wMat = ridgeTest(trainX,trainY)
# 循環(huán)遍歷矩陣中的30組回歸系數(shù)
for k in range(30):
# 讀取訓(xùn)練集和數(shù)據(jù)集
matTestX = mat(testX); matTrainX=mat(trainX)
# 對(duì)數(shù)據(jù)進(jìn)行標(biāo)準(zhǔn)化
meanTrain = mean(matTrainX,0)
varTrain = var(matTrainX,0)
matTestX = (matTestX-meanTrain)/varTrain
# 測(cè)試回歸效果并存儲(chǔ)
yEst = matTestX * mat(wMat[k,:]).T + mean(trainY)
# 計(jì)算誤差
errorMat[i,k] = ((yEst.T.A-array(testY))**2).sum()
# 計(jì)算誤差估計(jì)值的均值
meanErrors = mean(errorMat,0)
minMean = float(min(meanErrors))
bestWeights = wMat[nonzero(meanErrors==minMean)]
# 不要使用標(biāo)準(zhǔn)化的數(shù)據(jù),需要對(duì)數(shù)據(jù)進(jìn)行還原來(lái)得到輸出結(jié)果
xMat = mat(xArr); yMat=mat(yArr).T
meanX = mean(xMat,0); varX = var(xMat,0)
unReg = bestWeights/varX
# 輸出構(gòu)建的模型
print "the best model from Ridge Regression is:\n",unReg
print "with constant term: ",-1*sum(multiply(meanX,unReg)) + mean(yMat)
# predict for lego's price
def regression5():
lgX = []
lgY = []
setDataCollect(lgX, lgY)
crossValidation(lgX, lgY, 10)
使用算法:這次練習(xí)的目標(biāo)就是生成數(shù)據(jù)模型
完整代碼地址: https://github.com/apachecn/MachineLearning/blob/master/src/python/8.PredictiveNumericalDataRegression/regression.py
6、附加 權(quán)衡偏差和方差
任何時(shí)候,一旦發(fā)現(xiàn)模型和測(cè)量值之間存在差異,就說(shuō)出現(xiàn)了誤差。當(dāng)考慮模型中的 “噪聲” 或者說(shuō)誤差時(shí),必須考慮其來(lái)源。你可能會(huì)對(duì)復(fù)雜的過(guò)程進(jìn)行簡(jiǎn)化,這將導(dǎo)致在模型和測(cè)量值之間出現(xiàn) “噪聲” 或誤差,若無(wú)法理解數(shù)據(jù)的真實(shí)生成過(guò)程,也會(huì)導(dǎo)致差異的產(chǎn)生。另外,測(cè)量過(guò)程本身也可能產(chǎn)生 “噪聲” 或者問(wèn)題。下面我們舉一個(gè)例子,我們使用 線性回歸
和 局部加權(quán)線性回歸
處理過(guò)一個(gè)從文件導(dǎo)入的二維數(shù)據(jù)。
其中的 N(0, 1) 是一個(gè)均值為 0、方差為 1 的正態(tài)分布。我們嘗試過(guò)禁用一條直線來(lái)擬合上述數(shù)據(jù)。不難想到,直線所能得到的最佳擬合應(yīng)該是 3.0+1.7x 這一部分。這樣的話,誤差部分就是 0.1sin(30x)+0.06N(0, 1) 。在上面,我們使用了局部加權(quán)線性回歸來(lái)試圖捕捉數(shù)據(jù)背后的結(jié)構(gòu)。該結(jié)構(gòu)擬合起來(lái)有一定的難度,因此我們測(cè)試了多組不同的局部權(quán)重來(lái)找到具有最小測(cè)試誤差的解。
下圖給出了訓(xùn)練誤差和測(cè)試誤差的曲線圖,上面的曲面就是測(cè)試誤差,下面的曲線是訓(xùn)練誤差。我們根據(jù) 預(yù)測(cè)鮑魚(yú)年齡 的實(shí)驗(yàn)知道: 如果降低核的大小,那么訓(xùn)練誤差將變小。從下圖開(kāi)看,從左到右就表示了核逐漸減小的過(guò)程。
一般認(rèn)為,上述兩種誤差由三個(gè)部分組成: 偏差、測(cè)量誤差和隨機(jī)噪聲。局部加權(quán)線性回歸 和 預(yù)測(cè)鮑魚(yú)年齡 中,我們通過(guò)引入了三個(gè)越來(lái)越小的核來(lái)不斷增大模型的方差。
在縮減系數(shù)來(lái)“理解”數(shù)據(jù)這一節(jié)中,我們介紹了縮減法,可以將一些系數(shù)縮減成很小的值或直接縮減為 0 ,這是一個(gè)增大模型偏差的例子。通過(guò)把一些特征的回歸系數(shù)縮減到 0 ,同時(shí)也就減小了模型的復(fù)雜度。例子中有 8 個(gè)特征,消除其中兩個(gè)后不僅使模型更易理解,同時(shí)還降低了預(yù)測(cè)誤差。對(duì)照上圖,左側(cè)是參數(shù)縮減過(guò)于嚴(yán)厲的結(jié)果,而右側(cè)是無(wú)縮減的效果。
方差是可以度量的。如果從鮑魚(yú)數(shù)據(jù)中取一個(gè)隨機(jī)樣本集(例如取其中 100 個(gè)數(shù)據(jù))并用線性模型擬合,將會(huì)得到一組回歸系數(shù)。同理,再取出另一組隨機(jī)樣本集并擬合,將會(huì)得到另一組回歸系數(shù)。這些系數(shù)間的差異大小也就是模型方差的反映。
- 作者:小瑤 片刻
- GitHub地址: https://github.com/apachecn/MachineLearning
- 版權(quán)聲明:歡迎轉(zhuǎn)載學(xué)習(xí) => 請(qǐng)標(biāo)注信息來(lái)源于 ApacheCN