在MATLAB中实现alpha突触函数的更好方法

时间:2017-05-21 10:53:30

标签: matlab performance

我实现了一个漏洞集成和火神经元系统,它以alpha函数alpha = t/tau * exp(1 - (t/tau))的形式提供输出。但是,我用来实现它的代码占用了至少80%的运行时(大约5s中的4s)。在程序过程中,这个prt被调用至少30000次,而computealphasynapseoutput函数被调用至少150万次。所以我想减少这部分的运行时间。我已经尝试使用arrayfun来实现它,但这比这花费了更多的时间。

任何人都可以建议更有效地实施此代码吗?

为了实现alpha突触,我使用了以下代码:

% Get the identity of the currently active neurons
idAllActiveNeuron = idAllActiveNeuron > 0;
if any(idAllActiveNeuron) % Only run if atleast one neuron is active
    for iActiveNeuron = find(idAllActiveNeuron).' % run for each active neuron
        %% synapticOutputArray stores the synaptic output for each neuron for each time instant
        %% iIntegration is the time instant
        %% spikeTimesArray is a cell array that is composed of spike times for 
        % each neuron. So if I have 5 neurons and neuron 4 spikes twice 
        % while neuron 5 spikes once, spikeTimesArray would be something 
        % like {[], [], [], [0.0023, 0.0034], [0.0675]}
        %% integrationInstant would be a time value like 0.0810
        %% tau_syn stores the value of tau for each neuron

        synapticOutputArray(iActiveNeuron, iIntegration) = computealphasynapseoutput(spikeTimesArray{iActiveNeuron}, integrationInstant, tau_syn(iActiveNeuron));

    end % iActiveNeuron
end

函数computealphasynapse实现如下:

function synapticOutput = computealphasynapseoutput(firingTime, integrationInstant, tauSyn)
%%COMPUTEALPHASYNAPSEOUTPUT Calculates the synaptic output over all
%previous spikes of the neuron at a particular time instant using the
%alpha synapse function
%
% Usage:
%   synapticOutput = computealphasynapseoutput(firingTime, integrationInstant, tauSyn)
%
% Inputs:
%           firingTime: vector of previous firing times (in seconds)
%   integrationInstant: current integration time instant (in seconds)
%               tauSyn: synaptic time constant of the neuron (in seconds)
%
% Output:
%   synapticOutput: Synaptic output of the neuron at current time summed
%                   over all firing instances
%

% Calculate the time difference of firing from current time
timeDifference = (integrationInstant - firingTime) / tauSyn;
% Calculate and sum the synaptic output for each spike
synapticOutput = sum(timeDifference .* exp(1 - timeDifference));

end % computealphasynapseoutput

编辑:

  

我终于将这个问题的赏金给了gnovice   他的精彩回答。它帮我刮了整整40秒   从我的模拟时间(从68到28秒)。我希望它对人们有用   在未来也是如此。我还要感谢MrAzzaman和   qbzenker花时间回答这个问题   教我一些很酷的新方法。还有,评论和评论的其他人   帮助过我。感谢

3 个答案:

答案 0 :(得分:2)

我测试了以下代码,其中包含一组随机产生的点火时间和taus,并且它的运行速度始终提高了约7-8倍。这有点奇怪,但请忍受我。

主要思想是MATLAB在矩阵上的操作方面表现优异,但如果必须打破这些操作,例如使用for循环,则可能会非常慢。那么,我们的想法是尝试一次完成整个单元阵列。如果您可以修改点火时间的存储方式(即不在单元格数组中),这会更容易,但我已经完成了您的工作。

基本上,我们要做的是将点火时间的单元阵列转换为一个大矢量,一次计算所有这些的突触输出,然后根据它们的神经元对它们求和。因此,举例来说,我们最终会得到以下结果:

% given spikeTimesArray = {[], [], [], [0.0023, 0.0034], [0.0675],...}
firingTimes = [0.0023, 0.0034, 0.0675,...];
neuronInd = [4,4,5,...];

其中neuronInd向量是每个发射时间的相应神经元指数。这是一个向量,如果您可以在创建触发时间数组时创建它,则整个过程会更快。实际上,计算neuronInd的过程有点不透明。

无论如何,我们按如下方式计算:

