本主題講述線性回歸的欠擬合問(wèn)題:
- 怎么評(píng)估擬合效果
一. 回歸中的問(wèn)題
??回歸一詞來(lái)自一種任務(wù)背景,測(cè)試孩子與父母身高的關(guān)系,如果父母身高比較高,孩子身高也會(huì)比較高,但沒(méi)有父母的平均身高高,更加趨向于人類(lèi)平均身高,這就是身高回歸平均身高的一種現(xiàn)象,稱(chēng)為回歸。
1. 擬合性問(wèn)題
線性回歸中,怎么判定訓(xùn)練的模型的好壞呢?因?yàn)閿?shù)據(jù)集本身的影響,一般會(huì)產(chǎn)生如下兩種情況:
??|-(1)欠擬合
??|-(2)過(guò)擬合
??
我們使用下面兩個(gè)數(shù)據(jù)集來(lái)建立直觀判定,哪一種數(shù)據(jù)集會(huì)產(chǎn)生更好的擬合?
??|- 擬合好壞的判別標(biāo)準(zhǔn)。
1.1. 數(shù)據(jù)集1
% matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
x_dat=np.loadtxt('ex2x.dat')
y_dat=np.loadtxt('ex2y.dat')
figure=plt.figure('數(shù)據(jù)集可視化',figsize=(6,4))
ax=figure.add_axes([0.1,0.1,0.8,0.8],xlabel='年齡',ylabel='身高')
ax.set_xlim(0,10)
ax.set_ylim(0,1.5)
ax.scatter(x_dat,y_dat,label='年齡身高數(shù)據(jù)集',marker='.',color=(0,0,1,1))
plt.show()

1.2. 數(shù)據(jù)集2
% matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
# 第一列用來(lái)做偏置項(xiàng)1的,真正的數(shù)據(jù)是第2列與第3列
data=np.loadtxt('ex0.txt')
figure=plt.figure('數(shù)據(jù)集可視化',figsize=(6,4))
ax=figure.add_axes([0.1,0.1,0.8,0.8],xlabel='年齡',ylabel='身高')
ax.set_xlim(-0.2,1.2)
ax.set_ylim(1,5.5)
ax.scatter(data[:,1],data[:,2],label='不知名數(shù)據(jù)集',marker='.',color=(1,0,0,1))
plt.show()

1.3. 相關(guān)系數(shù)
在數(shù)學(xué)上提供了一個(gè)專(zhuān)門(mén)的概念來(lái)描述這種數(shù)據(jù)集的擬合性:相關(guān)系數(shù)。
??|-相關(guān)系數(shù):用來(lái)度量?jī)蓚€(gè)向量相關(guān)程度的量。
??
相關(guān)系數(shù)的公式定義:
??|-
??其中表示
的樣本協(xié)方差(無(wú)偏):
??其中表示向量
的樣本方差(無(wú)偏方差):
????|-其中表示樣本均值:
1.4. 練習(xí)作業(yè)
- 實(shí)現(xiàn)協(xié)方差,方差與均值;
- 調(diào)用numpy中協(xié)方差,方差與均值函數(shù);
- 對(duì)比自己實(shí)現(xiàn)的計(jì)算結(jié)果與numpy的計(jì)算結(jié)果;
- 理解總體方差(有偏)與樣本方差(無(wú)偏)的區(qū)別
1.5. 數(shù)據(jù)集相關(guān)性評(píng)估
下面對(duì)上面兩個(gè)數(shù)據(jù)集,計(jì)算他們的相關(guān)系數(shù)。
??|- 計(jì)算預(yù)測(cè)輸出值與真實(shí)值之間的相關(guān)系數(shù),從而可以判定數(shù)據(jù)集的擬合性好壞。
- 數(shù)據(jù)集1的相關(guān)系數(shù)計(jì)算(sklearn實(shí)現(xiàn))
% matplotlib inline
import matplotlib.pyplot as plt
from sklearn.linear_model import *
import numpy as np
X_DATA = np.loadtxt('ex2x.dat')
Y_DATA = np.loadtxt('ex2y.dat')
regression=LinearRegression()
X_DATA=X_DATA.reshape(-1, 1)
Y_DATA=Y_DATA.reshape(-1, 1)
regression.fit(X_DATA, Y_DATA)
# 預(yù)測(cè)輸出
Y=regression.predict(X_DATA)
#print(Y)
print(Y.shape)
print(Y_DATA.shape)
# 判定Y_DATA與Y的相關(guān)程度
# 必須確保是行向量
cor=np.corrcoef(Y.T,Y_DATA.T)
print(cor)
# 可視化
figure=plt.figure('數(shù)據(jù)集可視化',figsize=(6,4))
ax=figure.add_axes([0.1,0.1,0.8,0.8],xlabel='年齡',ylabel='身高')
ax.set_xlim(0,10)
ax.set_ylim(0,1.5)
ax.scatter(X_DATA,Y_DATA,label='年齡身高數(shù)據(jù)集',marker='.',color=(0,0,1,1))
ax.plot(X_DATA,Y,label='線性擬合',color='r',linewidth=3)
ax.legend()
plt.show()
(50, 1)
(50, 1)
[[1. 0.92631702]
[0.92631702 1. ]]

??上面的相關(guān)系數(shù)的說(shuō)明:
????|-在對(duì)角線上是自己與自己的關(guān)聯(lián)系數(shù);
??????|-對(duì)角線上第一個(gè)1是與自己的相關(guān)系數(shù);
??????|-對(duì)角線第二個(gè)1是與自己的相關(guān)系數(shù);
????|-反對(duì)角線上的兩個(gè)數(shù)是與
、
與
的相關(guān)性系數(shù)。
??
??自己與自己的相關(guān)系數(shù)是最好的,就是1。完全不相關(guān)的的就是0,等于協(xié)方差完全無(wú)關(guān)。
- 數(shù)據(jù)集2的相關(guān)系數(shù)計(jì)算(sklearn實(shí)現(xiàn))
% matplotlib inline
import matplotlib.pyplot as plt
from sklearn.linear_model import *
import numpy as np
data=np.loadtxt('ex0.txt')
X_DATA=data[:,1]
Y_DATA=data[:,2]
regression=LinearRegression()
X_DATA=X_DATA.reshape(-1, 1)
Y_DATA=Y_DATA.reshape(-1, 1)
regression.fit(X_DATA, Y_DATA)
# 預(yù)測(cè)輸出
Y=regression.predict(X_DATA)
#print(Y)
print(Y.shape)
print(Y_DATA.shape)
# 判定Y_DATA與Y的相關(guān)程度
# 必須確保是行向量
cor=np.corrcoef(Y.T,Y_DATA.T)
print(cor)
# 可視化
figure=plt.figure('數(shù)據(jù)集可視化',figsize=(6,4))
ax=figure.add_axes([0.1,0.1,0.8,0.8],xlabel='年齡',ylabel='身高')
ax.set_xlim(-0.2,1.2)
ax.set_ylim(1,5.5)
ax.scatter(data[:,1],data[:,2],label='不知名數(shù)據(jù)集',marker='.',color=(1,0,0,1))
ax.plot(X_DATA,Y,label='線性擬合',color='b',linewidth=3)
ax.legend()
plt.show()
(200, 1)
(200, 1)
[[1. 0.98647356]
[0.98647356 1. ]]

??從上面兩個(gè)數(shù)據(jù)集的直觀視覺(jué)與相關(guān)系數(shù)都看得出來(lái),數(shù)據(jù)集2明顯具有較好的擬合效果。
??更好的擬合性表現(xiàn)在數(shù)據(jù)集樣本點(diǎn)盡可能在回歸(擬合)直線上。
-
問(wèn)題
??能否有好的方式提高數(shù)據(jù)的擬合性?這就可以解決樣本欠擬合的問(wèn)題。
二、局部加權(quán)回歸算法
??局部加權(quán)回歸(Locally Weighted Linear Regression:LWLR)
??局部加權(quán)回歸是解決線性回歸欠擬合問(wèn)題二提出的。
??解決問(wèn)題的出發(fā)點(diǎn)是:線性回歸的最終核心是使均方差函數(shù)最?。ㄗ钚【秸`差),局部加權(quán)是引入加權(quán)的方式,降低均方誤差,使得擬合性的度量結(jié)果更好。
??因?yàn)榫€性擬合中的均方差是無(wú)偏的,適當(dāng)引入一些偏差,可以有效降低均方差。
1. 局部加權(quán)模型
??
??其中就是局部加權(quán)系數(shù)。
??
??其中加權(quán)系數(shù)的確定是一個(gè)麻煩的事情,該系數(shù)必須確保,預(yù)測(cè)樣本點(diǎn)離已知樣本越遠(yuǎn)加權(quán)系數(shù)越小,預(yù)測(cè)樣本點(diǎn)離已知樣本越近加權(quán)系數(shù)越大。如果大家還記得正態(tài)分布(高斯分布)函數(shù)的性質(zhì),如果在0點(diǎn)取值1,在接近無(wú)窮遠(yuǎn)點(diǎn),取值0。我們就利用正態(tài)分布這個(gè)特性,可以取,其其他位置的元素都為0,對(duì)角線上的值為:
??
??矩陣形式是:
??
??下面使用圖示的方式,來(lái)理解該系數(shù)在回歸訓(xùn)練中控制訓(xùn)練樣本參與訓(xùn)練的作用與價(jià)值。
% matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
# 圖形數(shù)據(jù)
sigma=1
x_0=0
x=np.linspace(-1,1,101,dtype=np.float32)
y_1=np.exp((x_0-x)**2/(-2.0*sigma*sigma))
sigma=0.5
y_05=np.exp((x_0-x)**2/(-2.0*sigma*sigma))
sigma=0.1
y_001=np.exp((x_0-x)**2/(-2.0*sigma*sigma))
# 坐標(biāo)軸
figure=plt.figure('機(jī)器學(xué)習(xí)-回歸優(yōu)化',figsize=(6,4))
ax=figure.add_axes([0.1,0.1,0.8,0.8],label='高斯分布系數(shù)權(quán)重關(guān)系')
ax.set_xlim(-1,1)
ax.set_ylim(0,1)
# 圖形繪制
ax.plot(x,y_1,color=(1,0,0,1),label='$\sigma=1$')
ax.plot(x,y_05,color=(0,1,0,1),label='$\sigma=0.5$')
ax.plot(x,y_001,color=(0,0,1,1),label='$\sigma=0.01$')
ax.legend()
plt.show()

2. 局部加權(quán)實(shí)現(xiàn)
??因?yàn)槭强傮w擬合性考慮,所以每個(gè)測(cè)試樣本對(duì)總體訓(xùn)練樣本都有一個(gè)加權(quán)矩陣,該測(cè)試樣本根據(jù)加權(quán)矩陣計(jì)算出對(duì)應(yīng)的預(yù)測(cè)值。這樣預(yù)測(cè)值總體的擬合性就會(huì)更好點(diǎn)。
- 結(jié)構(gòu)設(shè)計(jì)
import matplotlib.pyplot as plt
import numpy as np
# 求一個(gè)樣本的局部權(quán)重預(yù)測(cè)值(下面函數(shù)按照多維模式,但只考慮1維情況)
class LWLinearRegression:
def __init__(self, sample_x, label_y, sigma=1.0):
self.X=sample_x
self.Y=label_y
def train_one_sample(self, data):
# data 測(cè)試樣本
# 數(shù)據(jù)加權(quán)預(yù)測(cè)值
# 數(shù)據(jù)格式化
# 1. 計(jì)算局部加權(quán)矩陣
# 2. 計(jì)算線性回歸系數(shù)矩陣
# 3.計(jì)算預(yù)測(cè)值
return [0.0]
def train(self, datasets):
Y=np.zeros(shape=(datasets.shape[0],1), dtype=np.float)
# 循環(huán)計(jì)算預(yù)測(cè)值
for idx in range(datasets.shape[0]):
Y[idx]=self.train_one_sample(datasets[idx])
return Y
# 數(shù)據(jù)初始化
data=np.loadtxt('ex0.txt')
X_DATA=data[:,1]
Y_DATA=data[:,2]
# 數(shù)據(jù)格式化
X=np.zeros(shape=(X_DATA.shape[0], 2), dtype=np.float) # 考慮偏置項(xiàng),在測(cè)試特征加一維
Y=Y_DATA.reshape(Y_DATA.shape[0], 1)
X[:, 0]=X_DATA
X[:, 1]=1
lwlr=LWLinearRegression(X, Y)
predict=lwlr.train(X)
#print(predict)
- 實(shí)現(xiàn)
import matplotlib.pyplot as plt
import numpy as np
# 求一個(gè)樣本的局部權(quán)重預(yù)測(cè)值(下面函數(shù)按照多維模式,但只考慮1維情況)
class LWLinearRegression:
def __init__(self, sample_x, label_y, sigma=1.0):
self.X=sample_x
self.Y=label_y
self.sigma=sigma
def train_one_sample(self, data):
# data 測(cè)試樣本
# 數(shù)據(jù)加權(quán)預(yù)測(cè)值
# 數(shù)據(jù)格式化
# 1. 計(jì)算局部加權(quán)矩陣
local_weight=np.zeros(shape=(self.X.shape[0],self.X.shape[0]),dtype=np.float32)
for idx in range(local_weight.shape[0]):
# 求差
diff=data-self.X[idx,:]
# 求平方,然后除以sigma
sqrt=np.matmul(diff,diff.T)/(-2.0*(self.sigma**2))
# 計(jì)算局部加權(quán)矩陣的對(duì)角線
local_weight[idx,idx]=np.exp(sqrt)
# 2. 計(jì)算線性回歸系數(shù)矩陣
# 這里應(yīng)該先做一個(gè)奇異值判定(我們暫時(shí)不去判定,等系統(tǒng)處理發(fā)生的異常)
W=np.matmul(
np.matmul(
np.matmul(
np.linalg.inv(
np.matmul(
np.matmul(
self.X.T,
local_weight
),
self.X
)
),
self.X.T
),
local_weight
),
self.Y
)
# 3.計(jì)算預(yù)測(cè)值
out_value=np.matmul(data,W)
return out_value
def train(self, datasets):
Y=np.zeros(shape=(datasets.shape[0],1), dtype=np.float)
# 循環(huán)計(jì)算預(yù)測(cè)值
for idx in range(datasets.shape[0]):
Y[idx]=self.train_one_sample(datasets[idx])
return Y
# 數(shù)據(jù)初始化
data=np.loadtxt('ex0.txt')
X_DATA=data[:,1]
Y_DATA=data[:,2]
# 數(shù)據(jù)格式化
X=np.zeros(shape=(X_DATA.shape[0], 2), dtype=np.float) # 考慮偏置項(xiàng),在測(cè)試特征加一維
Y=Y_DATA.reshape(Y_DATA.shape[0], 1)
X[:, 0]=X_DATA
X[:, 1]=1
lwlr=LWLinearRegression(X, Y)
# 使用訓(xùn)練集作為測(cè)試集
predict=lwlr.train(X)
#print(predict)
- 擬合可視化
??通過(guò)可視化來(lái)觀察擬合效果;為了保證代碼的完整性,我們把計(jì)算于可視化代碼放在一起。
import matplotlib.pyplot as plt
import numpy as np
# 求一個(gè)樣本的局部權(quán)重預(yù)測(cè)值(下面函數(shù)按照多維模式,但只考慮1維情況)
class LWLinearRegression:
def __init__(self, sample_x, label_y, sigma=1.0):
self.X=sample_x
self.Y=label_y
self.sigma=sigma
def train_one_sample(self, data):
# data 測(cè)試樣本
# 數(shù)據(jù)加權(quán)預(yù)測(cè)值
# 數(shù)據(jù)格式化
# 1. 計(jì)算局部加權(quán)矩陣
local_weight=np.zeros(shape=(self.X.shape[0],self.X.shape[0]),dtype=np.float32)
for idx in range(local_weight.shape[0]):
# 求差
diff=data-self.X[idx,:]
# 求平方,然后除以sigma
sqrt=np.matmul(diff,diff.T)/(-2.0*(self.sigma**2))
# 計(jì)算局部加權(quán)矩陣的對(duì)角線
local_weight[idx,idx]=np.exp(sqrt)
# 2. 計(jì)算線性回歸系數(shù)矩陣
# 這里應(yīng)該先做一個(gè)奇異值判定(我們暫時(shí)不去判定,等系統(tǒng)處理發(fā)生的異常)
W=np.matmul(
np.matmul(
np.matmul(
np.linalg.inv(
np.matmul(
np.matmul(
self.X.T,
local_weight
),
self.X
)
),
self.X.T
),
local_weight
),
self.Y
)
# 3.計(jì)算預(yù)測(cè)值
out_value=np.matmul(data,W)
return out_value
def train(self, datasets):
Y=np.zeros(shape=(datasets.shape[0],1), dtype=np.float)
# 循環(huán)計(jì)算預(yù)測(cè)值
for idx in range(datasets.shape[0]):
Y[idx]=self.train_one_sample(datasets[idx])
return Y
# 數(shù)據(jù)初始化
data=np.loadtxt('ex0.txt')
X_DATA=data[:,1]
Y_DATA=data[:,2]
# 數(shù)據(jù)格式化
X=np.zeros(shape=(X_DATA.shape[0], 2), dtype=np.float) # 考慮偏置項(xiàng),在測(cè)試特征加一維
Y=Y_DATA.reshape(Y_DATA.shape[0], 1)
X[:, 0]=X_DATA
X[:, 1]=1
sigma=0.003
lwlr=LWLinearRegression(X, Y,sigma)
# 使用訓(xùn)練集作為測(cè)試集
predict=lwlr.train(X)
#print(predict)
# 可視化
# 可視化
figure=plt.figure('數(shù)據(jù)集可視化',figsize=(6,4))
ax=figure.add_axes([0.1,0.1,0.8,0.8],xlabel='年齡',ylabel='身高')
ax.set_xlim(-0.2,1.2)
ax.set_ylim(1,5.5)
ax.scatter(X_DATA,Y_DATA,label='不知名數(shù)據(jù)集',marker='.',color=(1,0,0,1))
# 排序顯示
# 得到排序索引
sort_idx=X_DATA.argsort(0)
# 得到排序的結(jié)果
X_SORT=X_DATA[sort_idx]
Y_SORT=predict[sort_idx]
ax.plot(X_SORT,Y_SORT,label='線性擬合',color='b',linewidth=3)
ax.legend()
plt.show()

- 擬合度量計(jì)算
??通過(guò)相關(guān)系數(shù),來(lái)判定擬合的效果;
import matplotlib.pyplot as plt
import numpy as np
# 求一個(gè)樣本的局部權(quán)重預(yù)測(cè)值(下面函數(shù)按照多維模式,但只考慮1維情況)
class LWLinearRegression:
def __init__(self, sample_x, label_y, sigma=1.0):
self.X=sample_x
self.Y=label_y
self.sigma=sigma
def train_one_sample(self, data):
# data 測(cè)試樣本
# 數(shù)據(jù)加權(quán)預(yù)測(cè)值
# 數(shù)據(jù)格式化
# 1. 計(jì)算局部加權(quán)矩陣
local_weight=np.zeros(shape=(self.X.shape[0],self.X.shape[0]),dtype=np.float32)
for idx in range(local_weight.shape[0]):
# 求差
diff=data-self.X[idx,:]
# 求平方,然后除以sigma
sqrt=np.matmul(diff,diff.T)/(-2.0*(self.sigma**2))
# 計(jì)算局部加權(quán)矩陣的對(duì)角線
local_weight[idx,idx]=np.exp(sqrt)
# 2. 計(jì)算線性回歸系數(shù)矩陣
# 這里應(yīng)該先做一個(gè)奇異值判定(我們暫時(shí)不去判定,等系統(tǒng)處理發(fā)生的異常)
W=np.matmul(
np.matmul(
np.matmul(
np.linalg.inv(
np.matmul(
np.matmul(
self.X.T,
local_weight
),
self.X
)
),
self.X.T
),
local_weight
),
self.Y
)
# 3.計(jì)算預(yù)測(cè)值
out_value=np.matmul(data,W)
return out_value
def train(self, datasets):
Y=np.zeros(shape=(datasets.shape[0],1), dtype=np.float)
# 循環(huán)計(jì)算預(yù)測(cè)值
for idx in range(datasets.shape[0]):
Y[idx]=self.train_one_sample(datasets[idx])
return Y
# 數(shù)據(jù)初始化
data=np.loadtxt('ex0.txt')
X_DATA=data[:,1]
Y_DATA=data[:,2]
# 數(shù)據(jù)格式化
X=np.zeros(shape=(X_DATA.shape[0], 2), dtype=np.float) # 考慮偏置項(xiàng),在測(cè)試特征加一維
Y=Y_DATA.reshape(Y_DATA.shape[0], 1)
X[:, 0]=X_DATA
X[:, 1]=1
sigma=0.003
lwlr=LWLinearRegression(X, Y,sigma)
# 使用訓(xùn)練集作為測(cè)試集
predict=lwlr.train(X)
#print(predict)
cor=np.corrcoef(predict.T,Y.T)
print(cor)
[[1. 0.99931945]
[0.99931945 1. ]]
??從相關(guān)系數(shù)來(lái)看,擬合的效果應(yīng)該是非常的好了!
??但是該代碼中,還是可能存在矩陣奇異問(wèn)題。當(dāng)取其中sigma參數(shù)為很小的時(shí)候,由于參與訓(xùn)練的樣本減少,矩陣出現(xiàn)奇異的可能性非常大。
??奇異值問(wèn)題的解決留待下一個(gè)主題講解。
??本主題中故意采用了ndarray的向量形式。如果使用標(biāo)準(zhǔn)的矩陣運(yùn)算,則在格式化上面會(huì)簡(jiǎn)單很多,因?yàn)榫仃嚕╪p.matrix)支持*這樣的內(nèi)積運(yùn)算符號(hào)。就不用使用matmul這樣的函數(shù)運(yùn)算。