梯度下降三种方法的python代码实现

梯度下降的三种方法

梯度下降的三种方法有:
1.批量梯度下降(Batch Gradient Descent)
2.随机梯度下降(Stochastic Gradient Descent)
3.小批量梯度下降(Mini-batch Gradient Descent)

我们要利用代码来实现的话,首先定义一个可以保存图像的函数,代码如下

#导包
import numpy as np
import os
import matplotlib.pyplot as plt
%matplotlib inline

#随机种子
np.random.seed(42)

#保存图像设置
PROJECT_ROOT_DIR = "."
MODEL_ID = "linear_models"

#保存图像函数
def save_fig(fig_id, tight_layout = True):   #定义一个保存图像的函数
    path = os.path.join(PROJECT_ROOT_DIR, "images", MODEL_ID, fig_id +".png")#指定保存图像的路径 当前目录下的images文件夹下的model_id文件夹
    print("Saving figure", fig_id)  #提示函数,正在保存图片
    plt.savefig(path, format='png', dpi = 300)  #保存图片(需要指定保存途径,保存格式,清晰度)
 
#如果说你不想有某些警告类的信息,可以用述方法进行过滤
#把讨厌的警告信息过滤掉
import warnings
warnings.filterwarnings(action="ignore", message="^internal gelsd")

我们来试验一下函数的效果:

X = 2 * np.random.rand(100,1)  #生成训练集(特征部分)
y = 4 + X * 3 +np.random.randn(100,1)  #生成训练数据(标签部分)
plt.plot(X, y, "r.") #画图
plt.xlabel("$x_1$", fontsize = 18)  #定义X轴
plt.ylabel("$y$", rotation = 0, fontsize = 18)  #定义Y轴
plt.axis([0,2,0,15])
save_fig("generated_data_plot")   #保存图片
plt.show()

结果如下:

梯度下降法python中权重迭代公式代码 梯度下降 python_数据


接下来对上述给定数据集测试一下:

#添加新特征
X_b = np.c_[np.ones((100,1)), X]
#创建测试数据
X_new = np.array([[0],[2]])
X_new_b = np.c_[np.ones((2,1)), X_new]


from sklearn.linear_model import LinearRegression #从sklearn包里导入线性回归模型
lin_reg = LinearRegression()  #创建线性回归对象
lin_reg.fit(X, y)  #拟合训练数据
lin_reg.intercept_, lin_reg.coef_  #输出截距,斜率

结果为:

(array([4.21509616]), array([[2.77011339]]))

对测试集进行预测一下:

# 这里还是引用上面导入的新型回归模型
lin_reg.predict(X_new)  #对测试集进行预测

结果为:

array([[4.21509616],
       [9.75532293]])
用批量梯度下降求解线性回归

我是利用迭代次数进行梯度下降,从而得出结果。代码如下:

eta = 0.1  #给定步长
n_iterations = 1000  #设置迭代次数
m = 100   #数据集长度
theta = np.random.randn(2,1)   #theta的数据

for iteration in range(n_iterations):  #对迭代次数进行循环
    gradients = 1/m * X_b.T.dot(X_b.dot(theta) - y)  #损失函数求偏导
    theta = theta - eta * gradients  #更新theta值
theta_path_bgd= []  #设置一个空列表接收theta变化值
#定义一个函数会出图像
def plot_gradient_descent(theta,eta,theta_path = None):
    m = len(X_b)  #数据集的长度
    plt.plot(X, y, "b.")   #画出原数据的图(蓝色点状)
    n_iterations = 1000  #设置迭代次数
    for iteration in range(n_iterations):  #对设定迭代次数进行循环
        if iteration < 10:   #当迭代次数小于10的时候
            y_predict = X_new_b.dot(theta)  #根据X得出Y的值
            style = "r-"    #设置绘图的形状(红色线型)
            plt.plot(X_new, y_predict, style)  #画出图像
        gradients = 2/m * X_b.T.dot(X_b.dot(theta) - y)  #对损失函数求偏导
        theta = theta - eta * gradients   #对theta值的更新
        if theta_path is not None:    #对theta_path进行判断
            theta_path.append(theta)   #如果不是空就把每一次进行迭代更新的theta值加入到上述给定的空列表中
        plt.xlabel("$x_1$",fontsize = 18)   #设定X轴和字体大小
        plt.ylabel("$y$", rotation=0, fontsize=18) #设定Y轴和字体大小以及Y轴标题的角度
        plt.axis([0,2,0,15])
        plt.title(r"$\eta = {}$".format(eta), fontsize = 16)  #设定标题
np.random.seed(42)   #引用种子
theta = np.random.randn(2,1)   #设定theta值

plt.figure(figsize=(10,4))  #设定画布大小
plt.subplot(131);plot_gradient_descent(theta, eta=0.02)  #画出0.02步长的图像
plt.subplot(132);plot_gradient_descent(theta,eta=0.1,theta_path=theta_path_bgd)  #画出0.1步长的图像,并且给定了theta_path
plt.subplot(133);plot_gradient_descent(theta,eta=0.5)  #画出0.5的图像
save_fig("gradient_descent_plot")  #保存图片
plt.show()    #画出图像

画出的图像为:

梯度下降法python中权重迭代公式代码 梯度下降 python_梯度下降_02


并且上述中,0.05的步长中给定了theta_path,查看空列表的迭代theta值为:

梯度下降法python中权重迭代公式代码 梯度下降 python_迭代_03

随机梯度下降算法
theta_path_sgd = [] #设定一个空列表接收theta变化的值
m = len(X_b)   #数据集的长度
np.random.seed(42)

