我正在尝试实施NEAT(增强拓扑的神经演化)。
我有一个网络连接列表,称为“基因”。 neuron1和neuron2之间的联系将是gene.from = neuron1,gene.to = neuron2。
我的任务是从这些基因生成神经网络(神经网络只是从索引到神经元的映射,gene.from和gene.to是映射中神经元的关键)。
我有numPossibleInputs
个输入节点,所以我们先添加它们(0-numPossibleInputs-1是输入神经元)。
我有numOutputs
个输出节点,所以我们也添加了这些节点。
然后,我们根据他们的“到”连接索引对基因进行排序。
最后,我们基于基因创建隐藏层神经元......由于神经网络是一个地图,我们只是检查连接的来源是否已经是神经元,否则创建一个新神经元。这种算法可以很好地创建网络。
public void generateNetwork()
{
neuralNetwork.clear();
for(int i = 0; i < numPossibleInputs; i++)
{
neuralNetwork.put(i, new Neuron());
}
for(int i = 0; i < numOutputs; i++)
{
neuralNetwork.put(i+numPossibleInputs+numPossibleHidden, new Neuron());
}
genes.sort((ConnectionGene g1, ConnectionGene g2)-> Integer.compare(g1.toNeuronIndex, g2.toNeuronIndex));
for(ConnectionGene gene : getCleanGenes(genes))
{
if(gene.enabled)
{
if(!neuralNetwork.containsKey(gene.toNeuronIndex))
{
neuralNetwork.put(gene.toNeuronIndex, new Neuron());
}
neuralNetwork.get(gene.toNeuronIndex).incomingConnections.add(gene); // Add this gene to the incoming of the above neuron
if(!neuralNetwork.containsKey(gene.fromNeuronIndex))
{
neuralNetwork.put(gene.fromNeuronIndex, new Neuron());
}
}
}
}
问题出现在进化算法关闭某些基因时(注意gene.enabled
)。例如,考虑以下基因(还有其他基因,但它们被禁用):
2→4
4-→4
13-→4
0→13
1→13
5→13
我们还有残基基因,2-> 5和4-> 13。这些不能在网络中使用,因为它们没有被表达。 (这就是为什么我必须每代都生成一个新网络,可以添加,启用,禁用基因等)。
这是numPossibleInputs ==3
,因此0 1和2是输入(2是偏差)。图5是从5开始的隐藏层节点。 3,但小于10 + 3 = 13. 13是输出节点,我有numPossibleHidden == 10
所以10 + 3 = 13 ......只有一个输出。
可以这样画:
[输入输入隐藏* 10输出* 1]用于3个输入,10个隐藏和1个输出
这是天真生成的网络图片: Simple Network
正如我们所看到的,简化网络不应该有4或5,因为它们对任何输出都没有影响(在这种情况下只有一个输出,13)。减少的神经网络将仅为0-> 13且1-> 13。
我对如何解决这个问题有一些初步想法:
一个。 1.遍历每个连接并散列gene.from ids。这些是神经元id,是其他东西的输入 2.填充哈希后,再次循环并删除任何带有gene.to不在哈希中的基因(如果不在哈希中,则gene.to不是其他任何内容的输入)。 3.重复,直到我们不删除任何东西
B中。生成天真的网络...然后,从网络中向后爬行,从每个输出直到我们不能再继续(注意重复循环)。哈希我们找到的每个节点。完成图搜索后,将找到的节点哈希值与基因列表中表示的总节点进行比较。只在发现的节点的散列中使用带有神经元的基因并重新制作网络。
我希望根据我的网络表示得到一些关于可能是最好的算法的反馈 - 我认为我的B比A更好,但我希望有一个更优雅的解决方案没有'我涉及解析图形拓扑。也许通过对连接进行排序(By to,by from)可以做些聪明的事情?
谢谢!
答案 0 :(得分:0)
我使用上面的B解决方案,使用各种不同的网络类型对其进行测试,并且工作正常 - 也就是说,网络将摆脱从输入到输出没有正确路径的所有节点。我会在这里发布代码,以防有人想要使用它:
private List<ConnectionGene> cleanGenes(Map<Integer,Neuron> network)
{
// For each output, go backwards
Set<Integer> visited = new HashSet();
for(int i = 0; i < numOutputs; i++)
{
visited.add(i+numPossibleInputs+numPossibleHidden);
cleanGenes(i+numPossibleInputs+numPossibleHidden, network, visited);
}
List<ConnectionGene> slimGenes = new ArrayList();
for(ConnectionGene gene : genes)
{
// Only keep gene if from/to are nodes we visited
if(visited.contains(gene.fromNeuronIndex) && visited.contains(gene.toNeuronIndex))
{
slimGenes.add(gene);
}
}
return slimGenes;
}
private boolean cleanGenes(int neuronIndex, Map<Integer, Neuron> network, Set<Integer> visited)
{
int numGoodConnections = 0;
for(ConnectionGene gene : network.get(neuronIndex).incomingConnections)
{
numGoodConnections++;
if(gene.enabled && !visited.contains(gene.fromNeuronIndex))
{
visited.add(gene.fromNeuronIndex);
if(!cleanGenes(gene.fromNeuronIndex, network, visited))
{
numGoodConnections--;
visited.remove(gene.fromNeuronIndex); // We don't want this node in visited, it has no incoming connections and isn't an input.
}
}
}
if(numGoodConnections == 0)
{
return neuronIndex < numPossibleInputs; // True if an input neuron, false if no incoming connections and not input
}
return true; // Success
}
根据我的分析器,这个NEAT算法花费的绝大部分时间都在模拟本身。也就是说,与针对艰巨任务测试网络相比,生成适当的网络是微不足道的。
答案 1 :(得分:0)
有一种更有效的方法来添加神经元。您可以采用随机连接,将其分成两个连接并在它们之间添加神经元,而不是仅仅添加一个新神经元并希望它有一天连接。