简单神经网络中的奇异收敛性

时间:2017-12-10 16:57:03

标签: java neural-network convergence

我在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.仍然希望有人会为我清理事情。 :(

1 个答案:

答案 0 :(得分:1)

我看到您的代码存在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是当前输出节点的输出的预期输出nd_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

要更新偏差,只需总结已连接节点的增量,将其乘以学习率并从特定偏差中减去此产品。