PyNest——Part1:neurons and simple neural networks
neurons and simple neural networks
pynest – nest模拟器的界面
神经模拟工具(NEST:www.nest-initiative.org)专为仿真点神经元的大型异构网络而设计。 它是根据GPL许可证发布的开源软件。 该模拟器带有Python的接口[4]。 图1说明了用户的模拟脚本(mysimulation.py)和NEST模拟器之间的交互。 [2]包含该接口实现的技术详细描述,本文的部分内容均基于此参考。 仿真内核使用C ++编写,以获得最高性能的仿真。
您可以从Python提示符或ipython内部以交互方式使用PyNEST。 当你在探索PyNEST时,这是非常有用的,试图学习一个新的功能或调试一个例程。 一旦退出探索模式,您会发现它可以节省大量时间在文本文件中编写模拟。 这些可以依次从命令行或Python或ipython提示符运行。
无论是交互式,半交互式还是纯粹执行的脚本,首先需要将NEST的功能导入Python解释器。
import nest
和Python的其他所有模块一样,可以提供可用的函数。
dir(nest)
一个这样的命令是nest.Models?,它将返回您可以使用的所有可用模型的列表。 如果你想获得更多关于特定命令的信息,你可以使用IPython的标准帮助系统。
nest.Models?
这将返回帮助文本(docstring),解释这个特定函数的用法。 NEST内还有一个帮助系统。 您可以使用nest.helpdesk()在浏览器中打开帮助页面,并且可以使用nest.help(object)获取特定对象的帮助页面。
creating nodes
NEST中的神经网络由两种基本元素类型组成:节点和连接。 节点是神经元,设备或子网络。 设备用于刺激神经元或从它们进行记录。 节点可以安排在子网络中以构建分层网络,例如图层,列和区域 - 我们将在后面的课程中讨论。 现在我们将在我们启动NEST时出现的默认子网络中工作,这就是所谓的根节点。
首先,根子网络是空的。 使用Create命令创建新节点,该命令将参数作为所需节点类型的型号名称,以及可选的要创建的节点数量和初始化参数。 该函数返回新节点的句柄列表,您可以将其分配给变量供以后使用。 这些句柄是整数,称为ids。 许多PyNEST函数期望或返回一个id列表(参见第8节)。 因此,使用单个函数调用将函数应用于大型节点集很容易。
在导入NEST以及Pylab接口到Matplotlib [[3]](#3)后,我们将使用它来显示结果,我们可以开始创建节点。 作为第一个例子,我们将创建一个类型为iaf_psc_alpha的神经元。 该神经元是具有α形突触后电流的整合火焰神经元。 该函数返回所有创建的神经元的id的列表,在本例中只有一个,我们将其存储在名为neuron的变量中。
import pylab
import nest
neuron = nest.Create("iaf_psc_alpha")
我们现在可以使用id来访问这个神经元的属性。 NEST中节点的属性通常通过形式为{key:value}的键值对的Python字典来访问。 为了查看神经元的属性,你可以问它的状态。
nest.GetStatus(neuron)
这将在Python控制台中打印出相应的字典。 这些属性中的许多与神经元的动力学无关。 要了解哪些有趣的属性,请通过帮助台查看模型的文档。 如果您已经知道您感兴趣的属性,您可以指定一个键或一个键列表作为GetStatus的可选参数:
nest.GetStatus(neuron, "I_e")
nest.GetStatus(neuron, ["V_reset", "V_th"])
在第一种情况下,我们查询常数背景电流I_e的值; 结果以包含一个元素的元组的形式给出。 在第二种情况下,我们查询重置电位和神经元阈值的值,并将结果作为嵌套元组接收。 如果为节点列表调用GetStatus,则外部元组的维度是节点列表的长度,内部元组的维度是指定的键的数量。
要修改字典中的属性,我们使用SetStatus。 在以下示例中,背景电流设置为376.0pA,这是导致神经元周期性尖峰的值。
nest.SetStatus(neuron, {"I_e": 376.0})
请注意,我们可以通过在字典中给出多个以逗号分隔的键:值对来同时设置多个属性。 另外请注意,NEST是类型敏感的 - 如果某个特定属性是double类型的,那么您确实需要明确写入小数点:
nest.SetStatus(neuron, {"I_e": 376})
将导致错误。 这很方便地保护我们避免发生整数除法错误,这很难被发现。
接下来我们创建一个万用表,这是一种我们可以用来记录神经元膜电压随时间变化的器件。 我们设置其属性的时间,以便它也将记录膜电压采样的时间点。 record_from属性需要一个我们想要记录的变量的名字列表。 暴露于万用表的变量因型号而异。 对于特定的模型,您可以通过查看神经元的属性可记录来检查暴露变量的名称。
multimeter = nest.Create("multimeter")
nest.SetStatus(multimeter, {"withtime":True, "record_from":["V_m"]})
我们现在创建一个尖峰探测器,另一个记录神经元产生的尖峰事件的设备。 我们使用可选的关键字参数params来设置它的属性。 这是使用SetStatus的替代方法。 属性withgid指示尖峰检测器是否记录它从其接收事件的源ID(即,我们的神经元的ID)。
spikedetector = nest.Create("spike_detector",
params={"withgid": True, "withtime": True})
关于命名的简短说明:这里我们称之为神经元神经元,万用表万用表等。 当然,您可以将创建的节点分配给您喜欢的任何变量名称,但如果选择反映仿真中的概念的名称,则该脚本更易于阅读。
connecting nodes with default connections
现在我们知道如何创建单个节点,我们可以开始连接它们以形成一个小型网络。
nest.Connect(multimeter, neuron)
nest.Connect(neuron, spikedetector)
指定连接参数的顺序反映了事件的流程:如果神经元尖峰,它会向尖峰检测器发送一个事件。 相反,万用表会定期向神经元发送请求,以便在该时间点询问其膜电位。 这可以被认为是粘在神经元中的完美电极。
现在我们连接了网络,我们可以开始模拟。 我们必须通知仿真内核模拟运行的时间。 这里我们选择1000ms。
nest.Simulate(1000.0)
恭喜,你刚刚在NEST模拟你的第一个网络!
extracting and plotting data from devices
模拟完成后,我们可以获取万用表记录的数据。
dmm = nest.GetStatus(multimeter)[0]
Vms = dmm["events"]["V_m"]
ts = dmm["events"]["times"]
在第一行中,我们获取所有查询节点的状态字典列表。 这里,变量万用表只是一个节点的ID,所以返回的列表只包含一个字典。 我们通过索引它来提取这个列表的第一个元素(因此最后的[0])。 这种类型的操作在使用PyNEST时非常频繁地发生,因为大多数函数都被设计为接收和返回列表,而不是单独的值。 这是为了使项目组(通常情况下设置神经网络模拟)的操作更方便。
该词典包含一个名为events的条目,用于保存记录的数据。 它本身就是一个字典,其中的条目V_m和时间分别存储在Vms和ts中,分别存储在第二行和第三行中。 如果您无法想象字典词典以及您从哪里提取的内容,请首先尝试将dmm打印到屏幕上,以便更好地了解其结构,然后在下一步中提取字典事件等。
现在我们准备在一个图中显示数据。 为此,我们使用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()
在这里,我们通过使用GetStatus的可选关键字参数键更简洁地提取事件。 这会使用关键事件而不是整个状态字典来提取字典元素。 输出结果应该如图2所示。如果要将其作为脚本执行,只需将所有行粘贴到名为one-neuron.py的文本文件中即可。 然后,您可以通过在python前加上文件名,或者在Python或ipython提示符前加上run前缀来从命令行运行它。
可以在单个万用表上收集多个神经元的信息。 这确实使检索信息复杂化:n个神经元中的每一个神经元的数据将以交错方式存储和返回。 幸运的是,Python为我们提供了一个方便的数组操作来轻松分割数据:使用一个步骤(有时称为步幅)进行数组切片。 为了解释这个,你必须调整前一部分创建的模型。 以新名称保存您的代码,在下一节中您还将使用此代码。 创建一个额外的神经元,背景电流给定不同的值:
neuron2 = nest.Create("iaf_neuron")
nest.SetStatus(neuron2 , {"I_e": 370.0})
现在将这个新创建的神经元连接到万用表上:
nest.Connect(multimeter, neuron2)
运行模拟并绘制结果,它们看起来不正确。 要解决这个问题,你必须单独绘制两条神经元轨迹。 用以下行替换从万用表中提取事件的代码。
pylab.figure(2)
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)
connecting nodes with specific connections
一种常用的神经活动模型是泊松过程。 我们现在修改前面的例子,以便神经元接收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的抑制事件。 突触权重可以在字典中定义,该字典使用关键字syn_spec(突触规范)传递给Connect函数。 一般来说,可以在突触词典中指定确定突触的所有参数,例如“重量”,“延迟”,突触模型(“模型”)和特定于突触模型的参数。
syn_dict_ex = {"weight": 1.2}
syn_dict_in = {"weight": -2.0}
nest.Connect([noise[0]], neuron, syn_spec=syn_dict_ex)
nest.Connect([noise[1]], neuron, syn_spec=syn_dict_in)
剩下的代码仍然像以前一样。 你应该看到如图3所示的膜电位。
在引言的下一部分(第2部分:神经元群体)中,我们将看到更多的方法来一次连接许多神经元。
two connected neurons
连接神经元没有额外的魔法。 为了证明这一点,我们从一个具有恒定输入电流的神经元的原始示例开始,并添加第二个神经元。
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"]})
我们现在将神经元1连接到神经元2,并记录来自神经元2的膜电位,以便我们可以观察由神经元1的尖峰引起的突触后电位。
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})
如果您像以前一样模拟网络并绘制膜电位,您应该可以看到由神经元1的尖峰引起的神经元2的突触后电位,如图4所示。
command overview
这些是我们在这份讲义中为例子介绍的功能; 本介绍的以下部分将增加更多内容。
Getting information about NEST
Models(mtype="all", sel=None)
:
返回所有可用模型(节点和突触)的列表。 使用mtype =“节点”仅查看节点模型,mtype =“突触”仅查看突触模型。 sel可以是一个字符串,用于过滤结果列表并只返回包含它的模型。helpdesk(browser="firefox")
:
在给定的浏览器中打开NEST文档页面。help(obj=None,pager="less")
:
打开给定对象的帮助页面。
Nodes
Create(model, n=1, params=None)
:
在当前的子网络中创建n个类型模型的实例。 新节点的参数可以以参数形式给出(单个字典或大小为n的字典列表)。 如果省略,则使用模型的默认值。GetStatus(nodes, keys=None)
:
返回给定节点列表的参数字典列表。 如果给出键,则会返回值列表。 键也可以是一个列表,在这种情况下返回的列表包含值列表。SetStatus(nodes, params, val=None)
:
将给定节点的参数设置为参数,该参数可以是单个字典,也可以是与节点大小相同的字典列表。 如果给定val,则params必须是属性的名称,该属性在节点上设置为val。 val可以是单个值,也可以是与节点大小相同的列表。
Connections
这是Connect函数文档的缩写版本,请参阅NEST的完整版和连接管理的在线帮助以获取简介和工作示例。
连接(pre,post,conn_spec = None,syn_spec = None,model =无):
将pre神经元连接到post神经元。pre和post中的神经使用指定的连接(默认为“one_to_one”)和突触类型(默认为“static_synapse”)进行连接。 细节取决于连接规则。 注意:Connect不会遍历子网,它只会连接显式指定的节点。 突触前神经元,以GID列表的形式给出 - 突触后神经元,以GID列表形式给出conn_spec - 指定连接规则的名称或字典,见下文syn_spec - 指定突触的名称或字典,参见下文
连接
连接可以指定为包含连接规则名称(默认值:“one_to_one”)的字符串,也可以指定规则和规则特定参数(例如“indegree”)的字典,必须给出连接规则。 此外,字典中还可以包含允许自连接(“autapses”,默认值:True)和一对神经元(“multapses”,默认值:True)之间的多个连接的开关。
突触
可以将突触模型及其属性插入为描述一个突触模型的字符串(突触模型在synapsedict中列出)或作为字典插入,如下所述。如果没有指定突触模型,将使用默认模型“static_synapse”。突触字典中的可用键是“模型”,“体重”,“延迟”,“受体类型”和特定于所选突触模型的参数。所有参数都是可选的,如果未指定,将使用当前突触模型确定的默认值。 “模型”确定突触类型,取自NEST中的预定义突触类型或通过CopyModel()手动创建的突触。所有其他参数可以是标量或分布。在标量参数的情况下,除了必须用整数初始化的“receptor_type”之外,所有的键都会加倍。分布式参数用指定分布(“分布”,如“正常”)和分布特定参数(如“mu”和“sigma”)的另一个字典进行初始化。
模拟控制
模拟(T):
模拟网络t毫秒。