机器学习算法-LR(linear)

发布于 2020-10-01  1396 次阅读


线性回归(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}})
$$
解释:

  1. 当样本点 $x^i$接近预测点$x$时,权值大。$w^i\approx 1$
  2. 当样本点 $x^i$远离预测点$x$时,权值大。$w^i\approx 0$
  3. 其中参数$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太难写了,所以我一般笔记是写了重要的部分,我在考虑要不要准备一个本子来记录数学内容,下一篇应该树回归了