home *** CD-ROM | disk | FTP | other *** search
- /* 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 */