GithubHelp home page GithubHelp logo

tomlamprecht / easy-ml-for-java Goto Github PK

View Code? Open in Web Editor NEW
36.0 3.0 4.0 407 KB

A Java Framework to implement Machine Learning using Neural Networks and a Genetic Algorithm

License: MIT License

Java 100.00%
artificial-intelligence genetic-algorithm java machine-learning neural-network open-source open-source-contributions

easy-ml-for-java's Introduction

Easy ML for Java

The easiest way to start with Machine Learning in Java!

Explore our Website»

Version 1.0

Tweet


Overview

This Framework can be used to implement a Machine Learning Algorithm.
It uses Neural Networks which are beeing trained by a Genetic Algorithm
The motivation for this Framework was that many universities and schools teach as a first language Java to their students. Sadly for Java there aren't many Machine Learning Libraries and Frameworks out there, and if there are you most likely don't want to spend months trying to learn how to use it just to try out some stuff.
Hence, Easy ML for Java:
It's easy to use and designed to be played around with. You can choose the settings of the training process and see how this affects the outcome. There is no wrong way to use Easy ML for Java!


Get Started

To create your own AI you first need to have at least a basic understanding of the below listet classes. To implement an AI for your own personal problem you will have to follow these steps:

  1. Design the structure of the NeuralNet using the NeuralNetBuilder
  2. implement a NeuralNetFitnessFunction
  3. Choose and configure the values of Selector, Recombiner and Mutator
  4. Configure the parameters of the Genetic Algorithm
  5. combine everything in the GeneticAlgorithm.Builder class

Finding the correct configurations is a very challenging task and you should always use Loggers to help you find the correct ones!

An abstract example could look like this:

        Selector<NeuralNetIndividual> SELECTOR = new EliteSelector<>( 0.1 );
    Recombiner<NeuralNetIndividual> RECOMBINER = new NNUniformCrossoverRecombiner( 2 );
    Mutator<NeuralNetIndividual> MUTATOR = new NNRandomMutator( 0.9, 0.4, new Randomizer( -0.01, 0.01 ), 0.01 );
    int GENS = 50;
    NeuralNetFitnessFunction FITNESS_FUNCTION = (nn) -> //calculate Fitness; 

    NeuralNetSupplier neuralNetSupplier = ( ) -> new NeuralNet.Builder( NEURALNET_INPUT_SIZE, NEURALNET_OUTPUT_SIZE )
                .addLayer( HIDDEN_LAYER_SIZE )
                .withActivationFunction( x -> x )
                .build( );
                
        NeuralNetPopulationSupplier supplier = new NeuralNetPopulationSupplier( neuralNetSupplier, FITNESS_FUNCTION, POP_SIZE );

  GeneticAlgorithm<NeuralNetIndividual> geneticAlgorithm =
                new GeneticAlgorithm.Builder<>( supplier, GENS, SELECTOR )
                        .withRecombiner( RECOMBINER )
                        .withMutator( MUTATOR )
                        .build( );
    
    geneticAlgorithm.solve();

EXAMPLE

This example will show how to use the Framework for a number prediction based on the simple mathematic equation: f(x) = 2x
EXAMPLE

In this example the framework is used to predict if someone has diabetes or not based on given trainingsdata.
DIABETES EXAMPLE

You can find a full tutorial for this example on our Website

This one includes a basic implementation of the game "Snake" and will show the use of a more complex scenario of the Framework.
SNAKE EXAMPLE


Classes and Interfaces of the Neural Network:

NeuralNet

This is the "brain" of the Aritfical Intelligence. For further information about the way this Neural Network works read the Wikipedia-Article about it.
To create a NeuralNet you need to use Builder of this class. It is necessary to at least provide the input and the output size of this Network. Its recommended though to also provide at least 1 hidden layer for more complex problems. A Example for a Neural Network with the input size 100 output size 2 and a hidden layer with 50 nodes could look like this:

NeuralNet neuralNet = new NeuralNet.Builder( 100, 12 )
                .addLayer( 50 )
                .build()


with the method

withActivationFunction(...)

you can provide your own Activation Function. The default one is

$$ f(x) = \frac{1 + tanh\Bigl(\frac{x}{2}\Bigr)}{2} $$


The most simple one would be:

withActicationFunction(x -> x)

Classes and Interfaces of the Genetic Algorithm:

Individual

