先建立一个能用的直觉
ANN 神经元通常对一批输入做一次加权求和,再通过连续激活函数输出一个数。SNN 神经元则保留内部状态:同一输入在不同时间到达,会得到不同结果。
Spike 是某个时间步是否发放的事件,单个 spike 本身只含“发生/未发生”,信息来自一串 spike 的计数、精确时刻、群体模式以及它们与状态的关系。
SNN 不是把 ReLU 粗暴替换为 0/1。关键变化是网络获得了状态、时间和事件触发的计算语义。
它到底解决了什么麻烦
如果输入本身带有时间结构,或硬件能跳过无事件的计算,那么“何时发生”与“是否发生”都可以成为有效信息。
Math · 不背公式,顺着计算读
每张卡先讲人话,再拆符号、走计算顺序,最后代入一组数。读完后请合上说明,自己复算一次。
先把输入按权重加起来,再过激活函数。ANN 神经元在这里做的是一次映射,没有上一时刻的状态参与。
符号分别是谁
- x
- 输入向量,例如两个特征 [0.6, 0.2]
- w
- 与输入一一对应的可学习权重
- b
- 偏置,让决策边界不必经过原点
- φ
- 激活函数,例如 ReLU、GELU 或 sigmoid
按这个顺序计算
- 做点积 wᵀx,也就是把每个输入乘权重后相加。
- 加上偏置 b,得到激活前的 z。
- 把 z 交给 φ,得到本层输出 y。
代入一组数
若 x=[0.6,0.2]、w=[0.5,−1]、b=0.1,则 z=0.6×0.5+0.2×(−1)+0.1=0.2;用 ReLU 后 y=0.2。
落到代码就是
z = x @ w + b
y = torch.relu(z)膜电位 V 只要越过阈值就记一个 spike;没有越过就记 0。注意:这条式子只负责‘判定’,V 如何积累要看神经元动力学。
符号分别是谁
- S[t]
- 第 t 个时间步的输出事件,通常取 0 或 1
- V[t]
- 判定发放前的膜电位
- Vth
- 发放阈值
- H
- Heaviside 阶跃函数:输入≥0 输出1,否则输出0
按这个顺序计算
- 先由历史状态和本步输入算出 V[t]。
- 计算 V[t]−Vth。
- 结果非负则 S[t]=1,否则 S[t]=0;发放后还要执行 reset。
代入一组数
阈值为 1 时,V[3]=0.94 不发放;V[4]=1.08 发放。1.08 不是输出概率,而是触发 spike 前的内部状态。
落到代码就是
spike = (mem >= threshold).to(mem.dtype)在一段观察时间里数 spike,再除以这段时间有多长;单位是 Hz,而不是‘每步几个’。
符号分别是谁
- T
- 离散时间步数量
- Δt
- 一个时间步代表的真实时长,需用秒计算 Hz
- ΣS[t]
- 窗口内 spike 总数
- r
- 平均发放率,单位 s⁻¹,也就是 Hz
按这个顺序计算
- 把 T 个 0/1 spike 相加,得到 spike count。
- 计算观察窗长度 TΔt。
- 用 count 除以窗口长度。
代入一组数
100 个时间步、每步 1 ms,共观察 0.1 s;若出现 4 个 spike,则 r=4/0.1=40 Hz。
落到代码就是
rate_hz = spikes.sum() / (num_steps * dt_seconds)把几个容易混的概念拆开
Spike train
沿离散时间轴排列的 0/1 序列;也可表示成一组发放时刻。
Event-driven
理想事件驱动硬件只在事件到达时更新相关状态;GPU 上按稠密时间步模拟并不自动具备这一优势。
跟着机制一步一步走
不要把生物类比当作一一对应
树突、胞体、轴突帮助我们理解“接收—积累—发放”,但工程 SNN 是数学模型,不是完整脑组织模拟。膜电位是状态变量,spike 是通信事件,突触权重仍是可学习参数。
- ANN:一次前向通常只保留层间激活。
- SNN:每层神经元还保存跨时间步状态。
- 事件驱动:只有后端真的跳过无事件计算时才成立。
一列 spike train 可以测什么?
除了 firing rate,还应观察 spike count、首次发放时间和 inter-spike interval(ISI)。两个序列可能发放率相同,却有完全不同的精确时序。
- Spike count:窗口内事件数量。
- Firing rate:count / observation time。
- ISI:相邻 spike 时间差,可描述节律与 burst。