Skip to content

Commit

Permalink
add support for genetic algorithms
Browse files Browse the repository at this point in the history
- add copy functionality: generate an exact and "independent" copy of a neural network calling the therefore added copy constructor by copy()-method
- add merge functionality: merges the weights and biases of two neural networks either by a ratio of 50:50 or given probability, returns a new neural network
- add mutate functionality: slightly changes the weights and biases of a neural network using gaussian ditribution by a given probability
- change MatrixConverter-class to MatrixUtilities-class and add static method to merge two matrices with a given probability (this will be called when merging two neural networks)
- add two new WrongDimensionException-constructors
  • Loading branch information
kim-marcel committed May 23, 2018
1 parent d99d221 commit 422cf46
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 46 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.kim-marcel.basic_neural_network</groupId>
<artifactId>basic_neural_network</artifactId>
<version>v0.3.1</version>
<version>v0.4</version>

<build>
<plugins>
Expand Down
94 changes: 88 additions & 6 deletions src/main/java/basicneuralnetwork/NeuralNetwork.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@

import basicneuralnetwork.activationfunctions.*;
import basicneuralnetwork.utilities.FileReaderAndWriter;
import basicneuralnetwork.utilities.MatrixConverter;
import basicneuralnetwork.utilities.MatrixUtilities;
import org.ejml.simple.SimpleMatrix;

import java.util.Arrays;
import java.util.Random;

/**
* Created by KimFeichtinger on 04.03.18.
*/
public class NeuralNetwork {

private ActivationFunctionFactory activationFunctionFactory= new ActivationFunctionFactory();
private ActivationFunctionFactory activationFunctionFactory = new ActivationFunctionFactory();

private Random random = new Random();

Expand Down Expand Up @@ -49,6 +50,29 @@ public NeuralNetwork(int inputNodes, int hiddenLayers, int hiddenNodes, int outp
initializeBiases();
}

// Copy constructor
public NeuralNetwork(NeuralNetwork nn) {
this.inputNodes = nn.inputNodes;
this.hiddenLayers = nn.hiddenLayers;
this.hiddenNodes = nn.hiddenNodes;
this.outputNodes = nn.outputNodes;

this.weights = new SimpleMatrix[hiddenLayers + 1];
this.biases = new SimpleMatrix[hiddenLayers + 1];

for (int i = 0; i < nn.weights.length; i++) {
this.weights[i] = nn.weights[i].copy();
}

for (int i = 0; i < nn.biases.length; i++) {
this.biases[i] = nn.biases[i].copy();
}

this.learningRate = nn.learningRate;

this.activationFunctionKey = nn.activationFunctionKey;
}

private void initializeDefaultValues() {
this.setLearningRate(0.1);

Expand Down Expand Up @@ -93,13 +117,13 @@ public double[] guess(double[] input) {
ActivationFunction activationFunction = activationFunctionFactory.getActivationFunctionByKey(activationFunctionKey);

// Transform array to matrix
SimpleMatrix output = MatrixConverter.arrayToMatrix(input);
SimpleMatrix output = MatrixUtilities.arrayToMatrix(input);

for (int i = 0; i < hiddenLayers + 1; i++) {
output = calculateLayer(weights[i], biases[i], output, activationFunction);
}

return MatrixConverter.getColumnFromMatrixAsArray(output, 0);
return MatrixUtilities.getColumnFromMatrixAsArray(output, 0);
}
}

Expand All @@ -113,8 +137,8 @@ public void train(double[] inputArray, double[] targetArray) {
ActivationFunction activationFunction = activationFunctionFactory.getActivationFunctionByKey(activationFunctionKey);

// Transform 2D array to matrix
SimpleMatrix input = MatrixConverter.arrayToMatrix(inputArray);
SimpleMatrix target = MatrixConverter.arrayToMatrix(targetArray);
SimpleMatrix input = MatrixUtilities.arrayToMatrix(inputArray);
SimpleMatrix target = MatrixUtilities.arrayToMatrix(targetArray);

// Calculate the values of every single layer
SimpleMatrix layers[] = new SimpleMatrix[hiddenLayers + 2];
Expand Down Expand Up @@ -147,6 +171,60 @@ public void train(double[] inputArray, double[] targetArray) {
}
}

// Generates an exact copy of a NeuralNetwork
public NeuralNetwork copy(){
return new NeuralNetwork(this);
}

// Merges the weights and biases of two NeuralNetworks and returns a new object
// Merge-ratio: 50:50 (half of the values will be from nn1 and other half from nn2)
public NeuralNetwork merge(NeuralNetwork nn){
return this.merge(nn, 0.5);
}

// Merges the weights and biases of two NeuralNetworks and returns a new object
// Everything besides the weights and biases will be the same
// of the object on which this method is called (Learning Rate, activation function, etc.)
// Merge-ratio: defined by probability
public NeuralNetwork merge(NeuralNetwork nn, double probability){
// Check whether the nns have the same dimensions
if(!Arrays.equals(this.getDimensions(), nn.getDimensions())){
throw new WrongDimensionException(this.getDimensions(), nn.getDimensions());
}else{
NeuralNetwork result = this.copy();

for (int i = 0; i < result.weights.length; i++) {
result.weights[i] = MatrixUtilities.mergeMatrices(this.weights[i], nn.weights[i], probability);
}

for (int i = 0; i < result.biases.length; i++) {
result.biases[i] = MatrixUtilities.mergeMatrices(this.biases[i], nn.biases[i], probability);
}
return result;
}
}

// Gaussian mutation with given probability, Slightly modifies values (weights + biases) with given probability
// Probability: number between 0 and 1
// Depending on probability more/ less values will be mutated (e.g. prob = 1.0: all the values will be mutated)
public void mutate(double probability) {
applyMutation(weights, probability);
applyMutation(biases, probability);
}

// Adds a randomly generated gaussian number to each element of a Matrix in an array of matrices
// Probability: determines how many values will be modified
private void applyMutation(SimpleMatrix[] matrices, double probability) {
for (SimpleMatrix matrix : matrices) {
for (int j = 0; j < matrix.getNumElements(); j++) {
if (random.nextDouble() < probability) {
double offset = random.nextGaussian() / 2;
matrix.set(j, matrix.get(j) + offset);
}
}
}
}

// Generic function to calculate one layer
private SimpleMatrix calculateLayer(SimpleMatrix weights, SimpleMatrix bias, SimpleMatrix input, ActivationFunction activationFunction) {
// Calculate outputs of layer
Expand Down Expand Up @@ -236,4 +314,8 @@ public void setBiases(SimpleMatrix[] biases) {
this.biases = biases;
}

public int[] getDimensions(){
return new int[]{inputNodes, hiddenLayers, hiddenNodes, outputNodes};
}

}
10 changes: 10 additions & 0 deletions src/main/java/basicneuralnetwork/WrongDimensionException.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package basicneuralnetwork;

import java.util.Arrays;

/**
* Created by KimFeichtinger on 28.04.18.
*/
Expand All @@ -9,4 +11,12 @@ public WrongDimensionException(int actual, int expected, String layer){
super("Expected " + expected + " value(s) for " + layer + "-layer but got " + actual + ".");
}

public WrongDimensionException(int[] actual, int[] expected){
super("The dimensions of these two neural networks don't match: " + Arrays.toString(actual) + ", " + Arrays.toString(expected));
}

public WrongDimensionException(){
super("Dimensions don't match.");
}

}
39 changes: 0 additions & 39 deletions src/main/java/basicneuralnetwork/utilities/MatrixConverter.java

