Моя многоуровневая реализация персептрона в 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}
(Это все двойные массивы, я отформатировал вывод для иллюстрации)