home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
aijournl
/
1988_08
/
kohonen.c
< prev
next >
Wrap
Text File
|
1988-07-22
|
16KB
|
455 lines
/* Kohonen Feature Map simulation */
/*********************************************************
Kohonen Network
Written by Maureen Caudill
in Lightspeed C on a Macintosh
See the August, 1988 AI Expert, Neural Network Primer, Part 4
for a discussion of this network.
This program simulates a Kohonen feature map layer. A total of NUMITERS
2-dimensional data patterns are presented to the network. After each
SAVECOUNT patterns, a data file is written which saves the current
state of the weight vectors for the network. These snapshots, when
properly plotted, will generate the topological map of the system at each
point in time. See Kohonen Plot.C for source code detailing how to draw
the feature map.
This program is generic to any computer system. Kohonen Plot.C
uses Macintosh-specific code to draw the graphics. If another computer
is used, individual graphics routines will have to be written by the
user to generate on-screen graphics.
⌐Adaptics
16776 Bernardo Center Drive, Suite 110B
San Diego, CA 92128
(619) 451-3752
*********************************************************/
#include <math.h> /*needed for floating point and math functions*/
#include <stdio.h> /* gives fopen, fclose, printf functions */
#include <strings.h> /* string manipulation functions like strcpy and strcat */
#include <EventMgr.h> /* included for Mac event traps only */
#include <MacTypes.h> /* included for Mac event traps only */
#define NUMROWS 10 /* number of neurodes = NUMROWS*NUMCOLS */
#define NUMCOLS 10
#define PATSIZE 2 /* keep input size small for easy plotting */
#define NUMITERS 2000 /* total iterations to run */
#define SAVECOUNT 100 /* when to save net (should be about 0.1*NUMITERS) */
#define ADJNBORS 500 /* how often to lower neighborhood size (about 0.2*NUMITERS) */
/*-----------------------------------------------------------------------------------------------------------
Global storage
-------------------------------------------------------------------------------------------------------------*/
float inpatt[PATSIZE][NUMITERS] ; /* input pattern data */
float weights[NUMROWS][NUMCOLS][PATSIZE] ; /* weight storage */
double gaindelta = 0.010 ; /* gain decrease increment (about 0.1*initial gain)*/
int winrow,wincol ; /* winner neurode */
int iteration ; /* iteration count */
int neighborsize = 3 ; /* initial neighbor size */
double gain = 0.25 ; /* initial gain value */
FILE *fopen(),*savefile ;/* output file to save network state */
char *path = "Kohonen save" ;/* file storage for network state */
/*-----------------------------------------------------------------------------------
GET_RAND_INPUT -- sets a new random input pattern into inpatt[][]
This routine should be modified to force different patterns of
input data (different probability density functions). One way
to do this is to force the random values to fit inside various
envelopes which are more restrictive than a simple +1..-1 range.
For example, this version forces the data to fit inside a
circle of radius 0.5, centered about (0.25,0). It also is
set up so that points in the 1st and 2nd quadrants will be
more likely than points in the 3rd and 4th quadrants. Thus
we have a non-uniform probability density function for the
network to model.
It should be noted that this routine is strictly arbitrary and can
be modified or replaced by any other input pattern generator
you like.
No parameters, no return
-------------------------------------------------------------------------------------*/
get_rand_input()
{
double size;
double in0, in1;
double factor_1_2, factor_3_4;
double quad0, quad1; /* these are multiplicative factors (+1, -1) to determine the quadrant of the
input values. quad0 multiplies the x-coord, quad1 multiplies the y-coord. */
int i,iter; /* iteration number */
factor_1_2 = 0.6; /* these are relative likelihoods of being in
the 1st/3rd quadrant vs. the 2nd/4th quadrant */
factor_3_4 = 0.3;
for (iter=0; iter<NUMITERS; iter++)
{
in0 = ((double) (rand()));
in1 = ((double) (rand()));
/* force the input pattern to lie in a non-uniform pdf */
in0 *= factor_1_2; /* first multiply by weighting factor */
in1 *= factor_3_4;
/* we 'flip a weighted coin' to decide if this input will be
in 1st/2nd or 3rd/4th quadrants. */
if (in0 > in1)
{
/* 1st/2nd quadrant won, so we only need to decide which
quadrant (1 or 2) to put it in */
if (2*in1 < in0)
{ /* quadrant 1 */
quad0 = 1.0; /* x positive */
quad1 = 1.0; /* y positive */
}
else
{ /* quadrant 2 */
quad0 = -1.0; /* x negative */
quad1 = 1.0; /* y positive */
}
}
else
{
/* 3rd/4th quadrant won, so we need to decide which of
quadrant 3 or 4 to put it in */
if (2*in1 < in0)
{ /* quadrant 3 */
quad0 = -1.0; /* x negative */
quad1 = -1.0; /* y negative */
}
else
{ /* quadrant 4 */
quad0 = 1.0; /* x positive */
quad1 = -1.0; /* y negative */
}
} /* end else quadrant choice */
/* we can now get an input pattern. The rand() returns a
random integer 0..32767. Dividing by 32768 forces a
range of 0 to 1. We multiply by the quadrant factor
to put it in the correct quadrant. */
inpatt[0][iter] = quad0 * (((float) (rand())) / 32768.0);
inpatt[1][iter] = quad1 * (((float) (rand())) / 32768.0);
/* normalize the input vector */
size = (double)(inpatt[0][iter] *inpatt[0][iter] + inpatt[1][iter] * inpatt[1][iter]);
size = sqrt(size);
inpatt[0][iter] /= (float)size;
inpatt[1][iter] /= (float)size;
printf(" .");
} /* end for each pattern */
return;
}
/*---------------------------------------------------------------------------------------------------
GET_WINNER -- sets the winner into winrow,wincol
curr_iter parameter: current iteration number
-----------------------------------------------------------------------------------------------------*/
get_winner(curr_iter)
int curr_iter;
{
int row,col,pat;
double mindist,dist;
double diff;
mindist=0.0;
for (row=0; row<NUMROWS; row++)
{
for (col=0; col<NUMCOLS; col++)
{
dist = 0.0;
for (pat=0; pat<PATSIZE; pat++)
{
/* compute distance of this neurode's weight vector to input pattern
we don't care about taking the square root here -- we only want relative
distance */
diff = (inpatt[pat][curr_iter]) - weights[row][col][pat];
dist += diff*diff;
}
if ( (row==0) && (col==0) ) /* first neurode is always the winner so far! */
{
mindist = dist;
winrow = row;
wincol = col;
}
else
{
if (dist < mindist) /* there's a new winner
notice that in a tie, the first neurode found
will be the winner always */
{
winrow = row;
wincol = col;
mindist = dist;
} /* end if new winner */
} /* end else not first neurode */
} /* end for col */
} /* end for row */
return;
} /* end get_winner */
/*----------------------------------------------------------------------------------------------------------
ADJUST_WTS -- adjust the weights of the neurodes in the winning
neighborhood
curr_iter parameter: current iteration parameter
------------------------------------------------------------------------------------------------------------*/
adjust_wts(curr_iter)
int curr_iter ; /* current iteration number */
{
int minrow,mincol,maxrow,maxcol;
int row,col;
/* neighborhood of winner is determined by 'neighborsize'
and is allowed to go to 0 -- i.e., only the winner is
adjusted in that case.*/
minrow = winrow - neighborsize;
maxrow = winrow + neighborsize;
mincol = wincol - neighborsize;
maxcol = wincol + neighborsize;
/* make sure that the neighborhood is constrained to be
inside the network! */
if (minrow < 0)
minrow = 0;
if (maxrow >= NUMCOLS)
maxrow = NUMCOLS - 1;
if (mincol < 0)
mincol = 0;
if (maxcol >= NUMCOLS)
maxcol = NUMCOLS - 1;
/* we have a good neighborhood, so adjust the weights */
for (row=minrow; row<= maxrow; row++)
{
for (col=mincol; col<= maxcol; col++)
{
weights[row][col][0] +=
gain*(inpatt[0][curr_iter]-weights[row][col][0]);
weights[row][col][1] +=
gain*(inpatt[1][curr_iter]-weights[row][col][1]);
} /* end for col */
} /* end for row */
return;
} /* end adjust_wts */
/*------------------------------------------------------------------------------------------------
DISP_ITERATION -- show iteration number on screen
Parameter = iteration number
No return
--------------------------------------------------------------------------------------------------*/
disp_iteration (itnum)
int itnum;
{
printf("\n Iteration count: %4d",itnum);
return;
} /* end disp_iteration */
/*-------------------------------------------------------------------------------------------------
INITIALIZE -- initialize weights and random number generator
No parameters, no return
---------------------------------------------------------------------------------------------------*/
initialize()
{
unsigned int seed;
int row, col, patt;
double number;
float sum;
int i;
/* initialize random number generator */
printf ("\n Please enter a random number seed (unsigned integer):");
scanf ("%d",&seed);
srand(seed);
/* initialize weight matrix with random values */
printf ("\n Initializing weight matrix ");
for (row=0; row<NUMROWS; row++)
{
for (col=0; col<NUMCOLS; col++)
{
sum = 0.0;
for (patt=0; patt < PATSIZE; patt++)
{
number = (double) rand();
weights[row][col][patt] = (float)number / 32768.0;
sum += (weights[row][col][patt]*weights[row][col][patt]);
} /* for each pattern element */
/* now normalize the weights for this neurode */
sum = (float)(sqrt((double)sum));
for (patt=0; patt<PATSIZE; patt++)
{
weights[row][col][patt] /= (float)sum;
}
} /* end for each column */
printf(" ."); /* marker on screen to let operator know you're running */
} /* end for each row */
return;
} /* end initialize */
/*--------------------------------------------------------------------------------------------------
SAVE_DATA(iteration)
This routine will dump the current state of the weight
vectors to a file where they can later be plotted.
The format of the data file is:
Iteration number
neurode 0,0 weights
neurode 0,1 weights
neurode 0,2 weights
...
neurode 1,0 weights
...
neurode 9,9 weights
Iteration number
...
The routine or program reading this file must be told the
number of neurodes in each row and column of the network,
and the number of elements in the input pattern.
This routine is called every SAVECOUNT iterations,
which should be set so there will be about 10 data dumps
every time this program is run.
By looking at the data dumps, we will be able to see the
self organization process.
Parameter iteration is the current iteration count for this
save.
-----------------------------------------------------------------------------------------------------*/
save_data(count)
int count;
{
int row, col, wt;
char *savepath;
char *strcpy();
char *strcat();
char digits;
char itoa();
switch (count)
{
case 0: savepath = strcpy(savepath,"Kohonen Iter 0"); break;
case 100: savepath = strcpy(savepath,"Kohonen Iter 100"); break;
case 200: savepath = strcpy(savepath,"Kohonen Iter 200"); break;
case 300: savepath = strcpy(savepath,"Kohonen Iter 300"); break;
case 400: savepath = strcpy(savepath,"Kohonen Iter 400"); break;
case 500: savepath = strcpy(savepath,"Kohonen Iter 500"); break;
case 600: savepath = strcpy(savepath,"Kohonen Iter 600"); break;
case 700: savepath = strcpy(savepath,"Kohonen Iter 700"); break;
case 800: savepath = strcpy(savepath,"Kohonen Iter 800"); break;
case 900: savepath = strcpy(savepath,"Kohonen Iter 900"); break;
case 1000: savepath = strcpy(savepath,"Kohonen Iter 1000"); break;
case 1100: savepath = strcpy(savepath,"Kohonen Iter 1100"); break;
case 1200: savepath = strcpy(savepath,"Kohonen Iter 1200"); break;
case 1300: savepath = strcpy(savepath,"Kohonen Iter 1300"); break;
case 1400: savepath = strcpy(savepath,"Kohonen Iter 1400"); break;
case 1500: savepath = strcpy(savepath,"Kohonen Iter 1500"); break;
case 1600: savepath = strcpy(savepath,"Kohonen Iter 1600"); break;
case 1700: savepath = strcpy(savepath,"Kohonen Iter 1700"); break;
case 1800: savepath = strcpy(savepath,"Kohonen Iter 1800"); break;
case 1900: savepath = strcpy(savepath,"Kohonen Iter 1900"); break;
case 2000: savepath = strcpy(savepath,"Kohonen Iter 2000"); break;
default: savepath=strcpy(savepath,"Kohonen Iter");break;
}
savefile = fopen(savepath,"w"); /* open an output text file */
fprintf(savefile,"\n Iteration count %d",count); /* iteration count */
for (row=0; row<NUMROWS; row++)
{
for (col=0; col<NUMCOLS; col++)
{
fprintf(savefile,"\n"); /* new line in file */
for (wt=0; wt<PATSIZE; wt++)
{
fprintf(savefile,"%8.3f",weights[row][col][wt]);
} /* end for each weight in neurode */
} /* end for each neurode in column */
} /* end for each neurode in row */
fclose(savefile);
return;
}
/*----------------------------------------------------------------------------------------------------
Main program starts here
------------------------------------------------------------------------------------------------------*/
main()
{
int i;
/* The next two definitions are Mac unique defined in Mac Toolbox
You should substitute whatever code is necessary to allow your computer
to interrupt processing on operator command. This might be a break or
other operating system command which you can issue at any time during
execution of the program. The idea is to allow you to "bail out" at will */
int mouse_pressed; /* used to check for mouse press */
EventRecord *event; /* used to check for mouse press */
/* Set event mask to screen for either mouse pressed or released */
mouse_pressed = BitOr (mouseDown, mouseUp); /* Macintosh unique code */
initialize();
get_rand_input();
save_data(0); /* save the initial weights */
printf("\n Initial neighborhood size is %d",neighborsize);
printf("\n Initial gain is %8.3f",gain);
for (i=1; i<=NUMITERS; i++)
{
iteration = i;
disp_iteration(iteration);
get_winner(iteration);
adjust_wts(iteration);
if ((iteration % SAVECOUNT) == 0)
{
printf("\n Saving current network state for iteration %d...",iteration);
save_data(iteration); /* dump wts to file for later plotting */
gain -= gaindelta; /* lower the gain slightly */
printf("\n Gain now equals %8.3f",gain);
/* having the neighborhood size adjustment inside the "data save count" loop
only works if ADJNBORS is an integral number times SAVECOUNT! */
if ((iteration % ADJNBORS) == 0)
{
neighborsize -= 1;
if (neighborsize < 0) neighborsize = 0;
printf("\n Neighborhood size now equals %d",neighborsize);
} /* end every ADJNBORS iterations */
} /* end every SAVECOUNT iterations */
/* The following is Macintosh unique code to allow an easy bailout */
/* Any mouse click will stop the program. We just force the loop variable
to be too large to continue the loop. */
if (GetNextEvent(mouse_pressed,event)==TRUE)
i=NUMITERS+1;
} /* end for NUMITERS iterations */
printf("\n\n Training is complete. Saving last state of network now...");
save_data(iteration); /* dump the last state of the network */
return;
} /* end main */