首先介绍一下Tempotron这个监督算法,想要用SNN去解决一个分类问题,有三个步骤:
1、考虑如何将样本编码成和时间相关的脉冲序列。
2、如何构建一个神经元的模型。
3、确定一个学习算法,用于训练样本。
Tempotron方法提出:神经元后突触膜电位是所有与之相连的上一层脉冲输入的加权和。膜电位计算公式为:
其中K函数是一个kernel函数,它代表的是每一个传入脉冲的贡献值。它的公式为:
画成图像为:
其中 taum和taus是控制整个K(t)图像大致形状的参数,是个超参数。V0的作用是个归一化的作用。看到这,我产生了一个疑问,我们都知道在创建的神经网络中,它的时间都是一层一层往下延伸的,所以这个ti?是什么意思。
到这,我们还缺少一个参数w,在训练时我们通常根据随机化去产生一个初始权重矩阵,那么在训练的时候我们是怎么根据误差来更新权重?脉冲神经元只有两种状态,一个是发放,一个是不发放,转移到数字上就是0和1,如果在一个时间步骤内,我们只能产生一个脉冲。那么我们就可以得出一个规则,**如果我们的目标它要求是输出1,可是我们输出了0,那么我们就可以使权重变大,如果我们要求是输出0,但是它输出了1,那么我们就可以使脉冲减小。**这就是Tempotron的学习规则,所以它是监督学习然后我们只要得到一个△w公式就可以了。
λ是一个控制变化幅度的系数,类似与学习率。K就是上面的kernel函数,tmax是指在一个时间窗口内该输出层神经元达到电压最大值的时间。
接下去进入代码部分
代码地址:https://github.com/ajaykarpur/tempotron-classifier
那我们就从编码开始吧,具体的图片处理的,就去源码看好了。
def encode(self):
data_array = np.array(self.image)#data).reshape((28,28))
edges = canny(data_array, sigma=3) #提取边缘
def linear_mapping(data): # using principal components analysis
pca = decomposition.PCA(n_components=784) #主成分分析
pca.fit(data) #训练
mapping = pca.transform(data) #将原始数据转化成训练好后的数据
return mapping
# encoded = linear_mapping(edges)
encoded = np.array(edges).reshape(784) #这是平铺成一维数据
# encoded = []
# for d in self.data:
# if (d > 45):
# encoded.append(1)
# else:
# encoded.append(0)
return encoded
这一段就是编码的程序,他是先对图像进行canny提取边缘处理。在它注释掉的代码里面还有进行主成分分析的处理,不过这不是我们训练的关键。
下面这是Tempotron的神经元封装。
class Tempotron(object):
def __init__(self, length, Cm=4.9, Vreset=-63., Vthresh=-55., Vrest=-58., V0=2.12, Erev=-80., El=-58., gl=.02):
self.Vreset = float(Vreset) # mV
self.Vthresh = float(Vthresh) # mV 阈值
self.Vrest = float(Vrest) # mV 静息
self.V0 = float(V0) #归一化数值
self.Erev = float(Erev) # mV
self.El = float(El) # mV
self.gl = float(gl) # uS
self.length = length
# self.Rm = 1/gl
# self.taum = Cm*self.Rm
self.tau = 15 # in ms
self.taus = self.tau/4
self.weights = [random.random()]*length # initialize with randomly assigned weights
def __PSP(self, t): #kernel函数
return self.V0*(m.exp(-t/self.tau) - m.exp(-t/self.taus))
def V(self, t): #膜电位计算函数
return self.weights[t]*(self.__PSP(t) + self.Vrest)
def __cost(self, t):
Vthresh = self.Vthresh
spike = data[t] #脉冲 输入值
tmax = self.length - 1 #最后一个值
if (spike == 1): #
return Vthresh - self.V(tmax)
elif (spike == 0):
return self.V(tmax) - Vthresh
def __syn_update(self, t, maxsize=.00008): #maxsize就是学习率值 这个就是权重更新值
tmax = self.length - 1
return maxsize*self.__PSP(tmax - t)
def train(self, data): #权重更新 这里居然只有增加的,
for t, d in enumerate(data):
self.weights[t] += self.__syn_update(t)
def test(self, data):
for t, d in enumerate(data):
if (abs(self.V(t)) > abs(self.Vthresh)):
return 1
return 0