Professional Basis of AI Backprop Hypertext Documentation

Copyright (c) 1990-97 by Donald R. Tveter

Using a Network Within Another Program

If you save the weights produced by this simulator you can read them into another program and evaluate the network from there. Networks with arbitrary connections made using the ac command cannot be read in however networks with the extra input-output connections created with an 'x' in the make a network (m) command and networks made with the pseudo-cascade correlation architecture can be read in. Networks with weights turned off can be used.

To save a particular network you only need to do the typical save weights command (sw) and the information about how to make the network, various network parameters and the weights are written out.

To write a program that will read in a network you need to include the file, forward.h in your program like so:

#include "forward.h"
This defines a number of pointer types the only important one of which is the type, LAYER. It also defines the type WTTYPE as either float or double. The file also defines five functions you may need:

LAYER *readnetwork(char *wtfile)
int eval(LAYER *network,WTTYPE *in,WTTYPE *out)
int readscaling(LAYER *start,char dfile[])
void initnet(REAL val)
int unitval(int layerno,int unitno,REAL *val)
Readnetwork and eval are needed in every program you write, readscaling is needed if your training set scaled the values, initval and unitval are only needed for recurrent networks. To make and read in the network you call readnetwork with the name of the file to be read, as in:

network = readnetwork("weights");
if (network == NULL) printf("failed\n");
where network is defined as:

LAYER *network;
The function will return a pointer, here named network that is a pointer to the input layer of the network that was read in. Of course if the returned value is NULL something went wrong.

If your training set used the user scaling mechanism you must also read in the scaling factors from the top of the training file that contains the scaled values like so:

 if (readscaling(network,"xor.sca") == 0)
    {
     printf("bad scaling file\n");
     exit(1);
    };
The first argument is the pointer value returned by readnetwork and the second argument is the name of the file to read. If this function fails for some reason it returns 0 otherwise it returns 1.

To evaluate some input pattern make a call to eval as in:

success = eval(network,in,out);
where network is the network pointer returned by readnetwork, the variable in is an array containing the pattern to be input and the variable out is an array containing the output values of the network. Eval returns 1 if the function worked correctly and 0 if it failed for some reason.

An example of using this capability is in the file dummy.cpp where the program reads the file weights for the 2-1-1-x xor problem:

#include <stdio.h>
#include "forward.h"

void main()
{
 WTTYPE pat1[2] = {1.0,0.0};
 WTTYPE pat2[2] = {0.0,0.0};
 WTTYPE pat3[2] = {0.0,1.0};
 WTTYPE pat4[2] = {1.0,1.0};
 WTTYPE val;
 LAYER *dummy;

 dummy = readnetwork("weights");
 if (dummy == NULL)
    {
     printf("network not made\n");
     exit(1);
    };
 if (readscaling(dummy,"xor.sca") == 0)
    {
     printf("bad scaling file\n");
     exit(1);
    };

 eval(dummy,pat1,&val);
 printf("%f\n",val);
 eval(dummy,pat2,&val);
 printf("%f\n",val);
 eval(dummy,pat3,&val);
 printf("%f\n",val);
 eval(dummy,pat4,&val);
 printf("%f\n",val);
}
Both makefile.unx and makereal contain commands to make the program dummy as well as compile the network evaluation routines whose source is in forward.cpp.

A second example is the program rdummy.cpp which shows how to use the functions initnet and unitval to set up data that is passed to a recurrent network. In this example the network is 1+9-9-1, meaning one normal input plus 9 input unit values that are taken from the 9 hidden layer units. The input is stored in the array pat[10]. To start the process the 9 hidden layer units have to be given the intital value used in training the network (the value of ?, or 0.5 if you simply use the default values). Later in the process these 9 values will be copied to input units 2 through 10 (the C subscripts are 1 to 9). To set the values on the hidden layer to 0.5 use:

initnet(0.5);

The second important addition to this program is that you have to get the values from the hidden layer units into the array pat that stores all the input values. The following call to unitval gets the value of unit j in layer 2 and places it in the variable uvalue.

nread = unitval(2,j,&uvalue);
The return value of unitval is the number of values read, either 1 or 0, in case it comes up 0 it means something has gone wrong, almost certainly you've referenced a unit that does not exist.

The entire program is:

/* This is a sample program that shows how to incorporate a recurrent */
/* network into a C program.  The network is the rsin.bp network.     */

#include <stdio.h>
#include "forward.h"
#include "math.h"

void main()
{

 WTTYPE pat[10];              /* values to go on the input layer      */
 WTTYPE val;                  /* the output value goes here           */
 LAYER *network;              /* the pointer to the network structure */
 int i,j;                     /* counters                             */
 WTTYPE uvalue;               /* a hidden layer unit value            */
 WTTYPE x;                    /* the x of sin(x)                      */
 int nread;                   /* flags unit value returned or not     */

/* The readnetwork function reads the weight file to make the network */
/* and then read in the weights.                                      */

 network = readnetwork("rweights");
 if (network == NULL)
    {
     printf("network not made\n");
     exit(1);
    };

/* If your data has been scaled by the scale program then you will    */
/* have to read in that scaled data file that contains the scaling    */
/* parameters.  Do as follows in the comment, giving the name of      */
/* the file with the scaled data and scaling parameters:              */

/*
 if (readscaling(network,"xor.sca") == 0)
    {
     printf("bad scaling file\n");
     exit(1);
    };
*/

/* Evaluate the patterns and print the results. */

/* Use initnet to set all the unit values to 0.5, the usual value */
/* for the unknown.                                               */

initnet(0.5); 

for (i=0;i<=40;i++)
   {
    /* Put the value of sin(x) in the first position of pat */
    x = 3.14159 * (float) i / 20.0;
    pat[0] = sin((double) x);
    for (j=1;j<=9;j++)
       {
        /* use the function unitval to look up the value of hidden    */
        /* layer unit j; put the value in uvalue, then in the array   */
        /* pat; note that if the return value of the function is 0    */
        /* then the lookup failed, almost certainly because the unit  */
        /* does not exist.                                            */
        nread = unitval(2,j,&uvalue);
        if (nread == 0)
           {
            printf("tried to use a non-existent unit\n");
            exit(1);
           };
        pat[j] = uvalue;
       };
    eval(network,pat,&val);
    printf("x: %f sin(x): %f predicted next sin(x): %f\n",x,pat[0],val);
   };
}