I was trying to make a neural network with 40 middle neurons.I want to initial Neuron class 40 times by loop and append each iteration to a list and then pass it to final node.but then I've got that when I pass a list like [neuron1,neuron2, ...] it works without any problem but when I pass a list that I've appended it in loop it throws RecursionError: maximum recursion depth exceeded. here is my network initial code:
W1 = Weight('w1', random_weight())
W2 = Weight('w2', random_weight())
neurons = [None] * 2
A = Neuron('A', [i0, i1], [ W1, W2])
neurons[0] = A
B = Neuron('B', [i0, i1], [W1, W2])
neurons[1] = B
out = Neuron('out', [A,B], [W1, W2])
this one works good. but below code has problem !
W1 = Weight('w1', random_weight())
W2 = Weight('w2', random_weight())
neurons = [None] * 2
A = Neuron('A', [i0, i1], [ W1, W2])
neurons[0] = A
B = Neuron('B', [i0, i1], [W1, W2])
neurons[1] = B
out = Neuron('out', neurons, [W1, W2])
here is my Neuron class implementation.
class Neuron(DifferentiableElement):
def __init__(self, name, inputs, input_weights, use_cache=True):
assert len(inputs)==len(input_weights)
for i in range(len(inputs)):
assert isinstance(inputs[i],(Neuron,Input))
assert isinstance(input_weights[i],Weight)
DifferentiableElement.__init__(self)
self.my_name = name
self.my_inputs = inputs # list of Neuron or Input instances
self.my_weights = input_weights # list of Weight instances
self.use_cache = use_cache
self.clear_cache()
self.my_descendant_weights = None
self.my_direct_weights = None
def output(self):
if self.use_cache:
if self.my_output is None:
self.my_output = self.compute_output()
return self.my_output
return self.compute_output()
def compute_output(self):
output = 0
inputs = self.get_inputs()
weights = self.get_weights()
for i in range(len(inputs)):
output += inputs[i].output() * weights[i].get_value()
output = 1 / (1 + math.exp(-1 * output))
return output
You call the output function every time you execute the compute_output function and so on. That's leading to a RecursionError: maximum recursion depth exceeded, becuase there is only a limited time a function can call another function at one direct call.
The exact spot in compute_output is:
def compute_output(self):
…
for i in range(len(inputs)):
output += inputs[i].output() * weights[i].get_value()
you call inputs[i].output() at this point
PS:
Difference between +/ += and append() is mentioned Here
Related
I want to take the shape of Input data which is passed to Input layer with (None,) shape, and use it in a for loop for some purpose.
Here's part of my code implementation:
lst_rfrm = []
Inpt_lyr = keras.Input(shape = (None,))
for k in range(tm_stp):
F = keras.layers.Lambda(lambda x, i, j: x[:, None, j : j + i])
F.arguments = {'i' : sub_len, 'j' : k}
tmp_rfrm = F(Inpt_lyr)
lst_rfrm.append(tmp_rfrm)
cnctnt_lyr = keras.layers.merge.Concatenate(axis = 1)(lst_rfrm)
#defining other layers ...
because the Input shape is (None,), I don't know what to give to for loop as range( at the code i describe it with 'tm_stp'). how can i get the shape of the input layer (the data that is passed to input layer) in this situation?
any help is deeply appreciated
You can try a different type of loop. It seems you are trying sliding windows, right?
You don't know the "length" to run, but you know the window size and how much of the borders to remove... so....
This function gets the slices following that principle:
windowSize = sub_len
def getWindows(x):
borderCut = windowSize - 1 #lost length in the length dimension
leftCut = range(windowSize) #start of sequence
rightCut = [i - borderCut for i in leftCut] #end of sequence - negative
rightCut[-1] = None #because it can't be zero for slicing
croppedSequences = K.stack([x[:, l: r] for l,r in zip(leftCut, rightCut)], axis=-1)
return croppedSequences
Running test:
from keras.layers import *
from keras.models import Model
import keras.backend as K
import numpy as np
windowSize = 3
batchSize = 5
randomLength = np.random.randint(5,10)
inputData = np.arange(randomLength * batchSize).reshape((batchSize, randomLength))
def getWindows(x):
borderCut = windowSize - 1
leftCut = range(windowSize)
rightCut = [i - borderCut for i in leftCut]
rightCut[-1] = None
croppedSequences = K.stack([x[:, l: r] for l,r in zip(leftCut, rightCut)], axis=-1)
return croppedSequences
inputs = Input((None,))
outputs = Lambda(getWindows)(inputs)
model = Model(inputs, outputs)
preds = model.predict(inputData)
for i, (inData, pred) in enumerate(zip(inputData, preds)):
print('sample: ', i)
print('input sequence: ', inData)
print('output sequence: \n', pred, '\n\n')
As part of a larger project, I'm trying to build a neural network that is built using two independent training sets against one input training series. In other words, I have a single input array that I want to build two independent synapses to be trained against.
In simplest terms, it looks something like this:
x = [[0,0,0],[0,1,1],[0,1,0]...] y = [[0,1],[0,0],[1,0]...] z =
[[0,0,0,0,1],[1,0,0,0,0],[1,1,0,1,1]...]
where y and z would be trained as independent synapses against trainset x.
I've been unable to modify my code to get this to work and I know it can't be super complex but I have been stuck for over a day and need some help.
Below is the code that I can't seem to get to work using multiple dimensions.
# Import Necessary Modules for Function
import numpy as np
import time
# Computer a non-linear Sigmoid curve (__--)
def sigmoid(x):
sigmoidOutput = 1/(1+np.exp(-x))
return sigmoidOutput
# Convert the output of the signmoid function to its derivative
def sigmoidDerivative(sigmoidOutput):
return sigmoidOutput*(1-sigmoidOutput)
def cleanSentence(sentence):
# Tokenize the words within the input sentence
sentenceWords = nltk.word_tokenize(sentence)
# Stem the words within the tokenized setence
sentenceWords = [stemmer.stem(userInput.lower()) for userInput in sentenceWords]
return sentenceWords
# Return a binary bag of words [0 or 1] to evaluate whether or not a word exists within
# a sentence.
def bagWordCheck(sentence, userInput, show_details=False):
# Tokenize the sentence
sentenceWords = cleanSentence(sentence)
# Create a word bag using the training-data from the user-input
wordBag = [0]*len(userInput)
for sWord in sentenceWords:
for i,w in enumerate(userInput):
if w == sWord:
wordBag[i] = 1
if show_details:
print ("found in bag: %s" % w)
return(np.array(wordBag))
# Evaluate the user's input
def think(sentence, showDetails=False):
x = bagWordCheck(sentence.lower(), userInput, showDetails)
if showDetails:
print ("sentence:", sentence, "\n bagWordCheck:", x)
# input layer is our bag of words
l0 = x
# matrix multiplication of input and hidden layer
l1 = sigmoid(np.dot(l0, synapse0))
# output layer
l2 = sigmoid(np.dot(l1, synapse2))
return l2
# ANN and Gradient Descent code from https://iamtrask.github.io//2015/07/27/python-network-part2/
def train(X, y, hidden_neurons=10, alpha=1, epochs=50000, dropout=False, dropout_percent=0.5):
print ("Training with %s neurons, alpha:%s, dropout:%s %s" % (hidden_neurons, str(alpha), dropout, dropout_percent if dropout else '') )
print ("Input matrix: %sx%s Output matrix: %sx%s" % (len(X),len(X[0]),1, len(conversationTypes)) )
np.random.seed(1)
lastMeanError = 1
# randomly initialize our weights with mean 0
synapse0 = 2*np.random.random((len(X[0]), hidden_neurons)) - 1
synapse2 = 2*np.random.random((hidden_neurons, len(conversations))) - 1
#synapse2 = 2*np.random.random((hidden_neurons, len(conversationTypes))) - 1
#synapse2 = 2*np.random.random((hidden_neurons, len(conversationSubjects))) - 1
prev_synapse0_weight_update = np.zeros_like(synapse0)
prev_synapse2_weight_update = np.zeros_like(synapse2)
synapse0_direction_count = np.zeros_like(synapse0)
synapse2_direction_count = np.zeros_like(synapse2)
for j in iter(range(epochs+1)):
# Feed forward through layers 0, 1, and 2
layer_0 = X
layer_1 = sigmoid(np.dot(layer_0, synapse0))
if(dropout):
layer_1 *= np.random.binomial([np.ones((len(X),hidden_neurons))],1-dropout_percent)[0] * (1.0/(1-dropout_percent))
layer_2 = sigmoid(np.dot(layer_1, synapse2))
# how much did we miss the target value?
layer_2_error = y - layer_2
if (j% 10000) == 0 and j > 5000:
# if this 10k iteration's error is greater than the last iteration, break out
if np.mean(np.abs(layer_2_error)) < lastMeanError:
print ("delta after "+str(j)+" iterations:" + str(np.mean(np.abs(layer_2_error))) )
lastMeanError = np.mean(np.abs(layer_2_error))
else:
print ("break:", np.mean(np.abs(layer_2_error)), ">", lastMeanError )
break
# in what direction is the target value?
# were we really sure? if so, don't change too much.
layer_2_delta = layer_2_error * sigmoidDerivative(layer_2)
# how much did each l1 value contribute to the l2 error (according to the weights)?
layer_1_error = layer_2_delta.dot(synapse2.T)
# in what direction is the target l1?
# were we really sure? if so, don't change too much.
layer_1_delta = layer_1_error * sigmoidDerivative(layer_1)
synapse2_weight_update = (layer_1.T.dot(layer_2_delta))
synapse0_weight_update = (layer_0.T.dot(layer_1_delta))
if(j > 0):
synapse0_direction_count += np.abs(((synapse0_weight_update > 0)+0) - ((prev_synapse0_weight_update > 0) + 0))
synapse2_direction_count += np.abs(((synapse2_weight_update > 0)+0) - ((prev_synapse2_weight_update > 0) + 0))
synapse2 += alpha * synapse2_weight_update
synapse0 += alpha * synapse0_weight_update
prev_synapse0_weight_update = synapse0_weight_update
prev_synapse2_weight_update = synapse2_weight_update
now = datetime.datetime.now()
# persist synapses
synapse = {'synapse0': synapse0.tolist(), 'synapse2': synapse2.tolist(),
'datetime': now.strftime("%Y-%m-%d %H:%M"),
'userInput': userInput,
'conversations' : conversations,
'conversationTypes': conversationTypes,
'conversationSubjects' : conversationSubjects
}
synapse_file = "synapses.json"
with open(synapse_file, 'w') as outfile:
json.dump(synapse, outfile, indent=4, sort_keys=True)
print ("saved synapses to:", synapse_file)
X = np.array(trainingSet)
y = np.array(completeConversations)
y1 = np.array(completeTypes)
y2 = np.array(completeSubjects)
start_time = time.time()
train(X, y, hidden_neurons=5, alpha=0.1, epochs=30000, dropout=False, dropout_percent=0.2)
#train(X, y1, hidden_neurons=5, alpha=0.1, epochs=30000, dropout=False, dropout_percent=0.2)
#train(X, y2, hidden_neurons=5, alpha=0.1, epochs=30000, dropout=False, dropout_percent=0.2)
elapsed_time = time.time() - start_time
print ("processing time:", elapsed_time, "seconds")
# probability threshold
ERROR_THRESHOLD = 0.75
# load our calculated synapse values
synapse_file = 'synapses.json'
with open(synapse_file) as data_file:
synapse = json.load(data_file)
synapse0 = np.asarray(synapse['synapse0'])
synapse2 = np.asarray(synapse['synapse2'])
def classify(sentence, showDetails=False):
results = think(sentence, showDetails)
results = [[i,r] for i,r in enumerate(results) if r>ERROR_THRESHOLD ]
results.sort(key=lambda x: x[1], reverse=True)
return_results =[[conversations[r[0]],r[1]] for r in results]
#return_results =[[conversationSubjects[r[0]],r[1]] for r in results]
#return_results =[[conversationTypes[r[0]],r[1]] for r in results]
return return_results
classify("charlotte explorer is not letting me ")
Please help!
I'm working around some neural network code. I've wrote my own neuron class, that could be find here. Now, I'm writing the Brain Class, that should sumarize most of code used in a NN. In this class, self.Real_Outputs collects all the outputs and put it into a list to be used after.
I'm boiling my brain to find out why when I add an element in self.Real_Outputs the whole list receive this value. I find here in this topic a discussion similar to mine, but, in my case i've already used 'self' statement. Could you guys help me on that?
class Brain:
def __init__(self, training_set, desired_outputs, bias, learning_tax):
self.Training_Set = training_set
self.Desired_Outputs = desired_outputs
self.Bias = bias
self.Learning_Tax = learning_tax
self.Hidden_Layer = []
self.Hidden_Layer_Outputs = []
self.Hidden_Layer_Errors = []
self.Output_Layer = []
self.Output_Layer_Outputs = []
self.Output_Layer_Errors = []
self.Real_Outputs = [0 for x in self.Desired_Outputs]
def set_hidden_layers(self, number_of_layers, number_of_neurons, activation_function):
self.Hidden_Layer = [[Neuron.Neuron(len(self.Training_Set[0]), activation_function, 1, self.Bias)
for x in range(number_of_neurons)]
for y in range(number_of_layers)]
self.Hidden_Layer_Outputs = [[0 for x in range(number_of_neurons)]
for y in range(number_of_layers)]
self.Hidden_Layer_Errors = [[0 for x in range(number_of_neurons)]
for y in range(number_of_layers)]
def set_output_layer(self, number_of_neurons, activation_function):
self.Output_Layer = [Neuron.Neuron(len(self.Hidden_Layer[0]), activation_function, 0, self.Bias)
for x in range(number_of_neurons)]
self.Output_Layer_Outputs = [0 for x in range(number_of_neurons)]
self.Output_Layer_Errors = [0 for x in range(number_of_neurons)]
def start_converging(self):
j=0
while j < 10:
# Here we're coming inside the training set. If was the n-th time
# you pass here, it's the n-th iteration over the Training Set.
# 'a' represents the Training Set index
for a in range(len(self.Training_Set)):
# Here we're running over the hidden layers
# 'b' represent the layer index
for b in range(len(self.Hidden_Layer)):
# Here we're running over the neurons in the layers
# 'c' represents the neuron index
for c in range(len(self.Hidden_Layer[b])):
if b == 0:
self.Hidden_Layer[b][c].initialize_inputs(self.Training_Set[a])
self.Hidden_Layer[b][c].get_sum()
self.Hidden_Layer_Outputs[b][c] = self.Hidden_Layer[b][c].get_output()
else:
self.Hidden_Layer[b][c].initialize_inputs(self.Hidden_Layer_Outputs[b-1])
self.Hidden_Layer[b][c].get_sum()
self.Hidden_Layer_Outputs[b][c] = self.Hidden_Layer[b][c].get_output()
# Here we're running over the output layer
# 'd' represents the neuron index
for d in range(len(self.Output_Layer)):
self.Output_Layer[d].initialize_inputs(self.Hidden_Layer_Outputs[-1])
self.Output_Layer[d].get_sum()
self.Output_Layer_Outputs[d] = self.Output_Layer[d].get_output()
self.Output_Layer_Errors[d] = self.Output_Layer[d].get_error(0, self.Desired_Outputs[a])
self.Output_Layer[d].update_weights(0, self.Learning_Tax)
self.Real_Outputs[a] = self.Output_Layer_Outputs
# We're updating the hidden layers now. Notice that we should pass backwards, from
# last to first, so, we're using [-(e+1)] indexes.
# '[-(e+1)]' represents the layers index.
for e in range(len(self.Hidden_Layer)):
for f in range(len(self.Hidden_Layer[-(e+1)])):
if e == 0:
self.Hidden_Layer_Errors[-(e + 1)][-(f + 1)] = self.Hidden_Layer[-(e + 1)][-(f + 1)].get_error(0, self.Output_Layer_Errors)
self.Hidden_Layer[-(e + 1)][-(f + 1)].update_weights(0, self.Learning_Tax)
else:
self.Hidden_Layer[-(e + 1)][-(f + 1)].get_error(0, self.Hidden_Layer_Errors[- (e + 1)])
self.Hidden_Layer[-(e + 1)][-(f + 1)].update_weights(0, self.Learning_Tax)
j += 1
print (self.Desired_Outputs)
print (self.Real_Outputs)
I'm new to the world of neural network, which is very interesting. I wrote the basic algorithm of backpropagation on multi-layer NN to solve small problems.
I use the activation function sigmoid (like most of you I think) (x->1/(1+exp(-x))).
I tried my program on several problems :
The first one is the XOR problem. I took a 3 layers network of size [2,2,1] with one bias neuron in the two first layers (so actually the size is more [3,3,1]).
It tried it with 1000 sets of data (i.e. a couple (0/1, 0/1) and its XOR as output), and the algorithm seemed to converge at an error of 0.5 :( I found it weird so i raised the number to 10000, and as it didn't change anything, to 100000 (in despair :p) and it WORKS ! The error fell down to less that 0.02 in average. Does anybody have an idea why it needs so much data to work?
The second one is the sum problem between two numbers (like 4+8 = ?). I took randomly a [2, 5, 5, 1] network with one bias neuron in the three first layers (so actually the size is more [3, 6, 6, 1]). I put a training data set of 100000 couples of numbers below 100 and their sum. This time, the error does not converge at all, while the output of the network always return the number 1. Have you already seen such situations ? Is it a bug code ? (code that i checked many times but perhaps).
import random
import math
class Network:
def initdata(self):
#weights initialization
self.weights.append([])
self.threshold.append([])
for l in range(1,len(self.layers)):
n = self.layers[l]
thresholdl = []
weightsl = []
for i in range(n):
thresholdl.append(-random.random())
weightsli = []
for j in range(self.layers[l-1]):
weightsli.append(random.random()*2-1)
#adding bias neurons
weightsli.append(thresholdl[-1])
weightsl.append(weightsli)
self.weights.append(weightsl)
self.threshold.append(thresholdl)
def __init__(self, layers):
self.layers = layers
self.weights = []
self.threshold = []
self.initdata()
def activation_function(self, x):
return 1/(1+math.exp(-x))
def outputlayer(self, input, l):
if l==0:
return [input]
output = []
prevoutput = self.outputlayer(input, l-1)
for i in range(self.layers[l]):
f = 0
for k in range(len(prevoutput[-1])):
f += self.weights[l][i][k]*prevoutput[-1][k]
f += self.weights[l][i][-1] #bias weight !
output.append(self.activation_function(f))
return prevoutput+[output]
def layersoutput(self, input):
return self.outputlayer(input, len(self.layers)-1)
def finaloutput(self, input):
return self.layersoutput(input)[-1]
def train(self, data, nu):
for (input, finaloutput) in data:
output = self.layersoutput(input)
err = self.errorvector(finaloutput, output[-1])
self.changeweights(err, output, nu)
def changeweights(self, err, output, nu):
deltas = []
for i in range(len(self.layers)):
deltas.append([])
tempweights = self.weights.copy()
def changeweightslayer(layer):
if layer != len(self.layers)-1:
changeweightslayer(layer+1)
for i in range(self.layers[layer]):
delta = 0
if layer != len(self.layers)-1:
delta = output[layer][i]*(1-output[layer][i])*sum([deltas[layer+1][l]*self.weights[layer+1][l][i] for l in range(self.layers[layer+1])])
else:
delta = output[layer][i]*(1-output[layer][i])*err[i]
deltas[layer].append(delta)
for k in range(len(self.weights[layer][i])-1):
tempweights[layer][i][k] += nu*output[layer-1][k]*delta
tempweights[layer][i][-1] += nu*delta
changeweightslayer(1)
self.weights = tempweights
def quadraticerror(self, a, b):
return sum([(a[i]-b[i])**2 for i in range(len(a))])
def errorvector(self, a, b):
return [a[i]-b[i] for i in range(len(a))]
network = Network([2, 5, 5, 1])
print(network.weights)
data = []
for i in range(1000000):
bit1 = random.randrange(100)
bit2 = random.randrange(100)
data.append(([float(bit1), float(bit2)], [float(bit1+bit2)]))
network.train(data, 0.1)
print(network.weights)
I have a neural network (NN) which works perfectly when applied to a single data set. However if I want to run the NN on, for example, one set of data and then create a new instance of the NN to run on different set of data (or even the same set again) then the new instance will produce completely incorrect predictions.
For example, training on an XOR pattern:
test=[[0,0],[0,1],[1,0],[1,1]]
data = [[[0,0], [0]],[[0,1], [0]],[[1,0], [0]],[[1,1], [1]]]
n = NN(2, 3, 1) # Create a neural network with 2 input, 3 hidden and 1 output nodes
n.train(data,500,0.5,0) # Train it for 500 iterations with learning rate 0.5 and momentum 0
prediction = np.zeros((len(test)))
for row in range(len(test)):
prediction[row] = n.runNetwork(test[row])[0]
print prediction
#
# Now do the same thing again but with a new instance and new version of the data.
#
test2=[[0,0],[0,1],[1,0],[1,1]]
data2 = [[[0,0], [0]],[[0,1], [0]],[[1,0], [0]],[[1,1], [1]]]
p = NN(2, 3, 1)
p.train(data2,500,0.5,0)
prediction2 = np.zeros((len(test2)))
for row in range(len(test2)):
prediction2[row] = p.runNetwork(test2[row])[0]
print prediction2
Will output:
[-0.01 -0. -0.06 0.97]
[ 0. 0. 1. 1.]
Notice that the first prediction is quite good where as the second is completely wrong, and I can't see anything wrong with the class:
import math
import random
import itertools
import numpy as np
random.seed(0)
def rand(a, b):
return (b-a)*random.random() + a
def sigmoid(x):
return math.tanh(x)
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 + 1
self.no = no
# activations for nodes
self.ai = [1.0]*self.ni
self.ah = [1.0]*self.nh
self.ao = [1.0]*self.no
# create weights (rows=number of features, columns=number of processing nodes)
self.wi = np.zeros((self.ni, self.nh))
self.wo = np.zeros((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(-5, 5)
for j in range(self.nh):
for k in range(self.no):
self.wo[j][k] = rand(-5, 5)
# last change in weights for momentum
self.ci = np.zeros((self.ni, self.nh))
self.co = np.zeros((self.nh, self.no))
def runNetwork(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-1):
sum = 0.0
for i in range(self.ni):
sum = sum + self.ai[i] * self.wi[i][j]
self.ah[j] = sigmoid(sum)
# 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)
ao_simplified = [round(a,2) for a in self.ao[:]]
return ao_simplified
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 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.runNetwork(inputs)
error = error + self.backPropagate(targets, N, M)
if i % 100 == 0: # Prints error every 100 iterations
print('error %-.5f' % error)
Any help would be greatly appreciated!
Your error -- if there is one -- doesn't have anything to do with the class. As #Daniel Roseman suggested, the natural guess would be that it was a class/instance variable issue, or maybe a mutable default argument, or multiplication of a list, or something, the most common causes of mysterious behaviour.
Here, though, you're getting different results only because you're using different random numbers each time. If you random.seed(0) before you call NN(2,3,1), you get exactly the same results:
error 2.68110
error 0.44049
error 0.39256
error 0.26315
error 0.00584
[ 0.01 0.01 0.07 0.97]
error 2.68110
error 0.44049
error 0.39256
error 0.26315
error 0.00584
[ 0.01 0.01 0.07 0.97]
I can't judge whether your algorithm is right. Incidentally, I think your rand function is reinventing random.uniform.