考虑一个三层神经网络(一个输入层、一个隐含层、以及一个输出层),并且假定x是包含一个单一训练样本的列向量。则向量化的正向传播步骤如下:
z2 = w1.x + b1
a2 = f(z2)
z3 = w2.a2 + b2
h(x) = a3 = f(z3)
当我们需要处理m个训练样本时,则需要把如上步骤放入一个for循环中:
% 非向量化实现
for i=1:m,
z2 = W1 * x(:,i) + b1;
a2 = f(z2);
z3 = W2 * a2 + b2;
h(:,i) = f(z3);
end;
z2,a2,z3都是列向量,分别用来计算隐层和输出层的激励结果。为了充分利用并行化和高效矩阵运算的优势,我们希望算法能同时处理多个训练样本。让我们先暂时忽略前面公式中的b1和b2(把它们设置为0),那么可以实现如下:
% 向量化实现 (忽略 b1, b2)
z2 = W1 * x;
a2 = f(z2);
z3 = W2 * a2;
h = f(z3)
在这个实现中,z2,a2,z3都是矩阵,每个训练样本对应矩阵的一列。在对多个训练样本实现向量化时常用的设计模式是,虽然前面每个样本对应一个列向量(比如z2),但我们可把这些列向量堆叠成一个矩阵以充分享受矩阵运算带来的好处。这样,在这个例子中,a2就成了一个s2 X m的矩阵(s2是网络第二层中的神经元数,m是训练样本个数)。矩阵a2的含义是,当第i个训练样本x(:i)输入到网络中时,它的第i列就表示这个输入信号对隐神经元 (网络第二层)的激励结果。
在上面的实现中,我们假定激活函数f(z)接受矩阵形式的输入z,并对输入矩阵按列分别施以激活函数。假定激活函数采用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中表示对矩阵的每个元素分别进行除法操作
end
最后,我们上面的正向传播向量化实现中忽略了b1和b2,现在要把他们包含进来,为此我们需要用到Matlab的内建函数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 X m的矩阵。它和W1 * x相加,就等于是把W1 * x矩阵的每一列加上b1
注:上述代码引自Standford大学Andrew Ng的文章