/***************************************************************************** FILE : $Source: /projects/higgs1/SNNS/CVS/SNNS/kernel/sources/tacoma_learn.c,v $ SHORTNAME : tacoma_learn.c SNNS VERSION : 4.2 PURPOSE : Learn-Functions of Tacoma NOTES : For informations about the algorithms see one of the following papers: J.M. Lange, H.M. Voigt, D. Wolf: "Growing Artificial Neural Networks Based on Correlation Measures, Task Decomposition and Local Attention Neurons." IEEE '94, pp. 1355-1358. J.M. Lange, H.M. Voigt, D. Wolf: "Task Decomposition and Correlations in Growing Artificial Neural Networks." ICANN '94, pp. 735-738. J. Gatter: "Lernverfahren neuronaler Netze mit automatischer Bestimmung der Netzwerktopologie" Diplomarbeit Nr. 1337, university of Stuttgart AUTHOR : Juergen Gatter DATE : October 1995 - March 1996 CHANGED BY : RCS VERSION : $Revision: 1.10 $ LAST CHANGE : Copyright (c) 1990-1995 SNNS Group, IPVR, Univ. Stuttgart, FRG Copyright (c) 1996-1998 SNNS Group, WSI, Univ. Tuebingen, FRG ******************************************************************************/ #include #include #include #include #include #include #ifdef HAVE_VALUES_H #include #endif #include "random.h" #include "kr_typ.h" /* Kernel Types and Constants */ #include "kr_const.h" /* Constant Declarators for SNNS-Kernel */ #include "kr_def.h" /* Default Values */ #include "kernel.h" /* kernel function prototypes */ #include "kr_mac.h" /* Kernel Macros */ #include "kr_ui.h" #include "cc_type.h" #include "cc_mac.h" #include "cc_glob.h" #include "cc_display.h" #include "kr_newpattern.h" #include "cc_learn.ph" #include "tacoma_learn.ph" /***************************************************************************** FUNCTION : tac_testCorrectnessOfAddParameters PURPOSE : tests correctness of additional parameters. Correct values are : [0] TAC_KOHONEN [0 .. inf) [1] TAC_XI_RI_ETA [0.0 .. inf) [2] TAC_THRESHOLD (-inf .. 1.0) [3] TAC_LAMBDA (-inf .. inf) [4] TAC_BETA (0.0 .. 1.0) NOTES : if parameter setting is incorrect KRERR_CC_INVALID_ADD_PARAMETERS will be returned tests correctness of additional parameters UPDATE : 30.03.96 ******************************************************************************/ krui_err tac_testCorrectnessOfAddParameters(void) { if ((TAC_KOHONEN < 0) || (TAC_XI_RI_ETA < 0.0 ) || (TAC_THRESHOLD >= 1.0) || (TAC_BETA<=0.0) || (TAC_BETA>=1.0)) return (KRERR_CC_INVALID_ADD_PARAMETERS); return(KRERR_NO_ERROR); } /***************************************************************************** FUNCTION : tac_initVariables PURPOSE : Initializes global variables, read the Parameters, sorts the units, assigns the learning routines, generates the Layerlist, checks the parameters and calculates the initial display-Parameters. NOTES : UPDATE : 30.03.96 ******************************************************************************/ krui_err tac_initVariables(float* ParameterInArray, int StartPattern,int EndPattern) { int i; cc_LayerCorrectnessTest(ParameterInArray,StartPattern,EndPattern); srand48((long)time(NULL)); /* inits the random-generator */ cc_printOnOff = (int)ParameterInArray[8]; cc_backfittingOnOff = (int)ParameterInArray[18]; cc_MaxSpecialUnitNo = (int)ParameterInArray[12]; cc_modification = (int)ParameterInArray[21]; for (i=0;i<5;i++){ cc_Parameter[i]=ParameterInArray[22+i]; } cc_fastmode=(int)ParameterInArray[27]; cc_end=0; KernelErrorCode=tac_testCorrectnessOfAddParameters(); ERROR_CHECK; /* take the outputpart and overwrite the special part */ cc_propagateSpecialUnitsBackward = tac_propagateSpecial; cc_propagateOutputUnitsBackward = cc_propagateOutput; switch(LEARNING_FUNCTION) { case BACKPROP: cc_SpecialUnitUpdate = cc_OutputUnitUpdate = BackPropOfflinePart; break; case BACKPROP_ONLINE: cc_SpecialUnitUpdate = cc_OutputUnitUpdate = OnlineBackPropOfflinePart; cc_propagateOutputUnitsBackward = cc_propagateOutputOnlineCase; cc_propagateSpecialUnitsBackward = tac_propagateSpecialOnlineCase; break; case QUICKPROP: cc_SpecialUnitUpdate = cc_OutputUnitUpdate = QuickPropOfflinePart; break; case RPROP: cc_SpecialUnitUpdate = cc_OutputUnitUpdate = RPropOfflinePart; break; default: CC_ERROR(KRERR_CC_ERROR3); } KernelErrorCode = kr_topoSort(TOPOLOGICAL_CC); ERROR_CHECK; /* Sort the Units */ cc_setPointers(); if (NoOfHiddenUnits<=0) { KernelErrorCode = cc_calculateNetParameters(); ERROR_CHECK; } KernelErrorCode = cc_generateLayerList(); ERROR_CHECK; /* build the array with the dates of the Layers */ return KRERR_NO_ERROR; } /***************************************************************************** FUNCTION : tac_allocateStorage PURPOSE : allocates the first part of the storage used by TACOMA. Tacoma uses much more Arrays than for example Cascade-Correlation. The Second Part of the Arrays will be allocated later, when the number of Units to be inserted, is calculated NOTES : the second part of the needed storage will be allocated when then no of new units is calculated UPDATE : 30.03.96 ******************************************************************************/ krui_err tac_allocateStorage(int StartPattern, int EndPattern) { int start,end,n; int p,i; cc_getPatternParameter(StartPattern,EndPattern,&start,&end,&n); ERROR_CHECK; CALLOC_2DIMENSIONAL_ARRAY(SpecialUnitAct,n,cc_MaxSpecialUnitNo,float,p); CALLOC_2DIMENSIONAL_ARRAY(OutputUnitError,n,NoOfOutputUnits,float,p); CALLOC_2DIMENSIONAL_ARRAY (CorBetweenSpecialActAndOutError,cc_MaxSpecialUnitNo,NoOfOutputUnits,float,p); CALLOC_ERRORCHECK(SpecialUnitSumAct,cc_MaxSpecialUnitNo,float); MeanYi=SpecialUnitSumAct; CALLOC_ERRORCHECK(MeanOutputUnitError,NoOfOutputUnits,float); CALLOC_ERRORCHECK(PatternSumError,n,float); CALLOC_ERRORCHECK(SpecialUnitData,cc_MaxSpecialUnitNo,TAC_SPECIAL_UNIT_TYPE); CALLOC_2ND_ARRAY(SpecialUnitData,cc_MaxSpecialUnitNo,Ri, NoOfInputUnits,float,i); CALLOC_2ND_ARRAY(SpecialUnitData,cc_MaxSpecialUnitNo,Xi, NoOfInputUnits,float,i); CALLOC_2ND_ARRAY(SpecialUnitData,cc_MaxSpecialUnitNo,LinkError, NoOfInputUnits+NoOfHiddenUnits+cc_MaxSpecialUnitNo,TAC_LINK_ERROR_TYPE,i); if(cc_fastmode){ CALLOC_2DIMENSIONAL_ARRAY (ActOfUnit,n,NoOfInputUnits+NoOfHiddenUnits+cc_MaxSpecialUnitNo,float,p); } return(KRERR_NO_ERROR); } /***************************************************************************** FUNCTION : tac_freeStorage PURPOSE : Deallocates all the storage needed by TACOMA NOTES : Between two Learning-Cycles no add. memory should be held by TACOMA UPDATE : 30.03.96 *****************************************************************************/ krui_err tac_freeStorage(int StartPattern, int EndPattern) { int start,end,n; cc_getPatternParameter(StartPattern,EndPattern,&start,&end,&n); ERROR_CHECK; FREE_IF_USED(PatternSumError); FREE_2ND_ARRAY(SpecialUnitData,cc_MaxSpecialUnitNo,LinkError,i); FREE_2ND_ARRAY(SpecialUnitData,cc_MaxSpecialUnitNo,Xi,i); FREE_2ND_ARRAY(SpecialUnitData,cc_MaxSpecialUnitNo,Ri,i); FREE_2DIMENSIONAL_ARRAY(Rij,NoOfInstalledUnits,i); FREE_2DIMENSIONAL_ARRAY(Nij,NoOfInstalledUnits,i); FREE_IF_USED(SpecialUnitData); FREE_2DIMENSIONAL_ARRAY(PrimesOfSpecialUnits,NoOfInstalledUnits,i); return(cc_freeStorage(StartPattern,EndPattern,0)); } /***************************************************************************** FUNCTION : tac_calculateOutputUnitError PURPOSE : Calculates the error of all output units and stores the result in the array OutputUnitError. Additionaly the Arrays MeanOutputUnitError and PatternSumError are calculated here. And the WholeSummedError, too. PSE und WSE are using fabs(act-teach), whereas the others are using act-teach. The routine is similar to the one in cc_learn.c. NOTES : OUTPUT_UNIT_SUM_ERROR is only used temporarly UPDATE : 30.3.96 ******************************************************************************/ krui_err tac_calculateOutputUnitError(int StartPattern,int EndPattern) { register struct Unit *UnitPtr; register Patterns out_pat; register int o,p; int start, end,pat,sub,n; float devit; cc_getPatternParameter(StartPattern,EndPattern,&start,&end,&n); ERROR_CHECK; for(p=start; p<=end;p++){ PatternSumError[p] = 0.0; cc_getActivationsForActualPattern(p,start,&pat,&sub); /* propagate through in and hidden-Layers */ out_pat = kr_getSubPatData(pat,sub,OUTPUT,NULL); ERROR_CHECK;/* get Pattern-Data for Output-Units */ FOR_ALL_OUTPUT_UNITS(UnitPtr,o) { CALCULATE_ACTIVATION_AND_OUTPUT(UnitPtr, (*UnitPtr->act_func)(UnitPtr),p); /* Propagate through Output-Layer */ devit = UnitPtr->Out.output - *(out_pat++); /* difference between actual and Should be */ OUTPUT_UNIT_SUM_ERROR[o] += (OutputUnitError[p][o] = devit) ; PatternSumError[p] += fabs(OutputUnitError[p][o]); /* And calculate the summed Error for this Pattern */ } } WholeSummedError=0.0; for(p=start;p<=end;p++) WholeSummedError += PatternSumError[p]; FOR_ALL_OUTPUT_UNITS(UnitPtr,o){ MeanOutputUnitError[o] = (OUTPUT_UNIT_SUM_ERROR[o] / n); } cc_actualNetSaved=TRUE; return(KRERR_NO_ERROR); } /***************************************************************************** FUNCTION : tac_connect(int NewUnitNo,struct Unit* OldUnit,int StartPattern,int EndPattern) PURPOSE : returns true, iff s and OldUnit have to be connected. If the oldUnit is a input unit then it's clear, if it is a hidden Unit, then the two window functions have to have a siginificant overlap. NOTES : NewUnitNo is not GET_UNIT_NO(NewUnit). It's the internal number as used in the kohonen-mapping. UPDATE : 30.3.96 ******************************************************************************/ bool tac_connect(int s,struct Unit* OldUnit, int StartPattern,int EndPattern,float* Correlation) { int start,end,n,p,pat,sub,k; Patterns in_pat; float SumFirst,SumSecond,First,Second,devit,devit2,SumZaehler,Zaehler; struct Link* LinkPtr; int UnitNo; if (krui_getUnitActFuncName(GET_UNIT_NO(OldUnit)) != "ACT_TACOMA") return (TRUE); /* in/output or with CC calculated units */ /* Now start with connection routing */ cc_getPatternParameter(StartPattern,EndPattern,&start,&end,&n); if(KernelErrorCode!=KRERR_NO_ERROR) { return 0; } SumFirst = SumSecond = SumZaehler = 0.0; for(p=start;p<=end;p++){ kr_getSubPatternByNo(&pat,&sub,p); in_pat = kr_getSubPatData(pat,sub,INPUT,NULL); First = Second = Zaehler = 0.0; FOR_ALL_LINKS(OldUnit,LinkPtr) { if (IS_INPUT_UNIT(LinkPtr->to)){ UnitNo=GET_UNIT_NO(LinkPtr->to); k = (NoOfInputUnits - UnitNo); /* the patterns are stored backw. */ devit = (in_pat[k] - SpecialUnitData[s].Xi[k]) / SpecialUnitData[s].Ri[k]; devit2 = (in_pat[k] - XI_OF_LINK(LinkPtr)) / RI_OF_LINK(LinkPtr); First += (devit*devit); Second += (devit2*devit2); } } SumFirst += (TAC_EXP(-First) *TAC_EXP(-First)); SumSecond += (TAC_EXP(-Second)*TAC_EXP(-Second)); SumZaehler += (TAC_EXP(-First) *TAC_EXP(-Second)); } *Correlation = ((SumSecond>0.0) ? SumZaehler/sqrt(SumFirst*SumSecond) : 0.0 ); return(*Correlation>TAC_LAMBDA); } /***************************************************************************** FUNCTION : tac_initWindowFuncParameter(struct Unit* UnitPtr) PURPOSE : Link values of xi and ri are copied from the internal array NOTES : UnitNo is the internal number of the unit UPDATE : 30.03.96 ******************************************************************************/ void tac_initWindowFuncParameter(struct Unit* UnitPtr,int UnitNo) { struct Link * LinkPtr; int InpUnitNo; FOR_ALL_LINKS(UnitPtr,LinkPtr){ if(IS_INPUT_UNIT(LinkPtr->to)){ InpUnitNo = GET_UNIT_NO(LinkPtr->to)-1; XI_OF_LINK(LinkPtr) = SpecialUnitData[UnitNo].Xi[InpUnitNo]; RI_OF_LINK(LinkPtr) = SpecialUnitData[UnitNo].Ri[InpUnitNo]; } else{ XI_OF_LINK(LinkPtr) = RI_OF_LINK(LinkPtr) = 0.0; } } } /***************************************************************************** FUNCTION : tac_generateNewUnit PURPOSE : Builds a new special unit. The new unit is declared as a special unit to make the old routines of cc_learn/cc_rcc useable. The routine does the following things : Set type to SPECIAL Set activition function to ACT_TACOMA Actualizes the Layerlist and the LayerNo of the Unit Sets the display (like a hidden unit in CC) Generates the links between the input/ older hidden units and the new one. Sets Xi and Ri (see above) NOTES : UPDATE : 30.03.96 ******************************************************************************/ krui_err tac_generateNewUnit(int UnitNo,int LayerNo,int StartPattern,int EndPattern) { struct Unit* NewUnitPtr; struct Unit* UnitPtr; struct Link* NewLink; int CurrentUnit; float Correlation; KernelErrorCode = kr_unitSetTType(CurrentUnit=kr_makeDefaultUnit(),SPECIAL); ERROR_CHECK; KernelErrorCode = krui_setUnitActFunc(CurrentUnit,"Act_TACOMA"); ERROR_CHECK; NewUnitPtr = kr_getUnitPtr(CurrentUnit); ERROR_CHECK; KernelErrorCode = krui_setCurrentUnit(CurrentUnit); ERROR_CHECK; KernelErrorCode = cc_actualizeLayerlist(NewUnitPtr,LayerNo); CC_SET_LAYER_NO(NewUnitPtr,NoOfLayers); cc_setHiddenUnit(NewUnitPtr,NoOfLayers); FOR_ALL_UNITS(UnitPtr){ if((IS_INPUT_UNIT(UnitPtr) || IS_HIDDEN_UNIT(UnitPtr)) && UNIT_IN_USE(UnitPtr) && (CC_LAYER_NO(UnitPtr) ******************************************************************************/ krui_err tac_initXiAndRis(StartPattern,EndPattern) { int p,i; int start,end,n,pat,sub; float* MaxXI,*MinXI,*SumXI; float Xi; Patterns in_pat; cc_getPatternParameter(StartPattern,EndPattern,&start,&end,&n); CALLOC_ERRORCHECK(MaxXI,NoOfInputUnits,float); CALLOC_ERRORCHECK(MinXI,NoOfInputUnits,float); CALLOC_ERRORCHECK(SumXI,NoOfInputUnits,float); for(i=0;i MaxXI[i]) MaxXI[i]=*in_pat; if(*in_pat < MinXI[i]) MinXI[i]=*in_pat; SumXI[i] += *in_pat; in_pat++; } } for(p=0;p ******************************************************************************/ int tac_NextSpecialUnit(int p,Patterns in_pat_First) { int s,i,NextSpecialUnitInInputSpace; float devit,SummedSquaredDistance,BestSSD; Patterns in_pat; BestSSD = 1e20; for(s=0;s ******************************************************************************/ void tac_changeXi(int UnitNo,int p,int d,int MaxD,Patterns in_pat) { int i; float Xi_alt; float alpha; float Res_Error; alpha = TAC_ALPHA((float)d,(float)MaxD); for (i=0;i ******************************************************************************/ void tac_printRanks(float MaxSummedError) { int UnitNo,NewUnitCnt=0; if (cc_printOnOff) { cc_printHeadline("Installing new units",LENGTH_HEADLINE); for(UnitNo=0;UnitNo TAC_THRESHOLD){ printf(" Installed as hidden unit %d",++NewUnitCnt + NO_OF_NET_UNITS); } printf("\n"); } printf("\nInstalled %d units on layer %d\n",NewUnitCnt,NoOfLayers+1); } } /***************************************************************************** FUNCTION : void tac_calculateRanksAndRadius(int start,int end) PURPOSE : When we made a certain amount of changes to the X_i, we hope, that the X's are now located at the maxima of the remaining error. Now we count the no of Patterns, which are in our region, the correlated error and the mean distances of the patterns to the X. These results are used to determine, which of the units have to be taken and which initial radius they should get. (for formulas see one of the papers or the DA) NOTES : SUMMED_DISTANCES is only used internal. UPDATE : 30.03.96 ******************************************************************************/ int tac_calculateRanksAndRadius(int start,int end) { int p,pat,sub; int UnitNo,dim; Patterns in_pat; float MaxSummedError=0.0000001; float MeanDistance; /* init the values */ for(UnitNo=0;UnitNo MaxSummedError) MaxSummedError=SpecialUnitData[UnitNo].SummedErrorInRegion; } /* determine max (SummedErrorInRegion) */ for(UnitNo=0;UnitNo 0.0){ for(dim=0;dim ******************************************************************************/ int tac_MappingOfTheNewUnits(int StartPattern,int EndPattern) { int start,end,n,p,d; int pat,sub; Patterns in_pat; int UnitNo; int Modulo; Modulo = TAC_KOHONEN /20; /* for the printing */ cc_printHeadline("Kohonen-Training",LENGTH_HEADLINE); KernelErrorCode=tac_initXiAndRis(StartPattern,EndPattern); ERROR_CHECK; cc_getPatternParameter(StartPattern,EndPattern,&start,&end,&n); ERROR_CHECK; for (d=0;d0) { Nij[i][j] = sqrt(Sum); Rij[i][j] = (Zaehler - MeanYi[i]*MeanYi[j]*n) / Nij[i][j]; } else { Rij[i][j] = Nij[i][j] = 0.00001; /* no rij==0 allowed */ } FNenner += fabs(Rij[i][j]); } } FREE_2DIMENSIONAL_ARRAY(Di,NoOfInstalledUnits,i); return(FNenner); } /***************************************************************************** FUNCTION : tac_calculateAntiCorrelation PURPOSE : Calculates the AntiCorrelation F. F is a measure of : 1. How good is the Correlation between the units and the ouput error ? This is achieved by using the calculated and summarized Correlations S. 2. Do the new units all have different outputs ? The Correlation Rij between 2 different units is calculated, and the sum is placed in the Nenner, to guarantee minimal correlation. F will be used to determine, if the learning procedure is stagnant or further learning could be usefull. F will be trained by a gradient ascent procedure. NOTES : UPDATE : 30.03.96 ******************************************************************************/ float tac_calculateAntiCorrelation(int StartPattern, int EndPattern,bool First) { int start,end,n,i; cc_getPatternParameter(StartPattern,EndPattern,&start,&end,&n); ERROR_CHECK; AC_Nenner = TAC_ETA + tac_calculateRijAndSumRij(Rij,MeanYi,start,end,n); AC_Zaehler=0.0; for(i=0;i