简单神经网络中的奇怪收敛
Strange convergence in simple Neural Network
一段时间以来,我一直在努力在 Java 中构建一个简单的神经网络。我在这个项目上断断续续地工作了几个月,我想完成它。我的主要问题是我不知道如何正确地实现反向传播(所有来源都使用 Python、数学术语,或者过于简单地解释这个想法)。今天我尝试自己推导意识形态,我使用的规则是:
权重更新 = error * sigmoidDerivative(error) * weight itself;
错误 = 输出 - 实际; (最后一层)
error = sigmoidDerivative(来自上一层的错误)* 将此神经元附加到给出错误的神经元的权重(中间层)
我的主要问题是输出收敛于一个平均值,我的第二个问题是权重更新到一个非常奇怪的值。 (可能是权重问题导致收敛)
我要训练的内容:对于输入 1-9 ,预期输出为:(x*1.2+1)/10。这只是我随机想到的一条规则。我正在使用结构为 1-1-1 的神经网络(3 层,1 个网络/层)。在 link 波纹管中,我附上了两个 运行s:一个我使用遵循规则 (x*1.2+1)/10 的训练集,另一个我使用 ( x*1.2+1)/100。除以 10,第一个权重趋于无穷大;除以 100,第二个权重趋向于 0.I 一直试图调试它,但我不知道我应该寻找什么或出了什么问题。非常感谢任何建议。提前谢谢大家,祝大家有个美好的一天!
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;
编辑:想到一个快速的问题:因为我使用 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];
以便我保留输出错误本身的符号。这修复了第一个权重中的 Infinity 趋势。现在,对于 /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
是这个节点的delta。
这里需要注意的是,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
。
要更新偏差,只需将连接节点的增量相加,将其乘以学习率,然后从特定偏差中减去该乘积。
一段时间以来,我一直在努力在 Java 中构建一个简单的神经网络。我在这个项目上断断续续地工作了几个月,我想完成它。我的主要问题是我不知道如何正确地实现反向传播(所有来源都使用 Python、数学术语,或者过于简单地解释这个想法)。今天我尝试自己推导意识形态,我使用的规则是:
权重更新 = error * sigmoidDerivative(error) * weight itself;
错误 = 输出 - 实际; (最后一层)
error = sigmoidDerivative(来自上一层的错误)* 将此神经元附加到给出错误的神经元的权重(中间层)
我的主要问题是输出收敛于一个平均值,我的第二个问题是权重更新到一个非常奇怪的值。 (可能是权重问题导致收敛)
我要训练的内容:对于输入 1-9 ,预期输出为:(x*1.2+1)/10。这只是我随机想到的一条规则。我正在使用结构为 1-1-1 的神经网络(3 层,1 个网络/层)。在 link 波纹管中,我附上了两个 运行s:一个我使用遵循规则 (x*1.2+1)/10 的训练集,另一个我使用 ( x*1.2+1)/100。除以 10,第一个权重趋于无穷大;除以 100,第二个权重趋向于 0.I 一直试图调试它,但我不知道我应该寻找什么或出了什么问题。非常感谢任何建议。提前谢谢大家,祝大家有个美好的一天!
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;
编辑:想到一个快速的问题:因为我使用 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];
以便我保留输出错误本身的符号。这修复了第一个权重中的 Infinity 趋势。现在,对于 /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
是这个节点的delta。
这里需要注意的是,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
。
要更新偏差,只需将连接节点的增量相加,将其乘以学习率,然后从特定偏差中减去该乘积。