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

权重更新=错误* sigmoidDerivative(错误)*权重本身;
错误=输出-实际; (最后一层)
错误= 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个纪元记录一次错误,因为使用更少的数据点可以更轻松地进行绘制,而对于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;


编辑:浮现在脑海中的一个快速问题:由于我使用S形作为激活函数,因此我的输入和输出神经元是否应该只有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。仍然希望有人会为我整理东西。 :(

最佳答案

我发现您的代码存在伺服问题,例如您的体重更新不正确。我也强烈建议您通过介绍方法来组织代码清理器。

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

我可以通过描述伪代码中的正向和反向传递给您一些提示。

注意,我将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是此节点的增量。
这里重要的一点是,逻辑函数和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

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

关于java - 简单神经网络中的奇异收敛,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47741187/

10-11 19:53
查看更多