注:本文转载自https://github.com/ysh329/Chinese-UFLDL-Tutorial
因为github上的makedown格式显示的不够完全,看的非常不方便,因此放到CSDN上比较好查阅学习。
神经网络向量化(Neural Network Vectorization)
注:本章节翻译完全参考旧版 UFLDL 中文教程。
在本节,我们将引入神经网络的向量化版本。在前面关于神经网络介绍的章节中,我们已经给出了一个部分向量化的实现,它在一次输入一个训练样本时是非常有效率的。下边我们看看如何实现同时处理多个训练样本的算法。具体来讲,我们将把正向传播、反向传播这两个步骤以及稀疏特征集学习扩展为多训练样本版本。
正向传播( Forward propagation )
考虑一个三层网络(一个输入层、一个隐含层、以及一个输出层),并且假定
x
是包含一个单一训练样本
这对于单一训练样本而言是非常有效的一种实现,但是当我们需要处理 m 个训练样本时,则需要把如上步骤放入一个 for 循环中。
更具体点来说,参照逻辑回归向量化的例子,我们用 Matlab/Octave 风格变量
% 非向量化实现
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 都是列向量,分别用来计算隐层和输出层的激励结果。为了充分利用并行化和高效矩阵运算的优势,我们希望算法能同时处理多个训练样本。让我们先暂时忽略前面公式中的 b1 和 b2 (把它们设置为 0 ),那么可以实现如下:
% 向量化实现 (忽略 b1, b2)
z2 = W1 * x;
a2 = f(z2);
z3 = W2 * a2;
h = f(z3)
在这个实现中,
在上面的实现中,我们假定激活函数
% 低效的、非向量化的激活函数实现
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
最后,我们上面的正向传播向量化实现中忽略了 b1 和 b2 ,现在要把他们包含进来,为此我们需要用到 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 份,然后堆叠成如下矩阵:
这就构成一个
s2×m
的矩阵。它和
W1∗x
相加,就等于是把
W1∗x
矩阵(译者注:这里
x
是训练矩阵而非向量, 所以
反向传播(Backpropagation)
现在我们来描述反向传播向量化的思路。在阅读这一节之前,强烈建议各位仔细阅读前面介绍的正向传播的例子代码,确保你已经完全理解。下边我们只会给出反向传播向量化实现的大致纲要,而由你来完成具体细节的推导(见向量化练习)。
对于监督学习,我们有一个包含
m
个带类别标号样本的训练集
假定网络的输出有
s3
维,因而每个样本的类别标号向量就记为
y(i)∈Rs3
。在我们的 Matlab/Octave 数据结构实现中,把这些输出按列合在一起形成一个 Matlab/Octave 风格变量
y
,其中第
现在我们要计算梯度项 ∇W(l)J(W,b) 和 ∇b(l)J(W,b) 。对于梯度中的第一项,就像过去在反向传播算法中所描述的那样,对于每个训练样本 (x,y) ,我们可以这样来计算:
在这里 ∙ 表示对两个向量按对应元素相乘的运算(译者注:其结果还是一个向量)。为了描述简单起见,我们这里暂时忽略对参数 b(l) 的求导,不过在你真正实现反向传播时,还是需要计算关于它们的导数的。
假定我们已经实现了向量化的正向传播方法,如前面那样计算了矩阵形式的变量 z2,a2,z3 和 h ,那么反向传播的非向量化版本可如下实现:
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 循环的向量化版本。
为做到这一点,我们先把向量
在向量化练习中,我们要求你自己去推导出这个算法的向量化版本。如果你已经能从上面的描述中了解如何去做,那么我们强烈建议你去实践一下。虽然我们已经为你准备了反向传播的向量化实现提示,但还是鼓励你在不看提示的情况下自己去推导一下。
稀疏自编码网络(Sparse autoencoder )
稀疏自编码网络中包含一个额外的稀疏惩罚项,目的是限制神经元的平均激活率,使其接近某个(预设的)目标激活率
在非向量化的实现中,计算代码如下:
% 稀疏惩罚Delta
sparsity_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
列的矩阵,分别对应着
练习: 监督神经网络(Exercise: Supervised Neural Networks)
本次练习中,您将训练一个神经网络分类器,并在 MNIST 数据集上对 10 类的手写数字图像分类。神经网络的输出单元与您在 Softmax 回归 练习中创建的是相同的。仅使用 Softmax 回归的函数去拟合训练集效果并不会很好,其中一个原因是 欠拟合( underfitting ) 。
相比之下,有着更低偏差( bias )的神经网络应能更好地拟合训练集。在 多层神经网络 这一节中,网络参数的梯度是使用反向传播算法对所有参数计算平方误差形式的损失函数(译者注:损失函数,即代价函数)得到的。在本次练习中,需要用到在 Softmax 回归(交叉熵)形式的成本函数,而不是平方误差形式的代价函数。
神经网络的代价函数与 Softmax 回归的代价函数基本一样。需要注意的是,与从输入数据
x
做预测不同, Softmax 函数把网络
神经网络和 Softmax 回归在成本函数上的不同,会导致在对输出层 δ(nl) 的误差项上二者计算出的值不同。 Softmax (交叉熵)代价为:
使用这一项,您可以得到计算所有网络参数梯度的完整反向传播算法。
用前文给出的初学者代码,创建神经网络的前向传播代价函数,并计算其梯度。先前,用 minFunc 优化包来做基于梯度的优化。记得您要对梯度计算的结果进行数值检查。您的实现应该支持多隐含层的神经网络训练。当您在代码实现时,请遵循下面的操作要点:
- 实现一层隐含层的网络,并做梯度检查。在梯度检查时,您也许会想通过裁剪训练数据的矩阵,来减少输入维度和样本数量。梯度检查时,您可以使用较小数量的隐单元以减少计算时间。
- 实现两个隐含层网络的梯度检查。
- 训练并测试不同的网络架构。您可以实现在一层上有256个隐含单元的隐含层,该结构可以达到在训练集上 100% 的精度。因为有很多参数,所以存在过拟合的风险。通过对不同的层数,隐含层数,以及权重衰减惩罚值的实验,来进一步理解什么样的架构表现最好。您能找到一个优于您最好的单隐含层架构的多隐含层网络吗?
- (可选)扩展您的代码使其支持多种非线性隐含单元的选择(
S
型函数,双曲正切
tanh 函数和 ReLU 函数)。