Lec6.1
[TOC]
Fully Connected Networks

好的,我们现在就开始吧。首先,让我们来谈谈全连接网络。从某种意义上来说,这实际上只是对我们在三节课之前讲过的内容的一种复述。但这里的基本思想是,既然我们已经讲过自动微分,我们在定义全连接网络时就可以更简单,也可以更灵活。我们可以添加一些在实践中常见的元素。
所以特别地,我们将定义一个 层的全连接网络,也被称为多层感知器或者 MLP,我们将定义它如下:
我们说 ,即第 层的激活函数,或者说网络中的层,等于非线性函数 对上一层的线性函数加上偏置项 的处理结果。
现在,在这种情况下,我们实际上将明确地包含这个偏置项 ,而我们之前并未包括这个偏置项,主要是因为当我们手动推导反向传播时,这只是需要处理的又一个项。但现在,由于我们已经使用自动微分简化了这个过程,或者至少是我们如何做这些事情,我们将包含它,因为这是相当标准的做法。
我们将定义网络的输出为 层。所以我们首先重复这个迭代 次,然后输出就是这个迭代的最后一个值。我们也将第一层定义为等于 ,也就是网络的输入。
现在,我们网络的参数是 到 和 到 。这组成了我们所有参数 的集合。而 是一种非线性函数。所以这就是非线性激活函数,像ReLU或者Sigmoid。在这门课程的大部分示例中,我们通常会使用ReLU,至少在开始的时候会这样。
不过通常, 也可以根据 的值变化,我们通常会选择最后一层只是一个线性函数,这样我们的逻辑其实就是最后一层的线性函数。
比如说
是一个双层神经网络,其中最后一层的激活函数就是线性函数,但实际上如果把softmax函数视为非线性函数加入网络,这依然是双层网络。
现在我们看一下这些东西的大小,
让我们说 是 中的一个向量。所以 就是第 层的大小。
那么在这种情况下, 就会是一个矩阵。 转置必须将 映射到 。所以 应该是 乘以
也会是 中的向量,所以它必须是下一层的大小。好的,现在我们已经定义了自动微分,所以我们现在已经完成了。这就是全连接网络的完整定义。
这就是全连接网络的完整定义,与以前不同,我们不需要担心推导出这个的反向传播方程,当它连接到更大的网络部分时,找出所有这些事物的梯度。因为现在我们可以只在网络的前向传播中实现这些操作,并让我们的自动微分工具给我们一种自动计算梯度的方法。这是一件非常强大的事情。这就显示出自动微分的力量,现在我们已经完成了我们的层定义。
因为这里有一个小问题我想提一下,看起来像是一个小点,但当你实现一些网络时,你实际上会花费大量的时间。你可能已经开始遇到它了。
你已经在第一次作业中遇到了自动微分。你将会遇到一些关于这些各种操作的大小,尤其是,和矩阵的向量,尤其是当你使用这些表达式的矩阵形式时。
Matrix form and broadcasting subtleties

让我们来讨论这全连接网络表达式的矩阵形式。
最明显的一点就是,我们之前有了矩阵形式。回想一下,我们有了我们的项 。所以 就像是所有 的堆叠。
在第一行,我们有 的转置,在第二行,我们有 的 转置,以此类推。而我们已经看到了这种非常简单的矩阵形式。所以我们只是将这些东西应用于矩阵形式。而现在,因为这些东西已经转置了,你实际上不需要包含转置,你需要在后面包含 。但这个和我们原始的全连接网络是完全相同的。
这里的区别在于偏置项。记住, 是一个在 中的向量,它大小是。如果我们暂时忽略这个并且只用 来表示偏置项,那么是不对的。表达式右边的矩阵是 行 列,所以我们需要稍微调整这个。
实际上,这就是我们要进行的操作:这个操作就是1乘以 ,这里的1表示全部都是1的向量,在这种情况下,它是一个全部都是1的 向量。然后我们转置 ,所以这里的 转置就是一个 的矩阵,或者说,本质上是一个 维的行向量。它们的乘积就是一个 的矩阵。

