Пример #1
0
  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)
Пример #2
0
  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)
Пример #3
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
Пример #4
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
Пример #5
0
  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)
Пример #6
0
  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)
Пример #7
0
  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)
Пример #8
0
  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)
Пример #9
0
    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())
Пример #10
0
  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())
Пример #11
0
  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)
Пример #12
0
  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)
Пример #13
0
  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)
Пример #14
0
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
Пример #15
0
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
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]))
Пример #17
0
  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")
Пример #18
0
    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")
Пример #19
0
  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.")
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 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")
Пример #22
0
  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])