n_epochs = 50   #设定epoch的次数
theta = np.random.rand(2,1)   #给定theta值

for epoch in range(n_epochs):   #进行循环
    for i in range(m):   #遍历m中每一个元素
        if epoch == 0 and i <20:   
            y_predict = X_new_b.dot(theta)   #求出Y值
            style = "r-"    #设定线形(红色线形)
            plt.plot(X_new, y_predict, style)  #画出图像
        random_index = np.random.randint(m)   #在0-m中随机一个数用作下标
        xi = X_b[random_index:random_index+1]  #用切片根据下标拿出X_b的数据
        yi = y[random_index:random_index+1]    #用切片根据下标拿出y的数据
        gradients = 2 * xi.T.dot(xi.dot(theta) - yi)   #损失函数求偏导
        eta = 0.1    #步长
        theta = theta - eta * gradients     #theta值的更新
        theta_path_sgd.append(theta)    #将theta的更新值添加到空列表中

plt.plot(X, y, "b.")    #根据X,y画出图像
plt.xlabel("$x_1$",fontsize = 18)   #设定X轴
plt.ylabel("$y$", rotation = 0, fontsize = 18)  #设定Y轴
plt.axis([0,2,0,15])
plt.show()

结果为:

梯度下降法python中权重迭代公式代码 梯度下降 python_迭代_04


输出theta值为:

array([[4.13077121],
       [3.16436719]])

对SGD直接调用函数进行求解:

from sklearn.linear_model import SGDRegressor
sgd_reg = SGDRegressor(max_iter=50, tol=-np.infty, penalty=None, eta0=0.1, random_state=42)
sgd_reg.fit(X, y.ravel())

结果为:

SGDRegressor(alpha=0.0001, average=False, early_stopping=False, epsilon=0.1,
             eta0=0.1, fit_intercept=True, l1_ratio=0.15,
             learning_rate='invscaling', loss='squared_loss', max_iter=50,
             n_iter_no_change=5, penalty=None, power_t=0.25, random_state=42,
             shuffle=True, tol=-inf, validation_fraction=0.1, verbose=0,
             warm_start=False)

直接调用函数求解截距和斜率为:

sgd_reg.intercept_,sgd_reg.coef_

结果为:

(array([4.16782089]), array([2.72603052]))
小批量梯度下降算法
theta_path_mgd = [] #设定一个空列表接收theta变化值

n_iterations = 50  #迭代次数
minibatch_size = 20   #对数据集遍历时的步长

np.random.seed(42)
theta = np.random.randn(2,1)  #设定theta值

for epoch in range(n_iterations):  
    shuffled_indices = np.random.permutation(m) #洗牌
    X_b_shuffled = X_b[shuffled_indices]
    y_shuffled = y[shuffled_indices]
    for i in range(0,m,minibatch_size):
        xi = X_b_shuffled[i:i+minibatch_size] #洗牌后的X_b根据下标切片拿出数据
        yi = y_shuffled[i:i+minibatch_size]  ##洗牌后的y根据下标切片拿出数据
        gradients = 2/minibatch_size * xi.T.dot(xi.dot(theta) - yi) #对损失函数求偏导,直接用minibatch_size作为除数
        eta = 0.1  #给定步长
        theta = theta - eta * gradients  #更新theta值
        theta_path_mgd.append(theta)
对三种方法的对比分析

三种方法合在一起来看:

theta_path_bgd = np.array(theta_path_bgd)
theta_path_sgd = np.array(theta_path_sgd)
theta_path_mgd = np.array(theta_path_mgd)
plt.figure(figsize=(7,4))
plt.plot(theta_path_sgd[:,0], theta_path_sgd[:,1], "r-o", linewidth=1, label="Stochastic")
plt.plot(theta_path_mgd[:,0], theta_path_mgd[:,1], "g-s", linewidth=2, label="Mini-batch")
plt.plot(theta_path_bgd[:,0], theta_path_bgd[:,1], "b-+", linewidth=3, label="Batch")
plt.legend(loc="upper left", fontsize=16)
plt.xlabel(r"$\theta_0$", fontsize=20)
plt.ylabel(r"$\theta_1$", fontsize=20, rotation = 0)
plt.axis([2.5, 4.5, 2.3, 4.5])
save_fig("generated_Three_plot")
plt.show()

结果展示为:

梯度下降法python中权重迭代公式代码 梯度下降 python_迭代_05


三种方法分开来对比:

plt.figure(figsize=(7,4))
plt.subplot(131)
plt.plot(theta_path_sgd[:,0], theta_path_sgd[:,1], "r-o", linewidth=1, label="Stochastic")
plt.axis([2.5, 4.5, 2.3, 4.5])
plt.xlabel(r"$\theta_0$", fontsize=14)
plt.ylabel(r"$\theta_1$", fontsize=14, rotation = 0)
plt.subplot(132)
plt.plot(theta_path_mgd[:,0], theta_path_mgd[:,1], "g-s", linewidth=2, label="Mini-batch")
plt.axis([2.5, 4.5, 2.3, 4.5])
plt.xlabel(r"$\theta_0$", fontsize=14)
plt.subplot(133)
plt.plot(theta_path_bgd[:,0], theta_path_bgd[:,1], "b-+", linewidth=3, label="Batch")
plt.xlabel(r"$\theta_0$", fontsize=14)
plt.axis([2.5, 4.5, 2.3, 4.5])
save_fig("generated_itemsize_plot")
plt.show()

结果为:

梯度下降法python中权重迭代公式代码 梯度下降 python_数据_06