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

问题描述 投票:1回答:1

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

java neural-network convergence
1个回答
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。因此,只需用jki替换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替换ik替换j

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

© www.soinside.com 2019 - 2024. All rights reserved.