By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
428,853 Members | 2,246 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 428,853 IT Pros & Developers. It's quick & easy.

Neural Network assert crash

P: 88
Hey I have a neural network program crashing because of an assert command in the Net::feedForward function. Why?

I have tried to debug but I didn't understand the data given from it. It's just memmory adresses, some machine commands and it didn't look like it were any data in the adresses (as far as I could see).

Any help would be great :-)

Program:
Expand|Select|Wrap|Line Numbers
  1. #include "vector"
  2. #include <iostream>
  3. #include <cstdlib>
  4. #include <cassert>
  5. #include <cmath>
  6.  
  7. using namespace std;
  8.  
  9. struct Connection
  10. {
  11.     double weight;
  12.     double deltaWeight;
  13. };
  14.  
  15. class Neuron;
  16. typedef vector<Neuron> Layer;
  17.  
  18. class Neuron
  19. {
  20. public:
  21.     Neuron(unsigned numOutputs, unsigned myIndex);
  22.     void setOutputVal(double val) { m_outputVal = val; };
  23.     double getOutputVal(void) const { return m_outputVal; };
  24.     void feedForward(Layer &prevLayer);
  25.     void calcOutputGradients(double targetVal);
  26.     void calcHiddenGradients(const Layer &nextLayer);
  27.     void updateInputWeights(Layer &prevLayer);
  28.  
  29. private:
  30.     static double eta; //[0.0 .. 1.0] overall net training rate
  31.     static double alpha; //[0.0 .. n] multiplier of last weight change (momentum)
  32.     static double transferFunction(double x);
  33.     static double transferFunctionDerivative(double x);
  34.     static double randomWeight(void) { return rand() / double(RAND_MAX); };
  35.     double sumDOW(const Layer &nextLayer) const;
  36.     double m_outputVal;
  37.     vector<Connection> m_outputWeights;
  38.     unsigned m_myIndex;
  39.     double m_gradient;
  40. };
  41.  
  42. double Neuron::eta = 0.15; //overall net learning rate. [0.0 .. 1.0]
  43. double Neuron::alpha = 0.5; //momentum, multiplier of last deltaWeight, [0.0 .. n]
  44.  
  45. void Neuron::updateInputWeights(Layer &prevLayer)
  46. {
  47.     //the weights to be updates are in the Connection container
  48.     //in the neurons in the preciding layer
  49.  
  50.     for (unsigned n = 0; n < prevLayer.size(); n++)
  51.     {
  52.         Neuron &neuron = prevLayer[n];
  53.         double oldDeltaWeight = neuron.m_outputWeights[m_myIndex].deltaWeight;
  54.  
  55.         double newDeltaWeight =
  56.             //Individual input, magnified by the gradient and train rate
  57.             eta /*<- Net learning rate*/
  58.             * neuron.getOutputVal()
  59.             * m_gradient
  60.             //also add momentum = a fraction of the previous delta weight
  61.             + alpha /*<- momentum*/
  62.             *oldDeltaWeight;
  63.  
  64.         neuron.m_outputWeights[m_myIndex].deltaWeight = newDeltaWeight;
  65.         neuron.m_outputWeights[m_myIndex].weight += newDeltaWeight;
  66.     }
  67. }
  68.  
  69. double Neuron::sumDOW(const Layer &nextLayer) const
  70. {
  71.     double sum = 0.0;
  72.  
  73.     //sum out contributions of the errors at the nodes we feed
  74.     for (unsigned n = 0; n < nextLayer.size() - 1; n++)
  75.     {
  76.         sum += m_outputWeights[n].weight * nextLayer[n].m_gradient;
  77.     }
  78.  
  79.     return sum;
  80. }
  81.  
  82. void Neuron::calcHiddenGradients(const Layer &nextLayer)
  83. {
  84.     double dow = sumDOW(nextLayer);
  85.     m_gradient = dow * Neuron::transferFunctionDerivative(m_outputVal);
  86. }
  87.  
  88. void Neuron::calcOutputGradients(double targetVal)
  89. {
  90.     double delta = targetVal - m_outputVal;
  91.     m_gradient = delta * Neuron::transferFunctionDerivative(m_outputVal);
  92. }
  93.  
  94. double Neuron::transferFunction(double x)
  95. {
  96.     //tanh - output range [-1.0 .. 1.0]
  97.     return tanh(x);
  98. }
  99.  
  100. double Neuron::transferFunctionDerivative(double x)
  101. {
  102.     //tanh - output range [-1.0 .. 1.0]
  103.     return 1.0 - x*x;
  104. }
  105.  
  106. void Neuron::feedForward(Layer &prevLayer)
  107. {
  108.     double sum = 0.0;
  109.  
  110.     //sum the previous layer's outputs (witch are our inputs)
  111.     //Include the bias neuron from the pervious layer
  112.  
  113.     for (unsigned n = 0; n < prevLayer.size(); n++)
  114.     {
  115.         sum += prevLayer[n].getOutputVal() *
  116.                 prevLayer[n].m_outputWeights[m_myIndex].weight;
  117.     }
  118.  
  119.     m_outputVal = Neuron::transferFunction(sum);
  120. }
  121.  
  122. Neuron::Neuron(unsigned numOutputs, unsigned myIndex)
  123. {
  124.     for (unsigned c = 0; c < numOutputs; c++)
  125.     {
  126.         m_outputWeights.push_back(Connection());
  127.         m_outputWeights.back().weight = randomWeight();
  128.     }
  129.  
  130.     m_myIndex = myIndex;
  131. }
  132.  
  133. class Net
  134. {
  135. public:
  136.     Net(const vector<unsigned> &topology);
  137.     void feedForward(const vector<double> &inputVals);
  138.     void backProp(const vector<double> &targetVals);
  139.     void getResults(vector<double> &resultVals) const;
  140.  
  141. private:
  142.     vector<Layer> m_layer;
  143.     double m_error;
  144.     double m_recentAverageError;
  145.     double m_recentAverageSmoothingFactor;
  146. };
  147.  
  148. void Net::getResults(vector<double> &resultVals) const
  149. {
  150.     resultVals.clear();
  151.  
  152.     for (unsigned n = 0; n < m_layer.back().size() - 1; n++)
  153.     {
  154.         resultVals.push_back(m_layer.back()[n].getOutputVal());
  155.     }
  156. }
  157.  
  158. void Net::backProp(const vector<double> &targetVals)
  159. {
  160.     //Calculate overall net error (RMS of output neuron errors)
  161.  
  162.     Layer &outputLayer = m_layer.back();
  163.     m_error = 0.0;
  164.     for (unsigned n = 0; n < outputLayer.size() - 1; n++)
  165.     {
  166.         double delta = targetVals[n] - outputLayer[n].getOutputVal();
  167.         m_error += delta * delta;
  168.     }
  169.     m_error /= outputLayer.size() - 1; //get avarage error squared
  170.     m_error = sqrt(m_error); //RMS
  171.  
  172.     //Implement a recent average measurement
  173.  
  174.     m_recentAverageError = (m_recentAverageError * m_recentAverageSmoothingFactor + m_error)
  175.                             / (m_recentAverageSmoothingFactor + 1.0);
  176.  
  177.     //Calculate output layer gradients
  178.     for (unsigned n = 0; n < outputLayer.size() - 1; n++)
  179.     {
  180.         outputLayer[n].calcOutputGradients(targetVals[n]);
  181.     }
  182.  
  183.     //Calculate gradients on hidden layer
  184.     for (unsigned layerNum = m_layer.size() - 2; layerNum > 0; layerNum--)
  185.     {
  186.         Layer &hiddenLayer = m_layer[layerNum];
  187.         Layer &nextLayer = m_layer[layerNum + 1];
  188.  
  189.         for (unsigned n = 0; n < hiddenLayer.size(); n++)
  190.         {
  191.             hiddenLayer[n].calcHiddenGradients(nextLayer);
  192.         }
  193.     }
  194.  
  195.     //For all layer from output to first hidden layer,
  196.     //update connection weights
  197.     for (unsigned layerNum = m_layer.size() - 1; layerNum > 0; layerNum--)
  198.     {
  199.         Layer &layer = m_layer[layerNum];
  200.         Layer &prevLayer = m_layer[layerNum - 1];
  201.  
  202.         for (unsigned n = 0; n < layer.size() - 1; n++)
  203.         {
  204.             layer[n].updateInputWeights(prevLayer);
  205.         }
  206.     }
  207. }
  208.  
  209. Net::Net(const vector<unsigned> &topology)
  210. {
  211.     unsigned numLayers = topology.size();
  212.     for (unsigned layerNum = 0; layerNum < numLayers; layerNum++)
  213.     {
  214.         m_layer.push_back(Layer());
  215.         unsigned numOutputs = layerNum == topology.size() - 1 ? 0: topology[layerNum + 1];
  216.  
  217.         //we made a layer and now we need to fill it with neurons
  218.         for (unsigned neuronNum = 0; neuronNum <= topology[layerNum]; neuronNum++)
  219.         {
  220.             m_layer.back().push_back(Neuron(numOutputs, neuronNum));
  221.             cout << "Made a Neuron" << endl;
  222.         }
  223.     }
  224.  
  225.     //Force the bias node's output value to 1.0 It's the last neuron created above
  226.     m_layer.back().back().setOutputVal(1.0);
  227. }
  228.  
  229. void Net::feedForward(const vector<double> &inputVals)
  230. {
  231.     assert(inputVals.size() == m_layer[0].size() - 1);
  232.  
  233.     //Assign (latch) the input values into the input neurons
  234.     for (unsigned i = 0; i < inputVals.size(); i++)
  235.     {
  236.         m_layer[0][i].setOutputVal(inputVals[i]);
  237.     }
  238.  
  239.     //Forward propagate
  240.     for (unsigned layerNum = 1; layerNum < m_layer.size(); layerNum++)
  241.     {
  242.         Layer &prevLayer = m_layer[layerNum - 1];
  243.         for (unsigned n = 0; n < m_layer[layerNum].size() - 1; n++)
  244.         {
  245.             m_layer[layerNum][n].feedForward(prevLayer);
  246.         }
  247.     }
  248. }
  249.  
Nov 28 '15 #1
Share this Question
Share on Google+
11 Replies


weaknessforcats
Expert Mod 5K+
P: 9,197
This assert:

Expand|Select|Wrap|Line Numbers
  1. assert(inputVals.size() == m_layer[0].size() - 1);
inputVals is a vector<double> and m_layer is a vector<Neuron> making m_layer[0] a Neuron.

Is that right? The number of Neuron in m_layer[0] must be one more than the number of doubles in inputVals?

Actually, Net::feedforward is not called in the code you posted. I just see a call to Neuron::feedForward.

Give me more info.
Nov 28 '15 #2

P: 88
Yes that is correct.

Here's the main loop if it helps

Expand|Select|Wrap|Line Numbers
  1. vector<unsigned> topology;
  2.     topology.push_back(3);
  3.     topology.push_back(2);
  4.     topology.push_back(1);
  5.     Net myNet(topology);
  6.  
  7.     vector<double> inputVals;
  8.     myNet.feedForward(inputVals);
  9.  
  10.     vector<double> targetVals;
  11.     myNet.backProp(targetVals);
  12.  
  13.     vector<double> resultVals;
  14.     myNet.getResults(resultVals);
  15.  
Nov 29 '15 #3

P: 88
Here's a video about Neural Network. This is the one I watched:
https://vimeo.com/19569529
Nov 29 '15 #4

