我正在尝试制作一个由三层组成的简单神经网络来解决二进制分类问题。前两层有八个神经元(+偏置单元(。我正在使用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)
梯度应该是矩阵9x9,而不是18x1,所以我不能使用fminunc
。事实上,我曾多次尝试修改成本函数中的反向传播部分,以获得梯度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,我没有密切关注你的转置(。
最后,结尾处的垂直连接步骤的整个要点是";内爆;你的矩阵回到一个单列长的向量形式,使它们遵循相同的";说明书";作为输入"0";θVec";,因此,你会拿走你的(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应该有9个元素(与层1中的每个节点相乘。这给出了一个9x9矩阵,反映了层1中的每个节点如何通过参数Theta1产生层2的每个节点的误差。然而,因为我们不关心对层2的"偏置"节点的贡献,所以我们从Delta1中删除了这一部分,这给我们留下了一个8x9矩阵,与Theta1大小完全相同。
理论上,你也可以反向传播delta2来找到delta1,但由于我们没有使用它,我们跳过这一步。毕竟,我们真正关心的是参数中的错误,而不是节点中的错误。(即,我们只关心每一步节点中的错误,因为我们需要它们来从之前的层中获得参数中的错误(。