Program Structure - Composition of Objects (in a Neural Network)

*Disclaimer: This question involves inquiry about the program structure of an artificial neural network (ANN) but can be applied to many different projects as well.

While creating my own, simple ANN on an Arduino Uno I have become perplexed with how to structure my program. I want to uses classes and follow rules of encapsulation and composition.

Basically I want to created a "NeuralNetwork" object. This object will be comprised of multiple "Layer" objects. Each layer object will consist of multiple "Node" objects. Each node object will need to receive inputs and then create/modify an output. The amount of layers and nodes per each layer is defined by the user and known at compile time. --- I have made it to this point, it wasn't too difficult.

The challenge comes in when deciding how to structure the "feedForward" function. The nodes in a layer will need to be able to receive the output values of every node in the previous layer. I have tried to use pointers (aka: a pointer to an array of pointers) which has became very messy and breaks the rules of encapsulation (because node objects would have direct access to the output values of the nodes in the previous layer).

My best idea to structure this would be:
The user calls "myNeuralNetwork.feedForward(inputData...)" from the main sketch.
feedForward() begins running in the NeuralNetwork object.
The first hidden layer uses the inputs passed to the function as its inputs.
The next layer then must use the outputs generated from the nodes of the first layer as its inputs.
This could be done by calling "previousLayer.getOutputs()". Then the previous layer would call "getOutputs()" for each of its nodes.
**Now here is my real problem: How do I pass the values of these outputs back out into the feedForward() function and then down into the next layer to use as inputs? Do I create an array of the outputs in the previous layer and then somehow pass that to the next layer which will pass those values to its nodes.

  • I don't want to pass direct references to the outputs of the previous layer.
  • If an array is created to house the output values of the previous layer, obviously it can not go out of scope before it is used by the current layer.
  • I definitely do NOT want to use global variables. That would break encapsulation.

Whoo! That was a long post but I just wanted to get out all the information.
Yes this project could be accomplished faster and easier if I didn't make it sooo object oriented and if I didn't follow rules of encapsulation but I want to learn the best (*better, because there is no best) way to do it.

So any help on how to structure all the objects and data in this project would be greatly appreciated!

Also, I would like to know how to use the user inputed values as inputs for the first hidden layer but from then on use the outputs from the previous layer as the inputs for the current layer. I think this would take two different functions. Do I add some sort of identifier to the Layer class and set it as true if it is the first hidden layer? Otherwise how would it know which function to use?

Make a Synapse object to connect neurons. Each has an Input neuron and and an Output neuron. When you need Outputs, ask each neuron in the Output layer for their value. Those neurons would get values from their Input neurons and multiply by the weight. The synapses would get the value from their input neurons (in the previous layer). Those neurons would get their input from the layer before that... all the way to the input layer.

The input layer would be a special kind of neurons you could set directly.

Structure for feedforward network should rather be simple:

Yes, an array of pointers to Layer objects is the most feasible. Since the next layer should be immediately following the current index. And the previous layer is before the current index.

The Layer class should have a method to compute the output and take in the input

class Layer {
public:
   GetInput(float *input, int inputLength);
   ComputeOutput(float *output, int outputLength);

};

Note: this approach requires dynamic allocate memory.