USING NEURAL NETWORKS FOR PATTERN RECOGNITION

Recognizing and learning patterns is one thing neural nets do best

Todd King

Todd King is a programmer/analyst with the Institute of Geophysics and Planetary Physics at UCLA. He is also associated with the NASA/JPL Planetary Data Systems project. Todd can be reached at 1104 N. Orchard, Burbank, CA 91506.


By their very definition, neural networks are able to tolerate ambiguities in much the same way as the human brain can. Consequently, the types of problems that neural networks are best suited for unraveling are those in which decisions need to be made based on close approximations to -- not exact correspondence -- with the original input. One such class of problem is pattern recognition, where the neural network can memorize a pattern, then recognize it with 100 percent certainty when it sees the pattern again. And when an entirely new pattern is presented to the network, the network can determine which of the previously memorized patterns the new one matches the most closely.

One issue faced by programmers who are interested in using neural nets to solve pattern recognition problems is that there is more than one way to skin a pattern recognition problem. In fact, research into the physiological and cognitive processes of the mind has shown that there are several paradigms of pattern recognition, each with its own usefulness, purpose, and inherent goal. In this article I'll concentrate on the pattern associator and a called variation of the classification paradigm.

Broadly speaking, the goal of a pattern associator is to handle pairs of patterns -- one member of the pair being the input pattern and the other member the output pattern. The network is configured (either explicitly or by teaching) to associate the output pattern with the input pattern. Once this has been done, every time the input pattern is presented to the network the response will be the desired output pattern.

The classification paradigm is similar to the pattern associator in that it, too, makes associations between input patterns and output patterns. Where it differs is that the input patterns are placed into a fixed set of categories. A benefit of this categorizing is that distorted or incomplete versions of the input pattern can be presented to the network and the response will be the correct output pattern.

Network Topologies

You can choose from a wide range of neural network topologies when attempting to solve a particular problem. Choosing the right topology can mean a very simple solution; choosing the wrong one can result in an overly complicated solution or no solution at all.

After studying the problems that I discuss in this article - an executive board simulator, logic gates, and an optical character recognition (OCR) system -- I decided that all these examples could be implemented with the following attributes: a three-layer network consisting of an input layer, a hidden layer and an output layer. The number of neurons within each layer can be set at run time. All neurons within each layer are fully connected with all neurons in the next layer; that is, each neuron in one layer connects to every neuron in the next layer (see Figure 1, page 16). The activation function is the same for every neuron in every layer and is set to either a linear or linear-threshold function. The propagation function is always a weighted sum, and all connections activate in the direction of the output layer (feed-forward network). Starting with this foundation, you can build a variety of useful networks.

The Support Code

Let's first look at the support code that allows you to build neural networks. A simple, general-purpose library, it consists of seven functions and five pseudofunctions (defines) that allow you to build three-layer networks with any number of neurons in each layer (up to a maximum). It also allows you to set the activation, user input, and result output functions as well as the weights between connections. Listing One, page 90, contains the source for the support library. The pseudofunctions are in the include file in Listing Two, page 90. The functions are simple with few or no arguments. Rather then detail each function with words, I hope the names of the functions, the comments in the listings, and the following examples will demonstrate how to use the support code.

Building an Executive Board

A linear network is one of the simplest types of network. It operates by passing the result from the propagation function directly to its output; that is, the activation function performs no action on the input. To demonstrate this type of network, let's consider an executive board that has five voting members. Each member represents some number of stockholders who are voting by proxy. There is one vote taker to whom all board members give their votes. The vote taker then determines the net vote (how many for or against) by considering how each board member votes and how many stockholders he or she represents. The vote taker then reports the results to the board president, who announces the outcome of the vote.

This problem can be represented as a neural network with the board members as an input layer consisting of five neurons. The vote taker is a one-neuron hidden layer and the board president is a one-neuron output layer. The weight between the input layer and the vote taker is the number of stockholders a board member represents. The vote taker is considered to be honest, so the board president takes the results at face value (a weight of 1.0). Figure 2, page 16, is a diagram of the network, and Listing Three, page 91, is the source to implement the executive board neural network. When run, the program prompts you for each member's vote and prints out whether the vote was for, against, or a tie. The number of stockholders each member votes for is set to the board member's number (for example, three for board member 3).

The Logic Gate

A slightly more complicated network is the linear-threshold-based network. With this type of network, the output value is set to a specific value if the net input to the neuron exceeds some threshold (specific value) and to something different if the value is below the threshold. A good example of such a network is one that emulates logic gates. With a logic gate, the gate is either on or off, so you have what is called a binary linear threshold. For a two-input OR and a two-input AND gate, you can use an input layer of two neurons, a hidden layer of one neuron, and an output layer of one neuron. A two-gate XOR requires a two-neuron hidden layer. The difference is because the XOR logic gate needs to compare both inputs from two perspectives so it can perform the exclusive operation.

Now let's look at how you can "teach" a network to recognize the two-bit input patterns and produce the desired one-bit output pattern. Even though, with more code and execution time, the network could teach itself, let's calculate the weights for the connections between all neurons by deriving them mathematically. The first thing to note is that with linear networks, when you have a neuron with only one input connection, that neuron is not needed. The input connection can bypass the one connection neuron and go directly to all neurons that the one connection neuron connects to, without altering the solution. This means that for the AND and OR logic gates, you really only need two layers. Using three layers here means that the model is a more complicated solution than is needed, but I'll accept the three-layer model so that all examples can use the same support library. You can infer from this that the weight between the hidden layer and output layer is to be 1.0 (face value).

Starting with the OR gate, you can detail the mappings between the input pattern and the desired output pattern, as shown in Table 1, page 24.

Table 1. Mappings between input pattern and desired output pattern for an OR gate

  Input  Output
  --------------

   0 0     0
   1 0     1
   0 1     1
   1 1     1

From this you can derive the following simultaneous equations, which once solved will tell you what weights the connections between the input and hidden layer are to have. Let's use a threshold of 1.0 to differentiate between an on state and an off state. The general equation is:

  w1i1 + w2i2 >= 0

where w[1] is the weight for input neuron 1 and w[2] is the weight for neuron 2, i[1] and i[2] are the input patterns, and 0 is the output pattern. The >= is because you're using a threshold network. Because you have four patterns, the equations after substitution of the values are:

  
w1 >= 1.0   
w2 >= 1.0   
w1 + w2 >= 1.0

This not only gives you the answer immediately but also provides a check for the answer. Let's choose 1.0 for both connections. You can derive the weights for the AND gate. The values of w1 = 0.5 and w2 = 0.5 should be one of the solutions.

The solution for the XOR gate is more complicated to derive and requires more math, so I'll discuss the logic behind its solution. The weights between the input and hidden layers must be adjusted so that when both inputs are on the hidden-layer neurons are turned off. This is easy to do if, at any hidden-layer neuron, the weights given to the input neurons are equal in value but have opposite signs. Then, whenever both neurons are on, they cancel. This makes the input-hidden layer pair a filter that ensures that both hidden-layer neurons can never be on. The hidden-output-layer pair is then a simple OR gate.

Figure 3, page 24, shows what all the logic gates look like as neural, networks and Listing Four, page 91, shows how to build the logic gate networks. When you run the code, it prompts you for the values for the input pins for each type of gate, and the result is printed to the screen.

The OCR System

Now let's try something that is really suited for neural networks -- an optical character recognition (OCR) system, which translates an optical pattern composed of on and off bits to an ASCII code. The system will also tell you just how certain it is that the input pattern is the character it says it is. To do this I need to introduce one other concept -- namely, of clustering. A cluster is a collection of neurons, all in the same layer, which are in competition with one another to be the only one on. It's winner takes all in a cluster, so the rule that is applied is that the neuron that is closest to being on is forced on whereas all other neurons are forced off. The closeness to being on is a measure of the certainty to which the input pattern can be determined. In the cases in which two or more neurons have the same value and are closest, the first neuron encountered is turned on. Doing this doesn't affect the accuracy of the recognition because all neurons with equal values have the same certainty.

