我正在尝试编写一个将输入分为三个对象的MLP。 我有一个代表每个对象的数字。
1-10 : Banana
11-20 : Apple
21:30 : Carrot
MLP中只有两个层:一个隐藏层(2个单元)和一个输出层(3个单元)。
每个单位都有:
每个单位也都有激活功能:
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]);
}
所以问题是,当我将任何数字传递给识别函数时,我得到类似于此的输出:
Banana: 0.49984367018594233
Apple: 0.49984367018594233
Carrot: 0.5001563298140577
胡萝卜每次都被选中(胡萝卜也是网上最后一个受过训练的对象)。因此,如果我输入5,它将输出它是胡萝卜。如果我输入15,它将输出它是胡萝卜。如果我改变在训练函数中学习的对象的顺序并使香蕉成为最后学习的对象,那么网将始终选择香蕉作为其答案。
我已经在这几天工作了,我找不到任何解决办法,请帮助我,我做错了什么?
答案 0 :(得分:1)
我注意到您选择0-30之间的随机数,然后为其确定输出,但是您忘记将输入标准化。如果输入在0-1
范围内(取决于您使用的激活函数),神经网络的功能最佳。
所以剩下要做的就是规范化这个输入。这意味着,将输入平均转换为0
和1
之间的数字。
您的输入是一个数值,因此您所要做的就是选择一个用于划分所有值的最大值。在您的情况下,这可能是30
,因为没有高于30
的输入。所以每个数字的转换如下:
10 -> 10 / 30 -> 0.33
15 -> 15 / 30 -> 0.50
etc.
详细了解规范化here。