计算梯度:网络的前向与反向传播 和 优化方法:更新模型参数的方法
Posted repinkply
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计算梯度:网络的前向与反向传播 和 优化方法:更新模型参数的方法相关的知识,希望对你有一定的参考价值。
我们一同学习了损失函数的概念以及一些常用的损失函数。你还记得我们当时说的么:模型有了损失函数,才能够进行学习。那么问题来了,模型是如何通过损失函数进行学习的呢?
在接下来,我们将会学习前馈网络、导数与链式法则、反向传播、优化方法等内容,掌握了这些内容,我们就可以将模型学习的过程串起来作为一个整体,彻底搞清楚怎样通过损失函数训练模型。
下面我们先来看看最简单的前馈网络。
前馈网络
前馈网络,也称为前馈神经网络。顾名思义,是一种“往前走”的神经网络。它是最简单的神经网络,其典型特征是一个单向的多层结构。简化的结构如下图:
结合上面的示意图,我带你具体看看前馈网络的结构。这个图中,你会看到最左侧的绿色的一个个神经元,它们相当于第 0 层,一般适用于接收输入数据的层,所以我们把它们叫做输入层。
比如我们要训练一个 y=f(x) 函数的神经网络,x 作为一个向量,就需要通过这个绿色的输入层进入模型。那么在这个网络中,输入层有 5 个神经元,这意味着它可以接收一个 5 维长度的向量。
结合图解,我们继续往下看,网络的中间有一层红色的神经元,它们相当于模型的“内部”,一般来说对外不可见,或者使用者并不关心的非结果部分,我们称之为隐藏层。在实际的网络模型中,隐藏层会有非常多的层数,它们是网络最为关键的内部核心,也是模型能够学习知识的关键部分。
在图的右侧,蓝色的神经元是网络的最后一层。模型内部计算完成之后,就需要通过这一层输出到外部,所以也叫做输出层。
需要说明的是,神经元之间的连线,表示神经元之间连接的权重,通过权重就会知道网络中每个节点的重要程度。
那么现在我们回头再来看看前馈神经网络这个名字,是不是就很好理解了。在前馈网络中,数据从输入层进入到隐藏层的第一层,然后传播到第二层,第三层……一直到最后通过输出层输出。数据的传播是单向的,无法后退,只能前行。
反向传播
反向传播算法(Backpropagation)是目前训练神经网络最常用且最有效的算法。模型就是通过反向传播的方式来不断更新自身的参数,从而实现了“学习”知识的过程。
反向传播的主要原理是:
前向传播:数据从输入层经过隐藏层最后输出,其过程和之前讲过的前馈网络基本一致。计算误差并传播:计算模型输出结果和真实结果之间的误差,并将这种误差通过某种方式反向传播,即从输出层向隐藏层传递并最后到达输入层。
迭代:在反向传播的过程中,根据误差不断地调整模型的参数值,并不断地迭代前面两个步骤,直到达到模型结束训练的条件。
其中最重要的环节有两个:一是通过某种方式反向传播;二是根据误差不断地调整模型的参数值。
这两个环节,我们统称为优化方法,一般而言,多采用梯度下降的方法。这里就要使用到导数、梯度和链式法则相关的知识点,梯度下降我们将在下节课详细展开。
反向传播的数学推导以及证明过程是非常复杂的,在实际的研发过程中反向传播的过程已经被 PyTorch、TensorFlow 等深度学习框架进行了完善的封装,所以我们不需要手动去写这个过程。不过作为深度学习的研发人员,你还是需要深入了解这个过程的运转方式,这样才能搞清楚深度学习中模型具体是如何学习的。
上面我们共同了解了前馈网络、导数、梯度、反向传播等概念。但是距离真正完全了解神经网络的学习过程,我们还差一个重要的环节,那就是优化方法。只有搞懂了优化方法,才能做到真的明白反向传播的具体过程。
我们就来学习一下优化方法,为了让你建立更深入的理解,后面我还特意为你准备了一个例子,把这三节课的所有内容串联起来。
用下山路线规划理解优化方法
深度学习,其实包括了三个最重要的核心过程:模型表示、方法评估、优化方法。我们上节课学习的内容,都是为了优化方法做铺垫。
优化方法,指的是一个过程,这个过程的目的就是,寻找模型在所有可能性中达到评估效果指标最好的那一个。我们举个例子,对于函数 f(x),它包含了一组参数。
这个例子中,优化方法的目的就是找到能够使得 f(x) 的值达到最小值对应的权重。换句话说,优化过程就是找到一个状态,这个状态能够让模型的损失函数最小,而这个状态就是模型的权重。
常见的优化方法种类非常多,常见的有梯度下降法、牛顿法、拟牛顿法等,涉及的数学知识也更是不可胜数。同样的,PyTorch 也将优化方法进行了封装,我们在实际开发中直接使用即可,节省了大量的时间和劳动。
不过,为了更好地理解深度学习特别是反向传播的过程,我们还是有必要对一些重要的优化方法进行了解。我们这节课要学习的梯度下降法,也是深度学习中使用最为广泛的优化方法。
梯度下降其实很好理解,我给你举一个生活化的例子。假期你跟朋友去爬山,到了山顶之后忽然想上厕所,需要尽快到达半山腰的卫生间,这时候你就需要规划路线,该怎么规划呢?
在不考虑生命危险的情况下,那自然是怎么快怎么走了,能跳崖我们绝不走平路,也就是说:越陡峭的地方,就越有可能快速到达目的地。
所以,我们就有了一个送命方案:每走几步,就改变方向,这个方向就是朝着当前最陡峭的方向,即坡度下降最快的方向行走,并不断重复这个过程。这就是梯度下降的最直观的表示了。
在上节课中我们曾说过:梯度向量的方向即为函数值增长最快的方向,梯度的反方向则是函数减小最快的方向。
梯度下降,就是梯度在深度学习中最重要的用途了。下面我们用相对严谨的方式来表述梯度下降。
在一个多维空间中,对于任何一个曲面,我们都能够找到一个跟它相切的超平面。这个超平面上会有无数个方向(想想这是为什么?),但是这所有的方向中,肯定有一个方向是能够使函数下降最快的方向,这个方向就是梯度的反方向。每次优化的目标就是沿着这个最快下降的方向进行,就叫做梯度下降。
具体来说,在一个三维空间曲线中,任何一点我们都能找到一个与之相切的平面(更高维则是超平面),这个平面上就会有无穷多个方向,但是只有一个使曲线函数下降最快的梯度。再次叨叨一遍:每次优化就沿着梯度的反方向进行,就叫做梯度下降。使什么函数下降最快呢?答案就是损失函数。
这下你应该将几个知识点串联起来了吧:为了得到最小的损失函数,我们要用梯度下降的方法使其达到最小值。这两节课的最终目的,就是让你牢牢记住这句话。
我们继续回到刚才的例子。
图中红色的线路,是一个看上去还不错的上厕所的路线。但是我们发现,还有别的路线可选。不过,下山就算是不要命地跑,也得讲究方法。
损失函数和深度学习
【中文标题】损失函数和深度学习【英文标题】:Loss function and deep learning 【发布时间】:2018-12-27 20:34:02 【问题描述】:来自 deeplearning.ai:
构建神经网络的一般方法是:
定义神经网络结构(输入单元数、隐藏单元数等)。 初始化模型的参数 循环: 实现前向传播 计算损失 实施反向传播以获得梯度 更新参数(梯度下降)
损失函数如何影响网络的学习方式?
例如,这是我认为是正确的前向和反向传播的实现,因为我可以使用以下代码训练模型以获得可接受的结果:
for i in range(number_iterations):
# forward propagation
Z1 = np.dot(weight_layer_1, xtrain) + bias_1
a_1 = sigmoid(Z1)
Z2 = np.dot(weight_layer_2, a_1) + bias_2
a_2 = sigmoid(Z2)
mse_cost = np.sum(cost_all_examples)
cost_cross_entropy = -(1.0/len(X_train) * (np.dot(np.log(a_2), Y_train.T) + np.dot(np.log(1-a_2), (1-Y_train).T)))
# Back propagation and gradient descent
d_Z2 = np.multiply((a_2 - xtrain), d_sigmoid(a_2))
d_weight_2 = np.dot(d_Z2, a_1.T)
d_bias_2 = np.asarray(list(map(lambda x : [sum(x)] , d_Z2)))
# perform a parameter update in the negative gradient direction to decrease the loss
weight_layer_2 = weight_layer_2 + np.multiply(- learning_rate , d_weight_2)
bias_2 = bias_2 + np.multiply(- learning_rate , d_bias_2)
d_a_1 = np.dot(weight_layer_2.T, d_Z2)
d_Z1 = np.multiply(d_a_1, d_sigmoid(a_1))
d_weight_1 = np.dot(d_Z1, xtrain.T)
d_bias_1 = np.asarray(list(map(lambda x : [sum(x)] , d_Z1)))
weight_layer_1 = weight_layer_1 + np.multiply(- learning_rate , d_weight_1)
bias_1 = bias_1 + np.multiply(- learning_rate , d_bias_1)
注意以下几行:
mse_cost = np.sum(cost_all_examples)
cost_cross_entropy = -(1.0/len(X_train) * (np.dot(np.log(a_2), Y_train.T) + np.dot(np.log(1-a_2), (1-Y_train).T)))
我可以使用 mse 损失或交叉熵损失来了解系统的学习情况。但这仅供参考,成本函数的选择不会影响网络的学习方式。我相信我没有像深度学习文献中所说的那样理解基本的东西,损失函数的选择是深度学习的重要一步?但如我上面的代码所示,我可以选择交叉熵或 mse 损失,并且不会影响网络的学习方式,交叉熵或 mse 损失仅用于信息目的?
更新:
例如,这里是一个来自 deeplearning.ai 的计算成本的 sn-p 代码:
# GRADED FUNCTION: compute_cost
def compute_cost(A2, Y, parameters):
"""
Computes the cross-entropy cost given in equation (13)
Arguments:
A2 -- The sigmoid output of the second activation, of shape (1, number of examples)
Y -- "true" labels vector of shape (1, number of examples)
parameters -- python dictionary containing your parameters W1, b1, W2 and b2
Returns:
cost -- cross-entropy cost given equation (13)
"""
m = Y.shape[1] # number of example
# Retrieve W1 and W2 from parameters
### START CODE HERE ### (≈ 2 lines of code)
W1 = parameters['W1']
W2 = parameters['W2']
### END CODE HERE ###
# Compute the cross-entropy cost
### START CODE HERE ### (≈ 2 lines of code)
logprobs = np.multiply(np.log(A2), Y) + np.multiply((1 - Y), np.log(1 - A2))
cost = - np.sum(logprobs) / m
### END CODE HERE ###
cost = np.squeeze(cost) # makes sure cost is the dimension we expect.
# E.g., turns [[17]] into 17
assert(isinstance(cost, float))
return cost
此代码按预期运行并实现高精度/低成本。除了向机器学习工程师提供有关网络学习情况的信息外,此实现中不使用成本值。这让我质疑成本函数的选择如何影响神经网络的学习方式?
【问题讨论】:
我投票结束这个问题,因为这个问题是关于人工神经网络理论的。但简短的回答。损失函数是影响网络如何以及是否学习的非常重要的因素。我真的很喜欢这个教程。 neuralnetworksanddeeplearning.com @Framester 我也喜欢这个教程并认为答案在neuralnetworksanddeeplearning.com/chap3.html 部分,也许我的误解是如果改变成本函数那么激活函数也必须改变?在我上面的示例中,虽然可以更改损失函数值,但它不会产生影响,因为也不会更改激活函数。损失函数的梯度是否等于sigmoid函数的梯度? 您是否了解损失函数的一般机制:它如何影响参数更新?我读到您的问题是询问损失函数的选择,而不是 any 损失函数的效果。 @Prune 我了解成本函数衡量网络的训练效果。但我不明白它如何影响参数更新。正如我最初的问题一样,如果我使用 MSE 成本而不是交叉熵,它对网络的学习方式没有影响。也许成本函数的选择会影响激活函数的选择?换句话说,如果我改变成本函数,我上面提到的网络的另一部分是否也需要改变,以纳入成本函数的变化。我也更新了问题。谢谢。 感谢您的澄清;我看到它给你的答案比我给出的更完整。 【参考方案1】:嗯,这只是一个粗略的高级尝试,以回答可能对 SO 来说是题外话的问题(原则上我理解您的困惑)。
除了向机器学习工程师提供有关网络学习效果的信息外,此实现中不使用成本值。
这实际上是正确的;仔细阅读 Andrew Ng 为您发布的 compute_cost
函数的 Jupyter 笔记本,您会看到:
5 - 成本函数
现在您将实现前向和后向传播。您需要计算成本,因为您想检查您的模型是否真的在学习。
从字面上看,这是在代码中显式计算成本函数的实际值的唯一原因。
但这仅供参考,成本函数的选择不会影响网络的学习方式。
没那么快!这是(通常是不可见的)捕获:
成本函数的选择决定了用于计算 dw
和 db
数量的精确方程,因此是学习过程。
注意这里我说的是函数本身,而不是它的值。
换句话说,像你的计算
d_weight_2 = np.dot(d_Z2, a_1.T)
和
d_weight_1 = np.dot(d_Z1, xtrain.T)
没有从天上掉下来,但它们是应用到特定成本函数的反向传播数学的结果。
以下是 Andrew 在 Coursera 的入门课程中的一些相关高级幻灯片:
希望这会有所帮助;从成本函数的导数开始,我们如何精确地得出dw
和db
的特定计算形式的细节超出了本文的范围,但您可以在网上找到一些关于反向传播的好教程(here 是一个)。
最后,对于当我们选择错误的成本函数(用于多类分类的二元交叉熵,而不是正确的分类交叉熵)时可能发生的情况的(非常)高级描述,您可以查看在Keras binary_crossentropy vs categorical_crossentropy performance?我的回答中。
【讨论】:
所以在这种情况下,您为“特定成本函数”指定的方程是交叉熵成本函数及其导数? @blue-sky 确实以上是关于计算梯度:网络的前向与反向传播 和 优化方法:更新模型参数的方法的主要内容,如果未能解决你的问题,请参考以下文章