import java.io.*; import java.util.*; /** * Basic Neural Network that learns through back propagation. * A model of a network using sigmoidal Perceptrons connected in layers, starting with an input layer that accepts data from a file, passing through several hidden layers and ending with an output layer that prints to a file. * * @author Jeff Chen * @version %I%,%G% */ public class BackpropagationNetwork{ private ArrayList> neurons=new ArrayList>(); //each element in the ArrayList is an ArrayList of neurons representing a layer private ArrayList> edges=new ArrayList>(); private double[] input; private double[] output; /** * Constructs the Network with a variable number of layers and a variable number of neurons in each layer. */ public BackpropagationNetwork(){ neurons=new ArrayList>(); } /** * Adds a layer of edges to the end of the network. * There should be N-1 layers of edges for an N layer network. * The index I of each edge layer corresponds to the connection * between neurons of the Ith layer and the I+1th layer. */ public void addEdgeLayer(){ edges.add(new ArrayList()); } /** * Inserts a layer of edges at the specified position in the network. * * @param index index at which the edge layer is to be inserted. */ public void addEdgeLayer(int index){ edges.add(index,new ArrayList()); } /** * Adds a layer to the end of the network. * This layer will become the output layer. */ public void addLayer(){ neurons.add(new ArrayList()); } /** * Inserts a layer at the specified position in the network. * * @param index index at which the layer is to be inserted */ public void addLayer(int index){ neurons.add(index,new ArrayList()); } /** * Adds a neuron to a layer. * All neurons are added in sequential order. * * @param layer the layer in which the Neuron is to be inserted * @param n the Neuron to be inserted * @return the position of the Neuron in the layer */ public int addNeuron(int layer,Neuron n){ neurons.get(layer).add(n); return n.setIndex(neurons.get(layer).size()-1); } /** * Adds a neuron to a layer at the specified position in the layer. * * @param layer the layer in which the Neuron is to be inserted * @param pos the index at which the Neuron is to be inserted in the layer * @param n the Neuron to be inserted * @return the position of the Neuron in the layer */ public int addNeuron(int layer,int pos,Neuron n){ neurons.get(layer).add(pos,n); return n.setIndex(pos); } /** * Trains the network with the specified Case. * * @param a the Case to train the network on */ public void backProp(Case a){ backProp(a,run(a)[1]); } /** * Trains the network with the specified Case and error. * * @param a the Case to train the network on * @param err the error, calculated by subtracting expected output from actual output */ public void backProp(Case a, double err){ neurons.get(neurons.size()-1).get(0).backprop(a,err); } /** * Initializes the variables used in the Network according to the default scheme. * NOTE: this calls initWeights() as well. */ public void init(){ init(Neuron.RANDOM_WEIGHTS); } /** * Initializes the variables used in the Network according to the specified scheme. * NOTE: this calls initWeights() as well. *
* This needs to be called whenever a neuron is added or removed. * * @param scheme the scheme to use */ public void init(int scheme){ int num=0; ArrayList tmp=neurons.get(0); for(int n=0;n tmp; for(int n=0;n * Currently, the connections are not displayed. * The network is shown with each line as a new layer. *
* I stands for input neuron. *
* H stands for hidden neuron. *
* O stands for output neuron. *
* The number next to each letter is the number of inputs for that neuron. */ public void printNetwork(){ System.out.println(); for (int n=0;n * The first line is N, the number of layers in the network. *
* The lines 2 to N+1 each contain 1 integer, the number of neurons in each layer. *
* The remaining lines should be 3 space delimited integers on each line. *
* The first integer is the layer of the parent node, 0 based. *
* The second number is the index of the parent node within that layer, 0 based. *
* The third number is the index of the child node, automatically assumed to be in the next layer. * The index is also 0 based. * *

* ex. the line: 0 5 2 * would create the connection between the 6th node in the 1st layer and the 3rd node in the 2nd layer. * * @param f file to read the structure from */ public void resolveStructure(File f)throws IOException{ BufferedReader in=new BufferedReader(new FileReader(f)); String tmp=null; StringTokenizer st=null; int a=0,b=0,c=0; Neuron n1=null,n2=null; a=Integer.parseInt(in.readLine()); for(int n=0;n * NOTE: currently, error only works with one output neuron, as otherwise error does not calculate correctly. * * @param a the Case to run * @return a double array of length 2, with the first element being the output, and the second being the error */ public double[] run(Case a){ double out=this.run(a.getInput())[0]; return new double[]{out,a.getOutput()[0]-out}; } /** * Runs the entire network with the given input. *
* Note that for the inputs to get to multiple neurons, its better to have a dummy input layer * that has multiple children, which lead to the real network. * * @param input the input to be fed into the network. * @return the output generated by all the output nodes */ public double[] run(double[] input){ double[] out=new double[neurons.get(neurons.size()-1).size()]; for(int n=0;n * Recommended to be called instead of calling writeStructure() and writeWeights() separately. * * @param structureFile file to print the structure to * @param weightsFile file to print the weights of the neurons to */ public void writeNetwork(File structureFile,File weightsFile)throws IOException{ writeStructure(structureFile); writeWeights(weightsFile); } /** * Prints out the structure of the network to the specified file. *
* This is printed in the same format that the network can read in, effectively storing the structure. *
* NOTE: Only the structure of the network is saved, not the weights of the neurons themselves. * For this reason, it is advisable to call writeNetwork() instead, which will print both the structure and the weights. *
* Alternatively, you may call writeWeights() separately. * * @param f file to print the structure to */ public void writeStructure(File f)throws IOException{ PrintStream out=new PrintStream(new FileOutputStream(f)); out.println(neurons.size()); for(int n=0;n tmp; for(int n=0;n * Each line consists of 3 space separated integers followed by a double. *
* The first integer is the 0 based layer of the neuron that has the weight. *
* The second integer is the 0 based index of the neuron. *
* The third integer is the 0 based location of the weight in the weights[] array. *
* The double is the weight itself. * * @param f file to print the weights of the neurons to */ public void writeWeights(File f)throws IOException{ PrintStream out=new PrintStream(new FileOutputStream(f)); double[] tmp; for(int n=0;n