Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using namespace std;
- struct Connection
- {
- double weight;
- double deltaWeight;
- };
- class Neuron;
- typedef vector<Neuron> Layer;
- // ****************** class Neuron ******************
- class Neuron
- {
- public:
- Neuron(unsigned numOutputs, unsigned myIndex);
- void setOutputVal(double val) { m_outputVal = val; }
- double getOutputVal(void) const { return m_outputVal; }
- void feedForward(const vector<Neuron> &prevLayer);
- void calcOutputGradients(double targetVal);
- void calcHiddenGradients(const vector<Neuron> &nextLayer);
- void updateInputWeights(vector<Neuron> &prevLayer);
- private:
- static double eta; // [0.0..1.0] overall net training rate
- static double alpha; // [0.0..n] multiplier of last weight change (momentum)
- static double transferFunction(double x);
- static double transferFunctionDerivative(double x);
- static double randomWeight(void) { return rand() / double(RAND_MAX); }
- double sumDOW(const vector<Neuron> &nextLayer) const;
- double m_outputVal;
- vector<Connection> m_outputWeights;
- unsigned m_myIndex;
- double m_gradient;
- };
- double Neuron::eta = 0.15; // overall net learning rate, [0.0..1.0]
- double Neuron::alpha = 0.5; // momentum, multiplier of last deltaWeight, [0.0..1.0]
- void Neuron::updateInputWeights(vector<Neuron> &prevLayer)
- {
- // The weights to be updated are in the Connection container
- // in the neurons in the preceding layer
- for (unsigned n = 0; n < prevLayer.size(); ++n) {
- Neuron &neuron = prevLayer[n];
- double oldDeltaWeight = neuron.m_outputWeights[m_myIndex].deltaWeight;
- double newDeltaWeight = eta * neuron.getOutputVal() * m_gradient + alpha * oldDeltaWeight;
- neuron.m_outputWeights[m_myIndex].deltaWeight = newDeltaWeight;
- neuron.m_outputWeights[m_myIndex].weight += newDeltaWeight;
- }
- }
- double Neuron::sumDOW(const vector<Neuron> &nextLayer) const
- {
- double sum = 0.0;
- // Sum our contributions of the errors at the nodes we feed.
- for (unsigned n = 0; n < nextLayer.size() - 1; ++n) {
- sum += m_outputWeights[n].weight * nextLayer[n].m_gradient;
- }
- return sum;
- }
- void Neuron::calcHiddenGradients(const vector<Neuron> &nextLayer)
- {
- double dow = sumDOW(nextLayer);
- m_gradient = dow * Neuron::transferFunctionDerivative(m_outputVal);
- }
- void Neuron::calcOutputGradients(double targetVal)
- {
- double delta = targetVal - m_outputVal;
- m_gradient = delta * Neuron::transferFunctionDerivative(m_outputVal);
- }
- double Neuron::transferFunction(double x)
- {
- // tanh - output range [-1.0..1.0]
- return tanh(x);
- }
- double Neuron::transferFunctionDerivative(double x)
- {
- // tanh derivative
- return 1.0 - x * x;
- }
- void Neuron::feedForward(const vector<Neuron> &prevLayer)
- {
- double sum = 0.0;
- // Sum the previous layer's outputs (which are our inputs)
- // Include the bias node from the previous layer.
- for (unsigned n = 0; n < prevLayer.size(); ++n) {
- sum += prevLayer[n].getOutputVal() *
- prevLayer[n].m_outputWeights[m_myIndex].weight;
- }
- m_outputVal = Neuron::transferFunction(sum);
- }
- Neuron::Neuron(unsigned numOutputs, unsigned myIndex)
- {
- /*
- unsigned numLayers = topology.size();
- for (unsigned layerNum = 0; layerNum < numLayers; ++layerNum) {
- m_layers.push_back(Layer());
- // unsigned numOutputs = layerNum == topology.size() - 1 ? 0 : topology[layerNum + 1];
- unsigned numOutputs;
- if (layerNum == topology.size() - 1) numOutputs = 0;
- else numOutputs = topology[layerNum + 1];
- // We have a new layer, now fill it with neurons, and
- // add a bias neuron in each layer.
- for (unsigned neuronNum = 0; neuronNum <= topology[layerNum]; ++neuronNum) {
- m_layers.back().push_back(Neuron(numOutputs, neuronNum));
- */
- for (unsigned c = 0; c < numOutputs; ++c) {
- m_outputWeights.push_back(Connection());
- m_outputWeights.back().weight = randomWeight();
- }
- m_myIndex = myIndex;
- }
- // ****************** class Net ******************
- class Net
- {
- public:
- Net(const vector<unsigned> &topology);
- void feedForward(const vector<double> &inputVals);
- void backProp(const vector<double> &targetVals);
- void getResults(vector<double> &resultVals) const;
- double getRecentAverageError(void) const { return m_recentAverageError; }
- private:
- vector<vector<Neuron>> m_layers; // m_layers[layerNum][neuronNum]
- double m_error;
- double m_recentAverageError;
- static double m_recentAverageSmoothingFactor;
- };
- double Net::m_recentAverageSmoothingFactor = 100.0; // Number of training samples to average over
- void Net::getResults(vector<double> &resultVals) const
- {
- resultVals.clear();
- for (unsigned n = 0; n < m_layers.back().size() - 1; ++n) {
- resultVals.push_back(m_layers.back()[n].getOutputVal());
- }
- }
- void Net::backProp(const vector<double> &targetVals)
- {
- // Calculate overall net error (RMS of output neuron errors)
- vector<Neuron> &outputLayer = m_layers.back();
- m_error = 0.0;
- for (unsigned n = 0; n < outputLayer.size() - 1; ++n) {
- double delta = targetVals[n] - outputLayer[n].getOutputVal();
- m_error += delta * delta;
- }
- m_error /= outputLayer.size() - 1; // get average error squared
- m_error = sqrt(m_error); // RMS
- // Implement a recent average measurement
- m_recentAverageError =
- (m_recentAverageError * m_recentAverageSmoothingFactor + m_error)
- / (m_recentAverageSmoothingFactor + 1.0);
- // Calculate output layer gradients
- for (unsigned n = 0; n < outputLayer.size() - 1; ++n) {
- outputLayer[n].calcOutputGradients(targetVals[n]);
- }
- // Calculate hidden layer gradients
- for (unsigned layerNum = m_layers.size() - 2; layerNum > 0; --layerNum) {
- Layer &hiddenLayer = m_layers[layerNum];
- Layer &nextLayer = m_layers[layerNum + 1];
- for (unsigned n = 0; n < hiddenLayer.size(); ++n) {
- hiddenLayer[n].calcHiddenGradients(nextLayer);
- }
- }
- // For all layers from outputs to first hidden layer,
- // update connection weights
- for (unsigned layerNum = m_layers.size() - 1; layerNum > 0; --layerNum) {
- vector<Neuron> &layer = m_layers[layerNum];
- vector<Neuron> &prevLayer = m_layers[layerNum - 1];
- for (unsigned n = 0; n < layer.size() - 1; ++n) {
- layer[n].updateInputWeights(prevLayer);
- }
- }
- }
- void Net::feedForward(const vector<double> &inputVals)
- {
- assert(inputVals.size() == m_layers[0].size() - 1);
- // Assign (latch) the input values into the input neurons
- for (unsigned i = 0; i < inputVals.size(); ++i) {
- m_layers[0][i].setOutputVal(inputVals[i]);
- }
- // forward propagate
- for (unsigned layerNum = 1; layerNum < m_layers.size(); ++layerNum) {
- vector<Neuron> &prevLayer = m_layers[layerNum - 1];
- for (unsigned n = 0; n < m_layers[layerNum].size() - 1; ++n) {
- m_layers[layerNum][n].feedForward(prevLayer);
- }
- }
- }
- Net::Net(const vector<unsigned> &topology)
- {
- unsigned numLayers = topology.size();
- for (unsigned layerNum = 0; layerNum < numLayers; ++layerNum) {
- m_layers.push_back(Layer());
- // unsigned numOutputs = layerNum == topology.size() - 1 ? 0 : topology[layerNum + 1];
- unsigned numOutputs;
- if (layerNum == topology.size() - 1) numOutputs = 0;
- else numOutputs = topology[layerNum + 1];
- // We have a new layer, now fill it with neurons, and
- // add a bias neuron in each layer.
- for (unsigned neuronNum = 0; neuronNum <= topology[layerNum]; ++neuronNum) {
- m_layers.back().push_back(Neuron(numOutputs, neuronNum));
- cout << "Made a Neuron!" << endl;
- }
- // Force the bias node's output to 1.0 (it was the last neuron pushed in this layer):
- m_layers.back().back().setOutputVal(1.0);
- }
- }
Add Comment
Please, Sign In to add comment