敞开生长之旅!这是我参加「日新计划 2 月更文挑战」的第 2 天,点击检查活动概况。

一,前向传达与反向传达

1.1,神经网络练习进程

神经网络练习进程是:

  1. 先经过随机参数“猜“一个成果(模型前向传达进程),这儿称为猜测成果 aa
  2. 然后核算 aa 与样本标签值 yy 的距离(即丢失函数的核算进程);
  3. 随后经过反向传达算法更新神经元参数,运用新的参数再试一次,这一次就不是“猜”了,而是有依据地向正确的方向挨近,究竟参数的调整是有战略的(依据梯度下降战略)。

以上进程如此重复多次,一向到猜测成果和实在成果之间相差无几,亦即 ∣a−y∣→0|a-y|\rightarrow 0,则练习结束。

1.2,前向传达

前向传达(forward propagation 或 forward pass)指的是: 按顺序(从输入层到输出层)核算和存储神经网络中每层的成果。

为了更深入了解前向传达的核算进程,咱们可以依据网络结构制作网络的前向传达核算图。下图是简单网络与对应的核算图示例:

反向传播与梯度下降详解

其间正方形表明变量,圆圈表明操作符。数据流的方向是从左到右顺次核算。

1.3,反向传达

反向传达(backward propagation,简称 BP)指的是核算神经网络参数梯度的办法。其原理是依据微积分中的链式规则,按相反的顺序从输出层到输入层遍历网络,顺次核算每个中心变量和参数的梯度。

梯度的主动核算(主动微分)大大简化了深度学习算法的完成。

留意,反向传达算法会重复利用前向传达中存储的中心值,以防止重复核算,因而,需求保留前向传达的中心成果,这也会导致模型练习比单纯的猜测需求更多的内存(显存)。同时这些中心成果占用内存(显存)大小与网络层的数量和批量(batch_size)大小成正比,因而运用大 batch_size 练习更深层次的网络更简单导致内存不足(out of memory)的错误!

1.4,总结

  • 前向传达在神经网络界说的核算图中按顺序核算和存储中心变量,它的顺序是从输入层到输出层。
  • 反向传达按相反的顺序(从输出层到输入层)核算和存储神经网络的中心变量和参数的梯度。
  • 在练习神经网络时,在初始化模型参数后,咱们交替运用前向传达和反向传达,依据反向传达核算得到的梯度,结合随机梯度下降优化算法(或许 Adam 等其他优化算法)来更新模型参数。
  • 深度学习模型练习比猜测需求更多的内存。

二,梯度下降

2.1,深度学习中的优化

大多数深度学习算法都触及某种形式的优化。优化器的意图是更新网络权重参数,使得咱们平滑地抵达丢失面中丢失值的最小点

深度学习优化存在许多挑战。其间一些最令人烦恼的是部分最小值、鞍点和梯度消失。

  • 部分最小值(local minimum): 对于任何方针函数 f(x)f(x),假如在 xx 处对应的 f(x)f(x) 值小于在 xx 附近任何其他点的 f(x)f(x) 值,那么 f(x)f(x) 可能是部分最小值。假如 f(x)f(x)xx 处的值是整个域上方针函数的最小值,那么 f(x)f(x) 是大局最小值。
  • 鞍点(saddle point): 指函数的一切梯度都消失但既不是大局最小值也不是部分最小值的任何方位。
  • 梯度消失(vanishing gradient): 因为某些原因导致方针函数 ff 的梯度挨近零(即梯度消失问题),是在引入 ReLU 激活函数和 ResNet 之前练习深度学习模型相当棘手的原因之一。

在深度学习中,大多数方针函数都很复杂,没有解析解,因而,咱们需运用数值优化算法,本文中的优化算法: SGDAdam 都属于此类别。

2.2,如何了解梯度下降法

梯度下降(gradient descent, GD)算法是神经网络模型练习中最为常见的优化器。虽然梯度下降(gradient descent)很少直接用于深度学习,但了解它是了解随机梯度下降和小批量随机梯度下降算法的根底。

大多数文章都是以“一个人被困在山上,需求敏捷下到谷底”来举例了解梯度下降法,但这并不完全准确。在自然界中,梯度下降的最好比如,便是泉水下山的进程:

  1. 水受重力影响,会在当时方位,沿着最峻峭的方向活动,有时会构成瀑布(梯度的反方向为函数值下降最快的方向);
  2. 水流下山的途径不是仅有的,在同一个地址,有可能有多个方位具有同样的峻峭程度,而造成了分流(可以得到多个解);
  3. 遇到坑洼地区,有可能构成湖泊,而停止下山进程(不能得到大局最优解,而是部分最优解)。

示例参阅 AI-EDU: 梯度下降。

2.3,梯度下降原理

梯度下降的数学公式:

n+1=n−⋅∇J()(1)\theta_{n+1} = \theta_{n} – \eta \cdot \nabla J(\theta) \tag{1}

其间:

  • n+1\theta_{n+1}:下一个值(神经网络中参数更新后的值);
  • n\theta_n:当时值(当时参数值);
  • −-:减号,梯度的反向(梯度的反方向为函数值下降最快的方向);
  • \eta:学习率或步长,控制每一步走的距离,不要太快以免错过了最佳景点,不要太慢以免时刻太长(需求手动调整的超参数);
  • ∇\nabla:梯度,函数当时方位的最快上升点(梯度向量指向上坡,负梯度向量指向下坡);
  • J()J(\theta):函数(等候优化的方针函数)。

下图展示了梯度下降法的进程。梯度下降的意图便是使得 xx 值向极值点迫临。

反向传播与梯度下降详解

下面我经过一个简单的双变量凸函数 J(x,y)=x2+2y2J(x, y) = x^2 + 2y^2 为例,来描绘梯度下降的优化进程。

经过梯度下降法寻觅函数的最小值,首要得核算其函数梯度:

∂J(x,y)∂x=2x∂J(x,y)∂y=4y{\partial{J(x,y)} \over \partial{x}} = 2x \\ {\partial{J(x,y)} \over \partial{y}} = 4y

设初始点为 (x0,y0)=(−3,−3)(x_0, y_0) = (-3, -3),学习率 =0.1\eta = 0.1,依据梯度下降公式(1),可得参数迭代进程的核算公式:

(xn+1,yn+1)=(xn,yn)−⋅∇J(x,y)=(xn,yn)−⋅(2x,4y)(2)\begin{aligned} (x_{n+1}, y_{n+1}) &= (x_n, y_n) – \eta \cdot \nabla J(x, y) \\ &= (x_n, y_n) – \eta \cdot (2x, 4y) \tag{2} \end{aligned}

这儿手动核算下下一个迭代点的值:

(x1,y1)=(−3,−3)−0.1∗(2∗−3,4∗−3)=(−3+0.6,−3+1.2)=(−2.4,−1.8)\begin{aligned} (x_1, y_1) &= (-3, -3) – 0.1*(2*-3, 4*-3) \\ &= (-3 + 0.6, -3 + 1.2) \\ &= (-2.4, -1.8) \\ \end{aligned}

依据上述公式(2),假定停止条件为 J(x,y)<0.005J(x,y) < 0. 005,迭代进程如下表1所示。

表1 双变量函数梯度下降的迭代进程

迭代次数 xx yy J(x,y)J(x,y)
1 -3 -3 27
2 -2.4 y=-1.8 12.24
16 -0.084442 -0.000846 0.007132
17 -0.067554 y=-0.000508 0.004564

迭代 1717 次后,J(x,y)J(x,y) 的值为 0.0045640.004564,满意小于 0.0050.005 的条件,停止迭代。

由于是双变量,所以梯度下降的迭代进程需求用三维图来解释。表2可视化了三维空间内的梯度下降进程。

调查视点1 调查视点2
反向传播与梯度下降详解
反向传播与梯度下降详解

图中心那条隐约的黑色线,表明梯度下降的进程,从红色的高地一向沿着斜度向下走,直到蓝色的洼地。

双变量凸函数 J(x,y)=x2+2y2J(x, y) = x^2 + 2y^2 的梯度下降优化进程以及可视化代码如下所示:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def target_function(x,y):
    J = pow(x, 2) + 2*pow(y, 2)
    return J
def derivative_function(theta):
    x = theta[0]
    y = theta[1]
    return np.array([2*x, 4*y])
def show_3d_surface(x, y, z):
    fig = plt.figure()
    ax = Axes3D(fig)
    u = np.linspace(-3, 3, 100)
    v = np.linspace(-3, 3, 100)
    X, Y = np.meshgrid(u, v)
    R = np.zeros((len(u), len(v)))
    for i in range(len(u)):
        for j in range(len(v)):
            R[i, j] = pow(X[i, j], 2)+ 4*pow(Y[i, j], 2)
    ax.plot_surface(X, Y, R, cmap='rainbow')
    plt.plot(x, y, z, c='black', linewidth=1.5,  marker='o', linestyle='solid')
    plt.show()
if __name__ == '__main__':
    theta = np.array([-3, -3]) # 输入为双变量
    eta = 0.1 # 学习率
    error = 5e-3 # 迭代停止条件,方针函数值 < error
    X = []
    Y = []
    Z = []
    for i in range(50):
        print(theta)
        x = theta[0]
        y = theta[1]
        z = target_function(x,y)
        X.append(x)
        Y.append(y)
        Z.append(z)
        print("%d: x=%f, y=%f, z=%f" % (i,x,y,z))
        d_theta = derivative_function(theta)
        print("    ", d_theta)
        theta = theta - eta * d_theta
        if z < error:
            break
    show_3d_surface(X,Y,Z)

留意!总结下,不同的步长 \eta ,随着迭代次数的增加,会导致被优化函数 JJ 的值有不同的改变:

反向传播与梯度下降详解

图片来源如何了解梯度下降法?。

三,随机梯度下降与小批量随机梯度下降

3.1,随机梯度下降

在深度学习中,方针函数通常是练习数据会集每个样本的丢失函数的平均值。假如运用梯度下降法,则每个自变量迭代的核算价值为 O(n)O(n),它随 nn(样本数目)线性增⻓。因而,当练习数据集较大时,每次迭代的梯度下降核算价值将较高。

随机梯度下降(SGD)可降低每次迭代时的核算价值。在随机梯度下降的每次迭代中,咱们对数据样本随机均匀采样一个索引 ii,其间 i∈1,…,ni \in {1, . . . , n},并核算梯度 ∇J()\nabla J(\theta) 以更新权重参数 \theta:

n+1=n−⋅∇Ji()(3)\theta_{n+1} = \theta_{n} – \eta \cdot \nabla J_i(\theta) \tag{3}

每次迭代的核算价值从梯度下降的 O(n)O(n) 降至常数 O(1)O(1)。别的,值得着重的是,随机梯度 ∇Ji()\nabla J_i(\theta) 是对完好梯度 ∇J()\nabla J(\theta) 的无偏估量。

无偏估量是用样本统计量来估量总体参数时的一种无偏揣度。

在实践应用中,随机梯度下降 SGD 法有必要和动态学习率办法结合起来运用,不然运用固定学习率 + SGD的组合会使得模型收敛进程变得更复杂。学习率的调整战略可参阅我之前写的文章-深度学习炼丹-超参数设定和模型练习。

3.2,小批量随机梯度下降

前面讲的梯度下降(GD)和随机梯度下降(SGD)办法都过于极端,要么运用完好数据集来核算梯度并更新参数,要么一次只处理一个练习样本来更新参数。在实践项目中,会对两者取折中,即小批量随机梯度下降(minibatch gradient descent),运用小批量随机梯度下降还可以提高核算效率。

小批量的一切样本数据元素都是从练习会集随机抽出的,样本数目个数为 batch_size(缩写 bs)

n+1=n−⋅∇1bs∑ibsJi(x,)(3)\theta_{n+1} = \theta_{n} – \eta \cdot \nabla \frac{1}{bs}\sum_{i}^{bs}J_i(\textrm{x},\theta) \tag{3}

别的,一般项目中运用 SGD 优化算法都默许会运用小批量随机梯度下降,即 batch_size > 1,除非显卡显存不够了,才会设置 batch_size = 1

参阅资料

  1. 如何了解梯度下降法?
  2. AI-EDU: 梯度下降
  3. 《着手学习深度学习11章-优化算法》