NAPI Quick Start Guide

David Ray edited this page Sep 30, 2016 · 39 revisions

Next: NAPI In Depth Overview

Here you can find a more in depth explanation of the NAPI details, together with examples, and links to example code.

Let's get started!

Here's an example of the code it takes to get a full featured network up and running:

Parameters p = NetworkDemoHarness.getParameters(); // "Default" test parameters (you will need to tweak)
p = p.union(NetworkDemoHarness.getNetworkDemoTestEncoderParams()); // Combine "default" encoder parameters.

Network network = Network.create("Network API Demo", p)         // Name the Network whatever you wish...
    .add(Network.createRegion("Region 1")                       // Name the Region whatever you wish...
        .add(Network.createLayer("Layer 2/3", p)                // Name the Layer whatever you wish...
            .alterParameter(KEY.AUTO_CLASSIFY, Boolean.TRUE)    // (Optional) Add a CLAClassifier
            .add(Anomaly.create())                              // (Optional) Add an Anomaly detector
            .add(new TemporalMemory())                          // Core Component but also it's "optional"
            .add(new SpatialPooler())                           // Core Component, but also "optional"
            .add(Sensor.create(FileSensor::create, SensorParams.create(
                Keys::path, "", ResourceLocator.path("rec-center-hourly.csv"))))));  // Sensors automatically connect to your source data, but you may omit this and pump data direction in!

network.start();

(class NetworkDemoHarness is no longer located in htm.java package, you can find it here) in htm.java's new "Demo & Example" repo.

...and that's it! The above network (while simple), contains a full complement of all NuPIC algorithms and computational components.

Some more involved examples:

Here we see how to connect multiple layers...

Parameters p = NetworkDemoHarness.getParameters();
p = p.union(NetworkDemoHarness.getNetworkDemoTestEncoderParams());
        
Network network = Network.create("Network API Demo", p)
    .add(Network.createRegion("Region 1")
        .add(Network.createLayer("Layer 2/3", p)
            .alterParameter(KEY.AUTO_CLASSIFY, Boolean.TRUE)
            .add(Anomaly.create())
            .add(new TemporalMemory()))
        .add(Network.createLayer("Layer 4", p)
            .add(new SpatialPooler()))
        .add(Network.createLayer("Layer 5", p)
            .add(Sensor.create(FileSensor::create, SensorParams.create(
                Keys::path, "", ResourceLocator.path("rec-center-hourly.csv")))))
        .connect("Layer 2/3", "Layer 4")
        .connect("Layer 4", "Layer 5"));

Here we see how to connect multiple layers and multiple regions...

Parameters p = NetworkDemoHarness.getParameters();
p = p.union(NetworkDemoHarness.getNetworkDemoTestEncoderParams());

// Shared connections example
Connections connections = new Connections();
        
Network network = Network.create("Network API Demo", p)
    .add(Network.createRegion("Region 1")
        .add(Network.createLayer("Layer 2/3", p)
            .alterParameter(KEY.AUTO_CLASSIFY, Boolean.TRUE)
            .using(connections)       // Demonstrates Connections sharing between Layers in same Region
            .add(Anomaly.create())
            .add(new TemporalMemory()))
        .add(Network.createLayer("Layer 4", p)
            .using(connections)       // Shared with different Layer above
            .add(new SpatialPooler()))
        .connect("Layer 2/3", "Layer 4"))
    .add(Network.createRegion("Region 2")
        .add(Network.createLayer("Layer 2/3", p)
            .alterParameter(KEY.AUTO_CLASSIFY, Boolean.TRUE)
            .add(Anomaly.create())
            .add(new TemporalMemory())
            .add(new SpatialPooler()))
        .add(Network.createLayer("Layer 4", p)
            .add(Sensor.create(FileSensor::create, SensorParams.create(
                Keys::path, "", ResourceLocator.path("rec-center-hourly.csv")))))
        .connect("Layer 2/3", "Layer 4"))
    .connect("Region 1", "Region 2");

