1
$\begingroup$

I'm working on an implementation of NEAT, which evolves neural networks with small and sparse topologies.

Evaluating a sparse and possibly recurrent network requires a different approach than the matrix operations of dense networks, and I'm trying to wrap my head around the order in which nodes should be evaluated.

I've set up a simple example:

  • Every node (except inputs) starts at 0. (t0)
  • Every weight is 1. There's no activation function or biases.

enter image description here

Assuming that the algorithm has the same intuition we humans do: evaluate the nodes connected to the input, then the "aggregation" node, then the output. How should it decide whether to evaluate node [3] before node [4] or vice-versa?

By starting (t0) at [3], the value of [4] is 0. And vice-versa.

The side-effect of this behavior appears when comparing two networks. Say you have the network above, and a copy of it where [3] and [4] are inverted. I feel like both should return the same result for the same input in order for evolution to work properly.

Any thoughts?

$\endgroup$

1 Answer 1

2
$\begingroup$

I believe NEAT just loops through the nodes in the order they appear in a list.

Looking at the source (.cpp) and removing time features it looks like this in JS:

 do{
    // For each node, compute the sum of its incoming activation 
    for(let i=0;i<nodes.length;i++){
        if(nodes[i].type!=='input'){
            nodes[i].sum=0;
            nodes[i].active=false;
            for(let f=0;f<nodes[i].from.length;f++){
                let {index,weight}=nodes[i].from[f];
                if(nodes[index].activeCnt>0)nodes[i].sum+=weight*nodes[index].activeValue;
                if(nodes[index].active||nodes[index].type==='input')nodes[i].active=true;
            }
        }
    }
    // Now activate all the non-sensor nodes off their incoming activation 
    for(let i=0;i<nodes.length;i++){
        if(nodes[i].type!=='input'&&nodes[i].active){
            nodes[i].activeValue=sigmoid(nodes[i].sum);
            nodes[i].activeCnt++;
        }
    }
}while(!outputs.isActive());

On initialization the outer while loop might run >1 times. After this it will only run once on every time step.

From my understanding, on each time step NEAT does not "push each input value fully from input to output", only "pushes from one node to the next" except at intialization.

$\endgroup$
2
  • $\begingroup$ From the network.cpp(:170) file: Keep activating until all the outputs have become active. Your assumption is only right after the first activation. However, it's propagation strategy does seem to be topology agnostic. I wonder how it responds to larger/more complex networks. $\endgroup$
    – Hugo Aboud
    Commented Jan 5, 2022 at 4:02
  • $\begingroup$ what is the point of tracking activeCnt? $\endgroup$ Commented Jul 23, 2023 at 14:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .