/* * 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.training.hebbian; import java.util.List; import java.util.ArrayList; import org.encog.mathutil.matrices.Matrix; import org.encog.neural.data.NeuralData; import org.encog.neural.data.NeuralDataPair; import org.encog.neural.data.NeuralDataSet; import org.encog.neural.networks.BasicNetwork; import org.encog.neural.networks.layers.Layer; import org.encog.neural.networks.synapse.Synapse; import org.encog.neural.networks.training.BasicTraining; import org.encog.neural.networks.training.LearningRate; import org.encog.neural.networks.training.TrainingError; /** * This class implements Hebbian learning for Enocg. This class specifically * handles the following three cases of Hebbian learning. * * Supervised Hebbian Learning Unsupervised Hebbian Learning OJA Unsupervised * Hebbian Learning * * Choosing between supervised and unsupervised is simply a matter of passing in * training data that has ideal outputs, in the case of supervised, or lacks * ideal outputs, in the case of unsupervised. * * OJA's rule can be used with unsupervised training. It can be specified using * a flag to the constructor. For more information on OJA's rule, see: * * http://en.wikipedia.org/wiki/Oja%27s_rule * */ public class HebbianTraining extends BasicTraining implements LearningRate { /** * The learning rate. */ private double learningRate; /** * The network that is to be trained. */ private final BasicNetwork network; /** * The training data to be used. */ private final NeuralDataSet training; /** * Holds a true value if this training is supervised. */ private final boolean supervised; /** * True if OJA's rule should be used. */ private final boolean oja; /** * The output layer that is being trained. */ private final Layer outputLayer; /** * The output synapses that are being trained. */ private List<Synapse> outputSynapse = new ArrayList<Synapse>(); /** * Construct a Hebbian training object. It will train in supervised or * unsupervised mode, depending on the nature of the training data. * * @param network * The network to train. * @param training * The training data. * @param oja * True of OJA's rule should be used. This can only be used with * unsupervised data. * @param learningRate * The learning rate. */ public HebbianTraining(final BasicNetwork network, final NeuralDataSet training, final boolean oja, final double learningRate) { this.network = network; this.training = training; this.learningRate = learningRate; this.supervised = training.getIdealSize() > 0; this.oja = oja; this.outputLayer = this.network.getLayer(BasicNetwork.TAG_OUTPUT); if (this.outputLayer == null) { throw new TrainingError( "Can't use Hebbian training without an output layer."); } if ( (this.oja == true) && (this.supervised == true)) { throw new TrainingError( "Can't use OJA Hebbian training with supervised data."); } this.outputSynapse = this.network.getStructure().getPreviousSynapses( this.outputLayer); if (this.outputSynapse.size() == 0) { throw new TrainingError( "Can't use Hebbian learning, the output layer " + "has no inbound synapses."); } } /** * @return The learning rate. */ public double getLearningRate() { return this.learningRate; } /** * @return The network to train. */ public BasicNetwork getNetwork() { return this.network; } /** * @return The training data. */ @Override public NeuralDataSet getTraining() { return this.training; } /** * @return True if OJA's rule is in use. */ public boolean isOja() { return this.oja; } /** * @return True if this is supervised training. */ public boolean isSupervised() { return this.supervised; } /** * Perform a single training iteration. */ public void iteration() { preIteration(); for (final NeuralDataPair pair : this.training) { for (final Synapse synapse : this.outputSynapse) { trainSynapse(synapse, pair); } } postIteration(); } /** * Set the learning date. * @param rate The new learning rate. */ public void setLearningRate(final double rate) { this.learningRate = rate; } /** * Train a single synapse. * @param synapse The synapse to train. * @param pair The data to train it with. */ private void trainSynapse(final Synapse synapse, final NeuralDataPair pair) { final NeuralData outputData = this.network.compute(pair.getInput()); final double[] input = pair.getInput().getData(); final double[] output = outputData.getData(); final Matrix matrix = synapse.getMatrix(); for (int toNeuron = 0; toNeuron < synapse.getToNeuronCount(); toNeuron++) { double z; if (this.supervised) { z = pair.getIdeal().getData(toNeuron); } else { z = this.learningRate; } double deltaWeight; for (int fromNeuron = 0; fromNeuron < synapse.getFromNeuronCount(); fromNeuron++) { if (this.oja) { deltaWeight = (input[fromNeuron] - output[toNeuron] * matrix.get(fromNeuron,toNeuron) * output[toNeuron] * this.learningRate); } else { deltaWeight = input[toNeuron] * output[toNeuron] * z; } matrix.add(fromNeuron,toNeuron,deltaWeight); } } } }