我正在尝试使用 AND 示例进行 delta 规则学习,并且我注意到,当我不在权重校正中应用 sigmoid 激活的导数时,学习收敛得更快更好.
我正在使用偏差神经元。
如果我理解正确的话,delta规则应该考虑激活函数的导数来进行权重调整: Δ Wk(n) = η*𝑒(𝑛)*𝑔′(ℎ)*𝑥(𝑛) .
其中 e(n) =desired_output - 神经元输出。
这是我用来计算输出的 sigmoid:
public double calc(double sum) {
return 1 / (1 + Math.pow(Math.E, -sum));
}
根据此 dela rule 第 33 页第 4 步,权重更新应为:
double delta = learningRate * error * estimated * (1 - estimated) * input;
如果没有以下内容,效果会更好:
estimated * (1 - estimated)
这几乎是使用增量规则进行训练的代码:
@Override
public void train(List<LearningSample> samples, double[] weights, Function<double[], Double> neuronOutput) {
double[] weightDelta = new double[weights.length];
for (int i = 0; i < 10000; i++) {
// Collections.shuffle(samples);
for (LearningSample sample : samples) {
// sigmoid of dot product of weights and input vector, including bias
double estimated = neuronOutput.apply(sample.getInput());
double error = sample.getDesiredOutput() - estimated;
// this commented out version actually works better than the one bellow
// double delta = learningRate * error;
double delta = learningRate * error * estimated * (1 - estimated);
// aggregate delta per weight for each sample in epoch
deltaUpdate(delta, weightDelta, sample.getInput());
}
// batch update weights at the end of training epoch
for (int weight = 0; weight < weights.length; weight++) {
weights[weight] += weightDelta[weight];
}
weightDelta = new double[weights.length];
}
}
private void deltaUpdate(double delta, double[] weightsDelta, double[] input) {
for (int feature = 0; feature < input.length; feature++) {
weightsDelta[feature] = weightsDelta[feature] + delta * input[feature];
}
}
AND 的训练样本如下所示:
List<LearningSample> samples = new ArrayList<>();
LearningSample sample1 = new LearningSample(new double[] { 0, 0 }, 0);
LearningSample sample2 = new LearningSample(new double[] { 0, 1 }, 0);
LearningSample sample3 = new LearningSample(new double[] { 1, 0 }, 0);
LearningSample sample4 = new LearningSample(new double[] { 1, 1 }, 1);
偏差1作为构造函数中的第0个组件注入(inject)。
学习后测试输出的顺序:
System.out.println(neuron.output(new double[] { 1, 1, 1 }));
System.out.println(neuron.output(new double[] { 1, 0, 0 }));
System.out.println(neuron.output(new double[] { 1, 0, 1 }));
System.out.println(neuron.output(new double[] { 1, 1, 0 }));
这是我在 delta 计算中省略 sigmoid 导数时的结果:
10000 次迭代
- 0.9666565909058419
- 2.05087653022386E-5
- 0.023803593411627456
- 0.023803593411627456
35000 次迭代
- 0.9903810162649429
- 4.6475933225663785E-7
- 0.006870001301253153
- 0.006870001301253153
这些是应用导数的结果:
10000 次迭代
- 0.8446651307271656
- 0.004030424878725242
- 0.129178264332045
- 0.129178264332045
35000 次迭代
- 0.9218773156128204
- 4.169603485934177E-4
- 0.06555977437019253
- 0.06555977437019253
学习率为:0.021,偏置起始权重为:-2。
在第一个没有导数的例子中,误差更小,函数的逼近效果更好。 这是为什么?
更新
根据@Umberto 的回答,我想验证以下几件事:
事故实验(其中
delta =learningRate * error * input
)实际上是有效的,因为这可以最小化交叉熵成本函数? 是交叉熵显然更适合分类,那么什么时候应该使用 MSE 作为成本函数呢? 回归
请注意,我正在通过阈值函数运行输出,只是此处未显示,因此这是二元分类。
最佳答案
原因很简单。您可以最小化不同的成本函数。在您的情况下(如幻灯片所示),您可以最小化误差平方。如果您使用我在此处的推导中描述的形式的成本函数(交叉熵)github link ,您将获得更快的权重更新。通常在分类问题中(通常使用 S 形神经元进行二元分类),平方误差实际上并不是一个好的成本函数。
如果您使用交叉熵,则需要使用learningRate * error * input;
(根据您定义错误的方式,使用正确的符号)。
作为旁注,您实际上在做的是逻辑回归......
希望有帮助。如果您需要更多信息,请告诉我。检查我的链接,在那里我对其背后的数学进行了完整的推导。
关于java - sigmoid 的单神经元 delta 规则收敛,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46217098/