神经网络向量化
From Ufldl
Line 1: | Line 1: | ||
在本节,我们将引入神经网络的向量化版本。在前面关于[[神经网络]]介绍的章节中,我们已经给出了一个部分向量化的实现,它在一次输入一个训练样本时是非常有效率的。下边我们看看如何实现同时处理多个训练样本的算法。具体来讲,我们将把正向传播、反向传播这两个步骤以及稀疏特征集学习扩展为多训练样本版本。 | 在本节,我们将引入神经网络的向量化版本。在前面关于[[神经网络]]介绍的章节中,我们已经给出了一个部分向量化的实现,它在一次输入一个训练样本时是非常有效率的。下边我们看看如何实现同时处理多个训练样本的算法。具体来讲,我们将把正向传播、反向传播这两个步骤以及稀疏特征集学习扩展为多训练样本版本。 | ||
+ | |||
==正向传播== | ==正向传播== | ||
Line 11: | Line 12: | ||
h_{W,b}(x) &= a^{(3)} = f(z^{(3)}) | h_{W,b}(x) &= a^{(3)} = f(z^{(3)}) | ||
\end{align}</math> | \end{align}</math> | ||
+ | |||
这对于单一训练样本而言是非常有效的一种实现,但是当我们需要处理<tt>m</tt>个训练样本时,则需要把如上步骤放入一个<tt>for</tt>循环中。 | 这对于单一训练样本而言是非常有效的一种实现,但是当我们需要处理<tt>m</tt>个训练样本时,则需要把如上步骤放入一个<tt>for</tt>循环中。 | ||
Line 25: | Line 27: | ||
end; | end; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
这个<tt>for</tt>循环能否去掉呢?对于很多算法而言,我们使用向量来表示计算过程中的中间结果。例如在前面的非向量化实现中,<tt>z2</tt>,<tt>a2</tt>,<tt>z3</tt>都是列向量,分别用来计算隐层和输出层的激励结果。为了充分利用并行化和高效矩阵运算的优势,我们希望算法能同时处理多个训练样本。让我们先暂时忽略前面公式中的<tt>b1</tt>和<tt>b2</tt>(把它们设置为0),那么可以实现如下: | 这个<tt>for</tt>循环能否去掉呢?对于很多算法而言,我们使用向量来表示计算过程中的中间结果。例如在前面的非向量化实现中,<tt>z2</tt>,<tt>a2</tt>,<tt>z3</tt>都是列向量,分别用来计算隐层和输出层的激励结果。为了充分利用并行化和高效矩阵运算的优势,我们希望算法能同时处理多个训练样本。让我们先暂时忽略前面公式中的<tt>b1</tt>和<tt>b2</tt>(把它们设置为0),那么可以实现如下: | ||
Line 35: | Line 38: | ||
h = f(z3) | h = f(z3) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
在这个实现中,<tt>z2</tt>,<tt>a2</tt>,<tt>z3</tt>都是矩阵,每个训练样本对应矩阵的一列。在对多个训练样本实现向量化时常用的设计模式是,虽然前面每个样本对应一个列向量(比如<tt>z2</tt>),但我们可把这些列向量堆叠成一个矩阵以充分享受矩阵运算带来的好处。这样,在这个例子中,<tt>a2</tt>就成了一个<math>s_2</math> X <math>m</math>的矩阵(<math>s_2</math>是网络第二层中的神经元数,<math>m</math>是训练样本个数)。矩阵<tt>a2</tt>的物理含义是,当第<math>i</math>个训练样本<tt>x(:i)</tt>输入到网络中时,它的第<math>i</math>列就表示这个输入信号对隐神经元 (网络第二层)的激励结果。 | 在这个实现中,<tt>z2</tt>,<tt>a2</tt>,<tt>z3</tt>都是矩阵,每个训练样本对应矩阵的一列。在对多个训练样本实现向量化时常用的设计模式是,虽然前面每个样本对应一个列向量(比如<tt>z2</tt>),但我们可把这些列向量堆叠成一个矩阵以充分享受矩阵运算带来的好处。这样,在这个例子中,<tt>a2</tt>就成了一个<math>s_2</math> X <math>m</math>的矩阵(<math>s_2</math>是网络第二层中的神经元数,<math>m</math>是训练样本个数)。矩阵<tt>a2</tt>的物理含义是,当第<math>i</math>个训练样本<tt>x(:i)</tt>输入到网络中时,它的第<math>i</math>列就表示这个输入信号对隐神经元 (网络第二层)的激励结果。 | ||
Line 56: | Line 60: | ||
end | end | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
最后,我们上面的正向传播向量化实现中忽略了<tt>b1</tt>和<tt>b2</tt>,现在要把他们包含进来,为此我们需要用到Matlab/Octave的内建函数<tt>repmat</tt>: | 最后,我们上面的正向传播向量化实现中忽略了<tt>b1</tt>和<tt>b2</tt>,现在要把他们包含进来,为此我们需要用到Matlab/Octave的内建函数<tt>repmat</tt>: | ||
Line 76: | Line 81: | ||
\end{bmatrix}. | \end{bmatrix}. | ||
</math> | </math> | ||
+ | |||
这就构成一个<math>s_2</math> X <math>m</math>的矩阵。它和<tt>W1 * x</tt>相加,就等于是把<tt>W1 * x</tt>矩阵(译者注:这里<tt>x</tt>是训练矩阵而非向量, 所以<tt>W1 * x</tt>代表两个矩阵相乘,结果还是一个矩阵)的每一列加上<tt>b1</tt>。如果不熟悉的话,可以参考Matlab/Octave的帮助文档获取更多信息(输入“<tt>help repmat</tt>”)。<tt>rampat</tt>作为Matlab/Octave的内建函数,运行起来是相当高效的,远远快过我们自己用<tt>for</tt>循环实现的效果。 | 这就构成一个<math>s_2</math> X <math>m</math>的矩阵。它和<tt>W1 * x</tt>相加,就等于是把<tt>W1 * x</tt>矩阵(译者注:这里<tt>x</tt>是训练矩阵而非向量, 所以<tt>W1 * x</tt>代表两个矩阵相乘,结果还是一个矩阵)的每一列加上<tt>b1</tt>。如果不熟悉的话,可以参考Matlab/Octave的帮助文档获取更多信息(输入“<tt>help repmat</tt>”)。<tt>rampat</tt>作为Matlab/Octave的内建函数,运行起来是相当高效的,远远快过我们自己用<tt>for</tt>循环实现的效果。 | ||
+ | |||
现在我们来描述反向传播向量化的思路。在阅读这一节之前,强烈建议各位仔细阅读前面介绍的正向传播的例子代码,确保你已经完全理解。下边我们只会给出反向传播向量化实现的大致纲要,而由你来完成具体细节的推导(见[[Exercise:Vectorization|向量化练习]])。 | 现在我们来描述反向传播向量化的思路。在阅读这一节之前,强烈建议各位仔细阅读前面介绍的正向传播的例子代码,确保你已经完全理解。下边我们只会给出反向传播向量化实现的大致纲要,而由你来完成具体细节的推导(见[[Exercise:Vectorization|向量化练习]])。 | ||
Line 96: | Line 103: | ||
\end{align} | \end{align} | ||
</math> | </math> | ||
+ | |||
在这里<math>\bullet</math>表示对两个向量按对应元素相乘的运算(译者注:其结果还是一个向量)。为了描述简单起见,我们这里暂时忽略对参数<math>b^{(l)}</math>的求导,不过在你真正实现反向传播时,还是需要计算关于它们的导数的。 | 在这里<math>\bullet</math>表示对两个向量按对应元素相乘的运算(译者注:其结果还是一个向量)。为了描述简单起见,我们这里暂时忽略对参数<math>b^{(l)}</math>的求导,不过在你真正实现反向传播时,还是需要计算关于它们的导数的。 | ||
Line 112: | Line 120: | ||
end; | end; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
在这个实现中,有一个<tt>for</tt>循环。而我们想要一个能同时处理所有样本、且去除这个<tt>for</tt>循环的向量化版本。 | 在这个实现中,有一个<tt>for</tt>循环。而我们想要一个能同时处理所有样本、且去除这个<tt>for</tt>循环的向量化版本。 | ||
Line 128: | Line 137: | ||
+ \beta \left( - \frac{\rho}{\hat\rho_i} + \frac{1-\rho}{1-\hat\rho_i} \right) \right) f'(z^{(2)}_i) . | + \beta \left( - \frac{\rho}{\hat\rho_i} + \frac{1-\rho}{1-\hat\rho_i} \right) \right) f'(z^{(2)}_i) . | ||
\end{align}</math> | \end{align}</math> | ||
+ | |||
在非向量化的实现中,计算代码如下: | 在非向量化的实现中,计算代码如下: | ||
Line 140: | Line 150: | ||
end; | end; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
但在上面的代码中,仍旧含有一个需要在整个训练集上运行的<tt>for</tt>循环,这里<tt>delta2</tt>是一个列向量。 | 但在上面的代码中,仍旧含有一个需要在整个训练集上运行的<tt>for</tt>循环,这里<tt>delta2</tt>是一个列向量。 | ||
作为对照,回想一下在向量化的情况下,<tt>delta2</tt>现在应该是一个有m列的矩阵,分别对应着<math>m</math>个训练样本。还要注意,稀疏惩罚项<tt>sparsity_delta</tt>对所有的训练样本一视同仁。这意味着要向量化实现上面的计算,只需在构造<tt>delta2</tt>时,往矩阵的每一列上分别加上相同的值即可。因此,要向量化上面的代码,我们只需简单的用<tt>repmat</tt>命令把<tt>sparsity_delta</tt>加到<tt>delta2</tt>的每一列上即可(译者注:这里原文描述得不是很清楚,看似应加到上面代码中<tt>delta2</tt>行等号右边第一项,即<tt>W2'*delta3</tt>上)。 | 作为对照,回想一下在向量化的情况下,<tt>delta2</tt>现在应该是一个有m列的矩阵,分别对应着<math>m</math>个训练样本。还要注意,稀疏惩罚项<tt>sparsity_delta</tt>对所有的训练样本一视同仁。这意味着要向量化实现上面的计算,只需在构造<tt>delta2</tt>时,往矩阵的每一列上分别加上相同的值即可。因此,要向量化上面的代码,我们只需简单的用<tt>repmat</tt>命令把<tt>sparsity_delta</tt>加到<tt>delta2</tt>的每一列上即可(译者注:这里原文描述得不是很清楚,看似应加到上面代码中<tt>delta2</tt>行等号右边第一项,即<tt>W2'*delta3</tt>上)。 | ||
+ | |||
+ | |||
+ | {{Vectorized Implementation}} | ||
+ | |||
+ | |||
+ | ==中文译者== | ||
+ | |||
+ | @ztyan, @谭晓阳_南航, @邓亚峰-人脸识别 |