/* * Encog(tm) Core v2.5 - Java Version * http://www.heatonresearch.com/encog/ * http://code.google.com/p/encog-java/ * Copyright 2008-2010 Heaton Research, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * For more information on Heaton Research copyrights, licenses * and trademarks visit: * http://www.heatonresearch.com/copyright */ package org.encog.neural.networks.layers; import org.encog.engine.network.activation.ActivationLinear; import org.encog.engine.network.rbf.RadialBasisFunction; import org.encog.mathutil.randomize.RangeRandomizer; import org.encog.mathutil.rbf.GaussianFunction; import org.encog.mathutil.rbf.InverseMultiquadricFunction; import org.encog.mathutil.rbf.MultiquadricFunction; import org.encog.mathutil.rbf.RBFEnum; import org.encog.neural.NeuralNetworkError; import org.encog.neural.data.NeuralData; import org.encog.neural.data.basic.BasicNeuralData; import org.encog.persist.Persistor; import org.encog.persist.persistors.RadialBasisFunctionLayerPersistor; /** * This layer type makes use of several radial basis function to scale the * output from this layer. Each RBF can have a different center, peak, and * width. Proper selection of these values will greatly impact the success of * the layer. Currently, Encog provides no automated way of determining these * values. There is one RBF per neuron. * * Radial basis function layers have neither bias nor a regular activation * function. Calling any methods that deal with the activation function or bias * values will result in an error. * * Contributed to Encog By M.Fletcher and M.Dean University of Cambridge, Dept. * of Physics, UK * */ public class RadialBasisFunctionLayer extends BasicLayer { /** * The serial id. */ private static final long serialVersionUID = 2779781041654829282L; /** * The radial basis functions to use, there should be one for each neuron. */ private RadialBasisFunction[] radialBasisFunction; /** * Default constructor, mainly so the workbench can easily create a default * layer. */ public RadialBasisFunctionLayer() { this(1); } /** * Construct a radial basis function layer. * * @param neuronCount * The neuron count. */ public RadialBasisFunctionLayer(final int neuronCount) { super(new ActivationLinear(), false, neuronCount); this.radialBasisFunction = new RadialBasisFunction[neuronCount]; } /** * Compute the values before sending output to the next layer. This function * allows the activation functions to be called. * * @param pattern * The incoming Project. * @return The output from this layer. */ public NeuralData compute(final NeuralData pattern) { final NeuralData result = new BasicNeuralData(getNeuronCount()); for (int i = 0; i < getNeuronCount(); i++) { if (this.radialBasisFunction[i] == null) { final String str = "Error, must define radial functions for each neuron"; throw new NeuralNetworkError(str); } final RadialBasisFunction f = this.radialBasisFunction[i]; if (pattern.getData().length != f.getDimensions()) { throw new NeuralNetworkError( "Inputs must equal the number of dimensions."); } result.setData(i, f.calculate(pattern.getData())); } return result; } /** * @return Create a persistor for this layer. */ @Override public Persistor createPersistor() { return new RadialBasisFunctionLayerPersistor(); } /** * @return An array of radial basis functions. */ public RadialBasisFunction[] getRadialBasisFunction() { return this.radialBasisFunction; } /** * Set the RBF components to random values. * * @param dimensions * The number of dimensions in the network. * @param min * Minimum random value. * @param max * Max random value. * @param t * The type of RBF to use. */ public void randomizeRBFCentersAndWidths(final int dimensions, final double min, final double max, final RBFEnum t) { final double[] centers = new double[dimensions]; for (int i = 0; i < dimensions; i++) { centers[i] = RangeRandomizer.randomize(min, max); } for (int i = 0; i < getNeuronCount(); i++) { setRBFFunction(i, t, centers, RangeRandomizer.randomize(min, max)); } } /** * Set the array of radial basis functions. * * @param v * An array of radial basis functions. */ public void setRadialBasisFunction(final RadialBasisFunction[] v) { this.radialBasisFunction = v; } /** * Array containing center position. Row n contains centers for neuron n. * Row n contains x elements for x number of dimensions. * * @param centers The centers. * @param widths * Array containing widths. Row n contains widths for neuron n. * Row n contains x elements for x number of dimensions. * @param t * The RBF Function to use for this layer. */ public void setRBFCentersAndWidths(final double[][] centers, final double[] widths, final RBFEnum t) { for (int i = 0; i < getNeuronCount(); i++) { setRBFFunction(i, t, centers[i], widths[i]); } } /** * Equally spaces all hidden neurons within the n dimensional variable * space. * * @param minPosition * The minimum position neurons should be centered. Typically 0. * @param maxPosition * The maximum position neurons should be centered. Typically 1 * @param RBFType * The RBF type to use. * @param dimensions * The number of dimensions. * @param volumeNeuronRBFWidth * The neuron width of neurons within the mesh. * @param useWideEdgeRBFs * Enables wider RBF's around the boundary of the neuron mesh. */ public void setRBFCentersAndWidthsEqualSpacing(final double minPosition, final double maxPosition, final RBFEnum t, final int dimensions, final double volumeNeuronRBFWidth, final boolean useWideEdgeRBFs) { final int totalNumHiddenNeurons = getNeuronCount(); final double disMinMaxPosition = Math.abs(maxPosition - minPosition); // Check to make sure we have the correct number of neurons for the // provided dimensions final int expectedSideLength = (int) Math.pow(totalNumHiddenNeurons, 1.0 / dimensions); if (expectedSideLength != Math.pow(totalNumHiddenNeurons, 1.0 / dimensions)) { throw new NeuralNetworkError( "Total number of RBF neurons must be some integer to the power of 'dimensions'."); } final double edgeNeuronRBFWidth = 2.5 * volumeNeuronRBFWidth; final double[][] centers = new double[totalNumHiddenNeurons][]; final double[] widths = new double[totalNumHiddenNeurons]; for (int i = 0; i < totalNumHiddenNeurons; i++) { centers[i] = new double[dimensions]; final int sideLength = expectedSideLength; // Evenly distribute the volume neurons. int temp = i; // First determine the centers for (int j = dimensions; j > 0; j--) { // i + j * sidelength + k * sidelength ^2 + ... l * sidelength ^ // n // i - neuron number in x direction, i.e. 0,1,2,3 // j - neuron number in y direction, i.e. 0,1,2,3 // Following example assumes sidelength of 4 // e.g Neuron 5 - x position is (int)5/4 * 0.33 = 0.33 // then take modulus of 5%4 = 1 // Neuron 5 - y position is (int)1/1 * 0.33 = 0.33 centers[i][j - 1] = ((int) (temp / Math.pow(sideLength, j - 1)) * (disMinMaxPosition / (sideLength - 1))) + minPosition; temp = temp % (int) (Math.pow(sideLength, j - 1)); } // Now set the widths boolean contains = false; for (int z = 0; z < centers[0].length; z++) { if ((centers[i][z] == 1.0) || (centers[i][z] == 0.0)) { contains = true; } } if (contains && useWideEdgeRBFs) { widths[i] = edgeNeuronRBFWidth; } else { widths[i] = volumeNeuronRBFWidth; } // centers[i] = (double)(1 / (double)(neuronCount - 1)) * (double)i; } setRBFCentersAndWidths(centers, widths, t); // SaveOutNeuronCentersAndWeights(centers, widths); } /** * Set an RBF function. * * @param index * The index to set. * @param t * The function type. * @param centers * The centers. * @param width * The width. */ public void setRBFFunction(final int index, final RBFEnum t, final double[] centers, final double width) { if (t == RBFEnum.Gaussian) { this.radialBasisFunction[index] = new GaussianFunction(0.5, centers, width); } else if (t == RBFEnum.Multiquadric) { this.radialBasisFunction[index] = new MultiquadricFunction(0.5, centers, width); } else if (t == RBFEnum.InverseMultiquadric) { this.radialBasisFunction[index] = new InverseMultiquadricFunction( 0.5, centers, width); } } }