For the OCR system, the input-hidden-layer pair is defined to be a binary linear-threshold-based network in which neurons are either on or off. The other layer pair, composed of the hidden layer and the output layer, is defined to be a linear network. This approach allows you to associate any value you like with the bit-image input pattern. Only the neurons in the hidden layer are clustered, and all neurons in the hidden layer are placed in the same cluster. A further restriction that is placed on the OCR network is that the sum of all weights for all input connections to a neuron in the hidden layer will be 1.0 or less. Collectively, the input-hidden-layer pair, with all its features and restrictions, is a variety of what is commonly called a perceptron (Rosenblatt, 1957). One aspect of a perceptron is that it can classify its input patterns in as many groups as there are neurons in the hidden layer. Thus, to uniquely classify the 26 characters in the alphabet the hidden layer must have 26 neurons.

In order for the OCR network to recognize a pattern, it must be taught which patterns produce which outputs. You can simplify the learning process by understanding just what the network learns and then build in shortcuts that will speed things up. For this example, let's limit each group so that it recognizes only one character. By doing this the learning process is almost instantaneous.

Each letter to learn is read and presented to the network. The next unaltered hidden neuron is selected, and the weights from that neuron to all input neurons are set. The weight for each input connection that is on is set to 1.0 divided by the total number of input connections that are on. The weight for any input connection that is off is set to 0.0. The weight of the connections from the hidden-layer neurons to the output neuron is set to the numeric (ASCII, for example) value with which the input pattern is associated. The function learn_ocr( ) in Listing Five, page 94, does all this. Once a network has learned a set of characters, you can present it with any character pattern and it will tell you which one of the known patterns the presented pattern matches most closely.

Listing Five contains the code for the OCR system, and Figure 4, below, shows the topology of the neural network.

When you run this program, you specify the characters it is to learn and test on the command line as file names. Only one character is allowed in each file. The patterns to learn are given first, then the word -test, and then the file names of the patterns to test. The content of a file is the optical pattern for a character as a 5x7-bit pattern, with the bits represented as 1s and 0s. If the OCR system is to learn a pattern, the next value after the character pattern is the numeric value that pattern is to be known by. A pattern that is only to be tested does not need a numeric value because, when the OCR system is testing a pattern, it reads only the pattern. The format of the file allows the same file to be used for both learning and testing. Table 2, page 28, contains values for the letters A, B, C, E, and O. A good invocation would be:

  ocr letter.a letter.b letter.c -test letter.a letter.e letter.o

Table 2: Values for letters A, B, C, E, and O. The values for each letter must be in separate files when used with the OCR example. The bit image is shaded to emphasize how it was derived.

  A:  00100      B:  11110      C:  00110
      01010          10001          01001
      10001          10001          10000
      11111          11110          10000
      10001          10001          10000
      10001          10001          01001
      10001          11110          00110
      65.0           66.0           67.0

         E:  11111      O:  00100
             10000          01010
             10000          10001
             11100          10001
             10000          10001
             10000          01010
             11111          00100
             69.0           79.0

The OCR system should respond with A is an A, E is a B, and O is C, which is an intelligent deduction given that all the OCR network knows about are the letters A, B, and C.

Benchmarks

On an XT-compatible rated at CI: 1.8 and DI: 1.1 with Norton's SI program, I found that the OCR neural network, when compiled with Turbo C, could learn a new pattern in 0.2 seconds. With four pattern groups it could determine a pattern in 0.7 seconds. There was an overall start-up overhead of 0.2 seconds. The program and library were compiled so that they were optimized for speed, register use, and jumps, and floating-point emulation was in effect. Listing Six, page 95, contains the project files to build the examples using Turbo C. The source will also compile and run under Quick C.

