import math import random import sys # BEVector is the symbol used to "B"egin or "E"nd a sequence. BE_VECTOR = [1.0, 0.0, 0.0, 0.0, 0.0, 0.0] # 0 1 2 3 4 5 SAMPLE_INPUT = [[0.0, 0.0, 0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 1.0, 0.0, 0.0, 0.0]] NUM_TESTS = 10000 NUM_SAMPLES = 4 INPUT_NEURONS = 6 HIDDEN_NEURONS = 3 OUTPUT_NEURONS = 6 CONTEXT_NEURONS = 3 LEARNING_RATE = 0.2 # Rho. TRAINING_REPS = 2000 class Elman_Example: def __init__(self, numTests, numSamples, numInputs, numHidden, numOutputs, numContext, learningRate, epochs, beArray, sampleInput): self.mNumTests = numTests self.mNumSamples = numSamples self.mInputs = numInputs self.mHiddens = numHidden self.mOutputs = numOutputs self.mContexts = numContext self.mLearningRate = learningRate self.mEpochs = epochs self.mBEArray = beArray self.mSampleInput = sampleInput self.wih = [] # Input to Hidden Weights (with Biases). self.wch = [] # Context to Hidden Weight (with Biases). self.who = [] # Hidden to Output Weights (with Biases). self.whc = [] # Hidden to Context Weights (no Biases). # Activations. self.inputs = [] self.hidden = [] self.target = [] self.actual = [] self.context = [] # Unit errors. self.erro = [] self.errh = [] return def initialize_arrays(self): for i in range(self.mInputs + 1): # The extra element represents bias node. self.wih.append([0.0] * self.mHiddens) for j in range(self.mHiddens): # Assign a random weight value between -0.5 and 0.5 self.wih[i][j] = random.random() - 0.5 for i in range(self.mContexts + 1): self.wch.append([0.0] * self.mHiddens) for j in range(self.mHiddens): self.wch[i][j] = random.random() - 0.5 for i in range(self.mHiddens + 1): self.who.append([0.0] * self.mOutputs) for j in range(self.mOutputs): self.who[i][j] = random.random() - 0.5 for i in range(self.mOutputs + 1): self.whc.append([0.0] * self.mContexts) for j in range(self.mContexts): # These are all fixed weights set to 0.5 self.whc[i][j] = 0.5 self.inputs = [0.0] * self.mInputs self.hidden = [0.0] * self.mHiddens self.target = [0.0] * self.mOutputs self.actual = [0.0] * self.mOutputs self.context = [0.0] * self.mContexts self.erro = [0.0] * self.mOutputs self.errh = [0.0] * self.mHiddens return def sigmoid(self, value): return 1.0 / (1.0 + math.exp(-value)) def sigmoid_derivative(self, value): return value * (1.0 - value) def feed_forward(self): total = 0.0 # Calculate input and context connections to hidden layer. for j in range(self.mHiddens): total = 0.0 for i in range(self.mInputs): total += self.inputs[i] * self.wih[i][j] for i in range(self.mContexts): total += self.context[i] * self.wch[i][j] # Add in bias. total += self.wih[self.mInputs][j] total += self.wch[self.mContexts][j] self.hidden[j] = self.sigmoid(total) # Calculate the hidden to output layer. for j in range(self.mOutputs): total = 0.0 for i in range(self.mHiddens): total += self.hidden[i] * self.who[i][j] # Add in bias. total += self.who[self.mHiddens][j] self.actual[j] = self.sigmoid(total) # Copy outputs of the hidden to context layer. for i in range(self.mContexts): self.context[i] = self.hidden[i] return def back_propagate(self): # Calculate the output layer error (step 3 for output cell). for j in range(self.mOutputs): self.erro[j] = (self.target[j] - self.actual[j]) * self.sigmoid_derivative(self.actual[j]) # Calculate the hidden layer error (step 3 for hidden cell). for i in range(self.mHiddens): self.errh[i] = 0.0 for j in range(self.mOutputs): self.errh[i] += self.erro[j] * self.who[i][j] self.errh[i] *= self.sigmoid_derivative(self.hidden[i]) # Update the weights for the output layer (step 4). for j in range(self.mOutputs): for i in range(self.mHiddens): self.who[i][j] += (self.mLearningRate * self.erro[j] * self.hidden[i]) # Update the bias. self.who[self.mHiddens][j] += (self.mLearningRate * self.erro[j]) # Update the weights for the hidden layer (step 4). for j in range(self.mHiddens): for i in range(self.mInputs): self.wih[i][j] += (self.mLearningRate * self.errh[j] * self.inputs[i]) # Update the bias. self.wih[self.mInputs][j] += (self.mLearningRate * self.errh[j]) return def train_network(self): err = 0.0 sample = 0 iterations = 0 stopLoop = False while not stopLoop: if sample == 0: for i in range(self.mInputs): self.inputs[i] = self.mBEArray[i] else: for i in range(self.mInputs): self.inputs[i] = self.mSampleInput[sample - 1][i] # After the samples are entered into the input units, the sample are # then offset by one and entered into target-output units for # later comparison. if sample == self.mNumSamples - 1: for i in range(self.mInputs): self.target[i] = self.mBEArray[i] else: for i in range(self.mInputs): self.target[i] = self.mSampleInput[sample][i] self.feed_forward() err = 0.0 for i in range(self.mOutputs): err += math.sqrt(math.fabs(self.target[i] - self.actual[i])) err = 0.5 * err if iterations > self.mEpochs: stopLoop = True iterations += 1 self.back_propagate() sample += 1 if sample == self.mNumSamples: sample = 0 sys.stdout.write("Iterations = " + str(iterations) + "\n") return def test_network(self): index = 0 randomNumber = 0 predicted = 0 stopSample = False successful = False # Test the network with random input patterns. for j in range(self.mNumTests): # Enter Beginning string. self.inputs[0] = 1.0 self.inputs[1] = 0.0 self.inputs[2] = 0.0 self.inputs[3] = 0.0 self.inputs[4] = 0.0 self.inputs[5] = 0.0 sys.stdout.write("\n(0) ") self.feed_forward() stopSample = False successful = False index = 0 randomNumber = 0 predicted = 0 while stopSample == False: for i in range(self.mOutputs): sys.stdout.write("{:03.3f}".format(self.actual[i]) + " ") if self.actual[i] >= 0.3: # The output unit with the highest value (usually over 3.0) # is the network's predicted unit that it expects to appear # in the next input vector. # For example, if the 3rd output unit has the highest value, # the network expects the 3rd unit in the next input to # be 1.0 # If the actual value isn't what it expected, the random # sequence has failed, and a new test sequence begins. predicted = i sys.stdout.write("\n") index += 1 if index == self.mOutputs - 1: stopSample = True # Enter a random number between 0 and INPUT_NEURONS. randomNumber = random.randrange(0, self.mInputs) sys.stdout.write("(" + str(randomNumber) + ") ") for i in range(self.mInputs): if i == randomNumber: self.inputs[i] = 1.0 if i == predicted: successful = True else: # failure. Stop this sample and try a new sample. stopSample = True else: self.inputs[i] = 0.0 self.feed_forward() # then go back and enter another number into this sample sequence. if index > self.mOutputs - 2 and successful == True: # If the random sequence happens to be in the correct order, the network reports success. sys.stdout.write("Success.\n") sys.stdout.write("Completed " + str(j) + " tests.\n") break else: sys.stdout.write("Failed.\n") if j >= self.mNumTests: sys.stdout.write("Completed " + str(j) + " tests with no success.\n") break return if __name__ == '__main__': elman = Elman_Example(NUM_TESTS, NUM_SAMPLES, INPUT_NEURONS, HIDDEN_NEURONS, OUTPUT_NEURONS, CONTEXT_NEURONS, LEARNING_RATE, TRAINING_REPS, BE_VECTOR, SAMPLE_INPUT) elman.initialize_arrays() elman.train_network() elman.test_network()