简单神经网络中的奇异收敛性
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单神经网络中的奇异收敛性相关的知识,希望对你有一定的参考价值。
我一直在努力在Java中构建一个简单的NN。我已经在这个项目上工作了几个月,我想完成它。我的主要问题是我不知道如何正确实现反向传播(所有来源都使用Python,数学术语,或者简单地解释这个想法)。今天我试着自己推断出意识形态,我正在使用的规则是:
权重更新=错误* sigmoidDerivative(错误)*权重本身; 错误=输出 - 实际; (最后一层) error = sigmoidDerivative(来自上一层的错误)*权重将此神经元附加到神经元给出错误(中间层)
我的主要问题是输出收敛于平均值,而我的第二个问题是权重更新为非常奇怪的值。 (可能权重问题导致收敛)
我正在努力训练:对于输入1-9,预期输出为:(x * 1.2 + 1)/ 10。这只是随机出现的规则。我正在使用结构为1-1-1的NN(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个时期记录一次错误,因为用更少的数据点更容易绘制,而对于每个预期输出仍然有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];
至:
outputErrors[k][i] = sigmoidDerivative(outputErrors[k+1][j])[0] * network[k+1][i].emergingWeights[j]* outputErrors[k+1][j];
所以我保持输出错误本身的标志。这修复了第一重量的无限倾向。现在,在/ 10运行时,第一个重量趋向于0,而在/ 100运行时,第二个重量趋向于0.仍然希望有人会为我清理事情。 :(
我已经看到你的代码存在serval问题,比如你的体重更新是不正确的。我还强烈建议您通过引入方法来组织代码清理程序。
反向传播通常难以有效实施,但正式定义很容易翻译成任何语言。我不建议你看一下研究神经网络的代码。看看数学并尝试理解。这使您可以更灵活地从头开始实施。
我可以通过描述伪代码中的前向和后向传递给你一些提示。
作为表示法,我使用i
作为输入,j
用于隐藏,k
用于输出层。然后输入层的偏差是bias_i
。对于将一个节点连接到另一个节点的权重,权重是w_mn
。激活是a(x)
,它的衍生物a'(x)
。
前传:
for each n of j
dot = 0
for each m of i
dot += m*w_mn
n = a(dot + bias_i)
同样适用于输出层k
和隐藏层j
。因此,只需用j
的k
和i
替换j
这一步骤。
落后传球:
计算输出节点的增量:
for each n of k
d_n = a'(n)(n - target)
这里,target
是预期输出,n
是当前输出节点的输出。 d_n
是此节点的增量。这里一个重要的注意事项是,logistic和tanh函数的导数包含原始函数的输出,并且不必重新评估该值。逻辑函数是f(x) = 1/(1+e^(-x))
,它的衍生物是f'(x) = f(x)(1-f(x))
。由于每个输出节点n
的值先前已使用f(x)
进行评估,因此可以简单地应用n(1-n)
作为导数。在上面的情况下,这将计算delta如下:
d_n = n(1-n)(n - target)
以相同的方式,计算隐藏节点的增量。
for each n of j
d_n = 0
for each m of k
d_n += d_m*w_jk
d_n = a'(n)*d_n
下一步是使用渐变执行重量更新。这是通过称为梯度下降的算法完成的。没有详细说明,这可以通过以下方式完成:
for each n of j
for each m of k
w_nm -= learning_rate*n*d_m
同样适用于上面的层。只需用j
替换i
和k
替换j
。
要更新偏差,只需总结连接节点的增量,将其乘以学习速率,并从特定偏差中减去此产品。
以上是关于简单神经网络中的奇异收敛性的主要内容,如果未能解决你的问题,请参考以下文章
避免分析中的奇异性——OpenMDAO 是不是自动启用“完全同步”解决方案?