【Python & Brain】SNN脉冲神经网络开源模拟器 The Brian Simulator 仿真指南

本文档是Brian Simulator的SNN(脉冲神经网络)仿真教程,包括神经元模型、突触模型和模拟管理的详细介绍。教程通过Python代码展示了如何定义神经元模型,如添加脉冲、耐火性、随机性以及多个神经元的行为。同时,讲解了突触的最简单模型、权重、延迟、复杂连接和STDP(峰时依赖可塑性)机制。此外,还涵盖了模拟管理,如多次运行、运行时内容更改和添加输入。通过实例,深入浅出地探讨了Brian Simulator在神经网络仿真中的应用。
摘要由CSDN通过智能技术生成

前言

最近一直在看相关的内容,在官网的链接 :
Introduction to Brian part 1: Neurons
Introduction to Brian part 2: Synapses
Introduction to Brian part 3: Simulations
看网上也没有相关的内容,主要做了一些翻译和汉化的工作,时间也比较仓促,如果之后有时间的话再校正,如果不太通顺的地方敬请谅解,大体意思就是那样,有些专业名词可能我不是搞脑神经的,也不知道如何翻译合适,下面是具体内容。

第1部分介绍:神经元

所有 Brian 脚本都从以下开头。如果正在Jupyter笔记本中试用此笔记本,则应从运行此单元格开始。

from brian2 import *

稍后,我们将在Jupyter笔记本中进行一些绘图,通过执行此操作激活笔记本中的内联绘图:

%matplotlib inline

如果您没有使用 Jupyter 笔记本来运行此示例(例如,您使用的是标准 Python 终端,或者您将这些示例复制并粘贴到编辑器中并作为脚本运行),则不会自动显示绘图。在这种情况下,在绘图命令之后明确调用命令show()

单位系统

Brian 有一个用于使用物理尺寸数量的系统:

20*volt

20.0   V 20.0\,\mathrm{V} 20.0V

所有基本的 SI 单元都可以使用(伏特、放大器等)以及所有标准前缀(m=milli、pé pico 等),以及一些特殊缩写,如、等。mV 毫伏特, pF皮法等。

1000*amp

1.0   k   A 1.0\,\mathrm{k}\,\mathrm{A} 1.0kA

1e6*volt

1.0   M   V 1.0\,\mathrm{M}\,\mathrm{V} 1.0MV

1000*namp

1.0000000000000002   μ   A 1.0000000000000002\,\mathrm{\mu}\,\mathrm{A} 1.0000000000000002μA

另请注意,单位与的组合:

10*nA*5*Mohm

49.99999999999999   m   V 49.99999999999999\,\mathrm{m}\,\mathrm{V} 49.99999999999999mV

如果你试图相加电流和电压, 则会报错:

5*amp+10*volt
---------------------------------------------------------------------------

DimensionMismatchError                    Traceback (most recent call last)

<ipython-input-8-245c0c0332d1> in <module>
----> 1 5*amp+10*volt


~/programming/brian2/brian2/units/fundamentalunits.py in __add__(self, other)
   1429 
   1430     def __add__(self, other):
-> 1431         return self._binary_operation(other, operator.add,
   1432                                       fail_for_mismatch=True,
   1433                                       operator_str='+')


~/programming/brian2/brian2/units/fundamentalunits.py in _binary_operation(self, other, operation, dim_operation, fail_for_mismatch, operator_str, inplace)
   1369                 message = ('Cannot calculate {value1} %s {value2}, units do not '
   1370                            'match') % operator_str
-> 1371                 _, other_dim = fail_for_dimension_mismatch(self, other, message,
   1372                                                            value1=self,
   1373                                                            value2=other)


~/programming/brian2/brian2/units/fundamentalunits.py in fail_for_dimension_mismatch(obj1, obj2, error_message, **error_quantities)
    184             raise DimensionMismatchError(error_message, dim1)
    185         else:
--> 186             raise DimensionMismatchError(error_message, dim1, dim2)
    187     else:
    188         return dim1, dim2


DimensionMismatchError: Cannot calculate 5. A + 10. V, units do not match (units are A and V).

应该从底部开始,向上工作。最后一行给出错误类型以及更具体的消息DimensionMismatchError (在这种情况下,您尝试将两个数量与不同的 SI 单位加起来,这是不可能的)。

