'My Genetic Neural Network doesn't improve
I am trying my hands at Neural Networks and Genetic Algorithm by following the Nature of Code series from CodingTrain (https://www.youtube.com/playlist?list=PLRqwX-V7Uu6aCibgK1PTWWu9by6XFdCfh and https://www.youtube.com/playlist?list=PLRqwX-V7Uu6Yd3975YwxrR0x40XGJ_KGO). However, I use Python and numpy to handle all the matrix computations.
I am trying to evolve the NN to resolve the XOR problem (for now). It was previously working, but since then I added functionalities to my NN and now it doesn't improve anymore, and I can't figure out why. So I was hopping that a new pair of eyes could help me see the issue.
Here's the code for the NN:
import random
import numpy
import numpy.matlib
class NeuralNetwork:
#--------------------------------------------------------- Constructor ---------------------------------------------------------
def __init__(self, input, hidden_list, output):
#Memorize Size of Neural Network
self.input = input
self.output = output
self.hidden = []
for hidden in hidden_list:
self.hidden.append(hidden)
#Generate weights
#Input weights
self.input_weights = numpy.matlib.rand(self.hidden[0], self.input)
self.input_weights = self.input_weights * 2 - 1
#Hidden weights
self.hidden_weights = []
for i in range(len(self.hidden) - 1):
hidden_weight = numpy.matlib.rand(self.hidden[i+1], self.hidden[i])
hidden_weight = hidden_weight * 2 - 1
self.hidden_weights.append(hidden_weight)
#Output weights
self.output_weights = numpy.matlib.rand(self.output, self.hidden[-1])
self.output_weights = self.output_weights *2 - 1
#Generate bias
#Hidden bias
self.hidden_bias = []
for i in range(len(self.hidden)):
hidden_bias = numpy.matlib.rand(self.hidden[i], 1)
hidden_bias = hidden_bias * 2 - 1
self.hidden_bias.append(hidden_bias)
#Output bias
self.output_bias = numpy.matlib.rand(self.output, 1)
self.output_bias = self.output_bias * 2 - 1
#--------------------------------------------------------- Neural Network ---------------------------------------------------------
#Activation Function
def activation(self, x):
return 1/(1 + numpy.exp(-x))
#return x
#Feed Forward and return result
def feedForward(self, inputs):
#Input
inputs_matrix = numpy.matrix(inputs).transpose()
inputs_result = numpy.dot(self.input_weights, inputs_matrix)
inputs_result = numpy.add(inputs_result, self.hidden_bias[0])
#inputs_result = self.activation(inputs_result)
inputs_result = numpy.vectorize(self.activation)(inputs_result)
#Hidden
hidden_result = inputs_result
for i in range(1, len(self.hidden)):
hidden_result = numpy.dot(self.hidden_weights[i-1], hidden_result)
hidden_result = numpy.add(hidden_result, self.hidden_bias[i])
#hidden_result = self.activation(hidden_result)
hidden_result = numpy.vectorize(self.activation)(hidden_result)
#Output
output_result = numpy.dot(self.output_weights, hidden_result)
output_result = numpy.add(output_result, self.output_bias)
#output_result = self.activation(output_result)
output_result = numpy.vectorize(self.activation)(output_result)
return output_result
#--------------------------------------------------------- Evolution ---------------------------------------------------------
def copy(self):
#Create a new Neural Network of same size
new_NN = NeuralNetwork(self.input, self.hidden, self.output)
#Copy weights
new_NN.input_weights = self.input_weights.copy()
new_NN.hidden_weights = self.hidden_weights.copy()
new_NN.output_weights = self.output_weights.copy()
#Copy bias
new_NN.hidden_bias = self.hidden_bias.copy()
new_NN.output_bias = self.output_bias.copy()
#Return new Neural Network
return new_NN
#Mutate the Neural Network
def mutate(self, mutation_rate, mutation_scale):
#Mutate weights
self.input_weights = numpy.vectorize(mutate_value)(self.input_weights, mutation_rate, mutation_scale)
for i in range(len(self.hidden_weights)):
self.hidden_weights[i] = numpy.vectorize(mutate_value)(self.hidden_weights[i], mutation_rate, mutation_scale)
self.output_weights = numpy.vectorize(mutate_value)(self.output_weights, mutation_rate, mutation_scale)
#Mutate bias
for i in range(len(self.hidden_weights)):
self.hidden_bias[i] = numpy.vectorize(mutate_value)(self.hidden_bias[i], mutation_rate, mutation_scale)
self.output_bias = numpy.vectorize(mutate_value)(self.output_bias, mutation_rate, mutation_scale)
#Reproduce the Neural Network
def reproduce(self, mutation_rate = 0.01, mutation_scale = 0.1):
#Copy this Neural Network
child = self.copy()
#Mutate new Neural Network
child.mutate(mutation_rate, mutation_scale)
#Return new Neural Network
return child
#Crossover the Neural Network with another Neural Network
def crossover(self, other, selection_rate = 0.5, mutation_rate = 0.01, mutation_scale = 0.1):
#Create a new Neural Network of same size
child = NeuralNetwork(self.input, self.hidden, self.output)
#Copy weights from one of both parents randomly
child.input_weights = numpy.vectorize(random_selection)(self.input_weights, other.input_weights, selection_rate)
for i in range(len(self.hidden_weights)):
child.hidden_weights[i] = numpy.vectorize(random_selection)(self.hidden_weights[i], other.hidden_weights[i], selection_rate)
child.output_weights = numpy.vectorize(random_selection)(self.output_weights, other.output_weights, selection_rate)
#Copy bias from one of both parents randomly
for i in range(len(self.hidden_weights)):
child.hidden_bias[i] = numpy.vectorize(random_selection)(self.hidden_bias[i], other.hidden_bias[i], selection_rate)
child.output_bias = numpy.vectorize(random_selection)(self.output_bias, other.output_bias, selection_rate)
#Mutate new Neural Network
child.mutate(mutation_rate, mutation_scale)
#Return new Neural Network
return child
#--------------------------------------------------------- Util ---------------------------------------------------------
#Randomly decide and mutate given value
def mutate_value(value, mutation_rate, mutation_scale):
#Randomly decide
if random.random() < mutation_rate:
#Randomly mutate
mutation = numpy.random.uniform(-mutation_scale, mutation_scale)
return value + mutation
else:
#Return original value
return value
#Randomly select which of the two given values will be returned
def random_selection(value, other_value, selection_rate):
#Randomly decide
if random.random() < selection_rate:
#Return original value
return value
else:
#Return other value
return other_value
And here's the code for the XOR problem:
import math
import random
import numpy
import numpy.matlib
from NeuralNetwork import NeuralNetwork
#--------------------------------------------------------- Create Population ---------------------------------------------------------
population_size = 2000
population = []
fitness = []
for i in range(population_size):
population.append(NeuralNetwork(2, [2], 1))
fitness.append(0)
#--------------------------------------------------------- Create Dataset ---------------------------------------------------------
datas = [[0, 0], [0, 1], [1, 0], [1, 1]]
answers = [0, 1, 1, 0]
for generation in range(100):
#Init Top 10 List
top = []
for i in range(10):
top.append([])
top[i].append(None)
top[i].append(-math.inf)
#--------------------------------------------------------- Evaluate Fitness ---------------------------------------------------------
for i in range(population_size):
nb_tests = 100
for j in range(nb_tests):
index = random.choice([0, 1, 2, 3])
result = population[i].feedForward(datas[index]).getA1()
fitness[i] += (1 - numpy.absolute(answers[index] - result[0]))
fitness[i] = (fitness[i] / nb_tests) * 100
#Get TOP 10
for j in range(len(top)):
if fitness[i] > top[j][1]:
top[j][0] = population[i]
top[j][1] = fitness[i]
break
print("---------------------------- Generation " + str(generation + 1) + " ----------------------------")
for entry in top:
print(entry[1])
#--------------------------------------------------------- Make new Population ---------------------------------------------------------
population = []
fitness = []
#For each TOP 10
for i in range(len(top)):
#Add itself
population.append(top[i][0].copy())
fitness.append(0)
#Add 10 mutated version of itself
for j in range(100):
population.append(top[i][0].reproduce())
fitness.append(0)
#For each other TOP 10
for j in range(i+1, len(top)):
#Add 20 Crossovers
for k in range(20):
population.append(top[i][0].crossover(top[j][0]))
fitness.append(0)
#Fill remaining with new Neural Networks
for i in range(population_size - len(population)):
population.append(NeuralNetwork(2, [2], 1))
fitness.append(0)
population_size = len(population)
print(top[0].feedForward(datas[0]))
print(top[0].feedForward(datas[1]))
print(top[0].feedForward(datas[2]))
print(top[0].feedForward(datas[3]))
I don't know why, but I previously had the TOP 10 NN attain 100% fitness in 15 generations or so. Now, even after 100 generation, it doesn't go higher than 60%...
Please overlook any malpractice and/or Python aberrations, I am learning the language on the go while developing this project.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