这个乘积就是一个 的矩阵,其中每一行都包含了 的副本。所以,这个矩阵实际上就是等于一个矩阵,它在第一行有 转置,在第二行也有 转置,一直复制到有 份这个副本。这就是我们在线性代数中形成这些东西的方式。我们必须在线性代数中这样做,以便我们能准确地将这些矩阵写出来,或者说,让这些操作作为矩阵操作有意义。
现在,实际上,你并不想真的这么做。你并不想形成一个 ,或者说,你至少不想明确地形成一个 维的行向量,将它作为一个矩阵乘以你的偏置的转置版本,以及所有这样的东西。因为实际上,你并不会形成这些矩阵。你并不想这么做,因为本质上这只是浪费空间。我们稍后会更深入地讨论空间和内存的浪费,因为在某些情况下,你可能希望即时形成这些矩阵以进行更高效的操作。但是现在,如果我们只是在谈论内存,我们并不想要形成这个矩阵。这个矩阵只包含了同样的东西的很多份副本。所以我们要做的,就是讨论广播。
广播是你在处理时会遇到很多的一个话题。你已经在第一次作业中遇到了这个,当你通过广播操作实现自动微分时,这实际上是相当微妙的,并需要一些挣扎。在你的第一次作业中,你将会对此有一些挣扎。但是基本的想法就是,我们并不会明确地复制这个向量很多次,我们使用这种称为矩阵广播的技术。
矩阵广播
矩阵广播,如果你之前已经使用过像NumPy这样的库,你可能已经见过了。NumPy有一套非常先进但是也很混乱的矩阵广播程序,如果你之前没有见过,可能会觉得很混乱。但是基本的想法就是,广播会将具有单例维度的矩阵视为矩阵,这些矩阵可以自动地扩展它们的单例维度以重复多次,以此来匹配其他矩阵的大小。NumPy包含了一些非常复杂的广播规则。但在Needle中,这个框架你将会编写的,我们实际上会使用一种更明确的广播形式。
举个例子,在Needle中,如果你想要拿一个 维的向量,如果你想要拿这个 向量,并将其视为一个本质上是 维的矩阵,你需要做以下的操作。你需要调用像是 的东西。你首先必须明确地reshape它, 起初是一个 维的一维张量,本质上是一个有 维的1D张量。所以你需要调用像reshape这样的函数从。reshape到正确的大小。
当然,这是有效的,因为这并不改变它的元素的数量,它只是给我们的张量添加了一个维度。然后你需要将其广播到其他形状。所以你需要调用函数broadcast来广播它到,这是它的大小(包含了和)。
现在,这不是一个实现课程,所以我只是在这里说一下,但是这些操作,我将简短地说一下,但是这些操作你需要进行以正确地调整这个大小。并且我只是提一下,因为这是你在第一次作业中必须要做的事情。
但更重要的是,你需要思考如何在自动微分意义上实现这些reshape操作和这些broadcast操作,因为这实际上并不那么简单。你需要想一想,如果你有一个广播的操作,我如何计算它的伴随的呢?这有一点儿微妙,但如果你思考一下,你将能够理解出来,并且你实际上,将在你的第一次作业中实现这个。
所以你将在你的第一次作业中实现这个。但这只是一个快速的提示,因为这些看起来像是一些细节,但是他们实际上在实现的意义上非常重要,因为你需要知道你在复制什么,以及你没有复制什么。这就是我们在线性代数中做这些的基本原理,至少当我们在像NumPy或者像needle这样的库中实现它们时。
Key questions for fully connected networks

好的,现在我们已经了解了全连接网络,我们可以探讨一些相关的问题。坦白地说,接下来的几堂课将会探讨如何从数学上训练这样的网络来解决一些任务,以及如何实现这些网络。
所以,这堂课将会涵盖一些如何优化这类网络参数的内容。实际上,它会关注优化问题,但现在这将应用到优化这类问题上。我们还将详细讨论一些其他的细节,比如如何实现一个能以一种非常优雅且模块化的方式进行所有这些操作的Python库。你可以在这个库中添加哪些层或者哪些方面以确保优化、初始化等所有这些事情能很好地协同工作?
然后这些都会是一些在某种意义上非常针对问题的答案,但通过在接下来的几堂课中详细讨论所有这些点,你将能够了解如何完成这些事情。
好的,让我们直接开始优化部分吧。
请记住,我们的网络存在的基本问题,无论如何,我只是在这里再简单写一下,因为你们之前已经看过了,但我还是再简单写一下。这是核心的优化问题。你将在接下来的几张幻灯片中再次看到它。但请记住,我们的核心优化问题就是我们想要最小化我们的参数,我们想要最小化我们的假设在这个样例上的平均损失和输出。好的,这就是我们的目标。而在这一部分中,我只是将这整个东西称为,因为这只是一个函数。我们试图在优化方面进行优化的函数,只是我们参数的一种函数,我们想要找到最小化这个值的参数。在这种情况下,参数例如,在全连接网络的情况下,参数将会是我之前说过的 到 和 到 。但是真正重要的是,我们只是试图优化某个函数,对吧?所以现在我们要做的是,我们实际上将要深入研究一些在实际中用于大量深度学习训练的优化方法。在我说过,我们使用梯度下降或随机梯度下降之前,那并不完全正确。实际上,我们在深度学习中常常使用一些SGD和梯度下降的修改。
所以我们将在下一部分覆盖这些内容。
Gradient descent

