NuPIC OPF - CPU Example

Hello Everyone,

I am new to HTM and trying to learn how to implement HTM in Python on my Windows machine. For some reasons, I cannot get Swarming to work on my machine (don’t worry about this), and I decided to use Algorithms API which doesn’t need swarming as far as I know. Before switching to Algorithms API, I tried to run the first example of OPF (CPU example) which doesn’t need swarming and it worked. However, the prediction graph was just passing the actual CPU usage with some delay.I did some research here, and found this post; Bad prediction in detecting cpu usage

in which I understood according to Matt Taylor that “HTM would never predict a sudden change in CPU usage with no precedent.” So, I decided to feed algorithm with a sine function which has pattern and can be predicted with human brain. The result was acceptable, but it still seems to be a copy of input with lower sample rate and delay. See the image.

I was just wondering if I could improve this performance or not.

“”“A simple client to read CPU usage and predict it in real time.”""

from collections import deque
import time

import psutil
import matplotlib.pyplot as plt

from nupic.data.inference_shifter import InferenceShifter
from nupic.frameworks.opf.model_factory import ModelFactory
import numpy as np

import model_params

SECONDS_PER_STEP = 0.005
WINDOW = 360

turn matplotlib interactive mode on (ion)

plt.ion()
fig = plt.figure()

plot title, legend, etc

plt.title(‘CPU prediction example’)
plt.xlabel(‘time [s]’)
plt.ylabel(‘CPU usage [%]’)

def runCPU():
“”“Poll CPU usage, make predictions, and plot the results. Runs forever.”""

Create the model for predicting CPU usage.

model = ModelFactory.create(model_params.MODEL_PARAMS)
model.enableInference({‘predictedField’: ‘cpu’})

The shifter will align prediction and actual values.

shifter = InferenceShifter()

Keep the last WINDOW predicted and actual values for plotting.

actHistory = deque([0.0] * WINDOW, maxlen=360)
predHistory = deque([0.0] * WINDOW, maxlen=360)

Initialize the plot lines that we will update with each new record.

actline, = plt.plot(range(WINDOW), actHistory)
predline, = plt.plot(range(WINDOW), predHistory)

Set the y-axis range.

actline.axes.set_ylim(0, 24)
predline.axes.set_ylim(0, 24)

initial_value = 0
incremnet_value = 2*np.pi/360
while True:
s = time.time()

# Get the CPU usage.
cpu = (2*(np.sin(initial_value/2*np.pi))) + 6
#psutil.cpu_percent()

initial_value +=incremnet_value

# Run the input through the model and shift the resulting prediction.
modelInput = {'cpu': cpu}
print(modelInput)
result = shifter.shift(model.run(modelInput))

# Update the trailing predicted and actual value deques.
inference = result.inferences['multiStepBestPredictions'][5]
if inference is not None:
  actHistory.append(result.rawInput['cpu'])
  predHistory.append(inference)

# Redraw the chart with the new data.
actline.set_ydata(actHistory)  # update the data
predline.set_ydata(predHistory)  # update the data
plt.draw()
plt.legend( ('actual','predicted') )

# Make sure we wait a total of 2 seconds per iteration.
try:
  plt.pause(SECONDS_PER_STEP)
except KeyboardInterrupt:
  break
  return

if name == “main”:
runCPU()

Figure_1_Sin

Here is the model_params.py which I didn’t modify for the sine function:

