随机梯度下降原理及在线性回归算法中的应用python代码实现

1 介绍   

     梯度下降主要分为三种:批量梯度下降(又名最速下降),随机梯度下降 和 小批量梯度下降。
设:M为每次求梯度所使用到的样本数量,N为所有样本数,则
当:M等于N时,就是batch GD 批量梯度下降
当:M等于1时,就是SGD 随机梯度下降
当:M大于1,小于N时,就是mini-batch GD 小批量梯度下降        

        笔者刚开始接触随机梯度下降的时候,就有如下三个问题,困扰了笔者很久。

        问题1:随机梯度下降为什么随机选取一个样本反复多次迭代,就能实现全量样本多次迭代批量梯度下降的效果,每次迭代的梯度方向都是随机的,不仅不是最速方向,甚至是反方向,为什么多次迭代后就能找到局部最优点?

        问题2:随机梯度下降的迭代结束条件是什么,实际实现中,随机梯度下降迭代盲猜迭代N次,感觉行了就停,完全是一个非闭环的东西,随机迭代需要多少次迭代才能达到程序停止迭代的条件?

        问题3:为什么随机梯度下降计算速度更快?

2 分析        

        在梯度下降求梯度时,梯度方程只需一个样本和一组theta值便能求出当前theta值下的梯度值,如果每个样本完全符合模型关系,代入任意一个样本,将会得到完全相同的梯度值。

        问题就在于每个样本不会完全符合模型关系,具有不同程度的随机误差,故得到的梯度值也不会相同,为了得到全量样本较为精准的梯度值,故批量梯度下降就代入了所有样本求出梯度值再平均,使用了所有训练数据的误差,保证了各个样本之间随机误差对结果的影响程度最小。

        而在随机梯度下降法中会随机选择一个训练数据,并使用它来更新参数。如果每次仅代入一个样本,多次迭代不同样本,每个样本的随机误差也会被抵消掉,最终找到最优解。 

        因为随机梯度下降根据随机样本所求得的梯度值相对于全量样本来说,有可能不是最快下降方向,甚至可能是上升方向,故下降方向具有随机性,(因为每个样本包含了随机误差,所以每次迭代梯度具有随机性,但整体样本随机误差会相互抵消,多次迭代后,梯度方向最终还是会指引到极值点)更新参数结束条件不能像批量梯度下降那样,通过将更新前与更新后的参数代入迭代函数中比较两次损失函数的绝对值是否满足预设精度来判断是否结束迭代。

                        abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon

        因为每次迭代样本所包含的随机误差可能导致前后两次迭代计算出的梯度不是全样本最速方向,将这两次求得的theta值代入损失函数做比较得到的差值是不稳定的。无法通过判断批量梯度下降法的结束迭代方式如法炮制, 随机梯度下降一般会通过预设的迭代次数来结束迭代过程,同时还会随着迭代次数的增多,降低学习率的值,模拟退火的过程。(此处有点像是PID算法控制加热棒烧水一样,刚开始离目标温度较低,猛火烧水,慢慢接近目标温度后,小火烧水)。

