梯度下降算法(Gradient Descent)

一、定义

     梯度下降法Gradient desent) 是一个一阶最优算法,通常也称为最速下降法。要使用梯度下降法找到一个函数的局部极小值,必须向函数上当前点对应梯度(或者是近似梯度)的反方向的规定步长距离点进行迭代搜索。如果相反地梯度正方向迭代进行搜索,则会接近函数的局部极大值点;这个过长被称为梯度上升法
梯度下降算法(Gradient Descent)_第1张图片

二、利用Python代码实现梯度下降算法。

    在模拟梯度下降算法之前,我们将做好前期准备工作,也就是先准备好离散的数据,然后利用线性回归模型去拟合这些离散的数值点,代码如下:

import numpy as np
# os 模块在运维工作中是很常用的一个模块。通过os模块调用系统命令。os模块可以跨平台使用。
import os 
# 使图片可以在Jupyer notebook的行间显示
%matplotlib inline
from matplotlib import pyplot as plt
# 设置随机种子
np.random.seed(42)
# 保存图片
PROJECT_ROOT_DTR = "."		# 一个点代表当前路径,两个点代表当前路径的上级
MODEL_ID = "linear_models"
# 定义一个图片的存储函数
def save_fig(fig_id, tight_layout=True)		# tight_layout会自动调整子图参数,使之填充整个图像区域
path = os.path.join(PROJECT_ROOT_DTR, "images", MODEL_ID, fig_id + ".png")		# 设置保存图片的路径,在当前目录下的images文件夹下的linear_models文件夹中
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 + 3 * X + np.random.randn(100, 1)
# 画图
plt.plot(X, y, "b.")
# 设置x、y轴的标签和字号
plt.xlabel("$x_1$", fontsize=18)	# $…$ 为markdown语法,数学公式编辑语言
plt.ylabel("$y$", rotation=0, fontsize=18)
# 设置x、y轴的范围
plt.axis([0, 2, 0, 15])
# 调用图片存储函数保存图片
save_fig("generated_data_plot")
# 展示图片
plt.show()

梯度下降算法(Gradient Descent)_第2张图片
    画出的图片如上图所示,此时我们进一步添加新的数据创建测试数据,利用线性回归模型拟合原始数据,并计算预测值,代码如下:

# 添加新数据
X_b = np.c_[np.ones((100, 1)), X]
# 创建测试数据
X_new = np.array([[0], [2]])
X_new = np.c_[np.ones((2, 1)), X_new]
# 从sklearn包中导入线性回归模型
from sklearn.linear_model import LinearRegression
# 创建线性回归的对象
lin_reg = LinearRegression()
# 拟合训练集
lin_reg.fit(X, y)
# 输出截距(intercept)和相关系数(coefficient)
lin_reg.intercept_, lin_reg.coef_
# 根据拟合函数预测y值
lin.reg.predict(X, new)

    运行程序得出线性回归模型为: y = 2.770 x + 4.215 y = 2.770x + 4.215 y=2.770x+4.215 (保留三位小数),当 x = ( 0 , 2 ) x = (0, 2) x=(0,2)时预测的 y y y值为 ( 4.215 , 9.755 ) (4.215, 9.755) (4.215,9.755)

1. 批量梯度下降算法(Batch Gradient Descent)

alpha = 0.1		# 设置步长
n_iterations = 1000		# 设置循环次数
m = 100		# 训练数据集中数据的个数
theta = np.random.randn(2, 1)		# 随机一个初始的theta(两行一列)
# 迭代循环
for iteration in range(n_iterations):		# 指定迭代次数
	gradient = 2 / m * X_b.T.dot(X_b.dot(theta) - y)		# 下降梯度值
	theta = theta - alpha * gradient		# 每循环一次更新一次theta的值

theta_path_bgd = []
# 定义批量梯度下降法的画图函数
def plot_gradient_descent(theta, alpha, theta_path=None):
	m = len(X_b)
	plt.plot(X, y, "b.")
	n_iterations = 1000
	for iteration in range(n_iterations):
		if iteration < 10:
			y_predict = X_new_b.dot(theta)
			style = "b-"
			plt.plot(X_new, y_predict, style)
		gradients = 2 / m * X_b.T.dot(X_b.dot(theta) - y)
		theta = theta - alpha * gradient
		if theta_path is not None:
			theta.path.append(theta)
	plt.xlable("$x_1$", fontsize=18)
	plt.axis([0, 2, 0, 15])
	plt.title(r"$\alphat = {}$".format(alpha), fontsize=16)
