/*********************************************************************** This file is part of KEEL-software, the Data Mining tool for regression, classification, clustering, pattern mining and so on. Copyright (C) 2004-2010 F. Herrera (herrera@decsai.ugr.es) L. S�nchez (luciano@uniovi.es) J. Alcal�-Fdez (jalcala@decsai.ugr.es) S. Garc�a (sglopez@ujaen.es) A. Fern�ndez (alberto.fernandez@ujaen.es) J. Luengo (julianlm@decsai.ugr.es) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ **********************************************************************/ /** * <p> * @file Rbfn.java * @author Written by Antonio Jesus Rivera Rivas (University of Jaen) 03/03/2004 * @author Modified by Victor Manuel Rivas Santos (University of Jaen) 15/07/2004 * @version 0.1 * @since JDK1.5 *</p> */ package keel.Algorithms.Neural_Networks.EvRBF_CL; import org.core.*; import java.util.*; import java.io.*; public class Rbfn implements Cloneable { /** * <p> * Class representing a Radial Basis Function Neural Network for the EvRBF_CL algorithm * </p> **/ /** Number of RBF neurons thet net contains. */ int numRbfs; /** Dimension of inputs */ int nInputs; /** Dimension of outputs (thus, number of output neuron) */ int nOutputs; /** Whether it has been traind or not **/ double fitness; /** Hashtable to store rbf neurons */ Hashtable rbfn = new Hashtable(); /** * <p> * Creates a instance of rbnf of fixed structure just for test. * </p> */ public Rbfn() { this.numRbfs=0; this.nInputs=1; this.nOutputs=1; this.fitness=0; Rbf neurona=new Rbf(1,1); double [] aCenter=new double[1]; double [] weights=new double[1]; double aRadius; aCenter[0]=1; aRadius=0.3;weights[0]=0; neurona.setParam(aCenter, aRadius, weights); this.insertRbf((Rbf)neurona.clone()); aCenter[0]=2; aRadius=0.2;weights[0]=0; neurona.setParam(aCenter, aRadius, weights); this.insertRbf((Rbf)neurona.clone()); aCenter[0]=2.5; aRadius=0.1;weights[0]=0; neurona.setParam(aCenter, aRadius, weights); this.insertRbf((Rbf)neurona.clone()); aCenter[0]=2.8; aRadius=0.3;weights[0]=0; neurona.setParam(aCenter, aRadius, weights); this.insertRbf((Rbf)neurona.clone()); aCenter[0]=3; aRadius=0.1;weights[0]=0; neurona.setParam(aCenter, aRadius, weights); this.insertRbf((Rbf)neurona.clone()); aCenter[0]=3.3; aRadius=0.2;weights[0]=0; neurona.setParam(aCenter, aRadius,weights); this.insertRbf((Rbf)neurona.clone()); } /** * <p> * Creates a new instance of rbfn * </p> * @param nInputs Input dimension * @param nOutputs Ouput dimension (thus, number of ouput neurons) */ public Rbfn(int nInputs,int nOutputs) { this.numRbfs=0; this.nInputs=nInputs; this.nOutputs=nOutputs; this.fitness=0; } /** * <p> * Creates a new instance of rbfn from a matrix of instances. Sets randomly the centres of the neurons and sets * its radius taking intro account the maximun distance between centres. * </p> * @param X Matrix of instances * @param ndatos Number of instaces in X * @param nEnt Number of imputs of the net * @param nSal Number of outputs of the net * @param nNeuro Number of hidden neurons the net will have. */ public Rbfn(double [][] X,int ndatos,int nEnt,int nSal,int nNeuro) { // Setting instance variables this.numRbfs=0; this.nInputs=nEnt; this.nOutputs=nSal; this.fitness=0; int i,ran; //Constructing and adding _numNeurons RBF neurons double [] weights=new double[nOutputs]; for(i=0; i<nOutputs; ++i ) { weights[i]=0; }; double [][] aCenters=new double[nNeuro][nInputs]; for(i=0; i<nNeuro; ++i ) { ran=(int)Randomize.Randint( 0,ndatos-1); aCenters[i]= X[ran]; } //double aRadius=0.5*RBFUtils.maxDistance( aCenters ); double aRadius=RBFUtils.avegDistance(aCenters)/2; aRadius=(aRadius<=0)?(double)Randomize.Randdouble(0,1):aRadius; for(i=0; i<nNeuro; ++i ) { Rbf neurona=new Rbf(nInputs,nOutputs); neurona.setParam(aCenters[i], aRadius, weights); this.insertRbf((Rbf)neurona.clone()); } } /** * <p> * Creates a new instance of rbfn from a matrix of instances. Sets randomly the centres of the neurons and sets * its radius taking intro account the average distance between centres. * </p> * @param nNeuro Number of hidden neurons the net will have. * @param X Matrix of instances * @param ndatos Number of instaces in X * @param nEnt Number of imputs of the net * @param nSal Number of outputs of the net */ public Rbfn(int nNeuro,double [][] X,int ndatos,int nEnt,int nSal) { // Setting instance variables int ran,i,j,cont; int flag; int [] vaux = new int[ndatos]; this.numRbfs=0; this.nInputs=nEnt; this.nOutputs=nSal; this.fitness=0; //Constructing and adding _numNeurons RBF neurons double [] weights=new double[nOutputs]; for( i=0; i<nOutputs; ++i ) { weights[i]=0; }; double [][] aCenters=new double[nNeuro][nInputs]; for(i=0; i<nNeuro; ++i ) { cont=0; do{ ran=(int)Randomize.Randint( 0, ndatos-1); flag=0; j=0; cont++; do{ if (vaux[j++]==ran) flag=1; }while((j<i)&&(flag==0)); }while((flag==1)&&(cont<ndatos)); aCenters[i]=X[ran]; } double aRadius=RBFUtils.avegDistance(aCenters)/2; aRadius=(aRadius<=0)?(double)Randomize.Randdouble(0,1):aRadius; for( i=0; i<nNeuro; ++i ) { Rbf neurona=new Rbf(nInputs,nOutputs); neurona.setParam(aCenters[i], aRadius, weights); this.insertRbf((Rbf)neurona.clone()); } } /** * <p> * Clones a RBFN neural network * </p> */ public Object clone () { try{ Rbfn rbfNet=(Rbfn)super.clone(); rbfNet.numRbfs=0; rbfNet.nInputs=nInputs; rbfNet.nOutputs=nOutputs; rbfNet.fitness=fitness; rbfNet.rbfn=new Hashtable(); String [] indexes=new String[numRbfs]; indexes=getIndexes(); for ( int i=0; i<indexes.length; ++i ) { rbfNet.insertRbf( (Rbf)this.getRbf(indexes[i]).clone(), indexes[i] ); } return (rbfNet); } catch (CloneNotSupportedException e){ throw new InternalError(e.toString()); } } /** * <p> * Deletes a neuron from the net * </p> * @param idRbf Identifier of neuron to delete */ public void removeRbf(String idRbf){ rbfn.remove(idRbf); numRbfs--; } /** * <p> * Adds a neuron to the net, assigning an automatic name * </p> * @param rbf The neuron to insert */ public void insertRbf(Rbf rbf){ //rbf.idRbf=String.valueOf(numRbfs); // Trying to set a unique identifier rbf.idRbf=RBFUtils.createIdRbf(); rbfn.put(rbf.idRbf,rbf); numRbfs++; } /** * <p> * Adds a neuron to the net with a given name * </p> * @param rbf The neuron to insert * @param _key Its name */ public void insertRbf(Rbf rbf, String _key ){ rbf.idRbf=_key; rbfn.put(rbf.idRbf,rbf); ++numRbfs; } /** * <p> * Changes a neuron in the net * </p> * @param idRbf Identifier of neuron to delete * @param rbf Neuron to insert */ public void modifyRbf(Rbf rbf,String idRbf){ removeRbf(idRbf); rbf.idRbf = idRbf; rbfn.put(rbf.idRbf,rbf); numRbfs++; } /** * <p> * To get the number of inputs * </p> * @return The number of INPUTS the net has */ public int numInputs() { return nInputs; } /** * <p> * To get the number of outputs * </p> * @return The number of OUTPUTS the net has */ public int numOutputs() { return nOutputs; } /** * <p> * Returns the number of neurons in the net * </p> * @return A integer that is the number of neurons of the net */ public int rbfSize(){ return (numRbfs); } /** * <p> * Gets an RBF from the net given its identifier * </p> * @param id RBF's identifier */ public Rbf getRbf(String id){ return ((Rbf)this.rbfn.get(id)); } /** * <p> * Returns the list on index of the net neurons. * </p> */ public String [] getIndexes(){ String [] vect=new String[this.rbfSize()]; Enumeration aEnum=this.rbfn.keys(); /*se obtienen las claves de las neuronas*/ int i=0; while (aEnum.hasMoreElements()) { vect[i]=(String)aEnum.nextElement(); i++; } return (vect); } /** * <p> * Sets the fitness of a RBFN for classification problems * </p> */ public void setFitness_Cl(double [][] _X, double [][] _Y, int _nDatos, int _nClases ) { try { int [] yielded=new int[_nDatos]; int [] auxY=new int[_nDatos]; for( int i=0; i<_nDatos; ++i) { auxY[i]=(int) _Y[i][0]; } classificationTest( _X, _nDatos, yielded, _nClases, 0); double tmpDoub=(double) RBFUtils.computeMatches( auxY, yielded, _nDatos )/_nDatos; setFitness( tmpDoub ); } catch (Exception e) { throw new InternalError(e.toString()); } } /** * <p> * Sets the fitness of a RBF. * </p> * @param _fitness The value to be used for the fitness * @return The value _finess itself */ public double setFitness( double _fitness ){ if ( _fitness<0 ) { //System.err.println( "Trying to set a non-positive fitness to a RBF Neural net\n"): throw new InternalError("Trying to set a non-positive fitness to a RBF Neural net\n"); } else { fitness=_fitness; } return fitness; } /** * <p> * Gets the fitness of a RBF. * </p> * @return The value of the finess */ public double getFitness(){ return fitness; } /** * <p> * Passes an input to the net obtaining its output * </p> * @param _input The sample * @return The set of outputs provided by the net's output neurons. */ public double [] evalRbfn(double [] _input ) { double [] aux = new double [nOutputs]; int i; Enumeration it; Rbf rbf; for (i=0;i<nOutputs;i++) { aux[i]=0; it = rbfn.elements(); while (it.hasMoreElements()) { rbf=(Rbf)it.nextElement(); aux[i]+=rbf.evalRbf(_input)*rbf.weights[i]; } } return (aux); } /** * <p> * Computes the difference between the ouput of the net and desired output * </p> * @param desiredOutput Desired output * @param netOutput Outpunt of the net * @return A vector of doubles with the difference between the output of the net and the desired output */ public double [] errorRbfn(double [] desiredOutput,double [] netOutput) { int i; double [] error=new double[nOutputs]; for (i=0;i<nOutputs;i++) error[i]=desiredOutput[i]-netOutput[i]; return(error); } /** * <p> * Returns the nearest rbf/neuron to a vector v (pattern) * </p> * @param v vector * @return The index (or key) of the closest neuron */ public String closestRbf (double [] v) { String clave="nula"; double distmin=10000000; double dist; int i,ind; Rbf rbf; int nrbf=this.rbfSize(); /* Get the number of neurons composing the net*/ String [] vect=this.getIndexes(); /* Get the indexes for neurons*/ for( i=0; i<numRbfs; i++) { rbf=getRbf( vect[i] ); dist=rbf.euclideanDist(v); if (dist<distmin){ distmin=dist; clave=vect[i]; } } return(clave); } /** * <p> * Uses RAN algorithm to build a net * </p> * @param X matrix of inputs instances * @param Y matrix of outputs instances * @param ndatos Number of instances * @param epsilon minimun error to introduce a new RBF * @param delta minimun distance to introduce a new RBF * @param alfa learning factor when a new unit is not allocated */ public void RAN(double [][] X, double [][] Y,int ndatos,double delta,double epsilon,double alfa) { try { int numVectorSeleccionado,i; String clave; double [] aCenter=new double[nInputs]; double aRadius=1; double [] weights=new double[nOutputs]; double dist; double [] patronEntrada = new double[nInputs]; double [] patronSalida = new double[nOutputs]; double [] netOutput = new double[nOutputs]; double [] error = new double[nOutputs]; double errori; int nEnt=nInputs; int nSal=nOutputs; Rbf rbf; int cont=0; //Inserts first RBF numVectorSeleccionado=(int)Randomize.Randint( 0, ndatos-1 ); patronEntrada=X[numVectorSeleccionado]; patronSalida=Y[numVectorSeleccionado]; rbf=new Rbf(nInputs,nOutputs); rbf.setParam(patronEntrada, 1.75*delta , patronSalida); this.insertRbf((Rbf)rbf.clone()); do{//Major Loop numVectorSeleccionado=(int)Randomize.Randint( 0, ndatos-1 ); patronEntrada=X[numVectorSeleccionado]; patronSalida=Y[numVectorSeleccionado]; netOutput=this.evalRbfn( patronEntrada ); error = this.errorRbfn(patronSalida,netOutput); clave = this.closestRbf (patronEntrada); rbf=getRbf( clave ); dist=rbf.euclideanDist(patronEntrada); errori=0; for (i=0;i<nOutputs;i++) if (Math.abs(error[i])>epsilon) errori=error[i]; if ((Math.abs(errori)>epsilon) && (dist>delta)){ //Major Condition //Inserts a new Rbf rbf=new Rbf(nInputs,nOutputs); rbf.setParam(patronEntrada, 1.75*dist , error); this.insertRbf((Rbf)rbf.clone()); cont = 0; } else{ //Perform gradient descent on aCenters and weights of the Rbf clave aCenter=rbf.getCenter(); weights=rbf.getWeights(); aRadius=rbf.getRadius(); for (i=0;i<nOutputs;i++) weights[i]=weights[i] + (alfa * errori * rbf.evalRbf(patronEntrada)); for (i=0;i<nInputs;i++) aCenter[i]=aCenter[i] + ( 2 * (alfa/aRadius) * (patronEntrada[i]-aCenter[i]) * rbf.evalRbf(patronEntrada) * ( errori*weights[0]) ); rbf.setCenter(aCenter); rbf.setWeights(weights); cont++; } }while(cont<(ndatos)); } catch ( Exception e ) { throw new InternalError(e.toString()); } } /** * <p> * Uses a decremental algorithm to buid a net. After initializing and training (with LMS) * a net with several neurons, the algorithm in the major loop deletes the neurons with the * lowest weight and train the net. * </p> * @param X matrix of inputs instances * @param Y matrix of outputs instances * @param ndatos Number of instances * @param percent Percent under the average of the weights to delete a neuron * @param alfa Learnig factor of LMS algorithm */ public void decremental(double [][] X, double [][] Y,int ndatos,double percent,double alfa) { int i,j,k,cont=0,nrbf,flag; double [] aCenter=new double[nInputs]; double [] weights=new double[nOutputs]; double [] medPesos=new double[nOutputs]; double [] patronEntrada = new double[nInputs]; double [] patronSalida = new double[nOutputs]; double [] netOutput = new double[nOutputs]; double [] error = new double[nOutputs]; double errori; String [] vect; double aRadius; Rbf rbf; double peso,pesomed; double [][] vaux = new double[numRbfs][nOutputs]; nrbf=this.rbfSize(); vect=this.getIndexes(); this.LMSTrain(X,Y,ndatos,10,alfa); try { do{ pesomed=0; for (i=0;i<nrbf;i++){ rbf=getRbf(vect[i]); for (j=0;j<nOutputs;j++) vaux[i][j]=Math.abs(rbf.getWeight(j)); } medPesos=RBFUtils.medVect(vaux); for (j=0;j<nOutputs;j++) { if (medPesos[j]>pesomed) pesomed=medPesos[j]; } for (i=0;i<nrbf;i++){ rbf=getRbf( vect[i] ); peso=0; for(j=0;j<nOutputs;j++) { if (Math.abs(rbf.getWeight(j))>peso) peso=Math.abs(rbf.getWeight(j)); } if (Math.abs(peso)<(percent*pesomed)) { removeRbf(vect[i]); cont=0; } } vect=this.getIndexes(); nrbf=this.rbfSize(); for(j=0;j<nrbf;j++){ rbf=getRbf( vect[j] ); weights=rbf.getWeights(); peso=0; for(k=0;k<nOutputs;k++) if (weights[k]>peso) peso=weights[k]; if (peso<pesomed) { aCenter=rbf.getCenter(); aRadius=rbf.getRadius(); for(k=0;k<nInputs;k++) { aRadius+=Randomize.Randdouble(-aRadius*0.05,aRadius*0.05); aCenter[k]+=Randomize.Randdouble(-aRadius*0.05,aRadius*0.05); } rbf.setRadius(aRadius); rbf.setCenter(aCenter); } } this.LMSTrain(X,Y,ndatos,5,alfa); cont++; }while((cont<10)&&(nrbf>0)); } catch ( Exception e ) { throw new InternalError(e.toString()); } } /** * <p> * Uses LMS to train the net. * </p> * @param X matrix of inputs instances * @param Y matrix of outputs instances * @param ndatos Number of instances * @param iter Number of times the set of samples will be used. * @param alfa Learning factor. */ public void LMSTrain(double [][] X, double [][] Y,int ndatos,int iter,double alfa){ try { int i,j,z,nrbf; double [] error = new double[nOutputs]; double modulo; double peso; Rbf rbf; double [] evaluacion; double [] entradaRed; double [] desiredOutput; double [] netOutput; int [] aleat=new int[ndatos]; int nEnt=nInputs; int nSal=nOutputs; String [] vect=this.getIndexes(); nrbf=vect.length; //System.out.println( "PIntando red con "+this.rbfSize()+" neuronas " ); //this.paint(); evaluacion=new double[nrbf]; RBFUtils.verboseln( "Training RBFNN using LMS" ); for (z=0;z<iter;z++){ int tmpNDatos=ndatos; RBFUtils.verboseln( " - LMS iteration num. "+(z+1) ); RBFUtils.verboseln( " - Num. Muestras: "+ tmpNDatos ); for (i=0; i<tmpNDatos; i++){ aleat[i]=i; } while (tmpNDatos>0){ int ran=(int)Randomize.Randint( 0, tmpNDatos-1 ); int numVectorSeleccionado=aleat[ran]; aleat[ran]=aleat[--tmpNDatos]; entradaRed=X[numVectorSeleccionado]; desiredOutput=Y[numVectorSeleccionado]; netOutput=this.evalRbfn( entradaRed ); for (i=0;i<nOutputs;i++) error[i]=desiredOutput[i]-netOutput[i]; for (i=0;i<nOutputs;i++) { modulo=0.0; for (j=0;j<nrbf;++j){ rbf=getRbf( vect[j] ); evaluacion[j]=rbf.evalRbf( entradaRed ); modulo=modulo+evaluacion[j]*evaluacion[j]; } modulo=Math.sqrt(modulo); for (j=0;j<nrbf;j++){ rbf=getRbf( vect[j] ); peso=rbf.getWeight(i); peso=peso+alfa*(error[i]*evaluacion[j]/modulo); rbf.setWeight(i,peso); } } } RBFUtils.verboseln( "Error conseguido: "+error[0] ); } } catch ( Exception e ) { throw new InternalError(e.toString()); } } /** * <p> * Evaluates the net for modeling problem * </p> * @param X matrix of inputs instances * @param ndatos Number of instances * @param yieldedResults Vector of results of the evaluation */ public void modellingTest(double [][] X,int ndatos,double [] yieldedResults){ int i; for(i=0; i<ndatos; i++){ yieldedResults[i]=this.evalRbfn(X[i])[0]; } } /** * <p> * Evaluates the net for clasification problem * </p> * @param X matrix of inputs instances * @param ndatos Number of instances * @param yieldedResults Vector of results of the evaluation * @param max Class maximun identifier * @param min Class minimun identifier */ public void classificationTest(double [][] X,int ndatos,int [] yieldedResults,int max,int min) { //System.out.println( "Test clasificación de red " ); for(int i=0; i<ndatos; ++i){ double tmpDoub=this.evalRbfn(X[i])[0]; yieldedResults[i]= (int)Math.round(tmpDoub); // yieldedResults[i]= (int)RBFUtils.maxInVector(this.evalRbfn(X[i])); // Victor: 13-Oct-2005. Lo convierto en un metodo para n salidas if (yieldedResults[i]>max) yieldedResults[i]=max; if (yieldedResults[i]<min) yieldedResults[i]=min; //System.out.println( "Para patrón i: " ); //RBFUtils.printArray( X[i] ); //System.out.println( " tmpDoub es "+tmpDoub+ " y yieldedResults es "+yieldedResults[i] ); } } /** * <p> * Prints net on a stdout * </p> */ public void paint( ) { this.paint( "" ); } /** * <p> * Prints net on a file. * </p> * @param _fileName Name of the file. */ public void paint( String _fileName ) { int i; String ind; String [] indices=new String[6]; indices=this.getIndexes(); for (i=0;i<indices.length; ++i){ ind=indices[i]; if ( _fileName!="" ) { Files.addToFile( _fileName,"Neuron: "+ind+"\n" ); } else { System.out.println("Neuron: "+ind ); } Rbf neurona=this.getRbf(ind); neurona.paint( _fileName ); } } } /*enf of the class*/