/// <summary> /// set up values from constructor and intialize global value /// </summary> /// <param name="steps">sequence of the different steps of multi-step predictions to learn</param> /// <param name="alpha">The alpha used to compute running averages of the bucket duty ///cycles for each activation pattern bit.A lower alpha results ///in longer term memory.</param> /// <param name="actValueAlpha"></param> private void Initialize(IList <int> steps, double alpha, double actValueAlpha) { this.steps = steps; this.alpha = alpha; this.actValueAlpha = actValueAlpha; //for null value add -1 instead of 0 actualValues.Add(IsNumericType(default(T)) ? ConversionExtensions.Convert <T>(-1) : default(T)); }
/// <summary> /// Method computes the result after the data is provided by the temporal memory and encoder /// </summary> /// <param name="recordNum">Record number of this input pattern. /// Record numbers should normally increase sequentially by 1 each time unless there are missing records in the dataset. /// Knowing this information insures that we don't get confused by missing records.</param> /// <param name="classification">{@link Map} of the classification information: /// bucketIdx: index of the encoder bucket</param> /// <param name="patternNZ">list of the active indices from the output below</param> /// <param name="learn">if true, learn this sample</param> /// <param name="infer">if true, perform inference</param> /// <returns>ClassificationExperiment Object</returns> public ClassificationExperiment <T> Compute(int recordNum, Dictionary <string, object> classification, int[] patternNZ, bool learn, bool infer) { if (classification == null) { throw new ArgumentNullException(nameof(classification)); } if (patternNZ == null) { throw new ArgumentNullException(nameof(patternNZ)); } ClassificationExperiment <T> retVal = new ClassificationExperiment <T>(); List <T> actualValues = (List <T>) this.actualValues; // Save the offset between recordNum and learnIteration if this is the first // compute if (recordNumMinusLearnIteration == -1) { recordNumMinusLearnIteration = recordNum - learnIteration; } // Update the learn iteration learnIteration = recordNum - recordNumMinusLearnIteration; if (patternNZHistory.Any(x => x.Item1 == learnIteration)) { patternNZHistory.Add(Tuple.Create(learnIteration, patternNZ)); } //------------------------------------------------------------- // For each active bit in the activationPattern, get the classification // votes // // Return value dict. For buckets which we don't have an actual value // for yet, just plug in any valid actual value. It doesn't matter what // we use because that bucket won't have non-zero likelihood anyways. if (infer) { // If doing 0-step prediction, we shouldn't use any knowledge // of the classification input during inference. object defaultValue = default(T); if (steps[0] == 0) { defaultValue = 0; } else { defaultValue = classification["actValue"]; if (defaultValue == null) { if (IsNumericType(default(T))) { defaultValue = ConversionExtensions.Convert <T>(-1); } } } T[] actValues = new T[this.actualValues.Count]; for (int i = 0; i < actualValues.Count; i++) { if (IsNumericType(default(T))) { actValues[i] = (T)(actualValues[i].Equals(ConversionExtensions.Convert <T>(-1)) ? ConversionExtensions.Convert <T>(defaultValue) : actualValues[i]); } else { actValues[i] = (T)(actualValues[i].Equals(default(T)) ? defaultValue : actualValues[i]); } } retVal.setActualValues(actValues); // For each n-step prediction... foreach (int nSteps in steps) { // Accumulate bucket index votes and actValues into these arrays double[] sumVotes = new double[maxBucketIdx + 1]; double[] bitVotes = new double[maxBucketIdx + 1]; foreach (var bit in patternNZ) { var key = Tuple.Create(bit, nSteps); BitHistory history = null; activeBitHistory.TryGetValue(key, out history); if (history == null) { continue; } history.Infer(bitVotes); sumVotes = ArrayUtils.AddOffset(sumVotes, bitVotes); } // Return the votes for each bucket, normalized double total = sumVotes.Sum(); if (total > 0) { sumVotes = ArrayUtils.Divide(sumVotes, total); } else { // If all buckets have zero probability then simply make all of the // buckets equally likely. There is no actual prediction for this // timestep so any of the possible predictions are just as good. if (sumVotes.Length > 0) { double val = 1.0 / sumVotes.Length; for (int i = 0; i < sumVotes.Length; i++) { sumVotes[i] = val; } } } retVal.setStats(nSteps, sumVotes); } } // ------------------------------------------------------------------------ // Learning: // For each active bit in the activationPattern, store the classification // info. If the bucketIdx is None, we can't learn. This can happen when the // field is missing in a specific record. if (learn && classification["bucketIdx"] != null) { // Get classification info int bucketIdx = (int)classification["bucketIdx"]; object actValue = null; if (classification["actValue"] == null) { if (IsNumericType(default(T))) { actValue = ConversionExtensions.Convert <T>(-1); } } else { actValue = ConversionExtensions.Convert <T>(classification["actValue"]); } // Update maxBucketIndex maxBucketIdx = (int)Math.Max(maxBucketIdx, bucketIdx); // Update rolling average of actual values if it's a scalar. If it's // not, it must be a category, in which case each bucket only ever // sees one category so we don't need a running average. while (maxBucketIdx > actualValues.Count - 1) { actualValues.Add(IsNumericType(default(T)) ? ConversionExtensions.Convert <T>(-1) : default(T)); } if (actualValues[bucketIdx].Equals(IsNumericType(default(T)) ? ConversionExtensions.Convert <T>(-1) : default(T))) { actualValues[bucketIdx] = (T)actValue; } else { if (IsNumericType(actValue)) { double val = ((1.0 - actValueAlpha) * Convert.ToDouble(actualValues[bucketIdx], enusFormatProvider)) + (actValueAlpha * (Convert.ToDouble(actValue, enusFormatProvider))); actualValues[bucketIdx] = ConversionExtensions.Convert <T>(val); } else { actualValues[bucketIdx] = (T)actValue; } } // Train each pattern that we have in our history that aligns with the // steps we have in steps int nSteps = -1; int iteration = 0; int[] learnPatternNZ = null; foreach (var n in steps) { nSteps = n; // Do we have the pattern that should be assigned to this classification // in our pattern history? If not, skip it bool found = false; foreach (var t in patternNZHistory) { iteration = t.Item1; learnPatternNZ = t.Item2; if (iteration == learnIteration - nSteps) { found = true; break; } iteration++; } if (!found) { continue; } // Store classification info for each active bit from the pattern // that we got nSteps time steps ago. foreach (int bit in learnPatternNZ) { // Get the history structure for this bit and step var key = Tuple.Create(bit, nSteps); BitHistory history = null; activeBitHistory.TryGetValue(key, out history); if (history == null) { history = new BitHistory(alpha); activeBitHistory.Add(key, history); } history.store(learnIteration, bucketIdx); } } } return(retVal); }