/// <summary> /// Add a new SegmentUpdateInfo object to this Cell containing proposed changes to the /// specified segment. /// </summary> /// <remarks> /// If the segment is NULL, then a new segment is to be added, otherwise /// the specified segment is updated. If the segment exists, find all active /// synapses for the segment (either at t or t-1 based on the 'previous' parameter) /// and mark them as needing to be updated. If newSynapses is true, then /// Region.newSynapseCount - len(activeSynapses) new synapses are added to the /// segment to be updated. The (new) synapses are randomly chosen from the set /// of current learning cells (within Region.predictionRadius if set). /// /// These segment updates are only applied when the applySegmentUpdates /// method is later called on this Cell. /// </remarks> public SegmentUpdateInfo UpdateSegmentActiveSynapses(bool previous, Segment segment, bool newSynapses, UpdateType updateType) { List <DistalSynapse> activeDistalSyns = null; if (segment != null) { List <Synapse> activeSyns = previous ? segment.PrevActiveSynapses : segment.ActiveSynapses; activeDistalSyns = activeSyns.Cast <DistalSynapse>().ToList(); } var segmentUpdate = new SegmentUpdateInfo(); segmentUpdate.Initialize(this, segment, activeDistalSyns, newSynapses, this.Column.Region.StepCounter, updateType); this._segmentUpdates.Add(segmentUpdate); return(segmentUpdate); }
/// <summary> /// This function reinforces each segment in this Cell's SegmentUpdateInfo. /// </summary> /// <remarks> /// Using the segmentUpdateInfo, the following changes are /// performed. If positiveReinforcement is true then synapses on the active /// list get their permanence counts incremented by permanenceInc. All other /// synapses get their permanence counts decremented by permanenceDec. If /// positiveReinforcement is false, then synapses on the active list get /// their permanence counts decremented by permanenceDec. After this step, /// any synapses in segmentUpdate that do yet exist get added with a permanence /// count of initialPerm. These new synapses are randomly chosen from the /// set of all cells that have learnState output = 1 at time step t. /// </remarks> public void ApplySegmentUpdates(int curTime, ApplyUpdateTrigger trigger) { Segment segment = null; var modifiedSegments = new List <Segment>(); // Iterate through all segment updates, skipping those not to be applied now, and removing those that are applied. int segInfoIndex = 0; while (segInfoIndex < this._segmentUpdates.Count) { SegmentUpdateInfo segInfo = this._segmentUpdates[segInfoIndex]; // Do not apply the current segment update if it was created at the current time step, and was created as a result of the cell being predictive. // If this is the case, then this segment update can only be evaluated at a later time step, and so should remain in the queue for now. bool applyUpdate = !((segInfo.CreationTimeStep == curTime) && (segInfo.UpdateType == UpdateType.DueToPredictive)); // Do not apply the current segment update if its numPredictionSteps > 1, and if we are applying updates due to the cell still being predictive, // but with a greater numPredictionSteps. Unless the segment being updated predicted activation in 1 prediction step, it cannot be proven // incorrect, and so shouldn't be processed yet. This is because a "prediction step" can take more than one time step. if ((trigger == ApplyUpdateTrigger.LongerPrediction) && (segInfo.NumPredictionSteps > 1)) { applyUpdate = false; } if (applyUpdate) { segment = segInfo.Segment; if (segment != null) { if (trigger == ApplyUpdateTrigger.Active) { // The cell has become actve; positively reinforce the segment. segment.UpdatePermanences(ref segInfo.ActiveDistalSynapses); } else { // The cell has become inactive of is predictive but with a longer prediction. Negatively reinforce the segment. segment.DecreasePermanences(ref segInfo.ActiveDistalSynapses); } // Record that this segment has been modified, so it can later be checked for synapses that should be removed. if (modifiedSegments.Contains(segment) == false) { modifiedSegments.Add(segment); } } // Add new synapses (and new segment if necessary) if (segInfo.AddNewSynapses && (trigger == ApplyUpdateTrigger.Active)) { if (segment == null) { if (segInfo.CellsThatWillLearn.Count > 0) //only add if learning cells available { segment = segInfo.CreateCellSegment(this.Column.Region.StepCounter); } } else if (segInfo.CellsThatWillLearn.Count > 0) { //add new synapses to existing segment segInfo.CreateSynapsesToLearningCells(this.Column.Region.DistalSynapseParams); } } // Remove and release the current SegmentUpdateInfo. this._segmentUpdates.RemoveAt(segInfoIndex); } else { segInfoIndex++; } } // Only process modified segments if all segment updates have been processed, to avoid deleting segments or synapses that // are referred to by still-existing segment updates. if (this._segmentUpdates.Count == 0) { // All segment updates have been processed, so there are none left that may have references to this cell's // synapses or segments. Therefore we can iterate through all segments modified above, to prune unneeded synpses and segments. while (modifiedSegments.Count > 0) { // Get pointer to the current modified segment, and remove it from the modifiedSegments list. segment = modifiedSegments[0]; modifiedSegments.RemoveAt(0); // Remove from the current modified segment any synapses that have reached permanence of 0. int synIndex = 0; while (synIndex < segment.Synapses.Count) { Synapse syn = segment.Synapses[synIndex]; if (syn.Permanence == 0.0f) { // Remove and release the current synapse, whose permanence has reached 0. segment.Synapses.RemoveAt(synIndex); } else { synIndex++; } } // If this modified segment now has no synapses, remove the segment from this cell. if (segment.Synapses.Count == 0) { this.Segments.Remove(segment); } } } else { // Not all of the segment updates have been processed, so synapses and segments cannot be pruned. // (because they may be referred to by segment updates that still exist). So just clear the list of modified segments. modifiedSegments.Clear(); } }
/// <summary> /// Performs temporal pooling based on the current spatial pooler output. /// </summary> /// <remarks> /// From the Numenta white paper: /// The input to this code is activeColumns(t), as computed by the spatial pooler. /// The code computes the active and predictive state for each cell at the current /// timestep, t. The boolean OR of the active and predictive states for each cell /// forms the output of the temporal pooler for the next level. /// Phase 1: /// Compute the active state, activeState(t), for each cell. /// The first phase calculates the activeState for each cell that is in a winning /// column. For those columns, the code further selects one cell per column as the /// learning cell (learnState). The logic is as follows: if the bottom-up input was /// predicted by any cell (i.e. its predictiveState output was 1 due to a sequence /// segmentUpdateList), then those cells become active (lines 23-27). /// If that segmentUpdateList became active from cells chosen with learnState on, /// this cell is selected as the learning cell (lines 28-30). If the bottom-up input /// was not predicted, then all cells in the column become active (lines 32-34). /// In addition, the best matching cell is chosen as the learning cell (lines 36-41) /// and a new segmentUpdateList is added to that cell. /// Phase 2: /// Compute the predicted state, predictiveState(t), for each cell. /// The second phase calculates the predictive state for each cell. A cell will turn /// on its predictive state output if one of its segments becomes active, i.e. if /// enough of its lateral inputs are currently active due to feed-forward input. /// In this case, the cell queues up the following changes: a) reinforcement of the /// currently active segmentUpdateList (lines 47-48), and b) reinforcement of a /// segmentUpdateList that could have predicted this activation, i.e. a /// segmentUpdateList that has a (potentially weak) match to activity during the /// previous time step (lines 50-53). /// Phase 3: /// Update synapses. The third and last phase actually carries out learning. In this /// phase segmentUpdateList updates that have been queued up are actually implemented /// once we get feed-forward input and the cell is chosen as a learning cell /// (lines 56-57). Otherwise, if the cell ever stops predicting for any reason, we /// negatively reinforce the segments (lines 58-60). /// </remarks> public void PerformTemporalPooling() { int colIndex; Column column; Cell cell; // Determine whether temporal learning is currently allowed. bool allowTemporalLearning = Global.AllowTemporalLearning && ((this.TemporalLearningStartTime == -1) || (this.TemporalLearningStartTime <= this.StepCounter)) && ((this.TemporalLearningEndTime == -1) || (this.TemporalLearningEndTime >= this.StepCounter)); // Phase 1: Compute cell active states and segment learning updates for (colIndex = 0; colIndex < this.SizeX * this.SizeY; colIndex++) { column = this.Columns[colIndex]; if (column.IsActive) { bool predicted = false; bool learnCellChosen = false; for (int cellIndex = 0; cellIndex < this.CellsPerCol; cellIndex++) { cell = column.Cells[cellIndex]; if (cell.WasPredicted) { Segment segment = cell.GetPreviousActiveSegment(); if ((segment != null) && segment.IsSequence) { predicted = true; cell.IsActive = true; if (allowTemporalLearning && segment.GetWasActiveFromLearning()) { learnCellChosen = true; cell.IsLearning = true; } } } } if (!predicted) { for (int cellIndex = 0; cellIndex < this.CellsPerCol; cellIndex++) { cell = column.Cells[cellIndex]; cell.IsActive = true; } } if (allowTemporalLearning && !learnCellChosen) { // isSequence=true, previous=true Cell bestCell = null; Segment bestSegment = null; column.GetBestMatchingCell(1, true, out bestCell, out bestSegment); if (Global.Debug) { Debug.Assert(bestCell != null); } bestCell.IsLearning = true; // segmentToUpdate is added internally to the bestCell's update list. // Then set number of prediction steps to 1 (meaning it's a sequence segment) SegmentUpdateInfo segmentUpdateInfo = bestCell.UpdateSegmentActiveSynapses(true, bestSegment, true, UpdateType.DueToActive); segmentUpdateInfo.NumPredictionSteps = 1; } } } // Phase 2: Compute the predicted state for each cell for (colIndex = 0; colIndex < this.SizeX * this.SizeY; colIndex++) { column = this.Columns[colIndex]; for (int cellIndex = 0; cellIndex < this.CellsPerCol; cellIndex++) { cell = column.Cells[cellIndex]; // Process all segments on the cell to cache the activity for later. foreach (Segment seg in cell.Segments) { seg.ProcessSegment(); } foreach (Segment seg in cell.Segments) { // Now check for an active segment, we only need one for the cell to predict, but all Segments need to be checked // so that a segment update will be created for each active segment, and so that the lowest numPredictionSteps // among active segments is adopted by the cell. if (seg.IsActive) { cell.SetIsPredicting(true, seg.NumPredictionSteps); if (seg.IsSequence) { cell.IsSegmentPredicting = true; } // a) reinforcement of the currently active segments if (allowTemporalLearning) { // Add segment update to this cell cell.UpdateSegmentActiveSynapses(false, seg, false, UpdateType.DueToPredictive); } } } // b) reinforcement of a segment that could have predicted // this activation, i.e. a segment that has a (potentially weak) // match to activity during the previous time step (lines 50-53). // NOTE: The check against MaxTimeSteps is a correctly functioning way of enforcing a maximum number of time steps, // as opposed to the previous way of storing Max(numPredictionSteps, MaxTimeSteps) as a segment's numPredictionSteps, // which caused inaccurate numPredictionSteps values to be stored, resulting in duplicate segments being created. // Note also that if the system of recording and using an exact number of time steps is abandonded (and replaced with the // original sequence/non-sequence system), then all references to MaxTimeSteps can be removed. if (allowTemporalLearning && cell.IsPredicting && (cell.NumPredictionSteps != Segment.MaxTimeSteps)) { Segment predictiveSegment = cell.GetBestMatchingPreviousSegment(); // Either update existing or add new segment for this cell considering // only segments matching the number of prediction steps of the // best currently active segment for this cell. SegmentUpdateInfo predictiveSegUpdate = cell.UpdateSegmentActiveSynapses(true, predictiveSegment, true, UpdateType.DueToPredictive); if (predictiveSegment == null) { predictiveSegUpdate.NumPredictionSteps = cell.NumPredictionSteps + 1; } } } } // Phase 3: Update synapses if (allowTemporalLearning) { for (colIndex = 0; colIndex < this.SizeX * this.SizeY; colIndex++) { column = this.Columns[colIndex]; for (int cellIndex = 0; cellIndex < this.CellsPerCol; cellIndex++) { cell = column.Cells[cellIndex]; if (cell.IsLearning) { cell.ApplySegmentUpdates(this.StepCounter, ApplyUpdateTrigger.Active); } else if ((cell.IsPredicting == false) && cell.WasPredicted) { cell.ApplySegmentUpdates(this.StepCounter, ApplyUpdateTrigger.Inactive); } else if (cell.IsPredicting && cell.WasPredicted && (cell.NumPredictionSteps > 1) && (cell.PrevNumPredictionSteps == 1)) { cell.ApplySegmentUpdates(this.StepCounter, ApplyUpdateTrigger.LongerPrediction); } } } } }