/*
* Artificial Intelligence for Humans
* Volume 1: Fundamental Algorithms
* Java Version
* http://www.aifh.org
* http://www.jeffheaton.com
*
* Code repository:
* https://github.com/jeffheaton/aifh
* Copyright 2013 by Jeff Heaton
*
* 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 com.heatonresearch.aifh.learning;
import com.heatonresearch.aifh.learning.score.ScoreFunction;
import com.heatonresearch.aifh.randomize.GenerateRandom;
import com.heatonresearch.aifh.randomize.MersenneTwisterGenerateRandom;
/**
* The Greedy Random learning algorithm is a very primitive random-walk algorithm that only takes steps that serve
* to move the Machine Learning algorithm to a more optimal position. This learning algorithm essentially chooses
* random locations for the long term memory until a better set is found.
* <p/>
* http://en.wikipedia.org/wiki/Random_walk
*/
public class TrainGreedyRandom implements LearningMethod {
/**
* The Machine Learning algorithm to optimize.
*/
private final MachineLearningAlgorithm algorithm;
/**
* The random number generator to use.
*/
private final GenerateRandom rnd = new MersenneTwisterGenerateRandom();
/**
* The last error.
*/
private double lastError;
/**
* The score function.
*/
private final ScoreFunction score;
/**
* The low range for random number selection.
*/
private double lowRange = -10;
/**
* The high range for random number selection.
*/
private double highRange = 10;
/**
* True, if we are minimizing the score function.
*/
private final boolean shouldMinimize;
/**
* Construct a greedy random algorithm.
*
* @param theShouldMinimize True, if we should minimize.
* @param theAlgorithm The algorithm to optimize.
* @param theScore The score function.
*/
public TrainGreedyRandom(final boolean theShouldMinimize, final MachineLearningAlgorithm theAlgorithm, final ScoreFunction theScore) {
this.algorithm = theAlgorithm;
this.score = theScore;
this.shouldMinimize = theShouldMinimize;
// Set the last error to a really bad value so it will be reset on the first iteration.
if (this.shouldMinimize) {
this.lastError = Double.POSITIVE_INFINITY;
} else {
this.lastError = Double.NEGATIVE_INFINITY;
}
}
/**
* {@inheritDoc}
*/
@Override
public void iteration() {
final int len = this.algorithm.getLongTermMemory().length;
// backup current state
final double[] oldState = new double[len];
System.arraycopy(this.algorithm.getLongTermMemory(), 0, oldState, 0, len);
// randomize the method
performRandomize(this.algorithm.getLongTermMemory());
// did we improve it? Only keep the new method if it improved (greedy).
final double currentError = score.calculateScore(this.algorithm);
if ((currentError < this.lastError) ? shouldMinimize : !shouldMinimize) {
this.lastError = currentError;
} else {
System.arraycopy(oldState, 0, this.algorithm.getLongTermMemory(), 0, len);
}
}
/**
* Randomly move to a new location. To specify a new randomization function, override this method.
*
* @param memory The long term memory.
*/
public void performRandomize(final double[] memory) {
for (int i = 0; i < memory.length; i++) {
memory[i] = this.rnd.nextDouble(this.lowRange, this.highRange);
}
}
/**
* {@inheritDoc}
*/
@Override
public String getStatus() {
return "";
}
/**
* {@inheritDoc}
*/
@Override
public double getLastError() {
return this.lastError;
}
/**
* @return The low range.
*/
public double getLowRange() {
return lowRange;
}
/**
* Set the low range.
*
* @param lowRange The low range.
*/
public void setLowRange(final double lowRange) {
this.lowRange = lowRange;
}
/**
* @return The high range.
*/
public double getHighRange() {
return highRange;
}
/**
* Set the high range.
*
* @param highRange The high range.
*/
public void setHighRange(final double highRange) {
this.highRange = highRange;
}
/**
* {@inheritDoc}
*/
@Override
public boolean done() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public void finishTraining() {
}
}