Conclusion

I encourage you to experiment with neural networks. The support library contained in this article is a toolbox you can use to do this. The most difficult aspect of dealing with neural networks is the proper management of the neurons. One way to do this is revealed in Listing Two, which is the include file that is shared between the examples and the support library.

Even though neural networks have been a concept for almost as long as computers have existed, there's still room for new ideas and innovations. This article touches on only a small part of the topologies and functionality of neural networks. If you'd like to learn more about neural networks, see the bibliography for reading material.

I'm giving this neural network toolbox away freely. If you do use it to build applications, please do not charge for the toolbox portion.

Please direct any questions about this article to the author at the address given at the beginning of the article. If you need a response, include a self-addressed, stamped envelope.

Bibliography

McClelland, James L. and Rummelhart, David E. Explorations in Parallel Distributed Processing, Cambridge, Mass.: MIT Press, 1988.

NeuralWorks Professional Reference Manual. Sewickley, Penn.: NeuralWare Inc., 1987.

Rummelhart, David E., et al. Parallel Distributed Process, vol. 1, Cambridge, Mass.: The MIT Press, 1986.

_USING NEURAL NETWORKS FOR PATTERN RECOGNITION_ by Todd King [LISTING ONE]



#include <stdio.h>
#define EXTERN extern
#include "neural.h"

/*-- MAKE_MIND ---------------------------------------
  Constructs a mental unit with the given number
  of input, hidden and output neurons.
------------------------------------------------------*/
make_mind(in, hid, out)
int in;
int hid;
int out;
{
  if ( in > MAX_NEURONS  ||
       hid > MAX_NEURONS ||
       out > MAX_NEURONS ) return(0);
  if (in < 1 || hid < 1 || out < 1 ) return(0);
  Mind.n_input = in;
  Mind.n_hidden = hid;
  Mind.n_output = out;
  set_cluster_fun(NULL, NULL);
  set_all_weights(1.0);
  set_act_fun(pass);
  set_user_in_fun(prompted);
  set_result_fun(print_binary_state);
  strcpy(Prompt.string, "Input a value for neuron %d: ");
  Prompt.count = 1;
  return(1);
}

/*-- ACTIVATE_MIND -----------------------------------------
  Sets a mind in motion. Sequentially activating each neuron
-----------------------------------------------------------*/
activate_mind()
{
  int i;
  float net_input;

/* Activate input layer */
  Prompt.count = 1;
  for (i = 0; i < Mind.n_input; i++)
  {
     Mind.i_layer[i].value = Mind.user_in_fun();
  }

/* Activate hidden layer */
  for (i= 0; i < Mind.n_hidden; i++)
  {
     net_input = weighted_sum(i, HIDDEN);
     Mind.h_layer[i].value = Mind.act_fun(net_input);
  }

/* Activate feedback/certainty function (if one is set) */
  if ( Mind.certainty != NULL) Mind.cluster_fun(Mind.certainty);

/* Activate output layer */
  for (i=0; i < Mind.n_output; i++)
  {
     net_input = weighted_sum(i, OUTPUT);
     Mind.o_layer[i].value = Mind.act_fun(net_input);
     Mind.result_fun(Mind.o_layer[i].value);
  }
}

/*-- SET_ALL_WEIGHTS --------------------------------------
  Sets the weight of all connections between all neurons
  in all layers to the given value
----------------------------------------------------------*/
set_all_weights(value)
float value;
{
  int i, j;

/* Weights between input and hidden */

  for(i = 0; i < Mind.n_input; i++)
  {
    for(j = 0; j < Mind.n_hidden; j++)
    {
      Input_to_hidden[i][j].weight = value;
    }
  }

/* Weights between hidden and output */

  for(i=0; i< Mind.n_hidden; i++)
  {
    for(j = 0; j < Mind.n_output; j++)
    {
      Hidden_to_output[i][j].weight = value;
    }
  }
}

