多层感知器总是选择它被训练指定的最后一个 class。反向传播
Multilayer perceptron always picks the last class it was trained to specify. Backpropagation
我正在尝试编写一个将输入分类为三个对象的 MLP。
我有一个代表每个对象的数字。
1-10 : Banana
11-20 : Apple
21:30 : Carrot
MLP 中只有两层:一层隐藏层(2 个单元)和一层输出层(3 个单元)。
每个单元有:
- 输入[](传递给该单元的输入)
- 权重[]
- 增量
- sum(与输入相加的权重)
- 输出(激活总和)
每个单元都有一个激活函数:
double activate(double[] inputs) {
this.inputs = inputs;
sum = 0;
for (int i = 0; i < inputs.length; i++)
sum += weights[i] * inputs[i];
output = 1.0 / (1.0 + (Math.exp(-sum))); // activation
return output;
}
和校正权重的函数:
void correctWeights(double momentum, double learningRate) {
for (int i = 0; i < weights.length; i++) {
weights[i] = weights[i] * momentum + learningRate * delta * (output * (1 - output)) * inputs[i];
}
}
其中 (output * (1 - output))
是导数。
为了训练网络,我有一个循环 N 次的函数,在循环中我生成与对象相关的输入,然后将其传播到网络并使用反向传播。
private void train() {
for (int i = 0; i < 10000; i++) {
int[] expectedOutput = new int[3];
double[] inputs = {ThreadLocalRandom.current().nextInt(1, 30 + 1)};
if (inputs[0] <= 10) {
expectedOutput[0] = 1;
expectedOutput[1] = 0;
expectedOutput[2] = 0;
}
if (inputs[0] <= 20 && inputs[0] > 10) {
expectedOutput[0] = 0;
expectedOutput[1] = 1;
expectedOutput[2] = 0;
}
if (inputs[0] <= 30 && inputs[0] > 20) {
expectedOutput[0] = 0;
expectedOutput[1] = 0;
expectedOutput[2] = 1;
}
double[] outputs = propagate(inputs);
backPropagate(expectedOutput, outputs);
}
}
传播函数只是遍历整个网络并激活单元。
private double[] propagate(double[] inputs) {
double[] hiddenOutputs = new double[hiddenLayer.length];
for (int i = 0; i < hiddenLayer.length; i++)
hiddenOutputs[i] = hiddenLayer[i].activate(inputs);
double[] outputs = new double[outputLayer.length];
for (int i = 0; i < outputs.length; i++)
outputs[i] = outputLayer[i].activate(hiddenOutputs);
return outputs;
}
反向传播算法取自http://home.agh.edu.pl/~vlsi/AI/backp_t_en/backprop.html
private void backPropagate(int[] expectedOutput, double[] output) {
for (int i = 0; i < outputLayer.length; i++) {
outputLayer[i].setDelta(expectedOutput[i] - output[i]);
}
for (int i = 0; i < hiddenLayer.length; i++) {
double delta = 0;
for (int j = 0; j < outputLayer.length; j++) {
delta += outputLayer[j].getDelta() * outputLayer[j].getWeight(i);
}
hiddenLayer[i].setDelta(delta);
}
for (int i = 0; i < hiddenLayer.length; i++)
hiddenLayer[i].correctWeights(momentum, learningRate);
for (int i = 0; i < outputLayer.length; i++)
outputLayer[i].correctWeights(momentum, learningRate);
}
经过训练后,它还有识别物体的功能
private void recognize(String number) {
double[] inputs = {Double.parseDouble(number)};
double[] outputs = propagate(inputs);
System.out.println("Banana: " + outputs[0]);
System.out.println("Apple: " + outputs[1]);
System.out.println("Carrot: " + outputs[2]);
}
所以问题是,当我将任何数字传递给 recognize 函数时,我得到的输出类似于:
Banana: 0.49984367018594233
Apple: 0.49984367018594233
Carrot: 0.5001563298140577
每次都在选择胡萝卜(胡萝卜也是网络最后训练的对象)。因此,如果我输入 5,它会输出它是胡萝卜。如果我输入 15,它会输出它是胡萝卜。如果我改变在 train 函数中学习的对象的顺序,并使香蕉成为最后学习的对象,那么网络将始终选择香蕉作为它的答案。
我已经为此工作了几天,但我找不到任何解决方案,请帮助我,我做错了什么?
我注意到您 select 一个 0-30 之间的随机数,然后为其确定一个输出,但是您忘记了对输入进行归一化。如果输入在 0-1
范围内(取决于您使用的激活函数),神经网络的功能最佳。
那么剩下你要做的就是规范化这个输入。这意味着,将输入等同地转换为 0
和 1
之间的数字。
您输入的是一个数值,因此您只需选择一个最大值来除以所有值即可。在您的情况下,这可能是 30
,因为没有高于 30
的输入。所以每个数字都按如下方式转换:
10 -> 10 / 30 -> 0.33
15 -> 15 / 30 -> 0.50
etc.
详细了解规范化 here。
我正在尝试编写一个将输入分类为三个对象的 MLP。 我有一个代表每个对象的数字。
1-10 : Banana
11-20 : Apple
21:30 : Carrot
MLP 中只有两层:一层隐藏层(2 个单元)和一层输出层(3 个单元)。
每个单元有:
- 输入[](传递给该单元的输入)
- 权重[]
- 增量
- sum(与输入相加的权重)
- 输出(激活总和)
每个单元都有一个激活函数:
double activate(double[] inputs) {
this.inputs = inputs;
sum = 0;
for (int i = 0; i < inputs.length; i++)
sum += weights[i] * inputs[i];
output = 1.0 / (1.0 + (Math.exp(-sum))); // activation
return output;
}
和校正权重的函数:
void correctWeights(double momentum, double learningRate) {
for (int i = 0; i < weights.length; i++) {
weights[i] = weights[i] * momentum + learningRate * delta * (output * (1 - output)) * inputs[i];
}
}
其中 (output * (1 - output))
是导数。
为了训练网络,我有一个循环 N 次的函数,在循环中我生成与对象相关的输入,然后将其传播到网络并使用反向传播。
private void train() {
for (int i = 0; i < 10000; i++) {
int[] expectedOutput = new int[3];
double[] inputs = {ThreadLocalRandom.current().nextInt(1, 30 + 1)};
if (inputs[0] <= 10) {
expectedOutput[0] = 1;
expectedOutput[1] = 0;
expectedOutput[2] = 0;
}
if (inputs[0] <= 20 && inputs[0] > 10) {
expectedOutput[0] = 0;
expectedOutput[1] = 1;
expectedOutput[2] = 0;
}
if (inputs[0] <= 30 && inputs[0] > 20) {
expectedOutput[0] = 0;
expectedOutput[1] = 0;
expectedOutput[2] = 1;
}
double[] outputs = propagate(inputs);
backPropagate(expectedOutput, outputs);
}
}
传播函数只是遍历整个网络并激活单元。
private double[] propagate(double[] inputs) {
double[] hiddenOutputs = new double[hiddenLayer.length];
for (int i = 0; i < hiddenLayer.length; i++)
hiddenOutputs[i] = hiddenLayer[i].activate(inputs);
double[] outputs = new double[outputLayer.length];
for (int i = 0; i < outputs.length; i++)
outputs[i] = outputLayer[i].activate(hiddenOutputs);
return outputs;
}
反向传播算法取自http://home.agh.edu.pl/~vlsi/AI/backp_t_en/backprop.html
private void backPropagate(int[] expectedOutput, double[] output) {
for (int i = 0; i < outputLayer.length; i++) {
outputLayer[i].setDelta(expectedOutput[i] - output[i]);
}
for (int i = 0; i < hiddenLayer.length; i++) {
double delta = 0;
for (int j = 0; j < outputLayer.length; j++) {
delta += outputLayer[j].getDelta() * outputLayer[j].getWeight(i);
}
hiddenLayer[i].setDelta(delta);
}
for (int i = 0; i < hiddenLayer.length; i++)
hiddenLayer[i].correctWeights(momentum, learningRate);
for (int i = 0; i < outputLayer.length; i++)
outputLayer[i].correctWeights(momentum, learningRate);
}
经过训练后,它还有识别物体的功能
private void recognize(String number) {
double[] inputs = {Double.parseDouble(number)};
double[] outputs = propagate(inputs);
System.out.println("Banana: " + outputs[0]);
System.out.println("Apple: " + outputs[1]);
System.out.println("Carrot: " + outputs[2]);
}
所以问题是,当我将任何数字传递给 recognize 函数时,我得到的输出类似于:
Banana: 0.49984367018594233
Apple: 0.49984367018594233
Carrot: 0.5001563298140577
每次都在选择胡萝卜(胡萝卜也是网络最后训练的对象)。因此,如果我输入 5,它会输出它是胡萝卜。如果我输入 15,它会输出它是胡萝卜。如果我改变在 train 函数中学习的对象的顺序,并使香蕉成为最后学习的对象,那么网络将始终选择香蕉作为它的答案。
我已经为此工作了几天,但我找不到任何解决方案,请帮助我,我做错了什么?
我注意到您 select 一个 0-30 之间的随机数,然后为其确定一个输出,但是您忘记了对输入进行归一化。如果输入在 0-1
范围内(取决于您使用的激活函数),神经网络的功能最佳。
那么剩下你要做的就是规范化这个输入。这意味着,将输入等同地转换为 0
和 1
之间的数字。
您输入的是一个数值,因此您只需选择一个最大值来除以所有值即可。在您的情况下,这可能是 30
,因为没有高于 30
的输入。所以每个数字都按如下方式转换:
10 -> 10 / 30 -> 0.33
15 -> 15 / 30 -> 0.50
etc.
详细了解规范化 here。