1.概述
【说明】
梯度下降算法(Gradient Descent Optimization)是神经网络模型训练最常用的优化算法(n纬问题求最优解,梯度下降是最常用的方法);对于深度学习模型,基本都是采用梯度下降算法来进行优化训练。
【场景假设】
一人被困山上,需从山上下来(i.e. 找到山的最低点,也就是山谷)。但此时山上的浓雾很大,导致可视度很低。因此,下山的路径就无法确定,他必须利用自己周围的信息去找到下山的路径。这个时候,他就可以利用梯度下降算法来帮助自己下山。具体来说就是,以他当前的所处的位置为基准,寻找这个位置最陡峭的地方,然后朝着山的高度下降的地方走,同理,如果我们的目标是上山,也就是爬到山顶,那么此时应该是朝着最陡峭的方向往上走。然后每走一段距离,都反复采用同一个方法,最后就能成功的抵达山谷。
【梯度下降的基本过程】
首先,有一个可微分的函数(山),目标是找到函数的最小值(山底),依据之前的场景假设,最快的下山的方式是沿着当前位置最陡峭的方向,然后沿着此最陡峭的方向往下走,对应到可谓分的函数中,即找到给定点的梯度(是目标函数上升最快的方向),然后朝着梯度相反的方向,就能让函数值下降的最快。
反复使用该方法,反复求取梯度,最后就能达到局部的最小值。
2.梯度
(J(Θ) = 0.55-(5θ1+2θ2-12θ3))梯度是通过<>包裹起来的一组偏导的向量。梯度(多元微分:偏导数)是微分中一个十分重要的概念,意义如下:
①在单变量函数中,梯度即函数的微分,代表着函数在某个给定点的切线的斜率
②在多变量函数中,梯度即向量,向量有方向,梯度的方向就是函数在给定点上升最快的方向
因此,若想找到函数的最小值,则需要千方百计的求取梯度,梯度的方向时函数上升最快的方向,梯度的反方向就是函数在给定点下降最快的方向。
3.梯度下降算法的数学解释
J(Θ0):关于Θ0的函数,即目标函数
Θ0:current position当前所在位置
Θ1:next position 变换后的位置(下降一次)
α:学习率或者步长(太大太小均不好:太大,易错过最低点;太小,效率太低)
▽J(Θ0):即参数Θ-的梯度
负号:朝梯度相反的方向前进
4.梯度下降算法实例
4.1 单变量函数的梯度下降
目标函数:
Θ0: 1
α:0.4
import numpy as np
import matplotlib.pyplot as plt
def gradient_decent(x, a):
return x-a*2*x
if "__main__" == __name__:
lx = []
x = 1.0
for i in range(5):
lx.append(x)
y = gradient_decent(x, 0.4)
x = y
plt.figure(111, figsize=(9, 9))
plt.title("one-dimension Gradient Decent", fontsize=15)
# np.arange: 返回给定区间内的等间距值
x1 = np.arange(-1.2, 1.2, step=0.001)
y1 = np.power(x1, 2)
x2 = np.sqrt(lx)
plt.plot(x1, y1, label="target-func", color="black")
plt.plot(x2, lx, label="gradient", color="red", marker="o")
plt.xlabel("x", fontsize=15)
plt.ylabel("y", fontsize=15)
plt.legend()
plt.show()
4.2 多变量函数的梯度下降
目标函数:
Θ0:(1, 3)
α:0.1
5.梯度下降算法的实现
【场景】线性回归
【损失函数】
①m是数据集中点的个数
②½是一个常量,这样是为了在求梯度的时候,二次方乘下来就和这里的½抵消了,自然就没有多余的常数系数,方便后续的计算,同时对结果不会有影响
③y 是数据集中每个点的真实y坐标的值
【原函数】
【梯度】
5.1 梯度下降主体算法
def gradient_descent(X, y, alpha):
"""
梯度下降迭代计算
:param X: 函数系数
:param y: 真实的函数值
:param alpha: 学习速率
:return: 最低点坐标
"""
# 梯度下降 ==> 初始坐标
theta = np.array([1, 1]).reshape(2, 1)
# 当前坐标对应的 ==> 梯度
gradient = gradient_function(theta, X, y)
# np.absolute:逐个元素的计算绝对值
# np.all:测试给定条件下是否全部元素为True
while not np.all(np.absolute(gradient) <= 1e-5):
# 计算下一个坐标
theta = theta - alpha*gradient
gradient = gradient_function(theta, X, y)
return theta
5.2 计算当前坐标对应的梯度值
def gradient_function(theta, X, y):
"""
计算当前坐标theta对应的梯度
:param theta: 当前位置 (2, 1)
:param X: 输入变量 [1, 变量值] (m, 2)
:param y: 真实值 (m, 1)
:return: 梯度值
"""
# diff.shape: (m, 1)
# 计算预测值与真实值之间的误差
# 为了对这个公式进行矩阵化,我们可以给每一个点x增加一维,这一维的值固定为1,这一维将会乘到Θ0上。这样就方便我们统一矩阵化的计算
diff = np.dot(X, theta) - y
return (1./m)*np.dot(np.transpose(X), diff)
5.3 依据最低点坐标反推出损失值
def error_function(min_gradient, X, y):
"""
根据最小梯度反推出对应的 ”损失值“
:param min_gradient: minimum gradient
:param X: 输入变量 [1, num] (m, 2)
:param y: 真实值 (m, 1)
:return: 最低点对应的损失值
"""
# np.dot:计算两个数组的点积(矩阵乘法)
# X: (m, 2)
# min_gradient: (2, 1)
# diff: (m, 1)
diff = np.dot(X, min_gradient) - y
# 计算最低点坐标对应的误差平方和
# np.transpose: 将数组的行列互换
return (1./2*m)*np.dot(np.transpose(diff), diff)
5.4 程序调用入口
if "__main__" == __name__:
# ====定义数据集
# 数据量
m = 20
# inputX
x0 = np.ones((m, 1))
x1 = np.arange(1, m + 1).reshape(m, 1)
# hp.hstack: 按顺序堆栈数组
# fuction: 405.98496249324046
X = np.hstack((x0, x1))
y = np.array([3, 4, 5, 5, 2, 4, 7, 8, 11, 8, 12, 11, 13, 13, 16, 17, 18, 17, 19, 21]).reshape(m, 1)
# 学习速率/步长
alpha = 0.01
# 获得最低点
optimal = gradient_descent(X, y, alpha)
print("optimal:", optimal)
# 根据最低点反推损失值
print("error function:", error_function(optimal, X, y)[0, 0])