Overture: the first application

The XOR problem

It's a classic in neural network field.

The Implementation with NNFW

Create the Net

#include "nnfw/nnfw.h"
#include "nnfw/biasedcluster.h"
#include "nnfw/dotlinker.h"
#include "nnfw/liboutputfunctions.h"
#include "nnfw/backpropagationalgo.h"
#include "nnfw/random.h"

using namespace nnfw;

//-------- Neural Network Structures
BiasedCluster *in, *hid, *out;
DotLinker *l1, *l2;
BaseNeuralNet* net;

int main( int , char*  ) {

    Random::setSeed( time(0) );

    net = new BaseNeuralNet();

    // --- Create the Layers of network    
    in = new BiasedCluster( 2 );
    in->setFunction( SigmoidFunction( 1.0f ) );
    hid = new BiasedCluster( 4 );
    hid->setFunction( SigmoidFunction( 1.0f ) );
    out = new BiasedCluster( 1 );
    out->setFunction( SigmoidFunction( 1.0f ) );

    // --- Create the Matrix connection among layers
    l1 = new DotLinker( in, hid );
    l2 = new DotLinker( hid, out );

    // --- Add all in the BaseNeuralNet class
    net->addCluster( in, true );
    net->addCluster( hid );
    net->addCluster( out, false, true );
    net->addLinker( l1 );
    net->addLinker( l2 );
    // --- Specify the order on which layers and matrix weight will be updated
    UpdatableVec ord;
    net->setOrder( ord << in << l1 << hid << l2 << out );

    // --- Randomize the parameters of network (biases of neuron's layers and weight of matrix linkers)
    net->randomize( -1.0, 1.0 );

Create the BackPropagation Algorithm

    UpdatableVec bp_ord;
    bp_ord << out << l2 << hid << l1 << in;
    BackPropagationAlgo* bp = new BackPropagationAlgo( net, bp_ord, 0.2 );

Define the problem... and learn it

    cout.precision( 10 );
    // --- The learning Set 
    PatternSet learningSet(4);
    // --- Input <0,0> -> Output <0>
    learningSet[0].setInputsOf( in, RealVec() << 0.0 << 0.0 );
    learningSet[0].setOutputsOf( out, RealVec() << 0.0 );
    // --- Input <0,1> -> Output <1>
    learningSet[1].setInputsOf( in, RealVec() << 0.0 << 1.0 );
    learningSet[1].setOutputsOf( out, RealVec() << 1.0 );
    // --- Input <1,0> -> Output <1>
    learningSet[2].setInputsOf( in, RealVec() << 1.0 << 0.0 );
    learningSet[2].setOutputsOf( out, RealVec() << 1.0 );
    // --- Input <1,1> -> Output <0>
    learningSet[3].setInputsOf( in, RealVec() << 1.0 << 1.0 );
    learningSet[3].setOutputsOf( out, RealVec() << 0.0 );

    // --- Main loop for learning the network
    int i;
    for( i = 0; i<50000; i++ ) {
        bp->learnOnSet( learningSet );
        // --- each 1000 iteration print out the error
        if ( i%1000 == 0 ) {
            cout << "Error: " << bp->calculateMSEOnSet( learningSet ) << endl;
        }
    }
    cout << "Iterations: " << i << "\tError:" << bp->calculateMSEOnSet( learningSet ) << endl;
    // --- compare the outputs with learning set
    for( int i = 0; i<4; i++ ) {
        in->inputs() = learningSet[i].inputsOf( in );
        net->step();
        Real out1 = out->getOutput(0);
        Real out2 = learningSet[i].outputsOf( out )[0];
        cout << "Target: " << out2 << "\tRete: " << out1 << endl;
    }

    return 0;
}

Defining the Neural Network using XML file

All the code for create the network topology can be reduced to few lines that load it from an XML files containing the definition of a neural network.

Create the file

The file that define exactly the neural network used above look like the follow:

<nnfw version="1.0">
    <neuralnet>
        <!-- The Input Layer of the Network -->
        <cluster name="Input" type="BiasedCluster" size="2" >
            <outfunction type="SigmoidFunction">
                <lambda>1.0</lambda>
            </outfunction>
            <!-- Randomize the values of biases in the range specified -->
            <randomize min="-1.0" max="+1.0" />
        </cluster>

        <!-- The Hidden Layer of the Network -->
        <cluster name="Hidden" type="BiasedCluster" size="4">
            <outfunction type="SigmoidFunction">
                <lambda>1.0</lambda>
            </outfunction>
            <!-- Randomize the values of biases in the range specified -->
            <randomize min="-1.0" max="+1.0" />
        </cluster>

        <!-- The Output Layer of the Network -->
        <cluster name="Output" type="BiasedCluster" size="1" >
            <outfunction type="SigmoidFunction">
                <lambda>1.0</lambda>
            </outfunction>
            <!-- Randomize the values of biases in the range specified -->
            <randomize min="-1.0" max="+1.0" />
        </cluster>
    
        <!-- Full Connection from Input to Hidden -->
        <linker name="In2Hid" type="MatrixLinker" from="Input" to="Hidden">
            <!-- Randomize the values of weights in the range specified -->
            <randomize min="-1.0" max="+1.0" />
        </linker>

        <!-- Full Connection from Hidden to Ouput -->
        <linker name="Hid2Out" type="MatrixLinker" from="Hidden" to="Output">
            <!-- Randomize the values of weights in the range specified -->
            <randomize min="-1.0" max="+1.0" />
        </linker>

        <!-- Specify which are the Input layers -->
        <inputs> Input </inputs>
        <!-- Specify which are the Output layers -->
        <outputs> Output </outputs>
        <!-- Specify the update order during spreading of the net -->
        <order> Input In2Hid Hidden Hid2Out Output </order>
    </neuralnet>
</nnfw>

The Code for using the file

After saved the file, change the example above in that way:

#include "nnfw/nnfw.h"
#include "nnfw/ionnfw.h"
#include "nnfw/backpropagationalgo.h"
using namespace nnfw;

BaseNeuralNet* net;

int main( int , char*  ) {

    Random::setSeed( time(0) );
    //--- load the neural network from the file
    net = loadXML( "../xor2/xor1.xml" );
    
    //--- register the input and ouput cluster in local variable for fast access
    //--- WARNING: here we assume that there is only one cluster as Input and Output of the net
    //---          in general, this is not true. Pay attention when you change this tutorial
    Cluster* in = net->inputClusters()[0];
    Cluster* out = net->outputClusters()[0];

    //--- suppose that the reverse order is correct for backpropagation algorithm
    UpdatableVec bp_ord;
    bp_ord.resize( net->order().size() );
    bp_ord.assign_reverse( net->order() );
    BackPropagationAlgo* bp = new BackPropagationAlgo( net, bp_ord, 0.15 );

The above code substitute the code showed in overture_21 and overture_22. Attach the code showed in the overture_23 and you'll get a complete working program.

In addition, just before the "return 0" statement you can save your learned network in this way:

    saveXML( "../xor2/xor1_learned.xml", net );
    return 0;
}

Run the application with different architecture

Feel free to change the architecture and see the differences on time convergences for this simple XOR problem.
BerliOS Developer Logo Valid XHTML 1.0 Transitional Valid CSS!