一段时间以来,我一直在努力用 Java 构建一个简单的神经网络。我已经断断续续地致力于这个项目几个月了,我想完成它。我的主要问题是我不知道如何正确实现反向传播(所有来源都使用 Python、数学术语,或者过于简单地解释这个想法)。今天我尝试自己推导意识形态,我使用的规则是:
权重更新 = error * sigmoidDerivative(error) * 权重本身;
错误=输出-实际; (最后一层)
error = sigmoidDerivative(来自前一层的错误) * 将此神经元附加到给出错误的神经元的权重(中间层)
我的主要问题是输出收敛于平均值,第二个问题是权重更新为一个极其奇怪的值。 (可能是权重问题导致收敛)
我正在尝试训练:对于输入 1-9 ,预期输出为:(x*1.2+1)/10。这只是我随机想到的一条规则。我使用结构为 1-1-1 的神经网络(3 层,1 个网络/层)。在下面的链接中,我附加了两次运行:一次运行中我使用遵循规则 (x*1.2+1)/10 的训练集,另一次运行中我使用 (x*1.2+1)/100。除以 10 后,第一个权重趋于无穷大;除以 100 后,第二个权重趋向于 0。我一直在尝试调试它,但我不知道我应该寻找什么或出了什么问题。非常感谢任何建议。预先感谢大家,祝大家有美好的一天!
https://wetransfer.com/downloads/55be9e3e10c56ab0d6b3f36ad990ebe120171210162746/1a7b6f
我按照上述规则将训练样本 1->9 及其各自的输出作为训练样本,并将它们运行 100_000 轮。我每 100 个时期记录一次错误,因为使用较少的数据点更容易绘制,同时仍然为 9. 反向传播和权重更新的每个预期输出保留 1000 个数据点:
//for each layer in the Dweights array
for(int k=deltaWeights.length-1; k >= 0; k--)
{
for(int i=0; i<deltaWeights[k][0].length; i++) // for each neuron in the layer
{
if(k == network.length-2) // if we're on the last layer, we calculate the errors directly
{
outputErrors[k][i] = outputs[i] - network[k+1][i].result;
errors[i] = outputErrors[k][i];
}
else // otherwise the error is actually the sum of errors feeding backwards into the neuron currently being processed * their respective weight
{
for(int j=0; j<outputErrors[k+1].length; j++)
{ // S'(error from previous layer) * weight attached to it
outputErrors[k][i] += sigmoidDerivative(outputErrors[k+1][j])[0] * network[k+1][i].emergingWeights[j];
}
}
}
for (int i=0; i<deltaWeights[k].length; i++) // for each neuron
{
for(int j=0; j<deltaWeights[k][i].length; j++) // for each weight attached to that respective neuron
{ // error S'(error) weight connected to respective neuron
deltaWeights[k][i][j] = outputErrors[k][j] * sigmoidDerivative(outputErrors[k][j])[0] * network[k][i].emergingWeights[j];
}
}
}
// we use the learning rate as an order of magnitude, to scale how drastic the changes in this iteration are
for(int k=deltaWeights.length-1; k >= 0; k--) // for each layer
{
for (int i=0; i<deltaWeights[k].length; i++) // for each neuron
{
for(int j=0; j<deltaWeights[k][i].length; j++) // for each weight attached to that respective neuron
{
deltaWeights[k][i][j] *= 1; // previously was learningRate; MSEAvgSlope
network[k][i].emergingWeights[j] += deltaWeights[k][i][j];
}
}
}
return errors;
编辑:我想到了一个简单的问题:由于我使用 sigmoid 作为我的激活函数,我的输入和输出神经元应该只在 0-1 之间吗?我的输出在 0-1 之间,但我的输入实际上是 1-9。
Edit2:将输入值标准化为 0.1-0.9 并更改:
outputErrors[k][i] += sigmoidDerivative(outputErrors[k+1][j])[0] * network[k+1][i].emergingWeights[j];
to:
outputErrors[k][i] = sigmoidDerivative(outputErrors[k+1][j])[0] * network[k+1][i].emergingWeights[j]* outputErrors[k+1][j];
这样我就可以保留输出错误本身的符号。这修复了第一个重量中的无穷大趋势。现在,在 /10 运行中,第一个权重趋于 0,而在 /100 运行中,第二个权重趋于 0。仍然希望有人能够为我解决问题。 :(