MODEL_PARAMS = {
# Type of model that the rest of these parameters apply to.
‘model’: “HTMPrediction”,

# Version that specifies the format of the config.
'version': 1,

'predictAheadTime': None,

# Model parameter dictionary.
'modelParams': {
    # The type of inference that this model will perform
    'inferenceType': 'TemporalMultiStep',

    'sensorParams': {
        # Sensor diagnostic output verbosity control;
        # if > 0: sensor region will print out on screen what it's sensing
        # at each step 0: silent; >=1: some info; >=2: more info;
        # >=3: even more info (see compute() in py/regions/RecordSensor.py)
        'verbosity' : 0,

        # CPU usage encoder.
        'encoders': {
            'cpu': {
                'fieldname': u'cpu',
                'n': 200,
                'name': u'cpu',
                'type': 'ScalarEncoder',
                'minval': 0.0,
                'maxval': 100.0,
                'w': 21
            }
        },

        # A dictionary specifying the period for automatically-generated
        # resets from a RecordSensor;
        #
        # None = disable automatically-generated resets (also disabled if
        # all of the specified values evaluate to 0).
        # Valid keys is the desired combination of the following:
        #   days, hours, minutes, seconds, milliseconds, microseconds, weeks
        #
        # Example for 1.5 days: sensorAutoReset = dict(days=1,hours=12),
        'sensorAutoReset' : None,
    },

    'spEnable': True,

    'spParams': {
        # SP diagnostic output verbosity control;
        # 0: silent; >=1: some info; >=2: more info;
        'spVerbosity' : 0,

        'globalInhibition': 1,

        # Number of cell columns in the cortical region (same number for
        # SP and TM)
        # (see also tpNCellsPerCol)
        'columnCount': 2048,

        'inputWidth': 0,

        # SP inhibition control (absolute value);
        # Maximum number of active columns in the SP region's output (when
        # there are more, the weaker ones are suppressed)
        'numActiveColumnsPerInhArea': 40,

        'seed': 1956,

        # potentialPct
        # What percent of the columns's receptive field is available
        # for potential synapses. At initialization time, we will
        # choose potentialPct * (2*potentialRadius+1)^2
        'potentialPct': 0.5,

        # The default connected threshold. Any synapse whose
        # permanence value is above the connected threshold is
        # a "connected synapse", meaning it can contribute to the
        # cell's firing. Typical value is 0.10. Cells whose activity
        # level before inhibition falls below minDutyCycleBeforeInh
        # will have their own internal synPermConnectedCell
        # threshold set below this default value.
        # (This concept applies to both SP and TM and so 'cells'
        # is correct here as opposed to 'columns')
        'synPermConnected': 0.1,

        'synPermActiveInc': 0.1,

        'synPermInactiveDec': 0.01,
    },

    # Controls whether TM is enabled or disabled;
    # TM is necessary for making temporal predictions, such as predicting
    # the next inputs.  Without TM, the model is only capable of
    # reconstructing missing sensor inputs (via SP).
    'tmEnable' : True,

    'tmParams': {
        # TM diagnostic output verbosity control;
        # 0: silent; [1..6]: increasing levels of verbosity
        # (see verbosity in nupic/trunk/py/nupic/research/backtracking_tm.py and backtracking_tm_cpp.py)
        'verbosity': 0,

        # Number of cell columns in the cortical region (same number for
        # SP and TM)
        # (see also tpNCellsPerCol)
        'columnCount': 2048,

        # The number of cells (i.e., states), allocated per column.
        'cellsPerColumn': 32,

        'inputWidth': 2048,

        'seed': 1960,

        # Temporal Pooler implementation selector (see _getTPClass in
        # CLARegion.py).
        'temporalImp': 'cpp',

        # New Synapse formation count
        # NOTE: If None, use spNumActivePerInhArea
        #
        # TODO: need better explanation
        'newSynapseCount': 20,

        # Maximum number of synapses per segment
        #  > 0 for fixed-size CLA
        # -1 for non-fixed-size CLA
        #
        # TODO: for Ron: once the appropriate value is placed in TM
        # constructor, see if we should eliminate this parameter from
        # description.py.
        'maxSynapsesPerSegment': 32,

        # Maximum number of segments per cell
        #  > 0 for fixed-size CLA
        # -1 for non-fixed-size CLA
        #
        # TODO: for Ron: once the appropriate value is placed in TM
        # constructor, see if we should eliminate this parameter from
        # description.py.
        'maxSegmentsPerCell': 128,

        # Initial Permanence
        # TODO: need better explanation
        'initialPerm': 0.21,

        # Permanence Increment
        'permanenceInc': 0.1,

        # Permanence Decrement
        # If set to None, will automatically default to tpPermanenceInc
        # value.
        'permanenceDec' : 0.1,

        'globalDecay': 0.0,

        'maxAge': 0,

        # Minimum number of active synapses for a segment to be considered
        # during search for the best-matching segments.
        # None=use default
        # Replaces: tpMinThreshold
        'minThreshold': 12,

        # Segment activation threshold.
        # A segment is active if it has >= tpSegmentActivationThreshold
        # connected synapses that are active due to infActiveState
        # None=use default
        # Replaces: tpActivationThreshold
        'activationThreshold': 16,

        'outputType': 'normal',

        # "Pay Attention Mode" length. This tells the TM how many new
        # elements to append to the end of a learned sequence at a time.
        # Smaller values are better for datasets with short sequences,
        # higher values are better for datasets with long sequences.
        'pamLength': 1,
    },

    'clParams': {
        # Classifier implementation selection.
        'implementation': 'cpp',

        'regionName' : 'SDRClassifierRegion',

        # Classifier diagnostic output verbosity control;
        # 0: silent; [1..6]: increasing levels of verbosity
        'verbosity' : 0,

        # This controls how fast the classifier learns/forgets. Higher values
        # make it adapt faster and forget older patterns faster.
        'alpha': 0.0001,

        # This is set after the call to updateConfigFromSubConfig and is
        # computed from the aggregationInfo and predictAheadTime.
        'steps': '5',
    },

    'trainSPNetOnlyIfRequested': False,
},

}

