Подтвердить что ты не робот

Нейронная сеть в Javascript не учится должным образом

Я попытался переписать нейронную сеть, найденную здесь в javascript. Мой код javascript выглядит так.

function NeuralFactor(weight) {
    var self = this;
    this.weight = weight;
    this.delta =  0;
}

function Sigmoid(value) {
    return 1 / (1 + Math.exp(-value));
}

function Neuron(isInput) {
    var self = this;
    this.pulse = function() {
        self.output = 0;
        self.input.forEach(function(item) {
            self.output += item.signal.output * item.factor.weight;
        });

        self.output += self.bias.weight;
        self.output = Sigmoid(self.output);
    };

    this.bias = new NeuralFactor(isInput ? 0 : Math.random());
    this.error = 0;
    this.input = [];
    this.output = 0;

    this.findInput = function(signal) {
        var input = self.input.filter(function(input) {
            return signal == input.signal;
        })[0];
        return input;
    };
}

function NeuralLayer() {
    var self = this;
    this.pulse = function() {
        self.neurons.forEach(function(neuron) {
            neuron.pulse();
        });
    };
    this.neurons = [];
    this.train = function(learningRate) {
        self.neurons.forEach(function(neuron) {
            neuron.bias.weight += neuron.bias.delta * learningRate;
            neuron.bias.delta = 0;
            neuron.input.forEach(function(input) {
                input.factor.weight += input.factor.delta * learningRate;
                input.factor.delta = 0;
            })
        })
    }
}

function NeuralNet(inputCount, hiddenCount, outputCount) {
    var self = this;
    this.inputLayer = new NeuralLayer();
    this.hiddenLayer = new NeuralLayer();
    this.outputLayer = new NeuralLayer();
    this.learningRate = 0.5;

    for(var i = 0; i < inputCount; i++)
        self.inputLayer.neurons.push(new Neuron(true));

    for(var i = 0; i < hiddenCount; i++)
        self.hiddenLayer.neurons.push(new Neuron());

    for(var i = 0; i < outputCount; i++)
        self.outputLayer.neurons.push(new Neuron());

    for (var i = 0; i < hiddenCount; i++)
        for (var j = 0; j < inputCount; j++)
            self.hiddenLayer.neurons[i].input.push({
                signal: self.inputLayer.neurons[j],
                factor: new NeuralFactor(Math.random())
            });

    for (var i = 0; i < outputCount; i++)
        for (var j = 0; j < hiddenCount; j++)
            self.outputLayer.neurons[i].input.push({
                signal: self.hiddenLayer.neurons[j],
                factor: new NeuralFactor(Math.random())
            });

    this.pulse = function() {
        self.hiddenLayer.pulse();
        self.outputLayer.pulse();
    };

    this.backPropagation = function(desiredResults) {
        for(var i = 0; i < self.outputLayer.neurons.length; i++) {
            var outputNeuron = self.outputLayer.neurons[i];
            var output = outputNeuron.output;
            outputNeuron.error = (desiredResults[i] - output) * output * (1.0 - output);
        }
        for(var i = 0; i < self.hiddenLayer.neurons.length; i++) {
            var hiddenNeuron = self.hiddenLayer.neurons[i];
            var error = 0;
            for(var j = 0; j < self.outputLayer.neurons.length; j++) {
                var outputNeuron = self.outputLayer.neurons[j];
                error += outputNeuron.error * outputNeuron.findInput(hiddenNeuron).factor.weight * hiddenNeuron.output * (1.0 - hiddenNeuron.output);
            }
            hiddenNeuron.error = error;
        }
        for(var j = 0; j < self.outputLayer.neurons.length; j++) {
            var outputNeuron = self.outputLayer.neurons[j];
            for(var i = 0; i < self.hiddenLayer.neurons.length; i++) {
                var hiddenNeuron = self.hiddenLayer.neurons[i];
                outputNeuron.findInput(hiddenNeuron).factor.delta += outputNeuron.error * hiddenNeuron.output;
            }
            outputNeuron.bias.delta += outputNeuron.error * outputNeuron.bias.weight;
        }
        for(var j = 0; j < self.hiddenLayer.neurons.length; j++) {
            var hiddenNeuron = self.hiddenLayer.neurons[j];
            for(var i = 0; i < self.inputLayer.neurons.length; i++) {
                var inputNeuron = self.inputLayer.neurons[i];
                hiddenNeuron.findInput(inputNeuron).factor.delta += hiddenNeuron.error * inputNeuron.output;
            }
            hiddenNeuron.bias.delta += hiddenNeuron.error * hiddenNeuron.bias.weight;
        }
    };
    this.train = function(input, desiredResults) {
        for(var i = 0; i < self.inputLayer.neurons.length; i++) {
            var neuron = self.inputLayer.neurons[i];
            neuron.output = input[i];
        }

        self.pulse();
        self.backPropagation(desiredResults);

        self.hiddenLayer.train(self.learningRate);
        self.outputLayer.train(self.learningRate);
    };

}

