美文网首页
机器学习 - 线性回归梯度下降推导

机器学习 - 线性回归梯度下降推导

作者: 属七降九 | 来源:发表于2019-01-16 21:23 被阅读0次

    样本 x 有 m 个属性
    多元线性回归

    • 线性回归模型函数
      \begin{align} y & = w_0x_0 + w_1x_1 + \dots + w_mx_m \\ & = \sum_{i = 0}^mw_ix_i \\ & = \mathbf{w}^T\mathbf{x} \end{align}
    • 模型参数
      \mathbf{w}^T = \begin{bmatrix} w_0 & \dots & w_m \end{bmatrix}_{1 \times(m+1)}
    • 属性(特征值)列表
      \mathbf{x} = \begin{bmatrix} x_0 \\ \vdots \\ x_m \end{bmatrix}_{(m+1) \times 1}
      其中:
      x_0 = 1是线性回归的截距的权重,恒定为1
    • 代价函数
      \mathbf{J}(\mathbf{w}) = \frac{1}{2}\sum_{i = 1}^{n}(y^{(i)} - \hat{y}^{(i)})^2
      其中:
      \mathbf{J}(\mathbf{w})表示\mathbf{J}是关于参数\mathbf{w}的函数
      n 表示一共有n个样本数据
      y^{(i)} 为第i个样本对应的真实值
      \hat{y}^{(i)} 为第i个样本通过预测函数 \hat{y} = \mathbf{w}^T\mathbf{x}计算得到的预测值
      \frac{1}{2}是为了推导方便而添加的常量

    梯度下降法

    • 求当前\mathbf{w}的梯度需要求得\mathbf{w}w_j(j\in[0, m])的偏导数
      \begin{align} \frac{\partial{\mathbf{J(\mathbf{w})}}}{\partial{w_j}} & = \frac{1}{2}\sum_{i = 1}^{n}\frac{\partial}{\partial{w_j}}[(y^{(i)} - \hat{y}^{(i)})^2] \\ & = \sum_{i = 1}^{n}(y^{(i)} - \hat{y}^{(i)})\frac{\partial}{\partial{w_j}}(y^{(i)} - \hat{y}^{(i)}) \\ & = \sum_{i = 1}^{n}(y^{(i)} - \hat{y}^{(i)})(-x_j^{(i)}) \\ & = -\sum_{i = 0}^{n}x_j^{(i)}(y^{(i)} - \hat{y}^{(i)}) \end{align}
      其中:
      x_j^{(i)} 表示第i个样本的第j个特征值
    • 由于我们的目的是让代价函数\mathbf{J}(\mathbf{w})取得极小值,于是需要让参数\mathbf{w}往负梯度的方向更新,从而让代价函数\mathbf{J}(\mathbf{w})取值变小
    • 参数每次更新需要一个正数步长\eta(可以为0.001、0.0001)来确定每次参数更新的速度
    • 参数的负增量:\begin{align} \Delta\mathbf{w}_j & = -\eta\frac{\partial\mathbf{J}(\mathbf{w})}{\partial{w_j}} \\ & = \eta\sum_{i = 0}^{n}x_j^{(i)}(y^{(i)} - \hat{y}^{(i)}) \\ \end{align}

    梯度下降法过程:
    1.初始化权重\mathbf{w}
    2.使用代价函数计算代价
    3.计算梯度,使用\mathbf{w} +=\Delta\mathbf{w}更新权重\mathbf{w}
    4.若代价值小于某一阈值(或训练某个次数)则停止训练
    5.得到权重\mathbf{w}

    以下是使用Python实现线性回归的类

    import numpy as np
    class LinearRegressionLM(object):
        # 初始化(
        # eta -> 初始化学习率
        # n_iter -> 训练次数 
        def __init__(self, eta=0.001, n_iter=20):
            self.eta = eta
            self.n_iter = n_iter
        # 训练
        # X -> 训练的所有样本(n+1)xm
        # y -> 训练样本的真实值(n+1)x1
        def fit(self, X, y):
            # 初始化权重w_
            self.w_ = np.zeros(1 + X.shape[1])
            # 初始化代价
            self.cost_ = []
            
            for i in range(self.n_iter):
                # 得到预测值
                output = self.net_input(X)
                # 得到残差值
                errors = (y - output)
                # 更新第 0 个权重(这个比较特殊)
                self.w_[0] += self.eta * errors.sum()
                # 更新第 1...m个权重
                self.w_[1:] += self.eta * X.T.dot(errors)
                # 计算代价值
                cost = (errors**2).sum() / 2.0
                # 存储代价值
                self.cost_.append(cost)
            return self
        # 使用现有的权重w_得到预测值
        def net_input(self, X):
            return np.dot(X, self.w_[1:]) + self.w_[0]
        # 预测
        def predict(self, X):
            return self.net_input(X)
    

    注解:

    # 更新第 0 个权重(这个比较特殊)
    self.w_[0] += self.eta * errors.sum()
    

    推导过程:
    \begin{align} \Delta\mathbf{w}_0 & = \eta\sum_{i = 0}^nx_0^{(i)}(y^{(i)} - \hat{y}^{(i)}) \\ & = \eta\sum_{i = 0}^n(y^{(i)} - \hat{y}^{(i)}) \\ \end{align}

    关于这行代码

    # 更新第 1...m个权重
    self.w_[1:] += self.eta * X.T.dot(errors)
    

    为什么是X的转置点乘残差值?以下是推导过程

    由参数的负增量:\begin{align} \Delta\mathbf{w}_j & = -\eta\frac{\partial\mathbf{J}(\mathbf{w})}{\partial{w_j}} \\ & = \eta\sum_{i = 0}^{n}x_j^{(i)}(y^{(i)} - \hat{y}^{(i)}) \\ \end{align}

    注意:代码中\Delta\mathbf{w}不包括\Delta\mathbf{w}_0\Delta\mathbf{w}_0另外计算
    \Delta\mathbf{w} = \begin{bmatrix} \Delta\mathbf{w}_1 \\ \vdots \\ \Delta\mathbf{w}_m \\ \end{bmatrix} = \eta \begin{bmatrix} \sum_{i = 0}^{n}x_1^{(i)}(y^{(i)} - \hat{y}^{(i)}) \\ \vdots \\ \sum_{i = 0}^{n}x_m^{(i)}(y^{(i)} - \hat{y}^{(i)}) \\ \end{bmatrix}
    = \eta \begin{bmatrix} \sum_{i = 0}^{n}x_1^{(i)}(y^{(i)} - \hat{y}^{(i)}) \\ \vdots \\ \sum_{i = 0}^{n}x_m^{(i)}(y^{(i)} - \hat{y}^{(i)}) \\ \end{bmatrix}
    =\eta \begin{bmatrix} x_1^{(0)}(y^{(0)} - \hat{y}^{(0)}) + \dots + x_1^{(n)}(y^{(n)} - \hat{y}^{(n)})\\ \vdots \\ x_m^{(0)}(y^{(0)} - \hat{y}^{(0)}) + \dots + x_m^{(n)}(y^{(n)} - \hat{y}^{(n)})\\ \end{bmatrix}_{m\times1}
    = \eta \begin{bmatrix} x_1^{(0)} \dots x_1^{(n)} \\ \vdots \\ x_m^{(0)} \dots x_m^{(n)} \\ \end{bmatrix}_{m\times(n+1)} \begin{bmatrix} (y^{(0)} - \hat{y}^{(0)}) \\ \vdots \\ (y^{(n)} - \hat{y}^{(n)}) \\ \end{bmatrix}_{(n+1)\times1}
    = \eta \begin{bmatrix} x_1^{(0)} \dots x_m^{(0)} \\ \vdots \\ x_1^{(n)} \dots x_m^{(n)} \\ \end{bmatrix}_{(n+1)\times{m}}^T \begin{bmatrix} (y^{(0)} - \hat{y}^{(0)}) \\ \vdots \\ (y^{(n)} - \hat{y}^{(n)}) \\ \end{bmatrix}_{(n+1)\times1}
    实际上就是代码中

    = self.eta * X.T.dot(errors)
    

    ******** 以下是代码实践 ********

    1.初始化随机线性回归数据(每个样本只取一个属性)

    from sklearn.datasets import make_regression
    import matplotlib.pyplot as plt
    
    X, y, eof = make_regression(n_samples=1000, n_features=1, noise=40, coef=True, random_state=14)
    plt.scatter(X, y, c='r', s=3)
    
    随机数据.png

    2.初始化线性回归的类,开始训练

    linearRegressionLM = LinearRegressionLM()
    linearRegressionLM.fit(X, y)
    print('训练参数 = ' , linearRegressionLM.w_)
    

    训练参数 = [ 0.70041376 76.66596196]

    3.绘制回归线

    x_line = np.linspace(-3, 3, 20)
    print(x_line.shape)
    print(type(x_line))
    x_line_ones = np.ones((x_line.shape[0], 1))
    # 添加每个样本的 x_0 = 1
    x_line_all = np.column_stack((x_line_ones, x_line))
    y_line = np.dot(x_line_all, linearRegressionLM.w_)
    plt.scatter(X, y, c='r', s=3)
    plt.plot(x_line, y_line, color='blue')
    
    回归线.png

    相关文章

      网友评论

          本文标题:机器学习 - 线性回归梯度下降推导

          本文链接:https://www.haomeiwen.com/subject/yqppdqtx.html