跳至主要內容

线性回归

原创Xenny约 3893 字大约 16 分钟机器学习机器学习监督学习线性回归

线性回归

  • 机器学习中的线性回归(Linear Regression)来自于统计学中的方法,入门篇中我们了解到回归就是使用现有数据预测后续值,而机器学习中的线性回归就是让计算机通过训练数据得出一个线性模型,例如对于数据集D={(xi,yi)}D=\{(x_i,y_i)\},我们想要通过训练模型得到xix_iyiy_i之间的线性关系,从而能够预测未知的xjx_j对应的yjy_j值。

    显然对于这个问题,我们在数学上可以直接使用最小二乘法之类的方法得到一条拟合直线,而在ML中则有所不同,在这里我们假设最终得到的线性函数为hh(代表hypothesis),它是一个从xxyy的映射,则可以记为:

    hθ(x)=θ0+θ1x h_\theta(x) = \theta_0 + \theta_1 x

    其中xx是输入变量,θi\theta_i是由模型输出的系数。

一元线性回归

代价函数

  • 上面我们了解了ML中对线性回归的表示方法,现在我们将来了解模型是如何输出hh中的系数。首先我们通过一个示例来了解代价函数的概念。

    线性回归示例

    对于上图,蓝色点是训练数据,绿线是得到的hθ(x)h_\theta(x)函数,显然这条直线肉眼看上去就不够好,我们需要让拟合直线和实际数据之前的距离更小即穿过更多的点,为了用具体的指标来衡量这个距离,我们将引入代价函数

    J(θ)=12mi=1m(hθ(x(i))y(i))2 J(\theta) = \frac{1}{2m}\sum_{i=1}^{m}{(h_\theta(x^{(i)}) - y^{(i)})^2}

    这里首先需要解释这个函数中的参数含义,其中mm代表训练样本的数量,(x(i),y(i))(x^{(i)}, y^{(i)})代表第ii个样本的真实数据,那么该函数也就是计算每个预测值-真实值(也称为建模误差)平方和的均值,显然值越小,样本到直线的距离和越小。

    代价函数三维图

    上图是对应不同的直线参数得到的代价函数三维图,其中颜色越深的地方代表代价函数值越小,也就是距离和最小,那么选择最小值对应的θi\theta_i参数将可能获得更好的拟合效果。

    那么如何找到这个最小值点呢,大部分情况下,我们的数据要比这复杂得多,会有更高维、更多参数的情况,此时我们很难画出图可视化的找出最小值点,这就要用到一些自动寻找最小值点的算法了。

    提示

    绘制图像我使用的代码如下

    import numpy as np 
    import matplotlib.pyplot as plt 
    from mpl_toolkits.mplot3d import Axes3D 
    
    theta0_values = np.linspace(-10, 10, 20)  
    theta1_values = np.linspace(-10, 10, 20)  
    
    theta0, theta1 = np.meshgrid(theta0_values, theta1_values)
    
    x = np.array([1, 2, 3])
    y = np.array([1, 2, 3])
    
    def compute_cost(x, y, theta0, theta1):
        predictions = theta0 + theta1 * x
        cost = np.mean((predictions - y) ** 2) / 2  
        return cost
    
    cost_values = np.zeros((theta0.shape[0], theta1.shape[0]))  
    
    for i in range(theta0.shape[0]):  
        for j in range(theta1.shape[0]):  
            cost_values[i, j] = compute_cost(x, y, theta0[i, j], theta1[i, j])  
    
    fig = plt.figure()  
    ax = fig.add_subplot(111, projection='3d')  
    ax.plot_surface(theta0, theta1, cost_values, rstride=1, cstride=1, cmap='viridis', edgecolor='none') 
    ax.set_xlabel('theta0')  
    ax.set_ylabel('theta1')  
    ax.set_zlabel('Cost')  
    plt.show()
    