简单的模型

让我们从定义一个简单的神经元模型开始。在 Brian 中,所有模型都由微分方程系统定义。下面是一个简单的例子,它看起来像什么:

tau = 10*ms
eqs = '''
dv/dt = (1-v)/tau : 1
'''

在 Python 中,符号'''用于开始和结束多行字符串。因此,方程只是一个字符串,每个方程有一行。方程格式采用标准数学符号,并添加一个。在行的末尾,您写: unit到该变量的SI单位unit在哪里。请注意,这不是方程的两侧单位(这将是 1/second),而是方程所定义的变量的单位,即在这种情况下 v v v
现在,让我们使用这个定义来创建神经元。

G = NeuronGroup(1, eqs)

在Brian,你只创建神经元组,使用类NeuronGroup。创建这些对象中的一个时,前两个参数是神经元的数量(在此例中为 1)和定义的微分方程。NeuronGroup

让我们看看如果我们没有将变量 tau 放在等式中会发生什么:

eqs = '''
dv/dt = 1-v : 1
'''
G = NeuronGroup(1, eqs)
run(100*ms)
---------------------------------------------------------------------------

DimensionMismatchError                    Traceback (most recent call last)

~/programming/brian2/brian2/equations/equations.py in check_units(self, group, run_namespace)
    955                 try:
--> 956                     check_dimensions(str(eq.expr), self.dimensions[var] / second.dim,
    957                                      all_variables)


~/programming/brian2/brian2/equations/unitcheck.py in check_dimensions(expression, dimensions, variables)
     44                                                   expected=repr(get_unit(dimensions)))
---> 45     fail_for_dimension_mismatch(expr_dims, dimensions, err_msg)
     46 


~/programming/brian2/brian2/units/fundamentalunits.py in fail_for_dimension_mismatch(obj1, obj2, error_message, **error_quantities)
    183         if obj2 is None or isinstance(obj2, (Dimension, Unit)):
--> 184             raise DimensionMismatchError(error_message, dim1)
    185         else:


DimensionMismatchError: Expression 1-v does not have the expected unit hertz (unit is 1).


During handling of the above exception, another exception occurred:

DimensionMismatchError                    Traceback (most recent call last)

~/programming/brian2/brian2/core/network.py in before_run(self, run_namespace)
    897                 try:
--> 898                     obj.before_run(run_namespace)
    899                 except Exception as ex:


~/programming/brian2/brian2/groups/neurongroup.py in before_run(self, run_namespace)
    883         # Check units
--> 884         self.equations.check_units(self, run_namespace=run_namespace)
    885         # Check that subexpressions that refer to stateful functions are labeled


~/programming/brian2/brian2/equations/equations.py in check_units(self, group, run_namespace)
    958                 except DimensionMismatchError as ex:
--> 959                     raise DimensionMismatchError(('Inconsistent units in '
    960                                                   'differential equation '


DimensionMismatchError: Inconsistent units in differential equation defining variable v:
Expression 1-v does not have the expected unit hertz (unit is 1).


During handling of the above exception, another exception occurred:

BrianObjectException                      Traceback (most recent call last)

<ipython-input-11-97ed109f5888> in <module>
      3 '''
      4 G = NeuronGroup(1, eqs)
----> 5 run(100*ms)


~/programming/brian2/brian2/units/fundamentalunits.py in new_f(*args, **kwds)
   2383                                                      get_dimensions(newkeyset[k]))
   2384 
-> 2385             result = f(*args, **kwds)
   2386             if 'result' in au:
   2387                 if au['result'] == bool:


