Sunday, December 30, 2012

A Python Hopfield Network

Quick Background

Hopfield networks are general purpose neural networks that take a set of input values (boolean) and produce an output of same length. They are particularly useful in signal processing and cleaning up images where noise is an issue, (think smudged characters).

I imagine they would be particularly useful in normalizing inputs from different hardware (e.g. creating color profiles, removing static or echos from some microphones, etc.)

This Implementation

This implementation is a tanslation of the algorithm found in the Clever Algorithms book from Ruby to Python.
#!/usr/bin/env python3
# A translation of http://www.cleveralgorithms.com/nature-inspired/neural/hopfield_network.html
import random

def random_vector(minmax):
return [row[0] + ((row[1] - row[0]) * random.random()) for row in minmax]

def initialize_weights(problem_size):
return random_vector([[-0.5,0.5] for i in range(problem_size)])

def create_neuron(num_inputs):
return {'weights' : initialize_weights(num_inputs)}

def propagate_was_change(neurons):
i = random.randrange(len(neurons))
activation = 0

for j, other in enumerate(neurons):
activation += other['weights'][i] * other['output'] if i != j else 0

output = 1 if activation >= 0 else -1
change = output != neurons[i]['output']
neurons[i]['output'] = output
return change

def flatten(nested):
try:
return [item for sublist in nested for item in sublist]
except TypeError:
return nested

def get_output(neurons, pattern, evals=100):
vector = flatten(pattern)

for i, neuron in enumerate(neurons):
neuron['output'] = vector[i]

for j in range(evals):
propagate_was_change(neurons)

return [neuron['output'] for neuron in neurons]

def train_network(neurons, patters):
for i, neuron in enumerate(neurons):
for j in range((i+1), len(neurons)):
if i == j:
continue

wij = 0.0

for pattern in patters:
vector = flatten(pattern)
wij += vector[i] * vector[j]

neurons[i]['weights'][j] = wij
neurons[j]['weights'][i] = wij

def to_binary(vector):
return [0 if i == -1 else 1 for i in vector]

def print_patterns(provided, expected, actual):
p, e, a = to_binary(provided), to_binary(expected), to_binary(actual)
p1, p2, p3 = ', '.join(map(str, p[0:2])), ', '.join(map(str, p[3:5])), ', '.join(map(str, p[6:8]))
e1, e2, e3 = ', '.join(map(str, e[0:2])), ', '.join(map(str, e[3:5])), ', '.join(map(str, e[6:8]))
a1, a2, a3 = ', '.join(map(str, a[0:2])), ', '.join(map(str, a[3:5])), ', '.join(map(str, a[6:8]))
print( "Provided\tExpected\tGot")
print( "%s\t\t%s\t\t%s" % (p1, e1, a1))
print( "%s\t\t%s\t\t%s" % (p2, e2, a2))
print( "%s\t\t%s\t\t%s" % (p3, e3, a3))


def calculate_error(expected, actual):
return sum([1 for i in range(len(expected)) if expected[i] != actual[i]])

def perturb_pattern(vector, num_errors=1):
perturbed = [v for v in vector]
indicies = [0 for i in range(random.randrange(len(perturbed)))]

while len(indicies) < num_errors:
index = random.randrange(len(perturbed))
if not index in indicies:
indicies.append(index)

for i in indicies:
perturbed[i] = -1 if perturbed[i] == 1 else 1

return perturbed


def test_network(neurons, patterns):
error = 0.0

for pattern in patterns:
vector = flatten(pattern)
perturbed = perturb_pattern(vector)
output = get_output(neurons, perturbed)
error += calculate_error(vector, output)
print_patterns(perturbed, vector, output)

error = error / float(len(patterns))
print("Final Result: avg pattern error=%s" % (error))

return error

def execute(patters, num_inputs):
neurons = [create_neuron(num_inputs) for i in range(num_inputs)]
train_network(neurons, patters)
test_network(neurons, patters)
return neurons

if __name__ == "__main__":
# problem configuration
num_inputs = 9
p1 = [[1,1,1],[-1,1,-1],[-1,1,-1]] # T
p2 = [[1,-1,1],[1,-1,1],[1,1,1]] # U
patters = [p1, p2]
# execute the algorithm
execute(patters, num_inputs)

No comments:

Post a Comment