Get the entire book!
Introduction to Neural Networks with Java

Now that you have see how to use the neural network class we will examine a more complex example function. We will create a simple program that allows you to solve the XOR problem. You will also be able to enter other logic gates as well. The program that we will examine is shown in Figure 3.10.


Figure 3.10: The XOR problem

This program is implemented as a single class. The complete listing for this example is shown in Listing 3.3.

Listing 3.3: User interface to an XOR problem solution

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.text.*;

public class XorExample extends JFrame implements 
ActionListener,Runnable {

  protected JButton btnTrain;
  protected JButton btnRun;
  protected JButton btnQuit;
  protected JLabel status;
  protected Thread worker = null;
  protected final static int NUM_INPUT = 2;
  protected final static int NUM_OUTPUT = 1;
  protected final static int NUM_HIDDEN = 3;
  protected final static double RATE = 0.5;
  protected final static double MOMENTUM = 0.7;
  protected JTextField data[][] = new JTextField[4][4];
  protected Network network;

  /**
   * Constructor. Setup the components.
   */
  public XorExample()
  {
    setTitle("XOR Solution");    
    network = new Network(
                         NUM_INPUT,
                         NUM_HIDDEN,
                         NUM_OUTPUT,
                         RATE,
                         MOMENTUM);

    Container content = getContentPane();

    GridBagLayout gridbag = new GridBagLayout();
    GridBagConstraints c = new GridBagConstraints();
    content.setLayout(gridbag);

    c.fill = GridBagConstraints.NONE;
    c.weightx = 1.0;

    // Training input label
    c.gridwidth = GridBagConstraints.REMAINDER; //end row
    c.anchor = GridBagConstraints.NORTHWEST;
    content.add(
               new JLabel(
                         "Enter training data:"),c);         

    JPanel grid = new JPanel();
    grid.setLayout(new GridLayout(5,4));
    grid.add(new JLabel("IN1"));
    grid.add(new JLabel("IN2"));
    grid.add(new JLabel("Expected OUT   "));
    grid.add(new JLabel("Actual OUT"));

    for ( int i=0;i<4;i++ ) {
      int x = (i&1);
      int y = (i&2)>>1;
      grid.add(data[i][0] = new JTextField(""+y));
      grid.add(data[i][1] = new JTextField(""+x));
      grid.add(data[i][2] = new JTextField(""+(x^y)));
      grid.add(data[i][3] = new JTextField("??"));
      data[i][0].setEditable(false);
      data[i][1].setEditable(false);
      data[i][3].setEditable(false);
    }   

    content.add(grid,c);

    // the button panel
    JPanel buttonPanel = new JPanel(new FlowLayout());
    buttonPanel.add(btnTrain = new JButton("Train"));
    buttonPanel.add(btnRun = new JButton("Run"));
    buttonPanel.add(btnQuit = new JButton("Quit"));
    btnTrain.addActionListener(this);
    btnRun.addActionListener(this);
    btnQuit.addActionListener(this);

    // Add the button panel
    c.gridwidth = GridBagConstraints.REMAINDER; //end row
    c.anchor = GridBagConstraints.CENTER;
    content.add(buttonPanel,c);

    // Training input label
    c.gridwidth = GridBagConstraints.REMAINDER; //end row
    c.anchor = GridBagConstraints.NORTHWEST;
    content.add(
               status = new JLabel("Click train to begin training..."),c);         

    // adjust size and position   
    pack();
    Toolkit toolkit = Toolkit.getDefaultToolkit();
    Dimension d = toolkit.getScreenSize();
    setLocation(
               (int)(d.width-this.getSize().getWidth())/2,
               (int)(d.height-this.getSize().getHeight())/2 );
    setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    setResizable(false);        

    btnRun.setEnabled(false);
  }

  /**
   * The main function, just display the JFrame.
   * 
   * @param args No arguments are used.
   */
  public static void main(String args[])
  {
    (new XorExample()).show(true);
  }

  /**
   * Called when the user clicks one of the three
   * buttons.
   * 
   * @param e The event.
   */
  public void actionPerformed(ActionEvent e)
  {
    if ( e.getSource()==btnQuit )
      System.exit(0);
    else if ( e.getSource()==btnTrain )
      train();
    else if ( e.getSource()==btnRun )
      evaluate();
  }

  /**
   * Called when the user clicks the run button.
   */
  protected void evaluate()
  {
    double xorData[][] = getGrid();
    int update=0;

    for (int i=0;i<4;i++) {
      NumberFormat nf = NumberFormat.getInstance();            
      double d[] = network.computeOutputs(xorData[i]);
      data[i][3].setText(nf.format(d[0]));
    }

  }


  /**
  * Called when the user clicks the train button.
  */
  protected void train()
  {
    if ( worker != null )
      worker = null;
    worker = new Thread(this);
    worker.setPriority(Thread.MIN_PRIORITY);
    worker.start();    
  }

  /**
  * The thread worker, used for training
  */
  public void run()
  {
    double xorData[][] = getGrid();
    double xorIdeal[][] = getIdeal();
    int update=0;

    int max = 10000;
    for (int i=0;i<max;i++) {
      for (int j=0;j<xorData.length;j++) {
        network.computeOutputs(xorData[j]);
        network.calcError(xorIdeal[j]);
        network.learn();
      }

      update++;
      if (update==100) {
        status.setText( "Cycles Left:" + (max-i) + ",Error:" + network.getError(xorData.length) );
        update=0;
      }
    }
    btnRun.setEnabled(true);
  }


  /**
   * Called to generate an array of doubles based on
   * the training data that the user has entered.
   * 
   * @return An array of doubles
   */
  double [][]getGrid()
  {
    double array[][] = new double[4][2];

    for ( int i=0;i<4;i++ ) {
      array[i][0] = 
      Float.parseFloat(data[i][0].getText());
      array[i][1] = 
      Float.parseFloat(data[i][1].getText());
    }

    return array;
  }

  /**
   * Called to set the ideal values that that the neural network
   * should return for each of the grid training values.
   * 
   * @return The ideal results.
   */
  double [][]getIdeal()
  {
    double array[][] = new double[4][1];

    for ( int i=0;i<4;i++ ) {
      array[i][0] = 
      Float.parseFloat(data[i][2].getText());
    }
    return array;
  }
}

