计算两层神经网络的梯度
Computing the gradients for a two-layer neural network
我想知道下面的MATLAB/Octave代码是否可以向量化?
function grads = compute_grads(data, ann, lambda)
[~, N] = size(data.X);
% First propagate the data
S = evaluate(data.X, ann);
G = -(data.Y - S{2});
% Second layer gradient is easy.
l2g.W = G*S{1}';
l2g.b = mean(G)';
G = G' * ann{2}.W;
[m, d] = size(ann{1}.W);
[K, ~] = size(ann{2}.W);
% I would like to vectorize this calculation.
l1g.W = zeros(m, d);
l1g.b = mean(G)';
for i = 1:N
x = data.X(:, i);
g = G(i, :);
l1 = S{1}(:, i);
g = g * diag(l1 > 0);
l1g.W = l1g.W + g'*x';
end
grads = {l1g, l2g};
for k=1:length(grads)
grads{k}.W = grads{k}.W/N + 2*lambda*ann{k}.W;
end
end
代码计算双层神经网络的梯度。第二层有一个 softmax 激活函数,如第 4 行 G = -(data.Y - S{2});
所示。第一层具有 ReLU 激活,由 for
-loop 中的 gunk 实现,它一次对每个样本进行操作。
如您所见,中间有一个显式的 for
循环。是否有任何 array/matrix 函数可以用来代替隐式循环?
循环可以简化为:
l1g.W = (data.X * (G .* (S{1} > 0).')).';
解释:
在矢量化中,我们应该避免不必要的操作。例如在
g = g * diag(l1 > 0);;
我们可以用element-wize乘法来达到同样的目的:
g = g .* (l1.' > 0);
%or
g = g .* (l1 > 0).';
使用它我们可以将一些操作放在循环之外:
l1g.W = zeros(m, d);
G = G .* (S{1} > 0).';
for i = 1:N
x = data.X(:, i);
g = G(i, :);
l1g.W = l1g.W + g'*x';
end
所以我们有这样的东西:
W=0;
for i = 1:N
W = W + something(i);
end
可以写成:
W = sum(something);
我们的循环可以简化为:
l1g.W = sum(some_structrue_created_by_vectorizing(g'*x'));
我们可以使用 bsxfun
等函数来创建这样的结构(即 3D 矩阵),但通常这样的结构需要大量内存,并且循环可能比矢量化更有效。但是等等,我们想要计算 g
和 x
的 product 的总和,这样我们就可以 [并且应该始终] 考虑使用向量-矩阵或矩阵-矩阵乘法因为它们是非常快速的操作。
由于我们正在执行 g
和 x
的外积,因此矩阵乘法是正确的选择。
G = G .* (S{1} > 0).';
l1g.W = (data.X * G).'
或
l1g.W = (data.X * (G .* (S{1} > 0).')).';
我想知道下面的MATLAB/Octave代码是否可以向量化?
function grads = compute_grads(data, ann, lambda)
[~, N] = size(data.X);
% First propagate the data
S = evaluate(data.X, ann);
G = -(data.Y - S{2});
% Second layer gradient is easy.
l2g.W = G*S{1}';
l2g.b = mean(G)';
G = G' * ann{2}.W;
[m, d] = size(ann{1}.W);
[K, ~] = size(ann{2}.W);
% I would like to vectorize this calculation.
l1g.W = zeros(m, d);
l1g.b = mean(G)';
for i = 1:N
x = data.X(:, i);
g = G(i, :);
l1 = S{1}(:, i);
g = g * diag(l1 > 0);
l1g.W = l1g.W + g'*x';
end
grads = {l1g, l2g};
for k=1:length(grads)
grads{k}.W = grads{k}.W/N + 2*lambda*ann{k}.W;
end
end
代码计算双层神经网络的梯度。第二层有一个 softmax 激活函数,如第 4 行 G = -(data.Y - S{2});
所示。第一层具有 ReLU 激活,由 for
-loop 中的 gunk 实现,它一次对每个样本进行操作。
如您所见,中间有一个显式的 for
循环。是否有任何 array/matrix 函数可以用来代替隐式循环?
循环可以简化为:
l1g.W = (data.X * (G .* (S{1} > 0).')).';
解释:
在矢量化中,我们应该避免不必要的操作。例如在
g = g * diag(l1 > 0);;
我们可以用element-wize乘法来达到同样的目的:
g = g .* (l1.' > 0);
%or
g = g .* (l1 > 0).';
使用它我们可以将一些操作放在循环之外:
l1g.W = zeros(m, d);
G = G .* (S{1} > 0).';
for i = 1:N
x = data.X(:, i);
g = G(i, :);
l1g.W = l1g.W + g'*x';
end
所以我们有这样的东西:
W=0;
for i = 1:N
W = W + something(i);
end
可以写成:
W = sum(something);
我们的循环可以简化为:
l1g.W = sum(some_structrue_created_by_vectorizing(g'*x'));
我们可以使用 bsxfun
等函数来创建这样的结构(即 3D 矩阵),但通常这样的结构需要大量内存,并且循环可能比矢量化更有效。但是等等,我们想要计算 g
和 x
的 product 的总和,这样我们就可以 [并且应该始终] 考虑使用向量-矩阵或矩阵-矩阵乘法因为它们是非常快速的操作。
由于我们正在执行 g
和 x
的外积,因此矩阵乘法是正确的选择。
G = G .* (S{1} > 0).';
l1g.W = (data.X * G).'
或
l1g.W = (data.X * (G .* (S{1} > 0).')).';