#!/usr/bin/python import os import whrandom import string from math import exp #============================================================================== class SymbolicInput: def __init__(self, brain, range=[]): self.neurons_by_symbol = {} # this is a hashtable! self.brain = brain for symbol in range: self.neuron_for_symbol(symbol) def stimulate(self, symbol): neuron = self.neuron_for_symbol(symbol) # send a signal to the neuron for this symbol neuron.signal(1.0) for neuron in self.neurons: neuron.activate() # TODO: send 0.0 to all other neurons? not necessary at the moment def neuron_for_symbol(self, symbol): # if we have seen this symbol before, get the neuron for it try: neuron = self.neurons_by_symbol[symbol] # otherwise, create a new neuron, and attach it to the first layer except KeyError: neuron = self.neurons_by_symbol[symbol] = Neuron() for first_layer_neuron in self.brain.layers[0]: Nerve(neuron, first_layer_neuron) return neuron #============================================================================== class NumericInput: def __init__(self, brain): self.min = None self.max = None # create new neuron for this input, and connect it into the first layer self.neuron = Neuron() for neuron in brain.layers[0]: Nerve(self.neuron, neuron) def stimulate(self, number): # adjust behaviour for unprecedented input if self.min == None or number < self.min: self.min = number if self.max == None or number > self.max: self.max = number # normalise if self.min == self.max: normalised = 0.5 else: normalised = (number - self.min)/(self.max - self.min) # send the signal self.neuron.signal(normalised) self.neuron.activate() #============================================================================== class SymbolicOutput: def __init__(self, brain, range=[]): self.neurons_by_symbol = {} # this is a hashtable! self.brain = brain for symbol in range: self.neuron_for_symbol(symbol) def get_result(self): symbols = self.neurons_by_symbol.keys() if not symbols: return None activation = {} for symbol in symbols: neuron = self.neurons_by_symbol[symbol] neuron.activate() activation[symbol] = neuron.activation best_symbol = symbols[0] max_activation = activation[best_symbol] for symbol in symbols[1:]: if activation[symbol] > max_activation: best_symbol = symbol max_activation = activation[symbol] return best_symbol def neuron_for_symbol(self, symbol): # if we have seen this symbol before, get the neuron for it try: neuron = self.neurons_by_symbol[symbol] # otherwise, create a new neuron, and attach it to the last layer except KeyError: neuron = self.neurons_by_symbol[symbol] = Neuron() for last_layer_neuron in self.brain.layers[-1]: Nerve(last_layer_neuron, neuron) return neuron #============================================================================== class NumericOutput: def __init__(self, brain, min=None, max=None): self.min = min self.max = max # create new neuron for this input, and connect it into the last layer self.neuron = Neuron() for neuron in brain.layers[-1]: Nerve(neuron, self.neuron) def get_result(self): self.neuron.activate() activation = self.neuron.activation result = activation * (self.max - self.min) + self.min return result def adjust_range(self, number): if self.min == None or number < self.min: self.min = number if self.max == None or number > self.max: self.max = number #============================================================================== """ Neuron - a neuron should die when it has no dendrytes """ class Neuron: # dendrytes # synapses # threshold # input_sum # activation # error_sum def __init__(self): self.threshold = whrandom.uniform(-5, 5) self.dendrytes = [] self.synapses = [] self.input_sum = self.error_sum = 0 def add_dendryte(self, dendryte): self.dendrytes.append(dendryte) def add_synapse(self, synapse): self.synapses.append(synapse) # forward propogation def add_input(self, input): self.input_sum = self.input_sum + input def fire(self): # calculate activation sum = self.input_sum + self.threshold self.activation = 1 / (1 + exp( - sum )) # fire signals to all synapses for synapse in self.synapses: synapse.transmit_forward(self.activation) # reset sum for next time self.input_sum = 0 # back propogation def add_error(self, error): self.error_sum = self.error_sum + error def back_propogate(self, learning_rate): # print self.error_sum # calculate error activation_gradient = self.activation * (1 - self.activation) error = self.error_sum * activation_gradient # adjust threshold self.threshold = self.threshold + error * learning_rate # send error back through all dendrytes for dendryte in self.dendrytes: dendryte.transmit_backward(error, learning_rate) # reset sum for next time self.error_sum = 0 #============================================================================== class Nerve: # input_neuron # output_neuron # weight # delta_weight def __init__(self, input_neuron, output_neuron): self.weight = whrandom.uniform(-5, 5) self.input_neuron = input_neuron self.output_neuron = output_neuron input_neuron.add_synapse(self) output_neuron.add_dendryte(self) def transmit_forward(self, signal): self.output_neuron.add_input(signal * self.weight) def transmit_backward(self, error, learning_rate): delta_weight = learning_rate * self.input_neuron.activation * error self.input_neuron.add_error(error * self.weight) self.weight = self.weight + delta_weight #============================================================================== """ Brain class - creates the neural network - process a record and produce outputs """ class Brain: def __init__(self, width, height): # make rectangular brain, internal layers! self.layers = [] for layer_index in range(0, width): layer = []; for neuron_index in range(0, height): layer.append(Neuron()) self.layers.append(layer) # make the nerves for layer_index in range(0, width-1): layer_1 = self.layers[layer_index] layer_2 = self.layers[layer_index+1] for neuron_1 in layer_1: for neuron_2 in layer_2: Nerve(neuron_1, neuron_2) def process(self): for layer in self.layers: for neuron in layer: neuron.fire() def learn(self, learning_rate): for i in range(len(self.layers)-1, -1, -1): # how to go backwards?!! layer = self.layers[i] for neuron in layer: neuron.back_propogate(learning_rate) #============================================================================== class DataTable: def __init__(self, dfn_filename=None, data_filename=None): self.field_names = [] self.field_types = [] self.records = [] if data_filename: self.read_from_file(dfn_filename, data_filename) def read_from_file(self, dfn_filename, data_filename): # read in definition of fields dfn_file = open(dfn_filename, "r") while 1: line = dfn_file.readline() if not line: break [type, name] = string.split(line) self.field_names.append(name) self.field_types.append(type) # read in records data_file = open(data_filename, "r") while 1: line = data_file.readline() if not line: break record = string.split(line) self.records.append(record) #============================================================================== def main(): # os.chdir("/0_Python_Work/NeuralNet") # filename = "animal" # d = DataTable("data/%s.dfn" % filename, "data/%s.dat" % filename) # for i in range(0, len(d.field_names)): # print d.field_names[i] + " " + d.field_types[i] # print # for r in d.records: # print r brain = Brain(width=4, height=6) input_layer = brain.layers[0] output_layer = brain.layers[-1] learning_rate = 0.05 for i in range(0, 1000000): input = [] t = 0 for i in range(0, 4): v = whrandom.uniform(0.0, 0.2) t = t + v input.append(v) expected_output = [t, t, t, t] for i in range(0, 4): input_layer[i].add_input(input[i]) brain.process() output = [] for i in range(0,4): output.append(output_layer[i].activation) print "input", input print "output", output print "expect", expected_output # do the back-propogation for i in range(0,4): #for (int i=0; i<4; ++i) error = expected_output[i] - output[i] print error, output_layer[i].add_error(error) print brain.learn(learning_rate) ## # create the brain ## brain = Brain(width=4, height=8) ## # create the inputs ## species = SymbolicInput(brain, ['frog', 'fish', 'camel']) ## age = NumericInput(brain, min=0, max=100) ## # create the outputs ## mass = NumericOutput(brain, min=0, max=500) ## # test the network ## species.stimulate('camel') ## age.stimulate(15) ## brain.activate() ## print mass.get_result() ## ## mass.back_propogate(450) ## brain.back_propogate() #============================================================================== #if __name__ == "__main__": main()