Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NUP-2405: quick-start guide for the Network API #3557

Merged
merged 21 commits into from
Apr 26, 2017
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
80bb517
NUP-2394: use YAML file for model params
Apr 4, 2017
54a9d10
NUP-2394: network API quick-start example (WIP)
Apr 4, 2017
69841ff
NUP-2394: run inference outside network
Apr 5, 2017
6956e0c
NUP-2394: save and check all complete-examples.py predictions
Apr 5, 2017
956f569
Merge branch 'master' of https://github.com/numenta/nupic into NUP-23…
Apr 5, 2017
fb95987
NUP-2394: use YAML params in "algo" code example
Apr 6, 2017
dc3cdbf
NUP-2394: update comments of YAML params based on feedback
Apr 6, 2017
a771b84
NUP-2394: scripts to compare predictions results between the 3 code e…
Apr 6, 2017
93160cf
NUP-2394: Run classification inside network. Details:
Apr 8, 2017
4ce9edc
NUP-2394: Show RMSE in plot titles
Apr 8, 2017
9c77a66
Merge branch 'master' of https://github.com/numenta/nupic into NUP-23…
Apr 14, 2017
21d82b1
Merge branch 'master' of https://github.com/numenta/nupic into NUP-23…
Apr 17, 2017
b727b69
Code review feedback:
Apr 17, 2017
dc72590
NUP-2394: Fix YAML with new CLA model name (HTMPrediction)
Apr 17, 2017
fac2380
NUP-2394: make model_params camel case for consistency and update cod…
Apr 18, 2017
3c205af
NUP-2394: re-order network creation logic:
Apr 18, 2017
d7e8593
NUP-2394: fix indentation
Apr 18, 2017
0466c73
NUP-2405: quick-start guide for the network API:
Apr 18, 2017
829db9d
NUP-2405: Fix reference to old modelParams in OPF example:
Apr 18, 2017
214c9bd
NUP-2405: address PR feedback
Apr 26, 2017
5fa9357
Merge branch 'master' of https://github.com/numenta/nupic into NUP-24…
Apr 26, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 55 additions & 35 deletions docs/examples/algo/complete-example.py
Original file line number Diff line number Diff line change
@@ -1,87 +1,106 @@
import csv
import datetime

import numpy
import os
import yaml

from nupic.encoders.date import DateEncoder
from nupic.encoders.random_distributed_scalar import \
RandomDistributedScalarEncoder
RandomDistributedScalarEncoder
from nupic.research.spatial_pooler import SpatialPooler
from nupic.research.temporal_memory import TemporalMemory
from nupic.algorithms.sdr_classifier_factory import SDRClassifierFactory

_INPUT_FILE_PATH = "../data/gymdata.csv"
_NUM_RECORDS = 3000
_EXAMPLE_DIR = os.path.dirname(os.path.abspath(__file__))
_INPUT_FILE_PATH = os.path.join(_EXAMPLE_DIR, os.pardir, "data", "gymdata.csv")
_PARAMS_PATH = os.path.join(_EXAMPLE_DIR, os.pardir, "params", "model.yaml")

def runHotgym():

timeOfDayEncoder = DateEncoder(timeOfDay=(21,1))
weekendEncoder = DateEncoder(weekend=21)
scalarEncoder = RandomDistributedScalarEncoder(0.88)

encodingWidth = timeOfDayEncoder.getWidth() \
+ weekendEncoder.getWidth() \
+ scalarEncoder.getWidth()
def runHotgym():
with open(_PARAMS_PATH, "r") as f:
modelParams = yaml.safe_load(f)["modelParams"]
enParams = modelParams["sensorParams"]["encoders"]
spParams = modelParams["spParams"]
tmParams = modelParams["tmParams"]

timeOfDayEncoder = DateEncoder(
timeOfDay=enParams["timestamp_timeOfDay"]["timeOfDay"])
weekendEncoder = DateEncoder(
weekend=enParams["timestamp_weekend"]["weekend"])
scalarEncoder = RandomDistributedScalarEncoder(
enParams["consumption"]["resolution"])