/*-- SET_WEIGHT -------------------------------------
  Sets the weight between two neurons to a given value.
------------------------------------------------------*/
set_weight(from, to, layer, value)
int from;
int to;
int layer;
float value;
{
   switch (layer)
   {
     case HIDDEN:
       if (from > Mind.n_input) return;
       if (to > Mind.n_hidden) return;
       Input_to_hidden[from][to].weight = value;
       break;
     case OUTPUT:
       if (from > Mind.n_hidden) return;
       if (to > Mind.n_output) return;
       Hidden_to_output[from][to].weight = value;
       break;
     default:
       break;
   }
   return;
}

/*-- WEIGHT_SUM --------------------------------------------
  Calculates the weighted sum for a given neuron in a given
  layer
----------------------------------------------------------*/
float weighted_sum(this_neuron, this_layer)
int this_neuron;
int this_layer;
{
  int i;
  float sum = 0.0;

  switch (this_layer)
  {
    case HIDDEN:
      for (i = 0; i < Mind.n_input; i++)
      {
         sum += (Mind.i_layer[i].value * Input_to_hidden[i][this_neuron].weight);
      }
      break;
    case OUTPUT:
      for (i = 0; i < Mind.n_hidden; i++)
      {
        sum += (Mind.h_layer[i].value * Hidden_to_output[i][this_neuron].weight);
      }
      break;
    default:
      break;
  }

  return (sum);
}

/*-- PASS ----------------------------------------------
  Returns the input value. A dummy activation function.
--------------------------------------------------------*/
float pass(value)
float value;
{
  return (value);
}

/*-- PROMPTED ---------------------------------------
  Prompts the user for an input value and returns the
  value. A user input function.
-----------------------------------------------------*/
float prompted()
{
  float value;

  printf(Prompt.string, Prompt.count++);
  scanf("%f", &value);
  return(value);
}

/*-- PRINT_BINARY_STATE -------------------------------
   Prints the output state of a neuron. If greater than
   0.0 the value printed is "on", otherwise "off".
------------------------------------------------------*/
float print_binary_state(value)
float value;
{
  printf("The output gate is: ");

  if (value > 0.0) printf("ON.");
  else printf("OFF.");

  printf("\n");
}



[LISTING TWO]


#ifndef _NEURAL_
#define _NERUAL_

#define MAX_NEURONS 35

#define HIDDEN  1
#define OUTPUT  2

/* Type definition for neurons and neural networks */

typedef struct {
  float value;
} NEURON;

typedef struct {
  int n_input;
  int n_hidden;
  int n_output;
  float *certainty;
  float (*cluster_fun)();
  float (*act_fun)();
  float (*user_in_fun)();
  float (*result_fun)();
  NEURON i_layer[MAX_NEURONS];
  NEURON h_layer[MAX_NEURONS];
  NEURON o_layer[MAX_NEURONS];
} MIND;

typedef struct {
  float weight;
} WEIGHTS;

typedef struct
{
  char string[80];
  int count;
} PROMPT;

/* Global Variables */

EXTERN MIND Mind;
EXTERN WEIGHTS Input_to_hidden[MAX_NEURONS][MAX_NEURONS];
EXTERN WEIGHTS Hidden_to_output[MAX_NEURONS][MAX_NEURONS];
EXTERN PROMPT Prompt;

/* Functions */

float weighted_sum();
float pass();
float prompted();
float print_binary_state();
float certainty_fun();
int activate_mind();

/* Pseudo-functions */

#define set_act_fun(f)  Mind.act_fun = f
#define set_user_in_fun(f)  Mind.user_in_fun = f
#define set_back_prop_fun(f)    Mind.back_prop_fun = f
#define set_result_fun(f)   Mind.result_fun = f
#define set_cluster_fun(f, x)   Mind.cluster_fun = f; Mind.certainty = x

#endif







[LISTING THREE]


/* Linear network */

#define EXTERN
#include "neural.h"

#define MEMBERS 5

float print_vote_state();
#define plural(x)   (x == 1 ? "" : "s")

