octave/matlab 中的神经网络实现

Neural Network implementation in octave/matlab

我正在尝试制作一个由三层组成的简单神经网络来解决二元分类问题。前两层有八个神经元(+偏置单元)。我正在使用 fminunc。这是我的成本函数:

1    function [jVal, gradient] = cost2(thetaVec, X, y)
2      Theta1 = reshape(thetaVec(1:72),8, 9); % my weights used for 
3      Theta2 = reshape(thetaVec(73:81),1, 9); %forward propagation
4      Delta1 = 0; %Delta is divided in Delta1 and Delta2 for simplicity but 
5      Delta2 = 0; %they're combined to eventually calculate the gradient
6      jVal   = 0; %the value of the costfunction
7      m = length(y);
8      for i = 1:m
9        a1 = X(i, :); %X size: 3x9, a1 size: 1x9
10        z2 = Theta1 * a1';
11        a2 = 1 ./(1 + exp(-z2)); %a2 size: 8x1
12        a2 = [ones(columns(a2), 1) a2']; % bias unit added to a2: a2 size: 1x9
13        z3 = Theta2 * a2';
14        a3 = 1 ./(1 + exp(-z3)); %a3 = h(x(i)) size: 1x1
15        jVal   += (-1/m) * (y(i) * log(a3) + (1 - y(i)) * log(1 - a3));
16        delta3 = a3 - y(i); %delta3 size: 1x1
17        delta2 = Theta2' * delta3 .* a2 .* (1 - a2); %delta2 size: 9x9
18        Delta2 += delta3 * a2'; %I use Delta1 and Delta2 as accumulators
19        Delta1 += delta2 * a1'; %size Delta2: 9x1, size Delta1: 9x1
20      endfor
21      jVal = jVal/m; %avarage of jVal
22      Delta = [Delta1;Delta2]; %Deltas are combined. Size Delta: 18x1
23      gradient = (1/m) * Delta;% size gradient: 18x1
24    endfunction

我的主要:

    %the values of the vector from which I derive my weights are chosen randomly
    INIT_EPSILON = 0.1; %between thi interval
    Theta1 = rand(8, 9) * (2*INIT_EPSILON) - INIT_EPSILON;
    Theta2 = rand(1, 9) * (2*INIT_EPSILON) - INIT_EPSILON;
    thetaVec = [ Theta1(:); Theta2(:)];
    options = optimset('GradObj', 'on', 'MaxIter', 10000);
    [optTheta, functionVal, exitFlag] = fminunc(@(t) cost2(t, X, y), thetaVec, options)

gradient 应该是 9x9 的矩阵,而不是 18x1,所以我不能使用 fminunc。实际上,我试图多次修改成本函数中的 backpropagation 部分以获得梯度 9x9(特别是我曾经更改 delta2)。然而,它从来没有奏效,输出是:

optTheta = %a vector of various values
functionVal =  0.71681 %or a similar value
exitFlag =  1

因此,即使 exitflag 为 1,它也没有收敛。我哪里做错了?

您当前拥有以下代码:

delta3  = a3 - y(i);                            % (1×1)
delta2  = Theta2' * delta3 .* a2 .* (1 - a2);   % (9×1).*(1×9) = (9×9)
Delta2 += delta3 * a2';                         % (9×9)* (9×1) = (9×1)
Delta1 += delta2 * a1';                         % (9×9)* (n×1) = (9×1)

我认为它应该是这样的:

delta3  = a3 - y(i);                           % (1×1)
delta2  = Theta2 * delta3 .* a2 .* (1 - a2);   % (1×9).*(1×9) = (1×9)
Delta2 += delta3 * a2';                        % (9×9)* (9×1) = (9×1)
Delta1 += delta2.' * a1;                       % (9×1)* (1×9) = (9×9)

然后在每一步中丢弃 Delta1 中偏差的梯度,最终得到一个 (8×9) 矩阵作为正在进行的 Delta1 组件。 (您可能需要先转置 Delta1,我没有密切关注您的转置)。

最后,最后垂直串联步骤的重点是将您的矩阵“内爆”回单列长向量形式,以便它们遵循与输入“thetaVec”相同的“规范” ,因此您将获取 (8×9) 和 (1×9) Delta1 和 Delta2 对象,以及 'implode' 它们,即:

[Delta1(:) ; Delta2(:)]

更新

这里继续上面评论中的讨论。

考虑什么是 Delta1 和 Delta2。这是分别对应于 Theta1 和 Theta2 中的元素的总误差(在所有观察中)。换言之,Delta1 应与 Theta1 大小相同,Delta2 应与 Theta2 大小相同。现在您已经在代码中包含了矩阵大小,您可以立即发现情况并非如此。

此外,由于每次迭代都会将这些矩阵相加,因此每次迭代的结果应该是大小合适的 Delta1 和 Delta2,这样您就可以将每次迭代产生的误差相加,得到总误差(每个参数) 在所有迭代中。

另外,考虑一下 delta2 和 delta3 是什么。这些也是错误,但不是指参数的错误,而是指节点的错误。换句话说,它们显示了层中每个节点对最终错误的贡献/责任。因此,它们的大小需要是一个向量,其元素数与相应层中的节点数相同。您已经可以看到 delta2 的大小为 9x9 是没有意义的!

所以算法的逻辑是这样的。获取节点对错误的贡献,并将其反向传播到先前的节点以及它们之间的参数。

例如将每个 delta3(在本例中只有一个节点)与第 2 层中的每个节点相乘,以了解误差如何分布在 Theta2 中的每个参数元素上。

同样的,为了得到delta2,我们考虑了delta3中的误差,通过参数乘法倒着分布到layer 2的各个节点。然后最重要的是,我们乘以梯度(因为这定义了错误向前传播的程度/速率)。

现在我们已经处理了第 3 层及其与第 2 层的交互,我们继续讨论第 2 层及其与第 1 层的交互。因此,类似地,将每个 delta2 相乘(第 2 层中有 9 个节点,因此 delta2应该有 9 个元素)与第 1 层中的每个节点。这给出了一个 9x9 矩阵,反映了第 1 层中的每个节点如何通过参数 Theta1 引起第 2 层每个节点的错误。但是,因为我们不关心对第 2 层 'bias' 节点的贡献,我们从 Delta1 中删除了这部分,这给我们留下了一个 8x9 矩阵,与 Theta1 的大小完全相同。

理论上你也可以反向传播 delta2 来找到 delta1,但由于我们不需要它,我们跳过这一步。毕竟,我们真正关心的是参数中的错误,而不是节点中的错误。 (即我们只关心每一步节点中的错误,因为我们需要它们从之前的层中获取参数中的错误)。