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

样本 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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容