线性回归(LR)
基本概念
给定一个由d个属性决定的示例$x=(x_{1},x_{2},...)$,线性模型试图学得一个通过属性的线性组合来预测的函数,即
$$
f(x)=w_{1}x_{1}+w_{2}x_{2}+...+w_{d}x_{d}+b
$$
也可以写成向量形式
$$
f(x)=w^{T}x+b
$$
其中$w=(w_{1},w_{2},...)$,而$w$与$b$确定后,模型就得以确定
基本求解方法
针对确定$w$与$b$,均方误差是回归任务中最常用的性能度量,因此我们可以使均方误差最小化,针对函数
$$
f(x_{i})=w^Tx_{i}+b
$$
我们希望损失函数最小
$$
J(w)=\frac{1}{2m}\sum_{i=1}^{m}(f(x_{i})-y)^{2}
$$
最小二乘法
用矩阵形式就是
$$
J(w)=\frac{1}{2}(Xw-y)^T(Xw-y)
$$
对$w$求导,令其等于0得到
$$
w=(X^TX)^{-1}X^Ty
$$
这就是普通最小二乘法的内容,最小二乘法须要求解$(X^TX)^{-1}$,其计算量为$O(n^3)$,当训练数据集过于庞大的话,其求解过程非常耗时,所以我们同时有梯度下降方法来计算(在之前的文章里这部分写错了,我误认为多$w$无法使用最小二乘法)
梯度下降
其中梯度的计算如下
$$
\nabla f(w_{1},w_{2},...)=(\frac{\partial f}{\partial w_{1}},\frac{\partial f}{\partial w_{2}},...)
$$
梯度下降的公式则是
$$
w_{t+1}=w_{t}-\eta*\nabla f(w_{t})
$$
其中$\eta$代表学习率,一般我们使用$\eta=\frac{\eta}{t+1}+a$来防止步长过大和过小,通过迭代后我们可以获得我们想要的$w$值,梯度下降已经在之前的文章写过了,所以不再赘述
局部线性加权
线性回归的一个问题就是欠拟合,将不能取得很好的预测效果。因为它是具有最小均方误差的无偏估计。解决这个问题的方法就是允许在估计中一些偏差。其中一个非常有效的方法就是局部加权线性回归
$$
w=(X^TWX)^{-1}X^TWy
$$
其中W是权重矩阵,用来给每个点赋予权重
$$
w(i,i)=exp({-\frac{(x^{i}-x)^2}{2k^2}})
$$
解释:
- 当样本点 $x^i$接近预测点$x$时,权值大。$w^i\approx 1$
- 当样本点 $x^i$远离预测点$x$时,权值大。$w^i\approx 0$
- 其中参数$k$为衰减因子,即权重衰减的速率
从一般的结果来看 局部线性回归能很好地解决线性回归欠拟合的问题,但又可能出现过拟合,所以参数调整影响了模型的泛化能力,选取合适参数至关重要(调参侠)
过拟合问题解决
通过采用正则化方法,保留所有特征,但减小θ的量级,适用于有很多特征且每一个特征都对模型预测有影响的情况
正则化
为了防止过拟合的情况出现,即$θ$的值不能过大,构造更简单(奥卡姆剃刀原理)、更平滑的预测函数,需要对损失函数加上一个惩罚项/正则项(一般对$θ1, θ2,...,θn$正则化,不对$θ0$正则化),正则项的形式有L1-norm,L2-norm,Elastic Net等。
1.岭回归(Ridge Regression)
使用L2正则的线性回归模型就称为岭回归。L2-norm通过缩小模型参数来防止过拟合的效果,L2-norm不会让某些系数等于0,而是接近于0,且参数变小的过程加剧,通过增加以下约束
$$
\sum_{k=1}^{m}w_{k}^2\leq \lambda
$$
使损失函数重新为
$$
J(w)=\frac{1}{2m}\sum_{i=1}^{m}(f(x_{i})-y)^{2}+\lambda\sum_{j=1}^{n}w_{j}^2
$$
若使用最小二乘法,则求解出的矩阵为
$$
w=(X^TX+\lambda I)^{-1}X^Ty
$$
其也解决了非奇异矩阵的情况,但是缺点是计算量变大
2.Lasso回归
Lasso是用绝对值取代了平方和
$$
J(w)=\frac{1}{2m}\sum_{i=1}^{m}(f(x_{i})-y)^{2}+\lambda\sum_{j=1}^{n}\left |w_j \right |
$$
Lasso方法可以达到变量选择的效果,将不显著的变量系数压缩至0,而Ridge方法虽然也对原本的系数进行了一定程度的压缩,但是任一系数都不会压缩至0,最终模型保留了所有的变量。Lasso方法可以防止数据产生维数灾难(即样本量的个数远小于维数)
由于Lasso范数用的是绝对值,导致Lasso的优化目标不是连续可导的,也就是说,最小二乘法,梯度下降法,牛顿法,拟牛顿法都不能用,当然我们可以使用前向逐步回归,最小角算法,或者对不可导点重新定义等解决
可以看到Lasso回归可以存在维度消失的情况,而岭回归则不存在这种情况
代码
import numpy as np
import matplotlib.pyplot as plt
def loaddata(filename):
data = []
lable = []
with open(filename) as fr:
while True:
line = fr.readline()
if line:
linearr = line.strip().split()
mask_x = [float(x) for x in linearr]
data.append(mask_x[0:-1])
lable.append(float(linearr[-1]))
else:
break
pass
return data, lable
def caculate(Xarr, Yarr):
Xmat = np.mat(Xarr)
Ymat = np.mat(Yarr).T
XTX = Xmat.T * Xmat
if np.linalg.det(XTX) == 0.0:
print('error')
return
w = XTX.I * (Xmat.T * Ymat)
return w
def simpleshow(x, y, w):
Xmat = np.mat(x)
Ymat = np.mat(y)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(Xmat[:, 1].flatten().A[0], Ymat.T[:, 0].flatten().A[0])
xcopy = Xmat.copy()
xcopy.sort(0)
yhat = xcopy * w
ax.plot(xcopy[:, 1], yhat)
plt.show()
def show(testpoint, x, y, y2):
Xmat = np.mat(testpoint)
Ymat = np.mat(y2)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(Xmat[:, 1].flatten().A[0], Ymat.T.flatten().A[0], s=2)
srtind = Xmat[:, 1].argsort(0)
xsort = Xmat[srtind][:, 0, :]
yhat = ycaculate(testpoint, x, y, 10)
ax.plot(xsort[:, 1], yhat[srtind])
plt.show()
def lxl(testpoint, Xarr, Yarr, k=1.0):
Xmat = np.mat(Xarr)
Ymat = np.mat(Yarr).T
m = np.shape(Xmat)[0]
w = np.mat(np.eye((m)))
for j in range(m):
different = testpoint - Xmat[j, :]
w[j, j] = np.exp(different * different.T / (-2.0 * k ** 2))
xTx = Xmat.T * (w * Xmat)
if np.linalg.det(xTx) == 0.0:
return
ws = xTx.I * (Xmat.T * (w * Ymat))
return testpoint * ws
def ycaculate(testpoint, xarr, yarr, k=1.0):
testarr = np.mat(testpoint)
m = np.shape(testarr)[0]
yhat = np.zeros(m)
for i in range(m):
yhat[i] = lxl(testpoint[i], xarr, yarr, k)
return yhat
def ridgeregres(Xmat, Ymat, lam=0.2):
XTX = Xmat.T * Xmat
demon = XTX + np.eye(np.shape(Xmat)[1]) * lam
if np.linalg.det(demon) == 0.0:
print('error')
return
w = demon.I * (Xmat.T * Ymat)
return w
def ridgetest(x, y):
Xmat = np.mat(x)
Ymat = np.mat(y).T
ymean = np.mean(Ymat, 0)
xmean = np.mean(Xmat, 0)
xvar = np.var(Xmat, 0)
Xmat = (Xmat - xmean) / xvar
Ymat = Ymat - ymean
numtest = 30
wmat = np.zeros((numtest, np.shape(Xmat)[1]))
for i in range(numtest):
ws = ridgeregres(Xmat, Ymat, np.exp(i - 10))
wmat[i, :] = ws.T
return wmat
def rssError(yArr, yHatArr):
return ((yArr - yHatArr) ** 2).sum()
def stagewise(x, y, eps=0.01, num=100):
Xmat = np.mat(x)
Ymat = np.mat(y).T
ymean = np.mean(Ymat, 0)
xmean = np.mean(Xmat, 0)
xvar = np.var(Xmat, 0)
Xmat = (Xmat - xmean) / xvar
Ymat = Ymat - ymean
m,n=np.shape(Xmat)
returnmat=np.zeros(((num,n)))
ws=np.zeros((n,1))
wstest=ws.copy()
wsmax=ws.copy()
for i in range(num):
error=float('inf')
for j in range(n):
for sign in [-1,1]:
wstest=ws.copy()
wstest[j]+=eps*sign
ytest=Xmat*wstest
err=rssError(ytest.A,Ymat.A)
if err<error:
error=err
wsmax=wstest
ws=wsmax.copy()
returnmat[i,:]=ws.T
return returnmat
if __name__ == "__main__":
x,y=loaddata('abalone.txt')
print(stagewise(x,y,0.001,5000))
总结
因为数学推导的latex太难写了,所以我一般笔记是写了重要的部分,我在考虑要不要准备一个本子来记录数学内容,下一篇应该树回归了
Comments | NOTHING