最小二乘法

  • 要得到上述代价函数的最小值,我们可以使用最小二乘法,对于最小二乘法来说也有两种做法进行求解。
  1. 代数法

    J(θ)J(\theta)分别对θ0θ1\theta_0、\theta_1求导,并求解驻点。则有

    {i=1mθ0+θ1x(i)y(i)=0i=1mθ0+θ1x(i)y(i)x(i)=0 \begin{cases} \sum_{i=1}^m{\theta_0+\theta_1x^{(i)} - y^{(i)}} = 0\\ \sum_{i=1}^m{\theta_0+\theta_1x^{(i)} - y^{(i)}}x^{(i)} = 0 \end{cases}

    显然变成一个二元一次方程组,很容易得到解

    θ0=i=1m(x(i))2i=1my(i)i=1mx(i)i=1mx(i)y(i)mi=1m(x(i))2(i=1mx(i))2θ1=mi=1mx(i)y(i)i=1mx(i)i=1my(i)mi=1m(x(i))2(i=1mx(i))2 \begin{aligned} \theta_0 &= \frac{\sum_{i=1}^m(x^{(i)})^2\sum_{i=1}^m{y^{(i)}}-\sum_{i=1}^mx^{(i)}\sum_{i=1}^mx^{(i)}y^{(i)}}{m\sum_{i=1}^m(x^{(i)})^2-(\sum_{i=1}^mx^{(i)})^2}\\\\ \theta_1 &= \frac{m\sum_{i=1}^mx^{(i)}y^{(i)}-\sum_{i=1}^mx^{(i)}\sum_{i=1}^my^{(i)}}{m\sum_{i=1}^m(x^{(i)})^2-(\sum_{i=1}^mx^{(i)})^2}\\ \end{aligned}

  2. 矩阵法

    很容易我们可以发现

    h(θ)=(1,x)(θ0θ1)=Xθ h(\theta) = \begin{pmatrix} 1,x \end{pmatrix} \cdot \begin{pmatrix} \theta_0\\\theta_1 \end{pmatrix} = \mathbf{X}\theta

    则代价函数J(θ)=12(XθY)T(XθY)J(\theta) = \frac{1}{2}(\mathbf{X}\theta - \mathbf{Y})^T(\mathbf{X}\theta - \mathbf{Y})

    同样对θ\theta求导求解零点,有

    θJ(θ)=θ(XθY)XθY=XT(XθY)=0 \begin{aligned} \frac{\partial}{\partial \theta}J(\theta) &= \frac{\partial}{\partial \theta}(\mathbf{X}\theta - \mathbf{Y}) \cdot \mathbf{X}\theta - \mathbf{Y}\\ &=\mathbf{X}^T(\mathbf{X}\theta - \mathbf{Y}) = 0 \end{aligned}

    θ=(XTX)1XTY\theta = (\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{Y}

    直接便可以得到参数θ\theta的取值。

其他线性回归

多元线性回归

  • 当输出值yiy_i会被多个参数所影响时则需要进行多元线性回归,此时我们将得到的拟合直线hh

    h(θ)=θ0+(θ1x1++θnxn) h(\theta) = \theta_0 + (\theta_1\cdot x_1 + \dots + \theta_n\cdot x_n)

    一般我们将xx用向量表示,并且为了处理多出的θn\theta_n,我们给X\mathbf{X}在增加一列常数11,即X=(1,x1,x2,,xn)\mathbf{X} = (1, x_1, x_2, \dots, x_n)

    这样hh可以改写为h(θ)=Xθh(\theta) = \mathbf{X}\theta,我们要做的工作同样是找到MSE的最小值构建拟合直线。

    但是使用一元线性回归中的最小二乘法并不是总是管用,首先是计算过程中的逆矩阵可能不存在,其次时对于nn非常大时,求逆非常耗时,所以此时我们可以选用其他方法求解。

梯度下降

  • 这是一种用来求多元函数最小值的算法,对于一次函数,我们可以通过求导的方式获取某个点出的变化率,从而向着增长率减少的方向迭代得到极值。而梯度也就是多元函数的方向导数取最大值的方向。即gradf=(fx1,fx2,)(x10,x20,)\mathrm{grad}f = (\frac{\partial f}{\partial x_1}, \frac{\partial f}{\partial x_2}, \dots)|_{(x_{1_0}, x_{2_0}, \dots)}。显然梯度下降也只能得到一个局部最小值(极小值),但是不一定是全局最小值,所以开始参数的选取也是很重要的。

    在代价函数中使用梯度下降算法过程如下

    1. 初始化θi\theta_i

    2. J(θ)J(\theta)θ\theta的偏导θiJ(θ)\frac{\partial}{\partial \theta_i}J(\theta)

    3. 更新θi\theta_i

      θi=θiαθiJ(θ) \theta_i = \theta_i - \alpha \frac{\partial}{\partial \theta_i}J(\theta)

    其中α\alpha是学习率,显然增大它能够加速迭代的速率,但如果它过大也有可能会导致无法收敛到最小值。注意上述步骤通常称为批量梯度下降,因为其每一步迭代都需要对所有训练样本求和,有时候还会用到一些非批量的梯度下降。

    这里还有一个问题,上述我们介绍了梯度下降是局部最优解,为何还能用于代价函数中呢,因为这里的代价函数是一个凸函数,所以通过梯度下降求出的解必然是全局最优解。

    相关信息

    这里所使用的代价函数叫做均方误差Mean Squared Error,除此之外还有均方根误差RMSE平均绝对误差MAE决定系数R2等。

岭(Ridge)回归

  • 岭回归是一种常见的线性回归扩展形式,通过引入L2正则化项来解决线性回归模型中可能存在的过拟合问题,同时它还能解决在使用矩阵求解方法时出现不可逆矩阵的问题。

  • 正则化:模型过于复杂时会导致将所有的训练样本都拟合到了产生过拟合问题。此时可以减少模型复杂度或者增加训练集个数,而正则化正是一种减少模型复杂度的方式,其以最小化损失和复杂度为目标。

    正则化将在代价函数中添加一个与模型复杂度相关的惩罚项来实现,惩罚项的作用有以下几点:

    1. 减少模型复杂度:减少参数的绝对值或者平方和,提高泛化能力。
    2. 特征选择:在训练过程中让一些参数权重接近0,也就是模型将选择更加重要的特征,能够减少维度。
    3. 防止权重过大:参数的权重过大会导致模型对噪声过于敏感,正则化能让模型更加平滑。
  • 在岭回归中,代价函数为

    J(θ)=12m(i=1m(hθ(x(i))y(i))2+λj=1mθj2) J(\theta) = \frac{1}{2m}\left(\sum_{i=1}^m(h_\theta(x^{(i)}) - y^{(i)})^2 + \lambda\sum_{j=1}^m\theta_j^2\right)

    这里的λj=1mθj2\lambda\sum_{j=1}^m\theta_j^2便是L2正则项,其中λ\lambda为正则化参数,如果λ\lambda设置过大可能会影响拟合能力,但太小又会达不到控制过拟合的目的,所以这一块也是可以做工作量的。

    同时引入L2正则项后,矩阵的解变成了

    θ=(XTX+λI)1XTY \theta = (\mathbf{X}^T\mathbf{X} + \lambda\mathbf{I})^{-1}\mathbf{X}^T\mathbf{Y}

    λI\lambda\mathbf{I}的加入使得矩阵一定可逆。

  • 还有使用L1正则项的Lasso回归,这里就不写了。

欠拟合

  • 有时候数据由于测量误差、噪音或者说根本不存在完美的线性拟合函数时会产生欠拟合的情况,这里列举了一些常见的欠拟合常见以及解决方案。

局部线性回归

  • 有时数据具有一定局部性,此时可以重视局部点的影响,常见解决方式是给代价函数加上点权值来处理。例如权值可以是局部点距离点集中心的距离等。

    常见权重函数wi=exp((xix)22k2)w_i = \exp(-\frac{(x_i-x)^2}{2k^2}),其中kk是自定义参数。

离群点

图片来源机器学习常识 10: 线性回归
图片来源机器学习常识 10: 线性回归open in new window
  • 其中黄色是所有点的拟合直线,显然可以看出中间的一些点是不太正常的,处理的时候我们可以把它们删除掉重新拟合得到红色直线。

    同样的方法也适用于例如高杠杆点、强影响点等异常值情况,本质就是把这些点看做坏数据给丢掉。