/* * File: FeedforwardNeuralNetwork.java * Authors: Kevin R. Dixon * Company: Sandia National Laboratories * Project: Cognitive Foundry * * Copyright February 28, 2007, Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000, there is a non-exclusive license for use of this work by * or on behalf of the U.S. Government. Export of this program may require a * license from the United States Government. See CopyrightHistory.txt for * complete details. * */ package gov.sandia.cognition.learning.function.vector; import gov.sandia.cognition.math.matrix.Matrix; import gov.sandia.cognition.math.matrix.Vector; import gov.sandia.cognition.math.matrix.VectorFactory; import gov.sandia.cognition.math.matrix.VectorizableVectorFunction; import gov.sandia.cognition.math.UnivariateScalarFunction; import gov.sandia.cognition.util.AbstractCloneableSerializable; import gov.sandia.cognition.util.ObjectUtil; import java.util.ArrayList; /** * A feedforward neural network that can have an arbitrary number of layers, * and an arbitrary squashing (activation) function assigned to each layer. * The squashing functions need not be differentiable! To use a neural net * with backprop (GradientDescent), then use * DifferentiableFeedforwardNeuralNetwork * * @author Kevin R. Dixon * @since 1.0 * */ public class FeedforwardNeuralNetwork extends AbstractCloneableSerializable implements VectorizableVectorFunction { /** * Layers that comprise this neural network */ private ArrayList<? extends GeneralizedLinearModel> layers; /** * Creates a new instance of FeedforwardNeuralNetwork * @param nodesPerLayer * Number of nodes in each layer, must have no fewer than 2 layers * @param layerActivationFunctions * Squashing function to assign to each layer, must have one fewer squashing * function than you do layers (that is, the input layer has no squashing) * */ public FeedforwardNeuralNetwork( ArrayList<Integer> nodesPerLayer, ArrayList<? extends UnivariateScalarFunction> layerActivationFunctions ) { if (nodesPerLayer.size() != layerActivationFunctions.size() + 1) { throw new IllegalArgumentException( "Number of layers must equal layerActivationFunction + 1" ); } ArrayList<GeneralizedLinearModel> localLayers = new ArrayList<GeneralizedLinearModel>( layerActivationFunctions.size() ); for (int i = 0; i < nodesPerLayer.size() - 1; i++) { int currentNum = nodesPerLayer.get( i ); int nextNum = nodesPerLayer.get( i + 1 ); localLayers.add( new GeneralizedLinearModel( currentNum, nextNum, layerActivationFunctions.get( i ) ) ); } this.setLayers( localLayers ); } /** * Creates a new instance of FeedforwardNeuralNetwork * * @param numInputs * Number of nodes in the input layer * @param numHiddens * Number of nodes in the hidden (middle) layer * @param numOutputs * Number of nodes in the output layer * @param activationFunction * Squashing function to assign to all layers */ public FeedforwardNeuralNetwork( int numInputs, int numHiddens, int numOutputs, UnivariateScalarFunction activationFunction ) { ArrayList<GeneralizedLinearModel> localLayers = new ArrayList<GeneralizedLinearModel>( 2 ); localLayers.add( new GeneralizedLinearModel( numInputs, numHiddens, activationFunction ) ); localLayers.add( new GeneralizedLinearModel( numHiddens, numOutputs, activationFunction ) ); this.setLayers( localLayers ); } /** * Creates a new instance of FeedforwardNeuralNetwork * * @param layers * Layers that comprise this neural network */ public FeedforwardNeuralNetwork( ArrayList<? extends GeneralizedLinearModel> layers ) { this.setLayers( layers ); } @Override public FeedforwardNeuralNetwork clone() { FeedforwardNeuralNetwork clone = (FeedforwardNeuralNetwork) super.clone(); clone.setLayers( ObjectUtil.cloneSmartElementsAsArrayList( this.getLayers() ) ); return clone; } public Vector convertToVector() { int numParams = 0; ArrayList<Vector> layerParameters = new ArrayList<Vector>( this.getLayers().size() ); for (int i = 0; i < this.getLayers().size(); i++) { Vector p = this.getLayers().get( i ).convertToVector(); layerParameters.add( p ); numParams += p.getDimensionality(); } Vector parameters = VectorFactory.getDefault().createVector( numParams ); int index = 0; for( Vector p : layerParameters ) { int dim = p.getDimensionality(); for( int i = 0; i < dim; i++ ) { parameters.setElement( index, p.getElement(i) ); index++; } } return parameters; } public void convertFromVector( Vector parameters ) { int minIndex = 0; int maxIndex = -1; for (int i = 0; i < this.getLayers().size(); i++) { GeneralizedLinearModel layer = this.getLayers().get( i ); Matrix matrix = layer.getDiscriminant().getDiscriminant(); int num = matrix.getNumRows() * matrix.getNumColumns(); minIndex = maxIndex + 1; maxIndex = minIndex + num - 1; Vector layerParameters = parameters.subVector( minIndex, maxIndex ); layer.convertFromVector( layerParameters ); } } public Vector evaluate( Vector input ) { ArrayList<Vector> layerActivations = this.evaluateAtEachLayer( input ); return layerActivations.get( layerActivations.size() - 1 ); } /** * Returns the activations that occured at each layer * @param input Input to evaluate * @return activations at each layer, where get(0) is the input layer * and get(n) is the output layer */ protected ArrayList<Vector> evaluateAtEachLayer( Vector input ) { int N = this.getLayers().size(); ArrayList<Vector> layerActivations = new ArrayList<Vector>( N + 1 ); layerActivations.add( input ); Vector activation = input; for (GeneralizedLinearModel f : this.getLayers()) { activation = f.evaluate( activation ); layerActivations.add( activation ); } return layerActivations; } /** * Getter for layers * @return * Layers that comprise this neural network */ public ArrayList<? extends GeneralizedLinearModel> getLayers() { return this.layers; } /** * Setter for layers * @param layers * Layers that comprise this neural network */ public void setLayers( ArrayList<? extends GeneralizedLinearModel> layers ) { this.layers = layers; } @Override public String toString() { StringBuilder retval = new StringBuilder(); retval.append( this.getClass() + " with " + this.getLayers().size() + " Layers." ); retval.append( "\n" ); for (int i = 0; i < this.getLayers().size(); i++) { retval.append( "Layer " + i + "->" + (i + 1) ); retval.append( "\n" ); retval.append( this.getLayers().get( i ).toString() ); } return retval.toString(); } }