encodingWidth = (timeOfDayEncoder.getWidth()
+ weekendEncoder.getWidth()
+ scalarEncoder.getWidth())

sp = SpatialPooler(
# How large the input encoding will be.
inputDimensions=(encodingWidth),
# How many mini-columns will be in the Spatial Pooler.
columnDimensions=(2048),
# What percent of the columns's receptive field is available for potential
columnDimensions=(spParams["columnCount"]),
# What percent of the columns"s receptive field is available for potential
# synapses?
potentialPct=0.85,
potentialPct=spParams["potentialPct"],
# This means that the input space has no topology.
globalInhibition=True,
localAreaDensity=-1.0,
globalInhibition=spParams["globalInhibition"],
localAreaDensity=spParams["localAreaDensity"],
# Roughly 2%, giving that there is only one inhibition area because we have
# turned on globalInhibition (40 / 2048 = 0.0195)
numActiveColumnsPerInhArea=40.0,
numActiveColumnsPerInhArea=spParams["numActiveColumnsPerInhArea"],
# How quickly synapses grow and degrade.
synPermInactiveDec=0.005,
synPermActiveInc=0.04,
synPermConnected=0.1,
synPermInactiveDec=spParams["synPermInactiveDec"],
synPermActiveInc=spParams["synPermActiveInc"],
synPermConnected=spParams["synPermConnected"],
# boostStrength controls the strength of boosting. Boosting encourages
# efficient usage of SP columns.
boostStrength=3.0,
boostStrength=spParams["boostStrength"],
# Random number generator seed.
seed=1956,
seed=spParams["seed"],
# TODO: is this useful?
# Determines if inputs at the beginning and end of an input dimension should
# be considered neighbors when mapping columns to inputs.
wrapAround=False
)

tm = TemporalMemory(
# Must be the same dimensions as the SP
columnDimensions=(2048, ),
columnDimensions=(tmParams["columnCount"],),
# How many cells in each mini-column.
cellsPerColumn=32,
cellsPerColumn=tmParams["cellsPerColumn"],
# A segment is active if it has >= activationThreshold connected synapses
# that are active due to infActiveState
activationThreshold=16,
initialPermanence=0.21,
connectedPermanence=0.5,
activationThreshold=tmParams["activationThreshold"],
initialPermanence=tmParams["initialPerm"],
# TODO: This comes from the SP params, is this normal
connectedPermanence=spParams["synPermConnected"],
# Minimum number of active synapses for a segment to be considered during
# search for the best-matching segments.
minThreshold=12,
minThreshold=tmParams["minThreshold"],
# The max number of synapses added to a segment during learning
maxNewSynapseCount=20,
permanenceIncrement=0.1,
permanenceDecrement=0.1,
maxNewSynapseCount=tmParams["newSynapseCount"],
permanenceIncrement=tmParams["permanenceInc"],
permanenceDecrement=tmParams["permanenceDec"],
predictedSegmentDecrement=0.0,
maxSegmentsPerCell=128,
maxSynapsesPerSegment=32,
seed=1960
maxSegmentsPerCell=tmParams["maxSegmentsPerCell"],
maxSynapsesPerSegment=tmParams["maxSynapsesPerSegment"],
seed=tmParams["seed"]
)

classifier = SDRClassifierFactory.create()

with open (_INPUT_FILE_PATH) as fin:
with open(_INPUT_FILE_PATH, "r") as fin:
reader = csv.reader(fin)
headers = reader.next()
reader.next()
reader.next()

for count, record in enumerate(reader):

if count > _NUM_RECORDS: return

# Convert data string into Python date object.
dateString = datetime.datetime.strptime(record[0], "%m/%d/%y %H:%M")
# Convert data value string into float.
Expand All @@ -107,7 +126,7 @@ def runHotgym():
# Create an array to represent active columns, all initially zero. This
# will be populated by the compute method below. It must have the same
# dimensions as the Spatial Pooler.
activeColumns = numpy.zeros(2048)
activeColumns = numpy.zeros(spParams["columnCount"])

