基于Java的神经网络--如何实现反向传播算法

4
我正在构建一个测试神经网络,但它肯定不起作用。我的主要问题是反向传播。通过我的研究,我知道使用sigmoid函数很容易。因此,我通过(1-输出)(输出)(目标值-输出)更新每个权重,但这样做的问题是如果输出为1而目标值不是呢?如果在某个时刻输出为1,则权重更新将始终为0...目前我只是尝试让它添加来自2个输入神经元的输入,因此最佳权重应该只是1,因为输出神经元只是将其输入相加。我确定我在很多地方搞砸了,但这是我的代码:
    public class Main {

        public static void main(String[] args) {
            Double[] inputs = {1.0, 2.0};
            ArrayList<Double> answers = new ArrayList<Double>();
            answers.add(3.0);

            net myNeuralNet = new net(2, 1, answers);

            for(int i=0; i<200; i++){

                myNeuralNet.setInputs(inputs);
                myNeuralNet.start();
                myNeuralNet.backpropagation();
                myNeuralNet.printOutput();
                System.out.println("*****");
                for(int j=0; j<myNeuralNet.getOutputs().size(); j++){
                    myNeuralNet.getOutputs().get(j).resetInput();
                    myNeuralNet.getOutputs().get(j).resetOutput();
                    myNeuralNet.getOutputs().get(j).resetNumCalled();
                }
            }
        }

    }


    package myneuralnet;
    import java.util.ArrayList;

    public class net {

    private ArrayList<neuron> inputLayer;
    private ArrayList<neuron> outputLayer;
    private ArrayList<Double> answers;

    public net(Integer numInput, Integer numOut, ArrayList<Double> answers){
        inputLayer = new ArrayList<neuron>();
        outputLayer = new ArrayList<neuron>();
        this.answers = answers;

        for(int i=0; i<numOut; i++){
            outputLayer.add(new neuron(true));
        }

        for(int i=0; i<numInput; i++){
            ArrayList<Double> randomWeights = createRandomWeights(numInput);
            inputLayer.add(new neuron(outputLayer, randomWeights, -100.00, true));
        }

        for(int i=0; i<numOut; i++){
            outputLayer.get(i).setBackConn(inputLayer);
        }
    }

    public ArrayList<neuron> getOutputs(){
        return outputLayer;
    }

    public void backpropagation(){
        for(int i=0; i<answers.size(); i++){
            neuron iOut = outputLayer.get(i);
            ArrayList<neuron> iOutBack = iOut.getBackConn();
            Double iSigDeriv = (1-iOut.getOutput())*iOut.getOutput();
            Double iError = (answers.get(i) - iOut.getOutput());

            System.out.println("Answer: "+answers.get(i) + " iOut: "+iOut.getOutput()+" Error: "+iError+" Sigmoid: "+iSigDeriv);

            for(int j=0; j<iOutBack.size(); j++){
                neuron jNeuron = iOutBack.get(j);
                Double ijWeight = jNeuron.getWeight(i);

                System.out.println("ijWeight: "+ijWeight);
                System.out.println("jNeuronOut: "+jNeuron.getOutput());

                jNeuron.setWeight(i, ijWeight+(iSigDeriv*iError*jNeuron.getOutput()));
            }
        }

        for(int i=0; i<inputLayer.size(); i++){
            inputLayer.get(i).resetInput();
            inputLayer.get(i).resetOutput();
        }
    }

    public ArrayList<Double> createRandomWeights(Integer size){
        ArrayList<Double> iWeight = new ArrayList<Double>();

        for(int i=0; i<size; i++){
            Double randNum = (2*Math.random())-1;
            iWeight.add(randNum);
        }

        return iWeight;
    }

    public void setInputs(Double[] is){
        for(int i=0; i<is.length; i++){
            inputLayer.get(i).setInput(is[i]);
        }
        for(int i=0; i<outputLayer.size(); i++){
            outputLayer.get(i).resetInput();
        }
    }

    public void start(){
        for(int i=0; i<inputLayer.size(); i++){
            inputLayer.get(i).fire();
        }
    }

    public void printOutput(){
        for(int i=0; i<outputLayer.size(); i++){
            System.out.println(outputLayer.get(i).getOutput().toString());
        }
    }

}

package myneuralnet;
import java.util.ArrayList;

public class neuron {

    private ArrayList<neuron> connections;
    private ArrayList<neuron> backconns;
    private ArrayList<Double> weights;
    private Double threshold;
    private Double input;
    private Boolean isOutput = false;
    private Boolean isInput = false;
    private Double totalSignal;
    private Integer numCalled;
    private Double myOutput;

    public neuron(ArrayList<neuron> conns, ArrayList<Double> weights, Double threshold){
        this.connections = conns;
        this.weights = weights;
        this.threshold = threshold;
        this.totalSignal = 0.00;
        this.numCalled = 0;
        this.backconns = new ArrayList<neuron>();
        this.input = 0.00;
    }