~/programming/brian2/brian2/core/magic.py in run(duration, report, report_period, namespace, profile, level)
    371         intended use. See `MagicNetwork` for more details.
    372     '''
--> 373     return magic_network.run(duration, report=report, report_period=report_period,
    374                              namespace=namespace, profile=profile, level=2+level)
    375 run.__module__ = __name__


~/programming/brian2/brian2/core/magic.py in run(self, duration, report, report_period, namespace, profile, level)
    229             namespace=None, profile=False, level=0):
    230         self._update_magic_objects(level=level+1)
--> 231         Network.run(self, duration, report=report, report_period=report_period,
    232                     namespace=namespace, profile=profile, level=level+1)
    233 


~/programming/brian2/brian2/core/base.py in device_override_decorated_function(*args, **kwds)
    274                 return getattr(curdev, name)(*args, **kwds)
    275             else:
--> 276                 return func(*args, **kwds)
    277 
    278         device_override_decorated_function.__doc__ = func.__doc__


~/programming/brian2/brian2/units/fundamentalunits.py in new_f(*args, **kwds)
   2383                                                      get_dimensions(newkeyset[k]))
   2384 
-> 2385             result = f(*args, **kwds)
   2386             if 'result' in au:
   2387                 if au['result'] == bool:


~/programming/brian2/brian2/core/network.py in run(self, duration, report, report_period, namespace, profile, level)
   1007             namespace = get_local_namespace(level=level+3)
   1008 
-> 1009         self.before_run(namespace)
   1010 
   1011         if len(all_objects) == 0:


~/programming/brian2/brian2/core/base.py in device_override_decorated_function(*args, **kwds)
    274                 return getattr(curdev, name)(*args, **kwds)
    275             else:
--> 276                 return func(*args, **kwds)
    277 
    278         device_override_decorated_function.__doc__ = func.__doc__


~/programming/brian2/brian2/core/network.py in before_run(self, run_namespace)
    898                     obj.before_run(run_namespace)
    899                 except Exception as ex:
--> 900                     raise brian_object_exception("An error occurred when preparing an object.", obj, ex)
    901 
    902         # Check that no object has been run as part of another network before


BrianObjectException: Original error and traceback:
Traceback (most recent call last):
  File "/home/marcel/programming/brian2/brian2/equations/equations.py", line 956, in check_units
    check_dimensions(str(eq.expr), self.dimensions[var] / second.dim,
  File "/home/marcel/programming/brian2/brian2/equations/unitcheck.py", line 45, in check_dimensions
    fail_for_dimension_mismatch(expr_dims, dimensions, err_msg)
  File "/home/marcel/programming/brian2/brian2/units/fundamentalunits.py", line 184, in fail_for_dimension_mismatch
    raise DimensionMismatchError(error_message, dim1)
brian2.units.fundamentalunits.DimensionMismatchError: Expression 1-v does not have the expected unit hertz (unit is 1).

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/marcel/programming/brian2/brian2/core/network.py", line 898, in before_run
    obj.before_run(run_namespace)
  File "/home/marcel/programming/brian2/brian2/groups/neurongroup.py", line 884, in before_run
    self.equations.check_units(self, run_namespace=run_namespace)
  File "/home/marcel/programming/brian2/brian2/equations/equations.py", line 959, in check_units
    raise DimensionMismatchError(('Inconsistent units in '
brian2.units.fundamentalunits.DimensionMismatchError: Inconsistent units in differential equation defining variable v:
Expression 1-v does not have the expected unit hertz (unit is 1).

Error encountered with object named "neurongroup_1".
Object was created here (most recent call only, full details in debug log):
  File "<ipython-input-11-97ed109f5888>", line 4, in <module>
    G = NeuronGroup(1, eqs)

An error occurred when preparing an object. brian2.units.fundamentalunits.DimensionMismatchError: Inconsistent units in differential equation defining variable v:
Expression 1-v does not have the expected unit hertz (unit is 1).
(See above for original error message and traceback.)

提出了错误,但为什么?原因是微分方程现在在维度上不一致。左侧有单位 dv/dt,但右侧1-v是无维度的。人们经常觉得布赖恩的这种行为令人困惑,因为这种等式在数学中很常见。但是,对于具有物理尺寸的数量,这是不正确的,因为结果会根据您测量的单位而改变。对于时间,如果您在几秒钟内测量它,则相同的方程行为与以毫秒计量时间的方式不同。为了避免这种情况,我们坚持要求您始终指定维度一致的方程。

现在,让我们回到良好的方程,并实际运行模拟。

start_scope()

tau = 10*ms
eqs = '''
dv/dt = (1-v)/tau : 1
'''

G = NeuronGroup(1, eqs)
run(100*ms)
INFO       No numerical integration method specified for group 'neurongroup', using method 'exact' (took 0.02s). [brian2.stateupdaters.base.method_choice]

首先,忽略在细胞的顶部的start_scope() 。在这个教程中的每个单元格中,都会看到我们运行模拟。它所做的只是确保在功能被调用之前创建的任何 Brian 对象不包括在模拟的下一个运行中。

其次,您会看到有一个关于不指定数字集成方法的"INFO"消息。这是无害的,只是为了让你知道我们选择了什么方法,但我们会通过明确指定方法来修复它在下一个单元格。

那么,这里发生了什么?那么,命令运行模拟100毫秒run(100*ms) 。我们可以看到,这通过在模拟前后打印变量值v而起作用。

start_scope()

G = NeuronGroup(1, eqs, method='exact')
print('Before v = %s' % G.v[0])
run(100*ms)
print('After v = %s' % G.v[0])
Before v = 0.0
After v = 0.9999546000702376

默认情况下,所有变量都以值 0 开头。由于微分方程是dv/dt=(1-v)/tau,我们预计一段时间后, v 将倾向于价值1,这正是我们所看到的。具体来说,我们期望v有这个价值1-exp(-t/tau)。让我们看看是否正确。

print('Expected value of v = %s' % (1-exp(-100*ms/tau)))
Expected value of v = 0.9999546000702375

好消息是,模拟提供了我们所期望的价值!

现在,让我们来看看变量v如何随时间演变的图表。v

start_scope()

G = NeuronGroup(1, eqs, method='exact')
M = StateMonitor(G, 'v', record=True)

run(30*ms)

plot(M.t/ms, M.v[0])
xlabel('Time (ms)')
ylabel('v');


在这里插入图片描述

这一次,我们只运行了30毫秒的模拟,以便我们可以看到更好的行为。看起来它的行为似乎如预期的那样, 但让我们通过在上面绘制预期的行为来分析性地检查一下。

start_scope()

G = NeuronGroup(1, eqs, method='exact')
M = StateMonitor(G, 'v', record=0)

run(30*ms)

plot(M.t/ms, M.v[0], 'C0', label='Brian')
plot(M.t/ms, 1-exp(-M.t/tau), 'C1--',label='Analytic')
xlabel('Time (ms)')
ylabel('v')
legend();


在这里插入图片描述

如您所见,蓝色(Brain)和破折号橙色(分析解决方案)线重合。

在此示例中,我们使用了 StateMonitor 对象。这用于在模拟运行时记录神经元变量的值。前两个参数是要记录的组,以及要记录的变量。我们还指定record=0。这意味着我们记录神经元 0 的所有值。我们必须指定哪些神经元,我们希望记录,因为在大型模拟与许多神经元,它通常用了太多的RAM来记录所有神经元的值。StateMonitorrecord=0

现在尝试修改方程和参数,看看下面的单元格中会发生什么。

start_scope()

tau = 10*ms
eqs = '''
dv/dt = (sin(2*pi*100*Hz*t)-v)/tau : 1
'''

# Change to Euler method because exact integrator doesn't work here
G = NeuronGroup(1, eqs, method='euler')
M = StateMonitor(G, 'v', record=0)

G.v = 5 # initial value

run(60*ms)

plot(M.t/ms, M.v[0])
xlabel('Time (ms)')
ylabel('v');


在这里插入图片描述

添加脉冲

到目前为止,我们还没有做任何神经元,只是玩弄了微分方程。现在,让我们开始添加尖刺。

start_scope()

tau = 10*ms
eqs = '''
dv/dt = (1-v)/tau : 1
'''

G = NeuronGroup(1, eqs, threshold='v>0.8', reset='v = 0', method='exact')

M = StateMonitor(G, 'v', record=0)
run(50*ms)
plot(M.t/ms, M.v[0])
xlabel('Time (ms)')
ylabel('v');


在这里插入图片描述

我们在NeuronGroup 中增加了两个新关键词:threshold='v>0.8'reset='v = 0'这意味着,当我们发射一个尖峰,并在峰值v>0.8后立即重置v = 0。我们可以把任何表达和一系列的语句作为这些字符串。

正如你所看到的,在开始时,行为与以前相同,直到v 越过阈值v>0.8,此时您看到它重置为0。你不能在这个图中看到它, 但内部布赖恩已经注册了这个事件作为一个高峰。让我们来看看。

start_scope()

G = NeuronGroup(1, eqs, threshold='v>0.8', reset='v = 0', method='exact')

spikemon = SpikeMonitor(G)

run(50*ms)

print('Spike times: %s' % spikemon.t[:])
Spike times: [16.  32.1 48.2] ms

对象SpikeMonitor以您想要记录的峰值组作为参数,并将峰值时间存储在变量t中。让我们在另一个图形上绘制这些尖峰, 看看它是正确的。

start_scope()

G = NeuronGroup(1, eqs, threshold='v>0.8', reset='v = 0', method='exact')

statemon = StateMonitor(G, 'v', record=0)
spikemon = SpikeMonitor(G)

run(50*ms)

plot(statemon.t/ms, statemon.v[0])
for t in spikemon.t:
    axvline(t/ms, ls='--', c='C1', lw=3)
xlabel('Time (ms)')
ylabel('v');


在这里插入图片描述

在这里,我们已经使用matplotlib 命令axvline绘制一个橙色的,虚线的垂直线时,每个尖峰记录的SpikeMonitor

现在尝试更改上面的单元格中的字符串thresholdreset ,看看会发生什么。

耐火性(不应期)

神经元模型的一个共同特点是耐火性。这意味着,在神经元发射尖峰后,它会在一定持续时间内变得耐火,在此期间结束之前不能再发射一个尖峰。以下是我们在布莱恩的做事。

start_scope()

tau = 10*ms
eqs = '''
dv/dt = (1-v)/tau : 1 (unless refractory)
'''

G = NeuronGroup(1, eqs, threshold='v>0.8', reset='v = 0', refractory=5*ms, method='exact')

statemon = StateMonitor(G, 'v', record=0)
spikemon = SpikeMonitor(G)

run(50*ms)

plot(statemon.t/ms, statemon.v[0])
for t in spikemon.t:
    axvline(t/ms, ls='--', c='C1', lw=3)
xlabel('Time (ms)')
ylabel('v');


在这里插入图片描述

正如你可以看到在这个数字,在第一个峰值后,v 保持在0约5毫秒之前,它恢复正常的行为。为此,我们做了两件事。首先,我们在声明NeuronGroup中添加了关键字refractory=5*ms。就其本身,这仅意味着神经元不能在这一时期激增(见下文),但不会改变v 行为方式。为了使耐火期保持不变,我们必须在微分方程中添加(unless refractory) 在定义v的末尾。这意味着微分方程决定了除非其耐火性,否则v被关闭的行为。

下面是会发生什么,如果我们不包括 (unless refractory)。请注意,我们还降低了耐火期的值tau 并延长了耐火期的长度,使行为更加清晰。

start_scope()

tau = 5*ms
eqs = '''
dv/dt = (1-v)/tau : 1
'''

G = NeuronGroup(1, eqs, threshold='v>0.8', reset='v = 0', refractory=15*ms, method='exact')

statemon = StateMonitor(G, 'v', record=0)
spikemon = SpikeMonitor(G)

run(50*ms)

plot(statemon.t/ms, statemon.v[0])
for t in spikemon.t:
    axvline(t/ms, ls='--', c='C1', lw=3)
axhline(0.8, ls=':', c='C2', lw=3)
xlabel('Time (ms)')
ylabel('v')
print("Spike times: %s" % spikemon.t[:])
Spike times: [ 8. 23. 38.] ms

在这里插入图片描述

发生什么事了?第一个峰值的行为是相同的:v 上升到0.8,然后神经元在8毫秒时发射一个峰值,然后立即重置为0。由于耐火期现在是15毫秒,这意味着神经元将无法再次尖峰,直到时间8+15=23毫秒。紧接着第一个峰值v,现在的价值立即开始上升,因为我们没有在定义dv/dt中指定(unless refractory) 。然而,一旦它达到值0.8(虚线绿线)的时间大约8毫秒,它不会发射尖峰,即使阈值是v>0.8。这是因为神经元仍然耐火,直到时间23毫秒,在这一点上,它发射了一个尖峰。

请注意,您可以做更复杂和有趣的事情与耐火材料。有关其工作原理的更多详细信息,请参阅完整文档。

多个神经元

到目前为止,我们只与一个神经元工作。让我们用多个神经元做一些有趣的事情。

start_scope()

N = 100
tau = 10*ms
eqs = '''
dv/dt = (2-v)/tau : 1
'''</
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值