As you can see there are several properties that are defined for this class. These properties are used as follows.

  • btnTrain - The button that allows the user to begin the training process.
  • btnRun - The button that allows the user to actually run the neural network and see the output.
  • btnQuit - The button that allows the user to quit the program.
  • status - A status display that shows the user the progress of training.
  • worker - The background thread that handles the training process.
  • NUM_INPUT - The number of neurons in the input layer of the neural network.
  • NUM_OUTPUT - The number of output neurons in the output layer of the neural network.
  • NUM_HIDDEN - The number of neurons in the hidden layer.
  • RATE - The learning rate for the neural network.
  • MOMENTUM - The momentum for training the neural network.
  • data - An array that holds the grid of input and ideal results for the neural network.
  • network - Holds the actual neural network.

We will now examine three of the major activities that this program carries out. First, we will see how the program sets up the neural network. Second we will see how the neural network is trained. Finally we will see how data is presented to the neural network and the output of the neural network is displayed. We will begin by examining how the neural network is initialized.

Setting Up the Neural Network

Setting up the neural network is fairly straight forward. To setup the neural network the constructor for the neural network class is called with the input layer size, the hidden layer size, the output layer size, the rate and momentum. The following lines of code do this.

    network = new Network(
                         NUM_INPUT,
                         NUM_HIDDEN,
                         NUM_OUTPUT,
                         RATE,
                         MOMENTUM);