Thank you so much in advance,

kamyar

Why are the ‘minval’ & ‘maxval’ 0 & 100 for the sine wave case? From your plot it appears that it ranges from about 4 to 8 or so. I’d try changing the min & max to those smaller values and regenerate the plot.

Thank you for your help,

for some reasons, I can’t change the minval & maxval, it will throw an exception saying input out of range.
I increased the n and w to 1024 and 20 in encoder section in model_param.py file and it improved the resolution of prediction dramatically.

Figure_2_Sin_changed_n_w_2

Now, when I start the code ( as you see in the image), HTM model will start predicting the sine function very accurately right away. Is this correct?
since I though that the predictions will get more accurate once we let the code run for some time and let the model learn more about the input. However, it will predict accurately from the beginning without training the model first. I think I got a little bit confused now :))

Thanks,
Kamyar

Just so you know there’s a parameter to the scalar encoder which clips inputs outside the min/max - so any value below the min is treated as the min and same for the max. I think its saying out of range since your sequence starts at flatline 0 for a while.

  :param clipInput: if true, non-periodic inputs smaller than minval or greater
            than maxval will be clipped to minval/maxval

This makes sense to me since the sine wave is a simple sequence with no noise. Though FYI a better way to monitor the learning process of the algorithm is to plot the anomaly score. This will always start out flatlined at 1.0 and drop as the system familiarizes with the sequential patterns in the input.

A better way to truly measure this would be to plot the anomaly scores over time instead. With a noiseless sequence it should eventually flatline at 0 - and how long it takes to flatline depends on the length and complexity of the repeated noiseless sequence (like sine). If your sine wave is more granular (say it takes 100 time steps to repeat) that should take around 10x longer to flatline than if it only took 10 steps to repeat.

I’d also recommend injecting some noise into the sine wave to get a more realistic sense of the model behavior, since real data is rarely without noise – or ideally making a more complex sequence than a simple sine (maybe sine + tan or sine at different frequencies or something).

2 Likes

Thank you so much for all your help. I tried to observe Anomaly score and it went to zero very fast right after I started the process, even before the first period of sine was plotted. I also put some noise with/without the sine function and re-ran the code. As you see in the images that I captured, the predicted values are following the actual values not predicting them. It can even predict random noises! I set the “steps” parameter in clParams to 20 in order to compare both graphs better.
Figure_1 Figure_2

Even if you see the pure random noise graph, the predicted values are exactly the same as actual random noise values with 20 steps delay. I believe something is wrong with this prediction but I really can’t find it.

Thanks,
Kamyar

And one more thing, Shouldn’t my HTM model predict the next step? since It is now behind the actual values and doesn’t predict the next step.

This means there’s something wrong!! And I strongly suspect that something is that the encoders are mis-scoped. I mean that when all values range from 4-10, they are look very similar or identical to an encoder with min-max = 0-100.

When every value looks the same to the encoder, the TM is essentially seeing the sequence: A,A,A,A,A,…A,A,A. In this case the anomaly score will drop immediately because it learns immediately to predict A and only A - so its never surprised! Hence the anomaly score flatlines at 0 immediately.

To remedy this I strongly suggest tightening the min-max range substantially, maybe to 4-10 based on your latest chart.

If there are values below 4 or above 10, this can be handled by setting clipInput = True
in the ‘cpu’ encoder dict, alongside minval = 4 and maxval = 10.

I’d then regenerate your charts starting with the anomaly scores, which should drop more gradually, before eventually flatlining after a few periods of the sine wave. How many periods it will take for the anomaly score to flatline should grow linearly with the number of time steps in each period.