非监督特征学习与深度学习(八)----神经网络向量化

来源:互联网 发布:mysql to_date 编辑:程序博客网 时间:2024/05/22 00:55

注:本文转载自https://github.com/ysh329/Chinese-UFLDL-Tutorial
因为github上的makedown格式显示的不够完全,看的非常不方便,因此放到CSDN上比较好查阅学习。

神经网络向量化(Neural Network Vectorization)

注:本章节翻译完全参考旧版 UFLDL 中文教程。

在本节,我们将引入神经网络的向量化版本。在前面关于神经网络介绍的章节中,我们已经给出了一个部分向量化的实现,它在一次输入一个训练样本时是非常有效率的。下边我们看看如何实现同时处理多个训练样本的算法。具体来讲,我们将把正向传播、反向传播这两个步骤以及稀疏特征集学习扩展为多训练样本版本。

正向传播( Forward propagation )

考虑一个三层网络(一个输入层、一个隐含层、以及一个输出层),并且假定 x 是包含一个单一训练样本 x(i)Rn 的列向量。则向量化的正向传播步骤如下:

z(2)a(2)z(3)hW,b(x)=W(1)x+b(1)=f(z(2))=W(2)a(2)+b(2)=a(3)=f(z(3))

这对于单一训练样本而言是非常有效的一种实现,但是当我们需要处理 m 个训练样本时,则需要把如上步骤放入一个 for 循环中。

更具体点来说,参照逻辑回归向量化的例子,我们用 Matlab/Octave 风格变量 x 表示包含输入训练样本的矩阵, x(:,i) 代表第 i 个训练样本。则x正向传播步骤可如下实现:

% 非向量化实现for i=1:m,   z2 = W1 * x(:,i) + b1;  a2 = f(z2);  z3 = W2 * a2 + b2;  h(:,i) = f(z3);end;

这个 for 循环能否去掉呢?对于很多算法而言,我们使用向量来表示计算过程中的中间结果。例如在前面的非向量化实现中, z2,a2,z3 都是列向量,分别用来计算隐层和输出层的激励结果。为了充分利用并行化和高效矩阵运算的优势,我们希望算法能同时处理多个训练样本。让我们先暂时忽略前面公式中的 b1b2 (把它们设置为 0 ),那么可以实现如下:

% 向量化实现 (忽略 b1, b2)z2 = W1 * x;a2 = f(z2);z3 = W2 * a2;h = f(z3)

在这个实现中, z2,a2,z3 都是矩阵,每个训练样本对应矩阵的一列。在对多个训练样本实现向量化时常用的设计模式是,虽然前面每个样本对应一个列向量(比如 z2 ),但我们可把这些列向量堆叠成一个矩阵以充分享受矩阵运算带来的好处。这样,在这个例子中, a2 就成了一个 s2×m 的矩阵( s2 是网络第二层中的神经元数, m 是训练样本个数)。矩阵 a2 的物理含义是,当第 i 个训练样本 x(:i) 输入到网络中时,它的第 i 列就表示这个输入信号对隐神经元(网络第二层)的激励结果。

在上面的实现中,我们假定激活函数 f(z) 接受矩阵形式的输入 z ,并对输入矩阵按列分别施以激活函数。需要注意的是,你在实现 f(z) 的时候要尽量多用 Matlab/Octave 的矩阵操作,并尽量避免使用 for 循环。假定激活函数采用 Sigmoid 函数,则实现代码如下所示:

% 低效的、非向量化的激活函数实现function output = unvectorized_f(z)output = zeros(size(z))for i=1:size(z,1),   for j=1:size(z,2),    output(i,j) = 1/(1+exp(-z(i,j)));  end; end;end% 高效的、向量化激活函数实现function output = vectorized_f(z)output = 1./(1+exp(-z));     % "./" 在Matlab或Octave中表示对矩阵的每个元素分别进行除法操作end

最后,我们上面的正向传播向量化实现中忽略了 b1b2 ,现在要把他们包含进来,为此我们需要用到 Matlab/Octave 的内建函数 repmat :

