/htm

Hierarchical Temporal Memory implementation in Golang

Primary LanguageGoMIT LicenseMIT

htm

Hierarchical Temporal Memory Implementation in Golang

GoDoc Build Status

This is a direct port of the spatial & temporal poolers, temporal memory, and encoders as they currently exist in Numenta's Nupic Project. This project was done as a learning exercise, no effort has been made to optimize this implementation and it was not designed for production use.

The Nupic project basically demonstrates the CLA, a single stage of the cortical hierarchy. Eventually this same code can be extended to form a full HTM hierarchy. https://github.com/numenta/nupic

##Changes From Numentas Implementation

  • Temporal pooler ephemeral state is stored in strongly typed struct rather than a hashmap. t-1 vars have "last" appended to their names.
  • Temporal pooler params stored in "params" sub struct
  • Binary data structures are used rather than ints
  • No C++ dependency everything is written in Go

##Current State of Project

  • Temporal and Spatial poolers pass basic tests
  • Temporal memory passes basic unit tests
  • Basic scaler encoder implemented

##Todo * Finish temporal unit tests

  • Implement a better sparse binary matrix structure with versions optimized for col or row heavy access.
  • Implement better binary datastructure
  • Refactor to be more idiomatic Go. It is basically a line for line port of the python implementation, it could be refactored to make better use of Go's type system.
  • Implement some of the common encoders

##Examples

###Temporal Pooler

package main

import (
	"fmt"
	"github.com/zacg/htm"
	"github.com/nupic-community/htmutils"
)

func main() {
	tps := htm.NewTemporalPoolerParams()
	tps.Verbosity = 0
	tps.NumberOfCols = 50
	tps.CellsPerColumn = 2
	tps.ActivationThreshold = 8
	tps.MinThreshold = 10
	tps.InitialPerm = 0.5
	tps.ConnectedPerm = 0.5
	tps.NewSynapseCount = 10
	tps.PermanenceDec = 0.0
	tps.PermanenceInc = 0.1
	tps.GlobalDecay = 0
	tps.BurnIn = 1
	tps.PamLength = 10
	tps.CollectStats = true
	tp := htm.NewTemporalPooler(*tps)

	//Mock encoding of ABCDE
	inputs := make([][]bool, 5)
	inputs[0] = boolRange(0, 9, 50)   //bits 0-9 are "on"
	inputs[1] = boolRange(10, 19, 50) //bits 10-19 are "on"
	inputs[2] = boolRange(20, 29, 50) //bits 20-29 are "on"
	inputs[3] = boolRange(30, 39, 50) //bits 30-39 are "on"
	inputs[4] = boolRange(40, 49, 50) //bits 40-49 are "on"

	//Learn 5 sequences above
	for i := 0; i < 10; i++ {
		for p := 0; p < 5; p++ {
			tp.Compute(inputs[p], true, false)
		}
		tp.Reset()
	}

	//Predict sequences
	for i := 0; i < 4; i++ {
		tp.Compute(inputs[i], false, true)
		p := tp.DynamicState.InfPredictedState

		fmt.Printf("Predicted: %v From input: %v \n", p.NonZeroRows(), utils.OnIndices(inputs[i]))

	}

}

//helper method for creating boolean sequences
func boolRange(start int, end int, length int) []bool {
	result := make([]bool, length)
	for i := start; i <= end; i++ {
		result[i] = true
	}
	return result
}

###Spatial Pooler

package main

import (
	"fmt"
	"github.com/davecheney/profile"
	"github.com/zacg/htm"
	"github.com/nupic-community/htmutils"
	"math/rand"
)

func main() {
	
	ssp := htm.NewSpParams()
	ssp.ColumnDimensions = []int{64, 64}
	ssp.InputDimensions = []int{32, 32}
	ssp.PotentialRadius = ssp.NumInputs()
	ssp.NumActiveColumnsPerInhArea = int(0.02 * float64(ssp.NumColumns()))
	ssp.GlobalInhibition = true
	ssp.SynPermActiveInc = 0.01
	ssp.SpVerbosity = 10
	sp := htm.NewSpatialPooler(ssp)
	

	activeArray := make([]bool, sp.NumColumns())
	inputVector := make([]bool, sp.NumInputs())

	for idx, _ := range inputVector {
		inputVector[idx] = rand.Intn(5) >= 2
	}

	sp.Compute(inputVector, true, activeArray, sp.InhibitColumns)

	fmt.Println("Active Indices:", utils.OnIndices(activeArray))

}

###Temporal Memory

	tmp := NewTemporalMemoryParams()
	tmp.MaxNewSynapseCount = 1000

	tm := NewTemporalMemory(tmp)

###Encoding

	//Create new scaler encoder
	p := NewScalerEncoderParams(3, 1, 8)
	p.Radius = 1.5
	p.Periodic = true
	p.Verbosity = 5
	e := NewScalerEncoder(p)

	//Encode "1"
	encoded := e.Encode(1, false)

	//Print results
	fmt.Printfn("1 Encoded as: %v", utils.Bool2Int(encoded))
	//Create new date encoder
	p := NewDateEncoderParams()
	p.SeasonWidth = 3
	p.DayOfWeekWidth = 1
	p.WeekendWidth = 3
	p.TimeOfDayWidth = 5
	p.Verbosity = 5
	de := NewDateEncoder(p)
	
	d := time.Date(2010, 11, 4, 14, 55, 0, 0, time.UTC)
	encoded := de.Encode(d)

	//Print results
	fmt.Printfn("%v Encoded as: %v", d, utils.Bool2Int(encoded))