问题描述
我正在尝试制作一个由三层组成的简单神经网络来解决二元分类问题。前两层有八个神经元(+偏置单元)。我正在使用 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,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
因此,即使退出标志为 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 对象,然后“内爆”它们,即:
[Delta1(:) ; Delta2(:)]
更新
在上面的评论中继续讨论。
考虑 Delta1 和 Delta2 是什么。这是分别对应于 Theta1 和 Theta2 中的元素的总误差(在所有观测值上)。换句话说,Delta1 应与 Theta1 大小相同,Delta2 应与 Theta2 大小相同。现在您已经在代码中包含了矩阵大小,您可以立即看到情况并非如此。
此外,由于每次迭代都会添加到这些矩阵中,因此每次迭代的结果应该是大小合适的 Delta1 和 Delta2,这样您就可以添加每次迭代中贡献的误差,以获得总误差(每个参数)超过所有迭代。
另外,考虑 delta2 和 delta3 是什么。这些也是错误,但它们不是指参数中的错误,而是指节点中的错误。换句话说,它们显示了层中每个节点对最终错误的贡献/责任。因此,它们的大小需要是一个向量,其元素数量与相应层中的节点数量相同。您已经可以看到,delta2 的大小为 9x9 是没有意义的!
所以算法的逻辑是这样的。取节点对误差的贡献,并将其反向传播到之前的节点以及它们之间的参数。
例如将每个 delta3(在本例中只有一个节点)与第 2 层中的每个节点相乘,以找出误差在 Theta2 中的每个参数元素上的分布情况。
同样,为了获得delta2,我们考虑了delta3中的误差,并通过向后跟随参数乘法将其分布到第2层中的每个节点。然后最重要的是,我们乘以梯度(因为这定义了错误向前传播的程度/速率)。
既然我们已经处理了第 3 层及其与第 2 层的交互,我们继续讨论第 2 层及其与第 1 层的交互。因此,类似地,将每个 delta2 相乘(第 2 层有 9 个节点,因此 delta2 应该有第 1 层中的每个节点有 9 个元素)。这给出了一个 9x9 矩阵,反映了第 1 层中的每个节点如何通过参数 Theta1 引起第 2 层每个节点的误差。但是,因为我们不关心对第 2 层的 'bias' 节点,我们从 Delta1 中删除了这部分,这给我们留下了一个 8x9 的矩阵,与 Theta1 的大小完全相同。
理论上你也可以反向传播 delta2 来找到一个 delta1,但由于我们没有用它,我们跳过这一步。毕竟,我们真正关心的是参数中的错误,而不是节点中的错误。 (即我们只关心每一步节点中的错误,因为我们需要它们来获取之前层参数中的错误)。