前言:
{
之前从工作中了解到了脉冲神经网络(Spiking Neural Network,SNN)。SNN在1952年被首次提出,被誉为第三代神经网络[1]。不过这次不打算再读论文了,我想直接找段代码[2]读读看。
此篇记录中的代码和图全都来自[2]。
}
正文:
{
代码比较少,一共不到700行,只分成了两个.py文件:FinalProject.py和FinalProjectNeuron.py。FinalProject.py应该是人口文件。
GenreClassifier是分类器的类。根据代码1,此分类器的网络结构很简单,有1个输入层,3个隐含层和1个输出层,具体是138-10-20-10-1。可以看到输出层的权值被单独地初始化了。
#代码1
def __init__(self):
self.timeStep = 2
self.learningRate = 0.001
self.spikingThreshold = 0.8 #Found through testing my specific neurons
self.inputLayerSize = 138
self.numSeconds = 2005 #Number of input neurons/pixels
self.timeThreshold = 10 #Time to simulate neuron for
self.classifications = 2
self.hiddenLayerNum = 3
self.neuronPerLayer = [10, 20, 10]
self.dataList = []
self.isFirst = 0
self.inputLayer = []
for i in range(self.inputLayerSize):
self.inputLayer.append(Neuron(self.timeThreshold,0))
self.middleLayer = []
currNumInputs = 138
for i in range(self.hiddenLayerNum):
currLayer = []
for j in range(self.neuronPerLayer[i]):
currLayer.append(Neuron(self.timeThreshold, currNumInputs))
self.middleLayer.append(currLayer)
currNumInputs = self.neuronPerLayer[i]
self.outputLayer = Neuron(self.timeThreshold, 0)
weights = []
for i in range(math.floor(currNumInputs/2)):
self.outputLayer.weights.append(math.ceil(uniform(0,1000))/1000)
for i in range(math.floor(currNumInputs/2), currNumInputs):
self.outputLayer.weights.append(math.ceil(uniform(0,1000))/1000)
作者还给出了结构图,见图1。
图1(这个水印怎么去掉?)Neuron的构造函数(C++中叫构造函数,一时间忘了这个在Python里叫什么)如代码2。
#代码2
def __init__(self, durationForSimulation, numInputs):
self.T = durationForSimulation #Time to simulate, in ms
self.dT = 0.1 #the dT time step in dV/dT
self.VArray = zeros(int((self.T/self.dT) + 1)) #Array of membrane potentials, for plotting later
self.Vt = 1 #Threshold, in V
self.Vr = 0 #Reset potential, in mV
self.initialV = 0 #Initial Membrane Potential = Formula is change in mV
self.R = 1 #Membrane resistance, in kOhms
self.C = 10 #Capacitance in uF
self.tauM = self.R*self.C #Membrane time constant, in miliseconds
self.firingRate = 10
self.currentChangeInPotential = float(10.0) #Change in current membrane potential - Used in array
self.numFired = 0
self.notFired = 0
self.fired = 0
self.spikeRateForData = []
self.totalSpikingRate = 0
self.classificationRate = 0
self.classificationActivity = 0
self.weights = [];
for i in range(numInputs):
randomWeight = math.ceil(uniform(0,2000)-1000)/1000
self.weights.append(randomWeight)
可以看出,此分类器是全连接结构。但是作者对最后一层与前一层之间的权值单独进行了定义,之后应该会涉及到原因。另外还定义了电阻和电容等参数,比之前的神经网络要真实。
Neuron的反馈通过代码3得出。
#代码3
def runNeuron(self, inputCurrent):
self.counter = 0.0
I = inputCurrent #input current, in Amps - Given by parameter, plus minimum threshold to actually fire
spikeSum = 0.
self.VArray = zeros(int((self.T/self.dT) + 1)) #Array of membrane potentials, for plotting later
self.currentChangeInPotential = 0
for i in range(1, len(self.VArray)) :
self.currentChangeInPotential = (-1*self.VArray[i-1] + self.R*I)
self.VArray[i] = self.VArray[i-1] + self.currentChangeInPotential/self.tauM*self.dT
if(self.VArray[i] >= self.Vt):
self.VArray[i] = self.Vr
spikeSum += 1
return (math.ceil((spikeSum/self.T)*1000))/1000
可以看到,每个Neuron都有一个设定长度的VArray,其模拟一段时间(durationForSimulation)内此Neuron的状态。当计算Neuron的输出时,遍历VArray,根据VArray中的上一个元素(电压)、当前输入电流和Neuron的电阻更新VArray中当前的元素,其中增加的电压和上一个电压成反比,和输入电流成正比;并且在遍历中如果增加后的电压高于一个阈值,则产生一个脉冲(spike);最后输出一个和脉冲数成正比的值。
我的理解是,每更新一次相当于过滤durationForSimulation,Neuron的外环境对应地更新一次,但内环境的初始状态会重置,而且之后会更新很多次。
接下来是主要内容:训练。训练函数太大,比较复杂,而且不止一个,我就不在这放出来了。train函数负责更新隐含层的权值,其步骤大致如下:
看这段代码的时候我发现了一个问题,代码4中的两个elif永远执行不到,可能这两个elif原本是无条件的else。
#代码4
if(currWeight+deltaW <= 1 and currWeight+deltaW>-1):
neuron.weights[j] += deltaW
# neuron.weights[j] = round(neuron.weights[j])
elif(currWeight+deltaW == 1):
neuron.weights[j] = 1.000
#--------------------------------------------------------------------
if(currWeight+deltaW >= -1):
neuron.weights[j] -= deltaW
# neuron.weights[j] = round(neuron.weights[j])
elif(currWeight+deltaW == -1):
neuron.weights[j] = -1.000
}
结语:
{
本次就先更新这么多,我先消化一下,剩下的下次再更。
参考资料:
{
[1]https://baike.baidu.com/item/%E8%84%89%E5%86%B2%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C/4452657?fr=aladdin
[2]https://github.com/shashank135sharma/Spiking-Neural-Network---Genre-Recognizer
}
}