# HG changeset patch # User revcompgeek # Date 1207460490 21600 # Node ID 9655c8362b254a579b85f0c98e409b90de8dc724 # Parent 5dd9f598bcd8aa1ba971163e74a3d7066e837e16 Added the Perceptron class and the perceptron_test testing program. diff -r 5dd9f598bcd8 -r 9655c8362b25 trunk/aid/nn/perceptron.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/aid/nn/perceptron.d Sat Apr 05 23:41:30 2008 -0600 @@ -0,0 +1,142 @@ +module aid.nn.perceptron; + +import std.random; +import std.math; +import std.string; + +double rnd(){ // The function that should be included in every math library! + return (cast(double)rand())/uint.max; +} + +class InputException : Exception { + this(char[] message){ + super(message); + } +} + +// The output functions + +alias double function(double) OutputFunction; + +double sign(double y){ + if(y>0) return 1; + return -1; +} + +double sigmoid(double x){ + return 1/(1+exp(-x)); +} + +double tanh(double x){ + return cast(double)tanh(cast(real)x); +} + +// End output functions + + +class perceptron { + private int numInputs; + private double[] weights; + private OutputFunction func; + public double learningRate; + + /** + * This is the constructor for loading the neural network from a string. + * + * Params: + * savedString = The string that was output from the save function. + * + * Throws: + * Throws an InputException when the string is in the wrong format. + */ + + public this(char[] savedString){ + //TODO: Impliment loading! + throw new Exception("Not implimented."); + } + + // This is private because one type of perceptron training requires the use of the sign function. + this(int numInputs, double learningRate=0.3, bool randomize=true,OutputFunction f=null){ + this.numInputs = numInputs + 1; + weights.length = numInputs + 1; + func = f; + this.learningRate = learningRate; + if(randomize){ + for(int i = 0; i < this.numInputs; i++){ + weights[i] = rnd() * 2 - 1; + } + } else { + for(int i = 0; i < this.numInputs; i++){ + weights[i] = 0; + } + } + } + + /** + * Evaluates the neural network. + * + * Params: + * inputs = The set of inputs to evaluate. + * + * Returns: 1 to indicate true, -1 for false + */ + + double evaluate(double[] inputs){ + if(inputs.length != numInputs-1) throw new InputException("Wrong input length. %d %d"); + double total = weights[0]; + for(int i = 1; i < numInputs; i++){ + total += inputs[i-1] * weights[i]; + } + if(func != null) return func(total); + return total; + } + + public double[] getWeights(){ + return weights.dup; + } + + /** + * Trains the neural network. This must be overloaded in a subclass. + * + * Params: + * inputs = The array of inputs to the nerual network. + * targetOutput = The output that the nerual network should give. + */ + /* Returns: True if it trained the network, false if not. + */ + + void train(double[] inputs,double targetOutput){ + if(inputs.length != numInputs-1) throw new InputException("Wrong input length."); + double output = evaluate(inputs); + double error = this.learningRate * (targetOutput - output); + weights[0] += error; + for(int i = 1; i < numInputs; i++){ + weights[i] += error * inputs[i-1]; + } + } + + /** + * Calculates the error based on the sum squared error function. + * + * Params: + * inputs = An array of arrays of all testing inputs. + * outputs = An array of all the outputs that the cooresponding inputs should have. + * + * Returns: + * The error value. + */ + + double getErrorValue(double[][] inputsArray, double[] outputsArray){ + double total = 0; + if(inputsArray.length != outputsArray.length) throw new InputException("inputsArray and outputsArray must be the same length"); + if(inputsArray.length < 1) throw new InputException("Must have at least 1 training example"); + double output,temp; + for(int i = 0; i < inputsArray.length; i++){ + output=evaluate(inputsArray[i]); + temp = outputsArray[i] - output; + total += temp*temp; + } + return total*0.5; + } +} + diff -r 5dd9f598bcd8 -r 9655c8362b25 trunk/dsss.conf --- a/trunk/dsss.conf Sat Mar 29 12:30:20 2008 -0600 +++ b/trunk/dsss.conf Sat Apr 05 23:41:30 2008 -0600 @@ -1,5 +1,7 @@ [aid] exclude=aid/maze/ +exclude+=aid/containers/ [ga_code.d] [ga_maze.d] [mazegen.d] +[perceptron_test.d] diff -r 5dd9f598bcd8 -r 9655c8362b25 trunk/ga_code.d --- a/trunk/ga_code.d Sat Mar 29 12:30:20 2008 -0600 +++ b/trunk/ga_code.d Sat Apr 05 23:41:30 2008 -0600 @@ -1,2 +1,231 @@ -/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + Genetic Algorithm Code Test + + Author: Matt P. + + Version: 0.5 + + + + Runs a genetic algorithm test using a simple code + + guessing algorithm. + + + + Usage: ga_code [options] + + Options: + + -h + + Prints this information. + + -v + + Turns on verbose output. + + -m float + + Specifies the mutation rate for each generation. + + Default: 0.05 + + + + -d float + + Specifies the survival rate for each generation. + + Default: 0.5 + + + + -p integer + + Specifies the population for each generation. + + Default: 1000 + + + + -l integer + + Specifies how long the code to be guessed is. + + Default: 20 + + + + -n integer + + Specifies how many possibilities there are for + + each character of the code string. + + Default: 20 + + + + -r integer + + Specifies the number of times to repeat the test. + + Default: 20 + + + + -c char + + Specifies the crossover type + + s / 1 - One point crossover + + t / 2 - Two point crossover + + u / 3 - Uniform crossover + + Default: s + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/ module ga_code; import aid.ga; import tango.math.Random; import tango.math.Math; import tango.io.Stdout; //import tango.text.String; import Float = tango.text.convert.Float; import Integer = tango.text.convert.Integer; import bcd.sys.times; char[] target; int numChars = 20; int stringLen = 20; double average(double[] array){ double avg = 0; foreach (double v; array) avg += v; avg /= array.length; return avg; } double stdDev(double[] array){ double avg = average(array); double stddev = 0; foreach (double v; array){ stddev += (v - avg) * (v - avg); } stddev = sqrt(stddev/array.length); return stddev; } class Foo { public double calculateFitness(char[] gene){ double fitness=0; for (uint i = 0; i < stringLen; i++){ if (gene[i] == target[i]) fitness += 1; } return fitness; } public char[] getRandomGenotype(){ char[] t; t.length = stringLen; for (int i = 0; i < stringLen; i++){ t[i] = Random.shared.next(numChars) + 'a'; } return t; } public char getRandomChar(uint index){ return cast(char)(Random.shared.next(numChars)+'a'); } } void usage(){ auto s = "Usage: ga_test [options] \n" "Options: \n" "-h \n" " Prints this information. \n" "-v \n" " Turns on verbose output. \n" "-m float \n" " Specifies the mutation rate for each generation. \n" " Default: 0.05 \n" " \n" "-d float \n" " Specifies the survival rate for each generation. \n" " Default: 0.5 \n" " \n" "-p integer \n" " Specifies the population for each generation. \n" " Default: 1000 \n" " \n" "-l integer \n" " Specifies how long the code to be guessed is. \n" " Default: 20 \n" " \n" "-n integer \n" " Specifies how many possibilities there are for \n" " each character of the code string. \n" " Default: 20 \n" " \n" "-r integer \n" " Specifies the number of times to repeat the test.\n" " Default: 20 \n" " \n" "-c char \n" " Specifies the crossover type \n" " s / 1 - One point crossover \n" " t / 2 - Two point crossover \n" " u / 3 - Uniform crossover \n" " Default: s "; Stdout(s).newline; } void main(char[][] args){ Foo f = new Foo(); GeneticAlgorithm ga = new GeneticAlgorithm(); ga.calculateFitness = &f.calculateFitness; ga.getRandomGenotype = &f.getRandomGenotype; ga.getRandomChar = &f.getRandomChar; ga.fitnessThreshold = stringLen; int rep = 20; bool dotime=true; for (auto i = 1; i < args.length; i++){ char[] arg = args[i]; switch (arg){ case "-h": usage(); return; case "-m": ga.mutationRate = Float.parse(args[++i]); break; case "-d": ga.survivalRate = Float.parse(args[++i]); break; case "-p": ga.startPopulation = Integer.parse(args[++i]); break; case "-l": stringLen = Integer.parse(args[++i]); ga.fitnessThreshold = stringLen; break; case "-n": numChars = Integer.parse(args[++i]); break; case "-r": rep = Integer.parse(args[++i]); break; case "-v": ga.verbose = true; break; case "-c": auto t = args[++i]; switch (t){ case "s","1": ga.crossoverType = 1; break; case "t","2": ga.crossoverType = 2; break; default: ga.crossoverType = 3; } break; case "-b": ga.bailout = Integer.parse(args[++i]); break; case "-t": dotime = false; break; default: Stdout.format("Unknown parameter: {}",arg).newline; usage(); return; } } - target = ga.getRandomGenotype(); double[] time; double[] gens; long start,end; for(auto i = 0; i < rep; i++){ if(dotime) start = iutime(); uint generations = ga.run(); if(dotime) end = iutime() - start; if(dotime) time ~= end; gens ~= generations; } if(dotime) Stdout.format("{0}, {1:.5}, {2}, {3:.5}", average(gens),stdDev(gens),average(time),stdDev(time)).newline; else Stdout.format("{0}, {1:.5}", average(gens),stdDev(gens)).newline; //", {2}, {3:.5}" ,average(time),stdDev(time) } \ No newline at end of file +/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + Genetic Algorithm Code Test + + + Author: Matt P. + + + Version: 0.5 + + + + + + Runs a genetic algorithm test using a simple code + + + guessing algorithm. + + + + + + Usage: ga_code [options] + + + Options: + + + -h + + + Prints this information. + + + -v + + + Turns on verbose output. + + + -m float + + + Specifies the mutation rate for each generation. + + + Default: 0.05 + + + + + + -d float + + + Specifies the survival rate for each generation. + + + Default: 0.5 + + + + + + -p integer + + + Specifies the population for each generation. + + + Default: 1000 + + + + + + -l integer + + + Specifies how long the code to be guessed is. + + + Default: 20 + + + + + + -n integer + + + Specifies how many possibilities there are for + + + each character of the code string. + + + Default: 20 + + + + + + -r integer + + + Specifies the number of times to repeat the test. + + + Default: 20 + + + + + + -c char + + + Specifies the crossover type + + + s / 1 - One point crossover + + + t / 2 - Two point crossover + + + u / 3 - Uniform crossover + + + Default: s + + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/ + + +module ga_code; + +import aid.ga; +import tango.math.Random; +import tango.math.Math; +import tango.io.Stdout; +//import tango.text.String; +import Float = tango.text.convert.Float; +import Integer = tango.text.convert.Integer; + +import bcd.sys.times; + +char[] target; + +int numChars = 20; +int stringLen = 20; + +double average(double[] array){ + double avg = 0; + foreach (double v; array) + avg += v; + avg /= array.length; + return avg; +} + +double stdDev(double[] array){ + double avg = average(array); + double stddev = 0; + foreach (double v; array){ + stddev += (v - avg) * (v - avg); + } + stddev = sqrt(stddev/array.length); + return stddev; +} + +class Foo { + public double calculateFitness(char[] gene){ + double fitness=0; + for (uint i = 0; i < stringLen; i++){ + if (gene[i] == target[i]) + fitness += 1; + } + return fitness; + } + + public char[] getRandomGenotype(){ + char[] t; + t.length = stringLen; + for (int i = 0; i < stringLen; i++){ + t[i] = Random.shared.next(numChars) + 'a'; + } + return t; + } + + public char getRandomChar(uint index){ + return cast(char)(Random.shared.next(numChars)+'a'); + } +} + +void usage(){ + auto s = + "Usage: ga_test [options] \n" + "Options: \n" + "-h \n" + " Prints this information. \n" + "-v \n" + " Turns on verbose output. \n" + "-m float \n" + " Specifies the mutation rate for each generation. \n" + " Default: 0.05 \n" + " \n" + "-d float \n" + " Specifies the survival rate for each generation. \n" + " Default: 0.5 \n" + " \n" + "-p integer \n" + " Specifies the population for each generation. \n" + " Default: 1000 \n" + " \n" + "-l integer \n" + " Specifies how long the code to be guessed is. \n" + " Default: 20 \n" + " \n" + "-n integer \n" + " Specifies how many possibilities there are for \n" + " each character of the code string. \n" + " Default: 20 \n" + " \n" + "-r integer \n" + " Specifies the number of times to repeat the test.\n" + " Default: 20 \n" + " \n" + "-c char \n" + " Specifies the crossover type \n" + " s / 1 - One point crossover \n" + " t / 2 - Two point crossover \n" + " u / 3 - Uniform crossover \n" + " Default: s "; + Stdout(s).newline; +} + +void main(char[][] args){ + Foo f = new Foo(); + GeneticAlgorithm ga = new GeneticAlgorithm(); + ga.calculateFitness = &f.calculateFitness; + ga.getRandomGenotype = &f.getRandomGenotype; + ga.getRandomChar = &f.getRandomChar; + ga.fitnessThreshold = stringLen; + int rep = 20; + bool dotime=true; + + + for (auto i = 1; i < args.length; i++){ + char[] arg = args[i]; + switch (arg){ + case "-h": + usage(); + return; + case "-m": + ga.mutationRate = Float.parse(args[++i]); + break; + case "-d": + ga.survivalRate = Float.parse(args[++i]); + break; + case "-p": + ga.startPopulation = Integer.parse(args[++i]); + break; + case "-l": + stringLen = Integer.parse(args[++i]); + ga.fitnessThreshold = stringLen; + break; + case "-n": + numChars = Integer.parse(args[++i]); + break; + case "-r": + rep = Integer.parse(args[++i]); + break; + case "-v": + ga.verbose = true; + break; + case "-c": + auto t = args[++i]; + switch (t){ + case "s","1": + ga.crossoverType = 1; + break; + case "t","2": + ga.crossoverType = 2; + break; + default: + ga.crossoverType = 3; + } + break; + case "-b": + ga.bailout = Integer.parse(args[++i]); + break; + case "-t": + dotime = false; + break; + default: + Stdout.format("Unknown parameter: {}",arg).newline; + usage(); + return; + } + } + target = ga.getRandomGenotype(); + + double[] time; + double[] gens; + long start,end; + + for(auto i = 0; i < rep; i++){ + if(dotime) start = iutime(); + uint generations = ga.run(); + if(dotime) end = iutime() - start; + + if(dotime) time ~= end; + gens ~= generations; + } + + if(dotime) Stdout.format("{0}, {1:.5}, {2}, {3:.5}", average(gens),stdDev(gens),average(time),stdDev(time)).newline; + else Stdout.format("{0}, {1:.5}", average(gens),stdDev(gens)).newline; //", {2}, {3:.5}" ,average(time),stdDev(time) +} diff -r 5dd9f598bcd8 -r 9655c8362b25 trunk/perceptron_test.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/perceptron_test.d Sat Apr 05 23:41:30 2008 -0600 @@ -0,0 +1,51 @@ +module perceptron_test; + +import aid.nn.perceptron; +import std.stdio; +import std.string; +import std.conv; +import std.math; + +void main(char[][] args){ + perceptron nn; + double[][] inputs = [[ 0, 0], + [ 0, 1], + [ 1, 0], + [ 1, 1]]; + double[] outputs = [ 0, 0, 0, 1]; + double learningRate = 0.3; + int numReps = 20; + + if(args.length > 1) learningRate = toDouble(args[1]); + if(args.length > 2) numReps = toInt(args[2]); + + nn = new perceptron(2,learningRate,true,null); + + int iter = 0; + double[] weights; + double t,difference; + while(iter <= numReps){ + weights=nn.getWeights(); + writefln("[ %f, %f, %f ]",weights[0],weights[1],weights[2]); + //Evaluate + for(int i = 0; i < 4; i++){ + t = nn.evaluate(inputs[i]); + writefln(" D: %f %f",t,outputs[i]); + difference = abs(t-outputs[i]); + } + writefln("%d: Total Difference: %f",iter,difference); + // End evaluate + + // Train + for(int i = 0; i < 4; i++){ + nn.train(inputs[i],outputs[i]); + } + writefln("%d: Error Value: %f",iter,nn.getErrorValue(inputs,outputs)); + writefln(); + + iter++; + //if(iter > 20) break; + } + weights=nn.getWeights(); + writefln("[ %f, %f, %f ]",weights[0],weights[1],weights[2]); +}