% 正向传播的向量化实现z2 = W1 * x + repmat(b1,1,m);a2 = f(z2);z3 = W2 * a2 + repmat(b2,1,m);h = f(z3)

repmat(b1,1,m) 的运算效果是,它把列向量 b1 拷贝 m 份,然后堆叠成如下矩阵:

|b1||b1||b1|.

这就构成一个 s2×m 的矩阵。它和 W1x 相加,就等于是把 W1x 矩阵(译者注:这里 x 是训练矩阵而非向量, 所以 W1x 代表两个矩阵相乘,结果还是一个矩阵)的每一列加上 b1 。如果不熟悉的话,可以参考 Matlab/Octave 的帮助文档获取更多信息(输入 “help repmat” )。 repmat 作为 Matlab/Octave 的内建函数,运行起来是相当高效的,远远快过我们自己用 for 循环实现的效果。

反向传播(Backpropagation)

现在我们来描述反向传播向量化的思路。在阅读这一节之前,强烈建议各位仔细阅读前面介绍的正向传播的例子代码,确保你已经完全理解。下边我们只会给出反向传播向量化实现的大致纲要,而由你来完成具体细节的推导(见向量化练习)。

对于监督学习,我们有一个包含 m 个带类别标号样本的训练集 {(x(1),y(1)),,(x(m),y(m))} 。(对于自编码网络,我们只需令 y(i)=x(i) 即可,但这里考虑的是更一般的情况。)

假定网络的输出有 s3 维,因而每个样本的类别标号向量就记为 y(i)Rs3 。在我们的 Matlab/Octave 数据结构实现中,把这些输出按列合在一起形成一个 Matlab/Octave 风格变量 y ,其中第 iy(:,i) 就是 y(i)

现在我们要计算梯度项 W(l)J(W,b)b(l)J(W,b) 。对于梯度中的第一项,就像过去在反向传播算法中所描述的那样,对于每个训练样本 (x,y) ,我们可以这样来计算:

δ(3)δ(2)W(2)J(W,b;x,y)W(1)J(W,b;x,y)=(ya(3))f(z(3)),=((W(2))Tδ(3))f(z(2)),=δ(3)(a(2))T,=δ(2)(a(1))T.

在这里 表示对两个向量按对应元素相乘的运算(译者注:其结果还是一个向量)。为了描述简单起见,我们这里暂时忽略对参数 b(l) 的求导,不过在你真正实现反向传播时,还是需要计算关于它们的导数的。

假定我们已经实现了向量化的正向传播方法,如前面那样计算了矩阵形式的变量 z2,a2,z3h ,那么反向传播的非向量化版本可如下实现:

gradW1 = zeros(size(W1));gradW2 = zeros(size(W2)); for i=1:m,  delta3 = -(y(:,i) - h(:,i)) .* fprime(z3(:,i));   delta2 = W2'*delta3(:,i) .* fprime(z2(:,i));  gradW2 = gradW2 + delta3*a2(:,i)';  gradW1 = gradW1 + delta2*a1(:,i)'; end;

在这个实现中,有一个 for 循环。而我们想要一个能同时处理所有样本、且去除这个 for 循环的向量化版本。

为做到这一点,我们先把向量 delta3delta2 替换为矩阵,其中每列对应一个训练样本。我们还要实现一个函数 fprime(z) ,该函数接受矩阵形式的输入 z ,并且对矩阵的按元素分别执行 f() 。这样,上面 for 循环中的 4 行 Matlab 代码中每行都可单独向量化,以一行新的(向量化的) Matlab 代码替换它(不再需要外层的 for 循环)。

在向量化练习中,我们要求你自己去推导出这个算法的向量化版本。如果你已经能从上面的描述中了解如何去做,那么我们强烈建议你去实践一下。虽然我们已经为你准备了反向传播的向量化实现提示,但还是鼓励你在不看提示的情况下自己去推导一下。

稀疏自编码网络(Sparse autoencoder )

稀疏自编码网络中包含一个额外的稀疏惩罚项,目的是限制神经元的平均激活率,使其接近某个(预设的)目标激活率 ρ 。其实在对单个训练样本上执行反向传播时,我们已经考虑了如何计算这个稀疏惩罚项,如下所示:

δ(2)i=j=1s2W(2)jiδ(3)j+β(ρρ^i+1ρ1ρ^i)f(z(2)i).

在非向量化的实现中,计算代码如下:

% 稀疏惩罚Deltasparsity_delta = - rho ./ rho_hat + (1 - rho) ./ (1 - rho_hat);for i=1:m,  ...  delta2 = (W2'*delta3(:,i) + beta*sparsity_delta).* fprime(z2(:,i));   ...end;

但在上面的代码中,仍旧含有一个需要在整个训练集上运行的 for 循环,这里 delta2 是一个列向量。

作为对照,回想一下在向量化的情况下, delta2 现在应该是一个有 m 列的矩阵,分别对应着 m 个训练样本。还要注意,稀疏惩罚项 sparsity_delta 对所有的训练样本一视同仁。这意味着要向量化实现上面的计算,只需在构造 delta2 时,往矩阵的每一列上分别加上相同的值即可。因此,要向量化上面的代码,我们只需简单的用 repmat 命令把 sparsity_delta 加到 delta2 的每一列上即可(译者注:这里原文描述得不是很清楚,看似应加到上面代码中 delta2 行等号右边第一项,即 W2delta3 上)。

练习: 监督神经网络(Exercise: Supervised Neural Networks)

本次练习中,您将训练一个神经网络分类器,并在 MNIST 数据集上对 10 类的手写数字图像分类。神经网络的输出单元与您在 Softmax 回归 练习中创建的是相同的。仅使用 Softmax 回归的函数去拟合训练集效果并不会很好,其中一个原因是 欠拟合(underfitting

相比之下,有着更低偏差( bias )的神经网络应能更好地拟合训练集。在 多层神经网络 这一节中,网络参数的梯度是使用反向传播算法对所有参数计算平方误差形式的损失函数(译者注:损失函数,即代价函数)得到的。在本次练习中,需要用到在 Softmax 回归(交叉熵)形式的成本函数,而不是平方误差形式的代价函数。

神经网络的代价函数与 Softmax 回归的代价函数基本一样。需要注意的是,与从输入数据 x 做预测不同, Softmax 函数把网络 hW,b(x) 的隐含层的最后一层作为输入。其损失函数为:

J(θ)=i=1mk=1K1{y(i)=k}logexp(θ(k)hW,b(x(i)))Kj=1exp(θ(j)hW,b(x)(i))).

神经网络和 Softmax 回归在成本函数上的不同,会导致在对输出层 δ(nl) 的误差项上二者计算出的值不同。 Softmax (交叉熵)代价为:

δ(nl)=i=1m[(1{y(i)=k}P(y(i)=k|x(i);θ))]

使用这一项,您可以得到计算所有网络参数梯度的完整反向传播算法。

用前文给出的初学者代码,创建神经网络的前向传播代价函数,并计算其梯度。先前,用 minFunc 优化包来做基于梯度的优化。记得您要对梯度计算的结果进行数值检查。您的实现应该支持多隐含层的神经网络训练。当您在代码实现时,请遵循下面的操作要点:

  • 实现一层隐含层的网络,并做梯度检查。在梯度检查时,您也许会想通过裁剪训练数据的矩阵,来减少输入维度和样本数量。梯度检查时,您可以使用较小数量的隐单元以减少计算时间。
  • 实现两个隐含层网络的梯度检查。
  • 训练并测试不同的网络架构。您可以实现在一层上有256个隐含单元的隐含层,该结构可以达到在训练集上 100% 的精度。因为有很多参数,所以存在过拟合的风险。通过对不同的层数,隐含层数,以及权重衰减惩罚值的实验,来进一步理解什么样的架构表现最好。您能找到一个优于您最好的单隐含层架构的多隐含层网络吗?
  • (可选)扩展您的代码使其支持多种非线性隐含单元的选择( S 型函数,双曲正切 tanh 函数和 ReLU 函数)。
阅读全文
0 0
原创粉丝点击