我实现了一个漏洞集成和火神经元系统,它以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花时间回答这个问题 教我一些很酷的新方法。还有,评论和评论的其他人 帮助过我。感谢
答案 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
计算的取幂次数,这在计算上很昂贵。这是单个神经元的重新制定:
对于结果,我们有一个时间函数f(t)
(计算当前时间点t
的一个指数)乘以t
中带有时不变参数{{}的线性函数。 1}}和A
。请注意,B
和A
仅取决于峰值时间,并将所有先前峰值出现的指数相加。我们可以为每个神经元存储这两个参数B
和A
,当神经元中出现新的尖峰时,我们只需计算一对新的术语并将它们添加到{{1的现有值}和B
。
以下是警告:注意指数的值。对于A
和B
,值为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个时间点):
发生的峰值越多,原始解决方案完成的时间就越长。然而,重新配制的溶液所需的时间几乎恒定,并且从未超过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
是行向量。