Recoding htm.core's hotgym.py (I'm making many errors)

Hey Johan, thanks for the example, lovely stuff. It makes sense that we teach the predictor (which is a neural net used to convert the TM’s final-state SDR into a prediction, i think?) using .learn() with some data + a label at each iteration, and .infer() can be used to predict outputs from a given input state.

Everything I mention from here out is up on my github as ‘alternate_hotgym.ipynb’:

The hotgym.py code is a little different, it seems; they call predictor.infer() at each training iteration to store the prediction for use in anomaly detection (power consumption spikes etc) later on. This was giving me the aforementioned "must call learn before infer" error.

I was able to sidestep this error by adding another altered copy of the training loop immediately before the original loop, where I added a predictor.learn() right after the tm.compute() and ended it there:

predictor = Predictor(steps=[1,5], alpha=parameters['predictor']['sdrc_alpha'])
predictor_resolution = 1
inputs = []
anomaly = []
anomalyProb = []
predictions = {1: [], 5:[]}
for count, record in enumerate(records): # iterate through listified CSV
    dateString = datetime.datetime.strptime(record[0], '%m/%d/%y %H:%M')
    consumption = float(record[1])
    inputs.append(consumption)
    # encoders at work
    dateBits = dateEncoder.encode(dateString)
    consumptionBits = scalarEncoder.encode(consumption)
    # concatenate these bad boys for a composite x,y input
    encoding = SDR(encodingWidth).concatenate([consumptionBits, dateBits])
    enc_info.addData(encoding) # keep track in metrics
    # create an SDR for active columns, same dimensions as SP
    activeColumns = SDR(sp.getColumnDimensions())
    # hurl input into the pool
    sp.compute(encoding, True, activeColumns)
    tm_info.addData(tm.getActiveCells().flatten())
    # dunno why we add the TM active state before tm.compute(), but i don't know most things
    tm.compute(activeColumns, learn=True)
    # let's change things up and try to just let the predictor learn before making any predictions
    predictor.learn(count, tm.getActiveCells(), int(round(consumption/predictor_resolution)))

The second, original training loop is the same up to tm.compute(), where it replaces predictor.learn() with predictor.infer(); the original .learn() is right at the end:

for [...]:
    pdf = predictor.infer( tm.getActiveCells() )
    for n in (1,5):
        if pdf[n]:
            predictions[n].append( np.argmax( pdf[n] ) * predictor_resolution )
        else:
            predictions[n].append(float('nan'))
    
    anomalyLikelihood = anomaly_history.anomalyProbability( consumption, tm.anomaly )
    anomaly.append(tm.anomaly)
    anomalyProb.append(anomalyLikelihood)
    # original position of learn()
    predictor.learn(count, tm.getActiveCells(), int(consumption/predictor_resolution))

This works… well, maybe. I’ve attached the output graph.


Had a bothersome error where it was predicting all 0’s for anomalies, and then I realized I’d somehow deleted the tm.compute() from the 2nd original training loop.

There’s also some oddity with the prediction-shift loop:

# Shift the predictions so that they are aligned with the input they predict.
for n_steps, pred_list in predictions.items():
    for x in range(n_steps):
        pred_list.insert(0, float('nan'))
        pred_list.pop()

This inserts some 0’s and nans into the predictions and later crashes the accuracy_score calculation with a divide by 0:

acc_lists = {1: [], 5: []} # i added this one for graphing
# calculate predictive accuracy, RMS
accuracy         = {1: 0, 5: 0}
accuracy_samples = {1: 0, 5: 0}
for idx, inp in enumerate(inputs):
    for n in predictions: # for each [N]umber of timesteps ahead which was predicted
        val = predictions[n][idx]
        if not math.isnan(val):
            accuracy[n] += (inp - val) ** 2 # RMSE
            accuracy_samples[n] += 1
    for n in sorted(predictions):
#         print("accuracy[n]: ",accuracy[n], "    accuracy_samples[n]:  ",accuracy_samples[n])
        accuracy[n] = (accuracy[n] / accuracy_samples[n]) ** .5
#         print("Predictive Error (root-mean-squared): ", n, "steps ahead:", accuracy[n])
#         print()
        acc_lists[n].append(accuracy[n]) # add to lists
        
    print('Anomaly Mean: ', np.mean(anomaly))
    print('Anomaly Std: ', np.std(anomaly))

The div0 seems to happen near the end, with accuracy[n] = (accuracy[n] / accuracy_samples[n]) ** .5.
I tried skipping this loop entirely in the run, and it… kinda looks like it works. Dunno.

I’m not sure of the ramifications of my two workarounds; perhaps the added pre-training loop induces overfitting, perhaps the lack of prediction-shift means the predictions are misaligned and useless? Not sure.

If anyone’s got some feedback or ideas on how to improve this, please throw them my way. Looking through NuPIC’s docs, what with modelFactory and such, it feels like Keras to the htm.core’s PyTorch - though I certainly can’t complain about learning the more granular workings of HTM code.

2 Likes