/**
* Copyright 2010 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.learning;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import org.neuroph.core.Connection;
import org.neuroph.core.Layer;
import org.neuroph.core.Neuron;
import org.neuroph.core.Weight;
import org.neuroph.core.data.DataSet;
import org.neuroph.core.data.DataSetRow;
import org.neuroph.core.learning.error.ErrorFunction;
import org.neuroph.core.learning.error.MeanSquaredError;
import org.neuroph.core.learning.stop.MaxErrorStop;
/**
* Base class for all supervised learning algorithms.
* It extends IterativeLearning, and provides general supervised learning principles.
* Based on Template Method Pattern with abstract method calculateWeightChanges
*
* TODO: random pattern order
*
* @author Zoran Sevarac
*/
abstract public class SupervisedLearning extends IterativeLearning implements
Serializable {
/**
* The class fingerprint that is set to indicate serialization
* compatibility with a previous version of the class
*/
private static final long serialVersionUID = 3L;
/**
* Total network error in previous epoch
*/
protected transient double previousEpochError;
/**
* Max allowed network error (condition to stop learning)
*/
protected double maxError = 0.01d;
/**
* Stopping condition: training stops if total network error change is smaller than minErrorChange
* for minErrorChangeIterationsLimit number of iterations
*/
private double minErrorChange = Double.POSITIVE_INFINITY;
/**
* Stopping condition: training stops if total network error change is smaller than minErrorChange
* for minErrorChangeStopIterations number of iterations
*/
private int minErrorChangeIterationsLimit = Integer.MAX_VALUE;
/**
* Count iterations where error change is smaller then minErrorChange.
*/
private transient int minErrorChangeIterationsCount;
/**
* Setting to determine if learning (weights update) is in batch mode.
* False by default.
*/
private boolean batchMode = false;
private ErrorFunction errorFunction;
/**
* Creates new supervised learning rule
*/
public SupervisedLearning() {
super();
errorFunction = new MeanSquaredError();
stopConditions.add(new MaxErrorStop(this));
}
/**
* This method should implement the weights update procedure for the whole network
* for the given output error vector.
*
* @param outputError output error vector for some network input (aka. patternError, network error)
* usually the difference between desired and actual output
*/
abstract protected void calculateWeightChanges(double[] outputError);
/**
* Trains network for the specified training set and maxError
*
* @param trainingSet training set to learn
* @param maxError learning stop condition. If maxError is reached learning stops
*/
public final void learn(DataSet trainingSet, double maxError) {
this.maxError = maxError;
learn(trainingSet);
}
/**
* Trains network for the specified training set, maxError and number of iterations
*
* @param trainingSet training set to learn
* @param maxError learning stop condition. if maxError is reached learning stops
* @param maxIterations maximum number of learning iterations
*/
public final void learn(DataSet trainingSet, double maxError, int maxIterations) {
this.trainingSet = trainingSet;
this.maxError = maxError;
setMaxIterations(maxIterations);
learn(trainingSet);
}
@Override
protected void onStart() {
super.onStart(); // reset iteration counter
minErrorChangeIterationsCount = 0;
previousEpochError = 0d;
}
@Override
protected void beforeEpoch() {
previousEpochError = errorFunction.getTotalError();
errorFunction.reset();
}
@Override
protected void afterEpoch() {
// calculate abs error change and count iterations if its below specified min error change (used for stop condition)
double absErrorChange = Math.abs(previousEpochError - errorFunction.getTotalError());
if (absErrorChange <= this.minErrorChange) {
minErrorChangeIterationsCount++;
} else {
minErrorChangeIterationsCount = 0;
}
// if learning is performed in batch mode, apply accumulated weight changes from this epoch
if (batchMode == true) {
doBatchWeightsUpdate();
}
}
/**
* This method implements basic logic for one learning epoch for the
* supervised learning algorithms. Epoch is the one pass through the
* training set. This method iterates through the training set
* and trains network for each element. It also sets flag if conditions
* to stop learning has been reached: network error below some allowed
* value, or maximum iteration count
*
* @param trainingSet training set for training network
*/
@Override
public void doLearningEpoch(DataSet trainingSet) {
Iterator iterator = trainingSet.iterator();
while (iterator.hasNext() && !isStopped()) { // iterate all elements from training set - maybe remove isStopped from here
DataSetRow dataSetRow = iterator.next();
learnPattern(dataSetRow); // learn current input/output pattern defined by SupervisedTrainingElement
}
}
/**
* Trains network with the input and desired output pattern from the specified training element
*
* @param trainingElement supervised training element which contains input and desired output
*/
protected final void learnPattern(DataSetRow trainingElement) {
neuralNetwork.setInput(trainingElement.getInput());
neuralNetwork.calculate();
double[] output = neuralNetwork.getOutput();
double[] patternError = errorFunction.addPatternError(output, trainingElement.getDesiredOutput());
calculateWeightChanges(patternError);
if (!batchMode) applyWeightChanges(); // batch mode updates are done i doBatchWeightsUpdate
}
/**
* This method updates network weights in batch mode - use accumulated weights change stored in Weight.deltaWeight
* It is executed after each learning epoch, only if learning is done in batch mode.
*
* @see SupervisedLearning#doLearningEpoch(org.neuroph.core.data.DataSet)
*/
protected void doBatchWeightsUpdate() {
// iterate layers from output to input
List layers = neuralNetwork.getLayers();
for (int i = neuralNetwork.getLayersCount() - 1; i > 0; i--) {
// iterate neurons at each layer
for (Neuron neuron : layers.get(i).getNeurons()) {
// iterate connections/weights for each neuron
for (Connection connection : neuron.getInputConnections()) {
// for each connection weight apply accumulated weight change
Weight weight = connection.getWeight();
weight.value += weight.weightChange / getTrainingSet().size(); // apply delta weight which is the sum of delta weights in batch mode - TODO: add mini batch
weight.weightChange = 0; // reset deltaWeight
}
}
}
}
/**
* Returns true if learning is performed in batch mode, false otherwise
*
* @return true if learning is performed in batch mode, false otherwise
*/
public boolean isBatchMode() {
return batchMode;
}
/**
* Sets batch mode on/off (true/false)
*
* @param batchMode batch mode setting
*/
public void setBatchMode(boolean batchMode) {
this.batchMode = batchMode;
}
/**
* Sets allowed network error, which indicates when to stopLearning training
*
* @param maxError network error
*/
public void setMaxError(double maxError) {
this.maxError = maxError;
}
/**
* Returns learning error tolerance - the value of total network error to stop learning.
*
* @return learning error tolerance
*/
public double getMaxError() {
return maxError;
}
/**
* Returns total network error in previous learning epoch
*
* @return total network error in previous learning epoch
*/
public double getPreviousEpochError() {
return previousEpochError;
}
/**
* Returns min error change stopping criteria
*
* @return min error change stopping criteria
*/
public double getMinErrorChange() {
return minErrorChange;
}
/**
* Sets min error change stopping criteria
*
* @param minErrorChange value for min error change stopping criteria
*/
public void setMinErrorChange(double minErrorChange) {
this.minErrorChange = minErrorChange;
}
/**
* Returns number of iterations for min error change stopping criteria
*
* @return number of iterations for min error change stopping criteria
*/
public int getMinErrorChangeIterationsLimit() {
return minErrorChangeIterationsLimit;
}
/**
* Sets number of iterations for min error change stopping criteria
*
* @param minErrorChangeIterationsLimit number of iterations for min error change stopping criteria
*/
public void setMinErrorChangeIterationsLimit(int minErrorChangeIterationsLimit) {
this.minErrorChangeIterationsLimit = minErrorChangeIterationsLimit;
}
/**
* Returns number of iterations count for for min error change stopping criteria
*
* @return number of iterations count for for min error change stopping criteria
*/
public int getMinErrorChangeIterationsCount() {
return minErrorChangeIterationsCount;
}
public ErrorFunction getErrorFunction() {
return errorFunction;
}
public void setErrorFunction(ErrorFunction errorFunction) {
this.errorFunction = errorFunction;
}
public double getTotalNetworkError() {
return errorFunction.getTotalError();
}
private void applyWeightChanges() {
List layers = neuralNetwork.getLayers();
for (int i = neuralNetwork.getLayersCount() - 1; i > 0; i--) {
// iterate neurons at each layer
for (Neuron neuron : layers.get(i)) {
// iterate connections/weights for each neuron
for (Connection connection : neuron.getInputConnections()) {
// for each connection weight apply accumulated weight change
Weight weight = connection.getWeight();
if (!isBatchMode()) {
weight.value += weight.weightChange;
} else {
weight.value += (weight.weightChange / getTrainingSet().size());
}
weight.weightChange = 0; // reset deltaWeight
}
}
}
}
}
Ads help maintain this website.