class OneDDepthEncoder(Encoder): """ Given an array of numbers, each representing distance to the closest object, returns an SDR representation of that depth data. At each given position, computes the closest distance within radius 3, and encodes that distance with a scalar encoder. The concatenation of all these scalar encodings is the final encoding. """ def __init__(self, positions=range(36), radius=3, wrapAround=False, nPerPosition=57, wPerPosition=3, minVal=0, maxVal=1, name=None, verbosity=0): """ See `nupic.encoders.base.Encoder` for more information. @param positions (list) Positions at which to encode distance @param radius (int) Radius of positions over which to consider to get closest distance for encoding @param wrapAround (bool) Whether radius should wrap around the sides of the input array @param nPerPosition (int) Number of bits available for scalar encoder when encoding each position @param wPerPosition (int) Number of bits active for scalar encoder when encoding each position @param minVal (int) Minimum distance that can be encoded @param maxVal (int) Maximum distance that can be encoded """ self.positions = positions self.radius = radius self.wrapAround = wrapAround self.scalarEncoder = ScalarEncoder(wPerPosition, minVal, maxVal, n=nPerPosition, forced=True) self.verbosity = verbosity self.encoders = None self.n = len(self.positions) * nPerPosition self.w = len(self.positions) * wPerPosition if name is None: name = "[%s:%s]" % (self.n, self.w) self.name = name def getWidth(self): """See `nupic.encoders.base.Encoder` for more information.""" return self.n def getDescription(self): """See `nupic.encoders.base.Encoder` for more information.""" return [('data', 0)] def getScalars(self, inputData): """See `nupic.encoders.base.Encoder` for more information.""" return numpy.array([0] * len(inputData)) def encodeIntoArray(self, inputData, output): """ See `nupic.encoders.base.Encoder` for more information. @param inputData (tuple) Contains depth data (numpy.array) @param output (numpy.array) Stores encoded SDR in this numpy array """ output[:] = 0 for i, position in enumerate(self.positions): indices = range(position - self.radius, position + self.radius + 1) mode = 'wrap' if self.wrapAround else 'clip' values = inputData.take(indices, mode=mode) start = i * self.scalarEncoder.getWidth() end = (i + 1) * self.scalarEncoder.getWidth() output[start:end] = self.scalarEncoder.encode(max(values)) def dump(self): print "OneDDepthEncoder:" print " w: %d" % self.w print " n: %d" % self.n @classmethod def read(cls, proto): encoder = object.__new__(cls) encoder.w = proto.w encoder.n = proto.n encoder.radius = proto.radius encoder.verbosity = proto.verbosity encoder.name = proto.name return encoder def write(self, proto): proto.w = self.w proto.n = self.n proto.radius = self.radius proto.verbosity = self.verbosity proto.name = self.name
class DateEncoder(Encoder): """A date encoder encodes a date according to encoding parameters specified in its constructor. The input to a date encoder is a datetime.datetime object. The output is the concatenation of several sub-encodings, each of which encodes a different aspect of the date. Which sub-encodings are present, and details of those sub-encodings, are specified in the DateEncoder constructor. Each parameter describes one attribute to encode. By default, the attribute is not encoded. season (season of the year; units = day): (int) width of attribute; default radius = 91.5 days (1 season) (tuple) season[0] = width; season[1] = radius dayOfWeek (monday = 0; units = day) (int) width of attribute; default radius = 1 day (tuple) dayOfWeek[0] = width; dayOfWeek[1] = radius weekend (boolean: 0, 1) (int) width of attribute holiday (boolean: 0, 1) (int) width of attribute timeOfday (midnight = 0; units = hour) (int) width of attribute: default radius = 4 hours (tuple) timeOfDay[0] = width; timeOfDay[1] = radius customDays TODO: what is it? forced (default True) : if True, skip checks for parameters' settings; see encoders/scalar.py for details """ def __init__(self, season=0, dayOfWeek=0, weekend=0, holiday=0, timeOfDay=0, customDays=0, name = '', forced=True): self.width = 0 self.description = [] self.name = name # This will contain a list of (name, encoder, offset) tuples for use by # the decode() method self.encoders = [] self.seasonEncoder = None if season != 0: # Ignore leapyear differences -- assume 366 days in a year # Radius = 91.5 days = length of season # Value is number of days since beginning of year (0 - 355) if hasattr(season, "__getitem__"): w = season[0] radius = season[1] else: w = season radius = 91.5 self.seasonEncoder = ScalarEncoder(w = w, minval=0, maxval=366, radius=radius, periodic=True, name="season", forced=forced) self.seasonOffset = self.width self.width += self.seasonEncoder.getWidth() self.description.append(("season", self.seasonOffset)) self.encoders.append(("season", self.seasonEncoder, self.seasonOffset)) self.dayOfWeekEncoder = None if dayOfWeek != 0: # Value is day of week (floating point) # Radius is 1 day if hasattr(dayOfWeek, "__getitem__"): w = dayOfWeek[0] radius = dayOfWeek[1] else: w = dayOfWeek radius = 1 self.dayOfWeekEncoder = ScalarEncoder(w = w, minval=0, maxval=7, radius=radius, periodic=True, name="day of week", forced=forced) self.dayOfWeekOffset = self.width self.width += self.dayOfWeekEncoder.getWidth() self.description.append(("day of week", self.dayOfWeekOffset)) self.encoders.append( ("day of week", self.dayOfWeekEncoder, self.dayOfWeekOffset)) self.weekendEncoder = None if weekend != 0: # Binary value. Not sure if this makes sense. Also is somewhat redundant # with dayOfWeek #Append radius if it was not provided if not hasattr(weekend, "__getitem__"): weekend = (weekend, 1) self.weekendEncoder = ScalarEncoder(w=weekend[0], minval=0, maxval=1, periodic=False, radius=weekend[1], name="weekend", forced=forced) self.weekendOffset = self.width self.width += self.weekendEncoder.getWidth() self.description.append(("weekend", self.weekendOffset)) self.encoders.append(("weekend", self.weekendEncoder, self.weekendOffset)) #Set up custom days encoder, first argument in tuple is width #second is either a single day of the week or a list of the days #you want encoded as ones. self.customDaysEncoder = None if customDays !=0: customDayEncoderName = "" daysToParse = [] assert len(customDays)==2, "Please provide a w and the desired days" if isinstance(customDays[1], list): for day in customDays[1]: customDayEncoderName+=str(day)+" " daysToParse=customDays[1] elif isinstance(customDays[1], str): customDayEncoderName+=customDays[1] daysToParse = [customDays[1]] else: assert False, "You must provide either a list of days or a single day" #Parse days self.customDays = [] for day in daysToParse: if(day.lower() in ["mon","monday"]): self.customDays+=[0] elif day.lower() in ["tue","tuesday"]: self.customDays+=[1] elif day.lower() in ["wed","wednesday"]: self.customDays+=[2] elif day.lower() in ["thu","thursday"]: self.customDays+=[3] elif day.lower() in ["fri","friday"]: self.customDays+=[4] elif day.lower() in ["sat","saturday"]: self.customDays+=[5] elif day.lower() in ["sun","sunday"]: self.customDays+=[6] else: assert False, "Unable to understand %s as a day of week" % str(day) self.customDaysEncoder = ScalarEncoder(w=customDays[0], minval = 0, maxval=1, periodic=False, radius=1, name=customDayEncoderName, forced=forced) self.customDaysOffset = self.width self.width += self.customDaysEncoder.getWidth() self.description.append(("customdays", self.customDaysOffset)) self.encoders.append(("customdays", self.customDaysEncoder, self.customDaysOffset)) self.holidayEncoder = None if holiday != 0: # A "continuous" binary value. = 1 on the holiday itself and smooth ramp # 0->1 on the day before the holiday and 1->0 on the day after the holiday. self.holidayEncoder = ScalarEncoder(w = holiday, minval = 0, maxval=1, periodic=False, radius=1, name="holiday", forced=forced) self.holidayOffset = self.width self.width += self.holidayEncoder.getWidth() self.description.append(("holiday", self.holidayOffset)) self.encoders.append(("holiday", self.holidayEncoder, self.holidayOffset)) self.timeOfDayEncoder = None if timeOfDay != 0: # Value is time of day in hours # Radius = 4 hours, e.g. morning, afternoon, evening, early night, # late night, etc. if hasattr(timeOfDay, "__getitem__"): w = timeOfDay[0] radius = timeOfDay[1] else: w = timeOfDay radius = 4 self.timeOfDayEncoder = ScalarEncoder(w = w, minval=0, maxval=24, periodic=True, radius=radius, name="time of day", forced=forced) self.timeOfDayOffset = self.width self.width += self.timeOfDayEncoder.getWidth() self.description.append(("time of day", self.timeOfDayOffset)) self.encoders.append(("time of day", self.timeOfDayEncoder, self.timeOfDayOffset)) def getWidth(self): return self.width def getScalarNames(self, parentFieldName=''): """ See method description in base.py """ names = [] # This forms a name which is the concatenation of the parentFieldName # passed in and the encoder's own name. def _formFieldName(encoder): if parentFieldName == '': return encoder.name else: return '%s.%s' % (parentFieldName, encoder.name) # ------------------------------------------------------------------------- # Get the scalar values for each sub-field if self.seasonEncoder is not None: names.append(_formFieldName(self.seasonEncoder)) if self.dayOfWeekEncoder is not None: names.append(_formFieldName(self.dayOfWeekEncoder)) if self.customDaysEncoder is not None: names.append(_formFieldName(self.customDaysEncoder)) if self.weekendEncoder is not None: names.append(_formFieldName(self.weekendEncoder)) if self.holidayEncoder is not None: names.append(_formFieldName(self.holidayEncoder)) if self.timeOfDayEncoder is not None: names.append(_formFieldName(self.timeOfDayEncoder)) return names def getEncodedValues(self, input): """ See method description in base.py """ if input == SENTINEL_VALUE_FOR_MISSING_DATA: return numpy.array([None]) assert isinstance(input, datetime.datetime) values = [] # ------------------------------------------------------------------------- # Get the scalar values for each sub-field timetuple = input.timetuple() timeOfDay = timetuple.tm_hour + float(timetuple.tm_min)/60.0 if self.seasonEncoder is not None: dayOfYear = timetuple.tm_yday # input.timetuple() computes the day of year 1 based, so convert to 0 based values.append(dayOfYear-1) if self.dayOfWeekEncoder is not None: dayOfWeek = timetuple.tm_wday #+ timeOfDay / 24.0 values.append(dayOfWeek) if self.weekendEncoder is not None: # saturday, sunday or friday evening if timetuple.tm_wday == 6 or timetuple.tm_wday == 5 \ or (timetuple.tm_wday == 4 and timeOfDay > 18): weekend = 1 else: weekend = 0 values.append(weekend) if self.customDaysEncoder is not None: if timetuple.tm_wday in self.customDays: customDay = 1 else: customDay = 0 values.append(customDay) if self.holidayEncoder is not None: # A "continuous" binary value. = 1 on the holiday itself and smooth ramp # 0->1 on the day before the holiday and 1->0 on the day after the holiday. # Currently the only holiday we know about is December 25 # holidays is a list of holidays that occur on a fixed date every year holidays = [(12, 25)] val = 0 for h in holidays: # hdate is midnight on the holiday hdate = datetime.datetime(timetuple.tm_year, h[0], h[1], 0, 0, 0) if input > hdate: diff = input - hdate if diff.days == 0: # return 1 on the holiday itself val = 1 break elif diff.days == 1: # ramp smoothly from 1 -> 0 on the next day val = 1.0 - (float(diff.seconds) / (86400)) break else: diff = hdate - input if diff.days == 0: # ramp smoothly from 0 -> 1 on the previous day val = 1.0 - (float(diff.seconds) / 86400) values.append(val) if self.timeOfDayEncoder is not None: values.append(timeOfDay) return values def getScalars(self, input): """ See method description in base.py Parameters: ----------------------------------------------------------------------- input: A datetime object representing the time being encoded Returns: A numpy array of the corresponding scalar values in the following order: [season, dayOfWeek, weekend, holiday, timeOfDay] Note: some of these fields might be omitted if they were not specified in the encoder """ return numpy.array(self.getEncodedValues(input)) def getBucketIndices(self, input): """ See method description in base.py """ if input == SENTINEL_VALUE_FOR_MISSING_DATA: # Encoder each sub-field return [None] * len(self.encoders) else: assert isinstance(input, datetime.datetime) # Get the scalar values for each sub-field scalars = self.getScalars(input) # Encoder each sub-field result = [] for i in xrange(len(self.encoders)): (name, encoder, offset) = self.encoders[i] result.extend(encoder.getBucketIndices(scalars[i])) return result def encodeIntoArray(self, input, output): """ See method description in base.py """ if input == SENTINEL_VALUE_FOR_MISSING_DATA: output[0:] = 0 else: if not isinstance(input, datetime.datetime): raise ValueError("Input is type %s, expected datetime. Value: %s" % ( type(input), str(input))) # Get the scalar values for each sub-field scalars = self.getScalars(input) # Encoder each sub-field for i in xrange(len(self.encoders)): (name, encoder, offset) = self.encoders[i] encoder.encodeIntoArray(scalars[i], output[offset:]) def getDescription(self): return self.description @classmethod def read(cls, proto): encoder = object.__new__(cls) encoder.encoders = [] encoder.description = [] encoder.width = 0 encoder.name = proto.name def addEncoder(encoderAttr, offsetAttr): protoVal = getattr(proto, encoderAttr) if protoVal.n: setattr(encoder, encoderAttr, ScalarEncoder.read(protoVal)) innerEncoder = getattr(encoder, encoderAttr) setattr(encoder, offsetAttr, encoder.width) innerOffset = getattr(encoder, offsetAttr) encoder.width += innerEncoder.getWidth() encoder.description.append((innerEncoder.name, innerOffset)) encoder.encoders.append((innerEncoder.name, innerEncoder, innerOffset)) else: setattr(encoder, encoderAttr, None) addEncoder("seasonEncoder", "seasonOffset") addEncoder("dayOfWeekEncoder", "dayOfWeekOffset") addEncoder("weekendEncoder", "weekendOffset") addEncoder("customDaysEncoder", "customDaysOffset") addEncoder("holidayEncoder", "holidayOffset") addEncoder("timeOfDayEncoder", "timeOfDayOffset") return encoder def write(self, proto): for name in ("seasonEncoder", "dayOfWeekEncoder", "weekendEncoder", "customDaysEncoder", "holidayEncoder", "timeOfDayEncoder"): encoder = getattr(self, name) if encoder: encoder.write(getattr(proto, name))
class LVF(object): """Class implementing Localization with Vision Features""" def __init__( self, minX, maxX, minY, maxY, bottomUpInputSize, bottomUpOnBits, ): self.xEncoder = ScalarEncoder(5, minX, 10 * maxX, n=75, forced=True) self.yEncoder = ScalarEncoder(5, minY, 10 * maxY, n=75, forced=True) self.externalSize = self.xEncoder.getWidth()**2 self.externalOnBits = self.xEncoder.w**2 self.bottomUpInputSize = bottomUpInputSize self.bottomUpOnBits = bottomUpOnBits self.trainingIterations = 0 self.testIterations = 0 self.maxPredictionError = 0 self.totalPredictionError = 0 self.numMissedPredictions = 0 self.tm = TM(columnCount=self.bottomUpInputSize, basalInputSize=self.externalSize, cellsPerColumn=4, initialPermanence=0.4, connectedPermanence=0.5, minThreshold=self.externalOnBits, sampleSize=40, permanenceIncrement=0.1, permanenceDecrement=0.00, activationThreshold=int( 0.75 * (self.externalOnBits + self.bottomUpOnBits)), basalPredictedSegmentDecrement=0.00, seed=42) def compute(self, x, y, bottomUpSDR, learn): # Encode the inputs appropriately and train the HTM externalSDR = self.encodePosition(x, y) if learn: # During learning we provide the current pose angle as bottom up input self.trainTM(bottomUpSDR, externalSDR) self.trainingIterations += 1 else: print >> sys.stderr, "Learn: ", learn def encodePosition(self, x, y): """Return the SDR for x,y""" xe = self.xEncoder.encode(x) ye = self.yEncoder.encode(y) ex = np.outer(xe, ye) return ex.flatten().nonzero()[0] def trainTM(self, bottomUp, externalInput): #print >> sys.stderr, "Bottom up: ", bottomUp #print >> sys.stderr, "ExternalInput: ",externalInput self.tm.compute(bottomUp, basalInput=externalInput, learn=True)
class LogEncoder(Encoder): """ This class wraps the ScalarEncoder class. A Log encoder represents a floating point value on a logarithmic scale. valueToEncode = log10(input) w -- number of bits to set in output minval -- minimum input value. must be greater than 0. Lower values are reset to this value maxval -- maximum input value (input is strictly less if periodic == True) periodic -- If true, then the input value "wraps around" such that minval = maxval For a periodic value, the input must be strictly less than maxval, otherwise maxval is a true upper bound. Exactly one of n, radius, resolution must be set. "0" is a special value that means "not set". n -- number of bits in the representation (must be > w) radius -- inputs separated by more than this distance in log space will have non-overlapping representations resolution -- The minimum change in scaled value needed to produce a change in encoding. This should be specified in log space. For example, the scaled values 10 and 11 will be distinguishable in the output. In terms of the original input values, this means 10^1 (1) and 10^1.1 (1.25) will be distinguishable. name -- an optional string which will become part of the description verbosity -- level of debugging output you want the encoder to provide. clipInput -- if true, non-periodic inputs smaller than minval or greater than maxval will be clipped to minval/maxval forced -- (default False), if True, skip some safety checks """ def __init__(self, w=5, minval=1e-07, maxval=10000, periodic=False, n=0, radius=0, resolution=0, name="log", verbosity=0, clipInput=True, forced=False): # Lower bound for log encoding near machine precision limit lowLimit = 1e-07 # Limit minval as log10(0) is undefined. if minval < lowLimit: minval = lowLimit # Check that minval is still lower than maxval if not minval < maxval: raise ValueError( "Max val must be larger than min val or the lower limit " "for this encoder %.7f" % lowLimit) self.encoders = None self.verbosity = verbosity # Scale values for calculations within the class self.minScaledValue = math.log10(minval) self.maxScaledValue = math.log10(maxval) if not self.maxScaledValue > self.minScaledValue: raise ValueError( "Max val must be larger, in log space, than min val.") self.clipInput = clipInput self.minval = minval self.maxval = maxval self.encoder = ScalarEncoder(w=w, minval=self.minScaledValue, maxval=self.maxScaledValue, periodic=False, n=n, radius=radius, resolution=resolution, verbosity=self.verbosity, clipInput=self.clipInput, forced=forced) self.width = self.encoder.getWidth() self.description = [(name, 0)] self.name = name # This list is created by getBucketValues() the first time it is called, # and re-created whenever our buckets would be re-arranged. self._bucketValues = None ############################################################################ def getWidth(self): return self.width ############################################################################ def getDescription(self): return self.description ############################################################################ def getDecoderOutputFieldTypes(self): """ Encoder class virtual method override """ return (FieldMetaType.float, ) ############################################################################ def _getScaledValue(self, inpt): """ Convert the input, which is in normal space, into log space """ if inpt == SENTINEL_VALUE_FOR_MISSING_DATA: return None else: val = inpt if val < self.minval: val = self.minval elif val > self.maxval: val = self.maxval scaledVal = math.log10(val) return scaledVal ############################################################################ def getBucketIndices(self, inpt): """ See the function description in base.py """ # Get the scaled value scaledVal = self._getScaledValue(inpt) if scaledVal is None: return [None] else: return self.encoder.getBucketIndices(scaledVal) ############################################################################ def encodeIntoArray(self, inpt, output): """ See the function description in base.py """ # Get the scaled value scaledVal = self._getScaledValue(inpt) if scaledVal is None: output[0:] = 0 else: self.encoder.encodeIntoArray(scaledVal, output) if self.verbosity >= 2: print "input:", inpt, "scaledVal:", scaledVal, "output:", output print "decoded:", self.decodedToStr(self.decode(output)) ############################################################################ def decode(self, encoded, parentFieldName=''): """ See the function description in base.py """ # Get the scalar values from the underlying scalar encoder (fieldsDict, fieldNames) = self.encoder.decode(encoded) if len(fieldsDict) == 0: return (fieldsDict, fieldNames) # Expect only 1 field assert (len(fieldsDict) == 1) # Convert each range into normal space (inRanges, inDesc) = fieldsDict.values()[0] outRanges = [] for (minV, maxV) in inRanges: outRanges.append((math.pow(10, minV), math.pow(10, maxV))) # Generate a text description of the ranges desc = "" numRanges = len(outRanges) for i in xrange(numRanges): if outRanges[i][0] != outRanges[i][1]: desc += "%.2f-%.2f" % (outRanges[i][0], outRanges[i][1]) else: desc += "%.2f" % (outRanges[i][0]) if i < numRanges - 1: desc += ", " # Return result if parentFieldName != '': fieldName = "%s.%s" % (parentFieldName, self.name) else: fieldName = self.name return ({fieldName: (outRanges, desc)}, [fieldName]) ############################################################################ def getBucketValues(self): """ See the function description in base.py """ # Need to re-create? if self._bucketValues is None: scaledValues = self.encoder.getBucketValues() self._bucketValues = [] for scaledValue in scaledValues: value = math.pow(10, scaledValue) self._bucketValues.append(value) return self._bucketValues ############################################################################ def getBucketInfo(self, buckets): """ See the function description in base.py """ scaledResult = self.encoder.getBucketInfo(buckets)[0] scaledValue = scaledResult.value value = math.pow(10, scaledValue) return [ EncoderResult(value=value, scalar=value, encoding=scaledResult.encoding) ] ############################################################################ def topDownCompute(self, encoded): """ See the function description in base.py """ scaledResult = self.encoder.topDownCompute(encoded)[0] scaledValue = scaledResult.value value = math.pow(10, scaledValue) return EncoderResult(value=value, scalar=value, encoding=scaledResult.encoding) ############################################################################ def closenessScores(self, expValues, actValues, fractional=True): """ See the function description in base.py """ # Compute the percent error in log space if expValues[0] > 0: expValue = math.log10(expValues[0]) else: expValue = self.minScaledValue if actValues[0] > 0: actValue = math.log10(actValues[0]) else: actValue = self.minScaledValue if fractional: err = abs(expValue - actValue) pctErr = err / (self.maxScaledValue - self.minScaledValue) pctErr = min(1.0, pctErr) closeness = 1.0 - pctErr else: err = abs(expValue - actValue) closeness = err #print "log::", "expValue:", expValues[0], "actValue:", actValues[0], \ # "closeness", closeness #import pdb; pdb.set_trace() return numpy.array([closeness]) @classmethod def read(cls, proto): encoder = object.__new__(cls) encoder.verbosity = proto.verbosity encoder.minScaledValue = proto.minScaledValue encoder.maxScaledValue = proto.maxScaledValue encoder.clipInput = proto.clipInput encoder.minval = proto.minval encoder.maxval = proto.maxval encoder.encoder = ScalarEncoder.read(proto.encoder) encoder.name = proto.name encoder.width = encoder.encoder.getWidth() encoder.description = [(encoder.name, 0)] encoder._bucketValues = None return encoder def write(self, proto): proto.verbosity = self.verbosity proto.minScaledValue = self.minScaledValue proto.maxScaledValue = self.maxScaledValue proto.clipInput = self.clipInput proto.minval = self.minval proto.maxval = self.maxval self.encoder.write(proto.encoder) proto.name = self.name
class DateEncoder(Encoder): """A date encoder encodes a date according to encoding parameters specified in its constructor. The input to a date encoder is a datetime.datetime object. The output is the concatenation of several sub-encodings, each of which encodes a different aspect of the date. Which sub-encodings are present, and details of those sub-encodings, are specified in the DateEncoder constructor. Each parameter describes one attribute to encode. By default, the attribute is not encoded. season (season of the year; units = day): (int) width of attribute; default radius = 91.5 days (1 season) (tuple) season[0] = width; season[1] = radius dayOfWeek (monday = 0; units = day) (int) width of attribute; default radius = 1 day (tuple) dayOfWeek[0] = width; dayOfWeek[1] = radius weekend (boolean: 0, 1) (int) width of attribute holiday (boolean: 0, 1) (int) width of attribute timeOfday (midnight = 0; units = hour) (int) width of attribute: default radius = 4 hours (tuple) timeOfDay[0] = width; timeOfDay[1] = radius customDays TODO: what is it? forced (default True) : if True, skip checks for parameters' settings; see encoders/scalar.py for details """ def __init__(self, season=0, dayOfWeek=0, weekend=0, holiday=0, timeOfDay=0, customDays=0, name = '', forced=True): self.width = 0 self.description = [] self.name = name # This will contain a list of (name, encoder, offset) tuples for use by # the decode() method self.encoders = [] self.seasonEncoder = None if season != 0: # Ignore leapyear differences -- assume 366 days in a year # Radius = 91.5 days = length of season # Value is number of days since beginning of year (0 - 355) if hasattr(season, "__getitem__"): w = season[0] radius = season[1] else: w = season radius = 91.5 self.seasonEncoder = ScalarEncoder(w = w, minval=0, maxval=366, radius=radius, periodic=True, name="season", forced=forced) self.seasonOffset = self.width self.width += self.seasonEncoder.getWidth() self.description.append(("season", self.seasonOffset)) self.encoders.append(("season", self.seasonEncoder, self.seasonOffset)) self.dayOfWeekEncoder = None if dayOfWeek != 0: # Value is day of week (floating point) # Radius is 1 day if hasattr(dayOfWeek, "__getitem__"): w = dayOfWeek[0] radius = dayOfWeek[1] else: w = dayOfWeek radius = 1 self.dayOfWeekEncoder = ScalarEncoder(w = w, minval=0, maxval=7, radius=radius, periodic=True, name="day of week", forced=forced) self.dayOfWeekOffset = self.width self.width += self.dayOfWeekEncoder.getWidth() self.description.append(("day of week", self.dayOfWeekOffset)) self.encoders.append( ("day of week", self.dayOfWeekEncoder, self.dayOfWeekOffset)) self.weekendEncoder = None if weekend != 0: # Binary value. Not sure if this makes sense. Also is somewhat redundant # with dayOfWeek #Append radius if it was not provided if not hasattr(weekend, "__getitem__"): weekend = (weekend, 1) self.weekendEncoder = ScalarEncoder(w=weekend[0], minval=0, maxval=1, periodic=False, radius=weekend[1], name="weekend", forced=forced) self.weekendOffset = self.width self.width += self.weekendEncoder.getWidth() self.description.append(("weekend", self.weekendOffset)) self.encoders.append(("weekend", self.weekendEncoder, self.weekendOffset)) #Set up custom days encoder, first argument in tuple is width #second is either a single day of the week or a list of the days #you want encoded as ones. self.customDaysEncoder = None if customDays !=0: customDayEncoderName = "" daysToParse = [] assert len(customDays)==2, "Please provide a w and the desired days" if isinstance(customDays[1], list): for day in customDays[1]: customDayEncoderName+=str(day)+" " daysToParse=customDays[1] elif isinstance(customDays[1], str): customDayEncoderName+=customDays[1] daysToParse = [customDays[1]] else: assert False, "You must provide either a list of days or a single day" #Parse days self.customDays = [] for day in daysToParse: if(day.lower() in ["mon","monday"]): self.customDays+=[0] elif day.lower() in ["tue","tuesday"]: self.customDays+=[1] elif day.lower() in ["wed","wednesday"]: self.customDays+=[2] elif day.lower() in ["thu","thursday"]: self.customDays+=[3] elif day.lower() in ["fri","friday"]: self.customDays+=[4] elif day.lower() in ["sat","saturday"]: self.customDays+=[5] elif day.lower() in ["sun","sunday"]: self.customDays+=[6] else: assert False, "Unable to understand %s as a day of week" % str(day) self.customDaysEncoder = ScalarEncoder(w=customDays[0], minval = 0, maxval=1, periodic=False, radius=1, name=customDayEncoderName, forced=forced) self.customDaysOffset = self.width self.width += self.customDaysEncoder.getWidth() self.description.append(("customdays", self.customDaysOffset)) self.encoders.append(("customdays", self.customDaysEncoder, self.customDaysOffset)) self.holidayEncoder = None if holiday != 0: # A "continuous" binary value. = 1 on the holiday itself and smooth ramp # 0->1 on the day before the holiday and 1->0 on the day after the holiday. #Append radius if it was not provided if not hasattr(holiday, "__getitem__"): holiday = (holiday, 1) self.holidayEncoder = ScalarEncoder(w = holiday[0], minval = 0, maxval=1, periodic=False, radius=holiday[1], name="holiday", forced=forced) self.holidayOffset = self.width self.width += self.holidayEncoder.getWidth() self.description.append(("holiday", self.holidayOffset)) self.encoders.append(("holiday", self.holidayEncoder, self.holidayOffset)) self.timeOfDayEncoder = None if timeOfDay != 0: # Value is time of day in hours # Radius = 4 hours, e.g. morning, afternoon, evening, early night, # late night, etc. if hasattr(timeOfDay, "__getitem__"): w = timeOfDay[0] radius = timeOfDay[1] else: w = timeOfDay radius = 4 self.timeOfDayEncoder = ScalarEncoder(w = w, minval=0, maxval=24, periodic=True, radius=radius, name="time of day", forced=forced) self.timeOfDayOffset = self.width self.width += self.timeOfDayEncoder.getWidth() self.description.append(("time of day", self.timeOfDayOffset)) self.encoders.append(("time of day", self.timeOfDayEncoder, self.timeOfDayOffset)) def getWidth(self): return self.width def getScalarNames(self, parentFieldName=''): """ See method description in base.py """ names = [] # This forms a name which is the concatenation of the parentFieldName # passed in and the encoder's own name. def _formFieldName(encoder): if parentFieldName == '': return encoder.name else: return '%s.%s' % (parentFieldName, encoder.name) # ------------------------------------------------------------------------- # Get the scalar values for each sub-field if self.seasonEncoder is not None: names.append(_formFieldName(self.seasonEncoder)) if self.dayOfWeekEncoder is not None: names.append(_formFieldName(self.dayOfWeekEncoder)) if self.customDaysEncoder is not None: names.append(_formFieldName(self.customDaysEncoder)) if self.weekendEncoder is not None: names.append(_formFieldName(self.weekendEncoder)) if self.holidayEncoder is not None: names.append(_formFieldName(self.holidayEncoder)) if self.timeOfDayEncoder is not None: names.append(_formFieldName(self.timeOfDayEncoder)) return names def getEncodedValues(self, input): """ See method description in base.py """ if input == SENTINEL_VALUE_FOR_MISSING_DATA: return numpy.array([None]) assert isinstance(input, datetime.datetime) values = [] # ------------------------------------------------------------------------- # Get the scalar values for each sub-field timetuple = input.timetuple() timeOfDay = timetuple.tm_hour + float(timetuple.tm_min)/60.0 if self.seasonEncoder is not None: dayOfYear = timetuple.tm_yday # input.timetuple() computes the day of year 1 based, so convert to 0 based values.append(dayOfYear-1) if self.dayOfWeekEncoder is not None: dayOfWeek = timetuple.tm_wday + timeOfDay / 24.0 values.append(dayOfWeek) if self.weekendEncoder is not None: # saturday, sunday or friday evening if timetuple.tm_wday == 6 or timetuple.tm_wday == 5 \ or (timetuple.tm_wday == 4 and timeOfDay > 18): weekend = 1 else: weekend = 0 values.append(weekend) if self.customDaysEncoder is not None: if timetuple.tm_wday in self.customDays: customDay = 1 else: customDay = 0 values.append(customDay) if self.holidayEncoder is not None: # A "continuous" binary value. = 1 on the holiday itself and smooth ramp # 0->1 on the day before the holiday and 1->0 on the day after the holiday. # Currently the only holiday we know about is December 25 # holidays is a list of holidays that occur on a fixed date every year val = 0 holidays = FedHol.holidays(FedHol(),start = input - pd.Timedelta('2 days'), end= input + pd.Timedelta('2 days')) if len(holidays) > 0: for hdate in holidays: # hdate is midnight on the holiday # hdate = datetime.datetime(timetuple.tm_year, h[0], h[1], 0, 0, 0) if input > hdate: diff = input - hdate if diff.days == 0: # return 1 on the holiday itself val = 1 break elif diff.days == 1: # ramp smoothly from 1 -> 0 on the next day val = 1.0 - (float(diff.seconds) / (86400)) break else: diff = hdate - input if diff.days == 0: # ramp smoothly from 0 -> 1 on the previous day val = 1.0 - (float(diff.seconds) / 86400) # holidays = [(12, 25)] # for h in holidays: # # hdate is midnight on the holiday # hdate = datetime.datetime(timetuple.tm_year, h[0], h[1], 0, 0, 0) # if input > hdate: # diff = input - hdate # if diff.days == 0: # # return 1 on the holiday itself # val = 1 # break # elif diff.days == 1: # # ramp smoothly from 1 -> 0 on the next day # val = 1.0 - (float(diff.seconds) / (86400)) # break # else: # diff = hdate - input # if diff.days == 0: # # ramp smoothly from 0 -> 1 on the previous day # val = 1.0 - (float(diff.seconds) / 86400) values.append(val) if self.timeOfDayEncoder is not None: values.append(timeOfDay) return values def getScalars(self, input): """ See method description in base.py Parameters: ----------------------------------------------------------------------- input: A datetime object representing the time being encoded Returns: A numpy array of the corresponding scalar values in the following order: [season, dayOfWeek, weekend, holiday, timeOfDay] Note: some of these fields might be omitted if they were not specified in the encoder """ return numpy.array(self.getEncodedValues(input)) def getBucketIndices(self, input): """ See method description in base.py """ if input == SENTINEL_VALUE_FOR_MISSING_DATA: # Encoder each sub-field return [None] * len(self.encoders) else: assert isinstance(input, datetime.datetime) # Get the scalar values for each sub-field scalars = self.getScalars(input) # Encoder each sub-field result = [] for i in xrange(len(self.encoders)): (name, encoder, offset) = self.encoders[i] result.extend(encoder.getBucketIndices(scalars[i])) return result def encodeIntoArray(self, input, output): """ See method description in base.py """ if input == SENTINEL_VALUE_FOR_MISSING_DATA: output[0:] = 0 else: if not isinstance(input, datetime.datetime): raise ValueError("Input is type %s, expected datetime. Value: %s" % ( type(input), str(input))) # Get the scalar values for each sub-field scalars = self.getScalars(input) # Encoder each sub-field for i in xrange(len(self.encoders)): (name, encoder, offset) = self.encoders[i] encoder.encodeIntoArray(scalars[i], output[offset:]) def getDescription(self): return self.description @classmethod def read(cls, proto): encoder = object.__new__(cls) encoder.encoders = [] encoder.description = [] encoder.width = 0 encoder.name = proto.name def addEncoder(encoderAttr, offsetAttr): protoVal = getattr(proto, encoderAttr) if protoVal.n: setattr(encoder, encoderAttr, ScalarEncoder.read(protoVal)) innerEncoder = getattr(encoder, encoderAttr) setattr(encoder, offsetAttr, encoder.width) innerOffset = getattr(encoder, offsetAttr) encoder.width += innerEncoder.getWidth() encoder.description.append((innerEncoder.name, innerOffset)) encoder.encoders.append((innerEncoder.name, innerEncoder, innerOffset)) else: setattr(encoder, encoderAttr, None) addEncoder("seasonEncoder", "seasonOffset") addEncoder("dayOfWeekEncoder", "dayOfWeekOffset") addEncoder("weekendEncoder", "weekendOffset") addEncoder("customDaysEncoder", "customDaysOffset") addEncoder("holidayEncoder", "holidayOffset") addEncoder("timeOfDayEncoder", "timeOfDayOffset") return encoder def write(self, proto): for name in ("seasonEncoder", "dayOfWeekEncoder", "weekendEncoder", "customDaysEncoder", "holidayEncoder", "timeOfDayEncoder"): encoder = getattr(self, name) if encoder: encoder.write(getattr(proto, name))
class CategoryEncoder(Encoder): """Encodes a list of discrete categories (described by strings), that aren't related to each other, so we never emit a mixture of categories. The value of zero is reserved for "unknown category" Internally we use a ScalarEncoder with a radius of 1, but since we only encode integers, we never get mixture outputs. The SDRCategoryEncoder uses a different method to encode categories""" def __init__(self, w, categoryList, name="category", verbosity=0, forced=False): """params: forced (default False) : if True, skip checks for parameters' settings; see encoders/scalar.py for details """ self.encoders = None self.verbosity = verbosity # number of categories includes "unknown" self.ncategories = len(categoryList) + 1 self.categoryToIndex = dict() self.indexToCategory = dict() self.indexToCategory[0] = UNKNOWN for i in xrange(len(categoryList)): self.categoryToIndex[categoryList[i]] = i + 1 self.indexToCategory[i + 1] = categoryList[i] self.encoder = ScalarEncoder(w, minval=0, maxval=self.ncategories - 1, radius=1, periodic=False, forced=forced) self.width = w * self.ncategories assert self.encoder.getWidth() == self.width self.description = [(name, 0)] self.name = name # These are used to support the topDownCompute method self._topDownMappingM = None # This gets filled in by getBucketValues self._bucketValues = None def getDecoderOutputFieldTypes(self): """ [Encoder class virtual method override] """ # TODO: change back to string meta-type after the decoding logic is fixed # to output strings instead of internal index values. #return (FieldMetaType.string,) return (FieldMetaType.integer, ) def getWidth(self): return self.width def getDescription(self): return self.description def getScalars(self, input): """ See method description in base.py """ if input == SENTINEL_VALUE_FOR_MISSING_DATA: return numpy.array([None]) else: return numpy.array([self.categoryToIndex.get(input, 0)]) def getBucketIndices(self, input): """ See method description in base.py """ # Get the bucket index from the underlying scalar encoder if input == SENTINEL_VALUE_FOR_MISSING_DATA: return [None] else: return self.encoder.getBucketIndices( self.categoryToIndex.get(input, 0)) def encodeIntoArray(self, input, output): # if not found, we encode category 0 if input == SENTINEL_VALUE_FOR_MISSING_DATA: output[0:] = 0 val = "<missing>" else: val = self.categoryToIndex.get(input, 0) self.encoder.encodeIntoArray(val, output) if self.verbosity >= 2: print "input:", input, "va:", val, "output:", output print "decoded:", self.decodedToStr(self.decode(output)) def decode(self, encoded, parentFieldName=''): """ See the function description in base.py """ # Get the scalar values from the underlying scalar encoder (fieldsDict, fieldNames) = self.encoder.decode(encoded) if len(fieldsDict) == 0: return (fieldsDict, fieldNames) # Expect only 1 field assert (len(fieldsDict) == 1) # Get the list of categories the scalar values correspond to and # generate the description from the category name(s). (inRanges, inDesc) = fieldsDict.values()[0] outRanges = [] desc = "" for (minV, maxV) in inRanges: minV = int(round(minV)) maxV = int(round(maxV)) outRanges.append((minV, maxV)) while minV <= maxV: if len(desc) > 0: desc += ", " desc += self.indexToCategory[minV] minV += 1 # Return result if parentFieldName != '': fieldName = "%s.%s" % (parentFieldName, self.name) else: fieldName = self.name return ({fieldName: (outRanges, desc)}, [fieldName]) def closenessScores( self, expValues, actValues, fractional=True, ): """ See the function description in base.py kwargs will have the keyword "fractional", which is ignored by this encoder """ expValue = expValues[0] actValue = actValues[0] if expValue == actValue: closeness = 1.0 else: closeness = 0.0 if not fractional: closeness = 1.0 - closeness return numpy.array([closeness]) def getBucketValues(self): """ See the function description in base.py """ if self._bucketValues is None: numBuckets = len(self.encoder.getBucketValues()) self._bucketValues = [] for bucketIndex in range(numBuckets): self._bucketValues.append( self.getBucketInfo([bucketIndex])[0].value) return self._bucketValues def getBucketInfo(self, buckets): """ See the function description in base.py """ # For the category encoder, the bucket index is the category index bucketInfo = self.encoder.getBucketInfo(buckets)[0] categoryIndex = int(round(bucketInfo.value)) category = self.indexToCategory[categoryIndex] return [ EncoderResult(value=category, scalar=categoryIndex, encoding=bucketInfo.encoding) ] def topDownCompute(self, encoded): """ See the function description in base.py """ encoderResult = self.encoder.topDownCompute(encoded)[0] value = encoderResult.value categoryIndex = int(round(value)) category = self.indexToCategory[categoryIndex] return EncoderResult(value=category, scalar=categoryIndex, encoding=encoderResult.encoding) @classmethod def read(cls, proto): encoder = object.__new__(cls) encoder.verbosity = proto.verbosity encoder.encoder = ScalarEncoder.read(proto.encoder) encoder.width = proto.width encoder.description = [(proto.name, 0)] encoder.name = proto.name encoder.indexToCategory = { x.index: x.category for x in proto.indexToCategory } encoder.categoryToIndex = { category: index for index, category in encoder.indexToCategory.items() if category != UNKNOWN } encoder._topDownMappingM = None encoder._bucketValues = None return encoder def write(self, proto): proto.width = self.width proto.indexToCategory = [{ "index": index, "category": category } for index, category in self.indexToCategory.items()] proto.name = self.name proto.verbosity = self.verbosity self.encoder.write(proto.encoder)
class NIK(object): """Class implementing NIK""" def __init__( self, minDx=-2.0, maxDx=2.0, minDy=-2.0, maxDy=2.0, minTheta1=0.0, maxTheta1=85.0, minTheta2=0.0, maxTheta2=360.0, ): self.dxEncoder = ScalarEncoder(5, minDx, maxDx, n=75, forced=True) self.dyEncoder = ScalarEncoder(5, minDy, maxDy, n=75, forced=True) self.externalSize = self.dxEncoder.getWidth()**2 self.externalOnBits = self.dxEncoder.w**2 self.theta1Encoder = ScalarEncoder(5, minTheta1, maxTheta1, n=75, forced=True) self.theta2Encoder = ScalarEncoder(5, minTheta2, maxTheta2, n=75, forced=True) self.bottomUpInputSize = self.theta1Encoder.getWidth( ) * self.theta2Encoder.getWidth() self.bottomUpOnBits = self.theta1Encoder.w * self.theta2Encoder.w self.minDx = 100.0 self.maxDx = -100.0 self.minTheta1 = minTheta1 self.minTheta2 = minTheta2 self.maxTheta1 = maxTheta1 self.maxTheta2 = maxTheta2 self.trainingIterations = 0 self.testIterations = 0 self.maxPredictionError = 0 self.totalPredictionError = 0 self.numMissedPredictions = 0 self.tm = TM(columnDimensions=(self.bottomUpInputSize, ), basalInputDimensions=(self.externalSize, ), cellsPerColumn=1, initialPermanence=0.4, connectedPermanence=0.5, minThreshold=self.externalOnBits, maxNewSynapseCount=40, permanenceIncrement=0.1, permanenceDecrement=0.00, activationThreshold=int( 0.75 * (self.externalOnBits + self.bottomUpOnBits)), predictedSegmentDecrement=0.00, checkInputs=False) print >> sys.stderr, "TM parameters:" print >> sys.stderr, " num columns=", self.tm.getColumnDimensions() print >> sys.stderr, " activation threshold=", self.tm.getActivationThreshold( ) print >> sys.stderr, " min threshold=", self.tm.getMinThreshold() print >> sys.stderr, " basal input dimensions=", self.tm.getBasalInputDimensions( ) print >> sys.stderr print >> sys.stderr def compute(self, xt1, yt1, xt, yt, theta1t1, theta2t1, theta1, theta2, learn): """ The main function to call. If learn is False, it will print a prediction: (theta1, theta2) """ dx = xt - xt1 dy = yt - yt1 self.minDx = min(self.minDx, dx) self.maxDx = max(self.maxDx, dx) print >> sys.stderr, "Learn: ", learn print >> sys.stderr, "Training iterations: ", self.trainingIterations print >> sys.stderr, "Test iterations: ", self.testIterations print >> sys.stderr, "Xt's: ", xt1, yt1, xt, yt, "Delta's: ", dx, dy print >> sys.stderr, "Theta t-1: ", theta1t1, theta2t1, "t:", theta1, theta2 bottomUpSDR = self.encodeThetas(theta1, theta2) self.decodeThetas(bottomUpSDR) # Encode the inputs appropriately and train the HTM externalSDR = self.encodeDeltas(dx, dy) if learn: # During learning we provide the current pose angle as bottom up input bottomUpSDR = self.encodeThetas(theta1, theta2) self.trainTM(bottomUpSDR, externalSDR) self.trainingIterations += 1 else: # During inference we provide the previous pose angle as bottom up input # If we don't get a prediction, we keep trying random shifts until we get # something. predictedCells = [] newt1 = theta1t1 newt2 = theta2t1 newdx = dx newdy = dy angleRange = 10 numAttempts = 1 while len(predictedCells) == 0 and numAttempts < 3: print >> sys.stderr, "Attempt:", numAttempts, print >> sys.stderr, "Trying to predict using thetas:", newt1, newt2, print >> sys.stderr, "and deltas:", newdx, newdy externalSDR = self.encodeDeltas(newdx, newdy) bottomUpSDR = self.encodeThetas(newt1, newt2) predictedCells = self.inferTM(bottomUpSDR, externalSDR) predictedValues = self.decodeThetas(predictedCells) print >> sys.stderr, "Predicted values", predictedValues newt1 = theta1t1 + random.randrange(-angleRange, angleRange) newt2 = theta2t1 + random.randrange(-angleRange, angleRange) newdx = dx + (random.random() / 2.0 - 0.25) newdy = dy + (random.random() / 2.0 - 0.25) # Ensure we are in bounds otherwise we get an exception newt1 = min(self.maxTheta1, max(self.minTheta1, newt1)) newt2 = min(self.maxTheta2, max(self.minTheta2, newt2)) newdx = min(2.0, max(-2.0, newdx)) newdy = min(2.0, max(-2.0, newdy)) numAttempts += 1 if numAttempts % 10 == 0: angleRange += 2 print predictedValues # Accumulate errors for our metrics if len(predictedCells) == 0: self.numMissedPredictions += 1 self.testIterations += 1 error = abs(predictedValues[0] - theta1) + abs(predictedValues[1] - theta2) self.totalPredictionError += error if self.maxPredictionError < error: self.maxPredictionError = error print >> sys.stderr, "Error: ", error print >> sys.stderr def reset(self): self.tm.reset() def encodeDeltas(self, dx, dy): """Return the SDR for dx,dy""" dxe = self.dxEncoder.encode(dx) dye = self.dyEncoder.encode(dy) ex = numpy.outer(dxe, dye) return ex.flatten().nonzero()[0] def encodeThetas(self, theta1, theta2): """Return the SDR for theta1 and theta2""" # print >> sys.stderr, "encoded theta1 value = ", theta1 # print >> sys.stderr, "encoded theta2 value = ", theta2 t1e = self.theta1Encoder.encode(theta1) t2e = self.theta2Encoder.encode(theta2) # print >> sys.stderr, "encoded theta1 = ", t1e.nonzero()[0] # print >> sys.stderr, "encoded theta2 = ", t2e.nonzero()[0] ex = numpy.outer(t2e, t1e) return ex.flatten().nonzero()[0] def decodeThetas(self, predictedCells): """ Given the set of predicted cells, return the predicted theta1 and theta2 """ a = numpy.zeros(self.bottomUpInputSize) a[predictedCells] = 1 a = a.reshape( (self.theta1Encoder.getWidth(), self.theta1Encoder.getWidth())) theta1PredictedBits = a.mean(axis=0).nonzero()[0] theta2PredictedBits = a.mean(axis=1).nonzero()[0] # To decode it we need to create a flattened array again and pass it # to encoder. # TODO: We use encoder's topDownCompute method - not sure if that is best. t1 = numpy.zeros(self.theta1Encoder.getWidth()) t1[theta1PredictedBits] = 1 t1Prediction = self.theta1Encoder.topDownCompute(t1)[0].value t2 = numpy.zeros(self.theta2Encoder.getWidth()) t2[theta2PredictedBits] = 1 t2Prediction = self.theta2Encoder.topDownCompute(t2)[0].value # print >> sys.stderr, "predicted cells = ", predictedCells # print >> sys.stderr, "decoded theta1 bits = ", theta1PredictedBits # print >> sys.stderr, "decoded theta2 bits = ", theta2PredictedBits # print >> sys.stderr, "decoded theta1 value = ", t1Prediction # print >> sys.stderr, "decoded theta2 value = ", t2Prediction return t1Prediction, t2Prediction def printStats(self): print >> sys.stderr, "min/max dx=", self.minDx, self.maxDx print >> sys.stderr, "Total number of segments=", numSegments(self.tm) if self.testIterations > 0: print >> sys.stderr, "Maximum prediction error: ", self.maxPredictionError print >> sys.stderr, "Mean prediction error: ", self.totalPredictionError / self.testIterations print >> sys.stderr, "Num missed predictions: ", self.numMissedPredictions def trainTM(self, bottomUp, externalInput): # print >> sys.stderr, "Bottom up: ", bottomUp # print >> sys.stderr, "ExternalInput: ",externalInput self.tm.depolarizeCells(externalInput, learn=True) self.tm.activateCells(bottomUp, reinforceCandidatesExternalBasal=externalInput, growthCandidatesExternalBasal=externalInput, learn=True) # print >> sys.stderr, ("new active cells " + str(self.tm.getActiveCells())) print >> sys.stderr, "Total number of segments=", numSegments(self.tm) def inferTM(self, bottomUp, externalInput): """ Run inference and return the set of predicted cells """ self.reset() # print >> sys.stderr, "Bottom up: ", bottomUp # print >> sys.stderr, "ExternalInput: ",externalInput self.tm.compute(bottomUp, activeCellsExternalBasal=externalInput, learn=False) # print >> sys.stderr, ("new active cells " + str(self.tm.getActiveCells())) # print >> sys.stderr, ("new predictive cells " + str(self.tm.getPredictiveCells())) return self.tm.getPredictiveCells() def save(self, filename="temp.pkl"): """ Save TM in the filename specified above """ output = open(filename, 'wb') cPickle.dump(self.tm, output, protocol=cPickle.HIGHEST_PROTOCOL) def load(self, filename="temp.pkl"): """ Save TM in the filename specified above """ inputFile = open(filename, 'rb') self.tm = cPickle.load(inputFile)
class LogEncoder(Encoder): """ This class wraps the ScalarEncoder class. A Log encoder represents a floating point value on a logarithmic scale. valueToEncode = log10(input) w -- number of bits to set in output minval -- minimum input value. must be greater than 0. Lower values are reset to this value maxval -- maximum input value (input is strictly less if periodic == True) periodic -- If true, then the input value "wraps around" such that minval = maxval For a periodic value, the input must be strictly less than maxval, otherwise maxval is a true upper bound. Exactly one of n, radius, resolution must be set. "0" is a special value that means "not set". n -- number of bits in the representation (must be > w) radius -- inputs separated by more than this distance in log space will have non-overlapping representations resolution -- The minimum change in scaled value needed to produce a change in encoding. This should be specified in log space. For example, the scaled values 10 and 11 will be distinguishable in the output. In terms of the original input values, this means 10^1 (1) and 10^1.1 (1.25) will be distinguishable. name -- an optional string which will become part of the description verbosity -- level of debugging output you want the encoder to provide. clipInput -- if true, non-periodic inputs smaller than minval or greater than maxval will be clipped to minval/maxval forced -- (default False), if True, skip some safety checks """ def __init__( self, w=5, minval=1e-07, maxval=10000, periodic=False, n=0, radius=0, resolution=0, name="log", verbosity=0, clipInput=True, forced=False, ): # Lower bound for log encoding near machine precision limit lowLimit = 1e-07 # Limit minval as log10(0) is undefined. if minval < lowLimit: minval = lowLimit # Check that minval is still lower than maxval if not minval < maxval: raise ValueError( "Max val must be larger than min val or the lower limit " "for this encoder %.7f" % lowLimit ) self.encoders = None self.verbosity = verbosity # Scale values for calculations within the class self.minScaledValue = math.log10(minval) self.maxScaledValue = math.log10(maxval) if not self.maxScaledValue > self.minScaledValue: raise ValueError("Max val must be larger, in log space, than min val.") self.clipInput = clipInput self.minval = minval self.maxval = maxval self.encoder = ScalarEncoder( w=w, minval=self.minScaledValue, maxval=self.maxScaledValue, periodic=False, n=n, radius=radius, resolution=resolution, verbosity=self.verbosity, clipInput=self.clipInput, forced=forced, ) self.width = self.encoder.getWidth() self.description = [(name, 0)] self.name = name # This list is created by getBucketValues() the first time it is called, # and re-created whenever our buckets would be re-arranged. self._bucketValues = None ############################################################################ def getWidth(self): return self.width ############################################################################ def getDescription(self): return self.description ############################################################################ def getDecoderOutputFieldTypes(self): """ Encoder class virtual method override """ return (FieldMetaType.float,) ############################################################################ def _getScaledValue(self, inpt): """ Convert the input, which is in normal space, into log space """ if inpt == SENTINEL_VALUE_FOR_MISSING_DATA: return None else: val = inpt if val < self.minval: val = self.minval elif val > self.maxval: val = self.maxval scaledVal = math.log10(val) return scaledVal ############################################################################ def getBucketIndices(self, inpt): """ See the function description in base.py """ # Get the scaled value scaledVal = self._getScaledValue(inpt) if scaledVal is None: return [None] else: return self.encoder.getBucketIndices(scaledVal) ############################################################################ def encodeIntoArray(self, inpt, output): """ See the function description in base.py """ # Get the scaled value scaledVal = self._getScaledValue(inpt) if scaledVal is None: output[0:] = 0 else: self.encoder.encodeIntoArray(scaledVal, output) if self.verbosity >= 2: print "input:", inpt, "scaledVal:", scaledVal, "output:", output print "decoded:", self.decodedToStr(self.decode(output)) ############################################################################ def decode(self, encoded, parentFieldName=""): """ See the function description in base.py """ # Get the scalar values from the underlying scalar encoder (fieldsDict, fieldNames) = self.encoder.decode(encoded) if len(fieldsDict) == 0: return (fieldsDict, fieldNames) # Expect only 1 field assert len(fieldsDict) == 1 # Convert each range into normal space (inRanges, inDesc) = fieldsDict.values()[0] outRanges = [] for (minV, maxV) in inRanges: outRanges.append((math.pow(10, minV), math.pow(10, maxV))) # Generate a text description of the ranges desc = "" numRanges = len(outRanges) for i in xrange(numRanges): if outRanges[i][0] != outRanges[i][1]: desc += "%.2f-%.2f" % (outRanges[i][0], outRanges[i][1]) else: desc += "%.2f" % (outRanges[i][0]) if i < numRanges - 1: desc += ", " # Return result if parentFieldName != "": fieldName = "%s.%s" % (parentFieldName, self.name) else: fieldName = self.name return ({fieldName: (outRanges, desc)}, [fieldName]) ############################################################################ def getBucketValues(self): """ See the function description in base.py """ # Need to re-create? if self._bucketValues is None: scaledValues = self.encoder.getBucketValues() self._bucketValues = [] for scaledValue in scaledValues: value = math.pow(10, scaledValue) self._bucketValues.append(value) return self._bucketValues ############################################################################ def getBucketInfo(self, buckets): """ See the function description in base.py """ scaledResult = self.encoder.getBucketInfo(buckets)[0] scaledValue = scaledResult.value value = math.pow(10, scaledValue) return [EncoderResult(value=value, scalar=value, encoding=scaledResult.encoding)] ############################################################################ def topDownCompute(self, encoded): """ See the function description in base.py """ scaledResult = self.encoder.topDownCompute(encoded)[0] scaledValue = scaledResult.value value = math.pow(10, scaledValue) return EncoderResult(value=value, scalar=value, encoding=scaledResult.encoding) ############################################################################ def closenessScores(self, expValues, actValues, fractional=True): """ See the function description in base.py """ # Compute the percent error in log space if expValues[0] > 0: expValue = math.log10(expValues[0]) else: expValue = self.minScaledValue if actValues[0] > 0: actValue = math.log10(actValues[0]) else: actValue = self.minScaledValue if fractional: err = abs(expValue - actValue) pctErr = err / (self.maxScaledValue - self.minScaledValue) pctErr = min(1.0, pctErr) closeness = 1.0 - pctErr else: err = abs(expValue - actValue) closeness = err # print "log::", "expValue:", expValues[0], "actValue:", actValues[0], \ # "closeness", closeness # import pdb; pdb.set_trace() return numpy.array([closeness])
class CategoryEncoder(Encoder): """ Encodes a list of discrete categories (described by strings), that aren't related to each other, so we never emit a mixture of categories. The value of zero is reserved for "unknown category" Internally we use a :class:`.ScalarEncoder` with a radius of 1, but since we only encode integers, we never get mixture outputs. The :class:`.SDRCategoryEncoder` uses a different method to encode categories. :param categoryList: list of discrete string categories :param forced: if True, skip checks for parameters' settings; see :class:`.ScalarEncoder` for details. (default False) """ def __init__(self, w, categoryList, name="category", verbosity=0, forced=False): self.encoders = None self.verbosity = verbosity # number of categories includes "unknown" self.ncategories = len(categoryList) + 1 self.categoryToIndex = dict() self.indexToCategory = dict() self.indexToCategory[0] = UNKNOWN for i in xrange(len(categoryList)): self.categoryToIndex[categoryList[i]] = i+1 self.indexToCategory[i+1] = categoryList[i] self.encoder = ScalarEncoder(w, minval=0, maxval=self.ncategories - 1, radius=1, periodic=False, forced=forced) self.width = w * self.ncategories assert self.encoder.getWidth() == self.width self.description = [(name, 0)] self.name = name # These are used to support the topDownCompute method self._topDownMappingM = None # This gets filled in by getBucketValues self._bucketValues = None def getDecoderOutputFieldTypes(self): """ [Encoder class virtual method override] """ # TODO: change back to string meta-type after the decoding logic is fixed # to output strings instead of internal index values. #return (FieldMetaType.string,) return (FieldMetaType.integer,) def getWidth(self): return self.width def getDescription(self): return self.description def getScalars(self, input): """ See method description in base.py """ if input == SENTINEL_VALUE_FOR_MISSING_DATA: return numpy.array([None]) else: return numpy.array([self.categoryToIndex.get(input, 0)]) def getBucketIndices(self, input): """ See method description in base.py """ # Get the bucket index from the underlying scalar encoder if input == SENTINEL_VALUE_FOR_MISSING_DATA: return [None] else: return self.encoder.getBucketIndices(self.categoryToIndex.get(input, 0)) def encodeIntoArray(self, input, output): # if not found, we encode category 0 if input == SENTINEL_VALUE_FOR_MISSING_DATA: output[0:] = 0 val = "<missing>" else: val = self.categoryToIndex.get(input, 0) self.encoder.encodeIntoArray(val, output) if self.verbosity >= 2: print "input:", input, "va:", val, "output:", output print "decoded:", self.decodedToStr(self.decode(output)) def decode(self, encoded, parentFieldName=''): """ See the function description in base.py """ # Get the scalar values from the underlying scalar encoder (fieldsDict, fieldNames) = self.encoder.decode(encoded) if len(fieldsDict) == 0: return (fieldsDict, fieldNames) # Expect only 1 field assert(len(fieldsDict) == 1) # Get the list of categories the scalar values correspond to and # generate the description from the category name(s). (inRanges, inDesc) = fieldsDict.values()[0] outRanges = [] desc = "" for (minV, maxV) in inRanges: minV = int(round(minV)) maxV = int(round(maxV)) outRanges.append((minV, maxV)) while minV <= maxV: if len(desc) > 0: desc += ", " desc += self.indexToCategory[minV] minV += 1 # Return result if parentFieldName != '': fieldName = "%s.%s" % (parentFieldName, self.name) else: fieldName = self.name return ({fieldName: (outRanges, desc)}, [fieldName]) def closenessScores(self, expValues, actValues, fractional=True,): """ See the function description in base.py kwargs will have the keyword "fractional", which is ignored by this encoder """ expValue = expValues[0] actValue = actValues[0] if expValue == actValue: closeness = 1.0 else: closeness = 0.0 if not fractional: closeness = 1.0 - closeness return numpy.array([closeness]) def getBucketValues(self): """ See the function description in base.py """ if self._bucketValues is None: numBuckets = len(self.encoder.getBucketValues()) self._bucketValues = [] for bucketIndex in range(numBuckets): self._bucketValues.append(self.getBucketInfo([bucketIndex])[0].value) return self._bucketValues def getBucketInfo(self, buckets): """ See the function description in base.py """ # For the category encoder, the bucket index is the category index bucketInfo = self.encoder.getBucketInfo(buckets)[0] categoryIndex = int(round(bucketInfo.value)) category = self.indexToCategory[categoryIndex] return [EncoderResult(value=category, scalar=categoryIndex, encoding=bucketInfo.encoding)] def topDownCompute(self, encoded): """ See the function description in base.py """ encoderResult = self.encoder.topDownCompute(encoded)[0] value = encoderResult.value categoryIndex = int(round(value)) category = self.indexToCategory[categoryIndex] return EncoderResult(value=category, scalar=categoryIndex, encoding=encoderResult.encoding) @classmethod def getSchema(cls): return CategoryEncoderProto @classmethod def read(cls, proto): encoder = object.__new__(cls) encoder.verbosity = proto.verbosity encoder.encoder = ScalarEncoder.read(proto.encoder) encoder.width = proto.width encoder.description = [(proto.name, 0)] encoder.name = proto.name encoder.indexToCategory = {x.index: x.category for x in proto.indexToCategory} encoder.categoryToIndex = {category: index for index, category in encoder.indexToCategory.items() if category != UNKNOWN} encoder._topDownMappingM = None encoder.ncategories = len(proto.indexToCategory) encoder._bucketValues = None encoder.encoders = None return encoder def write(self, proto): proto.width = self.width proto.indexToCategory = [ {"index": index, "category": category} for index, category in self.indexToCategory.items() ] proto.name = self.name proto.verbosity = self.verbosity self.encoder.write(proto.encoder)
u = numpy.asarray(U) v = numpy.asarray(V) du = UnitEncoder.decode(u) dv = UnitEncoder.decode(v) return DecodeUnit(numpy.asarray(U)), DecodeUnit(numpy.asarray(V)) # 일단 크기와 각도가 랜덤인 Vector Field(10 by 10)를 만들어보자. VectorField = numpy.zeros((0,), dtype=numpy.uint8) CreateEncodedUnitTable() xRange = 10 yRange = 10 colDims = UnitEncoder.getWidth() * 2 * xRange * yRange tm = TM(columnDimensions=(colDims,), cellsPerColumn=2, initialPermanence=0.5, connectedPermanence=0.5, minThreshold=10, maxNewSynapseCount=20, permanenceIncrement=0.1, permanenceDecrement=0.0, activationThreshold=8, ) print "Started!" #plt.ion()
class OneDDepthEncoder(Encoder): """ Given an array of numbers, each representing distance to the closest object, returns an SDR representation of that depth data. At each given position, computes the closest distance within radius 3, and encodes that distance with a scalar encoder. The concatenation of all these scalar encodings is the final encoding. """ def __init__(self, positions=range(36), radius=3, wrapAround=False, nPerPosition=57, wPerPosition=3, minVal=0, maxVal=1, name=None, verbosity=0): """ See `nupic.encoders.base.Encoder` for more information. @param positions (list) Positions at which to encode distance @param radius (int) Radius of positions over which to consider to get closest distance for encoding @param wrapAround (bool) Whether radius should wrap around the sides of the input array @param nPerPosition (int) Number of bits available for scalar encoder when encoding each position @param wPerPosition (int) Number of bits active for scalar encoder when encoding each position @param minVal (int) Minimum distance that can be encoded @param maxVal (int) Maximum distance that can be encoded """ self.positions = positions self.radius = radius self.wrapAround = wrapAround self.scalarEncoder = ScalarEncoder(wPerPosition, minVal, maxVal, n=nPerPosition, forced=True) self.verbosity = verbosity self.encoders = None self.n = len(self.positions) * nPerPosition self.w = len(self.positions) * wPerPosition if name is None: name = "[%s:%s]" % (self.n, self.w) self.name = name def getWidth(self): """See `nupic.encoders.base.Encoder` for more information.""" return self.n def getDescription(self): """See `nupic.encoders.base.Encoder` for more information.""" return [('data', 0)] def getScalars(self, inputData): """See `nupic.encoders.base.Encoder` for more information.""" return numpy.array([0]*len(inputData)) def encodeIntoArray(self, inputData, output): """ See `nupic.encoders.base.Encoder` for more information. @param inputData (tuple) Contains depth data (numpy.array) @param output (numpy.array) Stores encoded SDR in this numpy array """ output[:] = 0 for i, position in enumerate(self.positions): indices = range(position-self.radius, position+self.radius+1) mode = 'wrap' if self.wrapAround else 'clip' values = inputData.take(indices, mode=mode) start = i * self.scalarEncoder.getWidth() end = (i + 1) * self.scalarEncoder.getWidth() output[start:end] = self.scalarEncoder.encode(max(values)) def dump(self): print "OneDDepthEncoder:" print " w: %d" % self.w print " n: %d" % self.n @classmethod def read(cls, proto): encoder = object.__new__(cls) encoder.w = proto.w encoder.n = proto.n encoder.radius = proto.radius encoder.verbosity = proto.verbosity encoder.name = proto.name return encoder def write(self, proto): proto.w = self.w proto.n = self.n proto.radius = self.radius proto.verbosity = self.verbosity proto.name = self.name
class CategoryEncoder(Encoder): """Encodes a list of discrete categories (described by strings), that aren't related to each other, so we never emit a mixture of categories. The value of zero is reserved for "unknown category" Internally we use a ScalarEncoder with a radius of 1, but since we only encode integers, we never get mixture outputs. The SDRCategoryEncoder uses a different method to encode categories""" def __init__(self, w, categoryList, name="category", verbosity=0, forced=False): """params: forced (default False) : if True, skip checks for parameters' settings; see encoders/scalar.py for details """ self.encoders = None self.verbosity = verbosity # number of categories includes "unknown" self.ncategories = len(categoryList) + 1 self.categoryToIndex = dict() # check_later: what is the purpose of categoryToIndex and indexToCategory? self.indexToCategory = dict() self.indexToCategory[0] = UNKNOWN for i in xrange(len(categoryList)): self.categoryToIndex[categoryList[i]] = i+1 self.indexToCategory[i+1] = categoryList[i] self.encoder = ScalarEncoder(w, minval=0, maxval=self.ncategories - 1, radius=1, periodic=False, forced=forced) self.width = w * self.ncategories assert self.encoder.getWidth() == self.width self.description = [(name, 0)] self.name = name # These are used to support the topDownCompute method self._topDownMappingM = None # This gets filled in by getBucketValues self._bucketValues = None ############################################################################ def getDecoderOutputFieldTypes(self): """ [Encoder class virtual method override] """ # TODO: change back to string meta-type after the decoding logic is fixed # to output strings instead of internal index values. #return (FieldMetaType.string,) return (FieldMetaType.integer,) ############################################################################ def getWidth(self): return self.width ############################################################################ def getDescription(self): return self.description ############################################################################ def getScalars(self, input): """ See method description in base.py """ if input == SENTINEL_VALUE_FOR_MISSING_DATA: return numpy.array([None]) else: return numpy.array([self.categoryToIndex.get(input, 0)]) # to_note: returns the scalar value of the input, as stored in the categoryToIndex # Will return in the format of a numpy array (e.g. [1] or [2]), return [0] if the # input does not match with any of the key in categoryToIndex dictionary ############################################################################ def getBucketIndices(self, input): """ See method description in base.py """ # Get the bucket index from the underlying scalar encoder if input == SENTINEL_VALUE_FOR_MISSING_DATA: return [None] else: return self.encoder.getBucketIndices(self.categoryToIndex.get(input, 0)) # to_note: get the first ON bit from the ScalarEncoder for a given input. # Unknown value will have the first ON bit at position 1, then other values at k*w # Value NONE will have all 0s # problem_with_this_approach: this approach might be fast, but treating # category encoding as rigid scalar encoding might make it hard for learning ############################################################################ def encodeIntoArray(self, input, output): # if not found, we encode category 0 if input == SENTINEL_VALUE_FOR_MISSING_DATA: output[0:] = 0 val = "<missing>" else: val = self.categoryToIndex.get(input, 0) self.encoder.encodeIntoArray(val, output) if self.verbosity >= 2: print "input:", input, "va:", val, "output:", output print "decoded:", self.decodedToStr(self.decode(output)) ############################################################################ def decode(self, encoded, parentFieldName=''): """ See the function description in base.py """ # Get the scalar values from the underlying scalar encoder (fieldsDict, fieldNames) = self.encoder.decode(encoded) if len(fieldsDict) == 0: return (fieldsDict, fieldNames) # Expect only 1 field assert(len(fieldsDict) == 1) # Get the list of categories the scalar values correspond to and # generate the description from the category name(s). (inRanges, inDesc) = fieldsDict.values()[0] # to_note: dict.values() returns values in [list] form, that's why we need [0] outRanges = [] desc = "" for (minV, maxV) in inRanges: minV = int(round(minV)) maxV = int(round(maxV)) outRanges.append((minV, maxV)) while minV <= maxV: if len(desc) > 0: desc += ", " desc += self.indexToCategory[minV] minV += 1 """ ## Test with noisy encoding (very likely if such encoding comes from output of the predicting process) catfish = numpy.zeros(20, 'int') # catfish[5:10] = 1 # catfish[15:20] = 1 catfish[5:10] = 1 catfish[8] = 1 catfish[11] = 1 catfish[14] = 1 catfish[17] = 1 print "Cat fish =", catfish; print ## Note: this kind of encoding is highly unstable, for a little noisy output like this [0 0 0 0 0 1 1 1 1 1 0 1 0 0 1 0 0 1 0 0], ## it should safely generate 'cat'. However, it generates 'cat', 'dog', 'fish'. To improve this encoding/decoding scheme, ## we might need to replace the filling in process, even might need to think about other way to process information. ## problem_with_this_approach """ # Return result if parentFieldName != '': fieldName = "%s.%s" % (parentFieldName, self.name) else: fieldName = self.name return ({fieldName: (outRanges, desc)}, [fieldName]) ############################################################################ def closenessScores(self, expValues, actValues, fractional=True,): """ See the function description in base.py kwargs will have the keyword "fractional", which is ignored by this encoder """ expValue = expValues[0] actValue = actValues[0] if expValue == actValue: closeness = 1.0 else: closeness = 0.0 if not fractional: closeness = 1.0 - closeness return numpy.array([closeness]) ############################################################################ def getBucketValues(self): """ See the function description in base.py """ if self._bucketValues is None: numBuckets = len(self.encoder.getBucketValues()) self._bucketValues = [] for bucketIndex in range(numBuckets): self._bucketValues.append(self.getBucketInfo([bucketIndex])[0].value) # to_note: list of category corresponding to bucket indices # each bucket is a number that is spaced (radius/w) each other return self._bucketValues ############################################################################ def getBucketInfo(self, buckets): """ See the function description in base.py """ # For the category encoder, the bucket index is the category index bucketInfo = self.encoder.getBucketInfo(buckets)[0] categoryIndex = int(round(bucketInfo.value)) category = self.indexToCategory[categoryIndex] # to_note: map the bucket index to category return [EncoderResult(value=category, scalar=categoryIndex, encoding=bucketInfo.encoding)] ############################################################################ def topDownCompute(self, encoded): """ See the function description in base.py """ encoderResult = self.encoder.topDownCompute(encoded)[0] # to_note: return EncoderResult, which includes the value (depend on ScalarEncoder) value = encoderResult.value categoryIndex = int(round(value)) category = self.indexToCategory[categoryIndex] return EncoderResult(value=category, scalar=categoryIndex, encoding=encoderResult.encoding) @classmethod def read(cls, proto): encoder = object.__new__(cls) encoder.verbosity = proto.verbosity encoder.encoder = ScalarEncoder.read(proto.encoder) encoder.width = proto.width encoder.description = [(proto.name, 0)] encoder.name = proto.name encoder.indexToCategory = {x.index: x.category for x in proto.indexToCategory} encoder.categoryToIndex = {category: index for index, category in encoder.indexToCategory.items() if category != UNKNOWN} encoder._topDownMappingM = None encoder._bucketValues = None return encoder def write(self, proto): proto.width = self.width proto.indexToCategory = [ {"index": index, "category": category} for index, category in self.indexToCategory.items() ] proto.name = self.name proto.verbosity = self.verbosity self.encoder.write(proto.encoder)
EncodedWidth = UnitEncoder.getWidth(); U = Vec[0 :EncodedWidth] V = Vec[EncodedWidth:EncodedWidth*2] u = numpy.asarray(U) v = numpy.asarray(V) return DecodeUnit(u), DecodeUnit(v) # 일단 크기와 각도가 랜덤인 Vector Field(10 by 10)를 만들어보자. CreateEncodedUnitTable() xRange = 10 yRange = 10 colDims = UnitEncoder.getWidth() * 2 * xRange * yRange tm = TM(columnDimensions=(colDims,), cellsPerColumn=4, initialPermanence=0.5, connectedPermanence=0.5, minThreshold=10, maxNewSynapseCount=20, permanenceIncrement=0.1, permanenceDecrement=0.0, activationThreshold=4, ) print "Started!" #plt.ion()
class LogEncoder(Encoder): """ This class wraps the :class:`.ScalarEncoder`. A Log encoder represents a floating point value on a logarithmic scale. .. code-block:: python valueToEncode = log10(input) :param resolution: The minimum change in scaled value needed to produce a change in encoding. This should be specified in log space. For example, the scaled values 10 and 11 will be distinguishable in the output. In terms of the original input values, this means 10^1 (1) and 10^1.1 (1.25) will be distinguishable. :param radius: inputs separated by more than this distance in log space will have non-overlapping representations """ def __init__(self, w=5, minval=1e-07, maxval=10000, periodic=False, n=0, radius=0, resolution=0, name="log", verbosity=0, clipInput=True, forced=False): # Lower bound for log encoding near machine precision limit lowLimit = 1e-07 # Limit minval as log10(0) is undefined. if minval < lowLimit: minval = lowLimit # Check that minval is still lower than maxval if not minval < maxval: raise ValueError("Max val must be larger than min val or the lower limit " "for this encoder %.7f" % lowLimit) self.encoders = None self.verbosity = verbosity # Scale values for calculations within the class self.minScaledValue = math.log10(minval) self.maxScaledValue = math.log10(maxval) if not self.maxScaledValue > self.minScaledValue: raise ValueError("Max val must be larger, in log space, than min val.") self.clipInput = clipInput self.minval = minval self.maxval = maxval self.encoder = ScalarEncoder(w=w, minval=self.minScaledValue, maxval=self.maxScaledValue, periodic=False, n=n, radius=radius, resolution=resolution, verbosity=self.verbosity, clipInput=self.clipInput, forced=forced) self.width = self.encoder.getWidth() self.description = [(name, 0)] self.name = name # This list is created by getBucketValues() the first time it is called, # and re-created whenever our buckets would be re-arranged. self._bucketValues = None def getWidth(self): return self.width def getDescription(self): return self.description def getDecoderOutputFieldTypes(self): """ Encoder class virtual method override """ return (FieldMetaType.float, ) def _getScaledValue(self, inpt): """ Convert the input, which is in normal space, into log space """ if inpt == SENTINEL_VALUE_FOR_MISSING_DATA: return None else: val = inpt if val < self.minval: val = self.minval elif val > self.maxval: val = self.maxval scaledVal = math.log10(val) return scaledVal def getBucketIndices(self, inpt): """ See the function description in base.py """ # Get the scaled value scaledVal = self._getScaledValue(inpt) if scaledVal is None: return [None] else: return self.encoder.getBucketIndices(scaledVal) def encodeIntoArray(self, inpt, output): """ See the function description in base.py """ # Get the scaled value scaledVal = self._getScaledValue(inpt) if scaledVal is None: output[0:] = 0 else: self.encoder.encodeIntoArray(scaledVal, output) if self.verbosity >= 2: print "input:", inpt, "scaledVal:", scaledVal, "output:", output print "decoded:", self.decodedToStr(self.decode(output)) def decode(self, encoded, parentFieldName=''): """ See the function description in base.py """ # Get the scalar values from the underlying scalar encoder (fieldsDict, fieldNames) = self.encoder.decode(encoded) if len(fieldsDict) == 0: return (fieldsDict, fieldNames) # Expect only 1 field assert(len(fieldsDict) == 1) # Convert each range into normal space (inRanges, inDesc) = fieldsDict.values()[0] outRanges = [] for (minV, maxV) in inRanges: outRanges.append((math.pow(10, minV), math.pow(10, maxV))) # Generate a text description of the ranges desc = "" numRanges = len(outRanges) for i in xrange(numRanges): if outRanges[i][0] != outRanges[i][1]: desc += "%.2f-%.2f" % (outRanges[i][0], outRanges[i][1]) else: desc += "%.2f" % (outRanges[i][0]) if i < numRanges-1: desc += ", " # Return result if parentFieldName != '': fieldName = "%s.%s" % (parentFieldName, self.name) else: fieldName = self.name return ({fieldName: (outRanges, desc)}, [fieldName]) def getBucketValues(self): """ See the function description in base.py """ # Need to re-create? if self._bucketValues is None: scaledValues = self.encoder.getBucketValues() self._bucketValues = [] for scaledValue in scaledValues: value = math.pow(10, scaledValue) self._bucketValues.append(value) return self._bucketValues def getBucketInfo(self, buckets): """ See the function description in base.py """ scaledResult = self.encoder.getBucketInfo(buckets)[0] scaledValue = scaledResult.value value = math.pow(10, scaledValue) return [EncoderResult(value=value, scalar=value, encoding = scaledResult.encoding)] def topDownCompute(self, encoded): """ See the function description in base.py """ scaledResult = self.encoder.topDownCompute(encoded)[0] scaledValue = scaledResult.value value = math.pow(10, scaledValue) return EncoderResult(value=value, scalar=value, encoding = scaledResult.encoding) def closenessScores(self, expValues, actValues, fractional=True): """ See the function description in base.py """ # Compute the percent error in log space if expValues[0] > 0: expValue = math.log10(expValues[0]) else: expValue = self.minScaledValue if actValues [0] > 0: actValue = math.log10(actValues[0]) else: actValue = self.minScaledValue if fractional: err = abs(expValue - actValue) pctErr = err / (self.maxScaledValue - self.minScaledValue) pctErr = min(1.0, pctErr) closeness = 1.0 - pctErr else: err = abs(expValue - actValue) closeness = err #print "log::", "expValue:", expValues[0], "actValue:", actValues[0], \ # "closeness", closeness #import pdb; pdb.set_trace() return numpy.array([closeness]) @classmethod def read(cls, proto): encoder = object.__new__(cls) encoder.verbosity = proto.verbosity encoder.minScaledValue = proto.minScaledValue encoder.maxScaledValue = proto.maxScaledValue encoder.clipInput = proto.clipInput encoder.minval = proto.minval encoder.maxval = proto.maxval encoder.encoder = ScalarEncoder.read(proto.encoder) encoder.name = proto.name encoder.width = encoder.encoder.getWidth() encoder.description = [(encoder.name, 0)] encoder._bucketValues = None return encoder def write(self, proto): proto.verbosity = self.verbosity proto.minScaledValue = self.minScaledValue proto.maxScaledValue = self.maxScaledValue proto.clipInput = self.clipInput proto.minval = self.minval proto.maxval = self.maxval self.encoder.write(proto.encoder) proto.name = self.name
def main(): DIR = "./sim_data" # Odom Encoder xSDR = ScalarEncoder(w=21,minval=0,maxval=20,n=256) ySDR = ScalarEncoder(w=21,minval=0,maxval=20,n=256) xyWidth = xSDR.getWidth() + ySDR.getWidth() # Visual input D = np.loadtxt(DIR + '/seq_multi_loop_noise05_al5.txt', dtype='i', delimiter=',') numberImages = D[:,0].size nColumns = D[0,:].size #time.sleep(10) # Odom input odom = np.loadtxt(DIR + '/seq_multi_loop_noise05_al5_gt.txt', dtype='f', delimiter=',') x = odom[:,0] y = odom[:,1] # Encoder Odom input odomSDR = np.zeros((numberImages,xyWidth), dtype=int) for i in range(1): _xSDR = np.zeros(xSDR.getWidth(), dtype=int) xSDR.encodeIntoArray(x[i], _xSDR) _ySDR = np.zeros(ySDR.getWidth(), dtype=int) ySDR.encodeIntoArray(y[i], _ySDR) odomSDR[i,:] = np.concatenate([_xSDR, _ySDR]) tm0 = TM( columnCount=nColumns, cellsPerColumn=4, initialPermanence=0.21, connectedPermanence=0.5, permanenceIncrement=0.1, permanenceDecrement=0.1, minThreshold=15, basalInputSize= 512, reducedBasalThreshold=1000, activationThreshold=1000, apicalInputSize=0, maxSynapsesPerSegment=-1, sampleSize=1, seed = 42 ) tm = TemporalMemory( # Must be the same dimensions as the SP columnDimensions=(2048,), # How many cells in each mini-column. cellsPerColumn=4, # A segment is active if it has >= activationThreshold connected synapses # that are active due to infActiveState activationThreshold=13, initialPermanence=0.21, connectedPermanence=0.5, # Minimum number of active synapses for a segment to be considered during # search for the best-matching segments. minThreshold=1, # The max number of synapses added to a segment during learning maxNewSynapseCount=3, #permanenceIncrement=0.01, #permanenceDecrement=0.01, predictedSegmentDecrement=0.0005, maxSegmentsPerCell=3, maxSynapsesPerSegment=3, seed=42 ) #time.sleep(10) # Simple HTM parameters params = Params() params.maxPredDepth = 0 params.probAdditionalCon = 0.05 # probability for random connection params.nCellPerCol = 32 # number of cells per minicolumn params.nInConPerCol = int(round(np.count_nonzero(D) / D.shape[0])) #print params.nInConPerCol params.minColumnActivity = int(round(0.25*params.nInConPerCol)) params.nColsPerPattern = 10 # minimum number of active minicolumns k_min params.kActiveColumn = 100 # maximum number of active minicolumns k_max params.kMin = 1 # run HTM t = time.time() print ('Simple HTM') htm = MCN('htm',params) outputSDR = [] max_index = [] for i in range (min(numberImages,D.shape[0])): loop = 0 #print('\n-------- ITERATION %d ---------' %i) # skip empty vectors if np.count_nonzero(D[i,:]) == 0: print('empty vector, skip\n') continue loop += 1 #print D[i,:] htm.compute(D[i,:]) max_index.append(max(htm.winnerCells)) outputSDR.append(htm.winnerCells) elapsed = time.time() - t print("Elapsed time: %f seconds\n" %elapsed) # create output SDR matrix from HTM winner cell output M = np.zeros((len(outputSDR),max(max_index)+1), dtype=int) for i in range(len(outputSDR)): for j in range(len(outputSDR[i])): winner = outputSDR[i][j] M[i][winner] = 1 # Temporal Pooler descriptors print 'Temporal Pooler descriptors' D1_tm=[] id_max1=[] t = time.time() for i in range(min(numberImages,D.shape[0])): D1_sp = np.nonzero(D[i,:])[0] tm.compute(D1_sp, learn=True) activeCells = tm.getWinnerCells() D1_tm.append(activeCells) id_max1.append(max(activeCells)) elapsed = time.time() - t print( "Elapsed time: %f seconds\n" %elapsed) # create output SDR matrix from HTM winner cell output T = np.zeros((len(D1_tm),max(id_max1)+1), dtype=int) for i in range(len(D1_tm)): for j in range(len(D1_tm[i])): winner = D1_tm[i][j] T[i][winner] = 1 # Temporal Pooler - Distal connections print 'Temporal Pooler - Distal connections' D2_tm=[] id_max2=[] t = time.time() for i in range(min(numberImages,D.shape[0])): D2_sp = np.nonzero(D[i,:])[0] basalInputs = np.nonzero(odomSDR[i,:])[0] tm0.compute(sorted(D2_sp), sorted(basalInputs), apicalInput=(), basalGrowthCandidates=None, apicalGrowthCandidates=None, learn=True) activeCells2 = tm0.getWinnerCells() D2_tm.append(activeCells2) id_max2.append(max(activeCells2)) elapsed = time.time() - t print( "Elapsed time: %f seconds\n" %elapsed) # create output SDR matrix from HTM winner cell output T2 = np.zeros((len(D2_tm),max(id_max2)+1), dtype=int) for i in range(len(D2_tm)): for j in range(len(D2_tm[i])): winner = D2_tm[i][j] T2[i][winner] = 1 # Create ground truth and show precision-recall curves GT_data = np.loadtxt(DIR + '/seq_multi_loop_noNoise_gt.txt', dtype='i', delimiter=',',skiprows=1) GT = np.zeros((numberImages,numberImages), dtype=int) for i in range(GT.shape[0]): for j in range(i,GT.shape[1]): GT[i,j] = (np.any(GT_data[i,:] != GT_data[j,:])==False) # Results print ('Results') fig, ax = plt.subplots() S0 = evaluateSimilarity(D) P, R = createPR(S0,GT) ax.plot(R, P, label='InputSDR: (avgP=%f)' %np.trapz(P,R)) S1 = evaluateSimilarity(M) P, R = createPR(S1,GT) ax.plot(R, P, label='MCN (avgP=%f)' %np.trapz(P,R)) S2 = evaluateSimilarity(T) P, R = createPR(S2,GT) ax.plot(R, P, label='HTM (avgP=%f)' %np.trapz(P,R)) S3 = evaluateSimilarity(T2) P, R = createPR(S3,GT) ax.plot(R, P, label='HTM Distal (avgP=%f)' %np.trapz(P,R)) ax.legend() ax.grid(True) plt.xlabel("Recall") plt.ylabel("Precision") plt.show() '''
def buildSDR(arrayOfShapeTypes,arrayOfDistances,arrayOfAreas,numCentroids): #Scalar Encoder for the area areaEncoder = ScalarEncoder(5, 1e3, 3.8e3, periodic=False, n=80, radius=0, resolution=0, name=None, verbosity=0, clipInput=False, forced=True) #Scalar encoder for the distances between centroids distanceEncoder = ScalarEncoder(5, 30, 240, periodic=False, n=80, radius=0, resolution=0, name=None, verbosity=0, clipInput=False, forced=True) if numCentroids ==3: distanceBits0 = np.zeros(distanceEncoder.getWidth()) distanceEncoder.encodeIntoArray(arrayOfDistances[0],distanceBits0) distanceBits1 = np.zeros(distanceEncoder.getWidth()) distanceEncoder.encodeIntoArray(arrayOfDistances[1],distanceBits1) distanceBits2 = np.zeros(distanceEncoder.getWidth()) distanceEncoder.encodeIntoArray(arrayOfDistances[2],distanceBits2) if numCentroids ==2: distanceBits0 = np.zeros(distanceEncoder.getWidth()) distanceEncoder.encodeIntoArray(arrayOfDistances[0],distanceBits0) # Build the Triangle's base SDR. One hot encoding used for all SDRs. TriangleSDR = np.zeros(10) # Part A: Number of Sides [0 - 4] TriangleSDR[2] = 1 # Part B: Number of Neighbors [5 - 9] TriangleSDR[7] = 1 # Build the Circle's base SDR CircleSDR = np.zeros(10) # Part A: Number of Sides [0 - 4] CircleSDR[0] = 1 # Part B: Number of Neighbors [5 - 9] CircleSDR[7] = 1 # Build the Square's base SDR SquareSDR = np.zeros(10) # Part A: Number of Sides [0 - 4] SquareSDR[3] = 1 # Part B: Number of Neighbors [5 - 9] SquareSDR[7] = 1 arrayOfSDRs = np.array([]) for i in range(numCentroids): # Encode the area areaBits = np.zeros(areaEncoder.getWidth()) areaEncoder.encodeIntoArray(arrayOfAreas[i],areaBits) # Figure out the shape type via the CNN output then us an # if, elseif tree to decide which SDR to concatenate if arrayOfShapeTypes[i] == 0: #Its a Triangle tempSDR = np.concatenate(( TriangleSDR, areaBits)) elif arrayOfShapeTypes[i] == 1: #Its a Circle tempSDR = np.concatenate(( CircleSDR, areaBits)) elif arrayOfShapeTypes[i] == 2: #Its a Square tempSDR = np.concatenate(( SquareSDR, areaBits)) if numCentroids == 3: if i == 0: #Its the first item tempSDR = np.concatenate((tempSDR,distanceBits0,distanceBits2)) elif i == 1: #Its the second tempSDR = np.concatenate((tempSDR,distanceBits0,distanceBits1)) elif i == 2: #Its the third tempSDR = np.concatenate((tempSDR,distanceBits1,distanceBits2)) elif numCentroids == 2: if i == 0: #Its the first item tempSDR = np.concatenate((tempSDR,distanceBits0,np.zeros(80))) elif i == 1: #Its the second tempSDR = np.concatenate((tempSDR,distanceBits0,np.zeros(80))) elif numCentroids == 1: tempSDR = np.concatenate((tempSDR,np.zeros(160))) arrayOfSDRs = np.append(arrayOfSDRs,tempSDR) if numCentroids == 2: arrayOfSDRs = np.append(arrayOfSDRs,np.zeros(250)) if numCentroids == 1: arrayOfSDRs = np.append(arrayOfSDRs,np.zeros(500)) # Concatenate all three SDRs #print(arrayOfSDRs) imageSDR = arrayOfSDRs return imageSDR