梯度下降三种方法的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()
结果如下:
接下来对上述给定数据集测试一下:
#添加新特征
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() #画出图像
画出的图像为:
并且上述中,0.05的步长中给定了theta_path,查看空列表的迭代theta值为:
随机梯度下降算法
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()
结果为:
输出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()
结果展示为:
三种方法分开来对比:
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()
结果为: