在 python 中模拟神经元脉冲序列

Posted

技术标签:

【中文标题】在 python 中模拟神经元脉冲序列【英文标题】:Simulating a neuron spike train in python 【发布时间】:2016-07-03 04:26:55 【问题描述】:

我正在研究的模型有一个神经元(由 Hodgkin-Huxley 方程建模),并且神经元本身接收来自其他神经元的一堆突触输入,因为它位于网络中。对输入进行建模的标准方法是使用由一组以指定速率到达的 delta 函数脉冲组成的尖峰序列,作为泊松过程。一些脉冲提供对神经元的兴奋反应,而一些提供抑制脉冲。所以突触电流应该是这样的:

这里,Ne 是兴奋性神经元的数量,Ni 是抑制性神经元,h 是 0 或 1(概率 p 为 1)表示是否成功传输了一个尖峰,而 delta 中的 $t_k^l$函数是第 k 个神经元的第 l^th 个尖峰的放电时间($t_m^n$ 相同)。所以我们尝试编码的基本想法是假设首先我有 100 个神经元向我的 HH 神经元提供脉冲(其中 80 个是兴奋性的,其中 20 个是抑制性的)。然后我们形成一个阵列,其中一列列举了神经元(因此神经元#0-79 是兴奋性的,#80-99 是抑制性的)。然后我们检查在某个时间间隔内是否有尖峰,如果有,则在 0-1 之间选择一个随机数,如果它低于我指定的概率 p,则将其分配为数字 1,否则将其设为 0。我们然后将电压绘制为时间的函数,以查看神经元何时出现峰值。

我认为代码有效,但问题是,一旦我在网络中添加更多神经元(一篇论文声称他们总共使用了 5000 个神经元),它需要永远运行,这对于进行数值模拟是不可行的.我的问题是:有没有更好的方法来模拟脉冲序列进入神经元,以便网络中大量神经元的计算速度大大加快?这是我们尝试的代码:(有点长,因为 HH 方程非常详细):

import scipy as sp
import numpy as np
import pylab as plt

#Constants
C_m  =   1.0 #membrane capacitance, in uF/cm^2"""
g_Na = 120.0 #Sodium (Na) maximum conductances, in mS/cm^2""
g_K  =  36.0 #Postassium (K) maximum conductances, in mS/cm^2"""
g_L  =   0.3 #Leak maximum conductances, in mS/cm^2"""
E_Na =  50.0 #Sodium (Na) Nernst reversal potentials, in mV"""
E_K  = -77.0 #Postassium (K) Nernst reversal potentials, in mV"""
E_L  = -54.387 #Leak Nernst reversal potentials, in mV"""

def poisson_spikes(t, N=100, rate=1.0 ):
    spks = []
    dt = t[1] - t[0]
    for n in range(N):
        spkt = t[np.random.rand(len(t)) < rate*dt/1000.] #Determine list of times of spikes
        idx = [n]*len(spkt) #Create vector for neuron ID number the same length as time
        spkn = np.concatenate([[idx], [spkt]], axis=0).T #Combine tw lists
        if len(spkn)>0:        
            spks.append(spkn)
    spks = np.concatenate(spks, axis=0)
    return spks

N = 100
N_ex = 80 #(0..79)
N_in = 20 #(80..99)
G_ex = 1.0
K = 4

dt = 0.01
t = sp.arange(0.0, 300.0, dt) #The time to integrate over """
ic = [-65, 0.05, 0.6, 0.32]

spks =  poisson_spikes(t, N, rate=10.)

def alpha_m(V):
        return 0.1*(V+40.0)/(1.0 - sp.exp(-(V+40.0) / 10.0))

def beta_m(V):
        return 4.0*sp.exp(-(V+65.0) / 18.0)

def alpha_h(V):
        return 0.07*sp.exp(-(V+65.0) / 20.0)

def beta_h(V):
        return 1.0/(1.0 + sp.exp(-(V+35.0) / 10.0))

def alpha_n(V):
        return 0.01*(V+55.0)/(1.0 - sp.exp(-(V+55.0) / 10.0))

def beta_n(V):
        return 0.125*sp.exp(-(V+65) / 80.0)

def I_Na(V, m, h):
        return g_Na * m**3 * h * (V - E_Na)

def I_K(V, n):
        return g_K  * n**4 * (V - E_K)

def I_L(V):
        return g_L * (V - E_L)

def I_app(t):
        return 3

def I_syn(spks, t):
    """
    Synaptic current
    spks = [[synid, t],]
    """
    exspk = spks[spks[:,0]<N_ex] # Check for all excitatory spikes
    delta_k = exspk[:,1] == t # Delta function
    if sum(delta_k) > 0:
        h_k = np.random.rand(len(delta_k)) < 0.5 # p = 0.5
    else:
        h_k = 0

    inspk = spks[spks[:,0] >= N_ex] #Check remaining neurons for inhibitory spikes
    delta_m = inspk[:,1] == t #Delta function for inhibitory neurons
    if sum(delta_m) > 0:
        h_m = np.random.rand(len(delta_m)) < 0.5 #p =0.5
    else:
        h_m = 0

    isyn = C_m*G_ex*(np.sum(h_k*delta_k) - K*np.sum(h_m*delta_m))

    return  isyn


def dALLdt(X, t):
        V, m, h, n = X
        dVdt = (I_app(t)+I_syn(spks,t)-I_Na(V, m, h) - I_K(V, n) - I_L(V)) / C_m
        dmdt = alpha_m(V)*(1.0-m) - beta_m(V)*m
        dhdt = alpha_h(V)*(1.0-h) - beta_h(V)*h
        dndt = alpha_n(V)*(1.0-n) - beta_n(V)*n
        return np.array([dVdt, dmdt, dhdt, dndt])

X = [ic]
for i in t[1:]:
    dx = (dALLdt(X[-1],i))
    x = X[-1]+dt*dx
    X.append(x)

X = np.array(X)    
V = X[:,0]        
m = X[:,1]
h = X[:,2]
n = X[:,3]
ina = I_Na(V, m, h)
ik = I_K(V, n)
il = I_L(V)

plt.figure()
plt.subplot(3,1,1)
plt.title('Hodgkin-Huxley Neuron')
plt.plot(t, V, 'k')
plt.ylabel('V (mV)')

plt.subplot(3,1,2)
plt.plot(t, ina, 'c', label='$I_Na$')
plt.plot(t, ik, 'y', label='$I_K$')
plt.plot(t, il, 'm', label='$I_L$')
plt.ylabel('Current')
plt.legend()

plt.subplot(3,1,3)
plt.plot(t, m, 'r', label='m')
plt.plot(t, h, 'g', label='h')
plt.plot(t, n, 'b', label='n')
plt.ylabel('Gating Value')
plt.legend()

plt.show()

我不熟悉其他专门为神经网络设计的包,但我想自己写,主要是因为我打算做随机分析,这需要相当多的数学细节,我不知道那些包提供了这样的细节。

【问题讨论】:

你考虑过使用带有python接口的NEURON模拟器吗?它处理数值积分性能问题,并让您专注于方程式。可以绘制任何变量,并且它带有内置的 HH 通道。当然,您可以使用自己的 DE 编写自己的通道机制。 neuron.yale.edu/neuron 您可能还对NEST Simulator 感兴趣,它被明确构建为在点神经元动力学方面速度很快,并且可以很好地扩展到最大的网络。输入是神经元和突触的方程,连接性,输出是脉冲序列。 Python 界面,HH 已经可用,可定制,...对于分析,您可能对 Elephant 感兴趣,其中包含许多用于统计分析的构建块。 【参考方案1】:

分析表明您的大部分时间都花在了这两行:

    if sum(delta_k) > 0:

    if sum(delta_m) > 0:

将这些更改为:

    if np.any(...)

将一切速度提高 10 倍。如果您想逐行进行更多分析,请查看 kernprof: https://github.com/rkern/line_profiler

【讨论】:

谢谢你。这无疑有助于加快计划!【参考方案2】:

作为对 welch 回答的补充,您可以使用 scipy.integrate.odeint 来加速集成:替换

X = [ic]
for i in t[1:]:
    dx = (dALLdt(X[-1],i))
    x = X[-1]+dt*dx
    X.append(x)

通过

from scipy.integrate import odeint
X=odeint(dALLdt,ic,t)

在我的计算机上将计算速度提高了 10 倍以上。

【讨论】:

所以通常我使用 odeint,但是当我有一堆 delta 函数输入时,我对使用它有些担心(我是数学家,所以我们从不使用 delta 函数,但物理学家会这样做我被困在使用它们)。当我通过打印时间来查看时间积分时,它没有使用 dt = 0.01 我不确定这是否会导致问题(实际上,我什至不确定“尖峰”是否出现了...似乎通过将成功概率设置为 1 表示兴奋和 0 表示抑制,没有尖峰......这很奇怪) 是的,我担心可能存在集成问题。 odeint 中有一个 hmaxparameter 可能可以使用,但也可能导致一些奇怪的行为。【参考方案3】:

如果您有 NVidia grpahics 板,您可以使用 numba/numbapro 来加速您的 Python 代码并达到实时 4K 神经元,每个神经元有 128 个突触前神经元。

【讨论】:

这将有助于扩展这个答案,也许有一个融合 numba 库的小例子。

以上是关于在 python 中模拟神经元脉冲序列的主要内容,如果未能解决你的问题,请参考以下文章

Scipy FFT 和 Numpy FFT 在脉冲序列频谱上存在分歧?

数字信号处理序列傅里叶变换 ( 基本序列的傅里叶变换 | 单位脉冲序列傅里叶变换 )

数字信号处理序列傅里叶变换 ( 基本序列的傅里叶变换 | 单位脉冲序列 δ(n) 傅里叶变换 )

数字信号处理基本序列 ( 单位阶跃序列 | 单位阶跃序列与单位脉冲序列关系 | 矩形序列 | 矩形序列与单位阶跃序列关系 | 矩形序列作用 )

教你搭建多变量时间序列预测模型LSTM(附代码数据集)

训练 LSTM 神经网络以预测 pybrain、python 中的时间序列