A method is needed that will pull the training sets from the grid. A method named "getGrid" is provided to do this. The "getGrid" method is shown here.

  double [][]getGrid()
  {
    double array[][] = new double[4][2];

    for ( int i=0;i<4;i++ ) {
      array[i][0] = 
      Float.parseFloat(data[i][0].getText());
      array[i][1] = 
      Float.parseFloat(data[i][1].getText());
    }

    return array;
  }

As you can see the get grid method simply passes over the 4X2 grid that allows the user to input the training data. This method will be used both for the training and execution methods. When the neural network is training, this method, along with the "getIdeal" method allows the neural network to construct training sets of input signals, along with the anticipated output. When the neural network is ran to produce output, the "getGrid" method is used to obtain the input signals for the neural network.

Along with the "getGrid" method the "getIdeal" method is used to obtain an array of the ideal outputs for each of the training sets. The "getIdeal" method is shown below.

  double [][]getIdeal()
  {
    double array[][] = new double[4][1];

    for ( int i=0;i<4;i++ ) {
      array[i][0] = 
      Float.parseFloat(data[i][2].getText());
    }
    return array;
  }
}

As you can see the "getIdeal" method moves across the same grid used by the "getGrid" method. The getIdeal method only returns the output column. Now that you have seen how the neural network is setup and the training data is accessed, you will be shown how the neural network is trained.

Training the Neural Network

The neural network must be trained if it is to output the correct data. The code from the training method will now be discussed. First the training data must be acquired. This is done by calling the "getGrid" and "getIdeal" methods to access the training data.

    double xorData[][] = getGrid();
    double xorIdeal[][] = getIdeal();
    int update=0;

An update variable is also kept to keep track of the last time that the status line is updated. This is done because we do not want to update the user interface for every time through the training algorithm. There could be thousands of loops of the training algorithm. It would be too slow to update the status line for each iteration. Rather we will update the status line for each 1,000 iterations.

Here you can see that we will be looping through 10,000 iterations. For the XOR problem 10,000 iterations is usually quite sufficient to get a low error rate. If the error rate is still high you can click the "Train" button again and retrain the neural network further.

    int max = 10000;
    for (int i=0;i<max;i++) {

Each iteration we will present the neural network with teach training set. For this example program the number of training sets is the four possible combinations for the XOR problem.

      for (int j=0;j<xorData.length;j++) {
        network.computeOutputs(xorData[j]);
        network.calcError(xorIdeal[j]);
        network.learn();
      }

As you can see from the above code each training set is first run through the computeOuputs method. Then the error rate is calculated by calling the "calcError" method. Finally, the "learn" method is called to update the weight matrix for this training set.

Finally the user interface is updated with the progress of the training.

      update++;
      if (update==100) {
        status.setText( "Cycles Left:" + (max-i) + ",Error:" + network.getError(xorData.length) );
        update=0;
      }

Now that you have see how to present data to the neural network for training we will discuss how to present data to the neural network for recognition. This will be covered in the next section.

Presenting Data to the Neural Network

The ultimate goal of a neural network is to be able to present data and have the nueral network recognize or classify the data in some way. The following lines of code show how data is presented to the neural network for recognition. First, the grid of input values is read.

     double xorData[][] = getGrid();

In this case we do not care what the ideal values are. The neural network will have hopefully learned this for itself by this point. The example program then proceeds to loop through each of the four sample inputs and displays the output that the neural network produced for each.

    for (int i=0;i<4;i++) {
      NumberFormat nf = NumberFormat.getInstance();            
      double d[] = network.computeOutputs(xorData[i]);
      data[i][3].setText(nf.format(d[0]));
    }

It is important to understand that the above code is actually presenting four individual patterns to the neural network. Each of the possible inputs to the XOR problem are presented.


Copyright 2005 - 2010 by Heaton Research, Inc.. Heaton Research™ and Encog™ are trademarks of Heaton Research. Click here for copyright and trademark information.