Реализация Java с обратным распространением MLP - PullRequest
0 голосов
/ 07 ноября 2019

Моя многоуровневая реализация персептрона в Java, кажется, выплевывает довольно случайные числа для XOR-проблемы, с которой я тестирую свой код. У меня есть одноуровневая реализация персептрона, которую я запускаю бок о бок, построенная на одном и том же фоне (https://www.nnwj.de/backpropagation.html). Где я иду не так, и / или что я неправильно понял / не полностью понял?

Я пытался использовать https://www.nnwj.de/backpropagation.html в качестве псевдокода для написания своей собственной «библиотеки», но я думаю, что неправильно интерпретирую некоторые части. Я хотел максимально использовать линейную алгебру. Я также смотрел серию 3Blue1Brown на NNs. https://www.youtube.com/watch?v=Ilg3gGewQ5U.

    /**
     * Backpropagation, alters weights and biases by gradient descent.
     *
     * @param fedForward the predicted value, given by {@link #predict(double[])}
     * @param correct The correct classification
     */
    private void backPropagate(Matrix fedForward, Matrix correct) {
        // Calculate errors. Will feed this backwards...
        Matrix errors = correct.subtract(fedForward); // Error vector

        // Last weight, we iterate backwards
        int lastWeight = this.layerConnections.amountOfWeights() - 1;

        for (int i = lastWeight; i >= 0; i--) {
            int layerIndex =
                i + 1; // As opposed to the weight, what layer are we using? - the i+1:th.
            Matrix currentWeight = this.layerConnections.getWeight(i);
            Matrix currentLayer = this.layerConnections.getActiveLayer(layerIndex);
            Matrix currentLayerBias = this.layerConnections.getLayer(layerIndex)
                .getBias(); // Bias of current layer.
            Matrix previousErrors = errors; // To use in this loop, and to not mutate to next iteration

            Matrix currentLayerBiased = currentLayer.add(currentLayerBias); // Add bias to the layer

            // Apply the derivative to each element of the layer,
            // hadamard-product with the errors, multiply with learning rate
            Matrix gradients = this.function.derivativeToMatrix(currentLayerBiased)
                .hadamard(previousErrors)
                .map((e) -> e * this.learningRate);

            // The bias deltas, add the gradients to the deltas.
            Matrix deltaBias = currentLayerBias.add(gradients);

            // Set the bias of this layer to deltaBias
            this.layerConnections.setActiveLayerBias(layerIndex, deltaBias);

            // Transpose the current weight, we are "moving" backwards.
            Matrix transposedW = currentWeight.transpose();
            // Multiply the weights with the neurons of this layer
            Matrix outputMapped = transposedW.multiply(currentLayer);

            // Get the deltas by multiplying deltas with the backward-passed values.
            Matrix deltas = gradients.multiply(outputMapped.transpose());
            // Change the weights by adding the deltas.
            Matrix newWeight = currentWeight.add(deltas);

            // Set the weighs.
            this.layerConnections.setWeight(i, newWeight);

            // Update the error, which is essentially moving the errors in a backwards pass.
            // This is one of the things where I feel unsure of my understanding.
            errors = newWeight.transpose().multiply(previousErrors);
        }
    }

и это мой прямой проход:

    /**
     * Feed the input through the network for classification.
     * @param in values to predict
     * @return classified values.
     */
    private Matrix feedForward(double[] in) {
        // Make input into matrix.
        Matrix input = Matrix.fromArray(in);

        // Assure that matrix size matches.
        if (input.getRows() != this.layerConnections.getInputNodes()) {
            throw new IllegalArgumentException("Dimensions do not match.");
        }

        // Get biases and weights from the network
        Matrix[] biases = this.layerConnections.getBiases();
        Matrix[] weights = this.layerConnections.getWeights();

        // Set the neurons of the first layer (input layer) to the input.
        this.layerConnections.setActiveLayer(0, input);

        Matrix output;
        Matrix inputIterated = input;
        for (int i = 1; i < weights.length + 1; i++) { // Loop over all weights
            // Weights multiplied with the input
            Matrix hiddenMultiplication = weights[i - 1].multiply(inputIterated);
            Matrix bias = biases[i];

            Matrix biasedHiddenMultiplication = hiddenMultiplication.add(bias);
            inputIterated = function.functionToMatrix(biasedHiddenMultiplication);
            // Set current layers neurons to the inputIterated matrix.
            this.layerConnections.setActiveLayer(i, inputIterated);
        }

        output = inputIterated;

        return output;
    }

Я ввел 4 логические комбинации для задачи XOR, и она выдает бессмысленные значения. Я запустил паруКоличество раз, чтобы написать это, и иногда, выходной код был полностью случайным, иногда все входные данные давали одинаковые выходные данные, например:

input: {{0,1},{1,0},{0,0},{1,1}};
output: {0.3759, 0.6015, 0.2172, 0.4274}

или

input: {{0,1},{1,0},{0,0},{1,1}};
output: {0.3759,0.3758,0.3759,0.3759}

(Это все двойные массивы, я отформатировал вывод для иллюстрации)

...