% Calculate the number of time spikes for each neuron
numSpikes = cellfun(@numel,spikeTimesArray);
% Determine the index of all neurons with spikes 
n = find(numSpikes);
% Calculate the starting indices for the neuronInd vector
m = cumsum([1,numSpikes]);
% Generate the neuronInd vector
neuronInd = interp1(m(n),n,1:(m(end)-1),'previous');
% Messy kludge to get around how interp1 works
neuronInd(isnan(neuronInd)) = n(end);
%Calculate timeDifference
delt = ([spikeTimesArray{:}] - integratonInstant)./tau_syn(neuronInd);
synapticOutputArray(:, iIntegration) = accumarray(neuronInd',delt.*exp(1-delt));

希望这有帮助。

答案 1 :(得分:2)

这个解决方案可能有点奇怪,而且对于手头的问题非常具体,但非常有效。我们的想法是重新组织计算汇总的alpha突触输出的公式,以便最大限度地减少完成的计算,特别减少使用exp计算的取幂次数,这在计算上很昂贵。这是单个神经元的重新制定:

enter image description here

对于结果,我们有一个时间函数f(t)(计算当前时间点t的一个指数)乘以t中带有时不变参数{{}的线性函数。 1}}和A。请注意,BA仅取决于峰值时间,并将所有先前峰值出现的指数相加。我们可以为每个神经元存储这两个参数BA,当神经元中出现新的尖峰时,我们只需计算一对新的术语并将它们添加到{{1的现有值}和B

以下是警告:注意指数的值。对于AB,值为A,如果值太大,则可能会溢出计算,从而导致B。对于我们在这里使用的参数,这不会发生。 ts/tau的值最大为1(因为我们只模拟一秒),Inf的最小值为0.01秒。指数最多为100,给出的值很大(大约10 ^ 43),但很容易由double变量处理。同样地,ts中的指数将具有-99的最大负值,给出非常小的值(大约10 ^ -43),但仍然可以由tau变量轻松处理而不会下溢到0。请注意,使用较小的f(t)值或较长的模拟时间可能会造成麻烦。

那么,我们该如何实现呢?以下是您必须添加/修改的相关代码段(请注意,您甚至不再需要double):

tau

它的表现如何?以下是我使用timeit)进行的一些测量,其中1101个神经元的平均尖峰发射频率以0.1毫秒的时间步长模拟一秒钟(10001个时间点):

enter image description here

发生的峰值越多,原始解决方案完成的时间就越长。然而,重新配制的溶液所需的时间几乎恒定,并且从未超过0.6秒。我还可以确认这两种方法的输出是相等的(突触输出波形的最大差异大约为10 ^ -13)。

答案 2 :(得分:1)

这段代码在正确的条件下给我一个显着的加速(通常在给定时间任何给定神经元的最大尖峰时间数和低数目神经元> 5)。由于神经元数量较少,它与上面代码的性能类似。

请注意,我还没有使用您的实际输入测试此代码,因此您可能需要进行一些调整才能使其正常工作。此外,您的数据实际上可能比我使用的虚拟数据慢。加速背后的想法是vectorization -  基本上对整个向量进行操作,而不是将它分解为一堆for循环。

这将要求您spikeTimesArray使用NaN s而不是缺少值的矩阵。所以像这样:

 spikeTimesArray={[], [], [], [0.0023, 0.0034], [0.0675]}

会变成

spikeTimesArray=[[NaN;NaN], [NaN;NaN], [NaN;NaN], [0.0023; 0.0034], [0.0675;NaN]]

而不是使用for循环,你只需要完成整个向量操作:

idAllActiveNeuron = idAllActiveNeuron > 0;
if any(idAllActiveNeuron) % Only run if atleast one neuron is active
   % for iActiveNeuron = find(idAllActiveNeuron) % ELIMINATE THIS FOR LOOP

iActiveNeuron = find(idAllActiveNeuron);

timeDifference = (integrationInstant -spikeTimesArray2(:,iActiveNeuron))...
                  ./ tau_syn(iActiveNeuron);

synapticOutputArray(iActiveNeuron, iIntegration) = ...
                                sum(timeDifference .* exp(1 - timeDifference),'omitnan');

请注意,在此实现中,我假设spikeTimesArray2是NxM矩阵,其中N对应于给定神经元的最大尖峰数,M是神经元的总数。此外,tau_syn是行向量。