This file was deleted.

63 changes: 63 additions & 0 deletions src/main/java/basicneuralnetwork/utilities/MatrixUtilities.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package basicneuralnetwork.utilities;

import basicneuralnetwork.WrongDimensionException;
import org.ejml.simple.SimpleMatrix;

import java.util.Random;

/**
* Created by KimFeichtinger on 07.03.18.
*/
public class MatrixUtilities {

// Converts a 2D array into a SimpleMatrix
public static SimpleMatrix arrayToMatrix(double[] i) {
double[][] input = {i};
return new SimpleMatrix(input).transpose();
}

// Converts a SimpleMatrix into a 2D array
public static double[][] matrixTo2DArray(SimpleMatrix i) {
double[][] result = new double[i.numRows()][i.numCols()];

for (int j = 0; j < result.length; j++) {
for (int k = 0; k < result[0].length; k++) {
result[j][k] = i.get(j, k);
}
}
return result;
}

// Returns one specific column of a matrix as a 1D array
public static double[] getColumnFromMatrixAsArray(SimpleMatrix data, int column) {
double[] result = new double[data.numRows()];

for (int i = 0; i < result.length; i++) {
result[i] = data.get(i, column);
}

return result;
}

// Merge two matrices and return a new one
public static SimpleMatrix mergeMatrices(SimpleMatrix matrixA, SimpleMatrix matrixB, double probability) {
if (matrixA.numCols() != matrixB.numCols() || matrixA.numRows() != matrixB.numRows()) {
throw new WrongDimensionException();
} else {
Random random = new Random();
SimpleMatrix result = new SimpleMatrix(matrixA.numRows(), matrixA.numCols());

for (int i = 0; i < matrixA.getNumElements(); i++) {
// %-chance of replacing this value with the one from the input nn
if (random.nextDouble() > probability) {
result.set(i, matrixA.get(i));
} else {
result.set(i, matrixB.get(i));
}
}

return result;
}
}

}

0 comments on commit 422cf46

Please sign in to comment.