多层感知器总是选择它被训练指定的最后一个 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 个单元)。
每个单元有:

每个单元都有一个激活函数:

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 范围内(取决于您使用的激活函数),神经网络的功能最佳。

那么剩下你要做的就是规范化这个输入。这意味着,将输入等同地转换为 01 之间的数字。

您输入的是一个数值,因此您只需选择一个最大值来除以所有值即可。在您的情况下,这可能是 30,因为没有高于 30 的输入。所以每个数字都按如下方式转换:

10 -> 10 / 30 -> 0.33
15 -> 15 / 30 -> 0.50
etc.

详细了解规范化 here