Python - Нейронная сеть обратного распространения XOR сходится к 0,5
Я прочитал пару вопросов, как мой, но я не могу найти ответ.
Я пытаюсь построить нейронную сеть, которая использует Back Propagation в качестве метода обучения с использованием python, я не следую ни одному примеру, я использую свою собственную архитектуру, и это может показаться немного странным, поэтому я объясню, как это работает.
Архитектура разделена на 3 части: Neuron, Layer и BPNeuralNetowrk, наиболее важная часть этого класса Neuron, в котором я храню функцию активации (я использую сигмоид) и веса синапсов, которые соединяют этот нейрон с предыдущим уровнем.,
Конкретная сеть, которую я тестирую, имеет один скрытый слой с 2 нейронами и один выходной слой с одним нейроном, оба с сигмоидом в качестве функции активации, сеть имеет два входа.
Проблема в том, что независимо от того, какую скорость обучения я использую (в моем алгоритме это называется альфа), и неважно, сколько эпох я запускаю для вывода для feedForward, всегда сходится к 0,5, и я не могу понять, почему
Вот код, который я написал, он может быть немного грязным:
import numpy as np
import sys
#np.random.seed(7)
def sigmoid(x):
return np.divide(1.0, np.add(1.0, np.exp(-x)))
def sigmoid_derivate(x):
return np.multiply(x, (np.add(1.0, -x)))
def linear(x):
return x
class Neuron(object):
def __init__(self, nWeights, actF, actFDer):
# nWeights : int --> number of weights for this neuron
# actF : function* --> pointer to the activation function
# Initializing weight vector randomly and adding one more for bias weight
self.weights = np.random.uniform(low = -1.0, high = 1.0, size=nWeights+1)
# Saving pointer to activating function
self.func = actF
self.funcDer = actFDer
# Initializing containers for delta value and output array
self.delta = None
self.output = None
def getOutput(self, inp):
# inp : NumpyArray<float> --> array with the inputs
# Appending bias to the input array
inp = np.append(np.array([1]), inp)
# Calculating sum of weighed inputs trough activation function
net = np.dot(inp, self.weights)
self.output = self.func(net)
#print(self.output)
return self.output
class Layer(object):
def __init__(self, size, prevSize, actF, actFDer):
# size : int --> number of neurons in this layer
# prevSize : int --> number of neurons in previous layer
# actF : function* --> pointer to the activation function
# Storing size data
self.size = size
# Creating and filling neuron list
self.neurons = []
for n in range(size):
self.neurons.append(Neuron(
nWeights = prevSize,
actF = actF,
actFDer = actFDer
))
def getNeuronOutputs(self):
outputs = []
for neuron in self.neurons:
if(neuron.output):
outputs.append(neuron.output)
else:
print(' Tried to take the output from an unassigned nueron output')
exit()
return np.array(outputs)
class BPNeuralNetwork(object):
def __init__(self, inputSize, outputSize, alpha, epochs):
# inputSize : int --> number of neurons in the input layer
# outputSize : int --> number of neurons in the output layer
# Storing input and output size values
self.inputSize = inputSize
self.outputSize = outputSize
# Layers list
self.layers = []
# Values for testing
self.alpha = alpha
self.epochs = epochs
# Values for shit
self.trainingErrorSq = []
self.testErrorSq = []
def addLayer(self, size, actF, actFDer):
# size : int --> number of neurons in the layer
# actF : function* --> pointer to the activation function
if( not self.layers ):
prevSize = self.inputSize
else:
prevSize = self.layers[-1].size
self.layers.append(Layer(
size = size,
prevSize = prevSize,
actF = actF,
actFDer = actFDer
))
return True
def feedForward(self, inp):
# inp : NumpyArray<float> --> array with the inputs
lastOut = inp
for layer in self.layers:
output = np.array([])
for neuron in layer.neurons:
output = np.append(output, neuron.getOutput(lastOut))
lastOut = output
return lastOut
def backPropagation(self, inp, target):
# inp : NumpyArray<float> --> array with the inputs
# target : NumpyArray<float> --> array with the desired outputs
# Getting output from FF
output = self.feedForward(inp)
# This shouldnt be here, this is cableado, i suck
error = np.add(target, -output)
sqError = np.square(error)
self.trainingErrorSq.append(sqError)
# Calculate output layer deltas
outputLayer = self.layers[-1]
for i, neuron in enumerate(outputLayer.neurons):
neuronError = np.add(target[i], -neuron.output)
neuron.delta = np.multiply(neuron.funcDer(neuron.output), neuronError)
# Calculate hidden layers deltas
for i in reversed(range(len(self.layers)-1)):
layer = self.layers[i]
nextLayer = self.layers[i+1]
for j, neuron in enumerate(layer.neurons):
aux = 0
for k, outputNeuron in enumerate(nextLayer.neurons):
aux = np.add(aux, np.multiply(outputNeuron.weights[j+1], outputNeuron.delta))
neuron.delta = np.multiply(neuron.funcDer(neuron.output), aux)
# Update weights
for i in range(0, len(self.layers)):
layer = self.layers[i]
# Getting output from previous layer
if(i != 0):
backLayerOutputs = self.layers[i-1].getNeuronOutputs()
else:
backLayerOutputs = inp
for l, neuron in enumerate(layer.neurons):
# Update weight of the bias
backLayerNeuronOutput = 1
deltaW = np.array([self.alpha, neuron.delta, backLayerNeuronOutput, neuron.weights[0]])
deltaW = np.prod(deltaW)
neuron.weights[0] = np.add(neuron.weights[0], deltaW)
# Update weights
for j in range(1, len(neuron.weights)):
backLayerNeuronOutput = backLayerOutputs[j-1]
deltaW = np.array([self.alpha, neuron.delta, backLayerNeuronOutput, neuron.weights[j]])
deltaW = np.prod(deltaW)
neuron.weights[j] = np.add(neuron.weights[j], deltaW)
return True
def train(self, inputs, outputs):
if(len(inputs) != len(outputs)):
print('Lengths for input and output on training data sets dont match')
print('Training netowrk... ')
epochErrorSqMean = 10000
epoch = 0
while(epochErrorSqMean > 0.2):
#for epoch in range(self.epochs):
print('Epoch: '+str(epoch))
#print('%f%% done'%(epoch/self.epochs*100))
for i in range(0, len(inputs)):
NN.backPropagation( inputs[i], outputs[i] )
epochErrorSq = self.trainingErrorSq[epoch*len(inputs):epoch*len(inputs)+len(inputs)]
epochErrorSqMean = np.mean(epochErrorSq)
print('Epoch square error mean: '+str(epochErrorSqMean))
epoch += 1
print('done!')
print('\a')
print('Layers: '+str(len(self.layers)-1))
print('Epochs: '+str(self.epochs))
print('Alpha: '+str(self.alpha))
print('Training error mean: '+str(np.mean(self.trainingErrorSq)))
def test(self, inputs, outputs, printRes=False):
if(len(inputs) != len(outputs)):
print('Lengths for input and output on testing data sets dont match')
fakeNegatives = 0
fakePositives = 0
output = []
for i in range(0, len(inputs)):
out = self.feedForward(inputs[i])
error = np.add(outputs[i], -out)
sqError = np.square(error)
self.testErrorSq.append(sqError)
output.append(out)
if(printRes):
print('INPUT: '+str(inputs[i]))
print('Expected=%f, Got=%.15f' % (outputs[i], out))
return output
if __name__ == "__main__":
if(len(sys.argv) > 1):
NN = BPNeuralNetwork(
inputSize = 2,
outputSize = 1,
alpha = float(sys.argv[1]),
epochs = int(sys.argv[2])
)
else:
NN = BPNeuralNetwork(
inputSize = 2,
outputSize = 1,
alpha = 0.1,
epochs = 100
)
if('-xor' in sys.argv):
NN.addLayer(2, sigmoid, sigmoid_derivate)
NN.addLayer(1, sigmoid, sigmoid_derivate)
dataset = np.array([
np.array([0, 0, 0]),
np.array([0, 1, 1]),
np.array([1, 0, 1]),
np.array([1, 1, 0]),
])
X = dataset[:,0:NN.inputSize]
Y = dataset[:,NN.inputSize:NN.inputSize+NN.outputSize]
NN.train(X, Y)
NN.test(X, Y, True)
Если вы хотите проверить это, просто запустите: python alpha epochs -xor
Параметр epochs не будет работать, так как он настроен на останов после ошибки < 0.2
Вывод, который вы увидите, является средним квадратом ошибок для этой конкретной эпохи.
Я ценю любые ответы или идеи о том, что может быть не так