线性回归只能拟合线性曲面(广义的曲面),如果一个回归任务中的输出变量 y ( y ∈ R ) y\,\,\left( y\in \mathbb{R} \right) y(y∈R) 关于特征向量 x = ( x 0 , x 1 , ⋯ , x n ) ( x ∈ R n + 1 , x 0 = 1 ) x=\left( x_0,x_1,\cdots ,x_n \right) \,\,\left( x\in \mathbb{R} ^{n+1},x_0=1 \right) x=(x0,x1,⋯,xn)(x∈Rn+1,x0=1) 不是线性的,那怎样拟合可以比较好呢?局部加权线性回归是一种方法。
当两个样本点的特征向量距离较近时,它们的输出变量通常比较接近,也就是说它们具有相似的性质。现在我们有 m m m 个训练样本点 ( x ( 1 ) , y ( 1 ) ) , ( x ( 2 ) , y ( 2 ) ) , ⋯ , ( x ( m ) , y ( m ) ) \left( x^{\left( 1 \right)},y^{\left( 1 \right)} \right) ,\left( x^{\left( 2 \right)},y^{\left( 2 \right)} \right) ,\cdots ,\left( x^{\left( m \right)},y^{\left( m \right)} \right) (x(1),y(1)),(x(2),y(2)),⋯,(x(m),y(m)) 构成训练集。那么当我们给一个特征向量 x x x 时,要根据训练集中的信息预测出它的输出变量 y y y ,距离 x x x 越近的训练集中的样本点越应该被重视。换句话说,我们需要给训练集中的每个样本点一个权值,距离 x x x 越近的样本点的权值应该越大,这是赋权的一个原则,那么具体应该如何赋权呢?
首先应该想一想,我们怎样衡量距离呢?欧氏距离(2范数)是一种常用的衡量方式,即:
d i s t ( x , x ( i ) ) = ∥ x − x ( i ) ∥ 2 dist\left( x,x^{\left( i \right)} \right) =\left\| x-x^{\left( i \right)} \right\| _2 dist(x,x(i))=∥∥∥x−x(i)∥∥∥2
我们用下面的公式给训练集中第 i i i 样本点赋权值:
w i = exp ( − ∥ x − x ( i ) ∥ 2 2 2 σ 2 ) w_i=\exp \left( -\frac{\left\| x-x^{\left( i \right)} \right\| _{2}^{2}}{2\sigma ^2} \right) wi=exp(−2σ2∥∥x−x(i)∥∥22)
这里似乎很突兀,还不知道为什么,直接就给出了这样的公式,我也不太清楚为什么,这个算法经常使用这样的函数来赋权,对于不是很想深入理论研究的我姑且认为是玄学吧!当然肯定不是玄学,这样做应该是有一定理论依据的,不过我就暂时不了解了。
对于给定的特征向量作为输入,我们要预测它的输出变量,使用下面的经过加权处理后的损失函数:
J ( θ ) = 1 2 ∑ i = 1 m w i ( h θ ( x ( i ) ) − y ( i ) ) 2 J\left( \theta \right) =\frac{1}{2}\sum_{i=1}^m{w_i\left( h_{\theta}\left( x^{\left( i \right)} \right) -y^{\left( i \right)} \right) ^2} J(θ)=21i=1∑mwi(hθ(x(i))−y(i))2
这里还是挺自然的,就是一个简单的加权求和,最小化这样的损失函数时,算法肯定会寻找使得距离 x x x 越近的样本点的损失越小的 θ \theta θ ,也就是说算法更加重视了那些距离 x x x 近的点,这也有利于更准确地预测 y y y 。该函数使用矩阵的方式写出来就是:
J ( θ ) = 1 2 ( X θ − y ) T W ( X θ − y ) J\left( \theta \right) =\frac{1}{2}\left( X\theta -y \right) ^TW\left( X\theta -y \right) J(θ)=21(Xθ−y)TW(Xθ−y)
其中权值矩阵 W = d i a g ( w 1 , w 2 , ⋯ , w m ) W=\mathrm{diag}\left( w_1,w_2,\cdots ,w_m \right) W=diag(w1,w2,⋯,wm) 。
优化这个损失函数的过程就是该算法的训练过程。优化这个损失函数后,就可以使用得到参数 θ \theta θ ,进而使用 h θ ( x ) h_{\theta}\left( x \right) hθ(x) 预测输出变量 y y y 。 如果我们有 K K K 个特征向量需要被预测,记这 K K K 个特征向量为 x ( m + 1 ) , x ( m + 2 ) , ⋯ , x ( m + K ) x^{\left( m+1 \right)},x^{\left( m+2 \right)},\cdots ,x^{\left( m+K \right)} x(m+1),x(m+2),⋯,x(m+K) ,那么对于每一个特征向量 x ( m + i ) ( 1 ⩽ i ⩽ K ) x^{\left( m+i \right)}\,\,\left( 1\leqslant i\leqslant K \right) x(m+i)(1⩽i⩽K) 我们都要计算一次权值矩阵,然后然后优化损失函数得到 θ \theta θ ,进而预测输出变量 y ( m + i ) y^{\left( m+i \right)} y(m+i) 。
梯度下降法是一种很常用的优化方法,这里当然也可以使用梯度下降法,主要就是求解出损失函数的梯度,求解梯度实际上也就是求导数,对其求导:
∂ J ( θ ) ∂ θ = X T W ( X θ − y ) = X T W X θ − X T W y \frac{\partial J\left( \theta \right)}{\partial \theta}=X^TW\left( X\theta -y \right) =X^TWX\theta -X^TWy ∂θ∂J(θ)=XTW(Xθ−y)=XTWXθ−XTWy
这样也就可以使用梯度下降法了,这里就不多说了,直接看正规方程的方法,令其导数为 0 0 0 可以解出该函数的极小值点:
θ = ( X T W X ) − 1 X T W y \theta =\left( X^TWX \right) ^{-1}X^TWy θ=(XTWX)−1XTWy
值得注意的是,局部加权线性回归时间代价十分高,因为对于每一个需要预测的点,我们都要求出相对应的权值矩阵并且单独训练。而普通的线性回归算法只需要训练一次即可预测所有的点。
局部加权线性回归的好处就是它几乎可以拟合任意形状,只要超参数 σ \sigma σ 选取合适,那么该算法可以拟合的较好。实际生活中,数据的输出变量和特征向量是线性关系的情况不多,因此纯线性回归模型可能适用性不强,但是局部加权线性回归可以对非线性的数据分布做出较好的拟合。
写出局部加权线性回归函数。
import numpy as np
def calweight(a, b): # 求解权值的函数
sigma = 0.3
return np.exp(-np.linalg.norm(a-b)**2 / (2*sigma**2))
def LWLR(X_train, y_train, X_test): # 局部加权线性回归
X_train = np.insert(X_train,0,1,axis=1)
X_test = np.insert(X_test, 0, 1, axis = 1)
y_pred = np.empty(X_test.shape[0]) # 预测值
for k in range(X_test.shape[0]): # 对所有的需要预测的特征向量先训练再预测
# 求解权值矩阵
w = np.empty(X_train.shape[0])
for i in range(X_train.shape[0]):
w[i] = calweight(X_test[k], X_train[i])
W = np.diag(w) #权值矩阵
theta=np.dot(np.linalg.pinv(np.dot(np.dot(X_train.T,W),X_train)),np.dot(np.dot(X_train.T,W),y_train))
y_pred[k] = np.dot(theta,X_test[k]) # 预测
return y_pred
创造数据,使用该函数拟合回归曲线。
import matplotlib.pyplot as plt
def CreateData():
X = np.arange(0,10,0.1)
y = np.empty(X.shape[0])
for i in range(X.shape[0]):
y[i] = X[i]**3 - 10*X[i]**2 + X[i] + np.random.uniform(-10,10)
return X[:,np.newaxis], y
np.random.seed(0)
X, y = CreateData()
plt.scatter(X, y) # 可视化
X_test = np.arange(0.05,10,0.1) #测试集,目的是通过其画出拟合曲线
X_test = X_test[:,np.newaxis]
y_pred = LWLR(X, y, X_test)
plt.plot(X_test, y_pred, color='red') # 画出拟合曲线
plt.show()
拟合曲线如下:
σ = 1 \sigma=1 σ=1 ,感觉在底部稍微有点欠拟合
σ = 0.5 \sigma=0.5 σ=0.5 ,拟合效果不错
σ = 0.1 \sigma=0.1 σ=0.1 ,有些过拟合
只要超参数 σ \sigma σ 选取合适,很多情况下都会有较好的拟合效果。