Machine Learning - 第5周(Neural Networks: Learning)
The Neural Network is one of the most powerful learning algorithms (when a linear classifier doesn't work, this is what I usually turn to), and this week's videos explain the 'backprogagation' algorithm for training these models. In this week's programming assignment, you'll also get to implement this algorithm and see it work for yourself. The Neural Network programming exercise will be one of the more challenging ones of this class.
Neural Networks: Learning神经网络:学习
Cost Function and Backpropagation代价函数和反向传播
Cost Function代价函数
神经网络是当今 最强大的学习算法之一 在本节课视频 和后面几次课程中 我将开始讲述一种 在给定训练集下 为神经网络拟合参数的学习算法 正如我们讨论大多数 学习算法一样 我们准备从拟合神经网络参数的 代价函数开始讲起
我准备重点讲解 神经网络在分类问题 中的应用 假设我们有一个如左边所示的 神经网络结构 然后假设我们有一个 像这样的训练集 m个训练样本x(i) y(i) 我用大写字母 L 来表示 这个神经网络结构的总层数 所以 对于左边的网络结构 我们得到 L等于4 然后我准备用 sl表示 第L层的单元的数量 也就是神经元的数量 这其中不包括L层的偏差单元 比如说 我们得到s1 也就是输入层 是等于3的单元 s2在这个例子里等于5个单位 然后输出层s4 也就是sl 因为L本身等于4 在左边这个例子中输出层 有4个单位
我们将会讨论两种分类问题 第一种是二元分类 在这里y只能等于0或1 在这个例子中 我们有一个输出单元 上面这个神经网络的有四个输出单元 但是如果我们 用二元分类的话 我们就只能有一个输出结果 也就是计算出来的h(x) 神经网络的输出结果 h(x)就会是 一个实数 在这类问题里 输出单元的个数 sl L同样代表最后一层的序号 因为这就是我们 在这个网络结构中的层数 所以我们在输出层的单元数目 就将是1 在这类问题里 为了简化记法 我会把K设为1 这样你可以把K看作 输出层的 单元数目
我们要考虑的第二类分类问题 就是多类别的分类问题 也就是会有K个不同的类 比如说 如果我们有四类的话 我们就用这样的表达形式来代表y 在这类问题里 我们就会有K个输出单元 我们的假设输出 就是一个K维向量 输出单元的个数 就等于K 通常这类问题里 我们都有K大于 或等于3 因为如果只有两个类别 我们就不需要 使用这种一对多的方法 我们只有在K大于 或者等于3个类的时候 才会使用这种 一对多的方法 因为如果只有两个类别 我们就只需要一个输出单元就可以了
现在我们来为神经网络定义代价函数 我们在神经网络里 使用的代价函数 应该是逻辑回归里 使用的代价函数的一般化形式 对于逻辑回归而言 我们通常使代价函数 J(θ) 最小化 也就是-1/m 乘以后面这个代价函数 然后再加上这个额外正则化项 这里是一个 j从1到n的求和形式 因为我们 并没有把偏差项 0正则化
对于一个神经网络来说 我们的代价函数是这个式子的一般化形式 这里不再是仅有一个 逻辑回归输出单元 取而代之的是K个 所以这是我们的代价函数 神经网络现在输出了 在K维的向量 这里K可以取到1 也就是 原来的二元分类问题 我准备用这样一个记法 h(x)带下标i 来表示第i个输出 也就是h(x)是一个K维向量 下标 i 表示 选择了神经网络输出向量的 第i个元素 我的代价函数 J(θ) 将成为下面这样的形式 -1/m乘以 一个类似于我们在 逻辑回归里所用的 求和项 除了这里我们求的是 k从1到K的所有和 这个求和项主要是 K个输出单元的求和 所以如果我有四个输出单元 也就是我的神经网络最后一层 有四个输出单元 那么这个求和就是 这个求和项就是 求k等于从1到4的 每一个的逻辑回归算法的代价函数 然后按四次输出的顺序 依次把这些代价函数 加起来 所以你会特别注意到 这个求和符号应用于 yk和hk 因为 我们主要是讨论 K个输出单元 并且把它和yk的值相比 yk的值就是 这些向量里表示 它应当属于哪个类别的量
最后 这里的第二项 这就是类似于我们在逻辑回归里所用的 正则化项 这个求和项看起来 确实非常复杂 它所做的就是把这些项全部相加 也就是对所有i j和l 的θji的值都相加 正如我们在逻辑回归里一样 这里要除去那些对应于偏差值的项 那些项我们是不加进去的 那些项我们是不加进去的 具体地说 我们不把 那些对于i等于0的项 加入其中 这是因为 当我们计算神经元的激励值时 我们会有这些项 θi0 加上θi1 乘以x1 再加上 等等等等 这里我认为 我们可以加上2的上标 如果这是第一个隐含层的话 所以这些带0的项 所以这些带0的项 对应于乘进去了 x0 或者是a0什么的 这就是一个类似于 偏差单元的项 类比于我们在做 逻辑回归的时候 我们就不应该把这些项 加入到正规化项里去 因为我们并不想正规化这些项 并把这些项设定为0 但这只是一个合理的规定 即使我们真的把他们加进去了 也就是i从0加到sL 这依然成立 并且不会有大的差异 但是这个"不把偏差项正规化" 的规定可能只是会 更常见一些
好了 这就是我们准备 应用于神经网络的代价函数 在下一个视频中 我会开始讲解一个算法 来最优化这个代价函数
Backpropagation Algorithm反向传播算法
在上一个视频里 我们讲解了 神经网络的代价函数 在这个视频里 让我们来说说 让代价函数最小化的算法 具体来说 我们将主要讲解反向传播算法
这个就是我们上一个视频里写好的 代价函数 我们要做的就是 设法找到参数 使得J(θ)取到最小值 为了使用梯度下降法或者 其他某种高级优化算法 我们需要做的就是 写好一个可以通过输入 参数 θ 然后计算 J(θ) 和这些 偏导数项的代码 记住 这些神经网络里 对应的参数 也就是 θ 上标 (l) 下标 ij 的参数 这些都是实数 所以这些都是我们需要计算的 偏导数项 为了计算代价函数 J(θ) 我们就是用上面这个公式 所以我们在本节视频里大部分时间 想要做的都是 重点关注 如何计算这些 偏导数项
我们从只有一个 训练样本的情况 开始说起 假设 我们整个训练集 只包含一个训练样本 也就是实数对 我这里不写成x(1) y(1) 就写成这样 把这一个训练样本记为 (x, y) 让我们粗看一遍 使用这一个训练样本 来计算的顺序 首先我们 应用前向传播方法来 计算一下在给定输入的时候 假设函数是否会真的输出结果 具体地说 这里的 a(1) 就是第一层的激励值 也就是输入层在的地方 所以我准备设定他为 然后我们来计算 z(2) 等于 θ(1) 乘以 a(1) 然后 a(2) 就等于 g(z(2)) 函数 其中g是一个S型激励函数 这就会计算出第一个 隐藏层的激励值 也就是神经网络的第二层 我们还增加这个偏差项 接下来我们再用2次 前向传播 来计算出 a(3) 和 最后的 a(4) 同样也就是假设函数 h(x) 的输出 所以这里我们实现了把前向传播 向量化 这使得我们可以计算 神经网络结构里的 每一个神经元的 激励值
接下来 为了计算导数项 我们将 采用一种叫做反向传播(Backpropagation)的算法 反向传播算法从直观上说 就是对每一个结点 我们计算这样一项 δ下标 j 上标(l) 这就用某种形式 代表了第 l 层的第 j 个结点的 误差 我们还记得 a 上标 (l) 下标 j 表示的是第 l 层第 j 个单元的 激励值 所以这个 δ 项 在某种程度上 就捕捉到了我们 在这个神经节点的激励值的误差 所以我们可能希望这个节点的 激励值稍微不一样 具体地讲 我们用 右边这个有四层 的神经网络结构做例子 所以这里大写 L 等于4 对于每一个输出单元 我们准备计算δ项 所以第四层的第j个单元的δ就等于 这个单元的激励值 减去训练样本里的 真实值0 所以这一项可以 同样可以写成 h(x) 下标 j 所以 δ 这一项就是 假设输出 和训练集y值 之间的差 这里 y 下标 j 就是 我们标记训练集里向量 的第j个元素的值 顺便说一下 如果你把 δ a 和 y 这三个 都看做向量 那么你可以同样这样写 向量化地实现 也就是 δ(4)等于 a(4) 减去 y 这里 每一个变量 也就是 δ(4) a(4) 和 y 都是一个向量 并且向量维数等于 输出单元的数目 所以现在我们计算出 网络结构的 误差项 δ(4) 我们下一步就是计算 网络中前面几层的误差项 δ 这个就是计算 δ(3) 的公式 δ(3) 等于 θ(3) 的转置乘以 δ(4) 然后这里的点乘 这是我们从 MATLAB 里知道的 对 y 元素的乘法操作 所以 θ(3) 转置乘以 δ(4) 这是一个向量 g'(z(3)) 同样也是一个向量 所以点乘就是 两个向量的元素间对应相乘 其中这一项 g'(z(3)) 其实是对激励函数 g 在输入值为 z(3) 的时候 所求的 导数 如果你掌握微积分的话 你可以试着自己解出来 然后可以简化得到我这里的结果 但是我只是从实际角度告诉你这是什么意思 你计算这个 g' 这个导数项其实是 a(3) 点乘 (1-a(3)) 这里a(3)是 激励向量 1是以1为元素的向量 a(3) 又是 一个对那一层的 激励向量
接下来你应用一个相似的公式 来计算 δ(2) 同样这里可以利用一个 相似的公式 只是在这里 是 a(2) 这里我并没有证明 但是如果你懂微积分的话 证明是完全可以做到的 那么这个表达式从数学上讲 就等于激励函数 g函数的偏导数 这里我用 g‘来表示 最后 就到这儿结束了 这里没有 δ(1) 项 因为 第一次对应输入层 那只是表示 我们在训练集观察到的 所以不会存在误差 这就是说 我们是不想改变这些值的 所以这个例子中我们的 δ 项就只有 第2层和第3层
反向传播法这个名字 源于我们从 输出层开始计算 δ项 然后我们返回到上一层 计算第三隐藏层的 δ项 接着我们 再往前一步来计算 δ(2) 所以说 我们是类似于把输出层的误差 反向传播给了第3层 然后是再传到第二层 这就是反向传播的意思
最后 这个推导过程是出奇的麻烦的 出奇的复杂 但是如果你按照 这样几个步骤计算 就有可能简单直接地完成 复杂的数学证明 如果你忽略标准化所产生的项 我们可以证明 我们要求的偏导数项 恰好就等于 激励函数和这些 δ 项 这里我们忽略了 λ 或者说 标准化项 λ 是等于 0 我们将在之后完善这一个 关于正则化项
所以到现在 我们通过 反向传播 计算这些δ项 可以非常快速的计算出 所有参数的 偏导数项 好了 现在讲了很多细节了 现在让我们把所有内容整合在一起 然后说说 如何实现反向传播算法 来计算关于这些参数的偏导数 当我们有 一个非常大的训练样本时 而不是像我们例子里这样的一个训练样本
我们是这样做的 假设我们有 m 个样本的训练集 正如此处所写 我要做的第一件事就是 固定这些 带下标 i j 的 Δ 这其实是 大写的希腊字母 δ 我们之前写的那个是小写 这个三角形是大写的 Δ 我们将对每一个i 和 j 对应的 Δ 等于0 实际上 这些大写 Δij 会被用来计算 偏导数项 就是 J(θ) 关于 θ 上标(l) 下标 i j 的 偏导数 所以 正如我们接下来看到的 这些 δ 会被作为累加项 慢慢地增加 以算出这些偏导数
接下来我们将遍历我们的训练集 我们这样写 写成 For i = 1 to m 对于第 i 个循环而言 我们将取训练样本 (x(i), y(i)) 我把1999年的值画在一列里 我们要做的第一件事是 设定a(1) 也就是 输入层的激励函数 设定它等于 x(i) x(i) 是我们第 i 个训练样本的 输入值 接下来我们运用正向传播 来计算第二层的激励值 然后是第三层 第四层 一直这样 到最后一层 L层 接下来 我们将用 我们这个样本的 输出值 y(i) 来计算这个输出值 所对应的误差项 δ(L) 所以 δ(L) 就是 假设输出减去 目标输出 接下来 我们将 运用反向传播算法 来计算 δ(L-1) δ(L-2) 一直这样直到 δ(2) 再强调一下 这里没有 δ(1) 因为我们不需要对输入层考虑误差项
最后我们将用 这些大写的 Δ 来累积我们在前面写好的 偏导数项 顺便说一下 如果你再看下这个表达式 你可以把它写成向量形式 具体地说 如果你把 δij 看作一个矩阵 i j代表矩阵中的位置 那么 如果 δ(L) 是一个矩阵 我们就可以写成 Δ(l) 等于 Δ(l) 加上 小写的 δ(l+1) 乘以 a(l) 的转置 这就是用向量化的形式 实现了对所有 i 和 j 的自动更新值 最后 执行这个 for 循环体之后 我们跳出这个 for 循环 然后计算下面这些式子 我们按照如下公式计算 大写 我们对于 j=0 和 j≠0 分两种情况讨论 在 j=0 的情况下 对应偏差项 所以当 j=0 的时候 这就是为什么 我们没有写额外的标准化项
最后 尽管严格的证明对于 你来说太复杂 你现在可以说明的是 一旦你计算出来了这些 这就正好是 代价函数对 每一个参数的偏导数 所以你可以把他们用在 梯度下降法 或者其他一种更高级的 优化算法上 这就是反向传播算法 以及你如何计算 神经网络代价函数的 偏导数 我知道这个里面 细节琐碎 步骤繁多 但是在后面的编程作业 和后续的视频里 我都会给你一个 清晰的总结 这样我们就可以把算法的所有细节 拼合到一起 这样 当你想运用反向传播算法 来计算你的神经网络的代价函数 关于这些参数的偏导数的时候 你就会清晰地知道 你要的是什么
Backpropagation Intuition
在上一段视频中 我们介绍了反向传播算法 对很多人来说 当第一次看到这种算法时 第一印象通常是 哇哦 这个算法需要那么多繁杂的步骤 简直是太复杂了 实在不知道这些步骤 到底应该如何合在一起使用 就好像一个黑箱 里面充满了复杂的步骤 如果你对反向传播算法 也有这种感受的话 这其实是正常的 相比于线性回归算法 和逻辑回归算法而言 从数学的角度上讲 反向传播算法 似乎并不简洁 对于反向传播这种算法 其实我已经使用了很多年了 但即便如此 即使是现在 我也经常感觉 自己对反向传播算法的理解并不是十分深入
对于反向传播算法究竟是如何执行的 并没有一个很直观的理解 做过编程练习的同学 应该可以感受到 这些练习或多或少能帮助你 将这些复杂的步骤梳理了一遍 巩固了反向传播算法具体是如何实现的 这样你才能自己掌握这种算法 在这段视频中 我想更加深入地 讨论一下 反向传播算法的这些复杂的步骤 并且希望给你一个 更加全面直观的感受 理解这些步骤究竟是在做什么 也希望通过这段视频 你能理解 它至少还是一个合理的算法 但可能你即使看了这段视频 你还是觉得 反向传播依然很复杂 依然像一个黑箱 太多复杂的步骤 依然感到有点神奇 这也是没关系的 我说了 即使是我 接触反向传播这么多年了 有时候仍然觉得这是一个难以理解的算法 但还是希望这段视频能有些许帮助
为了更好地理解 反向传播算法 我们再来仔细研究一下前向传播的原理 幻灯片所示的神经网络 包含两个输入单元 这不包括偏差单元 在第二层有两个隐藏单元 在下一层也有两个隐藏单元 最后的输出层 有一个输出单元 再提醒一下 这里说的2 2 2 都不算顶上附加的偏差单元+1 为了更清楚地展示前向传播 我想把这个网络 画得稍微不同一些 具体来讲 我把这个神经网络的节点 都画成椭圆型 以便在节点里面写字 在进行前向传播时 我们可以用一个具体的例子说明
假如说 训练样本 x(i) y(i) 那么这里的 x(i) 将被传入输入层 因此这里就是 x(i)1 和 x(i)2 这是我们输入层的值 那么 当我们进行前向传播 传播到第一个隐藏层时 我们的做法是 算出 z(2)1 和 z(2)2 因此这两个值 是输入单元的加权总和 接下来 我们将S型的逻辑函数 和S型的激励函数 应用到z值上 得出了这样的激励值 因此我们得到 a(2)1 和 a(2)2 的值 然后 再做一次前向传播 这里的 z(3)1 应用S型的逻辑函数 和激励函数 得到 a(3)1 类似这样进行下去 最后我们得到 z(4)1 应用激励函数 得到 a(4)1 这也是这个网络的输出单元的值 我把这个箭头擦掉 这样留点书写空间 那么 如果你仔细看这里的计算 关注这一层的隐藏单元 我们知道了这个权值 这里用桃红色表示的 这是我们的权值 θ(2)10 这里的角标不重要 而这里的权值 我用红色来标记的 是θ(2)11 而这里的权值 我用青色表示的 是θ(2)12 因此要计算 z(3)1 z(3)1 的值等于 这个桃红色的权值 乘以这个值 也就是θ(2)10 乘上1 加上这个红色的权值 乘以这个值 也就是θ(2)11 乘上a(2)1 最后是青色的权值乘上这个值 也就是 θ(2)12乘以a(2)1 那么这就是前向传播 事实上 正如我们后面将会看到的 反向传播的做法 其过程 非常类似于此 只有计算的方向不同而已 与这里前向传播的方向从左至右 不同的是 反向传播的算法中 计算的方向是 从右往左的 但计算的过程是完全类似的 在接下来的两页幻灯片中 我会详细地讲解
为了更好地理解 反向传播算法的原理 我们把目光转向代价函数 这个代价函数 对应的情况是只有一个输出单元 如果我们有不止一个 输出单元的话 只需要对所有的输出单元 进行一次求和运算 但如果只有一个输出单元时 代价函数就是这样 我们用同一个样本同时来做正向和反向传播 那么 请注意这组训练样本 x(i) y(i) 注意这种只有一个输出单元的情况 那么这里的 y(i) 就是一个实数 如果不考虑正则化 也就是说 λ 等于0 因此最后的正则化项就没有了 好的 那么如果你观察 这个求和运算括号里面 与第i个训练样本对应的 代价项 也就是说 和训练样本 x(i) y(i) 对应的代价项 将由这个式子确定 因此 第 i 个样本的代价值 可以写成如下的形式 而这个代价函数 所扮演的角色 可以看作是平方误差 因此 我们不必关心 这个复杂的表达式 当然如果你愿意 你可以把 cost(i) 想成是 该神经网络输出值 与实际值的 差的平方 就像在逻辑回归中 我们选择稍微复杂的一点的 代价函数 log函数 但为了容易理解 可以把这个代价函数 看作是某种 平方误差函数 因此 这里的cos(i) 表征了该神经网络 是否能准确地预测样本i的值 也就是输出值 和实际观测值y(i)的接近程度
现在我们来看反向传播是怎么做的 一种直观的理解是 反向传播算法就是在计算 所有这些δ(i)j项 并且我们可以 把它们看作是 这些激励值的 "误差" 注意这些激励值是 第 l 层中的 第 j 项 更正式一点的说法是 也许那些比较熟悉微积分的同学 更能理解 更正式地说 δ 项实际上是 关于 z(l)j 的 偏微分 也就是 cost 函数 关于我们计算出的 输入项的加权和 也就是 z 项的 偏微分 所以 实际上这个代价函数 是一个关于标签 y 和这个 h(x) 的值也就是 神经网络输出值 的函数 如果我们观察该网络内部的话 把这些 z(l)j 项 稍微改一点点 那就将影响到该神经网络的输出 并且最终会改变代价函数的值 当然 还是那句话 讲这些只是对那些熟悉微积分的同学 如果你对偏微分很熟悉的话 你能理解这些δ项是什么 它们实际上是 代价函数 关于这些中间项的偏微分 因此 它们度量着 我们对神经网络的权值 做多少的改变 对中间的计算量 影响是多少 进一步地 对整个神经网络的输出 h(x) 影响多少 以及对整个的代价值影响多少 可能刚才讲的 偏微分的这种理解 不太容易理解 没关系 不用偏微分的思想 我们同样也可以理解 我们再深入一点 研究一下反向传播的过程 对于输出层 如果我们设置δ项 比如说 δ(4)1 等于 y(i) 假设我们进行第i个训练样本的 正向传播 和反向传播 那么应该等于 y(i) 减去 a(4)1 因此这实际是两者的偏差 也就是 y 的实际值 减去预测值 得到的差值 这样 我们就算出了 δ(4)1 的值 接下来我们要对这些值进行反向传播 我稍后将详细解释 计算出前一层的 δ 项的值 那么这里我们计算出 δ(3)1 和 δ(3)2 然后同样的 再进行下一层的反向传播 这一次计算出 δ(2)1 以及 δ(2)2 反向传播的计算 和进行前向传播几乎相同 唯一的区别就是方向相反 我想表达的是 我们来看我们是怎样得到 δ(2)2 的值的 我们要计算 δ(2)2 与前向传播类似 我要对一些权值进行标记 那么这条权值 用桃红色表示的 就是 θ(2)12 然后这根箭头表示的权值 我用红色来标记 它代表的是 θ(2)22 所以 我们来看 δ(2)2是如何得到的 实际上 我们要做的是 我们要用这个 δ 值 和权值相乘 然后加上 这个 δ 值乘以权值的结果 也就是说 它其实是 这些δ值的加权和 权值是这些对应边的强度 让我把这些具体的值写出来 δ(2)2 的值 等于桃红色的这条权值 θ(2)12 乘以δ(3)1 加上 下一个是用红色标记的权值 θ(2)22 乘上δ(3)2 所以 简单地说 就是红色的权值乘以它指向的值 加上 桃红色的权值乘以它指向的值 这样我们就得到了上一层的 δ 值 再举一个例子 我们来看这个 δ 值 是怎么得到的呢? 仍然是类似的过程 如果这个权值 用绿色表示的这根箭头 假如这个权值 是θ(3)12 那么 δ(3)2 将等于这条绿色的权值 θ(3)12 乘以 δ(4)1 另外顺便提一下 目前为止 我写的 δ 值 仅仅是隐藏层中的 没有包括偏差单元+1 包不包括偏差单元取决于你 如何定义这个反向传播算法 或者取决于你怎样实现这个算法 你也可以 对这些偏差单元 计算 δ 的值 这些偏差单元 总是取为+1的值 一直都这么取 我们不能也没有必要 更改偏差单元的值 所以还是取决于你实现反向传播的方法 通常说来 我在执行反向传播的时候 我是算出了 这些偏差单元的δ值 但我通常忽略掉它们 而不把它们代入计算 因为它们其实 并不是计算那些微分的必要部分
好了 我希望这节课 能给你一个 有关反向传播算法的实现过程 更深刻的印象 我知道可能这些过程 还是看起来很神奇 很“黑箱” 不要紧 在后面的课程中 在"putting it together"视频中 我还会再介绍一点有关反向传播的内容 但是很遗憾的是 要想完全看清并且理解这个算法 的确是很困难的 但我想 幸运的是 多年来 很多人都能顺利地运用 反向传播算法 并且如果你执行一遍整个算法 你就能掌握这种很强大的机器学习算法 尽管它内部的工作原理 的确显得很难观察
Backpropagation in Practice反向传播练习
Implementation Nonrolling Parameters
在上一段视频中 我们谈到了怎样使用反向传播算法 计算代价函数的导数 在这段视频中 我想快速地向你介绍一个细节的实现过程 怎样把你的参数 从矩阵展开成向量 以便我们在高级最优化步骤中的使用需要
具体来讲 你 执行了代价函数costFunction 输入参数是theta 函数返回值是代价函数以及导数值 然后你可以将返回值 传递给高级最优化算法fminunc 顺便提醒 fminunc并不是唯一的算法 你也可以使用别的优化算法 但它们的功能 都是取出这些输入值 @costFunction 以及theta值的一些初始值 并且这些程序 都假设theta 和这些theta初始值 都是参数向量 也许是n或者n+1阶 但它们都是向量 同时假设这个代价函数 第二个返回值 也就是gradient值 也是n阶或者n+1阶 所以它也是一个向量 这部分在我们使用逻辑回归的时候 运行顺利 但现在 对于神经网络 我们的参数将不再是 向量 而是矩阵了 因此对于一个完整的神经网络 我们的参数矩阵为θ(1) θ(2) θ(3) 在Octave中我们可以设为 Theta1 Theta2 Theta3 类似的 这些梯度项gradient 也是需要得到的返回值 那么在之前的视频中 我们演示了如何计算 这些梯度矩阵 它们是D(1) D(2) D(3) 在Octave中 我们用矩阵D1 D2 D3来表示 在这节视频中 我想很快地向你介绍 怎样取出这些矩阵 并且将它们展开成向量 以便它们最终 成为恰当的格式 能够传入这里的Theta 并且得到正确的梯度返回值gradient 具体来说 假设我们有这样一个神经网络 其输入层有10个输入单元 隐藏层有10个单元 最后的输出层 只有一个输出单元 因此s1等于第一层的单元数 s2等于第二层的单元数 s3等于第三层的 单元个数 在这种情况下 矩阵θ的维度 和矩阵D的维度 将由这些表达式确定 比如说 θ(1)是一个10x11的矩阵 以此类推 因此 在Octave中 如果你想将这些矩阵 转化为向量 那么你要做的 是取出你的Theta1 Theta2 Theta3 然后使用这段代码 这段代码将取出 三个θ矩阵中的所有元素 也就是说取出Theta1 的所有元素 Theta2的所有元素 Theta3的所有元素 然后把它们全部展开 成为一个很长的向量 也就是thetaVec 同样的 第二段代码 将取出D矩阵的所有元素 然后展开 成为一个长向量 被叫做DVec 最后 如果你想从向量表达 返回到矩阵表达式的话 你要做的是 比如想再得到Theta1 那么取thetaVec 抽出前110个元素 因此 Theta1就有110个元素 因为它应该是一个10x11的矩阵 所以 抽出前110个元素 然后你就可以 reshape矩阵变维命令来重新得到Theta1 同样类似的 要重新得到Theta2矩阵 你需要抽出下一组110个元素并且重新组合 然后对于Theta3 你需要抽出最后11个元素 然后执行reshape命令 重新得到Theta3 以下是这一过程的Octave演示 对于这一个例子 让我们假设Theta1 为一个10x11的单位矩阵 因此它每一项都为1 为了更易看清 让我们把Theta2设为 一个10行11列矩阵 每个元素都为2 然后设Theta3 是一个1x11的矩阵 每个元素都为3 因此 这样我们得到三个独立的矩阵 Theta1 Theta2 Theta3 现在我们想把所有这些矩阵变成一个向量 thetaVec = ; 好的 注意中间有冒号 像这样 现在thetaVec矩阵 就变成了一个很长的向量 含有231个元素 如果把它打出来 我们就能看出它是一个很长的向量 包括第一个矩阵的所有元素 第二个矩阵的所有元素 以及第三个矩阵的所有元素 如果我想重新得到 我最初的三个矩阵 我可以对thetaVec使用reshape命令 抽出前110个元素 将它们重组为一个10x11的矩阵 这样我又再次得到了Theta1矩阵 然后我再取出 接下来的110个元素 也就是111到220号元素 我就又重组还原了第二个矩阵 最后 再抽出221到最后一个元素 也就是第231个元素 然后重组为1x11的矩阵 我就又得到了Theta3矩阵 为了使这个过程更形象 下面我们来看怎样将这一方法 应用于我们的学习算法 假设说你有一些 初始参数值 θ(1) θ(2) θ(3) 我们要做的是 取出这些参数并且将它们 展开为一个长向量 我们称之为initialTheta 然后作为theta参数的初始设置 传入函数fminunc 我们要做的另一件事是执行代价函数costFunction 实现算法如下 代价函数costFunction 将传入参数thetaVec 这也是包含 我所有参数的向量 是将所有的参数展开成一个向量的形式 因此我要做的第一件事是 我要使用 thetaVec和重组函数reshape 因此我要抽出thetaVec中的元素 然后重组 以得到我的初始参数矩阵 θ(1) θ(2) θ(3) 所以这些是我需要得到的矩阵 因此 这样我就有了 一个使用这些矩阵的 更方便的形式 这样我就能执行前向传播 和反向传播 来计算出导数 以求得代价函数的J(θ) 最后 我可以取出这些导数值 然后展开它们 让它们保持和我展开的θ值 同样的顺序 我要展开D1 D2 D3 来得到gradientVec 这个值可由我的代价函数返回 它可以以一个向量的形式返回这些导数值 现在 我想 对怎样进行参数的矩阵表达式 和向量表达式 之间的转换 有了一个更清晰的认识 使用矩阵表达式 的好处是 当你的参数以矩阵的形式储存时 你在进行正向传播 和反向传播时 你会觉得更加方便 当你将参数储存为矩阵时 一大好处是 充分利用了向量化的实现过程 相反地 向量表达式的优点是 如果你有像thetaVec或者DVec这样的矩阵 当你使用一些高级的优化算法时 这些算法通常要求 你所有的参数 都要展开成一个长向量的形式 希望通过我们刚才介绍的内容 你能够根据需要 更加轻松地 在两种形式之间转换
Gradient Checking
在之前的视频中 我们讨论了 如何使用前向传播 和反向传播 计算神经网络中的导数 但反向传播作为一个 有很多细节的算法 在实现的时候 会有点复杂 而且有一个不好的方面是 在实现反向传播时 会遇到很多细小的错误 所以如果你把它和梯度下降法 或者其他优化算法一起运行时 可能看起来它运行正常 并且 你的代价函数J 最后可能 在每次梯度下降法迭代时 都会减小 即使在实现反向传播时有一些小错误 可能也会检查不出来 所以它看起来是 J(θ)在减小 但是可能你最后得到的神经网络 但是可能你最后得到的 神经网络误差 比没有错误的要高 而且你很可能 就是不知道 你的结果是这些 小错误导致的 那你应该怎么办呢 有一个想法叫梯度检验 (Gradient Checking) 可以解决基本所有的问题 我现在每次实现 神经网络的反向传播 或者类似的 梯度下降算法 或者其他比较复杂的模型 我都会使用梯度检验 如果你这么做 它会帮你确定 并且能很确信 你实现的前向传播和反向传播 或者其他的什么 是100%正确的 我见过很多 这样解决那些 实现时容易有 有小错误的问题 在之前的视频中 我一般是让你相信 我给出的计算 δ,d项 等等之类的公式 我要求你们相信 他们计算的就是 代价函数的梯度 但一旦你们实现数值梯度检验 也就是这节视频的主题 你就能够自己验证 你写的代码 确实是在计算 代价函数J的导数 想法是这样的 考虑下面这个例子 假如我有一个 函数J(θ) 并且我有个值 θ 在这个例子中 我假定θ只是一个实数 假如说我想估计这个函数在这一点的导数 这个导数等于 这条切线的斜率 下面我要用数值方法 来计算近似的导数 这个是用数值方法 计算近似导数的过程 我要计算θ+ε 这个值在右边一点 然后计算θ-ε 然后看这两个点 用一条直线 把它们连起来 我要把这两个点 用一条直线连起来 然后用这条 红色线的斜率 来作为我 导数的近似值 真正的导数是这边这条 蓝色线的斜率 这看起来是个不错的近似 在数学上 这条红线的斜率等于 这个垂直的高度 除以这个 这个水平的宽度 所以上面这点 是J(θ+ε) 这点是J(Θ-ε) 垂直方向上的 差是J(θ+ε)-J(θ+ε) 也就是说 水平的距离就是2ε 那么 我的近似是这样的 J(θ) 对θ的导数 近似等于 J(θ+ε)-J(θ-ε) 除以2ε 近似于J(θ+ε)-J(θ-ε) 除以2ε 通常 我给ε取很小的值 比如可能取 10的-4次方 ε的取值在一个 很大范围内都是可行的 实际上 如果你让ε非常小 那么 数学上 这里这项实际上就是导数 就变成了函数 在这点上准确的斜率 只是我们不想用 非常非常小的ε 因为可能会产生数值问题 所以我通常让ε 差不多等于10^-4 顺便说一下 可能你们有些学习者 见过另外这种 估计导数的公式 右边这个叫做单侧拆分 左边这个公式叫做双侧差分 双侧差分给我们了一个 稍微精确些的估计 所以我通常用那个 而不用这个单侧差分估计 具体地说 你在Octave中实现时 要使用下面这个 你的程序要调用 gradApprox来计算 这个函数 会通过这个公式 J(θ+ε)-J(θ-ε) 除以2ε 它会给出这点导数的 数值估计 在这个例子中 它看起来是个很好的估计 在之前的幻灯片中 我们考虑了 θ是一个实数的情况 现在我们看更普遍的情况 θ是一个向量参数 假如说θ是n维向量 它可能是我们的 神经网络参数的 展开形式 所以θ是一个有 有n个元素的向量 θ1到θn 我们可以用类似的想法 来估计所有的偏导数项 具体地说 代价函数对 第一个参数θ1取偏导数 它可以用J 和增大的θ1得到 所以你有J(θ1+ε) 等等 减去J(θ1-ε) 然后除以2ε 对第二个参数θ2 取偏导数 还是这样 除了你要对 θ2+ε取J 这里还有θ2-ε 这样计算后面的偏导数 直到θn 它的算法是 对θn增加 和减少ε 这些公式 给出一个计算J 对任意参数求偏导数的 数值近似的方法 具体地说 你要实现的是下面这个 我们把这个用在Octave里 来计算数值导数 假如 i 等于 1 到 n n是我们的参数向量θ的维度 我通常用参数的展开形式来计算 你知道θ只是我们 神经网络模型的一长列参数 我让thetaPlus等于theta 然后给thetaPlus的第 i 项 加上EPSILON 这就是基本的 thetaPlus等于theta 除了thetaPlus(i) 它会增加EPSILON 所以如果thetaPlus 等于θ1 θ2 等等 那么θi 增加了EPSILON 然后一直到θn 这就是thetaPlus的作用 类似的 这两行 给thetaMinus 类似地赋值 只是θi不是加EPSILON 而是减EPSILON 最后 你运行这个 gradApprox(i) 它会给你近似的 J(θ)对θi的 偏导数 我们实现 神经网络时 是这样用的 我们要实现这个 用for循环来计算 代价函数对 每个网络中的参数的偏导数 然后我们用从 反向传播得到的梯度 DVec是我们从反向传播中 得到的导数 所以后向传播是一个 相对比较有效率的 计算代价函数 对参数的导数 或偏导数的方法 接下来 我通常做的是 计算数值导数 就是gradApprox 我们刚从上面这里得到的 来确定它等于 或者近似于 差距很小 非常接近我们 从反向传播得到的DVec 如果这两种 计算导数的方法 给你相同的结果或者非常接近结果 最多几位小数的差距 那么我就非常确信 我实现的反向传播时正确的 然后我把这些DVec向量用在 梯度下降法或者 其他高级优化算法里 然后我就可以比较确信 我计算的导数 是正确的 那么 我的代码 应该也可以正确运行 可以很好地优化J(θ) 最后 我想把 所有的东西放在一起 然后告诉你怎么实现这个数值梯度检验 这是我通常做的 第一件事 是实现反向传播来计算DVec 这个步骤是我们 之前的视频中讲过的 计算DVec 它可能是这些矩阵的展开形式 然后我要做的是 用gradApprox实现数值梯度检验 这是我在这节视频前面部分讲的 在之前的幻灯片里 然后你要确定DVec和gradApprox给出接近的结果 可能最多差几位小数 最后 这是最重要的一步 在使用你的代码去学习 训练你的网络之前 重要的是要关掉梯度检验 不再使用 这节视频前面讲的 这个数值导数公式 来计算 gradApprox 这样做的原因是 我们之前讲的这个 数值梯度检验代码 是一个计算量 非常大的程序 它是一个非常慢的计算近似导数的方法 而相对地 我们之前讲的 反向传播算法 也就是那个 DVec的D(1) D(2) D(3)的算法 反向传播是一个在计算导数上 效率更高的方法 所以当你确认了 你的反向传播算法是正确的 你应该关掉梯度检验 就是不使用它 再重申一下 在为了训练分类器 运行你的算法 做很多次梯度下降 或高级优化算法的迭代之前 要确定你 不再使用 梯度检验的程序 具体来说 如果你在每次的梯度下降法迭代时 都运行数值梯度检验 或者你用在 代价函数的内循环里 你的程序会变得非常慢 因为数值梯度检验程序 比反向传播算法 要慢很多 反向传播算法 就是我们计算 δ(4) δ(3) δ(2) 等等的 那就是反向传播算法 那是一个比梯度检验更快的计算导数的方法 所以当你准备好了 一旦你验证了 反向传播的实现是正确的 要确定你在训练算法时把它关闭了 或者说不再使用梯度检验程序 否则你的程序会运行得非常慢 所以如果你计算用数值方法计算导数 那是你用来确定反向传播实现 是否正确的的方法 当我实现反向传播 或者类似的复杂模型的梯度下降算法 我经常使用梯度检验 这的确能帮我确定我的代码是正确的
Random Initialization
在前面的视频中 我们总结了 在神经网络的实现和训练中 所有需要的知识 这是最后一个 我想要分享给你们的内容 这就是随机初始化的思想 当你运行一个算法 例如梯度下降算法 或者其他高级优化算法时 我们需要给变量 θ 一些初始值 所以对于那些高级的优化算法 假设 我们给变量 θ 传递一些初始值 现在让我们考虑梯度下降 同样 我们需要把 θ 初始化成一些值 接下来使用梯度下降方法 慢慢地执行这些步骤使其下降 使 θ 的函数 J 下降到最小 那么 θ 的初始值该设置为多少呢? 是否可以 将 θ 的初始值设为 全部是0的向量 虽然说在逻辑回归时 初始化所有变量为0 是可行的的 但在训练神经网络时这样做是不可行的 以训练这个神经网络为例 照之前所说将所有变量初始化为0 如果是这样的话 具体来说就是 当初始化这条蓝色权重 使这条被涂为蓝色的权重等于那条蓝色的权重 他们都是0 这条被涂上红色的权重 同样等于 被涂上红色的这条权重 同样这个权重 这个被涂成绿色的权重也一样 等于那条绿色的权重 那么这就意味着这两个隐藏单元 a1 a2 是两个相同的关于 输入的函数 这样一来 对每个样本进行训练 最后a(2)1与a(2)2结果必然相等 更多的原因 我就不详细讲述了 而由于 这些权重相同 同样可以证明 这些 δ 值也相同 具体地说 δ(2)1=δ(2)2 δ(2)1=δ(2)2 同时 如果你更深入地挖掘一下 你不难得出 这些变量对参数的偏导数 满足以下条件 也就是 代价函数的 偏导数 关于 我用这两条 蓝色的权重为例 你不难发现 这两个偏导数互为相等 这也就意味着 一旦更新梯度下降方法 第一个蓝色权重也会更新 等于学习率乘以这个式子 第二条蓝色权重更新为 学习率乘上这个式子 但是 这就意味着 一旦更新梯度下降 这两条 蓝色权重的值 在最后将 将互为相等 因此 即使权重现在不都为0 但参数的值 最后也互为相等 同样地 即使更新一个梯度下降 这条红色的权重也会等于这条红色的权重 也许会有些非0的值 但两条红色的值会互为相等 同样两条绿色的权重 开始它们有不同的值 最后这两个权重也会互为相等 所以每次更新后 两个隐藏单元的输入对应的参数 将是相同的 这只是说 两条绿色的权重将一直相同 两条红色的权重将一直相同 两条蓝色的权重 仍然相同 这就意味着 即使经过一次循环后 梯度下降的循环后 你们会发现两个隐藏单元 仍然是两个完全相同的输入函数 因此 a(1)2 仍然等于 a(2)2 回到这里 一直持续运行梯度下降 这两条蓝色的权重仍然相同 两条红色的权重 两条绿色的权重 也是同样的情况 这也就意味着 这个神经网络 的确不能计算更有价值的东西 想象一下 不止有两个隐藏单元 而是 有很多很多的隐藏单元 这就是说 所有的隐藏单元 都在计算相同的特征 所有的隐藏单元都通过完全相同的输入函数计算出来 这是完全多余的表达 因为 这意味着 最后的逻辑回归单元只会得到一种特征 因为所有的逻辑回归单元都一样 这样便阻止了神经网络学习出更有价值的信息 为了解决这个问题 神经网络变量 初始化的方式 采用随机初始化 具体地说 在上一张幻灯片中看到的 所有权重相同的问题 有时被我们也称为对称权重 所以随机初始化 解决的就是如何打破这种对称性 所以 我们需要做的是 对 θ 的每个值 进行初始化 范围在 -ɛ 到 +ɛ 之间 这个方括号意味着 -ɛ 到 +ɛ 之间 因此 变量的权重通常初始化为 -ɛ 到 +ɛ 之间的任意一个数 我在 Octave 里编写了这样的代码 我之前讲过的 Theta1 等于这个等式 所以 这个 10×11 的随机矩阵 这个 rand 就是用来 得出一个任意的 10×11 维矩阵 矩阵中的所有值 都介于0到1之间 所以 这些实数 取0到1之间的连续值 因此 如果取0到1之间的一个数 和 2ε 相乘 再减去 ε 然后得到 一个在 -ε 到 +ε 的数 顺便说一句 这里的这个 ε 在进行梯度检查中用的 不是一回事 因此在进行数值梯度检查时 会加一些 ε 值给 θ 这些值与这里的ε 没有关系 这就是为什么我要在这里用 INIT_EPSILON 表示 仅仅是为了区分 在梯度检查中使用的 EPSILON 值 当然 类似的 如果想要初始化θ2 为任意一个1×11的矩阵 可以使用这里的这段代码 总结来说 为了训练神经网络 应该对权重进行随机初始化 初始化为 --ε到+ε间 接近于0的小数 然后进行反向传播 执行梯度检查 使用梯度下降 或者 使用高级的优化算法 试着使代价函数 J 达到最小 从某个随机选取的 参数 θ 开始 通过打破对称性的过程 我们希望梯度下降 或者其他高级优化算法 可以找到θ的最优值
Putting It Together
我们已经用了 几节视频的内容 来介绍神经网络算法 在这段视频中 我想结合我们所讲的 所有这些内容 来做一个总体的回顾 看看这些零散的内容 相互之间有怎样的联系 以及神经网络学习算法的 总体实现过程 当我们在训练一个神经网络时 我们要做的第一件事 就是搭建网络的大体框架 这里我说的框架 意思是 神经元之间的连接模式 我们可能会从以下几种结构中选择 第一种神经网络的结构是 包含三个输入单元 五个隐藏单元 和四个输出单元 第二种结构是 三个输入单元作为输入层 两组五个隐藏单元作为隐藏层 四个输出单元的输出层 然后第三种是3 5 5 5 其中每个隐藏层包含五个单元 然后是四个输出单元 这些就是可能选择的结构 每一层可以选择 多少个隐藏单元 以及可以选择多少个隐藏层 这些都是你构建时的选择 那么我们该如何做出选择呢? 首先 我们知道 我们已经定义了输入单元的数量 一旦你确定了特征集x 对应的输入单元数目 也就确定了 也就是等于特征x{i}的维度 输入单元数目将会由此确定 如果你正在进行 多类别分类 那么输出层的单元数目 将会由你分类问题中 所要区分的类别个数确定 值得提醒的是 如果你的多元分类问题 y的取值范围 是在1到10之间 那么你就有10个可能的分类 别忘了把你的y 重新写成向量的形式 所以现在我们的y不是一个数了 我们重新把y写成 这种形式的向量 第二个分类我们可以写成这样的向量 所以 比如说 如果要表达 第五个分类 也就是说y等于5 那么在你的神经网络中 就不能直接用 数值5来表达 因为这里的输出层 有十个输出单元 你应该用一个向量 来表示 这个向量的第五个位置值是1 其它的都是0 所以对于输入单元 和输出单元数目的选择 还是比较容易理解的 而对于隐藏单元的个数 单元的个数 以及隐藏层的数目 我们有一个默认的规则 那就是只使用单个隐藏层 所以最左边所示的 这种只有一个隐藏层的神经网络 一般来说是最普遍的 或者如果你使用 不止一个隐藏层的话 同样我们也有一个默认规则 那就是每一个隐藏层 通常都应有相同的单元数 所以对于这个结构 我们有两个隐藏层 每个隐藏层都有相同的单元数 都是5个隐藏单元 这里也是一样 我们有三个隐藏层 每个隐藏层有相同的单元数 都是5个隐藏单元 但实际上通常来说 左边这个结构是较为合理的默认结构 而对于隐藏单元的个数 通常情况下 隐藏单元越多越好 不过 我们需要注意的是 如果有大量隐藏单元 计算量一般会比较大 当然 一般来说隐藏单元还是越多越好 并且一般来说 每个隐藏层 所包含的单元数量 还应该和输入x 的维度相匹配 也要和特征的数目匹配 可能隐藏单元的数目 和输入特征的数量相同 或者是它的二倍 或者三倍 四倍 因此 隐藏单元的数目需要和其他参数相匹配 一般来说 隐藏单元的数目取为稍大于 输入特征数目 都是可以接受的 希望这些能够给你 在选择神经网络结构时 提供一些有用的建议和选择的参考 如果你遵循了这些建议 你一般会得到比较好的模型结构 但是 在以后的一系列视频中 特别是在我谈到 学习算法的应用时 我还会更详细地介绍 如何选择神经网络的结构 后面的视频中 我还会着重介绍 怎样正确地选择隐藏层的个数 以及隐藏单元的数目 等等 下面我们就来具体介绍 如何实现神经网络的 训练过程 这里一共有六个步骤 这页幻灯片中罗列了前四步 剩下的两步 放在下一张幻灯片中 首先 第一步是构建一个 神经网络 然后随机初始化权值 通常我们把权值 初始化为很小的值 接近于零 然后我们执行前向传播算法 也就是 对于该神经网络的 任意一个输入x(i) 计算出对应的h(x)值 也就是一个输出值y的向量 接下来我们通过代码 计算出代价函数J(θ) 然后我们执行 反向传播算法 来算出这些偏导数 或偏微分项 也就是 J(θ)关于参数θ的偏微分 具体来说 我们要对所有训练集数据 使用一个for循环进行遍历 可能有部分同学之前听说过 一些比较先进的分解方法 可能不需要像这里一样使用 for循环来对所有 m个训练样本进行遍历 但是 这是你第一次进行反向传播算法 所以我建议你最好还是 使用一个for循环来完成程序 对每一个训练样本进行迭代 从x(1) y(1)开始 我们对第一个样本进行 前向传播运算和反向传播运算 然后在第二次循环中 同样地对第二个样本 执行前向传播和反向传播算法 以此类推 直到最后一个样本 因此 在你第一次做反向传播的时候 你还是应该用这样的for循环 来实现这个过程 其实实际上 有复杂的方法可以实现 并不一定要使用for循环 但我非常不推荐 在第一次实现反向传播算法的时候 使用更复杂更高级的方法 所以具体来讲 我们对所有的 m个训练样本上使用了for循环遍历 在这个for循环里 我们对每个样本执行 前向和反向算法 具体来说就是 我们把x(i) 传到输入层 然后执行前向传播和反向传播 这样我们就能得到 该神经网络中 每一层中每一个单元对应的 所有这些激励值a(l) 和delta项 接下来 还是在for循环中 让我画一个大括号 来标明这个 for循环的范围 当然这些是octave的代码 括号里是for循环的循环体 我们要计算出这些delta值 也就是用我们之前给出的公式 加上 delta(l+1) a(l)的转置矩阵 最后 外面的部分 计算出的这些delta值 这些累加项 我们将用别的程序 来计算出 这些偏导数项 那么这些偏导数项 也应该考虑使用 正则化项lambda值 这些公式在前面的视频中已经给出 那么 搞定所有这些内容 现在你就应该已经得到了 计算这些偏导数项的程序了 下面就是第五步了 我要做的就是使用梯度检查 来比较这些 已经计算得到的偏导数项 把用反向传播算法 得到的偏导数值 与用数值方法得到的 估计值进行比较 因此 通过进行梯度检查来 确保两种方法得到基本接近的两个值 通过梯度检查我们能确保 我们的反向传播算法 得到的结果是正确的 但必须要说明的一点是 我们需要去掉梯度检查的代码 因为梯度检查的计算非常慢 最后 我们就可以 使用一个最优化算法 比如说梯度下降算法 或者说是更加高级的优化方法 比如说BFGS算法 共轭梯度法 或者其他一些已经内置到fminunc函数中的方法 将所有这些优化方法 和反向传播算法相结合 这样我们就能计算出 这些偏导数项的值 到现在 我们已经知道了 如何去计算代价函数 我们知道了如何使用 反向传播算法来计算偏导数 那么 我们就能使用某个最优化方法 来最小化关于theta的函数值 代价函数J(θ) 另外顺便提一下 对于神经网络 代价函数 J(θ)是一个非凸函数 就是说不是凸函数 因此理论上是能够停留在 局部最小值的位置 实际上 梯度下降算法 和其他一些高级优化方法 理论上都能收敛于局部最小值 但一般来讲 这个问题其实 并不是什么要紧的事 尽管我们不能保证 这些优化算法一定会得到 全局最优值 但通常来讲 像梯度下降这类的算法 在最小化代价函数 J(θ)的过程中 还是表现得很不错的 通常能够得到一个很小的局部最小值 尽管这可能不一定是全局最优值 最后 梯度下降算法 似乎对于神经网络来说还是比较神秘 希望下面这幅图 能让你对梯度下降法在神经网络中的应用 产生一个更直观的理解 这实际上有点类似 我们早先时候解释梯度下降时的思路 我们有某个代价函数 并且在我们的神经网络中 有一系列参数值 这里我只写下了两个参数值 当然实际上 在神经网络里 我们可以有很多的参数值 theta1 theta2 等等 所有的这些都是矩阵 是吧 因此我们参数的维度就会很高了 由于绘图所限 我们不能绘出 更高维度情况的图像 所以这里我们假设 这个神经网络中只有两个参数值 实际上应该有更多参数 那么 代价函数J(θ) 度量的就是这个神经网络 对训练数据的拟合情况 所以 如果你取某个参数 比如说这个 下面这点 在这个点上 J(θ) 的值是非常小的 这一点的位置所对应的 参数theta的情况是 对于大部分 的训练集数据 我的假设函数的输出 会非常接近于y(i) 那么如果是这样的话 那么我们的代价函数值就会很小 而反过来 如果我们 取这个值 也就是这个点对应的值 那么对于大部分的训练集样本 该神经网络的输出 应该是远离 y(i)的实际值的 也就是我们在训练集观测到的输出值 因此 像这样的点 右边的这个点 对应的假设就是 神经网络的输出值 在这个训练集上的测试值 应该是远离y(i)的 因此这一点对应着对训练集拟合得不好的情况 而像这些点 代价函数值很小的点 对应的J(θ)值 是很小的 因此对应的是 神经网络对训练集数据 拟合得比较好的情况 我想表达的是 如果是这种情况的话 那么J(θ)的值应该是比较小的 因此梯度下降算法的原理是 我们从某个随机的 初始点开始 比如这一点 它将会不停的往下下降 那么反向传播算法 的目的就是算出 梯度下降的方向 而梯度下降的过程 就是沿着这个方向 一点点的下降 一直到我们希望得到的点 在这里我们希望找到的就是局部最优点 所以 当你在执行反向传播算法 并且使用梯度下降 或者 更高级的优化方法时 这幅图片很好地帮你解释了基本的原理 也就是 试图找到某个最优的参数值 这个值使得 我们神经网络的输出值 与y(i)的实际值 也就是训练集的输出观测值 尽可能的接近 希望这节课的内容能让你对 这些零散的神经网络知识 如何有机地结合起来 能有一个更直观的认识 但可能你即使看了这段视频 你可能还是觉得 有许多的细节 不能完全明白 为什么这么做 或者说是这些是如何 联系在一起的 没关系 神经网络和反向传播算法本身就是非常复杂的算法 尽管我已经完全理解了 反向传播算法背后的数学原理 尽管我使用反向传播已经很多年了 我认为 这么多年的使用还算是成功的 但尽管如此 到现在我还是觉得 我自己也并不是总能 很好地理解反向传播到底在做什么 以及最优化过程是如何 使J(θ)值达到最小值的 因为这本身的确是一个很难的算法 很难让你感觉到 自己已经完全理解 它不像线性回归 或者逻辑回归那样 数学上和概念上都很简单 反向传播算法不是那样的直观 如果你也有同感 那么完全不必担心 但如果你自己动手 完成一次反向传播算法 你一定会发现 这的确是一个很强大的 学习算法 如果你 执行一下这个算法 执行反向传播 执行其中的优化方法 你一定会发现 反向传播算法能够很好的 让更复杂 维度更大的 非线性的 函数模型跟你的数据很好地拟合 因此它的确是一种 最为高效的学习算法
Application of Neural Networks
Autonomous Driving
在这段视频中 我想向你介绍一个具有历史意义的 神经网络学习的重要例子 那就是使用神经网络来实现自动驾驶 也就是说使汽车通过学习来自己驾驶 接下来我将演示的 这段视频 是我从 Dean Pomerleau那里拿到的 他是我的同事 任职于美国东海岸的 卡耐基梅隆大学 在这部分视频中 你就会明白可视化技术到底是什么 在看这段视频之前 我会告诉你可视化技术是什么 在下面 也就是左下方 就是汽车所看到的 前方的路况图像 在图中你依稀能看出 一条道路 朝左延伸了一点 又向右了一点 然后上面的这幅图 你可以看到一条 水平的菜单栏 显示的是驾驶操作人 所选择的方向 就是这里的 这条白亮的区段 显示的就是 人类驾驶者选择的方向 比如 最左边的区段 对应的操作就是向左急转 而最右端则对应 向右急转的操作 因此 稍微靠左的区段 也就是这里 中心稍微向左一点的位置 则表示在这一点上 人类驾驶者的操作是 慢慢的向左拐 这幅图的第二部分 对应的就是 学习算法选出的行驶方向 并且 类似的 这一条白亮的区段 显示的就是 神经网络在这里 选择的行驶方向 是稍微的左转 并且实际上 在神经网络开始 学习之前 你会看到网络的输出是 一条灰色的区段 就像这样的一条灰色区段 覆盖着整个区域 这些均称的 灰色区域显示出 神经网络已经随机初始化了 并且初始化时 我们并不知道 汽车如何行驶 或者说 我们并不知道所选行驶方向 只有在学习算法运行了 足够长的时间之后 才会有这条白色的区段 出现在整条灰色区域之中 显示出一个具体的 行驶方向 这就表示神经网络算法 在这时候已经选出了一个 明确的行驶方向 不像刚开始的时候 输出一段模糊的浅灰色区域 而是输出 一条白亮的区段 表示已经选出了明确的行驶方向 ALVINN (Autonomous Land Vehicle In a Neural Network) 是一个基于神经网络的智能系统 通过观察人类的驾驶来学习驾驶 ALVINN能够控制NavLab载具—— 一辆改装版军用悍马 这辆悍马装载了 传感器 计算机和驱动器 用来进行自动驾驶的导航试验 实现ALVINN功能的第一步 是对它进行训练 也就是训练一个人驾驶汽车 然后让ALVINN观看 ALVINN每两秒 将前方的路况图生成一张数字化图片 并且记录驾驶者的驾驶方向 得到的训练集图片 被压缩为30x32像素 并且作为输入 提供给ALVINN的三层 神经网络 通过使用反向传播学习算法 ALVINN会训练得到一个 与人类驾驶员操纵方向 基本相近的结果 一开始 我们的网络选择出的方向是随机的 大约经过两分钟的训练后 我们的神经网络 便能够准确地模拟 人类驾驶者的 驾驶方向 对其他道路类型 也重复进行这个训练过程 当网络被训练完成后 操作者就可按下运行按钮 车辆便开始行驶 每秒钟ALVINN生成 12次数字化图片 并且将图像传送给神经网络进行训练 多个神经网络同时工作 每一个网络都生成一个行驶方向 以及一个预测自信度的参数 预测自信度最高的 那个神经网络得到的行驶方向 比如这里 在这条单行道上训练出的网络 将被最终用于控制车辆方向 车辆前方突然出现了 一个交叉十字路口 当车辆到达这个十字路口时 我们单行道网络对应的自信度骤减 当它穿过这个十字路口时 前方的双车道将进入其视线 双车道网络的自信度便开始上升 当它的自信度上升时 双车道的网络 将被选择来控制行驶方向 车辆将被安全地引导 进入双车道路 这就是基于神经网络的 自动驾驶技术 当然 我们还有很多 更加先进的试验 来实现自动驾驶技术 在美国 欧洲等一些国家和地区 他们提供了一些比这个方法 更加稳定的驾驶控制技术 但我认为 使用这样一个简单的 基于反向传播的神经网络 训练出如此强大的自动驾驶汽车 的确是一次令人惊讶的成就