def testOverlapDistanceMethodBadSparsity(self): """Sparsity (input dimensionality) less than input array""" params = {"distanceMethod": "rawOverlap"} classifier = KNNClassifier(**params) a = np.array([1, 3, 7, 11, 13, 17, 19, 23, 29], dtype=np.int32) # Learn with incorrect dimensionality, less than some bits (23, 29) with self.assertRaises(AssertionError): classifier.learn(a, 0, isSparse=20)
def testGetPartitionIdWithNoIdsAtFirst(self): """ Tests that we can correctly retrieve partition Id even if the first few vectors do not have Ids """ params = {"distanceMethod": "rawOverlap"} classifier = KNNClassifier(**params) dimensionality = 40 a = np.array([1, 3, 7, 11, 13, 17, 19, 23, 29], dtype=np.int32) b = np.array([2, 4, 8, 12, 14, 18, 20, 28, 30], dtype=np.int32) c = np.array([1, 2, 3, 14, 16, 19, 22, 24, 33], dtype=np.int32) d = np.array([2, 4, 8, 12, 14, 19, 22, 24, 33], dtype=np.int32) denseA = np.zeros(dimensionality) denseA[a] = 1.0 denseD = np.zeros(dimensionality) denseD[d] = 1.0 classifier.learn(a, 0, isSparse=dimensionality, partitionId=None) classifier.learn(b, 1, isSparse=dimensionality, partitionId=None) classifier.learn(c, 2, isSparse=dimensionality, partitionId=211) classifier.learn(d, 1, isSparse=dimensionality, partitionId=405) cat, _, _, _ = classifier.infer(denseA, partitionId=405) self.assertEquals(cat, 0) cat, _, _, _ = classifier.infer(denseD, partitionId=405) self.assertEquals(cat, 2) cat, _, _, _ = classifier.infer(denseD) self.assertEquals(cat, 1)
def testOverlapDistanceMethodEmptyArray(self): """Tests case where pattern has no ON bits""" params = {"distanceMethod": "rawOverlap"} classifier = KNNClassifier(**params) dimensionality = 40 a = np.array([], dtype=np.int32) numPatterns = classifier.learn(a, 0, isSparse=dimensionality) self.assertEquals(numPatterns, 1) denseA = np.zeros(dimensionality) denseA[a] = 1.0 cat, _, _, _ = classifier.infer(denseA) self.assertEquals(cat, 0)
def run(): numObjects = 10 objSize = 10 numPasses = 2 allIndices = np.array(xrange(1024), dtype=np.uint32) objs = [(label, [[int(i) for i in np.random.choice(allIndices, 20)] for _ in xrange(objSize)]) for label in xrange(numObjects)] pooler = UnionTemporalPooler( inputDimensions=(1024, ), columnDimensions=(1024, ), potentialRadius=1024, potentialPct=0.8, globalInhibition=True, numActiveColumnsPerInhArea=20.0, #boostStrength=10.0, #dutyCyclePeriod=50, ) classifier = KNNClassifier(k=1, distanceMethod="rawOverlap") train(pooler, classifier, objs, numPasses) result = test(pooler, classifier, objs) print result
def testMinSparsity(self): """Tests overlap distance with min sparsity""" # Require sparsity >= 20% params = {"distanceMethod": "rawOverlap", "minSparsity": 0.2} classifier = KNNClassifier(**params) dimensionality = 30 a = np.array([1, 3, 7, 11, 13, 17, 19, 23, 29], dtype=np.int32) b = np.array([2, 4, 8, 12, 14, 18, 20, 21, 28], dtype=np.int32) # This has 20% sparsity and should be inserted c = np.array([2, 3, 8, 11, 14, 18], dtype=np.int32) # This has 17% sparsity and should NOT be inserted d = np.array([2, 3, 8, 11, 18], dtype=np.int32) numPatterns = classifier.learn(a, 0, isSparse=dimensionality) self.assertEquals(numPatterns, 1) numPatterns = classifier.learn(b, 1, isSparse=dimensionality) self.assertEquals(numPatterns, 2) numPatterns = classifier.learn(c, 1, isSparse=dimensionality) self.assertEquals(numPatterns, 3) numPatterns = classifier.learn(d, 1, isSparse=dimensionality) self.assertEquals(numPatterns, 3) # Test that inference ignores low sparsity vectors but not others e = np.array([2, 4, 5, 6, 8, 12, 14, 18, 20], dtype=np.int32) dense= np.zeros(dimensionality) dense[e] = 1.0 cat, inference, _, _ = classifier.infer(dense) self.assertIsNotNone(cat) self.assertGreater(inference.sum(),0.0) # This has 20% sparsity and should be used for inference f = np.array([2, 5, 8, 11, 14, 18], dtype=np.int32) dense= np.zeros(dimensionality) dense[f] = 1.0 cat, inference, _, _ = classifier.infer(dense) self.assertIsNotNone(cat) self.assertGreater(inference.sum(),0.0) # This has 17% sparsity and should return null inference results g = np.array([2, 3, 8, 11, 19], dtype=np.int32) dense= np.zeros(dimensionality) dense[g] = 1.0 cat, inference, _, _ = classifier.infer(dense) self.assertIsNone(cat) self.assertEqual(inference.sum(),0.0)
def simulateCategories(numSamples=100, numDimensions=500): """Simulate running KNN classifier on many disjoint categories""" failures = "" LOGGER.info( "Testing the sparse KNN Classifier on many disjoint categories") knn = KNNClassifier(k=1, distanceNorm=1.0, useSparseMemory=True) for i in range(0, numSamples): # select category randomly and generate vector c = 2 * numpy.random.randint(0, 50) + 50 v = createPattern(c, numDimensions) knn.learn(v, c) # Go through each category and ensure we have at least one from each! for i in range(0, 50): c = 2 * i + 50 v = createPattern(c, numDimensions) knn.learn(v, c) errors = 0 for i in range(0, numSamples): # select category randomly and generate vector c = 2 * numpy.random.randint(0, 50) + 50 v = createPattern(c, numDimensions) inferCat, _kir, _kd, _kcd = knn.infer(v) if inferCat != c: LOGGER.info("Mistake with %s %s %s %s %s", v[v.nonzero()], \ "mapped to category", inferCat, "instead of category", c) LOGGER.info(" %s", v.nonzero()) errors += 1 if errors != 0: failures += "Failure in handling non-consecutive category indices\n" # Test closest methods errors = 0 for i in range(0, 10): # select category randomly and generate vector c = 2 * numpy.random.randint(0, 50) + 50 v = createPattern(c, numDimensions) p = knn.closestTrainingPattern(v, c) if not (c in p.nonzero()[0]): LOGGER.info("Mistake %s %s", p.nonzero(), v.nonzero()) LOGGER.info("%s %s", p[p.nonzero()], v[v.nonzero()]) errors += 1 if errors != 0: failures += "Failure in closestTrainingPattern method\n" return failures, knn
def testOverlapDistanceMethodInconsistentDimensionality(self): """Inconsistent sparsity (input dimensionality)""" params = {"distanceMethod": "rawOverlap"} classifier = KNNClassifier(**params) dimensionality = 40 a = np.array([1, 3, 7, 11, 13, 17, 19, 23, 29], dtype=np.int32) # Learn with incorrect dimensionality, greater than largest ON bit, but # inconsistent when inferring numPatterns = classifier.learn(a, 0, isSparse=31) self.assertEquals(numPatterns, 1) denseA = np.zeros(dimensionality) denseA[a] = 1.0 cat, _, _, _ = classifier.infer(denseA) self.assertEquals(cat, 0)
def __init__(self, l4N, l4W, numModules, moduleDimensions, maxActivePerModule, l6ActivationThreshold): self.numModules = numModules self.moduleDimensions = moduleDimensions self._cellsPerModule = np.prod(moduleDimensions) self.maxActivePerModule = maxActivePerModule self.l4N = l4N self.l4W = l4W self.l6ActivationThreshold = l6ActivationThreshold self.l4TM = TemporalMemory( columnCount=l4N, basalInputSize=numModules * self._cellsPerModule, cellsPerColumn=4, #activationThreshold=int(numModules / 2) + 1, #reducedBasalThreshold=int(numModules / 2) + 1, activationThreshold=1, reducedBasalThreshold=1, initialPermanence=1.0, connectedPermanence=0.5, minThreshold=1, sampleSize=numModules, permanenceIncrement=1.0, permanenceDecrement=0.0, ) self.l6Connections = [ Connections(numCells=self._cellsPerModule) for _ in xrange(numModules) ] self.pooler = ColumnPooler(inputWidth=self.numModules * self._cellsPerModule, ) self.classifier = KNNClassifier(k=1, distanceMethod="rawOverlap") #self.classifier = KNNClassifier(k=1, distanceMethod="norm") # Active state self.activeL6Cells = [[] for _ in xrange(numModules)] self.activeL5Cells = [[] for _ in xrange(numModules)] self.predictedL6Cells = [set([]) for _ in xrange(numModules)] # Debug state self.activeL6BeforeMotor = [[] for _ in xrange(numModules)] self.l6ToL4Map = collections.defaultdict(list)
def simulateCategories(numSamples=100, numDimensions=500): """Simulate running KNN classifier on many disjoint categories""" failures = "" LOGGER.info("Testing the sparse KNN Classifier on many disjoint categories") knn = KNNClassifier(k=1, distanceNorm=1.0, useSparseMemory=True) for i in range(0, numSamples): # select category randomly and generate vector c = 2*numpy.random.randint(0, 50) + 50 v = createPattern(c, numDimensions) knn.learn(v, c) # Go through each category and ensure we have at least one from each! for i in range(0, 50): c = 2*i+50 v = createPattern(c, numDimensions) knn.learn(v, c) errors = 0 for i in range(0, numSamples): # select category randomly and generate vector c = 2*numpy.random.randint(0, 50) + 50 v = createPattern(c, numDimensions) inferCat, _kir, _kd, _kcd = knn.infer(v) if inferCat != c: LOGGER.info("Mistake with %s %s %s %s %s", v[v.nonzero()], \ "mapped to category", inferCat, "instead of category", c) LOGGER.info(" %s", v.nonzero()) errors += 1 if errors != 0: failures += "Failure in handling non-consecutive category indices\n" # Test closest methods errors = 0 for i in range(0, 10): # select category randomly and generate vector c = 2*numpy.random.randint(0, 50) + 50 v = createPattern(c, numDimensions) p = knn.closestTrainingPattern(v, c) if not (c in p.nonzero()[0]): LOGGER.info("Mistake %s %s", p.nonzero(), v.nonzero()) LOGGER.info("%s %s", p[p.nonzero()], v[v.nonzero()]) errors += 1 if errors != 0: failures += "Failure in closestTrainingPattern method\n" return failures, knn
def __init__(self, l4N, l4W, numModules, moduleDimensions, maxActivePerModule, l6ActivationThreshold): self.numModules = numModules self.moduleDimensions = moduleDimensions self._cellsPerModule = np.prod(moduleDimensions) self.maxActivePerModule = maxActivePerModule self.l4N = l4N self.l4W = l4W self.l6ActivationThreshold = l6ActivationThreshold self.l4TM = TemporalMemory( columnCount=l4N, basalInputSize=numModules*self._cellsPerModule, cellsPerColumn=4, #activationThreshold=int(numModules / 2) + 1, #reducedBasalThreshold=int(numModules / 2) + 1, activationThreshold=1, reducedBasalThreshold=1, initialPermanence=1.0, connectedPermanence=0.5, minThreshold=1, sampleSize=numModules, permanenceIncrement=1.0, permanenceDecrement=0.0, ) self.l6Connections = [Connections(numCells=self._cellsPerModule) for _ in xrange(numModules)] self.pooler = ColumnPooler( inputWidth=self.numModules*self._cellsPerModule, ) self.classifier = KNNClassifier(k=1, distanceMethod="rawOverlap") #self.classifier = KNNClassifier(k=1, distanceMethod="norm") # Active state self.activeL6Cells = [[] for _ in xrange(numModules)] self.activeL5Cells = [[] for _ in xrange(numModules)] self.predictedL6Cells = [set([]) for _ in xrange(numModules)] # Debug state self.activeL6BeforeMotor = [[] for _ in xrange(numModules)] self.l6ToL4Map = collections.defaultdict(list)
def testOverlapDistanceMethodStandardUnsorted(self): """If sparse representation indices are unsorted expect error.""" params = {"distanceMethod": "rawOverlap"} classifier = KNNClassifier(**params) dimensionality = 40 a = np.array([29, 3, 7, 11, 13, 17, 19, 23, 1], dtype=np.int32) b = np.array([2, 4, 20, 12, 14, 18, 8, 28, 30], dtype=np.int32) with self.assertRaises(AssertionError): classifier.learn(a, 0, isSparse=dimensionality) with self.assertRaises(AssertionError): classifier.learn(b, 1, isSparse=dimensionality)
def testOverlapDistanceMethod_ClassifySparse(self): params = {"distanceMethod": "rawOverlap"} classifier = KNNClassifier(**params) dimensionality = 40 a = np.array([1, 3, 7, 11, 13, 17, 19, 23, 29], dtype=np.int32) b = np.array([2, 4, 8, 12, 14, 18, 20, 28, 30], dtype=np.int32) classifier.learn(a, 0, isSparse=dimensionality) classifier.learn(b, 1, isSparse=dimensionality) # TODO Test case where infer is passed a sparse representation after # infer() has been extended to handle sparse and dense cat, _, _, _ = classifier.infer(a) self.assertEquals(cat, 0) cat, _, _, _ = classifier.infer(b) self.assertEquals(cat, 1)
def testOverlapDistanceMethodStandard(self): """Tests standard learning case for raw overlap""" params = {"distanceMethod": "rawOverlap"} classifier = KNNClassifier(**params) dimensionality = 40 a = np.array([1, 3, 7, 11, 13, 17, 19, 23, 29], dtype=np.int32) b = np.array([2, 4, 8, 12, 14, 18, 20, 28, 30], dtype=np.int32) numPatterns = classifier.learn(a, 0, isSparse=dimensionality) self.assertEquals(numPatterns, 1) numPatterns = classifier.learn(b, 1, isSparse=dimensionality) self.assertEquals(numPatterns, 2) denseA = np.zeros(dimensionality) denseA[a] = 1.0 cat, _, _, _ = classifier.infer(denseA) self.assertEquals(cat, 0) denseB = np.zeros(dimensionality) denseB[b] = 1.0 cat, _, _, _ = classifier.infer(denseB) self.assertEquals(cat, 1)
class RelationalMemory(object): def __init__(self, l4N, l4W, numModules, moduleDimensions, maxActivePerModule, l6ActivationThreshold): self.numModules = numModules self.moduleDimensions = moduleDimensions self._cellsPerModule = np.prod(moduleDimensions) self.maxActivePerModule = maxActivePerModule self.l4N = l4N self.l4W = l4W self.l6ActivationThreshold = l6ActivationThreshold self.l4TM = TemporalMemory( columnCount=l4N, basalInputSize=numModules*self._cellsPerModule, cellsPerColumn=4, #activationThreshold=int(numModules / 2) + 1, #reducedBasalThreshold=int(numModules / 2) + 1, activationThreshold=1, reducedBasalThreshold=1, initialPermanence=1.0, connectedPermanence=0.5, minThreshold=1, sampleSize=numModules, permanenceIncrement=1.0, permanenceDecrement=0.0, ) self.l6Connections = [Connections(numCells=self._cellsPerModule) for _ in xrange(numModules)] self.pooler = ColumnPooler( inputWidth=self.numModules*self._cellsPerModule, ) self.classifier = KNNClassifier(k=1, distanceMethod="rawOverlap") #self.classifier = KNNClassifier(k=1, distanceMethod="norm") # Active state self.activeL6Cells = [[] for _ in xrange(numModules)] self.activeL5Cells = [[] for _ in xrange(numModules)] self.predictedL6Cells = [set([]) for _ in xrange(numModules)] # Debug state self.activeL6BeforeMotor = [[] for _ in xrange(numModules)] self.l6ToL4Map = collections.defaultdict(list) def reset(self): self.activeL6Cells = [[] for _ in xrange(self.numModules)] self.activeL5Cells = [[] for _ in xrange(self.numModules)] self.predictedL6Cells = [set([]) for _ in xrange(self.numModules)] self.l4TM.reset() self.pooler.reset() def trainFeatures(self, sensoryInputs): # Randomly assign bilateral connections and zero others for sense in sensoryInputs: # Choose L6 cells randomly activeL6Cells = [[np.random.randint(self._cellsPerModule)] for _ in xrange(self.numModules)] l4BasalInput = getGlobalIndices(activeL6Cells, self._cellsPerModule) # Learn L6->L4 connections self.l4TM.compute(activeColumns=sense, basalInput=l4BasalInput, learn=True) self.l4TM.compute(activeColumns=sense, basalInput=l4BasalInput, learn=True) self.l4TM.compute(activeColumns=sense, basalInput=l4BasalInput, learn=True) self.l4TM.compute(activeColumns=sense, basalInput=l4BasalInput, learn=True) activeL4Cells = self.l4TM.getActiveCells() # Debug: store the map for l6Cell in itertools.chain(*activeL6Cells): self.l6ToL4Map[l6Cell].extend(activeL4Cells) # Learn L4->L6 connections for l6Cells, connections in zip(activeL6Cells, self.l6Connections): # Assumes one cell active per L6 module when training features segment = connections.createSegment(l6Cells[0]) for l4Cell in activeL4Cells: connections.createSynapse(segment, l4Cell, 1.0) def compute(self, ff, motor, objClass, outputFile): """Run one iteration of the online sensorimotor algorithm. This function has three stages: - The FEEDFORWARD pass drives Prerequisites: `trainFeatures` must have been run already :param ff: feedforward sensory input :param motor: the motor command for next move, in the form of delta coordinates :param objClass: the object class to train the classifier, or None if not learning """ delta = motor # FEEDFORWARD # Determine active feature representation in l4, using lateral input # from l6 previous step feedback l4BasalInput = getGlobalIndices(self.predictedL6Cells, self._cellsPerModule) self.l4TM.compute(activeColumns=ff, basalInput=l4BasalInput, learn=False) predictedL4Cells = self.l4TM.getPredictedCells() activeL4Cells = self.l4TM.getActiveCells() # Drive L6 activation from l4 for m, connections in enumerate(self.l6Connections): newCells = [] activeConnectedPerSegment = connections.computeActivity(activeL4Cells, 0.5)[0] for flatIdx, activeConnected in enumerate(activeConnectedPerSegment): if activeConnected >= self.l6ActivationThreshold: cellIdx = connections.segmentForFlatIdx(flatIdx).cell newCells.append(cellIdx) #for cell in newCells: # print connections.segmentsForCell(cell) #print newCells #assert len(newCells) <= 1 self.activeL6Cells[m].insert(0, newCells) # TODO: This is the number of steps, not necessarily the number of cells lenBefore = len(self.activeL6Cells[m]) del self.activeL6Cells[m][self.maxActivePerModule:] lenAfter = len(self.activeL6Cells[m]) #assert lenBefore == lenAfter, "Debug assert to check that we aren't hitting limit on L6 activity. Can remove when we set max active low enough relative to object size (times number of train/test iterations)" self.activeL6BeforeMotor = [list(itertools.chain(*l6Module)) for l6Module in self.activeL6Cells] # Replace l5 activity with new transforms self.activeL5Cells = [] for activeL6Module in self.activeL6Cells: transforms = set() for newCell in activeL6Module[0]: for prevCell in itertools.chain(*activeL6Module[1:]): if newCell == prevCell: continue # Transform from prev to new t1 = bind(prevCell, newCell, self.moduleDimensions) transforms.add(t1) # Transform from new to prev t2 = bind(newCell, prevCell, self.moduleDimensions) transforms.add(t2) self.activeL5Cells.append(list(transforms)) # Pool into object representation classifierLearn = True if objClass is not None else False globalL5ActiveCells = sorted(getGlobalIndices(self.activeL5Cells, self._cellsPerModule)) self.pooler.compute(feedforwardInput=globalL5ActiveCells, learn=classifierLearn, predictedInput=globalL5ActiveCells) # Classifier classifierInput = np.zeros((self.pooler.numberOfCells(),), dtype=np.uint32) classifierInput[self.pooler.getActiveCells()] = 1 #print classifierInput.nonzero() #print self.pooler.getActiveCells() #print self.prediction = self.classifier.infer(classifierInput) if objClass is not None: self.classifier.learn(classifierInput, objClass) # MOTOR # Update L6 based on motor command numActivePerModuleBefore = [sum([len(cells) for cells in active]) for active in self.activeL6Cells] self.activeL6Cells = [ [[pathIntegrate(c, self.moduleDimensions, delta) for c in steps] for steps in prevActiveCells] for prevActiveCells in self.activeL6Cells] numActivePerModuleAfter = [sum([len(cells) for cells in active]) for active in self.activeL6Cells] assert numActivePerModuleAfter == numActivePerModuleBefore # FEEDBACK # Get all transforms associated with object # TODO: Get transforms from object in addition to current activity predictiveTransforms = [l5Active for l5Active in self.activeL5Cells] # Get set of predicted l6 representations (including already active) # and store them for next step l4 compute self.predictedL6Cells = [] for l6, l5 in itertools.izip(self.activeL6Cells, predictiveTransforms): predictedCells = [] for activeL6Cell in set(itertools.chain(*l6)): for activeL5Cell in l5: predictedCell = unbind(activeL6Cell, activeL5Cell, self.moduleDimensions) predictedCells.append(predictedCell) self.predictedL6Cells.append(set( list(itertools.chain(*l6)) + predictedCells)) # Log this step if outputFile: log = RelationalMemoryLog.new_message() log.ts = time.time() sensationProto = log.init("sensation", len(ff)) for i in xrange(len(ff)): sensationProto[i] = int(ff[i]) predictedL4Proto = log.init("predictedL4", len(predictedL4Cells)) for i in xrange(len(predictedL4Cells)): predictedL4Proto[i] = int(predictedL4Cells[i]) activeL4Proto = log.init("activeL4", len(activeL4Cells)) for i in xrange(len(activeL4Cells)): activeL4Proto[i] = int(activeL4Cells[i]) activeL6HistoryProto = log.init("activeL6History", len(self.activeL6Cells)) for i in xrange(len(self.activeL6Cells)): activeL6ModuleProto = activeL6HistoryProto.init(i, len(self.activeL6Cells[i])) for j in xrange(len(self.activeL6Cells[i])): activeL6ModuleStepProto = activeL6ModuleProto.init(j, len(self.activeL6Cells[i][j])) for k in xrange(len(self.activeL6Cells[i][j])): activeL6ModuleStepProto[k] = int(self.activeL6Cells[i][j][k]) activeL5Proto = log.init("activeL5", len(self.activeL5Cells)) for i in xrange(len(self.activeL5Cells)): activeL5ModuleProto = activeL5Proto.init(i, len(self.activeL5Cells[i])) for j in xrange(len(self.activeL5Cells[i])): activeL5ModuleProto[j] = int(self.activeL5Cells[i][j]) classifierResults = [(i, distance) for i, distance in enumerate(self.prediction[2]) if distance is not None] classifierResultsProto = log.init("classifierResults", len(classifierResults)) for i in xrange(len(classifierResults)): classifierResultProto = classifierResultsProto[i] classifierResultProto.label = classifierResults[i][0] classifierResultProto.distance = float(classifierResults[i][1]) motorDeltaProto = log.init("motorDelta", len(delta)) for i in xrange(len(delta)): motorDeltaProto[i] = int(delta[i]) predictedL6Proto = log.init("predictedL6", len(self.predictedL6Cells)) for i in xrange(len(self.predictedL6Cells)): predictedL6ModuleProto = predictedL6Proto.init(i, len(self.predictedL6Cells[i])) for j, c in enumerate(self.predictedL6Cells[i]): predictedL6ModuleProto[j] = int(c) json.dump(log.to_dict(), outputFile) outputFile.write("\n")
class L4TMExperiment(L4L2Experiment): """ L4-TM combined sequences experiment. """ def __init__( self, name, numCorticalColumns=1, inputSize=1024, numInputBits=20, externalInputSize=1024, numExternalInputBits=20, L2Overrides=None, networkType="L4L2TMColumn", L4Overrides=None, seed=42, logCalls=False, objectNamesAreIndices=False, TMOverrides=None, ): """ Creates the network. Parameters: ---------------------------- @param TMOverrides (dict) Parameters to override in the TM region """ # Handle logging - this has to be done first self.logCalls = logCalls registerAllResearchRegions() self.name = name self.numLearningPoints = 1 self.numColumns = numCorticalColumns self.inputSize = inputSize self.externalInputSize = externalInputSize self.numInputBits = numInputBits self.objectNamesAreIndices = objectNamesAreIndices # seed self.seed = seed random.seed(seed) # update parameters with overrides self.config = { "networkType": networkType, "numCorticalColumns": numCorticalColumns, "externalInputSize": externalInputSize, "sensorInputSize": inputSize, "enableFeedback": False, "L4Params": self.getDefaultL4Params(inputSize, numExternalInputBits), "L2Params": self.getDefaultL2Params(inputSize, numInputBits), "TMParams": self.getDefaultTMParams(self.inputSize, self.numInputBits), } if L2Overrides is not None: self.config["L2Params"].update(L2Overrides) if L4Overrides is not None: self.config["L4Params"].update(L4Overrides) if TMOverrides is not None: self.config["TMParams"].update(TMOverrides) # Recreate network including TM parameters self.network = createNetwork(self.config) self.sensorInputs = [] self.externalInputs = [] self.L4Regions = [] self.L2Regions = [] self.TMRegions = [] for i in xrange(self.numColumns): self.sensorInputs.append(self.network.regions["sensorInput_" + str(i)].getSelf()) self.externalInputs.append(self.network.regions["externalInput_" + str(i)].getSelf()) self.L4Regions.append(self.network.regions["L4Column_" + str(i)]) self.L2Regions.append(self.network.regions["L2Column_" + str(i)]) self.TMRegions.append(self.network.regions["TMColumn_" + str(i)]) self.L4Columns = [region.getSelf() for region in self.L4Regions] self.L2Columns = [region.getSelf() for region in self.L2Regions] self.TMColumns = [region.getSelf() for region in self.TMRegions] # will be populated during training self.objectL2Representations = {} self.objectL2RepresentationsMatrices = [ SparseMatrix(0, self.config["L2Params"]["cellCount"]) for _ in xrange(self.numColumns) ] self.objectNameToIndex = {} self.statistics = [] # Create classifier to hold supposedly unique TM states self.classifier = KNNClassifier(distanceMethod="rawOverlap") self.numTMCells = (self.TMColumns[0].cellsPerColumn * self.TMColumns[0].columnCount) def getTMRepresentations(self): """ Returns the active representation in TM. """ return [ set(column.getOutputData("activeCells").nonzero()[0]) for column in self.TMRegions ] def getTMNextPredictedCells(self): """ Returns the cells in TM that were predicted at the end of the most recent call to 'compute'. """ return [ set(column.getOutputData("nextPredictedCells").nonzero()[0]) for column in self.TMRegions ] def getTMPredictedActiveCells(self): """ Returns the cells in TM that were predicted at the beginning of the most recent call to 'compute' and are currently active. """ return [ set(column.getOutputData("predictedActiveCells").nonzero()[0]) for column in self.TMRegions ] def getDefaultTMParams(self, inputSize, numInputBits): """ Returns a good default set of parameters to use in the TM region. """ sampleSize = int(1.5 * numInputBits) if numInputBits == 20: activationThreshold = 18 minThreshold = 18 elif numInputBits == 10: activationThreshold = 8 minThreshold = 8 else: activationThreshold = int(numInputBits * .6) minThreshold = activationThreshold return { "columnCount": inputSize, "cellsPerColumn": 16, "learn": True, "learnOnOneCell": False, "initialPermanence": 0.41, "connectedPermanence": 0.6, "permanenceIncrement": 0.1, "permanenceDecrement": 0.03, "minThreshold": minThreshold, "basalPredictedSegmentDecrement": 0.003, "apicalPredictedSegmentDecrement": 0.0, "reducedBasalThreshold": int(activationThreshold * 0.6), "activationThreshold": activationThreshold, "sampleSize": sampleSize, "implementation": "ApicalTiebreak", "seed": self.seed } def averageSequenceAccuracy(self, minOverlap, maxOverlap, firstStat=0, lastStat=None): """ For each object, decide whether the TM uniquely classified it by checking that the number of predictedActive cells are in an acceptable range. """ numCorrectSparsity = 0.0 numCorrectClassifications = 0.0 numStats = 0.0 # For each object or sequence we classify every point or element # # A sequence element is considered correctly classified only if the number # of predictedActive cells is within a reasonable range and if the KNN # Classifier correctly classifies the active cell representation as # belonging to this sequence. # # A point on an object is considered correctly classified by the TM if the # number of predictedActive cells is within range. for stats in self.statistics[firstStat:lastStat]: # Keep running total of how often the number of predictedActive cells are # in the range. We always skip the first (unpredictable) count. predictedActiveStat = stats["TM PredictedActive C0"][1:] TMRepresentationStat = stats["TM Full Representation C0"][1:] # print "\n-----------" # print stats["object"], predictedActiveStat for numCells, sdr in zip(predictedActiveStat, TMRepresentationStat): numStats += 1.0 # print "numCells: ", numCells if numCells in range(minOverlap, maxOverlap + 1): numCorrectSparsity += 1.0 # Check KNN Classifier sdr = list(sdr) sdr.sort() dense = numpy.zeros(self.numTMCells) dense[sdr] = 1.0 (winner, inferenceResult, dist, categoryDist) = \ self.classifier.infer(dense) # print sdr, winner, stats['object'], winner == stats['object'] # print categoryDist # print if winner == stats['object']: numCorrectClassifications += 1.0 if numStats == 0: return 0.0, 0.0 return ((numCorrectSparsity / numStats), (numCorrectClassifications / numStats)) def stripStats(self): """Remove detailed stats - needed for large experiment pools.""" for stat in self.statistics: stat.pop("TM Full Representation C0") stat.pop("L2 Full Representation C0") def _unsetLearningMode(self): """ Unsets the learning mode, to start inference. """ for region in self.TMRegions: region.setParameter("learn", False) super(L4TMExperiment, self)._unsetLearningMode() def _setLearningMode(self): """ Sets the learning mode. """ for region in self.TMRegions: region.setParameter("learn", True) super(L4TMExperiment, self)._setLearningMode() def _updateInferenceStats(self, statistics, objectName=None): """ Updates the inference statistics. Parameters: ---------------------------- @param statistics (dict) Dictionary in which to write the statistics @param objectName (str) Name of the inferred object, if known. Otherwise, set to None. """ L4Representations = self.getL4Representations() L4PredictedCells = self.getL4PredictedCells() L4PredictedActiveCells = self.getL4PredictedActiveCells() L2Representation = self.getL2Representations() TMPredictedActive = self.getTMPredictedActiveCells() TMNextPredicted = self.getTMNextPredictedCells() TMRepresentation = self.getTMRepresentations() for i in xrange(self.numColumns): statistics["L4 Representation C" + str(i)].append( len(L4Representations[i])) statistics["L4 Predicted C" + str(i)].append( len(L4PredictedCells[i])) statistics["L4 PredictedActive C" + str(i)].append( len(L4PredictedActiveCells[i])) statistics["L2 Representation C" + str(i)].append( len(L2Representation[i])) statistics["L4 Apical Segments C" + str(i)].append( len(self.L4Columns[i]._tm.getActiveApicalSegments())) statistics["L4 Basal Segments C" + str(i)].append( len(self.L4Columns[i]._tm.getActiveBasalSegments())) statistics["TM Basal Segments C" + str(i)].append( len(self.TMColumns[i]._tm.getActiveBasalSegments())) statistics["TM PredictedActive C" + str(i)].append( len(TMPredictedActive[i])) # The number of cells that are in predictive state as a result of this # input statistics["TM NextPredicted C" + str(i)].append( len(TMNextPredicted[i])) # The indices of all active cells in the TM statistics["TM Full Representation C" + str(i)].append( TMRepresentation[i]) # The indices of all active cells in the TM statistics["L2 Full Representation C" + str(i)].append( L2Representation[i]) # Insert exact TM representation into the classifier if the number of # predictive active cells is potentially unique (otherwise we say it # failed to correctly predict this step). if ((len(TMPredictedActive[i]) < 1.5 * self.numInputBits) and (len(TMPredictedActive[i]) > 0.5 * self.numInputBits)): sdr = list(TMPredictedActive[i]) sdr.sort() self.classifier.learn(sdr, objectName, isSparse=self.numTMCells) # add true overlap if objectName was provided if objectName in self.objectL2Representations: objectRepresentation = self.objectL2Representations[objectName] statistics["Overlap L2 with object C" + str(i)].append( len(objectRepresentation[i] & L2Representation[i]))
def runTestPCAKNN(self, short = 0): LOGGER.info('\nTesting PCA/k-NN classifier') LOGGER.info('Mode=%s', short) numDims = 10 numClasses = 10 k = 10 numPatternsPerClass = 100 numPatterns = int(.9 * numClasses * numPatternsPerClass) numTests = numClasses * numPatternsPerClass - numPatterns numSVDSamples = int(.1 * numPatterns) keep = 1 train_data, train_class, test_data, test_class = \ pca_knn_data.generate(numDims, numClasses, k, numPatternsPerClass, numPatterns, numTests, numSVDSamples, keep) pca_knn = KNNClassifier(k=k,numSVDSamples=numSVDSamples, numSVDDims=keep) knn = KNNClassifier(k=k) LOGGER.info('Training PCA k-NN') for i in range(numPatterns): knn.learn(train_data[i], train_class[i]) pca_knn.learn(train_data[i], train_class[i]) LOGGER.info('Testing PCA k-NN') numWinnerFailures = 0 numInferenceFailures = 0 numDistFailures = 0 numAbsErrors = 0 for i in range(numTests): winner, inference, dist, categoryDist = knn.infer(test_data[i]) pca_winner, pca_inference, pca_dist, pca_categoryDist \ = pca_knn.infer(test_data[i]) if winner != test_class[i]: numAbsErrors += 1 if pca_winner != winner: numWinnerFailures += 1 if (numpy.abs(pca_inference - inference) > 1e-4).any(): numInferenceFailures += 1 if (numpy.abs(pca_dist - dist) > 1e-4).any(): numDistFailures += 1 s0 = 100*float(numTests - numAbsErrors) / float(numTests) s1 = 100*float(numTests - numWinnerFailures) / float(numTests) s2 = 100*float(numTests - numInferenceFailures) / float(numTests) s3 = 100*float(numTests - numDistFailures) / float(numTests) LOGGER.info('PCA/k-NN success rate=%s%s', s0, '%') LOGGER.info('Winner success=%s%s', s1, '%') LOGGER.info('Inference success=%s%s', s2, '%') LOGGER.info('Distance success=%s%s', s3, '%') self.assertEqual(s1, 100.0, "PCA/k-NN test failed")
def simulateKMoreThanOne(): """A small test with k=3""" failures = "" LOGGER.info("Testing the sparse KNN Classifier with k=3") knn = KNNClassifier(k=3) v = numpy.zeros((6, 2)) v[0] = [1.0, 0.0] v[1] = [1.0, 0.2] v[2] = [1.0, 0.2] v[3] = [1.0, 2.0] v[4] = [1.0, 4.0] v[5] = [1.0, 4.5] knn.learn(v[0], 0) knn.learn(v[1], 0) knn.learn(v[2], 0) knn.learn(v[3], 1) knn.learn(v[4], 1) knn.learn(v[5], 1) winner, _inferenceResult, _dist, _categoryDist = knn.infer(v[0]) if winner != 0: failures += "Inference failed with k=3\n" winner, _inferenceResult, _dist, _categoryDist = knn.infer(v[2]) if winner != 0: failures += "Inference failed with k=3\n" winner, _inferenceResult, _dist, _categoryDist = knn.infer(v[3]) if winner != 0: failures += "Inference failed with k=3\n" winner, _inferenceResult, _dist, _categoryDist = knn.infer(v[5]) if winner != 1: failures += "Inference failed with k=3\n" if len(failures) == 0: LOGGER.info("Tests passed.") return failures
def testDistanceMetrics(self): classifier = KNNClassifier(distanceMethod="norm", distanceNorm=2.0) dimensionality = 40 protoA = np.array([0, 1, 3, 7, 11], dtype=np.int32) protoB = np.array([20, 28, 30], dtype=np.int32) classifier.learn(protoA, 0, isSparse=dimensionality) classifier.learn(protoB, 0, isSparse=dimensionality) # input is an arbitrary point, close to protoA, orthogonal to protoB input = np.zeros(dimensionality) input[:4] = 1.0 # input0 is used to test that the distance from a point to itself is 0 input0 = np.zeros(dimensionality) input0[protoA] = 1.0 # Test l2 norm metric _, _, dist, _ = classifier.infer(input) l2Distances = [0.65465367, 1.0] for actual, predicted in zip(l2Distances, dist): self.assertAlmostEqual( actual, predicted, places=5, msg="l2 distance norm is not calculated as expected.") _, _, dist0, _ = classifier.infer(input0) self.assertEqual( 0.0, dist0[0], msg="l2 norm did not calculate 0 distance as expected.") # Test l1 norm metric classifier.distanceNorm = 1.0 _, _, dist, _ = classifier.infer(input) l1Distances = [0.42857143, 1.0] for actual, predicted in zip(l1Distances, dist): self.assertAlmostEqual( actual, predicted, places=5, msg="l1 distance norm is not calculated as expected.") _, _, dist0, _ = classifier.infer(input0) self.assertEqual( 0.0, dist0[0], msg="l1 norm did not calculate 0 distance as expected.") # Test raw overlap metric classifier.distanceMethod = "rawOverlap" _, _, dist, _ = classifier.infer(input) rawOverlaps = [1, 4] for actual, predicted in zip(rawOverlaps, dist): self.assertEqual( actual, predicted, msg="Raw overlap is not calculated as expected.") _, _, dist0, _ = classifier.infer(input0) self.assertEqual( 0.0, dist0[0], msg="Raw overlap did not calculate 0 distance as expected.") # Test pctOverlapOfInput metric classifier.distanceMethod = "pctOverlapOfInput" _, _, dist, _ = classifier.infer(input) pctOverlaps = [0.25, 1.0] for actual, predicted in zip(pctOverlaps, dist): self.assertAlmostEqual( actual, predicted, places=5, msg="pctOverlapOfInput is not calculated as expected.") _, _, dist0, _ = classifier.infer(input0) self.assertEqual( 0.0, dist0[0], msg="pctOverlapOfInput did not calculate 0 distance as expected.") # Test pctOverlapOfProto metric classifier.distanceMethod = "pctOverlapOfProto" _, _, dist, _ = classifier.infer(input) pctOverlaps = [0.40, 1.0] for actual, predicted in zip(pctOverlaps, dist): self.assertAlmostEqual( actual, predicted, places=5, msg="pctOverlapOfProto is not calculated as expected.") _, _, dist0, _ = classifier.infer(input0) self.assertEqual( 0.0, dist0[0], msg="pctOverlapOfProto did not calculate 0 distance as expected.") # Test pctOverlapOfLarger metric classifier.distanceMethod = "pctOverlapOfLarger" _, _, dist, _ = classifier.infer(input) pctOverlaps = [0.40, 1.0] for actual, predicted in zip(pctOverlaps, dist): self.assertAlmostEqual( actual, predicted, places=5, msg="pctOverlapOfLarger is not calculated as expected.") _, _, dist0, _ = classifier.infer(input0) self.assertEqual( 0.0, dist0[0], msg="pctOverlapOfLarger did not calculate 0 distance as expected.")
def testWriteRead(self): knn = KNNClassifier(distanceMethod="norm", numSVDDims=2, numSVDSamples=2, useSparseMemory=True, minSparsity=0.1, distThreshold=0.1) dimensionality = 40 a = np.array([1, 3, 7, 11, 13, 17, 19, 23, 29], dtype=np.int32) b = np.array([2, 4, 8, 12, 14, 18, 20, 28, 30], dtype=np.int32) c = np.array([1, 2, 3, 14, 16, 19, 22, 24, 33], dtype=np.int32) d = np.array([2, 4, 8, 12, 14, 19, 22, 24, 33], dtype=np.int32) knn.learn(a, 0, isSparse=dimensionality, partitionId=None) knn.learn(b, 1, isSparse=dimensionality, partitionId=None) knn.learn(c, 2, isSparse=dimensionality, partitionId=211) knn.learn(d, 1, isSparse=dimensionality, partitionId=405) knn.finishLearning() proto = KNNClassifierProto.new_message() knn.write(proto) with tempfile.TemporaryFile() as f: proto.write(f) f.seek(0) protoDeserialized = KNNClassifierProto.read(f) knnDeserialized = KNNClassifier.read(protoDeserialized) denseA = np.zeros(dimensionality) denseA[a] = 1.0 expected = knn.infer(denseA) actual = knnDeserialized.infer(denseA) self.assertEqual(expected[0], actual[0]) self.assertItemsEqual(expected[1], actual[1]) self.assertItemsEqual(expected[2], actual[2]) self.assertItemsEqual(expected[3], actual[3]) self.assertItemsEqual(knn.getPartitionIdList(), knnDeserialized.getPartitionIdList())
class RelationalMemory(object): def __init__(self, l4N, l4W, numModules, moduleDimensions, maxActivePerModule, l6ActivationThreshold): self.numModules = numModules self.moduleDimensions = moduleDimensions self._cellsPerModule = np.prod(moduleDimensions) self.maxActivePerModule = maxActivePerModule self.l4N = l4N self.l4W = l4W self.l6ActivationThreshold = l6ActivationThreshold self.l4TM = TemporalMemory( columnCount=l4N, basalInputSize=numModules * self._cellsPerModule, cellsPerColumn=4, #activationThreshold=int(numModules / 2) + 1, #reducedBasalThreshold=int(numModules / 2) + 1, activationThreshold=1, reducedBasalThreshold=1, initialPermanence=1.0, connectedPermanence=0.5, minThreshold=1, sampleSize=numModules, permanenceIncrement=1.0, permanenceDecrement=0.0, ) self.l6Connections = [ Connections(numCells=self._cellsPerModule) for _ in xrange(numModules) ] #self.classifier = KNNClassifier(k=1, distanceMethod="rawOverlap") self.classifier = KNNClassifier(k=1, distanceMethod="norm") # Active state self.activeL6Cells = [[] for _ in xrange(numModules)] self.activeL5Cells = [[] for _ in xrange(numModules)] self.predictedL6Cells = [set([]) for _ in xrange(numModules)] # Debug state self.activeL6BeforeMotor = [[] for _ in xrange(numModules)] self.l6ToL4Map = collections.defaultdict(list) def reset(self): self.activeL6Cells = [[] for _ in xrange(self.numModules)] self.activeL5Cells = [[] for _ in xrange(self.numModules)] self.predictedL6Cells = [set([]) for _ in xrange(self.numModules)] def trainFeatures(self, sensoryInputs): # Randomly assign bilateral connections and zero others for sense in sensoryInputs: # Choose L6 cells randomly activeL6Cells = [[np.random.randint(self._cellsPerModule)] for _ in xrange(self.numModules)] l4BasalInput = getGlobalIndices(activeL6Cells, self._cellsPerModule) # Learn L6->L4 connections self.l4TM.compute(activeColumns=sense, basalInput=l4BasalInput, learn=True) self.l4TM.compute(activeColumns=sense, basalInput=l4BasalInput, learn=True) self.l4TM.compute(activeColumns=sense, basalInput=l4BasalInput, learn=True) self.l4TM.compute(activeColumns=sense, basalInput=l4BasalInput, learn=True) activeL4Cells = self.l4TM.getActiveCells() # Debug: store the map for l6Cell in itertools.chain(*activeL6Cells): self.l6ToL4Map[l6Cell].extend(activeL4Cells) # Learn L4->L6 connections for l6Cells, connections in zip(activeL6Cells, self.l6Connections): # Assumes one cell active per L6 module when training features segment = connections.createSegment(l6Cells[0]) for l4Cell in activeL4Cells: connections.createSynapse(segment, l4Cell, 1.0) def compute(self, ff, motor, objClass, outputFile): """Run one iteration of the online sensorimotor algorithm. This function has three stages: - The FEEDFORWARD pass drives Prerequisites: `trainFeatures` must have been run already :param ff: feedforward sensory input :param motor: the motor command for next move, in the form of delta coordinates :param objClass: the object class to train the classifier, or None if not learning """ delta = motor # FEEDFORWARD # Determine active feature representation in l4, using lateral input # from l6 previous step feedback l4BasalInput = getGlobalIndices(self.predictedL6Cells, self._cellsPerModule) self.l4TM.compute(activeColumns=ff, basalInput=l4BasalInput, learn=False) predictedL4Cells = self.l4TM.getPredictedCells() activeL4Cells = self.l4TM.getActiveCells() # Drive L6 activation from l4 for m, connections in enumerate(self.l6Connections): newCells = [] activeConnectedPerSegment = connections.computeActivity( activeL4Cells, 0.5)[0] for flatIdx, activeConnected in enumerate( activeConnectedPerSegment): if activeConnected >= self.l6ActivationThreshold: cellIdx = connections.segmentForFlatIdx(flatIdx).cell newCells.append(cellIdx) #for cell in newCells: # print connections.segmentsForCell(cell) #print newCells #assert len(newCells) <= 1 self.activeL6Cells[m].insert(0, newCells) # TODO: This is the number of steps, not necessarily the number of cells lenBefore = len(self.activeL6Cells[m]) del self.activeL6Cells[m][self.maxActivePerModule:] lenAfter = len(self.activeL6Cells[m]) #assert lenBefore == lenAfter, "Debug assert to check that we aren't hitting limit on L6 activity. Can remove when we set max active low enough relative to object size (times number of train/test iterations)" self.activeL6BeforeMotor = [ list(itertools.chain(*l6Module)) for l6Module in self.activeL6Cells ] # Replace l5 activity with new transforms self.activeL5Cells = [] for activeL6Module in self.activeL6Cells: transforms = set() for newCell in activeL6Module[0]: for prevCell in itertools.chain(*activeL6Module[1:]): if newCell == prevCell: continue # Transform from prev to new t1 = bind(prevCell, newCell, self.moduleDimensions) transforms.add(t1) # Transform from new to prev t2 = bind(newCell, prevCell, self.moduleDimensions) transforms.add(t2) self.activeL5Cells.append(list(transforms)) # Pool into object representation globalL5ActiveCells = getGlobalIndices(self.activeL5Cells, self._cellsPerModule) denseL5 = np.zeros(self._cellsPerModule * self.numModules, dtype="bool") denseL5[globalL5ActiveCells] = 1 self.prediction = self.classifier.infer(denseL5) if objClass is not None: self.classifier.learn(denseL5, objClass) #print globalL5ActiveCells # MOTOR # Update L6 based on motor command numActivePerModuleBefore = [ sum([len(cells) for cells in active]) for active in self.activeL6Cells ] self.activeL6Cells = [[[ pathIntegrate(c, self.moduleDimensions, delta) for c in steps ] for steps in prevActiveCells] for prevActiveCells in self.activeL6Cells] numActivePerModuleAfter = [ sum([len(cells) for cells in active]) for active in self.activeL6Cells ] assert numActivePerModuleAfter == numActivePerModuleBefore # FEEDBACK # Get all transforms associated with object # TODO: Get transforms from object in addition to current activity predictiveTransforms = [l5Active for l5Active in self.activeL5Cells] # Get set of predicted l6 representations (including already active) # and store them for next step l4 compute self.predictedL6Cells = [] for l6, l5 in itertools.izip(self.activeL6Cells, predictiveTransforms): predictedCells = [] for activeL6Cell in set(itertools.chain(*l6)): for activeL5Cell in l5: predictedCell = unbind(activeL6Cell, activeL5Cell, self.moduleDimensions) predictedCells.append(predictedCell) self.predictedL6Cells.append( set(list(itertools.chain(*l6)) + predictedCells)) # Log this step if outputFile: log = RelationalMemoryLog.new_message() log.ts = time.time() sensationProto = log.init("sensation", len(ff)) for i in xrange(len(ff)): sensationProto[i] = int(ff[i]) predictedL4Proto = log.init("predictedL4", len(predictedL4Cells)) for i in xrange(len(predictedL4Cells)): predictedL4Proto[i] = int(predictedL4Cells[i]) activeL4Proto = log.init("activeL4", len(activeL4Cells)) for i in xrange(len(activeL4Cells)): activeL4Proto[i] = int(activeL4Cells[i]) activeL6HistoryProto = log.init("activeL6History", len(self.activeL6Cells)) for i in xrange(len(self.activeL6Cells)): activeL6ModuleProto = activeL6HistoryProto.init( i, len(self.activeL6Cells[i])) for j in xrange(len(self.activeL6Cells[i])): activeL6ModuleStepProto = activeL6ModuleProto.init( j, len(self.activeL6Cells[i][j])) for k in xrange(len(self.activeL6Cells[i][j])): activeL6ModuleStepProto[k] = int( self.activeL6Cells[i][j][k]) activeL5Proto = log.init("activeL5", len(self.activeL5Cells)) for i in xrange(len(self.activeL5Cells)): activeL5ModuleProto = activeL5Proto.init( i, len(self.activeL5Cells[i])) for j in xrange(len(self.activeL5Cells[i])): activeL5ModuleProto[j] = int(self.activeL5Cells[i][j]) classifierResults = [ (i, distance) for i, distance in enumerate(self.prediction[2]) if distance is not None ] classifierResultsProto = log.init("classifierResults", len(classifierResults)) for i in xrange(len(classifierResults)): classifierResultProto = classifierResultsProto[i] classifierResultProto.label = classifierResults[i][0] classifierResultProto.distance = float(classifierResults[i][1]) motorDeltaProto = log.init("motorDelta", len(delta)) for i in xrange(len(delta)): motorDeltaProto[i] = int(delta[i]) predictedL6Proto = log.init("predictedL6", len(self.predictedL6Cells)) for i in xrange(len(self.predictedL6Cells)): predictedL6ModuleProto = predictedL6Proto.init( i, len(self.predictedL6Cells[i])) for j, c in enumerate(self.predictedL6Cells[i]): predictedL6ModuleProto[j] = int(c) json.dump(log.to_dict(), outputFile) outputFile.write("\n")
def runTestPCAKNN(self, short=0): LOGGER.info('\nTesting PCA/k-NN classifier') LOGGER.info('Mode=%s', short) numDims = 10 numClasses = 10 k = 10 numPatternsPerClass = 100 numPatterns = int(.9 * numClasses * numPatternsPerClass) numTests = numClasses * numPatternsPerClass - numPatterns numSVDSamples = int(.1 * numPatterns) keep = 1 train_data, train_class, test_data, test_class = \ pca_knn_data.generate(numDims, numClasses, k, numPatternsPerClass, numPatterns, numTests, numSVDSamples, keep) pca_knn = KNNClassifier(k=k, numSVDSamples=numSVDSamples, numSVDDims=keep) knn = KNNClassifier(k=k) LOGGER.info('Training PCA k-NN') for i in range(numPatterns): knn.learn(train_data[i], train_class[i]) pca_knn.learn(train_data[i], train_class[i]) LOGGER.info('Testing PCA k-NN') numWinnerFailures = 0 numInferenceFailures = 0 numDistFailures = 0 numAbsErrors = 0 for i in range(numTests): winner, inference, dist, categoryDist = knn.infer(test_data[i]) pca_winner, pca_inference, pca_dist, pca_categoryDist \ = pca_knn.infer(test_data[i]) if winner != test_class[i]: numAbsErrors += 1 if pca_winner != winner: numWinnerFailures += 1 if (numpy.abs(pca_inference - inference) > 1e-4).any(): numInferenceFailures += 1 if (numpy.abs(pca_dist - dist) > 1e-4).any(): numDistFailures += 1 s0 = 100 * float(numTests - numAbsErrors) / float(numTests) s1 = 100 * float(numTests - numWinnerFailures) / float(numTests) s2 = 100 * float(numTests - numInferenceFailures) / float(numTests) s3 = 100 * float(numTests - numDistFailures) / float(numTests) LOGGER.info('PCA/k-NN success rate=%s%s', s0, '%') LOGGER.info('Winner success=%s%s', s1, '%') LOGGER.info('Inference success=%s%s', s2, '%') LOGGER.info('Distance success=%s%s', s3, '%') self.assertEqual(s1, 100.0, "PCA/k-NN test failed")
def testPartitionIdExcluded(self): """ Tests that paritionId properly excludes training data points during inference """ params = {"distanceMethod": "rawOverlap"} classifier = KNNClassifier(**params) dimensionality = 40 a = np.array([1, 3, 7, 11, 13, 17, 19, 23, 29], dtype=np.int32) b = np.array([2, 4, 8, 12, 14, 18, 20, 28, 30], dtype=np.int32) denseA = np.zeros(dimensionality) denseA[a] = 1.0 denseB = np.zeros(dimensionality) denseB[b] = 1.0 classifier.learn(a, 0, isSparse=dimensionality, partitionId=0) classifier.learn(b, 1, isSparse=dimensionality, partitionId=1) cat, _, _, _ = classifier.infer(denseA, partitionId=1) self.assertEquals(cat, 0) cat, _, _, _ = classifier.infer(denseA, partitionId=0) self.assertEquals(cat, 1) cat, _, _, _ = classifier.infer(denseB, partitionId=0) self.assertEquals(cat, 1) cat, _, _, _ = classifier.infer(denseB, partitionId=1) self.assertEquals(cat, 0) # Ensure it works even if you invoke learning again. To make it a bit more # complex this time we insert A again but now with Id=2 classifier.learn(a, 0, isSparse=dimensionality, partitionId=2) # Even though first A should be ignored, the second instance of A should # not be ignored. cat, _, _, _ = classifier.infer(denseA, partitionId=0) self.assertEquals(cat, 0)
def runTestKNNClassifier(self, short=0): """ Test the KNN classifier in this module. short can be: 0 (short), 1 (medium), or 2 (long) """ failures = "" if short != 2: numpy.random.seed(42) else: seed_value = int(time.time()) numpy.random.seed(seed_value) LOGGER.info('Seed used: %d', seed_value) f = open('seedval', 'a') f.write(str(seed_value)) f.write('\n') f.close() failures += simulateKMoreThanOne() LOGGER.info("\nTesting KNN Classifier on dense patterns") numPatterns, numClasses = getNumTestPatterns(short) patternSize = 100 patterns = numpy.random.rand(numPatterns, patternSize) patternDict = dict() testDict = dict() # Assume there are no repeated patterns -- if there are, then # numpy.random would be completely broken. # Patterns in testDict are identical to those in patternDict but for the # first 2% of items. for i in range(numPatterns): patternDict[i] = dict() patternDict[i]['pattern'] = patterns[i] patternDict[i]['category'] = numpy.random.randint( 0, numClasses - 1) testDict[i] = copy.deepcopy(patternDict[i]) testDict[i]['pattern'][:int(0.02 * patternSize)] = numpy.random.rand() testDict[i]['category'] = None LOGGER.info("\nTesting KNN Classifier with L2 norm") knn = KNNClassifier(k=1) failures += simulateClassifier(knn, patternDict, \ "KNN Classifier with L2 norm test") LOGGER.info("\nTesting KNN Classifier with L1 norm") knnL1 = KNNClassifier(k=1, distanceNorm=1.0) failures += simulateClassifier(knnL1, patternDict, \ "KNN Classifier with L1 norm test") # Test with exact matching classifications. LOGGER.info( "\nTesting KNN Classifier with exact matching. For testing we " "slightly alter the training data and expect None to be returned for the " "classifications.") knnExact = KNNClassifier(k=1, exact=True) failures += simulateClassifier( knnExact, patternDict, "KNN Classifier with exact matching test", testDict=testDict) numPatterns, numClasses = getNumTestPatterns(short) patterns = (numpy.random.rand(numPatterns, 25) > 0.7).astype(RealNumpyDType) patternDict = dict() for i in patterns: iString = str(i.tolist()) if iString not in patternDict: randCategory = numpy.random.randint(0, numClasses - 1) patternDict[iString] = dict() patternDict[iString]['pattern'] = i patternDict[iString]['category'] = randCategory LOGGER.info("\nTesting KNN on sparse patterns") knnDense = KNNClassifier(k=1) failures += simulateClassifier(knnDense, patternDict, \ "KNN Classifier on sparse pattern test") self.assertEqual(len(failures), 0, "Tests failed: \n" + failures) if short == 2: f = open('seedval', 'a') f.write('Pass\n') f.close()
def testGetPartitionId(self): """ Test a sequence of calls to KNN to ensure we can retrieve partition Id: - We first learn on some patterns (including one pattern with no partitionId in the middle) and test that we can retrieve Ids. - We then invoke inference and then check partitionId again. - We check incorrect indices to ensure we get an exception. - We check the case where the partitionId to be ignored is not in the list. - We learn on one more pattern and check partitionIds again - We remove rows and ensure partitionIds still work """ params = {"distanceMethod": "rawOverlap"} classifier = KNNClassifier(**params) dimensionality = 40 a = np.array([1, 3, 7, 11, 13, 17, 19, 23, 29], dtype=np.int32) b = np.array([2, 4, 8, 12, 14, 18, 20, 28, 30], dtype=np.int32) c = np.array([1, 2, 3, 14, 16, 19, 22, 24, 33], dtype=np.int32) d = np.array([2, 4, 8, 12, 14, 19, 22, 24, 33], dtype=np.int32) e = np.array([1, 3, 7, 12, 14, 19, 22, 24, 33], dtype=np.int32) denseA = np.zeros(dimensionality) denseA[a] = 1.0 classifier.learn(a, 0, isSparse=dimensionality, partitionId=433) classifier.learn(b, 1, isSparse=dimensionality, partitionId=213) classifier.learn(c, 1, isSparse=dimensionality, partitionId=None) classifier.learn(d, 1, isSparse=dimensionality, partitionId=433) self.assertEquals(classifier.getPartitionId(0), 433) self.assertEquals(classifier.getPartitionId(1), 213) self.assertEquals(classifier.getPartitionId(2), None) self.assertEquals(classifier.getPartitionId(3), 433) cat, _, _, _ = classifier.infer(denseA, partitionId=213) self.assertEquals(cat, 0) # Test with patternId not in classifier cat, _, _, _ = classifier.infer(denseA, partitionId=666) self.assertEquals(cat, 0) # Partition Ids should be maintained after inference self.assertEquals(classifier.getPartitionId(0), 433) self.assertEquals(classifier.getPartitionId(1), 213) self.assertEquals(classifier.getPartitionId(2), None) self.assertEquals(classifier.getPartitionId(3), 433) # Should return exceptions if we go out of bounds with self.assertRaises(RuntimeError): classifier.getPartitionId(4) with self.assertRaises(RuntimeError): classifier.getPartitionId(-1) # Learn again classifier.learn(e, 4, isSparse=dimensionality, partitionId=413) self.assertEquals(classifier.getPartitionId(4), 413) # Test getPatternIndicesWithPartitionId self.assertItemsEqual(classifier.getPatternIndicesWithPartitionId(433), [0, 3]) self.assertItemsEqual(classifier.getPatternIndicesWithPartitionId(666), []) self.assertItemsEqual(classifier.getPatternIndicesWithPartitionId(413), [4]) self.assertEquals(classifier.getNumPartitionIds(), 3) # Check that the full set of partition ids is what we expect self.assertItemsEqual(classifier.getPartitionIdList(), [433, 213, np.inf, 433, 413]) self.assertItemsEqual(classifier.getPartitionIdKeys(), [433, 413, 213]) # Remove two rows - all indices shift down self.assertEquals(classifier._removeRows([0,2]), 2) self.assertItemsEqual(classifier.getPatternIndicesWithPartitionId(433), [1]) self.assertItemsEqual(classifier.getPatternIndicesWithPartitionId(413), [2]) # Remove another row and check number of partitions have decreased classifier._removeRows([0]) self.assertEquals(classifier.getNumPartitionIds(), 2) # Check that the full set of partition ids is what we expect self.assertItemsEqual(classifier.getPartitionIdList(), [433, 413]) self.assertItemsEqual(classifier.getPartitionIdKeys(), [433, 413])
def testSparsifyVector(self): classifier = KNNClassifier(distanceMethod="norm", distanceNorm=2.0) inputPattern = np.array([0, 1, 3, 7, 11], dtype=np.int32) # Each of the 4 tests correspond with the each decisional branch in the # sparsifyVector method # # tests: if not self.relativeThreshold: outputPattern = classifier._sparsifyVector(inputPattern, doWinners=True) self.assertTrue(np.array_equal(np.array([0, 1, 3, 7, 11], dtype=np.int32), outputPattern)) # tests: elif self.sparseThreshold > 0: classifier = KNNClassifier(distanceMethod="norm", distanceNorm=2.0, relativeThreshold=True, sparseThreshold=.2) outputPattern = classifier._sparsifyVector(inputPattern, doWinners=True) self.assertTrue(np.array_equal(np.array([0, 0, 3, 7, 11], dtype=np.int32), outputPattern)) # tests: if doWinners: classifier = KNNClassifier(distanceMethod="norm", distanceNorm=2.0, relativeThreshold=True, sparseThreshold=.2, numWinners=2) outputPattern = classifier._sparsifyVector(inputPattern, doWinners=True) self.assertTrue(np.array_equal(np.array([0, 0, 0, 0, 0], dtype=np.int32), outputPattern)) # tests: Do binarization classifier = KNNClassifier(distanceMethod="norm", distanceNorm=2.0, relativeThreshold=True, sparseThreshold=.2, doBinarization=True) outputPattern = classifier._sparsifyVector(inputPattern, doWinners=True) self.assertTrue(np.array_equal(np.array( [0., 0., 1., 1., 1.], dtype=np.float32), outputPattern))
def __init__( self, name, numCorticalColumns=1, inputSize=1024, numInputBits=20, externalInputSize=1024, numExternalInputBits=20, L2Overrides=None, networkType="L4L2TMColumn", L4Overrides=None, seed=42, logCalls=False, objectNamesAreIndices=False, TMOverrides=None, ): """ Creates the network. Parameters: ---------------------------- @param TMOverrides (dict) Parameters to override in the TM region """ # Handle logging - this has to be done first self.logCalls = logCalls registerAllResearchRegions() self.name = name self.numLearningPoints = 1 self.numColumns = numCorticalColumns self.inputSize = inputSize self.externalInputSize = externalInputSize self.numInputBits = numInputBits self.objectNamesAreIndices = objectNamesAreIndices # seed self.seed = seed random.seed(seed) # update parameters with overrides self.config = { "networkType": networkType, "numCorticalColumns": numCorticalColumns, "externalInputSize": externalInputSize, "sensorInputSize": inputSize, "enableFeedback": False, "L4Params": self.getDefaultL4Params(inputSize, numExternalInputBits), "L2Params": self.getDefaultL2Params(inputSize, numInputBits), "TMParams": self.getDefaultTMParams(self.inputSize, self.numInputBits), } if L2Overrides is not None: self.config["L2Params"].update(L2Overrides) if L4Overrides is not None: self.config["L4Params"].update(L4Overrides) if TMOverrides is not None: self.config["TMParams"].update(TMOverrides) # Recreate network including TM parameters self.network = createNetwork(self.config) self.sensorInputs = [] self.externalInputs = [] self.L4Regions = [] self.L2Regions = [] self.TMRegions = [] for i in xrange(self.numColumns): self.sensorInputs.append(self.network.regions["sensorInput_" + str(i)].getSelf()) self.externalInputs.append(self.network.regions["externalInput_" + str(i)].getSelf()) self.L4Regions.append(self.network.regions["L4Column_" + str(i)]) self.L2Regions.append(self.network.regions["L2Column_" + str(i)]) self.TMRegions.append(self.network.regions["TMColumn_" + str(i)]) self.L4Columns = [region.getSelf() for region in self.L4Regions] self.L2Columns = [region.getSelf() for region in self.L2Regions] self.TMColumns = [region.getSelf() for region in self.TMRegions] # will be populated during training self.objectL2Representations = {} self.objectL2RepresentationsMatrices = [ SparseMatrix(0, self.config["L2Params"]["cellCount"]) for _ in xrange(self.numColumns) ] self.objectNameToIndex = {} self.statistics = [] # Create classifier to hold supposedly unique TM states self.classifier = KNNClassifier(distanceMethod="rawOverlap") self.numTMCells = (self.TMColumns[0].cellsPerColumn * self.TMColumns[0].columnCount)