线性回归梯度下降法python_线性回归中的梯度下降

0x00 前言

在上一篇文章《手动实现梯度下降(可视化)》的多元线性回归中,使用梯度下降法求损失函数的最小值。在多元线性回归中,我们已经使用“标准方程解”求了损失函数最小值,那么如何使用梯度下降来计算最小值呢?在本篇文章中,对多元线性回归的损失函数进行求导,并使用向量化的方式编写代码。最后证明了,在梯度下降之前需要使用归一化。

0x01 理论推导

1.1 目标

在多元线性回归中,其预测值为: 

已知训练数据样本、 ,找到,使得损失函数尽可能小.

下面就要使用梯度下降法求多元线性回归中损失函数的最小值。

1.2 整理损失函数

使用梯度下降法来求损失函数最小值时,要对损失函数进行一定的设计。

下面我们要进行一下化简。

将预测值  代入到损失函数中,可以得到:

其中  是列向量列向量,而且我们注意到,可以虚构第0个特征,另其恒等于1,推导时结构更整齐,也更加方便:

这样我们就可以改写成向量点乘的形式:

4b6f4d146c090e83da98d16324f8b09d.png

将带入损失函数中,可以将其改写为:

对进行求导,可以得到:

d921372e4b901905282a8263201c8ec6.png

在上式中梯度大小实际上是和样本数量m相关,m越大,累加之和越大,这是不合理的;为了使与m无关,除以m。

于是演变成:使损失函数尽可能小。同时注意到,损失函数变成了均方误差MSE:。

对其求导,有:

1206193af1acfbe9a0a4264e3a97a329.png

我们将这个复杂的表达式再一次进行分解,可以写成向量  再去点乘矩阵:

b135b3c54c5ad08273207cf1d8e9c47d.png

即:将求梯度的过程转换为两个矩阵之间的乘法运算:1*m的向量 乘以 m*(n+1)的矩阵,得到1*(n+1)的向量。其结果是 。

然后这里还需要一个转秩。为什么转秩?本来应该是一个列向量,但是这里需要转秩成行向量。因为得到的是1*(n+1)的行向量,梯度是(n+1)*1的列向量,因此需要再次进行转秩(分别转秩,再交换位置)。

1.3 最终得到的梯度

最终得到梯度结果为: 

0x02 代码演示

2.1 梯度下降代码

下面就可以整合梯度下降的代码,将上面推导出来的梯度结果的表达式带入梯度下降的流程中。

def fit_gd(self, X_train, y_train, eta=0.01, n_iters=1e4):    """根据训练数据集X_train, y_train, 使用梯度下降法训练Linear Regression模型"""    assert X_train.shape[0] == y_train.shape[0], \        "the size of X_train must be equal to the size of y_train"    def J(theta, X_b, y):        try:            return np.sum((y - X_b.dot(theta)) ** 2) / len(y)        except:            return float('inf')            def dJ(theta, X_b, y):        return X_b.T.dot(X_b.dot(theta) - y) * 2. / len(y)    def gradient_descent(X_b, y, initial_theta, eta, n_iters=1e4, epsilon=1e-8):        theta = initial_theta        cur_iter = 0        while cur_iter < n_iters:            gradient = dJ(theta, X_b, y)            last_theta = theta            theta = theta - eta * gradient            if (abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon):                break            cur_iter += 1        return theta    X_b = np.hstack([np.ones((len(X_train), 1)), X_train])    initial_theta = np.zeros(X_b.shape[1])    self._theta = gradient_descent(X_b, y_train, initial_theta, eta, n_iters)    self.intercept_ = self._theta[0]    self.coef_ = self._theta[1:]    return self

2.2 使用真实数据出现的问题

下面准备波士顿房产数据,准备进行验证

import numpy as npfrom sklearn import datasetsboston = datasets.load_boston()X = boston.datay = boston.targetX = X[y < 50.0]y = y[y < 50.0]

首先使用线性回归中的“标准方程解”进行计算,得到相应系数与截距的最优值,然后计算回归评分(R方):

from myAlgorithm.LinearRegression import LinearRegressionfrom myAlgorithm.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split(X, y, seed=666)lin_reg1 = LinearRegression()%time lin_reg1.fit_normal(X_train, y_train)lin_reg1.score(X_test, y_test)"""输出:CPU times: user 25.7 ms, sys: 25.5 ms, total: 51.2 msWall time: 116 ms0.8129802602658466"""

然后创建一个lin_reg2,使用梯度下降法得到最优参数:

lin_reg2 = LinearRegression()lin_reg2.fit_gd(X_train, y_train, )lin_reg2.coef_"""输出:array([nan,nan,nan,nan,nan,nan,nan,nan,nan,nan,nan,nan,nan])"""

发现在执行的过程中有一个RuntimeWarning的警告overflow。并且得到的全都是空。

这是因为,在一个真实的数据中,查看前3行数据,就可以观察到,每一个特征所对应的规模是不一样的,有一些很小,有一些很大,使用默认的学习率可能过大,导致不收敛。

42d8ef23c8053f5b67ba333ddb7a5779.png

下面我们传递一个很小的学习率来看一下结果:

lin_reg2.fit_gd(X_train, y_train, eta=0.000001)lin_reg2.score(X_test, y_test)"""输出:0.27556634853389195"""

我们发现得到的R方评分很差,这可能是因为学习率设置的过小,步长太小没有到最优值。为了验证这个假设,可以再进行一次验证:

lin_reg2.fit_gd(X_train, y_train, eta=0.000001, n_iters=1e6)lin_reg2.score(X_test, y_test)"""输出:0.75418523539807636"""

我们发现,即使计算了这么久,其结果还是没有“标准方程解”的评分高。

对于这种情况应该怎么办呢?之前已经分析出来了,之所以出现这种现象,是因为真实数据整体不在一个规模上,解决的方式,就是在梯度下降之前进行数据归一化

0bd652e977a9a5165043abe13ee87e84.png

2.3 数据归一化

from sklearn.preprocessing import StandardScalerstandardScaler = StandardScaler()standardScaler.fit(X_train)X_train_std = standardScaler.transform(X_train)lin_reg3 = LinearRegression()lin_reg3.fit_gd(X_train_std, y_train)X_test_std = standardScaler.transform(X_test)lin_reg2.score(X_test, y_test)"""输出:0.8129802602658466"""

这和“正规方程解”得到的score一样,这就说明我们找到了损失函数的最小值。

0xFF 总结

在本篇文章中,对多元线性回归的损失函数进行求导,其结果是 。

然后使用向量化的方式编写代码,但是发现在真实数据中效果比较差,这是因为数据的规模不一样,因此在梯度下降之前需要使用归一化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值