main()
{
  int i;

  make_mind(5,1,1);
  set_result_fun(print_vote_state);
  strcpy(Prompt.string, "Ballot for member %d: ");
  for(i=0; i<MEMBERS; i++)
  {
    set_weight(i, 0, HIDDEN, (float)(i+1) );
  }
  printf("Ballot values: 1 = for, 0 = obstain, -1 = against\n\n");
  activate_mind();
}

float print_vote_state(value)
float value;
{
  int votes;
  printf("The vote is: ");

  votes = (int)value;
  if (votes > 0)
    printf("FOR, by %d vote%s", votes, plural(votes) );
  else if (votes < 0)
    printf("AGAINST, by %d vote%s", -votes, plural(-votes) );
  else
    printf("A TIE");

  printf(".\n");
}








[LISTING FOUR]


/* Simple linear threshold network.
   Demonstates logic gates */

#define EXTERN
#include "neural.h"

float linear_threshold();

main()
{
  int i;

/* OR gates work using the default weights (1.0) */

  strcpy(Prompt.string, "Logic state of gate %d: ");
  printf("Logic values: 1, on; 0, off\n\n");

  printf("OR logic gate.\n");
  printf("--------------\n\n");
  make_mind(2, 1, 1);
  activate_mind();

/* AND gates must have weights < 1.0 ( and > 0.0) */

  printf("\n");
  printf("AND logic gate.\n");
  printf("--------------\n\n");

  for(i = 0; i < 2; i++)
  {
    set_weight(i, 0, HIDDEN, 0.5);
  }
  activate_mind();

/* XOR gates are the most complicated */

  printf("\n");
  printf("XOR logic gate.\n");
  printf("--------------\n\n");
  make_mind(2, 2, 1);
  set_weight(0, 0, HIDDEN, 1.0);
  set_weight(1, 0, HIDDEN, -1.0);
  set_weight(0, 1, HIDDEN, -1.0);
  set_weight(1, 1, HIDDEN, 1.0);
  set_weight(0, 0, OUTPUT, 1.0);
  set_weight(1, 0, OUTPUT, 1.0);
  set_act_fun(linear_threshold);
  activate_mind();
}

/*-- LINEAR_THRESHOLD -------------------------------------
  If the input value is greater than zero then it returns
  1.0, otherwise it returns 0.0. A linear threshold
  activation function.
----------------------------------------------------------*/
float linear_threshold(value)
float value;
{
  if (value > 0.0) return(1.0);
  else return(0.0);
}









[LISTING FIVE]


/*
   Optical Character Recognition (OCR) neural network
   This is a hybrid between a linear threshold, fully
   interconnected network and a linear network. The
   transition being at the hidden layer. A Feedback neuron
   gaurantees a pattern match in the threshold layer.
*/
#include <stdio.h>
#define EXTERN
#include "neural.h"

float percep();
float print_ocr();
float certainty_cluster();

float Certainty;
FILE *Ocr_fptr;

main(argc, argv)
int argc;
char *argv[];
{
  int i;

  if(argc < 2)
  {
    printf("proper usage: ocr [<train_file> ...] [-test <test_file> ...]\n");
    exit(-1);
  }

  make_mind(35, 3, 1);
  set_user_in_fun(percep);
  set_result_fun(print_ocr);
  set_cluster_fun(certainty_cluster, &Certainty);
  set_all_weights(0.0);

/* Teach the network about the patterns */
  i = 1;
  while(strcmp(argv[i], "-test") != 0)
  {
     printf("Learning: %s\n", argv[i]);
     if( i > Mind.n_hidden)
     {
       printf("Too many pattern groups for the given topology, aborting.\n");
       exit(-1);
     }
     ocr_learn(argv[i], i - 1);
     i++;
      if(i >= argc)
     {
       printf("Nothing to test - exiting\n");
       exit(-1);
     }
  }

/* Classify each pattern based on what the network knows */
  i++;      /* Skip over "-test" deliniator */
  while(i < argc)
  {
    printf("Testing %s\n", argv[i]);
    if ((Ocr_fptr = fopen(argv[i], "r")) == NULL)
    {
      perror(argv[i]);
      printf("Unable to open file, skipping pattern.\n");
      i++;
      continue;
    }
    activate_mind();
    fclose(Ocr_fptr);
    i++;
  }
}

