【问题标题】:Strange convergence in simple Neural Network简单神经网络中的奇怪收敛
【发布时间】:2018-05-24 06:47:42
【问题描述】:

我一直在努力用 Java 构建一个简单的 NN。我已经在这个项目上工作了几个月,我想完成它。我的主要问题是我不知道如何正确实现反向传播(所有来源都使用 Python、数学术语,或者过于简单地解释这个想法)。今天我尝试自己推断意识形态,我使用的规则是:

权重更新 = 误差 * sigmoidDerivative(error) * 权重本身;
错误 = 输出 - 实际; (最后一层)
error = sigmoidDerivative(error from previous layer) * 将此神经元附加到给出错误的神经元的权重(中间层)

我的主要问题是输出趋于平均值,而我的次要问题是权重更新为一个非常奇怪的值。 (可能是权重问题导致收敛)

我要训练的内容:对于输入 1-9 ,预期输出为:(x*1.2+1)/10。这只是一个随机出现的规则。我正在使用结构为 1-1-1(3 层,1 个网络/层)的 NN。在下面的链接中,我附上了两次运行:一次我使用遵循规则 (x*1.2+1)/10 的训练集,另一次我使用 (x*1.2+1)/100。除以 10 后,第一个权重趋于无穷大;除以 100 后,第二个权重趋于 0。我一直在尝试调试它,但我不知道我应该寻找什么或出了什么问题。任何建议都非常感谢。提前感谢大家,祝大家有美好的一天!

https://wetransfer.com/downloads/55be9e3e10c56ab0d6b3f36ad990ebe120171210162746/1a7b6f

按照上述规则,我将 1->9 及其各自的输出作为训练样本,我将它们运行了 100_000 个时期。我每 100 个 epoch 记录一次错误,因为使用更少的数据点更容易绘制,同时对于 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];     

到:

    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】:

    我已经看到您的代码存在一些问题,例如您的体重更新不正确。我还强烈建议您通过引入方法来组织代码清理器。

    反向传播通常很难有效地实现,但正式的定义很容易翻译成任何语言。我不建议您查看用于研究神经网络的代码。看看数学并尝试理解它。这使您可以更灵活地从头开始实施。

    我可以通过用伪代码描述前向和后向传递给你一些提示。

    作为符号,我使用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) 作为导数。在上述情况下,将按如下方式计算增量:

    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

    要更新偏差,只需将连接节点的增量相加,乘以学习率,然后从特定偏差中减去该乘积。

    【讨论】:

    • 感谢您的回答!我会慢慢浏览你的帖子并用你的版本更新我的实现。然后我会用“官方”评论回复你。
    • 当然。如果您对我的回答有任何疑问,请告诉我。如果我发现错误,我会通知您并相应地更新答案。
    • 快速提问:我记得在网上看到应该在每个 epoch 之后进行更新。因此,对于我的 9 个示例数据集,我计算了每个示例的权重更新,并在此基础上获得平均更新,然后在一个 epoch 结束时应用它。目前,我在每个示例之后都更新权重,这是一个问题吗?
    • 另一个问题:在隐藏层增量的代码中,你正在做:d_n = a'(x)*d_n。你不是说 a'(n)(也就是中间层当前神经元激活的导数)吗?
    • 当你更新你使用的权重时:w_nm -= learning_ratemd_m 这里的“m”代表神经元m的输入(来自输出层)?
    猜你喜欢
    • 2012-02-11
    • 2012-03-03
    • 2016-05-13
    • 2016-07-10
    • 2018-12-15
    • 2019-02-20
    • 2016-05-13
    • 2018-11-04
    相关资源
    最近更新 更多