Special "Fluent" methods

  1. Layer.using(Connections) - Used to set or share a Connections object between two Layers where one Layer has a SpatialPooler and the next Layer has a TemporalMemory - and for some reason you need to separate the two algorithms into different Layers but still wish to "share" the Connections.java object.

  2. Layer.alterParameter(KEY., value) - Used to set a value on a Layer WITHOUT affecting the Parameters object set in other locations - even though it may be the same Parameters object. This creates a copy locally and all subsequent changes to the original Parameters object will not affect the Layer in question from that time on.

  3. Region.connect(String sinkLayer, String sourceLayer) and Network.connect(String sinkRegion, String sourceRegion) - these are two connection methods which are used to establish the actual connection after adding them to their respective parent containers.

  4. Region.lookup(String layerName) - returns the Layer Object or null if it is not found.

  5. Network.lookup(String regionName) - returns the Region Object or null if it is not found.

  6. Sensors - See below for use of the 3 flavors; or click link to the left to get detailed information.

Sensor.create(FileSensor::create, SensorParams.create(
                Keys::path, "", ResourceLocator.path("rec-center-hourly.csv")))

Sensor.create(URISensor::create, SensorParams.create(
                Keys::uri, "", "http://www.metaware.us/nupic/rec-center-hourly.csv"))


PublisherSupplier manual = PublisherSupplier.builder()
    .addHeader("timestamp,consumption")
    .addHeader("datetime,float")
    .addHeader("T,B") //see SensorFlags.java for more info
    .build();
Sensor.create(ObservableSensor::create, SensorParams.create(
                Keys::obs, "", manual))

Note: Strings were used for lookup so that the various connect() methods could be used in "fluent" style without interrupting the declaration flow.


General Rules of Thumb regarding Network Creation and Administration

  1. Algorithms should not be duplicated within a single Layer (i.e. Don't have 2 SpatialPoolers in the same layer for example).

  2. Don't forget to connect Layers or Regions to each other as shown above. Adding a Layer and Region is not the same as connecting them; you must do both or there will be indeterminate behavior.

  3. Sensors (FileSensor, URISensor, ObservableSensor) must be added to a layer that is connected to the bottom of process flow. FileSensor and URISensor become "hot" the moment start() is called on the network. ObservableSensor's are "push" oriented so start() must be called before pushing data through the network, but obviously nothing will happen until you actually push data through. Each unit of data will ascend the entire network, one at a time - unless you have the SpatialPooler set to train a number of cycles before allowing data up the pipeline. This is accomplished by setting KEY.SP_PRIMER_DELAY with a value > 0, in the Parameters object the Network is configured with.

  4. Connections (shown in an example above utilizing the Layer.using() method) should only be shared within and among Layers inside a single Region. Layers themselves will create their own Connections object and any SP and TM within that layer will use the same Connections object (as it should) - but the Layer.using() method overwrites the internally created Connections object with the object specified as an argument to the using() method (allowing sharing between Layers of the same Region if specified). The sharing of Connections between Regions is undefined and will likely result in indeterminate behavior if not causing an Exception.

  5. Network.start() should always be called. (Except when feeding data directly into the Network via Network.compute() or Layer.compute()). Other than that, when using a FileSensor or URLSensor - but also when using an ObservableSensor and manually entering data because data must be submitted on a different thread then the thread on which the layer is running. The only time Network.start() does not need to be used is when directly entering data into the bottom layer of a Network via the Layer.compute() method. (see Layer.testBasicSetup_SpatialPooler_MANUAL_MODE() as an example of direct Layer input)

  6. When using an ObservableSensor, a PublisherSupplier should be used to programmatically enter the header and to setup the sensor. See LayerTest.testLayerWithObservableInput() for an example of how this is accomplished. EDIT: A PublisherSupplier is the "new" way to add a Publisher to the network. See this example

  7. The LayerTest class also shows how to "warm up" the SpatialPooler before forwarding data to a TemporalMemory within a given Layer. This simply means that you can specify the number of cycles to send the data through the SpatialPooler (skipping the rest of the Layer input chain), before allowing the data to be forwarded through to the rest of the Layer. This is a cool feature that optimizes initialization and learning of a Layer such that the TemporalMemory and SpatialPooler aren't learning at the same time (which can lead to sub-optimal performance).

For more detailed information start with the In-Depth Component Overview

Next: NAPI In Depth Component Overview