/*
* Artificial Intelligence for Humans
* Volume 3: Deep Learning and Neural Networks
* Java Version
* http://www.aifh.org
* http://www.jeffheaton.com
*
* Code repository:
* https://github.com/jeffheaton/aifh
*
* Copyright 2014-2015 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.dbnn;
import com.heatonresearch.aifh.AIFHError;
import com.heatonresearch.aifh.learning.RegressionAlgorithm;
import com.heatonresearch.aifh.randomize.GenerateRandom;
import com.heatonresearch.aifh.randomize.MersenneTwisterGenerateRandom;
/**
* A deep belief neural network.
*
* References:
* http://deeplearning.net/software/theano/
* https://github.com/yusugomori/DeepLearning
* http://en.wikipedia.org/wiki/Deep_learning
*/
public class DeepBeliefNetwork implements RegressionAlgorithm {
/**
* The hidden layers of the neural network.
*/
private final HiddenLayer[] layers;
/**
* The restricted boltzmann machines for the neural network, one for leach layer.
*/
private final RestrictedBoltzmannMachine[] rbm;
/**
* The output layer for the neural network.
*/
private final DeepLayer outputLayer;
/**
* The random number generator to use.
*/
private GenerateRandom random = new MersenneTwisterGenerateRandom();
/**
* Construct a deep belief neural network.
* @param inputCount The input count.
* @param hidden The counts for the hidden layers.
* @param outputCount The output neuron count.
*/
public DeepBeliefNetwork(int inputCount, int[] hidden, int outputCount) {
int inputSize;
this.layers = new HiddenLayer[hidden.length];
this.rbm = new RestrictedBoltzmannMachine[hidden.length];
for (int i = 0; i < this.rbm.length; i++) {
if (i == 0) {
inputSize = inputCount;
} else {
inputSize = hidden[i - 1];
}
this.layers[i] = new HiddenLayer(this,inputSize, hidden[i]);
this.rbm[i] = new RestrictedBoltzmannMachine(this.layers[i]);
}
this.outputLayer = new DeepLayer(this,hidden[this.layers.length - 1], outputCount);
}
/**
* Randomize the weights of the neural network.
*/
public void reset() {
for (int i = 0; i < this.rbm.length; i++) {
HiddenLayer layer = this.layers[i];
double a = 1.0 / layer.getInputCount();
for(int j=0; j<layer.getOutputCount(); j++) {
for(int k=0; k<layer.getInputCount(); k++) {
layer.getWeights()[j][k] = getRandom().nextDouble(-a, a);
}
}
}
}
/**
* The sigmoid/logistic function, used by the output layer.
* @param x The input.
* @return The output.
*/
public static double sigmoid(double x) {
return 1.0 / (1.0 + Math.exp(-x));
}
/**
* @return The layers of the neural network.
*/
public HiddenLayer[] getLayers() {
return this.layers;
}
/**
* @return The restricted Boltzmann machines.
*/
RestrictedBoltzmannMachine[] getRBMLayers() {
return this.rbm;
}
/**
* @return The input count.
*/
public int getInputCount() {
return this.layers[0].getInputCount();
}
/**
* @return The output (logistic) layer.
*/
public DeepLayer getLogLayer() {
return this.outputLayer;
}
/**
* @return The random number generator.
*/
public GenerateRandom getRandom() {
return this.random;
}
/**
* Set the random number generator.
* @param random The random number generator.
*/
public void setRandom(final GenerateRandom random) {
this.random = random;
}
/**
* @return The number of output neurons.
*/
public int getOutputCount() {
return this.outputLayer.getOutputCount();
}
/**
* Classify the input data into the list of probabilities of each class.
* @param input The input.
* @return An array that contains the probabilities of each class.
*/
@Override
public double[] computeRegression(final double[] input) {
double[] result = new double[getOutputCount()];
double[] layerInput = new double[0];
double[] prevLayerInput = new double[getInputCount()];
System.arraycopy(input, 0, prevLayerInput, 0, getInputCount());
double output;
for (int i = 0; i < this.layers.length; i++) {
layerInput = new double[this.layers[i].getOutputCount()];
for (int k = 0; k < this.layers[i].getOutputCount(); k++) {
output = 0.0;
for (int j = 0; j < this.layers[i].getInputCount(); j++) {
output += this.layers[i].getWeights()[k][j] * prevLayerInput[j];
}
output += this.layers[i].getBias()[k];
layerInput[k] = sigmoid(output);
}
if (i < this.layers.length - 1) {
prevLayerInput = new double[this.layers[i].getOutputCount()];
System.arraycopy(layerInput, 0, prevLayerInput, 0, this.layers[i].getOutputCount());
}
}
for (int i = 0; i < this.outputLayer.getOutputCount(); i++) {
result[i] = 0;
for (int j = 0; j < this.outputLayer.getInputCount(); j++) {
result[i] += this.outputLayer.getWeights()[i][j] * layerInput[j];
}
result[i] += this.outputLayer.getBias()[i];
}
this.outputLayer.softmax(result);
return result;
}
/**
* {@inheritDoc}
*/
@Override
public double[] getLongTermMemory() {
throw new AIFHError("Can't access DBM memory as array.");
}
}