如果你对HTM感兴趣,我建立了一个群,我们共同学习交流。515743445。
阅读本文前需要对HTM有基本的了解,建议阅读HTM白皮书。
本文研究使用HTM进行单变量的异常检测,并尝试复现了numenta的出租车异常检测结果。Numenta的异常检测项目在这里https://github.com/numenta/NAB。我们复现https://github.com/numenta/NAB/tree/master/results/numentaTM/realKnownCause下的numentaTM_nyc_taxi.csv的结果。
建议先阅读readme对NAB有个基本了解。
首先,在numentaTM_nyc_taxi.csv中我们看到,该出租车检测结果的异常分数一列,构成的图形,清晰的显示出了四个异常点,这与numenta的论文中的图形是一致的。
我们不使用NAB提供的框架重复结果,采用直接调用algorithm的方式来复现。
代码如下
from nupic.algorithms.spatial_pooler import SpatialPooler as SP
from nupic.algorithms.backtracking_tm_shim import TMCPPShim as TM
from nupic.encoders.random_distributed_scalar import RandomDistributedScalarEncoder
from nupic.encoders.date import DateEncoder
from nupic.algorithms import anomaly
from datetime import datetime
import numpy as np
from tqdm import tqdm
from matplotlib import pyplot as plt
DATA_URL = "https://github.com/numenta/NAB/tree/master/data/realKnownCause"
PATH = "C:/Users/mi/PycharmProjects/nupic-master/nab/"
DATA = "nyc_taxi.csv"
value = np.loadtxt(PATH+DATA,usecols=(1),delimiter=',',skiprows=1,dtype=np.int32)
time = np.loadtxt(PATH+DATA,usecols=(0),delimiter=',',skiprows=1,dtype=np.str)
sp = SP(inputDimensions=(454, 1),
columnDimensions=(2048, 1),
potentialRadius=2048,
potentialPct=0.8,
globalInhibition=True,
localAreaDensity=-1,
numActiveColumnsPerInhArea=40,
synPermInactiveDec=0.0005,
synPermActiveInc=0.003,
synPermConnected=0.2,
dutyCyclePeriod=1000,
boostStrength=0.0,
seed=1956)
tm = TM( numberOfCols=2048,
cellsPerColumn=32,
initialPerm=0.24,
connectedPerm=0.50,
minThreshold=13,
permanenceInc=0.04,
permanenceDec=0.008,
predictedSegmentDecrement=0.001,
newSynapseCount=31,
globalDecay=0.0,
activationThreshold=20,
seed=1960,
verbosity=0,
pamLength=3,
maxAge=0,
maxSegmentsPerCell=128,
maxSynapsesPerSegment=128,
outputType='normal',
)
dateEncoder = DateEncoder(timeOfDay=(21, 9.49))
randomEncoder = RandomDistributedScalarEncoder(resolution=422.03538461538466,seed=42)
def encode(date,value):
t = datetime.strptime(date[2:-3], "%y-%m-%d %H:%M")
dateSdr = dateEncoder.encode(t)
valueSdr = randomEncoder.encode(value)
sdr = np.concatenate((dateSdr,valueSdr))
return sdr
prdictiveColumns = np.zeros(2048)
x = np.arange(0,10320)
y = []
for t, v in zip(time, value):#[:200]:
sdr = encode(t, v)
column = np.zeros(2048,dtype=np.int32)
sp.compute(sdr, True, column)
#print 'activateColumns:',activateColumns
prdictiveColumnsSdr = tm.topDownCompute().copy()
prdictiveColumns = prdictiveColumnsSdr.nonzero()[0]
#print 'prdictivaColumns:', prdictiveColumns
tm.compute(column, True,True)
activateColumns = np.nonzero(column)[0]
activateColumns = activateColumns.astype(np.int32)
score = anomaly.computeRawAnomalyScore(activateColumns, prdictiveColumns)
#print score
y.append(score)
y = np.array(y)
plt.plot(x,y)
plt.show()
这里有几个注意点:
第一:我们使用nupic.algorithms.backtracking_tm_shim,该tm与标准tm实现不同,专门为异常检测做了优化,但是该方法官方已不再维护。
第二:encoder,sp和tm使用的参数,来自getScalarMetricWithTimeOfDayAnomalyParams方法,实际读取自nupic项目下的/src/nupic/frameworks/opf/common_models/anomaly_params_random_encoder/best_single_metric_anomaly_params_tm_cpp.json
第三:这些参数是numenta反复试验获得的最优结果。
然后我们获得了相同的结果:
很抱歉之前两篇解读sp和tm的文章写的很杂乱,但是我不想改了。。。读代码嘛,一行行读就是了,写的还是很清晰的。里面几个核心类使用c++加速,但是并不影响阅读。基本上到这里再想深入学习HTM,就要看numenta的最新论文了,没有成熟和结构良好的代码和文档了。他们最新的几篇论文关于位置理论,其最新思想似乎是将位置信号作为tm的偏置输入,这样相比于本文中将两个编码器的结构直接相连的效果要好。