Created
December 13, 2014 19:34
-
-
Save bart6114/5d908ae2a7290bede3d2 to your computer and use it in GitHub Desktop.
adjusted bpnn.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Back-Propagation Neural Networks | |
# | |
# Written in Python. See http://www.python.org/ | |
# Placed in the public domain. | |
# Neil Schemenauer <[email protected]> | |
# Adapted for instructional purposes by Bart Smeets <[email protected]> | |
import math | |
import random | |
import string | |
random.seed(0) | |
# calculate a random number where: a <= rand < b | |
def rand(a, b): | |
return (b-a)*random.random() + a | |
# Make a matrix (we could use NumPy to speed this up) | |
def makeMatrix(I, J, fill=0.0): | |
m = [] | |
for i in range(I): | |
m.append([fill]*J) | |
return m | |
# our sigmoid function, tanh is a little nicer than the standard 1/(1+e^-x) | |
def sigmoid(x): | |
return math.tanh(x) | |
# derivative of our sigmoid function, in terms of the output (i.e. y) | |
def dsigmoid(y): | |
return 1.0 - y**2 | |
class NN: | |
def __init__(self, ni, nh, no): | |
# number of input, hidden, and output nodes | |
self.ni = ni + 1 # +1 for bias node | |
self.nh = nh | |
self.no = no | |
# activations for nodes | |
self.ai = [1.0]*self.ni | |
self.ah = [1.0]*self.nh | |
self.ao = [1.0]*self.no | |
self.h_weights = [1.0]*self.nh | |
self.training_errors = [] | |
# create weights | |
self.wi = makeMatrix(self.ni, self.nh) | |
self.wo = makeMatrix(self.nh, self.no) | |
# set them to random vaules | |
for i in range(self.ni): | |
for j in range(self.nh): | |
self.wi[i][j] = rand(-0.2, 0.2) | |
for j in range(self.nh): | |
for k in range(self.no): | |
self.wo[j][k] = rand(-2.0, 2.0) | |
# last change in weights for momentum | |
self.ci = makeMatrix(self.ni, self.nh) | |
self.co = makeMatrix(self.nh, self.no) | |
def update(self, inputs): | |
if len(inputs) != self.ni-1: | |
raise ValueError('wrong number of inputs') | |
# input activations | |
for i in range(self.ni-1): | |
#self.ai[i] = sigmoid(inputs[i]) | |
self.ai[i] = inputs[i] | |
# hidden activations | |
for j in range(self.nh): | |
sum = 0.0 | |
for i in range(self.ni): | |
sum = sum + self.ai[i] * self.wi[i][j] | |
self.ah[j] = sigmoid(sum) * self.h_weights[j] | |
# output activations | |
for k in range(self.no): | |
sum = 0.0 | |
for j in range(self.nh): | |
sum = sum + self.ah[j] * self.wo[j][k] | |
self.ao[k] = sigmoid(sum) | |
return self.ao[:] | |
def backPropagate(self, targets, N, M): | |
if len(targets) != self.no: | |
raise ValueError('wrong number of target values') | |
# calculate error terms for output | |
output_deltas = [0.0] * self.no | |
for k in range(self.no): | |
error = targets[k]-self.ao[k] | |
output_deltas[k] = dsigmoid(self.ao[k]) * error | |
# calculate error terms for hidden | |
hidden_deltas = [0.0] * self.nh | |
for j in range(self.nh): | |
error = 0.0 | |
for k in range(self.no): | |
error = error + output_deltas[k]*self.wo[j][k] | |
hidden_deltas[j] = dsigmoid(self.ah[j]) * error | |
# update output weights | |
for j in range(self.nh): | |
for k in range(self.no): | |
change = output_deltas[k]*self.ah[j] | |
self.wo[j][k] = self.wo[j][k] + N*change + M*self.co[j][k] | |
self.co[j][k] = change | |
#print N*change, M*self.co[j][k] | |
# update input weights | |
for i in range(self.ni): | |
for j in range(self.nh): | |
change = hidden_deltas[j]*self.ai[i] | |
self.wi[i][j] = self.wi[i][j] + N*change + M*self.ci[i][j] | |
self.ci[i][j] = change | |
# calculate error | |
error = 0.0 | |
for k in range(len(targets)): | |
error = error + 0.5*(targets[k]-self.ao[k])**2 | |
return error | |
def test(self, patterns): | |
for p in patterns: | |
print(p[0], '->', self.update(p[0])) | |
def weights(self): | |
print('Input weights:') | |
for i in range(self.ni): | |
print(self.wi[i]) | |
print() | |
print('Output weights:') | |
for j in range(self.nh): | |
print(self.wo[j]) | |
def train(self, patterns, iterations=1000, N=0.5, M=0.1): | |
# N: learning rate | |
# M: momentum factor | |
for i in range(iterations): | |
error = 0.0 | |
for p in patterns: | |
inputs = p[0] | |
targets = p[1] | |
self.update(inputs) | |
error = error + self.backPropagate(targets, N, M) | |
self.training_errors.append(error) | |
if i % 100 == 0: | |
print('error %-.5f' % error) | |
def demo(): | |
# Teach network XOR function | |
pat = [ | |
[[0,0], [0]], | |
[[0,1], [1]], | |
[[1,0], [1]], | |
[[1,1], [0]] | |
] | |
# create a network with two input, two hidden, and one output nodes | |
n = NN(2, 4, 1) | |
# train it with some patterns | |
print("Training...") | |
n.train(pat, iterations = 1000) | |
print("Killing hnodes 1...") | |
n.h_weights[0]=0 | |
print("Training...") | |
n.train(pat, iterations = 1000) | |
print("Killing hnodes 1 & 2...") | |
n.h_weights[0]=0 | |
n.h_weights[1]=0 | |
print("Training...") | |
n.train(pat, iterations = 1000) | |
print("Killing hnodes 1 & 2 & 3...") | |
n.h_weights[0]=0 | |
n.h_weights[1]=0 | |
n.h_weights[2]=0 | |
print("Training...") | |
n.train(pat, iterations = 1000) | |
print("Killing hnodes 1 & 2 & 3 & 4...") | |
n.h_weights[0]=0 | |
n.h_weights[1]=0 | |
n.h_weights[2]=0 | |
n.h_weights[3]=0 | |
print("Training...") | |
n.train(pat, iterations = 1000) | |
# test it | |
print("Testing...") | |
n.test(pat) | |
with open( 'error_results.txt', 'w' ) as f: | |
for v in n.training_errors: | |
f.write(str(v) + '\n') | |
if __name__ == '__main__': | |
demo() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment