脉冲神经网络之Tempotron代码示例

        上一篇从原理的角度大致介绍了脉冲神经网络的神经元模型以及Tempotron监督学习方法,这一章记录了Tempotron的代码实现。这份代码是使用matlab编写,用脉冲神经网络实现对26个字母分类,我们从细节去解释该代码:

function TempotronClassify()
% Tempotron: a neuron that learns spike timing-based decisions
% Rober Gutig 2006 Nature Neuroscience
clear; clc;
NumImages=26;
for i=1:NumImages
    ImageName=strcat('Icon16X16\Letter-',char('A'+i-1),'-black-icon');% 从icon16X16文件夹中读取所有图片
    ImageMatrix=imread(ImageName,'bmp');% 读取图片为灰度图,保存在矩阵中,取反
    ImageMatrix=~ImageMatrix;  % make the white pixel be 0, and black be 1;
    TrainPtns(:,i)=image2ptn(ImageMatrix);
end
上面代码片段就是从Icon16X16\文件夹中读取图片文件,图片从A到Z,这个代码的图片数据很简单,只有0和1,由黑白两色构成字母,读取的数据以16*16矩阵形式保存,之后调用image2ptn()方法,将矩阵转换。这个方法实现的就是对外界刺激进行编码,也就是将图片数据转换成脉冲序列的形式。打开该方法查看代码:

%% convert a image to a spike train saved in a vector
RandParts=1;

spikeTrain=zeros(32,1);
if RandParts1
loadR=1;
AR=A(😃;
if loadR0
R = randperm(size(A,1)size(A,2));
save(‘RandIndex’,‘R’);
else
load(‘RandIndex’,‘R’);
end
numRandNeus=32;
for i=1:numRandNeus
IndexR=R((1+(i-1)8):(8+(i-1)8));
Bits=AR(IndexR);
Bits=Bits’;
spikeTime=bi2de(Bits,‘left-msb’); % 二进制转十进制
if spikeTime==0
spikeTime=2^8; % put 0 to the end of the interval
end
spikeTrain(i)=spikeTime;
end
end这里的rand函数目的只是打乱1616矩阵中的行,我是不太理解这个=-=,AR是个2561的数组,是将A(图片矩阵1616)展平(flatten)的结果,Bits=AR(IndexR);这句简单的说就是从AR中随机抽取8个数字,构成bits,然后将bits转换成spikeTime(十进制数字),代表脉冲发放的时间。综上原先的图片1616矩阵被转换成328矩阵,8代表8位二进制编码,范围在0255之间,也就是说时间窗口大小为256毫秒,然后将8为二进制数转为十进制,代表脉冲发放的时间,该时间在(0255)之间。总结起来,就是对于一张1616的黑白图片,转换成了一个321的数组,数组中有32个时间窗口,每个元素代表脉冲发送时间。


 
 
  1. TrainPtns=TrainPtns*1e-3; % scale to ms
  2. nAfferents = size(TrainPtns,1);
  3. nPtns = NumImages;
  4. %nOutputs = 1; %%%%%%%%%% 1
  5. nOutputs = 5;

TrainPtns是32*26的矩阵,也就是将所有转换后的spikeTrain拼接一起。nAfferents代表了输入脉冲数量,这里就是32,由这32个输入确定一个输出(字母)。输出就定义为5,也就是5个二进制数表示一个字母的编号。


 
 
  1. loadData=0;% 是否载入已保存的模型
  2. V_thr = 1; V_rest = 0;
  3. T = 256e-3; % pattern duration ms
  4. dt = 1e-3;
  5. tau_m = 20e-3; % tau_m = 15e-3;???
  6. tau_s = tau_m/4;
  7. % K(t?ti)=V0(exp[?(t?ti)/τ]–exp[?(t?ti)/τs])
  8. aa = exp(-(0:dt:3*tau_m)/tau_m)-exp(-(0:dt:3*tau_m)/tau_s);
  9. V0 = 1/max(exp(-(0:dt:3*tau_m)/tau_m)-exp(-(0:dt:3*tau_m)/tau_s));
  10. lmd = 2e-2;%1e-2/V0; % optimal performance lmd=3e-3*T/(tau_m*nAfferents*V0) 1e-4/V0;
  11. maxEpoch = 200;
  12. mu = 0.99; % momentum factor
上面定义了阈值电压V_thr,这是按经验来取,这里取1,复位电压则取0.定义了时间窗口大小为256毫秒,单位时间是1毫秒,aa则是为了核函数K的计算简化定义。即预先定义公式:
K(t-ti)=V0(exp[-(t-ti)/τ]–exp[-(t-ti)/τs])
 
 
V0也被预先定义。最大训练次数为200次(maxEpoch),lmd类似于学习率,而mu则是动量,该代码用了动量优化模型。


 
 
  1. if loadData ==0 %初始化网络
  2. weights = 1e-2*randn(nAfferents,nOutputs); % 1e-3*randn(nAfferents,1);
  3. save('weights0','weights');
  4. else
  5. load('weights0','weights');
  6. end
随机初始化权重,或者读取保存的权重文件。

 
 
  1. Class = de2bi(1:26,'left-msb'); Class=Class';
  2. correctRate=zeros(1,maxEpoch);
  3. dw_Past=zeros(nAfferents,nPtns,nOutputs); % momentum for accelerating learning.上一个权重的更新,用于动量计算
  4. for epoch=1:maxEpoch
  5. Class_Tr = false(nOutputs,nPtns); % actual outputs of training
  6. for pp=1:nPtns
  7. for neuron=1:nOutputs
  8. Vmax=0; tmax=0;
  9. fired=false;
  10. Vm1=zeros(1,256); indx1= 1; % trace pattern 1
  11. for t=dt:dt:T
  12. Vm = 0;
  13. if fired==false
  14. Tsyn=find(TrainPtns(:,pp) <=t+0.1*dt); % no cut window
  15. else
  16. Tsyn= find(TrainPtns(:,pp)<= t_fire+0.1*dt); % shut down inputs
  17. end
  18. if ~ isempty( Tsyn)
  19. A1= TrainPtns(:,pp);
  20. A2= A1(Tsyn);
  21. K = V0*(exp(-(t-A2)/tau_m)-exp(-(t-A2)/tau_s)); % the kernel value for each fired afferent
  22. A1= weights(:,neuron);
  23. firedWeights= A1(Tsyn);
  24. Vm = Vm + firedWeights'* K ;
  25. end
  26. Vm = Vm + V_rest;
  27. if Vm>=V_thr && fired==false % fire
  28. fired=true;
  29. t_fire=t;
  30. Class_Tr(neuron,pp)=true;
  31. end
  32. if Vm>Vmax
  33. Vmax=Vm; tmax=t;
  34. end
  35. %if pp==1
  36. Vm1(indx1)=Vm;
  37. indx1=indx1+1;
  38. %end
  39. end
  40. %if pp==1
  41. figure(1); plot(dt:dt:T,Vm1);
  42. title(strcat('Image ',char('A'+pp-1),'; neuron: ',num2str(neuron))); drawnow;
  43. %end
  44. if Vmax <=0
  45. tmax= max(TrainPtns(:,pp));
  46. end
  47. if Class_Tr( neuron, pp)~= Class(neuron,pp) % error
  48. Tsyn= find(TrainPtns(:,pp)<= tmax+0.1*dt);
  49. if ~ isempty( Tsyn)
  50. A1= TrainPtns(:,pp);
  51. A2= A1(Tsyn);
  52. K = V0*(exp(-(tmax-A2)/tau_m)-exp(-(tmax-A2)/tau_s)); % the kernel value for each fired afferent
  53. A1= weights(:,neuron);
  54. dwPst= dw_Past(:,pp,neuron);
  55. if fired== false % LTP
  56. Dw= lmd*K;
  57. else % LTD
  58. Dw= -1.1*lmd*K;
  59. end
  60. A1( Tsyn) = A1(Tsyn) + Dw + mu* dwPst( Tsyn);
  61. weights( :, neuron)= A1;
  62. dwPst( Tsyn)= Dw;
  63. dw_Past( :, pp, neuron) = dwPst;
  64. end
  65. end
  66. end % end of one neuron computation
  67. end % end of one image
  68. % CC= isequal(Class,Class_Tr);
  69. % correctRate( epoch)= sum(Class== Class_Tr)/length(Class);
  70. CC = bi2de(Class_Tr',' left-msb');
  71. end
  72. save(' TrainedWt',' weights');
  73. figure( 2); plot( 1:maxEpoch, correctRate,' -b.');
  74. end
class代表最后的类别,由5位二进制编码表示。最外层循环是最大训练次数,第二层nPtns代表了26个样本,最后一层循环nOutputs为5,代表对每个输出神经元进行一次计算。之后将Vmax,Tmax(代表最大膜电位出现时间)初始化为0,fired表示是否发出脉冲,这里初始化为false,Vm1记录了此输出神经元膜电位(在一个时间窗口内)的变化,之后进入循环,循环一个时间窗口长度,对一个时间窗口内的每个单位时间,若该神经元已经发送过脉冲,则shut down所有脉冲输入;若该神经元还未发出过脉冲(fired = false), 就执行Tsyn=find(TrainPtns(:,pp)<=t+0.1*dt);也就是在当前样本(pp)的脉冲序列中,找到比时间t+0.1*dt小的脉冲发送时间,也就是在t这个时间点之前有脉冲输入发生,此时A2就保存了该脉冲发送的时间(也就是ti),之后就可调用K(t - ti)来计算此时的核函数;下一步就是找到发送这个脉冲的输入神经元和该输出神经元之间连接的权重数值,用K*Weights就代表了该脉冲输入对膜电位的影响,最后将该贡献值累加到Vm上,得到此刻膜电位大小。

对整个时间窗口计算完后,将Vm加上复位电压,如果该值超过了阈值电压,则发放脉冲;若该值超过了已有的Vmax,则更新Vmax。之后进入判断语句:

if Class_Tr(neuron,pp)~=Class(neuron,pp) 神经元的输出和实际输出不相符时,分为两个情况:

(1)实际输出为0,但发放了脉冲。

此时应该抑制脉冲发放,于是权重应该减小:

Dw=-1.1*lmd*K;

(2)实际输出为1,但没有发放脉冲:

此时应该增大神经元的刺激,权重应该增加:

Dw=lmd*K;
最后保存此次循环的权重。于是单层的脉冲神经网路监督学习就这样完成了。下面附上完整代码:


 
 
  1. function TempotronClassify()
  2. % Tempotron: a neuron that learns spike timing-based decisions
  3. % Rober Gutig 2006 Nature Neuroscience
  4. clear; clc;
  5. NumImages=26;
  6. for i=1:NumImages
  7. ImageName=strcat('Icon16X16\Letter-',char('A'+i-1),'-black-icon');% 从icon16X16文件夹中读取所有图片
  8. ImageMatrix=imread(ImageName,'bmp');% 读取图片为灰度图,保存在矩阵中,取反
  9. ImageMatrix=~ImageMatrix; % make the white pixel be 0, and black be 1;
  10. TrainPtns(:,i)=image2ptn(ImageMatrix);
  11. end
  12. TrainPtns=TrainPtns*1e-3; % scale to ms
  13. nAfferents = size(TrainPtns,1);
  14. nPtns = NumImages;
  15. %nOutputs = 1; %%%%%%%%%% 1
  16. nOutputs = 5;
  17. loadData=0;% 是否载入已保存的模型
  18. V_thr = 1; V_rest = 0;
  19. T = 256e-3; % pattern duration ms
  20. dt = 1e-3;
  21. tau_m = 20e-3; % tau_m = 15e-3;???
  22. tau_s = tau_m/4;
  23. % K(t?ti)=V0(exp[?(t?ti)/τ]–exp[?(t?ti)/τs])
  24. aa = exp(-(0:dt:3*tau_m)/tau_m)-exp(-(0:dt:3*tau_m)/tau_s);
  25. V0 = 1/max(exp(-(0:dt:3*tau_m)/tau_m)-exp(-(0:dt:3*tau_m)/tau_s));
  26. lmd = 2e-2;%1e-2/V0; % optimal performance lmd=3e-3*T/(tau_m*nAfferents*V0) 1e-4/V0;
  27. maxEpoch = 200;
  28. mu = 0.99; % momentum factor
  29. % generate patterns (each pattern consists one spik-e per afferent)
  30. if loadData ==0 %初始化网络
  31. weights = 1e-2*randn(nAfferents,nOutputs); % 1e-3*randn(nAfferents,1);
  32. save('weights0','weights');
  33. else
  34. load('weights0','weights');
  35. end
  36. %Class = logical(eye(nOutputs)); % desired class label for each pattern
  37. %Class = false(1,26); Class(26)=true;
  38. Class = de2bi(1:26,'left-msb'); Class=Class';
  39. correctRate=zeros(1,maxEpoch);
  40. dw_Past=zeros(nAfferents,nPtns,nOutputs); % momentum for accelerating learning.上一个权重的更新,用于动量计算
  41. for epoch=1:maxEpoch
  42. Class_Tr = false(nOutputs,nPtns); % actual outputs of training
  43. for pp=1:nPtns
  44. % Class_Tr = false(nOutputs,1); % actual outputs of training
  45. for neuron=1:nOutputs
  46. Vmax=0; tmax=0;
  47. fired=false;
  48. Vm1=zeros(1,256); indx1= 1; % trace pattern 1
  49. for t=dt:dt:T
  50. Vm = 0;
  51. if fired==false
  52. Tsyn=find(TrainPtns(:,pp) <=t+0.1*dt); % no cut window
  53. else
  54. Tsyn= find(TrainPtns(:,pp)<= t_fire+0.1*dt); % shut down inputs
  55. end
  56. if ~ isempty( Tsyn)
  57. A1= TrainPtns(:,pp);
  58. A2= A1(Tsyn);
  59. K = V0*(exp(-(t-A2)/tau_m)-exp(-(t-A2)/tau_s)); % the kernel value for each fired afferent
  60. A1= weights(:,neuron);
  61. firedWeights= A1(Tsyn);
  62. Vm = Vm + firedWeights'* K ;
  63. end
  64. Vm = Vm + V_rest;
  65. if Vm>=V_thr && fired==false % fire
  66. fired=true;
  67. t_fire=t;
  68. Class_Tr(neuron,pp)=true;
  69. end
  70. if Vm>Vmax
  71. Vmax=Vm; tmax=t;
  72. end
  73. %if pp==1
  74. Vm1(indx1)=Vm;
  75. indx1=indx1+1;
  76. %end
  77. end
  78. %if pp==1
  79. figure(1); plot(dt:dt:T,Vm1);
  80. title(strcat('Image ',char('A'+pp-1),'; neuron: ',num2str(neuron))); drawnow;
  81. %end
  82. if Vmax <=0
  83. tmax=max(TrainPtns(:,pp));
  84. end
  85. if Class_Tr(neuron,pp)~=Class(neuron,pp) % error
  86. Tsyn=find(TrainPtns(:,pp)<=tmax+0.1*dt);
  87. if ~isempty(Tsyn)
  88. A1=TrainPtns(:,pp);
  89. A2=A1(Tsyn);
  90. K =V0*(exp(-(tmax-A2)/tau_m)-exp(-(tmax-A2)/tau_s)); % the kernel value for each fired afferent
  91. A1=weights(:,neuron);
  92. dwPst=dw_Past(:,pp,neuron);
  93. if fired==false % LTP
  94. Dw=lmd*K;
  95. else % LTD
  96. Dw=-1.1*lmd*K;
  97. end
  98. A1(Tsyn) = A1(Tsyn) + Dw + mu*dwPst(Tsyn);
  99. weights(:,neuron)=A1;
  100. dwPst(Tsyn)=Dw;
  101. dw_Past(:,pp,neuron) = dwPst;
  102. end
  103. end
  104. end % end of one neuron computation
  105. end % end of one image
  106. %CC=isequal(Class,Class_Tr);
  107. %correctRate(epoch)=sum(Class==Class_Tr)/length(Class);
  108. CC = bi2de(Class_Tr','left-msb');
  109. end
  110. save('TrainedWt','weights');
  111. figure(2); plot(1:maxEpoch,correctRate,'-b.');
  112. end
  113. %%将图片编码为脉冲序列并保存在向量中
  114. function spikeTrain=image2ptn(A)
  115. %% convert a image to a spike train saved in a vector
  116. RandParts=1;
  117. % A1=A';
  118. % B=[A1(:);A(:)];
  119. % numPixels=length(B);
  120. % numInputNeurons=numPixels/8; % 64 neurons
  121. % spikeTrain=zeros(numInputNeurons,1);
  122. % for i=1:numInputNeurons
  123. % Bits=B((1+(i-1)*8):(8+(i-1)*8));
  124. % Bits=Bits';
  125. % spikeTime=bi2de(Bits,'left-msb');
  126. % if spikeTime==0
  127. % spikeTime=2^8; % put 0 to the end of the interval
  128. % end
  129. % spikeTrain(i)=spikeTime;
  130. % end
  131. spikeTrain=zeros(32,1);
  132. if RandParts==1
  133. loadR=1;
  134. AR=A(:);
  135. if loadR==0
  136. R = randperm(size(A,1)*size(A,2));
  137. save('RandIndex','R');
  138. else
  139. load('RandIndex','R');
  140. end
  141. numRandNeus=32;
  142. for i=1:numRandNeus
  143. IndexR=R((1+(i-1)*8):(8+(i-1)*8));
  144. Bits=AR(IndexR);
  145. Bits=Bits';
  146. spikeTime=bi2de(Bits,'left-msb'); % 二进制转十进制
  147. if spikeTime==0
  148. spikeTime=2^8; % put 0 to the end of the interval
  149. end
  150. spikeTrain(i)=spikeTime;
  151. end
  152. end
  153. end



Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