首先,让我们回顾一下梯度下降。
请记住,我们的梯度下降算法是一个算法,它获取我们当前的参数值,然后将它们设置为这些值减去这个梯度项的一个小倍数。之前,我们是手动计算这个梯度项的,但现在的这个梯度项,其优点是我们可以通过自动微分自动地获取它。
之前,当我写东西时,我写的是我们的参数等于减去梯度的某个倍数,对吧?这就是我之前的写法。但我想在这里说的是,对于这一部分,我将更加明确。我将有明确的下标来指示我们的算法处于哪一次迭代。所以这里的t将是我们算法的迭代次数。我们这样做的原因只是因为这样更方便。这将是这些事情的实现视图。这有点像我们在代码或算法中如何实现梯度下降算法。
但在数学上,我们更喜欢在参数随时间的演变方面思考这些问题。当然,你并不想存储所有的参数,对吧?你并不想存储所有的参数,从你的第一次梯度迭代到最后的所有参数。你当然想覆盖它们。但在数学上,我们更喜欢认为这些事情是随时间演变的。
我也想提醒大家关于这个奇怪的梯度符号,这个表示我们要对哪个参数求导。我们在这里并不真正需要它,因为函数只有一个参数,但它表示我们要对哪个参数求导。这个参数是我们要评估梯度的值。所以这个符号也使梯度的含义更清楚。我一直觉得这个有点混乱,尽管我们必须使用它,因为这是人们常用的。记住,这里的是我们的步长。所以这就是我们在几堂课前讲过的梯度下降算法。它在某种意义上是所有优化的基础。请记住,梯度指向函数最陡峭的增加方向。所以这里减去梯度实际上是在最陡峭的方向上减小我们的函数。对于足够小的步长,这将在各种假设下导致我们的函数减小。好的,这就是我们的优化基础。这和你之前看到的是一样的。
Illustration of gradient descent

现在,让我们更详细地看看特定的情况,
因为当我们讨论其他方法、其他优化方法时,我们将使用这个案例。因此,我实际上要考虑一个我们正在优化的非常简单的二次函数(quadratic function)。我在这里考虑的 是一个二维向量,这样我就可以很容易地可视化这些东西。例如,在这些图表中,当我展示这个图表时,比如你这里的这些图表,我将根据 的演变来展示这个图表。所以这个图表的轴是 和 。换句话说,我正在显示实际的参数。所以我应该说这实际上是在 中,或者说 。
现在这个函数是一个二次函数,是一个在 P 中有二次项的函数。在这种情况下,P 会是一个 2x2 的矩阵。而 q 会是一个二维向量,所以当你在这里取所有的积时,你会得到一个标量输出。
这些图表显示的是我们的参数和我们的函数在这种情况下的两个不同步长的演变。
所以在步长 的情况下,我们的函数,我们的参数值有点来回弹跳,对吧?所以它们开始在这里,取一个梯度步,最后到达这里,然后是这里,这里,等等,直到我们达到这个函数的最优解。再次,这个函数,我在这里展示的是这些圆,这些椭圆,而是函数的等级线,所以它们是函数值常数的点。所以函数从这些点向外曲线。这展示了参数在这里的演变。它们在梯度下降的迭代中像这样在空间中弹跳。所以这个将是 ,这个将是 ,,等等。然后这个函数在这里展示了我们的目标 的演变。所以 在不同的迭代中的实际值是什么?如你所期望的,它在下降,对吧?所以我们在这里越来越接近最优解。但本质上,这个点在图中对应于这个点。我在这里展示的是 的两个不同的值。所以我们看到的是,对于较大的 值,你实际上收敛得相当快,但是你在参数空间中也会有点弹跳。
然而,如果我取一个较小的 ,,我收敛得更慢,但我在参数空间中不会弹跳那么多。这些实际上是我们上次谈到梯度下降时谈到的那种权衡。我们需要在快速收敛和参数变化不大之间做出决定。我们如何权衡优化的不同维度,这些东西,这在预先知道是非常困难的,对吧?因为如果你不知道这个函数长什么样,而且它不仅仅是一个2D函数,而是一个n维函数,你怎么知道如何选择你的步长,对吧?可能就是尝试不同的东西,看看他们收敛得有多快,看看什么会导致分歧,这种东西。
所以在某种意义上,本节剩下的部分将会讲述,以及深度学习优化中的很多内容,都是关于我们如何创建更新,能够更快地达到目标值的低点或好点,而不需要在调整步长或者可能的不同步长这些东西上花费太多的精力。这实际上是大量实际优化的内容。
我的想法
也就是说,可以通过优化方法,减少步长设置对收敛速度的影响,这也是为什么后面的优化是通过自动算出步长
作为目标函数是可以视为损失函数,因此优化过程的目的就是调整使得为0
所以我们现在要做的是介绍几种优化此函数的替代方法,它们可能有不同的收敛速率,或者说,有不同的收敛行为。
我们将主要关注这个简单的二次函数,至少在第一部分,这在某种意义上是相当误导的,因为这是一个简单的,在这种情况下,凸的二次函数,你在这里看到的行为并不总是能指示出你在我会说,真正的深度学习优化中得到的行为。但是,无论如何,思考这些算法如何在这些简单的目标上工作都是有启示性的,但是,当然,真正的证据是,以及它们的操作是它们在真正的深度学习和真正的深度学习网络的优化中能工作得有多好。
所以我们要讨论的第一种优化的替代方法是牛顿法。