3 代码实现

 首先构造梯度函数和退火函数(梯度函数的推导过程可查看笔者另一篇博文:

梯度下降法在线性回归中的应用及python代码实现_南山十一少的博客-CSDN博客

其次对X_train输入空间样本进行预处理,增加全为1的列向量,为了方便后续与theta进行矩阵点乘。

然后对theta赋初值,以输入空间的列向量个数一致进行随机赋值。

最后就可以进行迭代计算更新theta值了。

具体代码如下:

from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
import numpy as np
from sklearn.linear_model import SGDRegressor

def sgd(X_b, y, n=100, t0=5, t1=500):
    def dJ_sgd(theta, X_b_i, y_i):
        return X_b_i * (X_b_i.dot(theta) - y_i) * 2.

    def learning_rate(t):
        return t0 / (t + t1)

    X_b = np.hstack([np.ones((len(X_b), 1)), X_b])
    initial_theta = np.random.randn(X_b.shape[1])
    theta = initial_theta
    m = len(X_b)
    for i_iter in range(n):
        indexes = np.random.permutation(m)
        X_b_new = X_b[indexes, :]
        y_new = y[indexes]
        gradient = dJ_sgd(theta, X_b_new[i_iter], y_new[i_iter])
        theta = theta - learning_rate(i_iter) * gradient

    return theta

通过Sklearn自带的批量梯度下降、Sklearn自带的随机梯度下降 和 上诉写的随机梯度下降对一组线性关系的样本求解线性回归,实验证明,随机梯度下降即使用到一半的样本数据,也能较好的训练出参数值theta,与批量梯度下降得出的结果相差无几。但值得注意的是,批量下降每迭代一次就要进行所有样本的计算,计算量为样本数量乘以迭代次数,而随机梯度下降每迭代一次仅仅使用了一个样本计算了一次,最后计算量为迭代次数,所以随机梯度下降大大降低了计算量。三种方式的线性回归代码及对比结果如下所示:

Sklearn批量梯度下降 求解线性回归

Batch Gradient Descent Score = 0.6305101921596077
Batch Gradient Descent: y = [3.92018275] * x + 2.870032112246455 

Sklearn随机梯度下降 求解线性回归

Sklearn Stochastic Gradient Descent Score = 0.630465437994248
Sklearn Stochastic Gradient Descent: y = [3.91683263] * x + [2.87724967] 

自编随机梯度下降 求解线性回归

Stochastic Gradient Descent Score = 0.6058746652580193
Stochastic Gradient Descent: y = [4.38881505] * x + 3.2794452449036284 

if __name__ == "__main__":
    np.random.seed(666)    
    m = 1000
    x = np.random.normal(size=m)
    X = x.reshape(-1, 1)
    y = 4. * x + 3. + np.random.normal(0, 3, size=m)
    X_train, X_test, y_train, y_test = train_test_split(X, y)

    # Sklearn批量梯度下降 求解线性回归
    lin_reg = LinearRegression()
    lin_reg.fit(X_train, y_train)
    score = lin_reg.score(X_test, y_test)
    print("Batch Gradient Descent Score = {}".format(score))
    print("Batch Gradient Descent: y = {} * x + {} ".format(lin_reg.coef_, lin_reg.intercept_))

    # Sklearn随机梯度下降 求解线性回归
    sgd_reg = SGDRegressor(n_iter_no_change=5)
    sgd_reg.fit(X_train, y_train)
    score = sgd_reg.score(X_test, y_test)
    print("Sklearn Stochastic Gradient Descent Score = {}".format(score))
    print("Sklearn Stochastic Gradient Descent: y = {} * x + {} ".format(sgd_reg.coef_, sgd_reg.intercept_))

    # 自编随机梯度下降 求解线性回归
    theta = sgd(X_train, y_train, n=300)
    lin_reg.intercept_ = theta[0]
    lin_reg.coef_ = theta[1:]
    score = lin_reg.score(X_test, y_test)
    print("Stochastic Gradient Descent Score = {}".format(score))
    print("Stochastic Gradient Descent: y = {} * x + {} ".format(lin_reg.coef_, lin_reg.intercept_))

4. 结论

        本文讲解了随机梯度下降的原理,并通过自编随机梯度下降代码,并应用到线性回归求解中,让读者深入了解随机梯度的原理。最后与Sklearn自带的批量梯度下降和随机梯度下降进行对比,随机梯度下降尽可能的保证了计算精度的同时,能够有效的减少计算量。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是使用Python代码对梯度下降算法实现线性回归的示例: 首先,我们需要导入所需的包: ```python import numpy as np import matplotlib.pyplot as plt ``` 然后,我们定义一个函数来计算误差,即损失函数: ```python def compute_cost(X, y, theta): m = len(y) predictions = X.dot(theta) square_err = (predictions - y) ** 2 J = 1 / (2 * m) * np.sum(square_err) return J ``` 其,X是一个m行n列的特征矩阵,y是一个m行1列的目标向量,theta是一个n行1列的参数向量,m是样本数量,n是特征数量。 接下来,我们定义一个函数来执行梯度下降算法: ```python def gradient_descent(X, y, theta, alpha, num_iters): m = len(y) J_history = np.zeros((num_iters, 1)) for i in range(num_iters): predictions = X.dot(theta) errors = np.subtract(predictions, y) delta = (alpha / m) * X.transpose().dot(errors) theta = theta - delta J_history[i] = compute_cost(X, y, theta) return theta, J_history ``` 其,alpha是学习率,num_iters是迭代次数,J_history记录了每次迭代后的损失函数值。 最后,我们可以使用上述函数来拟合一个简单的线性模型: ```python # 生成随机数据 np.random.seed(0) X = 2 * np.random.rand(100, 1) y = 4 + 3 * X + np.random.randn(100, 1) # 对特征矩阵X添加一列全为1的向量,以便于计算截距 X_b = np.c_[np.ones((100, 1)), X] # 初始化参数向量theta theta = np.random.randn(2, 1) # 执行梯度下降算法 alpha = 0.1 num_iters = 1000 theta, J_history = gradient_descent(X_b, y, theta, alpha, num_iters) # 绘制拟合直线 plt.scatter(X, y) plt.plot(X, X_b.dot(theta), 'r') plt.show() ``` 这里我们生成了一个简单的一维数据集,然后对其进行线性回归拟合并绘制出拟合直线。 完整代码如下:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值