这是国庆前导师让了解的脉冲神经仿真平台NEST的部分介绍手册的翻译和整理,记录一下留个备份,主要内容可以通过查看文档中的链接索引到官网。
如需要手册代码合辑及例程ipynb文件,请查看NEST脉冲神经网络仿真手册例程合辑
索引目录
第1部分:神经元和简单神经网络
介绍
在本讲义中,我们介绍了使用PyNEST模拟神经元网络的第一步。阅读完本材料后,您将知道如何:
- 开始PyNEST
- 创建神经元和刺激/记录设备
- 查询并设置其参数
- 将它们彼此连接或连接到设备
- 模拟网络
- 从记录设备中提取数据
有关使用PyNEST的更多信息,请参见本底漆的其他部分:
可以在Example Networks上找到更高级的示例,或者在子目录中查看NEST安装的源目录pynest/examples/
。
PyNEST-NEST模拟器的界面
图1 Python界面图。Python解释器将NEST作为模块导入,并动态加载NEST仿真器内核(pynestkernel.so
)。核心功能在中定义hl_api.py
。用户(mysimulation.py
)的模拟脚本使用此高级API中定义的功能。这些函数以SLI(模拟语言解释器)生成代码,SLI是NEST解释器的本地语言。该解释器进而控制NEST仿真内核。
神经仿真工具(NEST:www.nest-initiative.org)1 设计用于仿真大型的点神经元异构网络。它是根据GPL许可发布的开源软件。该模拟器带有Python 2的接口。图1 说明了用户的模拟脚本(mysimulation.py
)和NEST模拟器之间的交互。埃普勒等。图3 包含对该接口实现的技术上的详细描述,本文的某些部分均基于此参考。仿真内核是用C ++编写的,以获得最高的仿真性能。
您可以从Python提示或在ipython中交互使用PyNEST。当您探索PyNEST,尝试学习新功能或调试例程时,这非常有用。退出探索模式后,您会发现它节省了大量时间,可以在文本文件中编写模拟。这些可以依次从命令行或Python或ipython提示符下运行。
无论是交互式,半交互式还是纯粹执行脚本,首先要做的就是将NEST的功能导入Python解释器中。
import nest
但是,应注意,在导入Nest *之前,*必须先导入某些外部软件包。这些包括scikit-learn 和SciPy。
from sklearn.svm import LinearSVC
from scipy.special import erf
import nest
与Python的所有其他模块一样,可以提示可用的功能。
dir(nest)
这样的命令之一是nest.Models()
或在ipython中nest.Models?
,它将返回您可以使用的所有可用模型的列表。如果要获取有关特定命令的更多信息,可以使用Python的标准帮助系统。
这将返回帮助文本(docstring),解释该特定功能的用法。NEST中也有一个帮助系统。您可以使用在浏览器中打开帮助页面,nest.helpdesk()
并使用可以获取特定对象的帮助页面nest.help(object)
。
创建节点
NEST中的神经网络由两种基本元素类型组成:节点和连接。节点是神经元,设备或子网。设备用于刺激神经元或从中进行记录。可以在子网中安排节点,以构建分层网络,例如层,列和区域-我们将在本课程的后面部分进行介绍。现在,我们将在启动NEST时存在的默认子网中工作。root node
首先,根子网为空。使用命令创建新节点,该命令Create
将所需节点类型的模型名称以及要创建的节点数和初始化参数作为参数。该函数返回到新节点的句柄列表,您可以将其分配给变量以供以后使用。这些句柄是整数,称为ids。许多PyNEST函数期望或返回ID列表(请参阅命令概述)。因此,很容易通过单个函数调用将函数应用于大型节点集。
导入NEST以及Matplotlib 4的Pylab接口后,我们将使用该接口来显示结果,我们可以开始创建节点。作为第一个示例,我们将创建一个类型为的神经元 iaf_psc_alpha
。该神经元是具有α形突触后电流的整合并发射神经元。该函数返回所有已创建神经元ID的列表,在这种情况下只有一个,我们将其存储在名为的变量中neuron
。
import pylab
import nest
neuron = nest.Create("iaf_psc_alpha")
现在,我们可以使用id来访问此神经元的属性。NEST中节点的属性通常通过形式为的键-值对的Python字典进行访问。为了查看神经元具有哪些属性,您可以询问它的状态。{key: value}
nest.GetStatus(neuron)
这将在Python控制台中打印出相应的字典。这些特性中的许多特性与神经元的动力学无关。要找出有趣的属性,请通过服务台查看模型的文档。如果您已经知道您感兴趣的属性,则可以指定一个键或键列表作为的可选参数GetStatus
:
nest.GetStatus(neuron, "I_e")
nest.GetStatus(neuron, ["V_reset", "V_th"])
在第一种情况下,我们查询恒定背景电流的值 I_e
; 结果以带有一个元素的元组给出。在第二种情况下,我们查询复位电位和神经元阈值的值,并将结果作为嵌套元组接收。如果GetStatus
为节点列表调用if ,则外部元组的维数是节点列表的长度,而内部元组的维数是指定的键数。
要修改字典中的属性,请使用SetStatus
。在下面的示例中,背景电流设置为376.0pA,该值导致神经元周期性地尖峰。
nest.SetStatus(neuron, {"I_e": 376.0})
请注意,通过在字典中提供多个逗号分隔的key:value对,我们可以同时设置多个属性。另请注意,NEST是类型敏感的-如果特定属性的类型是 double
,则您需要显式编写小数点:
nest.SetStatus(neuron, {"I_e": 376})
将导致错误。这方便地保护我们免受整数除法错误的影响,这些错误很难被发现。
接下来,我们创建一个设备multimeter
,该设备可用于记录一段时间内神经元的膜电压。我们设置其属性withtime
,以便它还将记录采样膜电压的时间点。该属性record_from
需要我们要记录的变量名称的列表。万用表暴露的变量因模型而异。对于特定模型,可以通过查看神经元的属性来检查暴露变量的名称recordables
。
multimeter = nest.Create("multimeter")
nest.SetStatus(multimeter, {"withtime":True, "record_from":["V_m"]})
现在spikedetector
,我们创建一个,用于记录神经元产生的尖峰事件的另一种设备。我们使用可选的关键字参数params
来设置其属性。这是使用的替代方法SetStatus
。该属性withgid
指示尖峰检测器是否要记录其从中接收事件的源ID(即我们的神经元的ID)。
spikedetector = nest.Create("spike_detector",
params={"withgid": True, "withtime": True})
关于命名的简短说明:在这里,我们称神经元neuron
,万用表multimeter
等。当然,您可以将创建的节点分配给您喜欢的任何变量名称,但是如果您选择反映仿真概念的名称,则脚本更易于阅读。
使用默认连接连接节点
现在我们知道了如何创建单个节点,我们可以开始将它们连接起来以形成一个小型网络。
nest.Connect(multimeter, neuron)
nest.Connect(neuron, spikedetector)
图2 具有恒定输入电流的集成并发射神经元的膜电位。
图3 神经元的尖峰。
Connect
指定参数的顺序反映了事件的流程:如果神经元出现尖峰,它将向尖峰检测器发送事件。相反,万用表会在该时间点定期向神经元发送请求以询问其膜电位。这可以看作是粘在神经元中的理想电极。
现在我们已连接网络,可以开始仿真了。我们必须告知仿真内核仿真要运行多长时间。这里我们选择1000ms。
nest.Simulate(1000.0)
恭喜,您刚刚在NEST中模拟了您的第一个网络!
从设备提取和绘制数据
仿真完成后,我们可以获得万用表记录的数据。
dmm = nest.GetStatus(multimeter)[0]
Vms = dmm["events"]["V_m"]
ts = dmm["events"]["times"]
在第一行中,我们获取所有查询节点的状态字典列表。在这里,变量multimeter
是只有一个节点的ID,因此返回的列表仅包含一个字典。我们通过索引来提取此列表的第一个元素(因此[0]
在末尾)。当使用PyNEST时,这种类型的操作非常频繁地发生,因为大多数函数被设计为接受和返回列表,而不是单个值。这是为了使对项目组的操作(设置神经元网络模拟时的通常情况)更加方便。
该词典包含一个名为的条目events
,用于保存记录的数据。它本身与所述条目的字典V_m
和 times
,我们分别存储Vms
和ts
分别在第二和第三行。如果您在想像词典的字典以及从中提取的内容时遇到困难,请首先尝试打印dmm
到屏幕上,以使您更好地了解其结构,然后在下一步中提取字典events
,依此类推。
现在我们准备在图中显示数据。为此,我们使用pylab
。
import pylab
pylab.figure(1)
pylab.plot(ts, Vms)
第二行打开一个数字(数字为1),第三行实际生成该图。您尚未看到它,因为我们还没有使用过pylab.show()
。在此之前,我们类似地进行操作,以获取并显示来自尖峰检测器的尖峰。
dSD = nest.GetStatus(spikedetector,keys="events")[0]
evs = dSD["senders"]
ts = dSD["times"]
pylab.figure(2)
pylab.plot(ts, evs, ".")
pylab.show()
在这里,我们通过使用可选的关键字参数keys
来更简洁地提取事件GetStatus
。这将使用键events
而不是整个状态字典提取字典元素。输出应如图2和图3所示。如果要将此作为脚本执行,只需将所有行粘贴到名为的文本文件中one-neuron.py
。然后,您可以在命令行中通过在文件名前面加上python
,或者在Python或ipython提示符下通过在文件前面加上前缀来运行它run
。
可以在一个万用表上收集多个神经元的信息。这确实使信息检索变得复杂:n个神经元中每个神经元的数据将以交错方式存储和返回。幸运的是,Python为我们提供了一个方便的数组操作,可轻松拆分数据:使用一个步骤(有时称为跨步)对数组进行切片。为了解释这一点,您必须调整上一部分中创建的模型。将代码保存为新名称,在下一部分中,您还将使用此代码。创建一个额外的神经元,其背景电流具有不同的值:
neuron2 = nest.Create("iaf_psc_alpha")
nest.SetStatus(neuron2 , {"I_e": 370.0})
现在将这个新创建的神经元连接到万用表:
nest.Connect(multimeter, neuron2)
运行模拟并绘制结果,它们看起来不正确。要解决此问题,必须分别绘制两个神经元迹线。用multimeter
以下行替换从中提取事件的代码。
pylab.figure(3)
Vms1 = dmm["events"]["V_m"][::2] # start at index 0: till the end: each second entry
ts1 = dmm["events"]["times"][::2]
pylab.plot(ts1, Vms1)
Vms2 = dmm["events"]["V_m"][1::2] # start at index 1: till the end: each second entry
ts2 = dmm["events"]["times"][1::2]
pylab.plot(ts2, Vms2)
可以在http://docs.scipy.org/doc/numpy-1.10.0/reference/arrays.indexing.html上找到更多信息 。
使用特定连接连接节点
神经活动的常用模型是泊松过程。现在,我们改编前面的示例,以使神经元接收2个泊松尖峰序列,一个是兴奋性的,另一个是抑制性的。因此,我们需要一个新设备,即poisson_generator
。创建神经元后,我们创建这两个生成器并将它们的频率分别设置为80000Hz和15000Hz。
noise_ex = nest.Create("poisson_generator")
noise_in = nest.Create("poisson_generator")
nest.SetStatus(noise_ex, {"rate": 80000.0})
nest.SetStatus(noise_in, {"rate": 15000.0})
此外,恒定输入电流应设置为0:
nest.SetStatus(neuron, {"I_e": 0.0})
兴奋性发生器的每个事件应产生1.2pA振幅的突触后电流,-2.0pA的抑制事件。可以在字典中定义突触权重,然后Connect
使用关键字syn_spec
(突触规范)将其传递给 函数。一般而言确定突触所有参数可以在突触字典来指定,例如"weight"
, "delay"
,突触模型("model"
)和参数特定于突触模型。
syn_dict_ex = {"weight": 1.2}
syn_dict_in = {"weight": -2.0}
nest.Connect(noise_ex, neuron, syn_spec=syn_dict_ex)
nest.Connect(noise_in, neuron, syn_spec=syn_dict_in)
图4 以Poisson噪声为输入的积分并发射神经元的膜电位。
图5 带有噪声的神经元的尖峰。
在引言的下一部分(第2部分:神经元种群)中,我们将研究更多一次连接多个神经元的方法。
两个相连的神经元
图6 Neuron1的尖峰诱发的Neuron2的突触后电位
连接神经元没有其他魔术。为了证明这一点,我们从具有恒定输入电流的一个神经元的原始示例开始,然后添加第二个神经元。
import pylab
import nest
neuron1 = nest.Create("iaf_psc_alpha")
nest.SetStatus(neuron1, {"I_e": 376.0})
neuron2 = nest.Create("iaf_psc_alpha")
multimeter = nest.Create("multimeter")
nest.SetStatus(multimeter, {"withtime":True, "record_from":["V_m"]}
现在我们连接neuron1
到neuron2
并记录膜电位,neuron2
这样我们就可以观察到尖峰引起的突触后电位neuron1
。
nest.Connect(neuron1, neuron2, syn_spec = {"weight":20.0})
nest.Connect(multimeter, neuron2)
此处使用默认延迟1ms。如果除了重量之外还指定了延迟,则可以使用以下快捷方式:
nest.Connect(neuron1, neuron2, syn_spec={"weight":20, "delay":1.0})
如果您模拟网络和以前绘制的膜电位,则应看到的突触后电位neuron2
通过的尖峰引起neuron1
在图6中。
命令概述
这些是我们在本讲义中为示例介绍的功能。本简介的以下部分将添加更多内容。
获取有关NEST的信息
请参阅“ 获得帮助”部分
节点数
-
-
Create(model, n=1, params=None)
在当前子网中创建
n
类型的实例model
。可以将新节点的参数指定为params
(单个字典,或具有size的字典列表n
)。如果省略,model
则使用的默认值。
-
-
-
GetStatus(nodes, keys=None)
返回给定列表的参数字典列表
nodes
。如果keys
-