def grow(self, rows, cols): """ Grows the histogram to have rows rows and cols columns. Must not have been initialized before, or already have the same number of columns. If rows is smaller than the current number of rows, does not shrink. Also updates the sizes of the row and column sums. :param rows: Integer number of rows. :param cols: Integer number of columns. """ if not self.hist_: self.hist_ = SparseMatrix(rows, cols) self.rowSums_ = numpy.zeros(rows, dtype=dtype) self.colSums_ = numpy.zeros(cols, dtype=dtype) self.hack_ = None else: oldRows = self.hist_.nRows() oldCols = self.hist_.nCols() nextRows = max(oldRows, rows) nextCols = max(oldCols, cols) if (oldRows < nextRows) or (oldCols < nextCols): self.hist_.resize(nextRows, nextCols) if oldRows < nextRows: oldSums = self.rowSums_ self.rowSums_ = numpy.zeros(nextRows, dtype=dtype) self.rowSums_[0:len(oldSums)] = oldSums self.hack_ = None if oldCols < nextCols: oldSums = self.colSums_ self.colSums_ = numpy.zeros(nextCols, dtype=dtype) self.colSums_[0:len(oldSums)] = oldSums self.hack_ = None
def countBitFrequenciesForTerms(client, tokens): # Accumulate counts by inplace-adding sparse matrices counts = SparseMatrix() width = RETINA_SIZES[client.retina]["width"] height = RETINA_SIZES[client.retina]["height"] counts.resize(1, width*height) # Pre-allocate buffer sparse matrix sparseBitmap = SparseMatrix() sparseBitmap.resize(1, width*height) for term in tokens: term = term.strip() try: bitmap = client.getBitmap(term)["fingerprint"]["positions"] except Exception as err: print "Skipping '{}', reason: {}".format(term, str(err)) continue if not bitmap: print "Skipping '{}', reason: empty".format(term) continue sparseBitmap.setRowFromSparse(0, bitmap, [1]*len(bitmap)) counts += sparseBitmap return counts
def clean_outcpd(self): """Hack to act like clean_outcpd on zeta1.TopLevelNode. Take the max element in each to column, set it to 1, and set all the other elements to 0. Only called by inferRowMaxProd() and only needed if an updateRow() has been called since the last clean_outcpd(). """ m = self.hist_.toDense() for j in range(m.shape[1]): # For each column. cmax = m[:, j].max() if cmax: m[:, j] = numpy.array(m[:, j] == cmax, dtype=dtype) self.hack_ = SparseMatrix(0, self.hist_.nCols()) for i in range(m.shape[0]): self.hack_.addRow(m[i, :])
class ForwardModel(object): """ Simple forward model. Every cell has a set of synapses. The cell fires when its number of active synapses reaches a threshold. """ def __init__(self, cellCount, inputSize, threshold): self.permanences = SparseMatrix(cellCount, inputSize) self.threshold = threshold self.activeCells = np.empty(0, dtype='uint32') def associate(self, activeCells, activeInput): self.activeCells = activeCells self.permanences.setZerosOnOuter( self.activeCells, activeInput, 1.0) def infer(self, activeInput): overlaps = self.permanences.rightVecSumAtNZSparse(activeInput) self.activeCells = np.where(overlaps >= self.threshold)[0] self.activeCells.sort()
def clean_outcpd(self): """Hack to act like clean_outcpd on zeta1.TopLevelNode. Take the max element in each to column, set it to 1, and set all the other elements to 0. Only called by inferRowMaxProd() and only needed if an updateRow() has been called since the last clean_outcpd(). """ m = self.hist_.toDense() for j in xrange(m.shape[1]): # For each column. cmax = m[:,j].max() if cmax: m[:,j] = numpy.array(m[:,j] == cmax, dtype=dtype) self.hack_ = SparseMatrix(0, self.hist_.nCols()) for i in xrange(m.shape[0]): self.hack_.addRow(m[i,:])
def __init__( self, inputWidth, lateralInputWidths=(), cellCount=4096, sdrSize=40, # Proximal synPermProximalInc=0.1, synPermProximalDec=0.001, initialProximalPermanence=0.6, sampleSizeProximal=20, minThresholdProximal=10, connectedPermanenceProximal=0.50, # Distal synPermDistalInc=0.1, synPermDistalDec=0.001, initialDistalPermanence=0.6, sampleSizeDistal=20, activationThresholdDistal=13, connectedPermanenceDistal=0.50, distalSegmentInhibitionFactor=1.001, seed=42): """ Parameters: ---------------------------- @param inputWidth (int) The number of bits in the feedforward input @param lateralInputWidths (list of ints) The number of bits in each lateral input @param sdrSize (int) The number of active cells in an object SDR @param synPermProximalInc (float) Permanence increment for proximal synapses @param synPermProximalDec (float) Permanence decrement for proximal synapses @param initialProximalPermanence (float) Initial permanence value for proximal synapses @param sampleSizeProximal (int) Number of proximal synapses a cell should grow to each feedforward pattern, or -1 to connect to every active bit @param minThresholdProximal (int) Number of active synapses required for a cell to have feedforward support @param connectedPermanenceProximal (float) Permanence required for a proximal synapse to be connected @param synPermDistalInc (float) Permanence increment for distal synapses @param synPermDistalDec (float) Permanence decrement for distal synapses @param sampleSizeDistal (int) Number of distal synapses a cell should grow to each lateral pattern, or -1 to connect to every active bit @param initialDistalPermanence (float) Initial permanence value for distal synapses @param activationThresholdDistal (int) Number of active synapses required to activate a distal segment @param connectedPermanenceDistal (float) Permanence required for a distal synapse to be connected @param distalSegmentInhibitionFactor (float) The minimum ratio of active dendrite segment counts that will lead to inhibition. For example, with value 1.5, cells with 2 active segments will be inhibited by cells with 3 active segments, but cells with 3 active segments will not be inhibited by cells with 4. @param seed (int) Random number generator seed """ assert distalSegmentInhibitionFactor > 0.0 self.inputWidth = inputWidth self.cellCount = cellCount self.sdrSize = sdrSize self.synPermProximalInc = synPermProximalInc self.synPermProximalDec = synPermProximalDec self.initialProximalPermanence = initialProximalPermanence self.connectedPermanenceProximal = connectedPermanenceProximal self.sampleSizeProximal = sampleSizeProximal self.minThresholdProximal = minThresholdProximal self.synPermDistalInc = synPermDistalInc self.synPermDistalDec = synPermDistalDec self.initialDistalPermanence = initialDistalPermanence self.connectedPermanenceDistal = connectedPermanenceDistal self.sampleSizeDistal = sampleSizeDistal self.activationThresholdDistal = activationThresholdDistal self.distalSegmentInhibitionFactor = distalSegmentInhibitionFactor self.activeCells = numpy.empty(0, dtype="uint32") self._random = Random(seed) # These sparse matrices will hold the synapses for each segment. # Each row represents one segment on a cell, so each cell potentially has # 1 proximal segment and 1+len(lateralInputWidths) distal segments. self.proximalPermanences = SparseMatrix(cellCount, inputWidth) self.internalDistalPermanences = SparseMatrix(cellCount, cellCount) self.distalPermanences = tuple( SparseMatrix(cellCount, n) for n in lateralInputWidths)
class ColumnPooler(object): """ This class constitutes a temporary implementation for a cross-column pooler. The implementation goal of this class is to prove basic properties before creating a cleaner implementation. """ def __init__( self, inputWidth, lateralInputWidths=(), cellCount=4096, sdrSize=40, # Proximal synPermProximalInc=0.1, synPermProximalDec=0.001, initialProximalPermanence=0.6, sampleSizeProximal=20, minThresholdProximal=10, connectedPermanenceProximal=0.50, # Distal synPermDistalInc=0.1, synPermDistalDec=0.001, initialDistalPermanence=0.6, sampleSizeDistal=20, activationThresholdDistal=13, connectedPermanenceDistal=0.50, distalSegmentInhibitionFactor=1.001, seed=42): """ Parameters: ---------------------------- @param inputWidth (int) The number of bits in the feedforward input @param lateralInputWidths (list of ints) The number of bits in each lateral input @param sdrSize (int) The number of active cells in an object SDR @param synPermProximalInc (float) Permanence increment for proximal synapses @param synPermProximalDec (float) Permanence decrement for proximal synapses @param initialProximalPermanence (float) Initial permanence value for proximal synapses @param sampleSizeProximal (int) Number of proximal synapses a cell should grow to each feedforward pattern, or -1 to connect to every active bit @param minThresholdProximal (int) Number of active synapses required for a cell to have feedforward support @param connectedPermanenceProximal (float) Permanence required for a proximal synapse to be connected @param synPermDistalInc (float) Permanence increment for distal synapses @param synPermDistalDec (float) Permanence decrement for distal synapses @param sampleSizeDistal (int) Number of distal synapses a cell should grow to each lateral pattern, or -1 to connect to every active bit @param initialDistalPermanence (float) Initial permanence value for distal synapses @param activationThresholdDistal (int) Number of active synapses required to activate a distal segment @param connectedPermanenceDistal (float) Permanence required for a distal synapse to be connected @param distalSegmentInhibitionFactor (float) The minimum ratio of active dendrite segment counts that will lead to inhibition. For example, with value 1.5, cells with 2 active segments will be inhibited by cells with 3 active segments, but cells with 3 active segments will not be inhibited by cells with 4. @param seed (int) Random number generator seed """ assert distalSegmentInhibitionFactor > 0.0 self.inputWidth = inputWidth self.cellCount = cellCount self.sdrSize = sdrSize self.synPermProximalInc = synPermProximalInc self.synPermProximalDec = synPermProximalDec self.initialProximalPermanence = initialProximalPermanence self.connectedPermanenceProximal = connectedPermanenceProximal self.sampleSizeProximal = sampleSizeProximal self.minThresholdProximal = minThresholdProximal self.synPermDistalInc = synPermDistalInc self.synPermDistalDec = synPermDistalDec self.initialDistalPermanence = initialDistalPermanence self.connectedPermanenceDistal = connectedPermanenceDistal self.sampleSizeDistal = sampleSizeDistal self.activationThresholdDistal = activationThresholdDistal self.distalSegmentInhibitionFactor = distalSegmentInhibitionFactor self.activeCells = numpy.empty(0, dtype="uint32") self._random = Random(seed) # These sparse matrices will hold the synapses for each segment. # Each row represents one segment on a cell, so each cell potentially has # 1 proximal segment and 1+len(lateralInputWidths) distal segments. self.proximalPermanences = SparseMatrix(cellCount, inputWidth) self.internalDistalPermanences = SparseMatrix(cellCount, cellCount) self.distalPermanences = tuple( SparseMatrix(cellCount, n) for n in lateralInputWidths) def compute(self, feedforwardInput=(), lateralInputs=(), feedforwardGrowthCandidates=None, learn=True): """ Runs one time step of the column pooler algorithm. @param feedforwardInput (sequence) Sorted indices of active feedforward input bits @param lateralInputs (list of sequences) For each lateral layer, a list of sorted indices of active lateral input bits @param feedforwardGrowthCandidates (sequence or None) Sorted indices of feedforward input bits that active cells may grow new synapses to. If None, the entire feedforwardInput is used. @param learn (bool) If True, we are learning a new object """ if feedforwardGrowthCandidates is None: feedforwardGrowthCandidates = feedforwardInput if learn: self._computeLearningMode(feedforwardInput, lateralInputs, feedforwardGrowthCandidates) else: self._computeInferenceMode(feedforwardInput, lateralInputs) def _computeLearningMode(self, feedforwardInput, lateralInputs, feedforwardGrowthCandidates): """ Learning mode: we are learning a new object. If there is no prior activity, we randomly activate 'sdrSize' cells and create connections to incoming input. If there was prior activity, we maintain it. These cells will represent the object and learn distal connections to each other and to lateral cortical columns. Parameters: ---------------------------- @param feedforwardInput (sequence) Sorted indices of active feedforward input bits @param lateralInputs (list of sequences) For each lateral layer, a list of sorted indices of active lateral input bits @param feedforwardGrowthCandidates (sequence or None) Sorted indices of feedforward input bits that the active cells may grow new synapses to """ prevActiveCells = self.activeCells # If there are no previously active cells, select random subset of cells. # Else we maintain previous activity. if len(self.activeCells) == 0: self.activeCells = _sampleRange(self._random, 0, self.numberOfCells(), step=1, k=self.sdrSize) self.activeCells.sort() if len(feedforwardInput) > 0: # Proximal learning self._learn(self.proximalPermanences, self._random, self.activeCells, feedforwardInput, feedforwardGrowthCandidates, self.sampleSizeProximal, self.initialProximalPermanence, self.synPermProximalInc, self.synPermProximalDec, self.connectedPermanenceProximal) # Internal distal learning if len(prevActiveCells) > 0: self._learn(self.internalDistalPermanences, self._random, self.activeCells, prevActiveCells, prevActiveCells, self.sampleSizeDistal, self.initialDistalPermanence, self.synPermDistalInc, self.synPermDistalDec, self.connectedPermanenceDistal) # External distal learning for i, lateralInput in enumerate(lateralInputs): self._learn(self.distalPermanences[i], self._random, self.activeCells, lateralInput, lateralInput, self.sampleSizeDistal, self.initialDistalPermanence, self.synPermDistalInc, self.synPermDistalDec, self.connectedPermanenceDistal) def _computeInferenceMode(self, feedforwardInput, lateralInputs): """ Inference mode: if there is some feedforward activity, perform spatial pooling on it to recognize previously known objects, then use lateral activity to activate a subset of the cells with feedforward support. If there is no feedforward activity, use lateral activity to activate a subset of the previous active cells. Parameters: ---------------------------- @param feedforwardInput (sequence) Sorted indices of active feedforward input bits @param lateralInputs (list of sequences) For each lateral layer, a list of sorted indices of active lateral input bits """ prevActiveCells = self.activeCells # Calculate the feedforward supported cells overlaps = self.proximalPermanences.rightVecSumAtNZGteThresholdSparse( feedforwardInput, self.connectedPermanenceProximal) feedforwardSupportedCells = numpy.where( overlaps >= self.minThresholdProximal)[0] # Calculate the number of active segments on each cell numActiveSegmentsByCell = numpy.zeros(self.cellCount, dtype="int") overlaps = self.internalDistalPermanences.rightVecSumAtNZGteThresholdSparse( prevActiveCells, self.connectedPermanenceDistal) numActiveSegmentsByCell[ overlaps >= self.activationThresholdDistal] += 1 for i, lateralInput in enumerate(lateralInputs): overlaps = self.distalPermanences[ i].rightVecSumAtNZGteThresholdSparse( lateralInput, self.connectedPermanenceDistal) numActiveSegmentsByCell[ overlaps >= self.activationThresholdDistal] += 1 # Activate some of the feedforward supported cells minNumActiveCells = self.sdrSize / 2 chosenCells = self._chooseCells(feedforwardSupportedCells, minNumActiveCells, numActiveSegmentsByCell) # If necessary, activate some of the previously active cells if len(chosenCells) < minNumActiveCells: remaining = numpy.setdiff1d(prevActiveCells, feedforwardSupportedCells) remaining = remaining[numActiveSegmentsByCell[remaining] > 0] chosenCells = numpy.append( chosenCells, self._chooseCells(remaining, minNumActiveCells - len(chosenCells), numActiveSegmentsByCell)) chosenCells.sort() self.activeCells = numpy.asarray(chosenCells, dtype="uint32") def _chooseCells(self, candidates, n, numActiveSegmentsByCell): """ Choose cells to activate, using their active segment counts to determine inhibition. Count backwards through the active segment counts. For each count, find all of the cells that this count is unable to inhibit. Activate these cells. If there aren't at least n active cells, repeat with the next lowest segment count. Parameters: ---------------------------- @param candidates (sequence) List of cells to consider activating @param n (int) Minimum number of cells to activate, if possible @param numActiveSegmentsByCell (associative) A mapping from cells to number of active segments. This can be any data structure that associates an index with a value. (list, dict, numpy array) @return (list) Cells to activate """ if n <= 0: return numpy.empty(0) # Keep a list of numpy arrays. allChosenCells = [] numChosen = 0 # Walk the active segment counts in decreasing order. activeSegmentCounts = numpy.unique( numActiveSegmentsByCell[candidates])[::-1] for activeSegmentCount in activeSegmentCounts: if activeSegmentCount == 0: allChosenCells.append(candidates) numChosen += candidates.size break else: # Activate all cells that are not inhibited by this segment count. # A cell is inhibited if another cell has at least # 'distalSegmentInhibitionFactor' times as many active segments. boundary = float( activeSegmentCount) / self.distalSegmentInhibitionFactor includeMask = numActiveSegmentsByCell[candidates] > boundary chosenCells = candidates[includeMask] numChosen += chosenCells.size allChosenCells.append(chosenCells) if numChosen >= n: break else: candidates = candidates[~includeMask] if len(allChosenCells) > 0: return numpy.concatenate(allChosenCells) else: return numpy.empty(0) def numberOfInputs(self): """ Returns the number of inputs into this layer """ return self.inputWidth def numberOfCells(self): """ Returns the number of cells in this layer. @return (int) Number of cells """ return self.cellCount def getActiveCells(self): """ Returns the indices of the active cells. @return (list) Indices of active cells. """ return self.activeCells def numberOfConnectedProximalSynapses(self, cells=None): """ Returns the number of proximal connected synapses on these cells. Parameters: ---------------------------- @param cells (iterable) Indices of the cells. If None return count for all cells. """ if cells is None: cells = xrange(self.numberOfCells()) return _countWhereGreaterEqualInRows(self.proximalPermanences, cells, self.connectedPermanenceProximal) def numberOfProximalSynapses(self, cells=None): """ Returns the number of proximal synapses with permanence>0 on these cells. Parameters: ---------------------------- @param cells (iterable) Indices of the cells. If None return count for all cells. """ if cells is None: cells = xrange(self.numberOfCells()) n = 0 for cell in cells: n += self.proximalPermanences.nNonZerosOnRow(cell) return n def numberOfDistalSegments(self, cells=None): """ Returns the total number of distal segments for these cells. A segment "exists" if its row in the matrix has any permanence values > 0. Parameters: ---------------------------- @param cells (iterable) Indices of the cells """ if cells is None: cells = xrange(self.numberOfCells()) n = 0 for cell in cells: if self.internalDistalPermanences.nNonZerosOnRow(cell) > 0: n += 1 for permanences in self.distalPermanences: if permanences.nNonZerosOnRow(cell) > 0: n += 1 return n def numberOfConnectedDistalSynapses(self, cells=None): """ Returns the number of connected distal synapses on these cells. Parameters: ---------------------------- @param cells (iterable) Indices of the cells. If None return count for all cells. """ if cells is None: cells = xrange(self.numberOfCells()) n = _countWhereGreaterEqualInRows(self.internalDistalPermanences, cells, self.connectedPermanenceDistal) for permanences in self.distalPermanences: n += _countWhereGreaterEqualInRows(permanences, cells, self.connectedPermanenceDistal) return n def numberOfDistalSynapses(self, cells=None): """ Returns the total number of distal synapses for these cells. Parameters: ---------------------------- @param cells (iterable) Indices of the cells """ if cells is None: cells = xrange(self.numberOfCells()) n = 0 for cell in cells: n += self.internalDistalPermanences.nNonZerosOnRow(cell) for permanences in self.distalPermanences: n += permanences.nNonZerosOnRow(cell) return n def reset(self): """ Reset internal states. When learning this signifies we are to learn a unique new object. """ self.activeCells = numpy.empty(0, dtype="uint32") @staticmethod def _learn( # mutated args permanences, rng, # activity activeCells, activeInput, growthCandidateInput, # configuration sampleSize, initialPermanence, permanenceIncrement, permanenceDecrement, connectedPermanence): """ For each active cell, reinforce active synapses, punish inactive synapses, and grow new synapses to a subset of the active input bits that the cell isn't already connected to. Parameters: ---------------------------- @param permanences (SparseMatrix) Matrix of permanences, with cells as rows and inputs as columns @param rng (Random) Random number generator @param activeCells (sorted sequence) Sorted list of the cells that are learning @param activeInput (sorted sequence) Sorted list of active bits in the input @param growthCandidateInput (sorted sequence) Sorted list of active bits in the input that the activeCells may grow new synapses to For remaining parameters, see the __init__ docstring. """ permanences.incrementNonZerosOnOuter(activeCells, activeInput, permanenceIncrement) permanences.incrementNonZerosOnRowsExcludingCols( activeCells, activeInput, -permanenceDecrement) permanences.clipRowsBelowAndAbove(activeCells, 0.0, 1.0) if sampleSize == -1: permanences.setZerosOnOuter(activeCells, activeInput, initialPermanence) else: existingSynapseCounts = permanences.nNonZerosPerRowOnCols( activeCells, activeInput) maxNewByCell = numpy.empty(len(activeCells), dtype="int32") numpy.subtract(sampleSize, existingSynapseCounts, out=maxNewByCell) permanences.setRandomZerosOnOuter(activeCells, growthCandidateInput, maxNewByCell, initialPermanence, rng)
def initializeConnections(self): """ Initialize connection matrix, including: _permanences : permanence of synaptic connections (sparse matrix) _potentialPools: potential pool of connections for each cell (sparse binary matrix) _connectedSynapses: connected synapses (binary sparse matrix) _connectedCounts: number of connections per cell (numpy array) """ numColumns = self._numColumns numInputs = self._numInputs # The SP should be setup so that stimulusThreshold is reasonably high, # similar to a TP's activation threshold. The pct of connected cells may # need to be low to avoid false positives given the monosynaptic rule initConnectedPct = self._initConnectedPct # Store the set of all inputs that are within each column's potential pool. # 'potentialPools' is a matrix, whose rows represent cortical columns, and # whose columns represent the input bits. if potentialPools[i][j] == 1, # then input bit 'j' is in column 'i's potential pool. A column can only be # connected to inputs in its potential pool. The indices refer to a # flattened version of both the inputs and columns. Namely, irrespective # of the topology of the inputs and columns, they are treated as being a # one dimensional array. Since a column is typically connected to only a # subset of the inputs, many of the entries in the matrix are 0. Therefore # the the potentialPool matrix is stored using the SparseBinaryMatrix # class, to reduce memory footprint and compuation time of algorithms that # require iterating over the data strcuture. self._potentialPools = SparseBinaryMatrix(numInputs) self._potentialPools.resize(numColumns, numInputs) # Initialize the permanences for each column. Similar to the # 'self._potentialPools', the permanences are stored in a matrix whose rows # represent the cortical columns, and whose columns represent the input # bits. if self._permanences[i][j] = 0.2, then the synapse connecting # cortical column 'i' to input bit 'j' has a permanence of 0.2. Here we # also use the SparseMatrix class to reduce the memory footprint and # computation time of algorithms that require iterating over the data # structure. This permanence matrix is only allowed to have non-zero # elements where the potential pool is non-zero. self._permanences = SparseMatrix(numColumns, numInputs) # 'self._connectedSynapses' is a similar matrix to 'self._permanences' # (rows represent cortical columns, columns represent input bits) whose # entries represent whether the cortial column is connected to the input # bit, i.e. its permanence value is greater than 'synPermConnected'. While # this information is readily available from the 'self._permanence' matrix, # it is stored separately for efficiency purposes. self._connectedSynapses = SparseBinaryMatrix(numInputs) self._connectedSynapses.resize(numColumns, numInputs) # Stores the number of connected synapses for each column. This is simply # a sum of each row of 'self._connectedSynapses'. again, while this # information is readily available from 'self._connectedSynapses', it is # stored separately for efficiency purposes. self._connectedCounts = numpy.zeros(numColumns, dtype=realDType) # Initialize the set of permanence values for each column. Ensure that # each column is connected to enough input bits to allow it to be # activated. for i in xrange(numColumns): potential = self._mapPotential(i, wrapAround=self._wrapAround) self._potentialPools.replaceSparseRow(i, potential.nonzero()[0]) perm = self._initPermanence(potential, initConnectedPct) self._updatePermanencesForColumn(perm, i, raisePerm=True)
def __init__(self, inputWidth, lateralInputWidths=(), cellCount=4096, sdrSize=40, # Proximal synPermProximalInc=0.1, synPermProximalDec=0.001, initialProximalPermanence=0.6, sampleSizeProximal=20, minThresholdProximal=10, connectedPermanenceProximal=0.50, # Distal synPermDistalInc=0.1, synPermDistalDec=0.001, initialDistalPermanence=0.6, sampleSizeDistal=20, activationThresholdDistal=13, connectedPermanenceDistal=0.50, distalSegmentInhibitionFactor=1.5, seed=42): """ Parameters: ---------------------------- @param inputWidth (int) The number of bits in the feedforward input @param lateralInputWidths (list of ints) The number of bits in each lateral input @param sdrSize (int) The number of active cells in an object SDR @param synPermProximalInc (float) Permanence increment for proximal synapses @param synPermProximalDec (float) Permanence decrement for proximal synapses @param initialProximalPermanence (float) Initial permanence value for proximal synapses @param sampleSizeProximal (int) Number of proximal synapses a cell should grow to each feedforward pattern, or -1 to connect to every active bit @param minThresholdProximal (int) Number of active synapses required for a cell to have feedforward support @param connectedPermanenceProximal (float) Permanence required for a proximal synapse to be connected @param synPermDistalInc (float) Permanence increment for distal synapses @param synPermDistalDec (float) Permanence decrement for distal synapses @param sampleSizeDistal (int) Number of distal synapses a cell should grow to each lateral pattern, or -1 to connect to every active bit @param initialDistalPermanence (float) Initial permanence value for distal synapses @param activationThresholdDistal (int) Number of active synapses required to activate a distal segment @param connectedPermanenceDistal (float) Permanence required for a distal synapse to be connected @param distalSegmentInhibitionFactor (float) The minimum ratio of active dendrite segment counts that will lead to inhibition. For example, with value 1.5, cells with 2 active segments will be inhibited by cells with 3 active segments, but cells with 3 active segments will not be inhibited by cells with 4. @param seed (int) Random number generator seed """ self.inputWidth = inputWidth self.cellCount = cellCount self.sdrSize = sdrSize self.synPermProximalInc = synPermProximalInc self.synPermProximalDec = synPermProximalDec self.initialProximalPermanence = initialProximalPermanence self.connectedPermanenceProximal = connectedPermanenceProximal self.sampleSizeProximal = sampleSizeProximal self.minThresholdProximal = minThresholdProximal self.synPermDistalInc = synPermDistalInc self.synPermDistalDec = synPermDistalDec self.initialDistalPermanence = initialDistalPermanence self.connectedPermanenceDistal = connectedPermanenceDistal self.sampleSizeDistal = sampleSizeDistal self.activationThresholdDistal = activationThresholdDistal self.distalSegmentInhibitionFactor = distalSegmentInhibitionFactor self.activeCells = () self._random = Random(seed) # These sparse matrices will hold the synapses for each segment. # Each row represents one segment on a cell, so each cell potentially has # 1 proximal segment and 1+len(lateralInputWidths) distal segments. self.proximalPermanences = SparseMatrix(cellCount, inputWidth) self.internalDistalPermanences = SparseMatrix(cellCount, cellCount) self.distalPermanences = tuple(SparseMatrix(cellCount, n) for n in lateralInputWidths)
class ColumnPooler(object): """ This class constitutes a temporary implementation for a cross-column pooler. The implementation goal of this class is to prove basic properties before creating a cleaner implementation. """ def __init__(self, inputWidth, lateralInputWidths=(), cellCount=4096, sdrSize=40, # Proximal synPermProximalInc=0.1, synPermProximalDec=0.001, initialProximalPermanence=0.6, sampleSizeProximal=20, minThresholdProximal=10, connectedPermanenceProximal=0.50, # Distal synPermDistalInc=0.1, synPermDistalDec=0.001, initialDistalPermanence=0.6, sampleSizeDistal=20, activationThresholdDistal=13, connectedPermanenceDistal=0.50, distalSegmentInhibitionFactor=1.5, seed=42): """ Parameters: ---------------------------- @param inputWidth (int) The number of bits in the feedforward input @param lateralInputWidths (list of ints) The number of bits in each lateral input @param sdrSize (int) The number of active cells in an object SDR @param synPermProximalInc (float) Permanence increment for proximal synapses @param synPermProximalDec (float) Permanence decrement for proximal synapses @param initialProximalPermanence (float) Initial permanence value for proximal synapses @param sampleSizeProximal (int) Number of proximal synapses a cell should grow to each feedforward pattern, or -1 to connect to every active bit @param minThresholdProximal (int) Number of active synapses required for a cell to have feedforward support @param connectedPermanenceProximal (float) Permanence required for a proximal synapse to be connected @param synPermDistalInc (float) Permanence increment for distal synapses @param synPermDistalDec (float) Permanence decrement for distal synapses @param sampleSizeDistal (int) Number of distal synapses a cell should grow to each lateral pattern, or -1 to connect to every active bit @param initialDistalPermanence (float) Initial permanence value for distal synapses @param activationThresholdDistal (int) Number of active synapses required to activate a distal segment @param connectedPermanenceDistal (float) Permanence required for a distal synapse to be connected @param distalSegmentInhibitionFactor (float) The minimum ratio of active dendrite segment counts that will lead to inhibition. For example, with value 1.5, cells with 2 active segments will be inhibited by cells with 3 active segments, but cells with 3 active segments will not be inhibited by cells with 4. @param seed (int) Random number generator seed """ self.inputWidth = inputWidth self.cellCount = cellCount self.sdrSize = sdrSize self.synPermProximalInc = synPermProximalInc self.synPermProximalDec = synPermProximalDec self.initialProximalPermanence = initialProximalPermanence self.connectedPermanenceProximal = connectedPermanenceProximal self.sampleSizeProximal = sampleSizeProximal self.minThresholdProximal = minThresholdProximal self.synPermDistalInc = synPermDistalInc self.synPermDistalDec = synPermDistalDec self.initialDistalPermanence = initialDistalPermanence self.connectedPermanenceDistal = connectedPermanenceDistal self.sampleSizeDistal = sampleSizeDistal self.activationThresholdDistal = activationThresholdDistal self.distalSegmentInhibitionFactor = distalSegmentInhibitionFactor self.activeCells = () self._random = Random(seed) # These sparse matrices will hold the synapses for each segment. # Each row represents one segment on a cell, so each cell potentially has # 1 proximal segment and 1+len(lateralInputWidths) distal segments. self.proximalPermanences = SparseMatrix(cellCount, inputWidth) self.internalDistalPermanences = SparseMatrix(cellCount, cellCount) self.distalPermanences = tuple(SparseMatrix(cellCount, n) for n in lateralInputWidths) def compute(self, feedforwardInput=(), lateralInputs=(), learn=True): """ Runs one time step of the column pooler algorithm. @param feedforwardInput (iterable) Indices of active feedforward input bits @param lateralInputs (list of iterables) Sets of indices of active lateral input bits, one per lateral layer @param learn (bool) If True, we are learning a new object """ if learn: self._computeLearningMode(feedforwardInput, lateralInputs) else: self._computeInferenceMode(feedforwardInput, lateralInputs) def _computeLearningMode(self, feedforwardInput, lateralInputs): """ Learning mode: we are learning a new object. If there is no prior activity, we randomly activate 'sdrSize' cells and create connections to incoming input. If there was prior activity, we maintain it. These cells will represent the object and learn distal connections to each other and to lateral cortical columns. Parameters: ---------------------------- @param feedforwardInput (iterable) List of indices of active feedforward input bits @param lateralInputs (list of iterables) Lists of indices of active lateral input bits, one per lateral layer """ prevActiveCells = self.activeCells # If there are no previously active cells, select random subset of cells. # Else we maintain previous activity. if len(self.activeCells) == 0: self.activeCells = sorted(_sampleRange(self._random, 0, self.numberOfCells(), step=1, k=self.sdrSize)) if len(feedforwardInput) > 0: # Proximal learning self._learn(self.proximalPermanences, self._random, self.activeCells, sorted(feedforwardInput), self.sampleSizeProximal, self.initialProximalPermanence, self.synPermProximalInc, self.synPermProximalDec, self.connectedPermanenceProximal) # Internal distal learning if len(prevActiveCells) > 0: self._learn(self.internalDistalPermanences, self._random, self.activeCells, prevActiveCells, self.sampleSizeDistal, self.initialDistalPermanence, self.synPermDistalInc, self.synPermDistalDec, self.connectedPermanenceDistal) # External distal learning for i, lateralInput in enumerate(lateralInputs): self._learn(self.distalPermanences[i], self._random, self.activeCells, sorted(lateralInput), self.sampleSizeDistal, self.initialDistalPermanence, self.synPermDistalInc, self.synPermDistalDec, self.connectedPermanenceDistal) def _computeInferenceMode(self, feedforwardInput, lateralInputs): """ Inference mode: if there is some feedforward activity, perform spatial pooling on it to recognize previously known objects, then use lateral activity to activate a subset of the cells with feedforward support. If there is no feedforward activity, use lateral activity to activate a subset of the previous active cells. Parameters: ---------------------------- @param feedforwardInput (iterable) Indices of active feedforward input bits @param lateralInputs (list of iterables) Sets of indices of active lateral input bits, one per lateral layer """ prevActiveCells = self.activeCells # Calculate the feedforward supported cells overlaps = self.proximalPermanences.rightVecSumAtNZGteThresholdSparse( list(feedforwardInput), self.connectedPermanenceProximal) feedforwardSupportedCells = set( numpy.where(overlaps >= self.minThresholdProximal)[0]) # Calculate the number of active segments on each cell numActiveSegmentsByCell = numpy.zeros(self.cellCount, dtype="int") overlaps = self.internalDistalPermanences.rightVecSumAtNZGteThresholdSparse( list(prevActiveCells), self.connectedPermanenceDistal) numActiveSegmentsByCell[overlaps >= self.activationThresholdDistal] += 1 for i, lateralInput in enumerate(lateralInputs): overlaps = self.distalPermanences[i].rightVecSumAtNZGteThresholdSparse( list(lateralInput), self.connectedPermanenceDistal) numActiveSegmentsByCell[overlaps >= self.activationThresholdDistal] += 1 # Activate some of the feedforward supported cells minNumActiveCells = self.sdrSize / 2 chosenCells = self._chooseCells(feedforwardSupportedCells, minNumActiveCells, numActiveSegmentsByCell) # If necessary, activate some of the previously active cells if len(chosenCells) < minNumActiveCells: remainingCandidates = [cell for cell in prevActiveCells if cell not in feedforwardSupportedCells if numActiveSegmentsByCell[cell] > 0] chosenCells.extend(self._chooseCells(remainingCandidates, minNumActiveCells - len(chosenCells), numActiveSegmentsByCell)) self.activeCells = sorted(chosenCells) def _chooseCells(self, candidates, n, numActiveSegmentsByCell): """ Choose cells to activate, using their active segment counts to determine inhibition. Count backwards through the active segment counts. For each count, find all of the cells that this count is unable to inhibit. Activate these cells. If there aren't at least n active cells, repeat with the next lowest segment count. Parameters: ---------------------------- @param candidates (iterable) List of cells to consider activating @param n (int) Minimum number of cells to activate, if possible @param numActiveSegmentsByCell (associative) A mapping from cells to number of active segments. This can be any data structure that associates an index with a value. (list, dict, numpy array) @return (list) Cells to activate """ orderedCandidates = sorted(candidates, key=numActiveSegmentsByCell.__getitem__, reverse=True) activeSegmentCounts = sorted(set(numActiveSegmentsByCell[cell] for cell in candidates), reverse=True) chosenCells = [] i = 0 for activeSegmentCount in activeSegmentCounts: if len(chosenCells) >= n or i >= len(orderedCandidates): break if activeSegmentCount == 0: chosenCells.extend(orderedCandidates[i:]) break # If one cell has 'distalSegmentInhibitionFactor' * the number of active # segments of another cell, the latter cell is inhibited. boundary = float(activeSegmentCount) / self.distalSegmentInhibitionFactor while (i < len(orderedCandidates) and numActiveSegmentsByCell[orderedCandidates[i]] > boundary): chosenCells.append(orderedCandidates[i]) i += 1 return chosenCells def numberOfInputs(self): """ Returns the number of inputs into this layer """ return self.inputWidth def numberOfCells(self): """ Returns the number of cells in this layer. @return (int) Number of cells """ return self.cellCount def getActiveCells(self): """ Returns the indices of the active cells. @return (list) Indices of active cells. """ return self.activeCells def numberOfConnectedProximalSynapses(self, cells=None): """ Returns the number of proximal connected synapses on these cells. Parameters: ---------------------------- @param cells (iterable) Indices of the cells. If None return count for all cells. """ if cells is None: cells = xrange(self.numberOfCells()) return _countWhereGreaterEqualInRows(self.proximalPermanences, cells, self.connectedPermanenceProximal) def numberOfProximalSynapses(self, cells=None): """ Returns the number of proximal synapses with permanence>0 on these cells. Parameters: ---------------------------- @param cells (iterable) Indices of the cells. If None return count for all cells. """ if cells is None: cells = xrange(self.numberOfCells()) n = 0 for cell in cells: n += self.proximalPermanences.nNonZerosOnRow(cell) return n def numberOfDistalSegments(self, cells=None): """ Returns the total number of distal segments for these cells. A segment "exists" if its row in the matrix has any permanence values > 0. Parameters: ---------------------------- @param cells (iterable) Indices of the cells """ if cells is None: cells = xrange(self.numberOfCells()) n = 0 for cell in cells: if self.internalDistalPermanences.nNonZerosOnRow(cell) > 0: n += 1 for permanences in self.distalPermanences: if permanences.nNonZerosOnRow(cell) > 0: n += 1 return n def numberOfConnectedDistalSynapses(self, cells=None): """ Returns the number of connected distal synapses on these cells. Parameters: ---------------------------- @param cells (iterable) Indices of the cells. If None return count for all cells. """ if cells is None: cells = xrange(self.numberOfCells()) n = _countWhereGreaterEqualInRows(self.internalDistalPermanences, cells, self.connectedPermanenceDistal) for permanences in self.distalPermanences: n += _countWhereGreaterEqualInRows(permanences, cells, self.connectedPermanenceDistal) return n def numberOfDistalSynapses(self, cells=None): """ Returns the total number of distal synapses for these cells. Parameters: ---------------------------- @param cells (iterable) Indices of the cells """ if cells is None: cells = xrange(self.numberOfCells()) n = 0 for cell in cells: n += self.internalDistalPermanences.nNonZerosOnRow(cell) for permanences in self.distalPermanences: n += permanences.nNonZerosOnRow(cell) return n def reset(self): """ Reset internal states. When learning this signifies we are to learn a unique new object. """ self.activeCells = () @staticmethod def _learn(# mutated args permanences, rng, # activity activeCells, activeInput, # configuration sampleSize, initialPermanence, permanenceIncrement, permanenceDecrement, connectedPermanence): """ For each active cell, reinforce active synapses, punish inactive synapses, and grow new synapses to a subset of the active input bits that the cell isn't already connected to. Parameters: ---------------------------- @param permanences (SparseMatrix) Matrix of permanences, with cells as rows and inputs as columns @param rng (Random) Random number generator @param activeCells (sorted sequence) Sorted list of the cells that are learning @param activeInput (sorted sequence) Sorted list of active bits in the input For remaining parameters, see the __init__ docstring. """ permanences.incrementNonZerosOnOuter( activeCells, activeInput, permanenceIncrement) permanences.incrementNonZerosOnRowsExcludingCols( activeCells, activeInput, -permanenceDecrement) permanences.clipRowsBelowAndAbove( activeCells, 0.0, 1.0) if sampleSize == -1: permanences.setZerosOnOuter( activeCells, activeInput, initialPermanence) else: permanences.increaseRowNonZeroCountsOnOuterTo( activeCells, activeInput, sampleSize, initialPermanence, rng)
def __init__(self, inputWidth, lateralInputWidth, numActiveColumnsPerInhArea=40, synPermProximalInc=0.1, synPermProximalDec=0.001, initialProximalPermanence=0.6, columnDimensions=(2048,), minThresholdProximal=10, activationThresholdDistal=13, minThresholdDistal=10, initialPermanence=0.41, connectedPermanence=0.50, maxNewProximalSynapseCount=20, maxNewDistalSynapseCount=20, permanenceIncrement=0.10, permanenceDecrement=0.10, predictedSegmentDecrement=0.0, maxSegmentsPerCell=255, maxSynapsesPerProximalSegment=255, maxSynapsesPerDistalSegment=255, seed=42): """ This classes uses an ExtendedTemporalMemory internally to keep track of distal segments. Please see ExtendedTemporalMemory for descriptions of constructor parameters not defined below. Parameters: ---------------------------- @param inputWidth (int) The number of proximal inputs into this layer @param lateralInputWidth (int) The number of lateral inputs into this layer @param numActiveColumnsPerInhArea (int) Target number of active cells @param synPermProximalInc (float) Permanence increment for proximal synapses @param synPermProximalDec (float) Permanence decrement for proximal synapses @param initialProximalPermanence (float) Initial permanence value for proximal segments """ self.inputWidth = inputWidth self.lateralInputWidth = lateralInputWidth self.numActiveColumnsPerInhArea = numActiveColumnsPerInhArea self.synPermProximalInc = synPermProximalInc self.synPermProximalDec = synPermProximalDec self.initialProximalPermanence = initialProximalPermanence self.connectedPermanence = connectedPermanence self.maxNewProximalSynapseCount = maxNewProximalSynapseCount self.maxNewDistalSynapseCount = maxNewDistalSynapseCount self.minThresholdProximal = minThresholdProximal self.minThresholdDistal = minThresholdDistal self.maxSynapsesPerProximalSegment = maxSynapsesPerProximalSegment self.activeCells = set() self._random = Random(seed) # Create our own instance of extended temporal memory to handle distal # segments. self.tm = createModel( modelName="etm_cpp", columnDimensions=columnDimensions, basalInputDimensions=(lateralInputWidth,), apicalInputDimensions=(), cellsPerColumn=1, activationThreshold=activationThresholdDistal, initialPermanence=initialPermanence, connectedPermanence=connectedPermanence, minThreshold=minThresholdDistal, maxNewSynapseCount=maxNewDistalSynapseCount, permanenceIncrement=permanenceIncrement, permanenceDecrement=permanenceDecrement, predictedSegmentDecrement=predictedSegmentDecrement, formInternalBasalConnections=False, learnOnOneCell=False, maxSegmentsPerCell=maxSegmentsPerCell, maxSynapsesPerSegment=maxSynapsesPerDistalSegment, seed=seed, ) # These sparse matrices will hold the synapses for each proximal segment. # # proximalPermanences - SparseMatrix with permanence values # proximalConnections - SparseBinaryMatrix of connected synapses self.proximalPermanences = SparseMatrix(self.numberOfColumns(), inputWidth) self.proximalConnections = SparseBinaryMatrix(inputWidth) self.proximalConnections.resize(self.numberOfColumns(), inputWidth)
def getUnionEncodingFromTokens(self, tokens): """ Create a single, sparsified bitmap from a union of bitmaps for given tokens @param tokens (sequence) A sequence of tokens @return (sequence) Bitmap """ # Accumulate counts by inplace-adding sparse matrices counts = SparseMatrix() counts.resize(1, self.width*self.height) # Pre-allocate buffer sparse matrix sparseBitmap = SparseMatrix() sparseBitmap.resize(1, self.width*self.height) for t in tokens: bitmap = self._getWordBitmap(t) sparseBitmap.setRowFromSparse(0, bitmap, [1]*len(bitmap)) counts += sparseBitmap numNonZeros = counts.nNonZerosOnRow(0) # Add some jitter to aid in tie-breaking during sort jitterValues = tuple((random.random()/100)+0.1 for _ in xrange(numNonZeros)) sparseBitmap.setRowFromSparse(0, counts.nonZeroCols(), jitterValues) counts += sparseBitmap maxSparsity = int(self.unionSparsity * self.n) w = min(numNonZeros, maxSparsity) # Return top w most popular positions return tuple(x[1] for x in counts.getNonZerosSorted())[:w]
def __init__(self, inputWidth, lateralInputWidths=(), cellCount=4096, sdrSize=40, onlineLearning = False, maxSdrSize = None, minSdrSize = None, # Proximal synPermProximalInc=0.1, synPermProximalDec=0.001, initialProximalPermanence=0.6, sampleSizeProximal=20, minThresholdProximal=10, connectedPermanenceProximal=0.50, predictedInhibitionThreshold=20, # Distal synPermDistalInc=0.1, synPermDistalDec=0.001, initialDistalPermanence=0.6, sampleSizeDistal=20, activationThresholdDistal=13, connectedPermanenceDistal=0.50, distalSegmentInhibitionFactor=0.999, inertiaFactor=1., seed=42): """ Parameters: ---------------------------- @param inputWidth (int) The number of bits in the feedforward input @param lateralInputWidths (list of ints) The number of bits in each lateral input @param sdrSize (int) The number of active cells in an object SDR @param onlineLearning (Bool) Whether or not the column pooler should learn in online mode. @param maxSdrSize (int) The maximum SDR size for learning. If the column pooler has more than this many cells active, it will refuse to learn. This serves to stop the pooler from learning when it is uncertain of what object it is sensing. @param minSdrSize (int) The minimum SDR size for learning. If the column pooler has fewer than this many active cells, it will create a new representation and learn that instead. This serves to create separate representations for different objects and sequences. If online learning is enabled, this parameter should be at least inertiaFactor*sdrSize. Otherwise, two different objects may be incorrectly inferred to be the same, as SDRs may still be active enough to learn even after inertial decay. @param synPermProximalInc (float) Permanence increment for proximal synapses @param synPermProximalDec (float) Permanence decrement for proximal synapses @param initialProximalPermanence (float) Initial permanence value for proximal synapses @param sampleSizeProximal (int) Number of proximal synapses a cell should grow to each feedforward pattern, or -1 to connect to every active bit @param minThresholdProximal (int) Number of active synapses required for a cell to have feedforward support @param connectedPermanenceProximal (float) Permanence required for a proximal synapse to be connected @param predictedInhibitionThreshold (int) How much predicted input must be present for inhibitory behavior to be triggered. Only has effects if onlineLearning is true. @param synPermDistalInc (float) Permanence increment for distal synapses @param synPermDistalDec (float) Permanence decrement for distal synapses @param sampleSizeDistal (int) Number of distal synapses a cell should grow to each lateral pattern, or -1 to connect to every active bit @param initialDistalPermanence (float) Initial permanence value for distal synapses @param activationThresholdDistal (int) Number of active synapses required to activate a distal segment @param connectedPermanenceDistal (float) Permanence required for a distal synapse to be connected @param distalSegmentInhibitionFactor (float) The proportion of the highest number of active lateral segments necessary for a cell not to be inhibited (must be <1). @param inertiaFactor (float) The proportion of previously active cells that remain active in the next timestep due to inertia (in the absence of inhibition). If onlineLearning is enabled, should be at most 1 - learningTolerance, or representations may incorrectly become mixed. @param seed (int) Random number generator seed """ assert distalSegmentInhibitionFactor > 0.0 assert distalSegmentInhibitionFactor < 1.0 assert maxSdrSize is None or maxSdrSize >= sdrSize assert minSdrSize is None or minSdrSize <= sdrSize self.inputWidth = inputWidth self.cellCount = cellCount self.sdrSize = sdrSize self.onlineLearning = onlineLearning if maxSdrSize is None: self.maxSdrSize = sdrSize else: self.maxSdrSize = maxSdrSize if minSdrSize is None: self.minSdrSize = sdrSize else: self.minSdrSize = minSdrSize self.synPermProximalInc = synPermProximalInc self.synPermProximalDec = synPermProximalDec self.initialProximalPermanence = initialProximalPermanence self.connectedPermanenceProximal = connectedPermanenceProximal self.sampleSizeProximal = sampleSizeProximal self.minThresholdProximal = minThresholdProximal self.predictedInhibitionThreshold = predictedInhibitionThreshold self.synPermDistalInc = synPermDistalInc self.synPermDistalDec = synPermDistalDec self.initialDistalPermanence = initialDistalPermanence self.connectedPermanenceDistal = connectedPermanenceDistal self.sampleSizeDistal = sampleSizeDistal self.activationThresholdDistal = activationThresholdDistal self.distalSegmentInhibitionFactor = distalSegmentInhibitionFactor self.inertiaFactor = inertiaFactor self.activeCells = numpy.empty(0, dtype="uint32") self._random = Random(seed) # These sparse matrices will hold the synapses for each segment. # Each row represents one segment on a cell, so each cell potentially has # 1 proximal segment and 1+len(lateralInputWidths) distal segments. self.proximalPermanences = SparseMatrix(cellCount, inputWidth) self.internalDistalPermanences = SparseMatrix(cellCount, cellCount) self.distalPermanences = tuple(SparseMatrix(cellCount, n) for n in lateralInputWidths) self.useInertia=True
def __init__(self, cellCount, inputSize, threshold): self.permanences = SparseMatrix(cellCount, inputSize) self.threshold = threshold self.activeCells = np.empty(0, dtype='uint32')
class ConditionalProbabilityTable2D(object): """ Holds frequencies in a 2D grid of bins. Binning is not performed automatically by this class. Bin updates must be done one row at a time. Based on nupic::SparseMatrix which is a compressed sparse row matrix. Number of columns cannot be changed once set. Number of rows may be increased. Also maintains the row and column sumProp distributions. Constructor constructs a new empty histogram with no rows or columns. :param rowHint: if specified, ncols must be specified (though not vice versa) :param ncols: if speicified, number of columns cannot be changed thereafter. """ def __init__(self, rowHint=None, ncols=None): self.hist_ = None self.rowSums_ = None self.colSums_ = None if ncols: if not rowHint: rowHint = 1 assert dtype self.grow(rowHint, ncols) else: assert not rowHint self.hack_ = None def numRows(self): """Gets the number of rows in the histogram. :returns: Integer number of rows. """ if self.hist_: return self.hist_.nRows() else: return 0 def numColumns(self): """ :return: (int) number of columns """ if self.hist_: return self.hist_.nCols() else: return 0 def grow(self, rows, cols): """ Grows the histogram to have rows rows and cols columns. Must not have been initialized before, or already have the same number of columns. If rows is smaller than the current number of rows, does not shrink. Also updates the sizes of the row and column sums. :param rows: Integer number of rows. :param cols: Integer number of columns. """ if not self.hist_: self.hist_ = SparseMatrix(rows, cols) self.rowSums_ = numpy.zeros(rows, dtype=dtype) self.colSums_ = numpy.zeros(cols, dtype=dtype) self.hack_ = None else: oldRows = self.hist_.nRows() oldCols = self.hist_.nCols() nextRows = max(oldRows, rows) nextCols = max(oldCols, cols) if (oldRows < nextRows) or (oldCols < nextCols): self.hist_.resize(nextRows, nextCols) if oldRows < nextRows: oldSums = self.rowSums_ self.rowSums_ = numpy.zeros(nextRows, dtype=dtype) self.rowSums_[0:len(oldSums)] = oldSums self.hack_ = None if oldCols < nextCols: oldSums = self.colSums_ self.colSums_ = numpy.zeros(nextCols, dtype=dtype) self.colSums_[0:len(oldSums)] = oldSums self.hack_ = None def updateRow(self, row, distribution): """ Add distribution to row row. Distribution should be an array of probabilities or counts. :param row: Integer index of the row to add to. May be larger than the current number of rows, in which case the histogram grows. :param distribution: Array of length equal to the number of columns. """ self.grow(row+1, len(distribution)) self.hist_.axby(row, 1, 1, distribution) self.rowSums_[row] += distribution.sum() self.colSums_ += distribution self.hack_ = None # Clear out the cached inference. def inferRow(self, distribution): """ Computes the sumProp probability of each row given the input probability of each column. Normalizes the distribution in each column on the fly. The semantics are as follows: If the distribution is P(col|e) where e is the evidence is col is the column, and the CPD represents P(row|col), then this calculates sum(P(col|e) P(row|col)) = P(row|e). :param distribution: Array of length equal to the number of columns. :returns: array of length equal to the number of rows. """ # normalize over colSums_ because P(row|col) = P(col,row)/P(col) return self.hist_ * (distribution / self.colSums_) def inferRowEvidence(self, distribution): """ Computes the probability of evidence given each row from the probability of evidence given each column. Essentially, this just means that it sums probabilities over (normalized) rows. Normalizes the distribution over each row on the fly. The semantics are as follows: If the distribution is P(e|col) where e is evidence and col is the column, and the CPD is of P(col|row), then this calculates sum(P(e|col) P(col|row)) = P(e|row). :param distribution: Array of length equal to the number of columns. :returns: array of length equal to the number of rows. """ # normalize over rowSums_ because P(col|row) = P(col,row)/P(row). return (self.hist_ * distribution) / self.rowSums_ def inferRowMaxProd(self, distribution): return self.hist_.vecMaxProd(distribution) def inferRowCompat(self, distribution): """ Equivalent to the category inference of zeta1.TopLevel. Computes the max_prod (maximum component of a component-wise multiply) between the rows of the histogram and the incoming distribution. May be slow if the result of clean_outcpd() is not valid. :param distribution: Array of length equal to the number of columns. :returns: array of length equal to the number of rows. """ if self.hack_ is None: self.clean_outcpd() return self.hack_.vecMaxProd(distribution) def clean_outcpd(self): """Hack to act like clean_outcpd on zeta1.TopLevelNode. Take the max element in each to column, set it to 1, and set all the other elements to 0. Only called by inferRowMaxProd() and only needed if an updateRow() has been called since the last clean_outcpd(). """ m = self.hist_.toDense() for j in xrange(m.shape[1]): # For each column. cmax = m[:,j].max() if cmax: m[:,j] = numpy.array(m[:,j] == cmax, dtype=dtype) self.hack_ = SparseMatrix(0, self.hist_.nCols()) for i in xrange(m.shape[0]): self.hack_.addRow(m[i,:])
class ConditionalProbabilityTable2D(object): """ Holds frequencies in a 2D grid of bins. Binning is not performed automatically by this class. Bin updates must be done one row at a time. Based on nupic::SparseMatrix which is a compressed sparse row matrix. Number of columns cannot be changed once set. Number of rows may be increased. Also maintains the row and column sumProp distributions. Constructor constructs a new empty histogram with no rows or columns. :param rowHint: if specified, ncols must be specified (though not vice versa) :param ncols: if speicified, number of columns cannot be changed thereafter. """ def __init__(self, rowHint=None, ncols=None): self.hist_ = None self.rowSums_ = None self.colSums_ = None if ncols: if not rowHint: rowHint = 1 assert dtype self.grow(rowHint, ncols) else: assert not rowHint self.hack_ = None def numRows(self): """Gets the number of rows in the histogram. :returns: Integer number of rows. """ if self.hist_: return self.hist_.nRows() else: return 0 def numColumns(self): """ :return: (int) number of columns """ if self.hist_: return self.hist_.nCols() else: return 0 def grow(self, rows, cols): """ Grows the histogram to have rows rows and cols columns. Must not have been initialized before, or already have the same number of columns. If rows is smaller than the current number of rows, does not shrink. Also updates the sizes of the row and column sums. :param rows: Integer number of rows. :param cols: Integer number of columns. """ if not self.hist_: self.hist_ = SparseMatrix(rows, cols) self.rowSums_ = numpy.zeros(rows, dtype=dtype) self.colSums_ = numpy.zeros(cols, dtype=dtype) self.hack_ = None else: oldRows = self.hist_.nRows() oldCols = self.hist_.nCols() nextRows = max(oldRows, rows) nextCols = max(oldCols, cols) if (oldRows < nextRows) or (oldCols < nextCols): self.hist_.resize(nextRows, nextCols) if oldRows < nextRows: oldSums = self.rowSums_ self.rowSums_ = numpy.zeros(nextRows, dtype=dtype) self.rowSums_[0:len(oldSums)] = oldSums self.hack_ = None if oldCols < nextCols: oldSums = self.colSums_ self.colSums_ = numpy.zeros(nextCols, dtype=dtype) self.colSums_[0:len(oldSums)] = oldSums self.hack_ = None def updateRow(self, row, distribution): """ Add distribution to row row. Distribution should be an array of probabilities or counts. :param row: Integer index of the row to add to. May be larger than the current number of rows, in which case the histogram grows. :param distribution: Array of length equal to the number of columns. """ self.grow(row + 1, len(distribution)) self.hist_.axby(row, 1, 1, distribution) self.rowSums_[row] += distribution.sum() self.colSums_ += distribution self.hack_ = None # Clear out the cached inference. def inferRow(self, distribution): """ Computes the sumProp probability of each row given the input probability of each column. Normalizes the distribution in each column on the fly. The semantics are as follows: If the distribution is P(col|e) where e is the evidence is col is the column, and the CPD represents P(row|col), then this calculates sum(P(col|e) P(row|col)) = P(row|e). :param distribution: Array of length equal to the number of columns. :returns: array of length equal to the number of rows. """ # normalize over colSums_ because P(row|col) = P(col,row)/P(col) return self.hist_ * (distribution / self.colSums_) def inferRowEvidence(self, distribution): """ Computes the probability of evidence given each row from the probability of evidence given each column. Essentially, this just means that it sums probabilities over (normalized) rows. Normalizes the distribution over each row on the fly. The semantics are as follows: If the distribution is P(e|col) where e is evidence and col is the column, and the CPD is of P(col|row), then this calculates sum(P(e|col) P(col|row)) = P(e|row). :param distribution: Array of length equal to the number of columns. :returns: array of length equal to the number of rows. """ # normalize over rowSums_ because P(col|row) = P(col,row)/P(row). return (self.hist_ * distribution) / self.rowSums_ def inferRowMaxProd(self, distribution): return self.hist_.vecMaxProd(distribution) def inferRowCompat(self, distribution): """ Equivalent to the category inference of zeta1.TopLevel. Computes the max_prod (maximum component of a component-wise multiply) between the rows of the histogram and the incoming distribution. May be slow if the result of clean_outcpd() is not valid. :param distribution: Array of length equal to the number of columns. :returns: array of length equal to the number of rows. """ if self.hack_ is None: self.clean_outcpd() return self.hack_.vecMaxProd(distribution) def clean_outcpd(self): """Hack to act like clean_outcpd on zeta1.TopLevelNode. Take the max element in each to column, set it to 1, and set all the other elements to 0. Only called by inferRowMaxProd() and only needed if an updateRow() has been called since the last clean_outcpd(). """ m = self.hist_.toDense() for j in range(m.shape[1]): # For each column. cmax = m[:, j].max() if cmax: m[:, j] = numpy.array(m[:, j] == cmax, dtype=dtype) self.hack_ = SparseMatrix(0, self.hist_.nCols()) for i in range(m.shape[0]): self.hack_.addRow(m[i, :])
def getUnionEncodingFromTokens(self, tokens): """ Create a single, sparsified bitmap from a union of bitmaps for given tokens @param tokens (sequence) A sequence of tokens @return (sequence) Bitmap """ # Accumulate counts by inplace-adding sparse matrices counts = SparseMatrix() counts.resize(1, self.width * self.height) # Pre-allocate buffer sparse matrix sparseBitmap = SparseMatrix() sparseBitmap.resize(1, self.width * self.height) for t in tokens: bitmap = self._getWordBitmap(t) sparseBitmap.setRowFromSparse(0, bitmap, [1] * len(bitmap)) counts += sparseBitmap numNonZeros = counts.nNonZerosOnRow(0) # Add some jitter to aid in tie-breaking during sort jitterValues = tuple( (random.random() / 100) + 0.1 for _ in xrange(numNonZeros)) sparseBitmap.setRowFromSparse(0, counts.nonZeroCols(), jitterValues) counts += sparseBitmap maxSparsity = int(self.unionSparsity * self.n) w = min(numNonZeros, maxSparsity) # Return top w most popular positions return tuple(x[1] for x in counts.getNonZerosSorted())[:w]
def countBitFrequenciesForTerms(client, lines, acceptanceProbability = 0.1, usePlaceholderEncoding = True, percentSparsity = 0.0102): # Accumulate counts by inplace-adding sparse matrices skippedWords = {} counts = SparseMatrix() width = RETINA_SIZES[client.retina]["width"] height = RETINA_SIZES[client.retina]["height"] counts.resize(1, width*height) # Pre-allocate buffer sparse matrix sparseBitmap = SparseMatrix() sparseBitmap.resize(1, width*height) # Accumulate counts for each bit for each word numWords=0 numLines=0 for line in lines: tokens = TextPreprocess().tokenize(line) for term in tokens: p = random.uniform(0,1) if p <= acceptanceProbability: if usePlaceholderEncoding: random.seed(term) bitmap = random.sample(xrange(width*height), int(width*height*percentSparsity)) bitmap.sort() random.seed(p) else: try: bitmap = client.getBitmap(term)["fingerprint"]["positions"] except Exception as err: print "Skipping '{}', reason: {}".format(term, str(err)) continue if not bitmap: skippedWords[term] = skippedWords.get(term,0)+1 # print "Skipping '{}', reason: empty".format(term) continue sparseBitmap.setRowFromSparse(0, bitmap, [1]*len(bitmap)) counts += sparseBitmap numWords += 1 numLines += 1 if numLines%1000==0: print "...processed=",numLines,"lines and",numWords,"words" # Compute normalized version of counts as a separate matrix frequencies = SparseMatrix() frequencies.resize(1, width*height) frequencies.copy(counts) frequencies.divide(float(numWords)) # Wrap up by printing some statistics and then saving the normalized version print "Processed",numLines,"lines" printFrequencyStatistics(counts, frequencies, numWords, width*height) frequencyFilename = "bit_frequencies_"+client.retina+".pkl" print "Saving frequency matrix in",frequencyFilename with open(frequencyFilename, "wb") as frequencyPickleFile: pickle.dump(frequencies, frequencyPickleFile) print "These words were skipped N times because of empty bitmap result" print skippedWords return counts
def countRandomBitFrequencies(numTerms = 100000, percentSparsity = 0.01): """Create a uniformly random counts matrix through sampling.""" # Accumulate counts by inplace-adding sparse matrices counts = SparseMatrix() size = 128*128 counts.resize(1, size) # Pre-allocate buffer sparse matrix sparseBitmap = SparseMatrix() sparseBitmap.resize(1, size) random.seed(42) # Accumulate counts for each bit for each word numWords=0 for term in xrange(numTerms): bitmap = random.sample(xrange(size), int(size*percentSparsity)) bitmap.sort() sparseBitmap.setRowFromSparse(0, bitmap, [1]*len(bitmap)) counts += sparseBitmap numWords += 1 # Compute normalized version of counts as a separate matrix frequencies = SparseMatrix() frequencies.resize(1, size) frequencies.copy(counts) frequencies.divide(float(numWords)) # Wrap up by printing some statistics and then saving the normalized version printFrequencyStatistics(counts, frequencies, numWords, size) frequencyFilename = "bit_frequencies_random.pkl" print "Saving frequency matrix in",frequencyFilename with open(frequencyFilename, "wb") as frequencyPickleFile: pickle.dump(frequencies, frequencyPickleFile) return counts
class ColumnPooler(object): """ This class constitutes a temporary implementation for a cross-column pooler. The implementation goal of this class is to prove basic properties before creating a cleaner implementation. """ def __init__(self, inputWidth, lateralInputWidths=(), cellCount=4096, sdrSize=40, onlineLearning = False, maxSdrSize = None, minSdrSize = None, # Proximal synPermProximalInc=0.1, synPermProximalDec=0.001, initialProximalPermanence=0.6, sampleSizeProximal=20, minThresholdProximal=10, connectedPermanenceProximal=0.50, predictedInhibitionThreshold=20, # Distal synPermDistalInc=0.1, synPermDistalDec=0.001, initialDistalPermanence=0.6, sampleSizeDistal=20, activationThresholdDistal=13, connectedPermanenceDistal=0.50, inertiaFactor=1., seed=42): """ Parameters: ---------------------------- @param inputWidth (int) The number of bits in the feedforward input @param lateralInputWidths (list of ints) The number of bits in each lateral input @param sdrSize (int) The number of active cells in an object SDR @param onlineLearning (Bool) Whether or not the column pooler should learn in online mode. @param maxSdrSize (int) The maximum SDR size for learning. If the column pooler has more than this many cells active, it will refuse to learn. This serves to stop the pooler from learning when it is uncertain of what object it is sensing. @param minSdrSize (int) The minimum SDR size for learning. If the column pooler has fewer than this many active cells, it will create a new representation and learn that instead. This serves to create separate representations for different objects and sequences. If online learning is enabled, this parameter should be at least inertiaFactor*sdrSize. Otherwise, two different objects may be incorrectly inferred to be the same, as SDRs may still be active enough to learn even after inertial decay. @param synPermProximalInc (float) Permanence increment for proximal synapses @param synPermProximalDec (float) Permanence decrement for proximal synapses @param initialProximalPermanence (float) Initial permanence value for proximal synapses @param sampleSizeProximal (int) Number of proximal synapses a cell should grow to each feedforward pattern, or -1 to connect to every active bit @param minThresholdProximal (int) Number of active synapses required for a cell to have feedforward support @param connectedPermanenceProximal (float) Permanence required for a proximal synapse to be connected @param predictedInhibitionThreshold (int) How much predicted input must be present for inhibitory behavior to be triggered. Only has effects if onlineLearning is true. @param synPermDistalInc (float) Permanence increment for distal synapses @param synPermDistalDec (float) Permanence decrement for distal synapses @param sampleSizeDistal (int) Number of distal synapses a cell should grow to each lateral pattern, or -1 to connect to every active bit @param initialDistalPermanence (float) Initial permanence value for distal synapses @param activationThresholdDistal (int) Number of active synapses required to activate a distal segment @param connectedPermanenceDistal (float) Permanence required for a distal synapse to be connected @param inertiaFactor (float) The proportion of previously active cells that remain active in the next timestep due to inertia (in the absence of inhibition). If onlineLearning is enabled, should be at most 1 - learningTolerance, or representations may incorrectly become mixed. @param seed (int) Random number generator seed """ assert maxSdrSize is None or maxSdrSize >= sdrSize assert minSdrSize is None or minSdrSize <= sdrSize self.inputWidth = inputWidth self.cellCount = cellCount self.sdrSize = sdrSize self.onlineLearning = onlineLearning if maxSdrSize is None: self.maxSdrSize = sdrSize else: self.maxSdrSize = maxSdrSize if minSdrSize is None: self.minSdrSize = sdrSize else: self.minSdrSize = minSdrSize self.synPermProximalInc = synPermProximalInc self.synPermProximalDec = synPermProximalDec self.initialProximalPermanence = initialProximalPermanence self.connectedPermanenceProximal = connectedPermanenceProximal self.sampleSizeProximal = sampleSizeProximal self.minThresholdProximal = minThresholdProximal self.predictedInhibitionThreshold = predictedInhibitionThreshold self.synPermDistalInc = synPermDistalInc self.synPermDistalDec = synPermDistalDec self.initialDistalPermanence = initialDistalPermanence self.connectedPermanenceDistal = connectedPermanenceDistal self.sampleSizeDistal = sampleSizeDistal self.activationThresholdDistal = activationThresholdDistal self.inertiaFactor = inertiaFactor self.activeCells = numpy.empty(0, dtype="uint32") self._random = Random(seed) # These sparse matrices will hold the synapses for each segment. # Each row represents one segment on a cell, so each cell potentially has # 1 proximal segment and 1+len(lateralInputWidths) distal segments. self.proximalPermanences = SparseMatrix(cellCount, inputWidth) self.internalDistalPermanences = SparseMatrix(cellCount, cellCount) self.distalPermanences = tuple(SparseMatrix(cellCount, n) for n in lateralInputWidths) self.useInertia=True def compute(self, feedforwardInput=(), lateralInputs=(), feedforwardGrowthCandidates=None, learn=True, predictedInput = None,): """ Runs one time step of the column pooler algorithm. @param feedforwardInput (sequence) Sorted indices of active feedforward input bits @param lateralInputs (list of sequences) For each lateral layer, a list of sorted indices of active lateral input bits @param feedforwardGrowthCandidates (sequence or None) Sorted indices of feedforward input bits that active cells may grow new synapses to. If None, the entire feedforwardInput is used. @param learn (bool) If True, we are learning a new object @param predictedInput (sequence) Sorted indices of predicted cells in the TM layer. """ if feedforwardGrowthCandidates is None: feedforwardGrowthCandidates = feedforwardInput # inference step if not learn: self._computeInferenceMode(feedforwardInput, lateralInputs) # learning step elif not self.onlineLearning: self._computeLearningMode(feedforwardInput, lateralInputs, feedforwardGrowthCandidates) # online learning step else: if (predictedInput is not None and len(predictedInput) > self.predictedInhibitionThreshold): predictedActiveInput = numpy.intersect1d(feedforwardInput, predictedInput) predictedGrowthCandidates = numpy.intersect1d( feedforwardGrowthCandidates, predictedInput) self._computeInferenceMode(predictedActiveInput, lateralInputs) self._computeLearningMode(predictedActiveInput, lateralInputs, feedforwardGrowthCandidates) elif not self.minSdrSize <= len(self.activeCells) <= self.maxSdrSize: # If the pooler doesn't have a single representation, try to infer one, # before actually attempting to learn. self._computeInferenceMode(feedforwardInput, lateralInputs) self._computeLearningMode(feedforwardInput, lateralInputs, feedforwardGrowthCandidates) else: # If there isn't predicted input and we have a single SDR, # we are extending that representation and should just learn. self._computeLearningMode(feedforwardInput, lateralInputs, feedforwardGrowthCandidates) def _computeLearningMode(self, feedforwardInput, lateralInputs, feedforwardGrowthCandidates): """ Learning mode: we are learning a new object in an online fashion. If there is no prior activity, we randomly activate 'sdrSize' cells and create connections to incoming input. If there was prior activity, we maintain it. If we have a union, we simply do not learn at all. These cells will represent the object and learn distal connections to each other and to lateral cortical columns. Parameters: ---------------------------- @param feedforwardInput (sequence) Sorted indices of active feedforward input bits @param lateralInputs (list of sequences) For each lateral layer, a list of sorted indices of active lateral input bits @param feedforwardGrowthCandidates (sequence or None) Sorted indices of feedforward input bits that the active cells may grow new synapses to. This is assumed to be the predicted active cells of the input layer. """ prevActiveCells = self.activeCells # If there are not enough previously active cells, then we are no longer on # a familiar object. Either our representation decayed due to the passage # of time (i.e. we moved somewhere else) or we were mistaken. Either way, # create a new SDR and learn on it. # This case is the only way different object representations are created. # enforce the active cells in the output layer if len(self.activeCells) < self.minSdrSize: self.activeCells = _sampleRange(self._random, 0, self.numberOfCells(), step=1, k=self.sdrSize) self.activeCells.sort() # If we have a union of cells active, don't learn. This primarily affects # online learning. if len(self.activeCells) > self.maxSdrSize: return # Finally, now that we have decided which cells we should be learning on, do # the actual learning. if len(feedforwardInput) > 0: self._learn(self.proximalPermanences, self._random, self.activeCells, feedforwardInput, feedforwardGrowthCandidates, self.sampleSizeProximal, self.initialProximalPermanence, self.synPermProximalInc, self.synPermProximalDec, self.connectedPermanenceProximal) # External distal learning for i, lateralInput in enumerate(lateralInputs): self._learn(self.distalPermanences[i], self._random, self.activeCells, lateralInput, lateralInput, self.sampleSizeDistal, self.initialDistalPermanence, self.synPermDistalInc, self.synPermDistalDec, self.connectedPermanenceDistal) # Internal distal learning self._learn(self.internalDistalPermanences, self._random, self.activeCells, prevActiveCells, prevActiveCells, self.sampleSizeDistal, self.initialDistalPermanence, self.synPermDistalInc, self.synPermDistalDec, self.connectedPermanenceDistal) def _computeInferenceMode(self, feedforwardInput, lateralInputs): """ Inference mode: if there is some feedforward activity, perform spatial pooling on it to recognize previously known objects, then use lateral activity to activate a subset of the cells with feedforward support. If there is no feedforward activity, use lateral activity to activate a subset of the previous active cells. Parameters: ---------------------------- @param feedforwardInput (sequence) Sorted indices of active feedforward input bits @param lateralInputs (list of sequences) For each lateral layer, a list of sorted indices of active lateral input bits """ prevActiveCells = self.activeCells # Calculate the feedforward supported cells overlaps = self.proximalPermanences.rightVecSumAtNZGteThresholdSparse( feedforwardInput, self.connectedPermanenceProximal) feedforwardSupportedCells = numpy.where( overlaps >= self.minThresholdProximal)[0] # Calculate the number of active segments on each cell numActiveSegmentsByCell = numpy.zeros(self.cellCount, dtype="int") overlaps = self.internalDistalPermanences.rightVecSumAtNZGteThresholdSparse( prevActiveCells, self.connectedPermanenceDistal) numActiveSegmentsByCell[overlaps >= self.activationThresholdDistal] += 1 for i, lateralInput in enumerate(lateralInputs): overlaps = self.distalPermanences[i].rightVecSumAtNZGteThresholdSparse( lateralInput, self.connectedPermanenceDistal) numActiveSegmentsByCell[overlaps >= self.activationThresholdDistal] += 1 chosenCells = [] # First, activate the FF-supported cells that have the highest number of # lateral active segments (as long as it's not 0) if len(feedforwardSupportedCells) == 0: pass else: numActiveSegsForFFSuppCells = numActiveSegmentsByCell[ feedforwardSupportedCells] # This loop will select the FF-supported AND laterally-active cells, in # order of descending lateral activation, until we exceed the sdrSize # quorum - but will exclude cells with 0 lateral active segments. ttop = numpy.max(numActiveSegsForFFSuppCells) while ttop > 0 and len(chosenCells) < self.sdrSize: chosenCells = numpy.union1d(chosenCells, feedforwardSupportedCells[numActiveSegsForFFSuppCells >= ttop]) ttop -= 1 # If we haven't filled the sdrSize quorum, add in inertial cells. if len(chosenCells) < self.sdrSize: if self.useInertia: prevCells = numpy.setdiff1d(prevActiveCells, chosenCells) inertialCap = int(len(prevCells) * self.inertiaFactor) if inertialCap > 0: numActiveSegsForPrevCells = numActiveSegmentsByCell[prevCells] # We sort the previously-active cells by number of active lateral # segments (this really helps). We then activate them in order of # descending lateral activation. sortIndices = numpy.argsort(numActiveSegsForPrevCells)[::-1] prevCells = prevCells[sortIndices] numActiveSegsForPrevCells = numActiveSegsForPrevCells[sortIndices] # We use inertiaFactor to limit the number of previously-active cells # which can become active, forcing decay even if we are below quota. prevCells = prevCells[:inertialCap] numActiveSegsForPrevCells = numActiveSegsForPrevCells[:inertialCap] # Activate groups of previously active cells by order of their lateral # support until we either meet quota or run out of cells. ttop = numpy.max(numActiveSegsForPrevCells) while ttop >= 0 and len(chosenCells) < self.sdrSize: chosenCells = numpy.union1d(chosenCells, prevCells[numActiveSegsForPrevCells >= ttop]) ttop -= 1 # If we haven't filled the sdrSize quorum, add cells that have feedforward # support and no lateral support. discrepancy = self.sdrSize - len(chosenCells) if discrepancy > 0: remFFcells = numpy.setdiff1d(feedforwardSupportedCells, chosenCells) # Inhibit cells proportionally to the number of cells that have already # been chosen. If ~0 have been chosen activate ~all of the feedforward # supported cells. If ~sdrSize have been chosen, activate very few of # the feedforward supported cells. # Use the discrepancy:sdrSize ratio to determine the number of cells to # activate. n = (len(remFFcells) * discrepancy) // self.sdrSize # Activate at least 'discrepancy' cells. n = max(n, discrepancy) # If there aren't 'n' available, activate all of the available cells. n = min(n, len(remFFcells)) if len(remFFcells) > n: selected = _sample(self._random, remFFcells, n) chosenCells = numpy.append(chosenCells, selected) else: chosenCells = numpy.append(chosenCells, remFFcells) chosenCells.sort() self.activeCells = numpy.asarray(chosenCells, dtype="uint32") def numberOfInputs(self): """ Returns the number of inputs into this layer """ return self.inputWidth def numberOfCells(self): """ Returns the number of cells in this layer. @return (int) Number of cells """ return self.cellCount def getActiveCells(self): """ Returns the indices of the active cells. @return (list) Indices of active cells. """ return self.activeCells def numberOfConnectedProximalSynapses(self, cells=None): """ Returns the number of proximal connected synapses on these cells. Parameters: ---------------------------- @param cells (iterable) Indices of the cells. If None return count for all cells. """ if cells is None: cells = xrange(self.numberOfCells()) return _countWhereGreaterEqualInRows(self.proximalPermanences, cells, self.connectedPermanenceProximal) def numberOfProximalSynapses(self, cells=None): """ Returns the number of proximal synapses with permanence>0 on these cells. Parameters: ---------------------------- @param cells (iterable) Indices of the cells. If None return count for all cells. """ if cells is None: cells = xrange(self.numberOfCells()) n = 0 for cell in cells: n += self.proximalPermanences.nNonZerosOnRow(cell) return n def numberOfDistalSegments(self, cells=None): """ Returns the total number of distal segments for these cells. A segment "exists" if its row in the matrix has any permanence values > 0. Parameters: ---------------------------- @param cells (iterable) Indices of the cells """ if cells is None: cells = xrange(self.numberOfCells()) n = 0 for cell in cells: if self.internalDistalPermanences.nNonZerosOnRow(cell) > 0: n += 1 for permanences in self.distalPermanences: if permanences.nNonZerosOnRow(cell) > 0: n += 1 return n def numberOfConnectedDistalSynapses(self, cells=None): """ Returns the number of connected distal synapses on these cells. Parameters: ---------------------------- @param cells (iterable) Indices of the cells. If None return count for all cells. """ if cells is None: cells = xrange(self.numberOfCells()) n = _countWhereGreaterEqualInRows(self.internalDistalPermanences, cells, self.connectedPermanenceDistal) for permanences in self.distalPermanences: n += _countWhereGreaterEqualInRows(permanences, cells, self.connectedPermanenceDistal) return n def numberOfDistalSynapses(self, cells=None): """ Returns the total number of distal synapses for these cells. Parameters: ---------------------------- @param cells (iterable) Indices of the cells """ if cells is None: cells = xrange(self.numberOfCells()) n = 0 for cell in cells: n += self.internalDistalPermanences.nNonZerosOnRow(cell) for permanences in self.distalPermanences: n += permanences.nNonZerosOnRow(cell) return n def reset(self): """ Reset internal states. When learning this signifies we are to learn a unique new object. """ self.activeCells = numpy.empty(0, dtype="uint32") def getUseInertia(self): """ Get whether we actually use inertia (i.e. a fraction of the previously active cells remain active at the next time step unless inhibited by cells with both feedforward and lateral support). @return (Bool) Whether inertia is used. """ return self.useInertia def setUseInertia(self, useInertia): """ Sets whether we actually use inertia (i.e. a fraction of the previously active cells remain active at the next time step unless inhibited by cells with both feedforward and lateral support). @param useInertia (Bool) Whether inertia is used. """ self.useInertia = useInertia @staticmethod def _learn(# mutated args permanences, rng, # activity activeCells, activeInput, growthCandidateInput, # configuration sampleSize, initialPermanence, permanenceIncrement, permanenceDecrement, connectedPermanence): """ For each active cell, reinforce active synapses, punish inactive synapses, and grow new synapses to a subset of the active input bits that the cell isn't already connected to. Parameters: ---------------------------- @param permanences (SparseMatrix) Matrix of permanences, with cells as rows and inputs as columns @param rng (Random) Random number generator @param activeCells (sorted sequence) Sorted list of the cells that are learning @param activeInput (sorted sequence) Sorted list of active bits in the input @param growthCandidateInput (sorted sequence) Sorted list of active bits in the input that the activeCells may grow new synapses to For remaining parameters, see the __init__ docstring. """ permanences.incrementNonZerosOnOuter( activeCells, activeInput, permanenceIncrement) permanences.incrementNonZerosOnRowsExcludingCols( activeCells, activeInput, -permanenceDecrement) permanences.clipRowsBelowAndAbove( activeCells, 0.0, 1.0) if sampleSize == -1: permanences.setZerosOnOuter( activeCells, activeInput, initialPermanence) else: existingSynapseCounts = permanences.nNonZerosPerRowOnCols( activeCells, activeInput) maxNewByCell = numpy.empty(len(activeCells), dtype="int32") numpy.subtract(sampleSize, existingSynapseCounts, out=maxNewByCell) permanences.setRandomZerosOnOuter( activeCells, growthCandidateInput, maxNewByCell, initialPermanence, rng)
def __init__(self, inputWidth, lateralInputWidths=(), cellCount=4096, sdrSize=40, onlineLearning = False, maxSdrSize = None, minSdrSize = None, # Proximal synPermProximalInc=0.1, synPermProximalDec=0.001, initialProximalPermanence=0.6, sampleSizeProximal=20, minThresholdProximal=10, connectedPermanenceProximal=0.50, predictedInhibitionThreshold=20, # Distal synPermDistalInc=0.1, synPermDistalDec=0.001, initialDistalPermanence=0.6, sampleSizeDistal=20, activationThresholdDistal=13, connectedPermanenceDistal=0.50, inertiaFactor=1., seed=42): """ Parameters: ---------------------------- @param inputWidth (int) The number of bits in the feedforward input @param lateralInputWidths (list of ints) The number of bits in each lateral input @param sdrSize (int) The number of active cells in an object SDR @param onlineLearning (Bool) Whether or not the column pooler should learn in online mode. @param maxSdrSize (int) The maximum SDR size for learning. If the column pooler has more than this many cells active, it will refuse to learn. This serves to stop the pooler from learning when it is uncertain of what object it is sensing. @param minSdrSize (int) The minimum SDR size for learning. If the column pooler has fewer than this many active cells, it will create a new representation and learn that instead. This serves to create separate representations for different objects and sequences. If online learning is enabled, this parameter should be at least inertiaFactor*sdrSize. Otherwise, two different objects may be incorrectly inferred to be the same, as SDRs may still be active enough to learn even after inertial decay. @param synPermProximalInc (float) Permanence increment for proximal synapses @param synPermProximalDec (float) Permanence decrement for proximal synapses @param initialProximalPermanence (float) Initial permanence value for proximal synapses @param sampleSizeProximal (int) Number of proximal synapses a cell should grow to each feedforward pattern, or -1 to connect to every active bit @param minThresholdProximal (int) Number of active synapses required for a cell to have feedforward support @param connectedPermanenceProximal (float) Permanence required for a proximal synapse to be connected @param predictedInhibitionThreshold (int) How much predicted input must be present for inhibitory behavior to be triggered. Only has effects if onlineLearning is true. @param synPermDistalInc (float) Permanence increment for distal synapses @param synPermDistalDec (float) Permanence decrement for distal synapses @param sampleSizeDistal (int) Number of distal synapses a cell should grow to each lateral pattern, or -1 to connect to every active bit @param initialDistalPermanence (float) Initial permanence value for distal synapses @param activationThresholdDistal (int) Number of active synapses required to activate a distal segment @param connectedPermanenceDistal (float) Permanence required for a distal synapse to be connected @param inertiaFactor (float) The proportion of previously active cells that remain active in the next timestep due to inertia (in the absence of inhibition). If onlineLearning is enabled, should be at most 1 - learningTolerance, or representations may incorrectly become mixed. @param seed (int) Random number generator seed """ assert maxSdrSize is None or maxSdrSize >= sdrSize assert minSdrSize is None or minSdrSize <= sdrSize self.inputWidth = inputWidth self.cellCount = cellCount self.sdrSize = sdrSize self.onlineLearning = onlineLearning if maxSdrSize is None: self.maxSdrSize = sdrSize else: self.maxSdrSize = maxSdrSize if minSdrSize is None: self.minSdrSize = sdrSize else: self.minSdrSize = minSdrSize self.synPermProximalInc = synPermProximalInc self.synPermProximalDec = synPermProximalDec self.initialProximalPermanence = initialProximalPermanence self.connectedPermanenceProximal = connectedPermanenceProximal self.sampleSizeProximal = sampleSizeProximal self.minThresholdProximal = minThresholdProximal self.predictedInhibitionThreshold = predictedInhibitionThreshold self.synPermDistalInc = synPermDistalInc self.synPermDistalDec = synPermDistalDec self.initialDistalPermanence = initialDistalPermanence self.connectedPermanenceDistal = connectedPermanenceDistal self.sampleSizeDistal = sampleSizeDistal self.activationThresholdDistal = activationThresholdDistal self.inertiaFactor = inertiaFactor self.activeCells = numpy.empty(0, dtype="uint32") self._random = Random(seed) # These sparse matrices will hold the synapses for each segment. # Each row represents one segment on a cell, so each cell potentially has # 1 proximal segment and 1+len(lateralInputWidths) distal segments. self.proximalPermanences = SparseMatrix(cellCount, inputWidth) self.internalDistalPermanences = SparseMatrix(cellCount, cellCount) self.distalPermanences = tuple(SparseMatrix(cellCount, n) for n in lateralInputWidths) self.useInertia=True
def __init__( self, name, numCorticalColumns=1, inputSize=1024, numInputBits=20, externalInputSize=1024, numExternalInputBits=20, L2Overrides=None, L4Overrides=None, seed=42, logCalls=False, objectNamesAreIndices=False, ): """ 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 self.numExternalInputBits = numExternalInputBits # seed self.seed = seed random.seed(seed) # Create default parameters and then update with overrides self.config = { "networkType": "CombinedSequenceColumn", "numCorticalColumns": numCorticalColumns, "externalInputSize": externalInputSize, "sensorInputSize": inputSize, "enableFeedback": False, "L2Params": self.getDefaultL2Params(inputSize, numInputBits), } self.config["L4Params"] = self._getDefaultCombinedL4Params( self.numInputBits, self.inputSize, self.numExternalInputBits, self.externalInputSize, self.config["L2Params"]["cellCount"]) if L2Overrides is not None: self.config["L2Params"].update(L2Overrides) if L4Overrides is not None: self.config["L4Params"].update(L4Overrides) pprint.pprint(self.config) # Recreate network including TM parameters self.network = createNetwork(self.config) self.sensorInputs = [] self.externalInputs = [] self.L2Regions = [] self.L4Regions = [] 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.L2Regions.append(self.network.regions["L2Column_" + str(i)]) self.L4Regions.append(self.network.regions["L4Column_" + str(i)]) self.L2Columns = [region.getSelf() for region in self.L2Regions] self.L4Columns = [region.getSelf() for region in self.L4Regions] # will be populated during training self.objectL2Representations = {} self.objectL2RepresentationsMatrices = [ SparseMatrix(0, self.config["L2Params"]["cellCount"]) for _ in xrange(self.numColumns) ] self.objectNameToIndex = {} self.statistics = []
def __init__( self, name, numCorticalColumns=1, inputSize=1024, numInputBits=20, externalInputSize=1024, numExternalInputBits=20, L2Overrides=None, L2RegionType="py.ColumnPoolerRegion", L4RegionType="py.ApicalTMPairRegion", networkType="MultipleL4L2Columns", implementation=None, longDistanceConnections=0, maxConnectionDistance=1, columnPositions=None, L4Overrides=None, numLearningPoints=3, seed=42, logCalls=False, enableLateralSP=False, lateralSPOverrides=None, enableFeedForwardSP=False, feedForwardSPOverrides=None, objectNamesAreIndices=False, enableFeedback=True, maxSegmentsPerCell=10, ): """ Creates the network. Parameters: ---------------------------- @param name (str) Experiment name @param numCorticalColumns (int) Number of cortical columns in the network @param inputSize (int) Size of the sensory input @param numInputBits (int) Number of ON bits in the generated input patterns @param externalInputSize (int) Size of the lateral input to L4 regions @param numExternalInputBits (int) Number of ON bits in the external input patterns @param L2Overrides (dict) Parameters to override in the L2 region @param L2RegionType (string) The type of region to use for L2 @param L4RegionType (string) The type of region to use for L4 @param networkType (string) Which type of L2L4 network to create. If topology is being used, it should be specified here. Possible values for this parameter are "MultipleL4L2Columns", "MultipleL4L2ColumnsWithTopology" and "L4L2Column" @param longDistanceConnections (float) The probability that a column will randomly connect to a distant column. Should be in [0, 1). Only relevant when using multiple columns with topology. @param L4Overrides (dict) Parameters to override in the L4 region @param numLearningPoints (int) Number of times each pair should be seen to be learnt @param logCalls (bool) If true, calls to main functions will be logged internally. The log can then be saved with saveLogs(). This allows us to recreate the complete network behavior using rerunExperimentFromLogfile which is very useful for debugging. @param enableLateralSP (bool) If true, Spatial Pooler will be added between external input and L4 lateral input @param lateralSPOverrides Parameters to override in the lateral SP region @param enableFeedForwardSP (bool) If true, Spatial Pooler will be added between external input and L4 feed-forward input @param feedForwardSPOverrides Parameters to override in the feed-forward SP region @param objectNamesAreIndices (bool) If True, object names are used as indices in the getCurrentObjectOverlaps method. Object names must be positive integers. If False, object names can be strings, and indices will be assigned to each object name. @param enableFeedback (bool) If True, enable feedback between L2 and L4 """ # Handle logging - this has to be done first self.logCalls = logCalls registerAllResearchRegions() self.name = name self.numLearningPoints = numLearningPoints 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 if implementation is None: self.config = { "networkType": networkType, "longDistanceConnections": longDistanceConnections, "enableFeedback": enableFeedback, "numCorticalColumns": numCorticalColumns, "externalInputSize": externalInputSize, "sensorInputSize": inputSize, "L2RegionType": L2RegionType, "L4RegionType": L4RegionType, "L4Params": self.getDefaultL4Params(inputSize, numExternalInputBits), "L2Params": self.getDefaultL2Params(inputSize, numInputBits), } else: if "Bayesian" in implementation: self.config = { "networkType": networkType, "longDistanceConnections": longDistanceConnections, "enableFeedback": enableFeedback, "numCorticalColumns": numCorticalColumns, "externalInputSize": externalInputSize, "sensorInputSize": inputSize, "L2RegionType": L2RegionType, "L4RegionType": L4RegionType, "L4Params": self.getBayesianL4Params(inputSize, numExternalInputBits), "L2Params": self.getBayesianL2Params(inputSize, numInputBits), } self.config["L4Params"][ "maxSegmentsPerCell"] = maxSegmentsPerCell self.config["L4Params"]["implementation"] = implementation self.config["L2Params"]["implementation"] = implementation if enableLateralSP: self.config["lateralSPParams"] = self.getDefaultLateralSPParams( inputSize) if lateralSPOverrides: self.config["lateralSPParams"].update(lateralSPOverrides) if enableFeedForwardSP: self.config[ "feedForwardSPParams"] = self.getDefaultFeedForwardSPParams( inputSize) if feedForwardSPOverrides: self.config["feedForwardSPParams"].update( feedForwardSPOverrides) if "Topology" in self.config["networkType"]: self.config["maxConnectionDistance"] = maxConnectionDistance # Generate a grid for cortical columns. Will attempt to generate a full # square grid, and cut out positions starting from the bottom-right if the # number of cortical columns is not a perfect square. if columnPositions is None: columnPositions = [] side_length = int(np.ceil(np.sqrt(numCorticalColumns))) for i in range(side_length): for j in range(side_length): columnPositions.append((i, j)) self.config[ "columnPositions"] = columnPositions[:numCorticalColumns] self.config["longDistanceConnections"] = longDistanceConnections if L2Overrides is not None: self.config["L2Params"].update(L2Overrides) if L4Overrides is not None: self.config["L4Params"].update(L4Overrides) # create network self.network = createNetwork(self.config) self.sensorInputs = [] self.externalInputs = [] self.L4Regions = [] self.L2Regions = [] 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.L4Columns = [region.getSelf() for region in self.L4Regions] self.L2Columns = [region.getSelf() for region in self.L2Regions] # will be populated during training self.objectL2Representations = {} self.objectL2RepresentationsMatrices = [ SparseMatrix(0, self.config["L2Params"]["cellCount"]) for _ in xrange(self.numColumns) ] self.objectNameToIndex = {} self.statistics = []
def countBitFrequenciesForTerms(client, lines, acceptanceProbability=0.1, usePlaceholderEncoding=True, percentSparsity=0.0102): # Accumulate counts by inplace-adding sparse matrices skippedWords = {} counts = SparseMatrix() width = RETINA_SIZES[client.retina]["width"] height = RETINA_SIZES[client.retina]["height"] counts.resize(1, width * height) # Pre-allocate buffer sparse matrix sparseBitmap = SparseMatrix() sparseBitmap.resize(1, width * height) # Accumulate counts for each bit for each word numWords = 0 numLines = 0 for line in lines: tokens = TextPreprocess().tokenize(line) for term in tokens: p = random.uniform(0, 1) if p <= acceptanceProbability: if usePlaceholderEncoding: random.seed(term) bitmap = random.sample( xrange(width * height), int(width * height * percentSparsity)) bitmap.sort() random.seed(p) else: try: bitmap = client.getBitmap( term)["fingerprint"]["positions"] except Exception as err: print "Skipping '{}', reason: {}".format( term, str(err)) continue if not bitmap: skippedWords[term] = skippedWords.get(term, 0) + 1 # print "Skipping '{}', reason: empty".format(term) continue sparseBitmap.setRowFromSparse(0, bitmap, [1] * len(bitmap)) counts += sparseBitmap numWords += 1 numLines += 1 if numLines % 1000 == 0: print "...processed=", numLines, "lines and", numWords, "words" # Compute normalized version of counts as a separate matrix frequencies = SparseMatrix() frequencies.resize(1, width * height) frequencies.copy(counts) frequencies.divide(float(numWords)) # Wrap up by printing some statistics and then saving the normalized version print "Processed", numLines, "lines" printFrequencyStatistics(counts, frequencies, numWords, width * height) frequencyFilename = "bit_frequencies_" + client.retina + ".pkl" print "Saving frequency matrix in", frequencyFilename with open(frequencyFilename, "wb") as frequencyPickleFile: pickle.dump(frequencies, frequencyPickleFile) print "These words were skipped N times because of empty bitmap result" print skippedWords return counts
class ColumnPooler(object): """ This class constitutes a temporary implementation for a cross-column pooler. The implementation goal of this class is to prove basic properties before creating a cleaner implementation. """ def __init__(self, inputWidth, lateralInputWidths=(), cellCount=4096, sdrSize=40, onlineLearning = False, maxSdrSize = None, minSdrSize = None, # Proximal synPermProximalInc=0.1, synPermProximalDec=0.001, initialProximalPermanence=0.6, sampleSizeProximal=20, minThresholdProximal=10, connectedPermanenceProximal=0.50, predictedInhibitionThreshold=20, # Distal synPermDistalInc=0.1, synPermDistalDec=0.001, initialDistalPermanence=0.6, sampleSizeDistal=20, activationThresholdDistal=13, connectedPermanenceDistal=0.50, distalSegmentInhibitionFactor=0.999, inertiaFactor=1., seed=42): """ Parameters: ---------------------------- @param inputWidth (int) The number of bits in the feedforward input @param lateralInputWidths (list of ints) The number of bits in each lateral input @param sdrSize (int) The number of active cells in an object SDR @param onlineLearning (Bool) Whether or not the column pooler should learn in online mode. @param maxSdrSize (int) The maximum SDR size for learning. If the column pooler has more than this many cells active, it will refuse to learn. This serves to stop the pooler from learning when it is uncertain of what object it is sensing. @param minSdrSize (int) The minimum SDR size for learning. If the column pooler has fewer than this many active cells, it will create a new representation and learn that instead. This serves to create separate representations for different objects and sequences. If online learning is enabled, this parameter should be at least inertiaFactor*sdrSize. Otherwise, two different objects may be incorrectly inferred to be the same, as SDRs may still be active enough to learn even after inertial decay. @param synPermProximalInc (float) Permanence increment for proximal synapses @param synPermProximalDec (float) Permanence decrement for proximal synapses @param initialProximalPermanence (float) Initial permanence value for proximal synapses @param sampleSizeProximal (int) Number of proximal synapses a cell should grow to each feedforward pattern, or -1 to connect to every active bit @param minThresholdProximal (int) Number of active synapses required for a cell to have feedforward support @param connectedPermanenceProximal (float) Permanence required for a proximal synapse to be connected @param predictedInhibitionThreshold (int) How much predicted input must be present for inhibitory behavior to be triggered. Only has effects if onlineLearning is true. @param synPermDistalInc (float) Permanence increment for distal synapses @param synPermDistalDec (float) Permanence decrement for distal synapses @param sampleSizeDistal (int) Number of distal synapses a cell should grow to each lateral pattern, or -1 to connect to every active bit @param initialDistalPermanence (float) Initial permanence value for distal synapses @param activationThresholdDistal (int) Number of active synapses required to activate a distal segment @param connectedPermanenceDistal (float) Permanence required for a distal synapse to be connected @param distalSegmentInhibitionFactor (float) The proportion of the highest number of active lateral segments necessary for a cell not to be inhibited (must be <1). @param inertiaFactor (float) The proportion of previously active cells that remain active in the next timestep due to inertia (in the absence of inhibition). If onlineLearning is enabled, should be at most 1 - learningTolerance, or representations may incorrectly become mixed. @param seed (int) Random number generator seed """ assert distalSegmentInhibitionFactor > 0.0 assert distalSegmentInhibitionFactor < 1.0 assert maxSdrSize is None or maxSdrSize >= sdrSize assert minSdrSize is None or minSdrSize <= sdrSize self.inputWidth = inputWidth self.cellCount = cellCount self.sdrSize = sdrSize self.onlineLearning = onlineLearning if maxSdrSize is None: self.maxSdrSize = sdrSize else: self.maxSdrSize = maxSdrSize if minSdrSize is None: self.minSdrSize = sdrSize else: self.minSdrSize = minSdrSize self.synPermProximalInc = synPermProximalInc self.synPermProximalDec = synPermProximalDec self.initialProximalPermanence = initialProximalPermanence self.connectedPermanenceProximal = connectedPermanenceProximal self.sampleSizeProximal = sampleSizeProximal self.minThresholdProximal = minThresholdProximal self.predictedInhibitionThreshold = predictedInhibitionThreshold self.synPermDistalInc = synPermDistalInc self.synPermDistalDec = synPermDistalDec self.initialDistalPermanence = initialDistalPermanence self.connectedPermanenceDistal = connectedPermanenceDistal self.sampleSizeDistal = sampleSizeDistal self.activationThresholdDistal = activationThresholdDistal self.distalSegmentInhibitionFactor = distalSegmentInhibitionFactor self.inertiaFactor = inertiaFactor self.activeCells = numpy.empty(0, dtype="uint32") self._random = Random(seed) # These sparse matrices will hold the synapses for each segment. # Each row represents one segment on a cell, so each cell potentially has # 1 proximal segment and 1+len(lateralInputWidths) distal segments. self.proximalPermanences = SparseMatrix(cellCount, inputWidth) self.internalDistalPermanences = SparseMatrix(cellCount, cellCount) self.distalPermanences = tuple(SparseMatrix(cellCount, n) for n in lateralInputWidths) self.useInertia=True def compute(self, feedforwardInput=(), lateralInputs=(), feedforwardGrowthCandidates=None, learn=True, predictedInput = None,): """ Runs one time step of the column pooler algorithm. @param feedforwardInput (sequence) Sorted indices of active feedforward input bits @param lateralInputs (list of sequences) For each lateral layer, a list of sorted indices of active lateral input bits @param feedforwardGrowthCandidates (sequence or None) Sorted indices of feedforward input bits that active cells may grow new synapses to. If None, the entire feedforwardInput is used. @param learn (bool) If True, we are learning a new object @param predictedInput (sequence) Sorted indices of predicted cells in the TM layer. """ if feedforwardGrowthCandidates is None: feedforwardGrowthCandidates = feedforwardInput if not learn: self._computeInferenceMode(feedforwardInput, lateralInputs) elif not self.onlineLearning: self._computeLearningMode(feedforwardInput, lateralInputs, feedforwardGrowthCandidates) else: if (predictedInput is not None and len(predictedInput) > self.predictedInhibitionThreshold): predictedActiveInput = numpy.intersect1d(feedforwardInput, predictedInput) predictedGrowthCandidates = numpy.intersect1d( feedforwardGrowthCandidates, predictedInput) self._computeInferenceMode(predictedActiveInput, lateralInputs) self._computeLearningMode(predictedActiveInput, lateralInputs, feedforwardGrowthCandidates) elif not self.minSdrSize <= len(self.activeCells) <= self.maxSdrSize: # If the pooler doesn't have a single representation, try to infer one, # before actually attempting to learn. self._computeInferenceMode(feedforwardInput, lateralInputs) self._computeLearningMode(feedforwardInput, lateralInputs, feedforwardGrowthCandidates) else: # If there isn't predicted input and we have a single SDR, # we are extending that representation and should just learn. self._computeLearningMode(feedforwardInput, lateralInputs, feedforwardGrowthCandidates) def _computeLearningMode(self, feedforwardInput, lateralInputs, feedforwardGrowthCandidates): """ Learning mode: we are learning a new object in an online fashion. If there is no prior activity, we randomly activate 'sdrSize' cells and create connections to incoming input. If there was prior activity, we maintain it. If we have a union, we simply do not learn at all. These cells will represent the object and learn distal connections to each other and to lateral cortical columns. Parameters: ---------------------------- @param feedforwardInput (sequence) Sorted indices of active feedforward input bits @param lateralInputs (list of sequences) For each lateral layer, a list of sorted indices of active lateral input bits @param feedforwardGrowthCandidates (sequence or None) Sorted indices of feedforward input bits that the active cells may grow new synapses to. This is assumed to be the predicted active cells of the input layer. """ prevActiveCells = self.activeCells # If there are not enough previously active cells, then we are no longer on # a familiar object. Either our representation decayed due to the passage # of time (i.e. we moved somewhere else) or we were mistaken. Either way, # create a new SDR and learn on it. # This case is the only way different object representations are created. if len(self.activeCells) < self.minSdrSize: self.activeCells = _sampleRange(self._random, 0, self.numberOfCells(), step=1, k=self.sdrSize) self.activeCells.sort() # If we have a union of cells active, don't learn. This primarily affects # online learning. if len(self.activeCells) > self.maxSdrSize: return # Finally, now that we have decided which cells we should be learning on, do # the actual learning. if len(feedforwardInput) > 0: self._learn(self.proximalPermanences, self._random, self.activeCells, feedforwardInput, feedforwardGrowthCandidates, self.sampleSizeProximal, self.initialProximalPermanence, self.synPermProximalInc, self.synPermProximalDec, self.connectedPermanenceProximal) # External distal learning for i, lateralInput in enumerate(lateralInputs): self._learn(self.distalPermanences[i], self._random, self.activeCells, lateralInput, lateralInput, self.sampleSizeDistal, self.initialDistalPermanence, self.synPermDistalInc, self.synPermDistalDec, self.connectedPermanenceDistal) # Internal distal learning self._learn(self.internalDistalPermanences, self._random, self.activeCells, prevActiveCells, prevActiveCells, self.sampleSizeDistal, self.initialDistalPermanence, self.synPermDistalInc, self.synPermDistalDec, self.connectedPermanenceDistal) def _computeInferenceMode(self, feedforwardInput, lateralInputs): """ Inference mode: if there is some feedforward activity, perform spatial pooling on it to recognize previously known objects, then use lateral activity to activate a subset of the cells with feedforward support. If there is no feedforward activity, use lateral activity to activate a subset of the previous active cells. Parameters: ---------------------------- @param feedforwardInput (sequence) Sorted indices of active feedforward input bits @param lateralInputs (list of sequences) For each lateral layer, a list of sorted indices of active lateral input bits """ prevActiveCells = self.activeCells # Calculate the feedforward supported cells overlaps = self.proximalPermanences.rightVecSumAtNZGteThresholdSparse( feedforwardInput, self.connectedPermanenceProximal) feedforwardSupportedCells = numpy.where( overlaps >= self.minThresholdProximal)[0] # Calculate the number of active segments on each cell numActiveSegmentsByCell = numpy.zeros(self.cellCount, dtype="int") overlaps = self.internalDistalPermanences.rightVecSumAtNZGteThresholdSparse( prevActiveCells, self.connectedPermanenceDistal) numActiveSegmentsByCell[overlaps >= self.activationThresholdDistal] += 1 for i, lateralInput in enumerate(lateralInputs): overlaps = self.distalPermanences[i].rightVecSumAtNZGteThresholdSparse( lateralInput, self.connectedPermanenceDistal) numActiveSegmentsByCell[overlaps >= self.activationThresholdDistal] += 1 chosenCells = [] minNumActiveCells = int(self.sdrSize * 0.75) numActiveSegsForFFSuppCells = numActiveSegmentsByCell[ feedforwardSupportedCells] # First, activate the FF-supported cells that have the highest number of # lateral active segments (as long as it's not 0) if len(feedforwardSupportedCells) == 0: pass else: # This loop will select the FF-supported AND laterally-active cells, in # order of descending lateral activation, until we exceed the # minNumActiveCells quorum - but will exclude cells with 0 lateral # active segments. ttop = numpy.max(numActiveSegsForFFSuppCells) while ttop > 0 and len(chosenCells) <= minNumActiveCells: chosenCells = numpy.union1d(chosenCells, feedforwardSupportedCells[numActiveSegsForFFSuppCells > self.distalSegmentInhibitionFactor * ttop]) ttop -= 1 # If we still haven't filled the minNumActiveCells quorum, add in the # FF-supported cells with 0 lateral support AND the inertia cells. if len(chosenCells) < minNumActiveCells: if self.useInertia: prevCells = numpy.setdiff1d(prevActiveCells, chosenCells) inertialCap = int(len(prevCells) * self.inertiaFactor) if inertialCap > 0: numActiveSegsForPrevCells = numActiveSegmentsByCell[prevCells] # We sort the previously-active cells by number of active lateral # segments (this really helps). We then activate them in order of # descending lateral activation. sortIndices = numpy.argsort(numActiveSegsForPrevCells)[::-1] prevCells = prevCells[sortIndices] numActiveSegsForPrevCells = numActiveSegsForPrevCells[sortIndices] # We use inertiaFactor to limit the number of previously-active cells # which can become active, forcing decay even if we are below quota. prevCells = prevCells[:inertialCap] numActiveSegsForPrevCells = numActiveSegsForPrevCells[:inertialCap] # Activate groups of previously active cells by order of their lateral # support until we either meet quota or run out of cells. ttop = numpy.max(numActiveSegsForPrevCells) while ttop >= 0 and len(chosenCells) <= minNumActiveCells: chosenCells = numpy.union1d(chosenCells, prevCells[numActiveSegsForPrevCells > self.distalSegmentInhibitionFactor * ttop]) ttop -= 1 # Finally, add remaining cells with feedforward support remFFcells = numpy.setdiff1d(feedforwardSupportedCells, chosenCells) # Note that this is 100% of the remaining FF-supported cells, there is no # attempt to select only certain ones or limit how many come active. chosenCells = numpy.append(chosenCells, remFFcells) chosenCells.sort() self.activeCells = numpy.asarray(chosenCells, dtype="uint32") def numberOfInputs(self): """ Returns the number of inputs into this layer """ return self.inputWidth def numberOfCells(self): """ Returns the number of cells in this layer. @return (int) Number of cells """ return self.cellCount def getActiveCells(self): """ Returns the indices of the active cells. @return (list) Indices of active cells. """ return self.activeCells def numberOfConnectedProximalSynapses(self, cells=None): """ Returns the number of proximal connected synapses on these cells. Parameters: ---------------------------- @param cells (iterable) Indices of the cells. If None return count for all cells. """ if cells is None: cells = xrange(self.numberOfCells()) return _countWhereGreaterEqualInRows(self.proximalPermanences, cells, self.connectedPermanenceProximal) def numberOfProximalSynapses(self, cells=None): """ Returns the number of proximal synapses with permanence>0 on these cells. Parameters: ---------------------------- @param cells (iterable) Indices of the cells. If None return count for all cells. """ if cells is None: cells = xrange(self.numberOfCells()) n = 0 for cell in cells: n += self.proximalPermanences.nNonZerosOnRow(cell) return n def numberOfDistalSegments(self, cells=None): """ Returns the total number of distal segments for these cells. A segment "exists" if its row in the matrix has any permanence values > 0. Parameters: ---------------------------- @param cells (iterable) Indices of the cells """ if cells is None: cells = xrange(self.numberOfCells()) n = 0 for cell in cells: if self.internalDistalPermanences.nNonZerosOnRow(cell) > 0: n += 1 for permanences in self.distalPermanences: if permanences.nNonZerosOnRow(cell) > 0: n += 1 return n def numberOfConnectedDistalSynapses(self, cells=None): """ Returns the number of connected distal synapses on these cells. Parameters: ---------------------------- @param cells (iterable) Indices of the cells. If None return count for all cells. """ if cells is None: cells = xrange(self.numberOfCells()) n = _countWhereGreaterEqualInRows(self.internalDistalPermanences, cells, self.connectedPermanenceDistal) for permanences in self.distalPermanences: n += _countWhereGreaterEqualInRows(permanences, cells, self.connectedPermanenceDistal) return n def numberOfDistalSynapses(self, cells=None): """ Returns the total number of distal synapses for these cells. Parameters: ---------------------------- @param cells (iterable) Indices of the cells """ if cells is None: cells = xrange(self.numberOfCells()) n = 0 for cell in cells: n += self.internalDistalPermanences.nNonZerosOnRow(cell) for permanences in self.distalPermanences: n += permanences.nNonZerosOnRow(cell) return n def reset(self): """ Reset internal states. When learning this signifies we are to learn a unique new object. """ self.activeCells = numpy.empty(0, dtype="uint32") def getUseInertia(self): """ Get whether we actually use inertia (i.e. a fraction of the previously active cells remain active at the next time step unless inhibited by cells with both feedforward and lateral support). @return (Bool) Whether inertia is used. """ return self.useInertia def setUseInertia(self, useInertia): """ Sets whether we actually use inertia (i.e. a fraction of the previously active cells remain active at the next time step unless inhibited by cells with both feedforward and lateral support). @param useInertia (Bool) Whether inertia is used. """ self.useInertia = useInertia @staticmethod def _learn(# mutated args permanences, rng, # activity activeCells, activeInput, growthCandidateInput, # configuration sampleSize, initialPermanence, permanenceIncrement, permanenceDecrement, connectedPermanence): """ For each active cell, reinforce active synapses, punish inactive synapses, and grow new synapses to a subset of the active input bits that the cell isn't already connected to. Parameters: ---------------------------- @param permanences (SparseMatrix) Matrix of permanences, with cells as rows and inputs as columns @param rng (Random) Random number generator @param activeCells (sorted sequence) Sorted list of the cells that are learning @param activeInput (sorted sequence) Sorted list of active bits in the input @param growthCandidateInput (sorted sequence) Sorted list of active bits in the input that the activeCells may grow new synapses to For remaining parameters, see the __init__ docstring. """ permanences.incrementNonZerosOnOuter( activeCells, activeInput, permanenceIncrement) permanences.incrementNonZerosOnRowsExcludingCols( activeCells, activeInput, -permanenceDecrement) permanences.clipRowsBelowAndAbove( activeCells, 0.0, 1.0) if sampleSize == -1: permanences.setZerosOnOuter( activeCells, activeInput, initialPermanence) else: existingSynapseCounts = permanences.nNonZerosPerRowOnCols( activeCells, activeInput) maxNewByCell = numpy.empty(len(activeCells), dtype="int32") numpy.subtract(sampleSize, existingSynapseCounts, out=maxNewByCell) permanences.setRandomZerosOnOuter( activeCells, growthCandidateInput, maxNewByCell, initialPermanence, rng)
def countRandomBitFrequencies(numTerms=100000, percentSparsity=0.01): """Create a uniformly random counts matrix through sampling.""" # Accumulate counts by inplace-adding sparse matrices counts = SparseMatrix() size = 128 * 128 counts.resize(1, size) # Pre-allocate buffer sparse matrix sparseBitmap = SparseMatrix() sparseBitmap.resize(1, size) random.seed(42) # Accumulate counts for each bit for each word numWords = 0 for term in xrange(numTerms): bitmap = random.sample(xrange(size), int(size * percentSparsity)) bitmap.sort() sparseBitmap.setRowFromSparse(0, bitmap, [1] * len(bitmap)) counts += sparseBitmap numWords += 1 # Compute normalized version of counts as a separate matrix frequencies = SparseMatrix() frequencies.resize(1, size) frequencies.copy(counts) frequencies.divide(float(numWords)) # Wrap up by printing some statistics and then saving the normalized version printFrequencyStatistics(counts, frequencies, numWords, size) frequencyFilename = "bit_frequencies_random.pkl" print "Saving frequency matrix in", frequencyFilename with open(frequencyFilename, "wb") as frequencyPickleFile: pickle.dump(frequencies, frequencyPickleFile) return counts
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 __init__(self, name, numCorticalColumns=1, inputSize=1024, numInputBits=20, externalInputSize=1024, numExternalInputBits=20, L2Overrides=None, L4RegionType="py.ExtendedTMRegion", L4Overrides=None, numLearningPoints=3, seed=42, logCalls=False, enableLateralSP=False, lateralSPOverrides=None, enableFeedForwardSP=False, feedForwardSPOverrides=None, objectNamesAreIndices=False ): """ Creates the network. Parameters: ---------------------------- @param name (str) Experiment name @param numCorticalColumns (int) Number of cortical columns in the network @param inputSize (int) Size of the sensory input @param numInputBits (int) Number of ON bits in the generated input patterns @param externalInputSize (int) Size of the lateral input to L4 regions @param numExternalInputBits (int) Number of ON bits in the external input patterns @param L2Overrides (dict) Parameters to override in the L2 region @param L4RegionType (string) The type of region to use for L4 @param L4Overrides (dict) Parameters to override in the L4 region @param numLearningPoints (int) Number of times each pair should be seen to be learnt @param logCalls (bool) If true, calls to main functions will be logged internally. The log can then be saved with saveLogs(). This allows us to recreate the complete network behavior using rerunExperimentFromLogfile which is very useful for debugging. @param enableLateralSP (bool) If true, Spatial Pooler will be added between external input and L4 lateral input @param lateralSPOverrides Parameters to override in the lateral SP region @param enableFeedForwardSP (bool) If true, Spatial Pooler will be added between external input and L4 feed-forward input @param feedForwardSPOverrides Parameters to override in the feed-forward SP region @param objectNamesAreIndices (bool) If True, object names are used as indices in the getCurrentObjectOverlaps method. Object names must be positive integers. If False, object names can be strings, and indices will be assigned to each object name. """ # Handle logging - this has to be done first self.logCalls = logCalls registerAllResearchRegions() self.name = name self.numLearningPoints = numLearningPoints 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": "MultipleL4L2Columns", "numCorticalColumns": numCorticalColumns, "externalInputSize": externalInputSize, "sensorInputSize": inputSize, "L4RegionType": L4RegionType, "L4Params": self.getDefaultL4Params(L4RegionType, inputSize, numExternalInputBits), "L2Params": self.getDefaultL2Params(inputSize, numInputBits), } if enableLateralSP: self.config["lateralSPParams"] = self.getDefaultLateralSPParams(inputSize) if lateralSPOverrides: self.config["lateralSPParams"].update(lateralSPOverrides) if enableFeedForwardSP: self.config["feedForwardSPParams"] = self.getDefaultFeedForwardSPParams(inputSize) if feedForwardSPOverrides: self.config["feedForwardSPParams"].update(feedForwardSPOverrides) if L2Overrides is not None: self.config["L2Params"].update(L2Overrides) if L4Overrides is not None: self.config["L4Params"].update(L4Overrides) # create network self.network = createNetwork(self.config) self.sensorInputs = [] self.externalInputs = [] self.L4Regions = [] self.L2Regions = [] 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.L4Columns = [region.getSelf() for region in self.L4Regions] self.L2Columns = [region.getSelf() for region in self.L2Regions] # will be populated during training self.objectL2Representations = {} self.objectL2RepresentationsMatrices = [ SparseMatrix(0, self.config["L2Params"]["cellCount"]) for _ in xrange(self.numColumns)] self.objectNameToIndex = {} self.statistics = []