np.random.seed(42)
theta = np.random.randn(2, 1)
plt.figure(figsize=(10, 4))		# 设置画图和画布大小
# 画布分为一行三列,三小块
plt.subplot(131)	# 第一块	
plot_gradient_descent(theta, alpha=0.02)
plt.ylable("$y$", rotation=0, fontsize=18)		# 设置y轴的标签
plt.subplot(132)	# 第二块	
plot_gradient_descent(theta, alpha=0.1, theta_path= theta_path_bgd)
plt.subplot(133)	# 第三块	
plot_gradient_descent(theta, alpha=0.5)
save_fig("gradient_descent_plot")		# 保存图片
plt.show()	

梯度下降算法(Gradient Descent)_第3张图片
    由上图可知,当使用梯度下降算法求极值时,步长太小、太大都不好。如果步长太小,迭代速度太慢,而且不能很快的接近最优解(左边的图);如果步长太大,会导致迭代速度过快,甚至有可能错过最优解(右边的图),所以不长的选择应该恰当(中间的图)。

2. 随机梯度下降算法(Stochatic Gradient Descent)

theta_path_sgd = []
m = len(X_b)
np.random.seed(42)
n_epochs = 50
theta = np.random.randn(2, 1) # 随机初始化
# 设置循环
for epoch in range(n_epochs):
	for i in range(m):
		if epoch == 0 and i < 20:
		y_predict = X_new_b.dot(theta)
		style = "b-"
		plt.plot(X_new, y_predict, style)
	xi = X_b[i : i + 1]
	yi = y[i : i + 1]
	gradients = 2 * xi.T.dot(xi.dot(theta) - yi)
	alpha = 0.1
	theta = theta - alpha * gradients
	theta_path_sgd.append(theta)
plt.plot(X, y, "b.")
plt.xlabel("$x_1$", fontsize=18)
plt.ylabel("$y$", rotation=0, fontsize=18)
plt.axis([0, 2, 0, 15])
save_fig("sgd_plot")
plt.show()
# 输出迭代结束后的thata
print(theta)
# 上述theta值与python中SGDRegressor模块计算的theta进行对比
from sklearn.linear_model import SDGRegressior
sgd_reg = SDGRegressior(max_iter=1000, tol=-np.infty, penalty=None, eta0=0.1, random_state=42)
sgd_reg.fit(X, y.ravel())
# 输出截距和相关系数
sgd_reg.intercept_, sgd_reg.coef_

梯度下降算法(Gradient Descent)_第4张图片
    程序运行结果theta=(3.763, 2.589), sgd_reg.intercept_=4.168,sgd_reg.coef_=2.726。对比可知,自己构造的函数得出来的结果和Python模块提供的函数计算的结果还是有些差异的。

3. 小批量梯度下降算法(Mini Batch Gradient Descent)

theta_path_mgd = []
n_iterations = 50
minibatch_size = 20
np.random.seed(42)
theta = np.random.randn(2, 1)
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]
		yi = y_shufffled[i : i + minibatch_size]
		gradients = 2 / minibatch_size * xi.T.dot(xi.dot(theta) - yi)
		alpha = 0.1
		theta = theta - alpha * gradients
		theta_path_mgd.appdend(tehta)
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_bgd[:, 0], theta_path_bgd[:,1], "b-o", linewidth=1, label="BGD")
plt.plot(theta_path_sgd[:, 0], theta_path_sgd[:,1], "r-s", linewidth=2, label="SGD")
plt.plot(theta_path_mgd[:, 0], theta_path_mgd[:,1], "g-+", linewidth=3, label="MGD")
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.3, 4.5, 2.3, 3.9])
sav_fig("gradient_descent_paths_plot")
plt.show()

梯度下降算法(Gradient Descent)_第5张图片
    由上图可知,三种梯度下降的方法,MGD方法相对而言能够更好的去逼近最优解(极小值),尤其是SGD在梯度下降的过程中,没有方向感求解的速度会受到影响,而BGD虽然最快但是其求得的局部最小值可能不等于全局最小值,综上所述,小批量梯度下降的方法相对而言是最好的。

你可能感兴趣的:(机器学习)