Я пытаюсь реализовать NEAT (Neuro Evolution of Augmenting Topologies).
У меня есть список сетевых подключений, называемых «генами». Связь между нейроном1 и нейроном2 будет иметь вид ген.от = нейрон1, ген.к = нейрон2.
Моя задача — сгенерировать нейронную сеть из этих генов (нейронная сеть — это просто карта от индекса до нейрона, ген.от и ген.то — ключи к нейронам на карте).
У меня есть 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 выход
Это изображение этой наивно сгенерированной сети: Простая сеть
Как видим, в редуцированной сети вообще не должно быть ни 4, ни 5, так как они не влияют ни на какие выходы (в данном случае только на один выход, 13). Уменьшенная нейронная сеть будет просто 0-> 13 и 1-> 13.
У меня были некоторые первоначальные мысли о том, как решить эту проблему:
A. 1. Зациклить каждое соединение и хешировать ген из идентификаторов. Это идентификаторы нейронов, которые являются входными данными для чего-то еще. 2. После заполнения хэша повторите цикл и удалите все гены, в которых ген. хэш). 3. Повторяем пока ничего не удаляем
B. Создайте наивную сеть... затем проползите назад по сети, от каждого выхода, пока мы не сможем двигаться дальше (позаботьтесь о повторяющихся циклах). Хэшируйте каждый найденный узел. Как только наш поиск по графу будет завершен, сравните наш хэш найденных узлов с общим количеством узлов, выраженным в нашем списке генов. Используйте только гены с нейронами в хеше найденных узлов и переделайте сеть.
Я надеялся получить отзывы о том, какой алгоритм может быть лучшим для этого, основываясь на моем сетевом представлении — я думаю, что мой B лучше, чем A, но я надеялся, что есть более элегантное решение, в котором я не участвовал. разбор топологии графа. Возможно, я могу сделать что-то умное, отсортировав соединения (по кому, по откуда)?
Спасибо!