Теперь я пытаюсь узнать, как решить проблему XOR. Я учу его так:

var net = new NeuralNet(2,2,1);

var testInputs = [[0,0], [0,1], [1,0], [1,1]];
var testOutputs = [[1],[0],[0],[1]];

for (var i = 0; i < 1000; i++)
    for(var j = 0; j < 4; j++)
        net.train(testInputs[j], testOutputs[j]);

function UseNet(a, b) {
    net.inputLayer.neurons[0].output = a;
    net.inputLayer.neurons[1].output = b;
    net.pulse();

    return net.outputLayer.neurons[0].output;
}

Проблема в том, что все результаты, которые я получаю, близки к 0.5 и довольно случайны, независимо от того, какие аргументы я использую. Например:

UseNet(0,0) => 0.5107701166677714
UseNet(0,1) => 0.4801498747476413
UseNet(1,0) => 0.5142463167153447
UseNet(1,1) => 0.4881829364416052

Что может быть неправильным с моим кодом?

4b9b3361

Ответ 1

Эта сеть достаточно велика для проблемы XOR, и я не вижу никаких очевидных ошибок, поэтому я подозреваю, что она застревает в локальном минимуме.

Попробуйте пройти тренировочный набор в 10 000 раз вместо 1000; это дает ему больше шансов выйти из любых минимумов и сходиться. Вы также можете увеличить конвергенцию, увеличивая количество скрытых нейронов, настраивая η (скорость обучения) или добавляя импульс. Чтобы реализовать последнее, попробуйте использовать это как свою обучающую функцию:

this.train = function(learningRate) {
    var momentum = 0 /* Some value, probably fairly small. */;
    self.neurons.forEach(function(neuron) {
        neuron.bias.weight += neuron.bias.delta * learningRate;
        neuron.bias.delta = 0;
        neuron.input.forEach(function(input) {
            input.factor.weight += (input.factor.delta * learningRate) + (input.factor.weight * momentum);
            input.factor.delta = 0;
        })
    })
}

У меня были хорошие результаты, меняющие скорость обучения до 1,5 (что довольно много) и импульс до 0,000001 (что довольно мало).

(Кстати, вы пытались запустить .NET-реализацию с несколькими разными семенами? Слишком долго может сходиться!)

Ответ 2

Эта система использует нечеткую логику. Как говорится в статье, не используйте целые числа вместо использования "близких" реальных чисел, как предлагает статья - попробуйте

UseNet(0.1,0.1) => 
UseNet(0.1,0.9) => 
UseNet(0.9,0.1) => 
UseNet(0.9,0.9) => 

Для результатов ничего выше 0,5 равно 1 и ниже 0

Ответ 3

Хмммм

Попробуйте вместо:

var testInputs = [[0,0], [0,1], [1,0], [1,1]];
var testOutputs = [[1],[0],[0],[1]];

Это:

var testInputs = [[0.05,0.05], [0.05,0.95], [0.95,0.05], [0.95,0.95]];
var testOutputs = [[1],[0],[0],[1]];

или

var testInputs = [[0,0], [0,1], [1,0], [1,1]];
var testOutputs = [[0.95],[0.05],[0.05],[0.95]];