# Execute Spatial Pooling algorithm over input space.
sp.compute(encoding, True, activeColumns)
Expand Down Expand Up @@ -141,5 +160,6 @@ def runHotgym():
print("1-step: {:16} ({:4.4}%)".format(value, probability * 100))



if __name__ == "__main__":
runHotgym()
154 changes: 154 additions & 0 deletions docs/examples/network/complete-example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import json
import os
import yaml

from nupic.engine import Network
from nupic.encoders import MultiEncoder
from nupic.data.file_record_stream import FileRecordStream

_NUM_RECORDS = 3000
_EXAMPLE_DIR = os.path.dirname(os.path.abspath(__file__))
_INPUT_FILE_PATH = os.path.join(_EXAMPLE_DIR, os.pardir, "data", "gymdata.csv")
_PARAMS_PATH = os.path.join(_EXAMPLE_DIR, os.pardir, "params", "model.yaml")



def createDataOutLink(network, sensorRegionName, regionName):
"""Link sensor region to other region so that it can pass it data."""
network.link(sensorRegionName, regionName, "UniformLink", "",
srcOutput="dataOut", destInput="bottomUpIn")


def createFeedForwardLink(network, regionName1, regionName2):
"""Create a feed-forward link between 2 regions: regionName1 -> regionName2"""
network.link(regionName1, regionName2, "UniformLink", "",
srcOutput="bottomUpOut", destInput="bottomUpIn")


def createResetLink(network, sensorRegionName, regionName):
"""Create a reset link from a sensor region: sensorRegionName -> regionName"""
network.link(sensorRegionName, regionName, "UniformLink", "",
srcOutput="resetOut", destInput="resetIn")


def createSensorToClassifierLinks(network, sensorRegionName,
classifierRegionName):
"""Create required links from a sensor region to a classifier region."""
network.link(sensorRegionName, classifierRegionName, "UniformLink", "",
srcOutput="bucketIdxOut", destInput="bucketIdxIn")
network.link(sensorRegionName, classifierRegionName, "UniformLink", "",
srcOutput="actValueOut", destInput="actValueIn")
network.link(sensorRegionName, classifierRegionName, "UniformLink", "",
srcOutput="categoryOut", destInput="categoryIn")


def createEncoder(encoderParams):
"""Create a multi-encoder from params."""
encoder = MultiEncoder()
encoder.addMultipleEncoders(encoderParams)
return encoder


def createNetwork(dataSource):
"""Create and initialize a network."""
with open(_PARAMS_PATH, "r") as f:
modelParams = yaml.safe_load(f)["modelParams"]

# Create a network that will hold the regions.
network = Network()

# Add a sensor region.
network.addRegion("sensor", "py.RecordSensor", '{}')

# Set the encoder and data source of the sensor region.
sensorRegion = network.regions["sensor"].getSelf()
sensorRegion.encoder = createEncoder(modelParams["sensorParams"]["encoders"])
sensorRegion.dataSource = dataSource

# Make sure the SP input width matches the sensor region output width.
modelParams["spParams"]["inputWidth"] = sensorRegion.encoder.getWidth()

# Add SP and TM regions.
network.addRegion("SP", "py.SPRegion", json.dumps(modelParams["spParams"]))
network.addRegion("TM", "py.TPRegion", json.dumps(modelParams["tmParams"]))

# Add a classifier region.
clName = "py.%s" % modelParams["clParams"].pop("regionName")
network.addRegion("classifier", clName, json.dumps(modelParams["clParams"]))

# Add all links
createSensorToClassifierLinks(network, "sensor", "classifier")
createDataOutLink(network, "sensor", "SP")
createFeedForwardLink(network, "SP", "TM")
createFeedForwardLink(network, "TM", "classifier")
# Reset links are optional, since the sensor region does not send resets.
createResetLink(network, "sensor", "SP")
createResetLink(network, "sensor", "TM")

# Make sure all objects are initialized.
network.initialize()

return network