/*-- PERCEP ------------------------------------------------
  Returns the value of the next pixel every time its called.
  The pixel state is determined from the contents of the
  pre-opened file pointed to by 'Ocr_fptr'.
----------------------------------------------------------*/
float percep()
{
  extern FILE *Ocr_fptr;
  int pixel_value;

  fscanf(Ocr_fptr, "%1d", &pixel_value);
  return( (float)pixel_value);
}

/*-- PRINT_OCR -------------------------------------
  Prints the character which the network determines
  it to be. Also prints the certainty of the match.
------------------------------------------------------*/
float print_ocr(value)
float value;
{
  extern float Certainty;

  printf("The character is '%c' (%d).\n", (int)value, (int)value);
  printf("with a certainty of %3.2f%.\n", Certainty);
}

/*-- OCR_LEARN -----------------------------
  Teach the network how to classify
  a pattern.
--------------------------------------------*/
ocr_learn(filename, group_id)
char filename[];
int group_id;
{
  int i;
  FILE *fptr;
  int pixel_cnt = 0;
  int pixel_value;
  float dist_weight;
  float output_value;

  if ((fptr = fopen(filename, "r")) == NULL)
  {
    perror(filename);
    printf("Skipping pattern.\n");
    return(0);
  }

/* Determine the number of "on" pixels, hence fractional weight */
  for(i=0; i < Mind.n_input; i++)
  {
    fscanf(fptr, "%1d", &pixel_value);
    if(pixel_value == 1) pixel_cnt++;
  }
  dist_weight = 1.0/pixel_cnt;
  rewind(fptr);

 /* Set fractional weight for each "on" connection */
  for(i=0; i < Mind.n_input; i++)
  {
    fscanf(fptr, "%1d", &pixel_value);
    if(pixel_value == 1) set_weight(i, group_id, HIDDEN, dist_weight);
  }

 /* Now set weight for output value for this character */
  fscanf(fptr, "%f", &output_value);
  set_weight(group_id, 0, OUTPUT, output_value);

  fclose(fptr);
  return(1);
}

/*-- CERTAINTY_CLUSTER ------------------------------------
   Performs a cluster function. It inhibits (sets to 0) all
   neurons in the cluster except the one which is closest to
   the value 1.0. This neuron is set to 1.0. The passed
   variable is assigned the certainty to which the closest
   neuron felt it matched the pattern
----------------------------------------------------------*/
float certainty_cluster(certainty)
float *certainty;
{
  int i;
  float highest = 0.0;
  int closest = -1;

  for(i=0; i<Mind.n_hidden; i++)
  {
    if(Mind.h_layer[i].value > highest)
    {
      closest = i;
      highest = Mind.h_layer[i].value;
    }
  }
  if(closest == -1) /* All are equally likely - choose the first */
  {
    closest = 0;
  }

  *certainty = Mind.h_layer[closest].value * 100.0;

/*
   Cause just enough feedback to the neuron which is closest
   to being "on" so that it is "on". That is set it "on"
   All others are given negative feedback to force them to
   zero. (set them to zero).
*/
  for( i = 0; i < Mind.n_hidden; i++)
  {
    if (i == closest) Mind.h_layer[i].value = 1.0;
    else Mind.h_layer[i].value = 0.0;
  }
}






[LISTING SIX]


Board:
        board.c (neural.h)
        neurlib.c (neural.h)

Logic:
        logic.c (neural.h)
        neurlib.c (neural.h)

OCR:
        ocr.c (neural.h)
        neurlib.c (neural.h)




Copyright © 1989, Dr. Dobb's Journal