    public neuron(ArrayList<neuron> conns, ArrayList<Double> weights, Double threshold, Boolean isin){
        this.connections = conns;
        this.weights = weights;
        this.threshold = threshold;
        this.totalSignal = 0.00;
        this.numCalled = 0;
        this.backconns = new ArrayList<neuron>();
        this.input = 0.00;
        this.isInput = isin;
    }

    public neuron(Boolean tf){
        this.connections = new ArrayList<neuron>();
        this.weights = new ArrayList<Double>();
        this.threshold = 0.00;
        this.totalSignal = 0.00;
        this.numCalled = 0;
        this.isOutput = tf;
        this.backconns = new ArrayList<neuron>();
        this.input = 0.00;
    }

    public void setInput(Double input){
        this.input = input;
    }

    public void setOut(Boolean tf){
        this.isOutput = tf;
    }

    public void resetNumCalled(){
        numCalled = 0;
    }

    public void setBackConn(ArrayList<neuron> backs){
        this.backconns = backs;
    }

    public Double getOutput(){
        return myOutput;
    }

    public Double getInput(){
        return totalSignal;
    }

    public Double getRealInput(){
        return input;
    }

    public ArrayList<Double> getWeights(){
        return weights;
    }

    public ArrayList<neuron> getBackConn(){
        return backconns;
    }

    public Double getWeight(Integer i){
        return weights.get(i);
    }

    public void setWeight(Integer i, Double d){
        weights.set(i, d);
    }

    public void setOutput(Double d){
        myOutput = d;
    }

    public void activation(Double myInput){
        numCalled++;
        totalSignal += myInput;

        if(numCalled==backconns.size() && isOutput){
            System.out.println("Total Sig: "+totalSignal);
            setInput(totalSignal);
            setOutput(totalSignal);
        }
    }

    public void activation(){
        Double activationValue = 1 / (1 + Math.exp(input));
        setInput(activationValue);
        fire();
    }

    public void fire(){
        for(int i=0; i<connections.size(); i++){
            Double iWeight = weights.get(i);
            neuron iConn = connections.get(i);
            myOutput = (1/(1+(Math.exp(-input))))*iWeight;
            iConn.activation(myOutput);
        }
    }

    public void resetInput(){
        input = 0.00;
        totalSignal = 0.00;
    }

    public void resetOutput(){
        myOutput = 0.00;
    }
}

好的,这是很多代码,让我解释一下。目前为止,网络很简单,只有一个输入层和一个输出层 --- 我想稍后添加一个隐藏层,但现在我正在小步前进。每个层都是神经元的arraylist。输入神经元装载了输入,在这个例子中是1和2。这些神经元发射,计算输入的sigmoid并将其输出到输出神经元,然后将它们相加并存储该值。然后网络通过取(答案-输出)(输出)(1-输出)(特定输入神经元的输出)进行反向传播,并相应地更新权重。很多时候,它会循环运行,我得到无限大,这似乎与负权重或sigmoid相关。当这种情况不发生时,它收敛于1,由于(1-输出1)为0,我的权重停止更新。

numCalled和totalSignal值只是为了使算法等待所有神经元输入才继续。我知道我这样做有点奇怪,但神经元类有一个名为connections的神经元arraylist,用于保存它正向连接到的神经元。另一个名为backconns的arraylist保存反向连接。我也应该更新正确的权重,因为我获取i和j之间的所有反向连接,但在所有神经元j(i上面的层)中,我只拉取了权重i。我为混乱道歉 --- 我已经尝试了很多东西,花了数小时的时间,仍然无法弄清楚。非常感谢任何帮助!


你可能会发现这篇文章有用:http://www.informit.com/articles/article.aspx?p=30596,最后一页列出了完整的源代码。 - wlk
3个回答

1

关于神经网络的一般最佳教材之一是Chris Bishop和Simon Haykin的书籍。尝试阅读有关反向传播章节,并理解为什么权重更新规则中的术语是这样的。我之所以要求你这样做,是因为反向传播比起一开始看起来更加微妙。如果您在输出层使用线性激活函数(考虑为什么您可能想要这样做。提示:后处理),或者添加隐藏层,则情况会有所改变。当我真正阅读这本书时,它变得更加清晰。


0
如果我的输出是1,但目标不是怎么办?
Sigmoid函数1/(1 + Math.exp(-x))永远不会等于1。当x趋近无穷大时,极限值等于0,但这只是一个水平渐近线,所以函数实际上从未触及1。因此,如果使用这个表达式来计算所有的输出值,那么你的输出将永远不会是1。所以(1 - output)永远不应该等于0。
我认为你的问题出在输出的计算上。对于神经网络,每个神经元的输出通常是输入和权重的乘积的sigmoid函数。换句话说,value = input1 * weight1 + input2 * weight2 + ... (对于每个神经元的权重) + biasWeight。然后该神经元的输出 = 1 / (1 + Math.exp(-value)。如果按照这种方式计算,输出将永远不会等于1。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接