def getPredictionResults(network, clRegionName):
"""Get prediction results for all prediction steps."""
classifierRegion = network.regions[clRegionName]
actualValues = classifierRegion.getOutputData("actualValues")
probabilities = classifierRegion.getOutputData("probabilities")
steps = classifierRegion.getSelf().stepsList
N = classifierRegion.getSelf().maxCategoryCount
results = {step: {} for step in steps}
for i in range(len(steps)):
# stepProbabilities are probabilities for this prediction step only.
stepProbabilities = probabilities[i * N:(i + 1) * N - 1]
mostLikelyCategoryIdx = stepProbabilities.argmax()
predictedValue = actualValues[mostLikelyCategoryIdx]
predictionConfidence = stepProbabilities[mostLikelyCategoryIdx]
results[steps[i]]["predictedValue"] = predictedValue
results[steps[i]]["predictionConfidence"] = predictionConfidence
return results


def runHotgym():
"""Run the Hot Gym example."""

# Create a data source for the network.
dataSource = FileRecordStream(streamID=_INPUT_FILE_PATH)
numRecords = min(_NUM_RECORDS, dataSource.getDataRowCount())
network = createNetwork(dataSource)

# Set predicted field index. It needs to be the same index as the data source.
predictedIdx = dataSource.getFieldNames().index("consumption")
network.regions["sensor"].setParameter("predictedFieldIdx", predictedIdx)

# Enable learning for all regions.
network.regions["SP"].setParameter("learningMode", 1)
network.regions["TM"].setParameter("learningMode", 1)
network.regions["classifier"].setParameter("learningMode", 1)

# Enable inference for all regions.
network.regions["SP"].setParameter("inferenceMode", 1)
network.regions["TM"].setParameter("inferenceMode", 1)
network.regions["classifier"].setParameter("inferenceMode", 1)

N = 1 # Run the network, N iterations at a time.
for iteration in range(0, numRecords, N):
network.run(N)

# Get prediction results.
results = getPredictionResults(network, "classifier")
oneStep = results[1]["predictedValue"]
oneStepConfidence = results[1]["predictionConfidence"]
fiveStep = results[5]["predictedValue"]
fiveStepConfidence = results[5]["predictionConfidence"]

print("1-step: {:16} ({:4.4}%)\t"
"5-step: {:16} ({:4.4}%)".format(oneStep,
oneStepConfidence * 100,
fiveStep,
fiveStepConfidence * 100))


if __name__ == "__main__":
runHotgym()
2 changes: 2 additions & 0 deletions docs/examples/network/example-add-classifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
clParams = modelParams["clParams"]
network.addRegion("classifier", "py.SDRClassifierRegion", json.dumps(clParams))
1 change: 1 addition & 0 deletions docs/examples/network/example-add-sensor-region.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
network.addRegion("sensor", "py.RecordSensor", '{}')
7 changes: 7 additions & 0 deletions docs/examples/network/example-add-sp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
spParams = modelParams["spParams"]

# Make sure the SP input width matches the sensor output width.
spParams["inputWidth"] = sensorRegion.encoder.getWidth()

# Add SP region.
network.addRegion("SP", "py.SPRegion", json.dumps(spParams))
2 changes: 2 additions & 0 deletions docs/examples/network/example-add-tm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
tmParams = modelParams["tmParams"]
network.addRegion("TM", "py.TPRegion", json.dumps(tmParams))
13 changes: 13 additions & 0 deletions docs/examples/network/example-create-encoder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from nupic.encoders import MultiEncoder

def createEncoder(encoderParams):
encoder = MultiEncoder()
encoder.addMultipleEncoders(encoderParams)
return encoder

# Use the same modelParams extracted from the YAML file earlier.
encoderParams = modelParams["sensorParams"]["encoders"]

# Add encoder to the sensor region.
sensorRegion = network.regions["sensor"].getSelf()
sensorRegion.encoder = createEncoder(encoderParams)
4 changes: 4 additions & 0 deletions docs/examples/network/example-create-network.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from nupic.engine import Network

# A network that will hold the regions.
network = Network()
Loading