weaknessforcats
Expert Mod 5K+
P: 9,197
None of the doubles in these vectors has been initialized. So the size() of inputVals is 0.

This code:
Expand|Select|Wrap|Line Numbers
  1. assert(inputVals.size() == m_layer[0].size() - 1);
  2.  
  3.     //Assign (latch) the input values into the input neurons
  4.     for (unsigned i = 0; i < inputVals.size(); i++)
  5.     {
  6.         m_layer[0][i].setOutputVal(inputVals[i]);
  7.     }
shows the loop executing if i < inputVals.size(). But i is 0 and inputVals.size() is 0 so 0 < 0 is false and the loop never executes.

That's why the assert.

I think you should run a loop that puts some doubles in these vectors with the double initialized to 0.0.

Do it here:

vector<double> inputVals;

//add doubles to inputVals here.

myNet.feedForward(inputVals);

This is just an eyeball debug. I didn't actually run the code.
Nov 29 '15 #5

P: 88
Ok, so if I understood you correctly I should add inputVals.push_back(0.46); in the main loop.

CurrentCode:

Expand|Select|Wrap|Line Numbers
  1. #include "stdafx.h"
  2. #include "vector"
  3. #include <iostream>
  4. #include <cstdlib>
  5. #include <cassert>
  6. #include <cmath>
  7.  
  8. using namespace std;
  9.  
  10. vector<double> inputVals;
  11. vector<double> targetVals;
  12. vector<double> resultVals;
  13.  
  14. struct Connection
  15. {
  16.     double weight;
  17.     double deltaWeight;
  18. };
  19.  
  20. class Neuron;
  21. typedef vector<Neuron> Layer;
  22.  
  23. class Neuron
  24. {
  25. public:
  26.     Neuron(unsigned numOutputs, unsigned myIndex);
  27.     void setOutputVal(double val) { m_outputVal = val; };
  28.     double getOutputVal(void) const { return m_outputVal; };
  29.     void feedForward(Layer &prevLayer);
  30.     void calcOutputGradients(double targetVal);
  31.     void calcHiddenGradients(const Layer &nextLayer);
  32.     void updateInputWeights(Layer &prevLayer);
  33.  
  34. private:
  35.     static double eta; //[0.0 .. 1.0] overall net training rate
  36.     static double alpha; //[0.0 .. n] multiplier of last weight change (momentum)
  37.     static double transferFunction(double x);
  38.     static double transferFunctionDerivative(double x);
  39.     static double randomWeight(void) { return rand() / double(RAND_MAX); };
  40.     double sumDOW(const Layer &nextLayer) const;
  41.     double m_outputVal;
  42.     vector<Connection> m_outputWeights;
  43.     unsigned m_myIndex;
  44.     double m_gradient;
  45. };
  46.  
  47. double Neuron::eta = 0.15; //overall net learning rate. [0.0 .. 1.0]
  48. double Neuron::alpha = 0.5; //momentum, multiplier of last deltaWeight, [0.0 .. n]
  49.  
  50. void Neuron::updateInputWeights(Layer &prevLayer)
  51. {
  52.     //the weights to be updates are in the Connection container
  53.     //in the neurons in the preciding layer
  54.  
  55.     for (unsigned n = 0; n < prevLayer.size(); n++)
  56.     {
  57.         Neuron &neuron = prevLayer[n];
  58.         double oldDeltaWeight = neuron.m_outputWeights[m_myIndex].deltaWeight;
  59.  
  60.         double newDeltaWeight =
  61.             //Individual input, magnified by the gradient and train rate
  62.             eta /*<- Net learning rate*/
  63.             * neuron.getOutputVal()
  64.             * m_gradient
  65.             //also add momentum = a fraction of the previous delta weight
  66.             + alpha /*<- momentum*/
  67.             *oldDeltaWeight;
  68.  
  69.         neuron.m_outputWeights[m_myIndex].deltaWeight = newDeltaWeight;
  70.         neuron.m_outputWeights[m_myIndex].weight += newDeltaWeight;
  71.     }
  72. }
  73.  
  74. double Neuron::sumDOW(const Layer &nextLayer) const
  75. {
  76.     double sum = 0.0;
  77.  
  78.     //sum out contributions of the errors at the nodes we feed
  79.     for (unsigned n = 0; n < nextLayer.size() - 1; n++)
  80.     {
  81.         sum += m_outputWeights[n].weight * nextLayer[n].m_gradient;
  82.     }
  83.  
  84.     return sum;
  85. }
  86.  
  87. void Neuron::calcHiddenGradients(const Layer &nextLayer)
  88. {
  89.     double dow = sumDOW(nextLayer);
  90.     m_gradient = dow * Neuron::transferFunctionDerivative(m_outputVal);
  91. }
  92.  
  93. void Neuron::calcOutputGradients(double targetVal)
  94. {
  95.     double delta = targetVal - m_outputVal;
  96.     m_gradient = delta * Neuron::transferFunctionDerivative(m_outputVal);
  97. }
  98.  
  99. double Neuron::transferFunction(double x)
  100. {
  101.     //tanh - output range [-1.0 .. 1.0]
  102.     return tanh(x);
  103. }
  104.  
  105. double Neuron::transferFunctionDerivative(double x)
  106. {
  107.     //tanh - output range [-1.0 .. 1.0]
  108.     return 1.0 - x*x;
  109. }
  110.  
  111. void Neuron::feedForward(Layer &prevLayer)
  112. {
  113.     double sum = 0.0;
  114.  
  115.     //sum the previous layer's outputs (witch are our inputs)
  116.     //Include the bias neuron from the pervious layer
  117.  
  118.     for (unsigned n = 0; n < prevLayer.size(); n++)
  119.     {
  120.         sum += prevLayer[n].getOutputVal() *
  121.                 prevLayer[n].m_outputWeights[m_myIndex].weight;
  122.     }
  123.  
  124.     m_outputVal = Neuron::transferFunction(sum);
  125. }
  126.  
  127. Neuron::Neuron(unsigned numOutputs, unsigned myIndex)
  128. {
  129.     for (unsigned c = 0; c < numOutputs; c++)
  130.     {
  131.         m_outputWeights.push_back(Connection());
  132.         m_outputWeights.back().weight = randomWeight();
  133.     }
  134.  
  135.     m_myIndex = myIndex;
  136. }
  137.  
  138. class Net
  139. {
  140. public:
  141.     Net(const vector<unsigned> &topology);
  142.     void feedForward(const vector<double> &inputVals);
  143.     void backProp(const vector<double> &targetVals);
  144.     void getResults(vector<double> &resultVals) const;
  145.  
  146. private:
  147.     vector<Layer> m_layer;
  148.     double m_error;
  149.     double m_recentAverageError;
  150.     double m_recentAverageSmoothingFactor;
  151. };
  152.  
  153. void Net::getResults(vector<double> &resultVals) const
  154. {
  155.     resultVals.clear();
  156.  
  157.     for (unsigned n = 0; n < m_layer.back().size() - 1; n++)
  158.     {
  159.         resultVals.push_back(m_layer.back()[n].getOutputVal());
  160.     }
  161. }
  162.  
  163. void Net::backProp(const vector<double> &targetVals)
  164. {
  165.     //Calculate overall net error (RMS of output neuron errors)
  166.  
  167.     Layer &outputLayer = m_layer.back();
  168.     m_error = 0.0;
  169.     for (unsigned n = 0; n < outputLayer.size() - 1; n++)
  170.     {
  171.         double delta = targetVals[n] - outputLayer[n].getOutputVal();
  172.         m_error += delta * delta;
  173.     }
  174.     m_error /= outputLayer.size() - 1; //get avarage error squared
  175.     m_error = sqrt(m_error); //RMS
  176.  
  177.     //Implement a recent average measurement
  178.  
  179.     m_recentAverageError = (m_recentAverageError * m_recentAverageSmoothingFactor + m_error)
  180.                             / (m_recentAverageSmoothingFactor + 1.0);
  181.  
  182.     //Calculate output layer gradients
  183.     for (unsigned n = 0; n < outputLayer.size() - 1; n++)
  184.     {
  185.         outputLayer[n].calcOutputGradients(targetVals[n]);
  186.     }
  187.  
  188.     //Calculate gradients on hidden layer
  189.     for (unsigned layerNum = m_layer.size() - 2; layerNum > 0; layerNum--)
  190.     {
  191.         Layer &hiddenLayer = m_layer[layerNum];
  192.         Layer &nextLayer = m_layer[layerNum + 1];
  193.  
  194.         for (unsigned n = 0; n < hiddenLayer.size(); n++)
  195.         {
  196.             hiddenLayer[n].calcHiddenGradients(nextLayer);
  197.         }
  198.     }
  199.  
  200.     //For all layer from output to first hidden layer, 
  201.     //update connection weights
  202.     for (unsigned layerNum = m_layer.size() - 1; layerNum > 0; layerNum--)
  203.     {
  204.         Layer &layer = m_layer[layerNum];
  205.         Layer &prevLayer = m_layer[layerNum - 1];
  206.  
  207.         for (unsigned n = 0; n < layer.size() - 1; n++)
  208.         {
  209.             layer[n].updateInputWeights(prevLayer);
  210.         }
  211.     }
  212. }
  213.  
  214. Net::Net(const vector<unsigned> &topology)
  215. {
  216.     unsigned numLayers = topology.size();
  217.     for (unsigned layerNum = 0; layerNum < numLayers; layerNum++)
  218.     {
  219.         m_layer.push_back(Layer());
  220.         unsigned numOutputs = layerNum == topology.size() - 1 ? 0: topology[layerNum + 1];
  221.  
  222.         //we made a layer and now we need to fill it with neurons
  223.         for (unsigned neuronNum = 0; neuronNum <= topology[layerNum]; neuronNum++)
  224.         {
  225.             m_layer.back().push_back(Neuron(numOutputs, neuronNum));
  226.             cout << "Made a Neuron" << endl;
  227.         }
  228.     }
  229.  
  230.     //Force the bias node's output value to 1.0 It's the last neuron created above
  231.     m_layer.back().back().setOutputVal(1.0);
  232. }
  233.  
  234. void Net::feedForward(const vector<double> &inputVals)
  235. {
  236.     //assert(inputVals.size() == m_layer[0].size() - 1);
  237.  
  238.     //Assign (latch) the input values into the input neurons
  239.     for (unsigned i = 0; i < inputVals.size(); i++)
  240.     {
  241.         m_layer[0][i].setOutputVal(inputVals[i]);
  242.     }
  243.  
  244.     //Forward propagate
  245.     for (unsigned layerNum = 1; layerNum < m_layer.size(); layerNum++)
  246.     {
  247.         Layer &prevLayer = m_layer[layerNum - 1];
  248.         for (unsigned n = 0; n < m_layer[layerNum].size() - 1; n++)
  249.         {
  250.             m_layer[layerNum][n].feedForward(prevLayer);
  251.         }
  252.     }
  253. }
  254.  
  255. //-----------------------MAIN LOOP--------------------
  256. int main()
  257. {
  258.     vector<unsigned> topology;
  259.     topology.push_back(3);
  260.     topology.push_back(2);
  261.     topology.push_back(1);
  262.     Net myNet(topology);
  263.  
  264.     inputVals.push_back(0.45);
  265.     inputVals.push_back(0.65);
  266.     inputVals.push_back(0.30);
  267.  
  268.     myNet.feedForward(inputVals);
  269.  
  270.     myNet.backProp(targetVals);
  271.  
  272.     myNet.getResults(resultVals);
  273. }
  274.  
Nov 30 '15 #6

weaknessforcats
Expert Mod 5K+
P: 9,197
That looks OK. At least inputVals.size() will return 3.

That makes inputVals[0],inputVals[1],inputVals[2] OK to use.
Nov 30 '15 #7

P: 88
Well actually I got errors with .push_back();
so I changed it to inputVals[0] = 0.45; and so on... but it still crashes even without the assert command and the bug fixed.
It's not happy with the vector header file in VC++.

It says "Expression: vector subscript out of range"
Program: c:/WINDOWS/SYSTEM32/MSVCP120D.dll
File: c/programs files (x86)/microsoft visual studio 12.0/vc/include/vector
Line: 1201
Nov 30 '15 #8

weaknessforcats
Expert Mod 5K+
P: 9,197
Your code is missing some vectors:

Expand|Select|Wrap|Line Numbers
  1. int main()
  2. {
  3.     vector<unsigned> topology;
  4.     topology.push_back(3);
  5.     topology.push_back(2);
  6.     topology.push_back(1);
  7.     Net myNet(topology);
  8.  
  9.     vector<double> targetVals; <<<<<<<!!!!
  10.     vector<double> resultVals; <<<<<!!!!!
  11.  
  12.     vector<double> inputVals;
  13.     inputVals.push_back(0.45);
  14.     inputVals.push_back(0.65);
  15.     inputVals.push_back(0.30);
  16.  
  17.     myNet.feedForward(inputVals);
  18.  
  19.     myNet.backProp(targetVals);
  20.  
  21.     myNet.getResults(resultVals);
  22. }
  23.  
Of course you must add some doubles that have an initialized value to these vectors.

This time I ran the code and I got nine lines of "Made a Neuron" then a crash that an index was out of range. That tells me that these vectors need to be precisely set up in main() before your first call to a feedForward function.

The [ ] operator in vector requires a double to be in that location or you get a crash.
Dec 1 '15 #9

P: 88
Still getting the "Debug Assertion failed" crash. "vector subscript out of range"

New Code:
Expand|Select|Wrap|Line Numbers
  1. // NeuralNet_SelfLearn.cpp : Defines the entry point for the console application.
  2. //
  3.  
  4. #include "stdafx.h"
  5. #include "vector"
  6. #include <iostream>
  7. #include <cstdlib>
  8. #include <cassert>
  9. #include <cmath>
  10.  
  11. using namespace std;
  12.  
  13. struct Connection
  14. {
  15.     double weight;
  16.     double deltaWeight;
  17. };
  18.  
  19. class Neuron;
  20. typedef vector<Neuron> Layer;
  21.  
  22. class Neuron
  23. {
  24. public:
  25.     Neuron(unsigned numOutputs, unsigned myIndex);
  26.     void setOutputVal(double val) { m_outputVal = val; };
  27.     double getOutputVal(void) { return m_outputVal; };
  28.     void feedForward(Layer &prevLayer);
  29.     void calcOutputGradients(double targetVal);
  30.     void calcHiddenGradients(Layer &nextLayer);
  31.     void updateInputWeights(Layer &prevLayer);
  32.  
  33. private:
  34.     static double eta; //[0.0 .. 1.0] overall net training rate
  35.     static double alpha; //[0.0 .. n] multiplier of last weight change (momentum)
  36.     static double transferFunction(double x);
  37.     static double transferFunctionDerivative(double x);
  38.     static double randomWeight(void) { return rand() / double(RAND_MAX); };
  39.     double sumDOW(Layer &nextLayer);
  40.     double m_outputVal;
  41.     vector<Connection> m_outputWeights;
  42.     unsigned m_myIndex;
  43.     double m_gradient;
  44. };
  45.  
  46. double Neuron::eta = 0.15; //overall net learning rate. [0.0 .. 1.0]
  47. double Neuron::alpha = 0.5; //momentum, multiplier of last deltaWeight, [0.0 .. n]
  48.  
  49. void Neuron::updateInputWeights(Layer &prevLayer)
  50. {
  51.     //the weights to be updates are in the Connection container
  52.     //in the neurons in the preciding layer
  53.  
  54.     for (unsigned n = 0; n < prevLayer.size(); n++)
  55.     {
  56.         Neuron &neuron = prevLayer[n];
  57.         double oldDeltaWeight = neuron.m_outputWeights[m_myIndex].deltaWeight;
  58.  
  59.         double newDeltaWeight =
  60.             //Individual input, magnified by the gradient and train rate
  61.             eta /*<- Net learning rate*/
  62.             * neuron.getOutputVal()
  63.             * m_gradient
  64.             //also add momentum = a fraction of the previous delta weight
  65.             + alpha /*<- momentum*/
  66.             *oldDeltaWeight;
  67.  
  68.         neuron.m_outputWeights[m_myIndex].deltaWeight = newDeltaWeight;
  69.         neuron.m_outputWeights[m_myIndex].weight += newDeltaWeight;
  70.     }
  71. }
  72.  
  73. double Neuron::sumDOW(Layer &nextLayer)
  74. {
  75.     double sum = 0.0;
  76.  
  77.     //sum out contributions of the errors at the nodes we feed
  78.     for (unsigned n = 0; n < nextLayer.size() - 1; n++)
  79.     {
  80.         sum += m_outputWeights[n].weight * nextLayer[n].m_gradient;
  81.     }
  82.  
  83.     return sum;
  84. }
  85.  
  86. void Neuron::calcHiddenGradients(Layer &nextLayer)
  87. {
  88.     double dow = sumDOW(nextLayer);
  89.     m_gradient = dow * Neuron::transferFunctionDerivative(m_outputVal);
  90. }
  91.  
  92. void Neuron::calcOutputGradients(double targetVal)
  93. {
  94.     double delta = targetVal - m_outputVal;
  95.     m_gradient = delta * Neuron::transferFunctionDerivative(m_outputVal);
  96. }
  97.  
  98. double Neuron::transferFunction(double x)
  99. {
  100.     //tanh - output range [-1.0 .. 1.0]
  101.     return tanh(x);
  102. }
  103.  
  104. double Neuron::transferFunctionDerivative(double x)
  105. {
  106.     //tanh - output range [-1.0 .. 1.0]
  107.     return 1.0 - x*x;
  108. }
  109.  
  110. void Neuron::feedForward(Layer &prevLayer)
  111. {
  112.     double sum = 0.0;
  113.  
  114.     //sum the previous layer's outputs (witch are our inputs)
  115.     //Include the bias neuron from the pervious layer
  116.  
  117.     for (unsigned n = 0; n < prevLayer.size(); n++)
  118.     {
  119.         sum += prevLayer[n].getOutputVal() *
  120.                 prevLayer[n].m_outputWeights[m_myIndex].weight;
  121.     }
  122.  
  123.     m_outputVal = Neuron::transferFunction(sum);
  124. }
  125.  
  126. Neuron::Neuron(unsigned numOutputs, unsigned myIndex)
  127. {
  128.     for (unsigned c = 0; c < numOutputs; c++)
  129.     {
  130.         m_outputWeights.push_back(Connection());
  131.         m_outputWeights.back().weight = randomWeight();
  132.     }
  133.  
  134.     m_myIndex = myIndex;
  135. }
  136.  
  137. class Net
  138. {
  139. public:
  140.     Net(vector<unsigned> &topology);
  141.     void feedForward(vector<double> &inputVals);
  142.     void backProp(vector<double> &targetVals);
  143.     void getResults(vector<double> &resultVals);
  144.  
  145. private:
  146.     vector<Layer> m_layer;
  147.     double m_error;
  148.     double m_recentAverageError;
  149.     double m_recentAverageSmoothingFactor;
  150. };
  151.  
  152. void Net::getResults(vector<double> &resultVals)
  153. {
  154.     resultVals.clear();
  155.  
  156.     for (unsigned n = 0; n < m_layer.back().size() - 1; n++)
  157.     {
  158.         resultVals.push_back(m_layer.back()[n].getOutputVal());
  159.     }
  160. }
  161.  
  162. void Net::backProp(vector<double> &targetVals)
  163. {
  164.     //Calculate overall net error (RMS of output neuron errors)
  165.  
  166.     Layer &outputLayer = m_layer.back();
  167.     m_error = 0.0;
  168.     for (unsigned n = 0; n < outputLayer.size() - 1; n++)
  169.     {
  170.         double delta = targetVals[n] - outputLayer[n].getOutputVal();
  171.         m_error += delta * delta;
  172.     }
  173.     m_error /= outputLayer.size() - 1; //get avarage error squared
  174.     m_error = sqrt(m_error); //RMS
  175.  
  176.     //Implement a recent average measurement
  177.  
  178.     m_recentAverageError = (m_recentAverageError * m_recentAverageSmoothingFactor + m_error)
  179.                             / (m_recentAverageSmoothingFactor + 1.0);
  180.  
  181.     //Calculate output layer gradients
  182.     for (unsigned n = 0; n < outputLayer.size() - 1; n++)
  183.     {
  184.         outputLayer[n].calcOutputGradients(targetVals[n]);
  185.     }
  186.  
  187.     //Calculate gradients on hidden layer
  188.     for (unsigned layerNum = m_layer.size() - 2; layerNum > 0; layerNum--)
  189.     {
  190.         Layer &hiddenLayer = m_layer[layerNum];
  191.         Layer &nextLayer = m_layer[layerNum + 1];
  192.  
  193.         for (unsigned n = 0; n < hiddenLayer.size(); n++)
  194.         {
  195.             hiddenLayer[n].calcHiddenGradients(nextLayer);
  196.         }
  197.     }
  198.  
  199.     //For all layer from output to first hidden layer, 
  200.     //update connection weights
  201.     for (unsigned layerNum = m_layer.size() - 1; layerNum > 0; layerNum--)
  202.     {
  203.         Layer &layer = m_layer[layerNum];
  204.         Layer &prevLayer = m_layer[layerNum - 1];
  205.  
  206.         for (unsigned n = 0; n < layer.size() - 1; n++)
  207.         {
  208.             layer[n].updateInputWeights(prevLayer);
  209.         }
  210.     }
  211. }
  212.  
  213. Net::Net(vector<unsigned> &topology)
  214. {
  215.     unsigned numLayers = topology.size();
  216.     for (unsigned layerNum = 0; layerNum < numLayers; layerNum++)
  217.     {
  218.         m_layer.push_back(Layer());
  219.         unsigned numOutputs = layerNum == topology.size() - 1 ? 0: topology[layerNum + 1];
  220.  
  221.         //we made a layer and now we need to fill it with neurons
  222.         for (unsigned neuronNum = 0; neuronNum <= topology[layerNum]; neuronNum++)
  223.         {
  224.             m_layer.back().push_back(Neuron(numOutputs, neuronNum));
  225.             cout << "Made a Neuron" << endl;
  226.         }
  227.     }
  228.  
  229.     //Force the bias node's output value to 1.0 It's the last neuron created above
  230.     m_layer.back().back().setOutputVal(1.0);
  231. }
  232.  
  233. void Net::feedForward(vector<double> &inputVals)
  234. {
  235.     assert(inputVals.size() == m_layer[0].size() - 1);
  236.  
  237.     //Assign (latch) the input values into the input neurons
  238.     for (unsigned i = 0; i < inputVals.size(); i++)
  239.     {
  240.         cout << "latching inputVals to input neurons" << endl;
  241.         m_layer[0][i].setOutputVal(inputVals[i]);
  242.     }
  243.  
  244.     //Forward propagate
  245.     for (unsigned layerNum = 1; layerNum < m_layer.size(); layerNum++)
  246.     {
  247.         Layer &prevLayer = m_layer[layerNum - 1];
  248.         for (unsigned n = 0; n < m_layer[layerNum].size() - 1; n++)
  249.         {
  250.             m_layer[layerNum][n].feedForward(prevLayer);
  251.         }
  252.     }
  253. }
  254.  
  255. //-----------------------MAIN LOOP--------------------
  256. int main()
  257. {
  258.     vector<unsigned> topology;
  259.     vector<double> inputVals;
  260.     vector<double> targetVals;
  261.     vector<double> resultVals;
  262.  
  263.     inputVals[0] = 0.45;
  264.     inputVals[1] = 0.65;
  265.     inputVals[2] = 0.30;
  266.  
  267.     targetVals[0] = 0.20;
  268.  
  269.     topology.push_back(3);
  270.     topology.push_back(2);
  271.     topology.push_back(1);
  272.  
  273.     Net myNet(topology);
  274.  
  275.     myNet.feedForward(inputVals);
  276.  
  277.     myNet.backProp(targetVals);
  278.  
  279.     myNet.getResults(resultVals);
  280. }
  281.  
Dec 1 '15 #10

P: 88
Sorry, I have apparently miss understood you Weaknessforcats and the error messages I recieve. The latest problem is me trying to use a vector as an array. I did inputVals[0] = 0.45; when I should have done inputVals.push_back(0.45);

Thank you for all the help. The program compiles and runs beautifly.
Dec 1 '15 #11

weaknessforcats
Expert Mod 5K+
P: 9,197
A vector is an array. It is the C++ replacement for an array. There is one big difference. In an array you must specify the number of elements but in a vector this is optional. So the index operator [ ] works if the element is already there. If the element is not there, you do a push_back, or equivalent, before you use the index operator. Otherwise, you crash with an index out of range error.
Dec 1 '15 #12

Post your reply

Sign in to post your reply or Sign up for a free account.