A Indivdual displays one Element in the Population of the Genetic Algorithm. The implementation details may differ from problem to problem and have to be therefor implemented by the user. To support the programmer with this task the Framework provides a basic structure to implement a Individual.

This is a simplified Version of the Individual interface

public interface Individual<T extends Individual<T>>{
	
     void calcFitness();
     
     double getFitness();
     
     T copy();

In your implementation of Invdividual T should always be the implementing Class itself
e.g:

public class IndividualImplementation extends Individual<IndividualImplementation>{...}

  • void calcFitness()
    This method will be used to calculate a Fitness value. Basic Rule here is: The higher the Fitness value the better the Individual!

  • double getFitness()
    Should return the fitness value calculated in the calcFitness Method. Due to Performance issues the calculation of the Fitness Value should always be outsourced to the calcFitness() method.

  • T copy()
    The copy method should be self explanatory: provides a copy of the Individual without sharing any rerences of to the Individual itself


PopulationSupplier

The Population Supplier is used to supply the Genetic Algorithm with a Population of Individuals. A example of a PopulationSupplier using Java 8 Lambda expression could looks like this:

() -> new Population(//List of Individuals);

The Framework already provides a Implementation of a PopulationSupplier to read a Population from a File:
PopulationByFileSupplier


Selector

The task of the Selector is to throw out the worst Individuals of the population based on their fitness values. There are many ways to achieve such a selection using differnt mathematical approaches.
This Framework provides 3 of them:

It is also possible to provide your own implementation though. To do this you will have to Implement the Selector-Interface. Before starting to write your own Selector you may want to look into the implementation of the already given Selectors first.
A possible EliteSelector could look like this:

new EliteSelector<>( 0.1 );

Recombiner

The Recombiner may be used to fill up the Popluation again after the selection process. Again there are many different approaches.
This framework provides 2 of them:

It's obviously also possible to provide your own Recombiner by Implementing the Recombiner-Interface
A possible NNUniformCrossOverRecombiner could look like:

new NNUniformCrossoverRecombiner( 2 );

Mutator

After the Selection and Recombination process its possible to provide a third processing step called Mutator. Some Individuals may randomly get changed to increase the probability of a positive mutation.
This framework provides one of them:

As always it's possible to provide your own Mutator by implementing the Mutator-Interface.
A possible NNRandomMutator could look like:

new NNRandomMutator( 0.2, 0.4, new Randomizer( -0.01, 0.01 ), 0.01 );

Logger

This Interface is just used to log the huge amount of metadata that is beeing generated in the evolution process. There are 3 Implementations of this Interface already given:

GraphPlotLogger

takes as arguements in the Constructor

  1. Plotting Interval as int - the interval in which the file gets created not in which the data is beeing logged
  2. Filename as String - Name of the file without file-ending
  3. Chart Title as String (Optional) - Title of the resulting chart, default: "Plot for Population size: {size}"
  4. Line Generators as LineGenerator[] - The parser for metadata into plottable double values

LineGenerator is a abstract class that is used to parse a Population into a single double value so its plottable. You may implement your own LineGenerators but the Framework provides 4 of them:

  • AvgFitnessLine - parses the Population to its average Fitness value
  • MaxFitnessLine - parses the Population to its maximum Fitness value
  • MinFitnessLine - parses the Population to its minimum Fitness value
  • NQuantilFitnessLine - parses the Population into its Fitness Value of its n-quantil

A possible GraphPlotLogger could look like this:

int plottingInterval = 100;
double quantilOf20Percent = 0.2
double quantilOf80Percent = 0.8
new GraphPlotLogger(plottingInterval, "plot",  
 new AvgFitnessLine(),  
 new MaxFitnessLine(),  
 new WorstFitnessLine(),  
 new NQuantilFitnessLine(quantilOf20Percent),  
 new NQuantilFitnessLine(quantilOf80Pecent))

a resulting graph may look like this: image


Genetic Algorithm

To create your own Genetic Algorithm you will need to build one using the GeneticAlgorithm.Builder Class.This class can be given the many optional parameters, the most essential ones will be listed below:

  • PopulationSupplier - provides the first Population
  • Generations - Amount of generations that should be calculated
  • Selector - Is used for the Selection process
  • Recombiner - Is used for the recombination process (Optional)
  • Mutator - Is used for the mutation process (Optional)
    A abstract Example for a GeneticAlgorithm could look like this:
 new GeneticAlgorithm.Builder<>( POPULATION_SUPPLIER, GENS_AMOUNT, SELECTOR )
                        .withRecombiner( RECOMBINER )
                        .withMutator( MUTATOR )
                        .build( );

With the .withMutliThreaded(//amount of Threads); method it's also possible to processes the evolution in a parallel matter.


Classes and Interfaces of the NetworkTrainer

Combination of Genetic Algorithm and Neural Network

NeuralNetFitnessFunction

This Interface provides one Method

 double calculateFitness( NeuralNet neuralNet );

which is used to calculate the Fitness Values of a NeuralNetIndividual.
See Individual.calcFitness()


NeuralNetIndividual

This is the combination between a NeuralNet and a Individual. The constructor of this class therefor takes a NeuralNet and a NeuralNetFitnessFunction to create a NeuralNetIndividual.


NeuralNetPopulationSupplier

This class provides a Population of NeuralNetIndviduals, using

 NeuralNetPopulationSupplier supplier = new NeuralNetPopulationSupplier( () -> new NeuralNet.Builder..., (nn) -> /*calculate Fitness*/, POP_SIZE );

License & Copyright

© Tom Lamprecht, David Kupper - FHWS Fakultät Informatik

easy-ml-for-java's People

Contributors

kuppere2n avatar mrredrhino avatar tomlamprecht avatar voxtone avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

easy-ml-for-java's Issues

Add Backpropagation as alternative to Genetic Algorithm

Instead of the Genetic Algorithm you could also use a Backpropagation technique. The Algorithm for the Backpropagation does need to be implemented first. Not necessarily already included into the structure of the Program. The algorithm alone would be already enough.
We basically need a Backpropagation Algorithm thats based on the Neural Network class

Exception in thread "main" java.lang.BootstrapMethodError: bootstrap method initialization exception

When using NQuantilFitnessLine or WorstFitnessLine graph line plotters, the following exception is raised:

Exception in thread "main" java.lang.BootstrapMethodError: bootstrap method initialization exception
	at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:188)
	at java.base/java.lang.invoke.CallSite.makeSite(CallSite.java:315)
	at java.base/java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:281)
	at java.base/java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:271)
	at de.fhws.easyml.geneticalgorithm.logger.loggers.graphplotter.lines.NQuantilFitnessLine.lambda$0(NQuantilFitnessLine.java:25)
	at de.fhws.easyml.geneticalgorithm.logger.loggers.graphplotter.lines.LineGenerator.convert(LineGenerator.java:34)
	at de.fhws.easyml.geneticalgorithm.logger.loggers.graphplotter.lines.LineGenerator.log(LineGenerator.java:30)
	at de.fhws.easyml.geneticalgorithm.logger.loggers.graphplotter.GraphPlotLogger.lambda$0(GraphPlotLogger.java:82)
	at java.base/java.util.Arrays$ArrayList.forEach(Arrays.java:4204)
	at de.fhws.easyml.geneticalgorithm.logger.loggers.graphplotter.GraphPlotLogger.savePopulationValues(GraphPlotLogger.java:82)
	at de.fhws.easyml.geneticalgorithm.logger.loggers.graphplotter.GraphPlotLogger.log(GraphPlotLogger.java:65)
	at de.fhws.easyml.geneticalgorithm.GeneticAlgorithm.lambda$1(GeneticAlgorithm.java:125)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at de.fhws.easyml.geneticalgorithm.GeneticAlgorithm.callLoggers(GeneticAlgorithm.java:125)
	at de.fhws.easyml.geneticalgorithm.GeneticAlgorithm.prepareNextEvolution(GeneticAlgorithm.java:117)
	at de.fhws.easyml.geneticalgorithm.GeneticAlgorithm.nextGen(GeneticAlgorithm.java:103)
	at de.fhws.easyml.geneticalgorithm.GeneticAlgorithm.evolute(GeneticAlgorithm.java:82)
	at de.fhws.easyml.geneticalgorithm.GeneticAlgorithm.solve(GeneticAlgorithm.java:65)
	at za.co.oevents.cify.easyml.Train.main(Train.java:84)
Caused by: java.lang.invoke.LambdaConversionException: Invalid receiver type class java.lang.Object; not a subtype of implementation type interface de.fhws.easyml.geneticalgorithm.Individual
	at java.base/java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:273)
	at java.base/java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:340)
	at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:134)
	... 18 more

This can be observed by simply running the snake training example.

Tested on Linux with JDK11 and JDK 17

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.