changeset 2:9655c8362b25

Added the Perceptron class and the perceptron_test testing program.
author revcompgeek
date Sat, 05 Apr 2008 23:41:30 -0600
parents 5dd9f598bcd8
children 314d68bafeff
files trunk/aid/nn/perceptron.d trunk/dsss.conf trunk/ga_code.d trunk/perceptron_test.d
diffstat 4 files changed, 426 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- /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;
+	}
+}
+
--- 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]
--- 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)
+}
--- /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]);
+}