/**
* Copyright 2014 Neuroph Project http://neuroph.sourceforge.net
*
* 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.
*/
package org.neuroph.core;
import org.neuroph.core.data.DataSet;
import org.neuroph.core.events.NeuralNetworkEvent;
import org.neuroph.core.events.NeuralNetworkEventListener;
import org.neuroph.core.exceptions.NeurophException;
import org.neuroph.core.exceptions.VectorSizeMismatchException;
import org.neuroph.core.learning.IterativeLearning;
import org.neuroph.core.learning.LearningRule;
import org.neuroph.util.NeuralNetworkType;
import org.neuroph.util.plugins.PluginBase;
import org.neuroph.util.random.RangeRandomizer;
import org.neuroph.util.random.WeightsRandomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.*;
/**
*
* Base class for artificial neural networks. It provides generic structure and functionality
* for the neural networks. Neural network contains a collection of neuron layers and learning rule.
* Custom neural networks are created by deriving from this class, creating layers of interconnected network specific neurons,
* and setting network specific learning rule.
*
*
* @author Zoran Sevarac
* @see Layer
* @see LearningRule
*/
public class NeuralNetwork implements Serializable {
/**
* The class fingerprint that is set to indicate serialization compatibility
* with a previous version of the class.
*/
private static final long serialVersionUID = 7L;
/**
* Network type id (see neuroph.util.NeuralNetworkType).
*/
private NeuralNetworkType type;
/**
* Neural network layers
*/
private List layers;
/**
* Learning rule for this network
*/
private L learningRule;
/**
* Neural network output buffer
*/
protected double[] outputBuffer;
/**
* List of network input neurons.
* These neurons are used to set external input to network.
*/
private List inputNeurons;
/**
* List of network output neurons.
* These neurons are used to read network's output.
*/
private List outputNeurons;
/**
* Plugins collection
*/
private Map plugins;
/**
* Network label
*/
private String label = "";
/**
* List of neural network listeners
*/
private transient List listeners = new ArrayList();
/**
* Neural network logger
*/
private final Logger LOGGER = LoggerFactory.getLogger(NeuralNetwork.class);
/**
* Creates an instance of empty neural network.
*/
public NeuralNetwork() {
this.layers = new ArrayList<>();
this.inputNeurons = new ArrayList<>();
this.outputNeurons = new ArrayList<>();
this.plugins = new HashMap<>();
}
/**
* Adds layer to neural network
*
* @param layer layer to add
*/
public void addLayer(Layer layer) {
// In case of null throw exception to prevent adding null layers
if (layer == null) {
throw new IllegalArgumentException("Layer cant be null!");
}
// set parent network for added layer
layer.setParentNetwork(this);
// add layer to layers collection
layers.add(layer);
// notify listeners that layer has been added
fireNetworkEvent(new NeuralNetworkEvent(layer, NeuralNetworkEvent.Type.LAYER_ADDED));
}
/**
* Adds layer to specified index position in network
*
* @param index index position to add layer
* @param layer layer to add
*/
public void addLayer(int index, Layer layer) {
// in case of null value throw exception to prevent adding null layers
if (layer == null) {
throw new IllegalArgumentException("Layer cant be null!");
}
// if layer position is negative also throw exception
if (index < 0) {
throw new IllegalArgumentException("Layer index cannot be negative: "+index);
}
// set parent network for added layer
layer.setParentNetwork(this);
// add layer to layers collection at specified position
layers.add(index, layer);
// notify listeners that layer has been added
fireNetworkEvent(new NeuralNetworkEvent(layer, NeuralNetworkEvent.Type.LAYER_ADDED));
}
/**
* Removes specified layer from network
*
* @param layer layer to remove
* @throws Exception
*/
public void removeLayer(Layer layer) {
if (!layers.remove(layer)) {
throw new RuntimeException("Layer not in Neural n/w");
}
// notify listeners that layer has been removed
fireNetworkEvent(new NeuralNetworkEvent(layer, NeuralNetworkEvent.Type.LAYER_REMOVED));
}
/**
* Removes layer at specified index position from net
*
* @param index int value represents index postion of layer which should be
* removed
*/
public void removeLayerAt(int index) {
Layer layer = layers.get(index);
layers.remove(index);
// notify listeners that layer has been removed
fireNetworkEvent(new NeuralNetworkEvent(layer, NeuralNetworkEvent.Type.LAYER_REMOVED));
}
/**
* Returns layers array
*
* @return array of layers
*/
public List getLayers() {
return Collections.unmodifiableList(this.layers);
}
/**
* Returns layer at specified index
*
* @param index layer index position
* @return layer at specified index position
*/
public Layer getLayerAt(int index) {
return layers.get(index);
}
/**
* Returns index position of the specified layer
*
* @param layer requested Layer object
* @return layer position index
*/
public int indexOf(Layer layer) {
return layers.indexOf(layer);
}
/**
* Returns number of layers in network
*
* @return number of layes in net
*/
public int getLayersCount() {
return layers.size();
}
/**
* Sets network input. Input is an array of double values.
*
* @param inputVector network input as double array
*/
public void setInput(double... inputVector) throws VectorSizeMismatchException {
if (inputVector.length != inputNeurons.size()) {
throw new VectorSizeMismatchException("Input vector size does not match network input dimension!");
}
// TODO: Make this more elegant
int i = 0;
for (Neuron neuron : this.inputNeurons) {
neuron.setInput(inputVector[i]); // set input to the corresponding neuron
i++;
}
}
/**
* Returns network output vector. Output vector is an array collection of Double
* values.
*
* @return network output vector
*/
public double[] getOutput() {
// TODO: Make this more elegant
int i = 0;
for (Neuron c : outputNeurons) {
outputBuffer[i] = c.getOutput();
i++;
}
return outputBuffer;
}
// This can be used to provid difefrent ways for leyer calculation
// public static interface Calculator {
// // default calculator
// public void calculate();
// }
//
// // default calculator is sequential using foreach loop
// transient Calculator calculator = new Calculator() {
// @Override
// public void calculate() {
// for (Layer layer : getLayers()) {
// layer.calculate();
// }
// }
// };
//
// public Calculator getCalculator() {
// return calculator;
// }
//
// public void setCalculator(Calculator calculator) {
// this.calculator = calculator;
// }
/**
* Performs calculation on whole network
*/
public void calculate() {
// for (Layer layer : this.layers) {
// layer.calculate();
// }
layers.forEach(Layer::calculate);
fireNetworkEvent(new NeuralNetworkEvent(this, NeuralNetworkEvent.Type.CALCULATED));
}
/**
* Resets the activation levels for whole network
*/
public void reset() {
for (Layer layer : this.layers) {
layer.reset();
}
}
/**
* Learn the specified training set
*
* @param trainingSet set of training elements to learn
*/
public void learn(DataSet trainingSet) {
if (trainingSet == null) {
throw new IllegalArgumentException("Training set is null!");
}
learningRule.learn(trainingSet);
}
/**
* Learn the specified training set, using specified learning rule
*
* @param trainingSet set of training elements to learn
* @param learningRule instance of learning rule to use for learning
*/
public void learn(DataSet trainingSet, L learningRule) {
setLearningRule(learningRule);
learningRule.learn(trainingSet);
}
/**
* Stops learning
*/
public void stopLearning() {
learningRule.stopLearning();
}
/**
* Pause the learning - puts learning thread in ca state. Makes sense only
* wen learning is done in new thread with learnInNewThread() method
*/
public void pauseLearning() {
if (learningRule instanceof IterativeLearning) {
((IterativeLearning) learningRule).pause();
}
}
/**
* Resumes paused learning - notifies the learning rule to continue
*/
public void resumeLearning() {
if (learningRule instanceof IterativeLearning) {
((IterativeLearning) learningRule).resume();
}
}
/**
* Randomizes connection weights for the whole network
*/
public void randomizeWeights() {
randomizeWeights(new WeightsRandomizer());
}
/**
* Randomizes connection weights for the whole network within specified
* value range
*/
public void randomizeWeights(double minWeight, double maxWeight) {
randomizeWeights(new RangeRandomizer(minWeight, maxWeight));
}
/**
* Randomizes connection weights for the whole network using specified
* random generator
*/
public void randomizeWeights(Random random) {
randomizeWeights(new WeightsRandomizer(random));
}
/**
* Randomizes connection weights for the whole network using specified
* randomizer
*
* @param randomizer random weight generator to use
*/
public void randomizeWeights(WeightsRandomizer randomizer) {
randomizer.randomize(this);
}
/**
* Returns type of this network
*
* @return network type
*/
public NeuralNetworkType getNetworkType() {
return type;
}
/**
* Sets type for this network
*
* @param type network type
*/
public void setNetworkType(NeuralNetworkType type) {
this.type = type;
}
/**
* Returns input neurons
*
* @return input neurons
*/
public List getInputNeurons() {
return this.inputNeurons;
}
/**
* Gets number of input neurons
*
* @return number of input neurons
*/
public int getInputsCount() {
return this.inputNeurons.size();
}
/**
* Sets input neurons
*
* @param inputNeurons array of input neurons
*/
public void setInputNeurons(List inputNeurons) {
for (Neuron neuron : inputNeurons) {
this.inputNeurons.add(neuron);
}
}
/**
* Returns output neurons
*
* @return list of output neurons
*/
public List getOutputNeurons() {
return this.outputNeurons;
}
public int getOutputsCount() {
return this.outputNeurons.size();
}
/**
* Sets output neurons
*
* @param outputNeurons output neurons collection
*/
public void setOutputNeurons(List outputNeurons) {
for (Neuron neuron : outputNeurons) {
this.outputNeurons.add(neuron);
}
this.outputBuffer = new double[outputNeurons.size()];
}
/**
* Sets labels for output neurons
*
* @param labels labels for output neurons
*/
public void setOutputLabels(String[] labels) {
for (int i = 0; i < outputNeurons.size(); i++) {
outputNeurons.get(i).setLabel(labels[i]);
}
}
/**
* Returns the learning algorithm of this network
*
* @return algorithm for network training
*/
public L getLearningRule() {
return this.learningRule;
}
/**
* Sets learning algorithm for this network
*
* @param learningRule learning algorithm for this network
*/
public void setLearningRule(L learningRule) {
if (learningRule == null) {
throw new IllegalArgumentException("Learning rule can't be null!");
}
learningRule.setNeuralNetwork(this);
this.learningRule = learningRule;
}
/**
* Returns all network weights as an double array
*
* @return network weights as an double array
*/
public Double[] getWeights() {
List weights = new ArrayList();
for (Layer layer : layers) {
for (Neuron neuron : layer.getNeurons()) {
for (Connection conn : neuron.getInputConnections()) {
weights.add(conn.getWeight().getValue());
}
}
}
return weights.toArray(new Double[weights.size()]);
}
/**
* Sets network weights from the specified double array
*
* @param weights array of weights to set
*/
public void setWeights(double[] weights) {
int i = 0;
for (Layer layer : layers) {
for (Neuron neuron : layer.getNeurons()) {
for (Connection conn : neuron.getInputConnections()) {
conn.getWeight().setValue(weights[i]);
i++;
}
}
}
}
public boolean isEmpty() {
return layers.isEmpty();
}
/**
* Creates connection with specified weight value between specified neurons
*
* @param fromNeuron neuron to connect
* @param toNeuron neuron to connect to
* @param weightVal connection weight value
*/
public void createConnection(Neuron fromNeuron, Neuron toNeuron, double weightVal) {
// Connection connection = new Connection(fromNeuron, toNeuron, weightVal);
toNeuron.addInputConnection(fromNeuron, weightVal);
}
@Override
public String toString() {
if (label != null) {
return label;
}
return super.toString();
}
/**
* Saves neural network into the specified file.
*
* @param filePath file path to save network into
*/
public void save(String filePath) {
ObjectOutputStream out = null;
try {
File file = new File(filePath);
out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
out.writeObject(this);
out.flush();
} catch (IOException ioe) {
throw new NeurophException("Could not write neural network to file!", ioe);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
}
}
}
}
/**
* Loads neural network from the specified file.
*
* @param filePath file path to load network from
* @return loaded neural network as NeuralNetwork object
* @deprecated Use createFromFile method instead
*/
public static NeuralNetwork load(String filePath) {
ObjectInputStream oistream = null;
try {
File file = new File(filePath);
if (!file.exists()) {
throw new FileNotFoundException("Cannot find file: " + filePath);
}
oistream = new ObjectInputStream(new BufferedInputStream(new FileInputStream(filePath)));
NeuralNetwork nnet = (NeuralNetwork) oistream.readObject();
return nnet;
} catch (IOException ioe) {
throw new NeurophException("Could not read neural network file!", ioe);
} catch (ClassNotFoundException cnfe) {
throw new NeurophException("Class not found while trying to read neural network from file!", cnfe);
} finally {
if (oistream != null) {
try {
oistream.close();
} catch (IOException ioe) {
}
}
}
}
/**
* Loads neural network from the specified InputStream.
*
* @param inputStream input stream to load network from
* @return loaded neural network as NeuralNetwork object
*/
public static NeuralNetwork load(InputStream inputStream) {
ObjectInputStream oistream = null;
try {
oistream = new ObjectInputStream(new BufferedInputStream(inputStream));
NeuralNetwork nnet = (NeuralNetwork) oistream.readObject();
return nnet;
} catch (IOException ioe) {
throw new NeurophException("Could not read neural network file!", ioe);
} catch (ClassNotFoundException cnfe) {
throw new NeurophException("Class not found while trying to read neural network from file!", cnfe);
} finally {
if (oistream != null) {
try {
oistream.close();
} catch (IOException ioe) {
}
}
}
}
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
listeners = new ArrayList();
}
/**
* Loads and return s neural network instance from specified file
*
* @param file neural network file
* @return neural network instance
*/
public static NeuralNetwork createFromFile(File file) {
ObjectInputStream oistream = null;
try {
if (!file.exists()) {
throw new FileNotFoundException("Cannot find file: " + file);
}
oistream = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)));
NeuralNetwork nnet = (NeuralNetwork) oistream.readObject();
return nnet;
} catch (IOException ioe) {
throw new NeurophException("Could not read neural network file!", ioe);
} catch (ClassNotFoundException cnfe) {
throw new NeurophException("Class not found while trying to read neural network from file!", cnfe);
} finally {
if (oistream != null) {
try {
oistream.close();
} catch (IOException ioe) {
}
}
}
}
public static NeuralNetwork createFromFile(String filePath) {
File file = new File(filePath);
return NeuralNetwork.createFromFile(file);
}
/**
* Adds plugin to neural network
*
* @param plugin neural network plugin to add
*/
public void addPlugin(PluginBase plugin) {
plugin.setParentNetwork(this);
this.plugins.put(plugin.getClass(), plugin);
}
/**
* Returns the requested plugin
*
* @param pluginClass class of the plugin to get
* @return instance of specified plugin class
*/
public T getPlugin(Class pluginClass) {
return pluginClass.cast(plugins.get(pluginClass));
}
/**
* Removes the plugin with specified name
*
* @param pluginClass class of the plugin to remove
*/
public void removePlugin(Class pluginClass) {
this.plugins.remove(pluginClass);
}
/**
* Get network label
*
* @return network label
*/
public String getLabel() {
return label;
}
/**
* Set network label
*
* @param label network label to set
*/
public void setLabel(String label) {
this.label = label;
}
// This methods allows classes to register for LearningEvents
public synchronized void addListener(NeuralNetworkEventListener listener) {
if (listener == null)
throw new IllegalArgumentException("listener is null!");
listeners.add(listener);
}
// This methods allows classes to unregister for LearningEvents
public synchronized void removeListener(NeuralNetworkEventListener listener) {
if (listener == null)
throw new IllegalArgumentException("listener is null!");
listeners.remove(listener);
}
// This method is used to fire NeuralNetworkEvents
public synchronized void fireNetworkEvent(NeuralNetworkEvent evt) {
for (NeuralNetworkEventListener listener : listeners) {
listener.handleNeuralNetworkEvent(evt);
}
}
}
Ads help maintain this website.