脉冲神经网络(SNN)基础
概述
脉冲神经网络(Spiking Neural Network, SNN)是第三代神经网络,其核心特点是使用离散的**脉冲序列(Spike Train)**进行信息传递。与传统ANN相比,SNN更接近真实生物神经元的工作方式,具有时间动力学特性。1
┌─────────────────────────────────────────────────────────────┐
│ 神经网络进化历程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 第一代:阈值神经元 (McCulloch-Pitts, 1943) │
│ └─→ 输出:0/1 二值信号 │
│ │
│ 第二代:连续激活神经网络 (1980s-至今) │
│ └─→ 输出:sigmoid, ReLU 等连续值 │
│ │
│ 第三代:脉冲神经网络 (1990s-至今) │
│ └─→ 输出:离散脉冲序列,时间动力学 │
│ │
└─────────────────────────────────────────────────────────────┘
1. 脉冲神经元的生物学背景
1.1 生物神经元结构
轴突末梢
↓
┌─────────────────────────────────────┐
│ 轴突 (Axon) │
│ ════════════════════════════════→ │
└─────────────────────────────────────┘
↑
轴突丘
(Axon Hillock)
│
┌──────────────────┼──────────────────┐
│ │ │
│ 树突 │ 树突 │
│ (Dendrites) │ (Dendrites) │
│ ↘ │ ↙ │
│ ╲ ● ╱ │
│ ╲ 细胞体 ╱ │
│ ╲ (Soma) ╱ │
│ ●────● │
│ /│╲ │╲ │
│ / │ ╲ │ ╲ │
│ / │ ╲ │ ╲ │
│ ←输入 →输入 ←输入 │
│ │
│ 突触前 突触 突触后 │
└─────────────────────────────────────┘
1.2 动作电位
神经元通过**动作电位(Action Potential)或脉冲(Spike)**进行通信:
- 膜电位迅速升高(去极化)
- 达到阈值后触发脉冲
- 脉冲宽度约1-2ms,幅度约100mV
- 脉冲后进入不应期(refracotry period)
2. 脉冲神经元模型
2.1 IF (Integrate-and-Fire) 模型
IF模型是最简单的脉冲神经元模型:
状态方程:
或离散形式:
脉冲发放规则:
重置机制:
# IF神经元实现
def IF_neuron(V, I, dt, tau, V_th, V_reset):
"""
Integrate-and-Fire神经元
V: 当前膜电位
I: 输入电流
dt: 时间步长
tau: 时间常数
V_th: 阈值
V_reset: 重置电位
"""
V = V + (dt / tau) * I # 积分
spike = 1 if V >= V_th else 0 # 脉冲检测
V = V_reset if spike else V # 重置
return V, spike2.2 LIF (Leaky Integrate-and-Fire) 模型
LIF模型引入”泄漏”机制,更接近真实神经元:
连续形式(微分方程):
其中:
- :膜时间常数
- :静息电位
- :膜电阻
- :输入电流
等效形式:
参数说明:
| 参数 | 典型值 | 说明 |
|---|---|---|
| -70 mV | 静息电位 | |
| -55 mV | 发放阈值 | |
| -70 mV | 重置电位 | |
| 10-20 ms | 膜时间常数 |
import torch
import torch.nn as nn
class LIF Neuron(nn.Module):
"""Leaky Integrate-and-Fire神经元"""
def __init__(self, tau=10.0, V_th=1.0, V_reset=0.0):
super().__init__()
self.tau = tau
self.V_th = V_th
self.V_reset = V_reset
def forward(self, V, I, dt=1.0):
"""
V: 膜电位 (batch, ..., n_neurons)
I: 输入电流
"""
# 膜电位更新:dV/dt = -(V - V_rest)/tau + I/tau
dV = (-(V - self.V_reset) + I) / self.tau
V_new = V + dV * dt
# 脉冲检测
spike = (V_new >= self.V_th).float()
# 重置
V_new = torch.where(spike > 0, self.V_reset, V_new)
return V_new, spike2.3 Hodgkin-Huxley (HH) 模型
HH模型是最精确的生物物理模型,描述了乌贼巨型轴突的电生理特性(获1963年诺贝尔奖)。
电路类比:
Cm
───┬───→ V ───┬───→ V
│ │
╱╲ ╱╲
╱ ╲ ╱ ╲
╱ ╲ ╱ ╲
→I G_K→ G_Na→
→ → →
│ │ │
└──────┴──────┘
G_L
动力学方程:
门控变量(非线性):
2.4 Izhikevich模型
Izhikevich模型在HH模型的精度和LIF模型的效率之间取得平衡:
方程:
重置:
| 参数 | 范围 | 含义 |
|---|---|---|
| 0.01-0.02 | 恢复变量时间尺度 | |
| 0.1-0.2 | 恢复变量对V的敏感性 | |
| -65 mV | 脉冲后重置值 | |
| 2-8 | 恢复变量更新量 |
def izhikevich_step(v, u, I, a=0.02, b=0.2, c=-65, d=2):
"""Izhikevich神经元单步更新"""
dv = 0.04*v*v + 5*v + 140 - u + I
v_new = v + dv
du = a * (b * v - u)
u_new = u + du
# 脉冲检测
if v_new >= 30:
v_new = c
u_new = u_new + d
spike = 1
else:
spike = 0
return v_new, u_new, spike2.5 模型对比
| 模型 | 微分方程 | 参数数量 | 计算复杂度 | 精度 |
|---|---|---|---|---|
| IF | 线性 | 2-3 | ⭐ | 低 |
| LIF | 线性 | 3-4 | ⭐⭐ | 中 |
| Izhikevich | 二次 | 4 | ⭐⭐⭐ | 高 |
| HH | 非线性 | 8+ | ⭐⭐⭐⭐⭐ | 最高 |
3. 信息编码方式
SNN利用脉冲的时间结构来编码信息,主要有以下几种编码方式:
3.1 Rate Coding (频率编码)
最直观的编码方式:脉冲发放频率代表信息强度。
其中 为时间窗口。
时间窗口 T
├── 频率低: ○ ○ ○ → 信息值小
├── 频率中: ○ ○ ○ ○ → 信息值中
└── 频率高: ○○○○○○○○○○○○○○○ → 信息值大
优点:直观、理论成熟
缺点:需要较长平均时间,丢失精确时间信息
3.2 Temporal Coding (时间编码)
利用脉冲的精确时间编码信息。
Latency Coding:
输入强度越大,脉冲发放越早。
输入x₁=0.9 (强): ●→ (早期发放)
输入x₂=0.5 (中): ●→ (中期发放)
输入x₃=0.1 (弱): ●→ (晚期发放)
Phase Coding:
3.3 Population Coding (群体编码)
多个神经元协同编码一个变量:
其中 为第 个神经元的权重, 为其发放率。
偏好方向
↗
↗ ↗
↗ ●●● ↗ ← 群体响应
↗ ●●●●●● ↗
↗ ●●●●●●●● ↗
←───────────→
刺激方向
3.4 编码方式对比
| 编码方式 | 信息容量 | 抗噪声能力 | 计算效率 |
|---|---|---|---|
| Rate Coding | 低 | 高 | 中 |
| Temporal Coding | 高 | 低 | 高 |
| Population Coding | 中 | 中 | 低 |
| 混合编码 | 高 | 高 | 中 |
4. 膜电位动力学深入分析
4.1 时间常数的影响
- 大:膜电位变化慢,响应迟缓但平滑
- 小:膜电位变化快,响应迅速但易波动
# 不同时间常数的效果
def simulate_lif_different_tau():
"""
模拟不同时间常数下LIF神经元的响应
"""
results = {}
for tau in [5, 10, 20]: # ms
V_trace = []
V = V_reset
for t in range(100):
I = 1.5 if 10 <= t <= 50 else 0 # 脉冲输入
dV = (-(V - V_reset) + I) / tau
V = V + dV
if V >= V_th:
V = V_reset
spike = 1
else:
spike = 0
V_trace.append(V)
results[tau] = V_trace
return results4.2 共模抑制与选择性
神经元对特定频率或强度的输入具有选择性:
4.3 不应期机制
脉冲发放后,神经元进入一段不应期(Refractory Period):
class LIFWithRefractory(nn.Module):
"""带不应期的LIF神经元"""
def __init__(self, tau=10.0, V_th=1.0, V_reset=0.0,
tau_ref=2.0, dt=1.0):
super().__init__()
self.tau = tau
self.V_th = V_th
self.V_reset = V_reset
self.tau_ref = tau_ref # 不应期时间
self.dt = dt
def forward(self, V, I, refractory_remaining):
# 不应期内不响应
active_mask = (refractory_remaining <= 0).float()
# 膜电位更新(仅在非不应期)
dV = (-(V - self.V_reset) + I) / self.tau
V_new = V + dV * self.dt
# 脉冲检测
spike = ((V_new >= self.V_th) & (refractory_remaining <= 0)).float()
# 重置与不应期更新
V_new = torch.where(spike > 0, self.V_reset, V_new)
refractory_new = torch.where(
spike > 0,
self.tau_ref,
torch.clamp(refractory_remaining - self.dt, min=0)
)
return V_new, spike, refractory_new5. 网络层面的动力学
5.1 突触模型
电流型突触:
电导型突触(更精确):
5.2 网络连接模式
全连接 局部连接 前馈
┌───┬───┐ ┌───┬───┐ ┌───┐
│ ● │ ● │ │ ● │ │ │ ● │
├───┼───┤ ├───┼───┤ ├───┤
│ ● │ ● │ │ ● │ ● │ → │ ● │ →
├───┼───┤ ├───┼───┤ ├───┤
│ ● │ ● │ │ │ ● │ │ ● │
└───┴───┘ └───┴───┘ └───┘
5.3 动态行为
SNN网络可以表现出丰富的动态行为:
- 同步振荡:神经元群同步发放
- 行波:激活模式在网络中传播
- 全局吸引子:网络收敛到稳定状态
6. 与ANN的对比
| 特性 | ANN | SNN |
|---|---|---|
| 信息载体 | 连续值 | 离散脉冲 |
| 时间动力学 | 无 | 有 |
| 计算基元 | MAC操作 | 事件驱动 |
| 能效 | 低(持续计算) | 高(稀疏激活) |
| 生物可信度 | 低 | 高 |
| 训练难度 | 成熟 | 仍在发展中 |
7. 代码实践:完整LIF神经元
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
class LeakyIntegrateFire(nn.Module):
"""
完整的LIF神经元实现
支持批处理和多种参数配置
"""
def __init__(
self,
tau_mem=10.0, # 膜时间常数 (ms)
tau_syn=5.0, # 突触时间常数 (ms)
V_th=1.0, # 发放阈值
V_reset=0.0, # 重置电位
V_rest=0.0, # 静息电位
dt=1.0, # 仿真时间步长
surrogate_grad='arctan', # 代理梯度类型
alpha=1.0 # 代理梯度参数
):
super().__init__()
self.tau_mem = tau_mem
self.tau_syn = tau_syn
self.V_th = V_th
self.V_reset = V_reset
self.V_rest = V_rest
self.dt = dt
self.surrogate_grad = surrogate_grad
self.alpha = alpha
# 衰减因子
self.beta_mem = torch.exp(torch.tensor(-dt / tau_mem))
self.beta_syn = torch.exp(torch.tensor(-dt / tau_syn))
def membrane_dynamics(self, V, I_syn):
"""
膜电位动力学: dV/dt = -(V - V_rest)/tau_mem + I_syn/C
"""
dV = (-(V - self.V_rest) + I_syn) / self.tau_mem
return V + dV * self.dt
def surrogate_gradient(self, V):
"""
代理梯度函数
"""
if self.surrogate_grad == 'arctan':
# arctan代理梯度(常用)
return (self.alpha / torch.pi) / (1 + (self.alpha * (V - self.V_th))**2)
elif self.surrogate_grad == 'sigmoid':
# Sigmoid代理梯度
x = V - self.V_th
return torch.sigmoid(x / self.alpha) * (1 - torch.sigmoid(x / self.alpha))
elif self.surrogate_grad == 'fast_sigmoid':
# 快速Sigmoid
return self.alpha / (self.alpha + torch.abs(V - self.V_th))**2
else:
raise ValueError(f"Unknown surrogate gradient: {self.surrogate_grad}")
def forward(self, V, I_ext):
"""
前向传播
V: 当前膜电位
I_ext: 外部输入电流
"""
# 突触电流更新
I_syn = I_syn * self.beta_syn + I_ext * (1 - self.beta_syn)
# 膜电位更新
V_new = self.membrane_dynamics(V, I_syn)
# 脉冲检测(可微版本用于反向传播)
if self.training:
# 训练时使用代理梯度
spike_prob = torch.sigmoid((V_new - self.V_th) * self.alpha)
spike = self.surrogate_gradient(V_new) * (V_new - self.V_th)
else:
# 推理时使用硬阈值
spike = (V_new >= self.V_th).float()
# 膜电位重置
V_new = torch.where(
V_new >= self.V_th,
self.V_reset,
V_new
)
return V_new, spike, I_syn
def reset(self, batch_size, device):
"""重置神经元状态"""
return {
'V': torch.full((batch_size,), self.V_reset, device=device),
'I_syn': torch.zeros((batch_size,), device=device),
'spike_count': torch.zeros((batch_size,), device=device)
}
def simulate_neuron_response():
"""模拟神经元对不同输入的响应"""
lif = LeakyIntegrateFire(tau_mem=10.0, V_th=1.0, alpha=2.0)
# 模拟参数
T = 100 # 总时间步
batch_size = 1
device = 'cpu'
state = lif.reset(batch_size, device)
V_history = []
spike_history = []
for t in range(T):
# 输入电流:不同阶段不同强度
if 10 <= t <= 40:
I_ext = 1.5 # 强输入
elif 50 <= t <= 70:
I_ext = 0.8 # 中等输入
else:
I_ext = 0.0 # 无输入
V, spike, I_syn = lif(
state['V'],
torch.tensor([I_ext], device=device)
)
state['V'] = V
state['spike_count'] += spike
V_history.append(V.item())
spike_history.append(spike.item())
# 绘图
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6))
t_axis = range(T)
ax1.plot(t_axis, V_history, 'b-', label='Membrane Potential')
ax1.axhline(y=lif.V_th, color='r', linestyle='--', label='Threshold')
ax1.set_ylabel('Membrane Potential (V)')
ax1.legend()
ax1.grid(True)
ax2.eventplot([i for i, s in enumerate(spike_history) if s > 0],
lineoffsets=0.5, linelengths=0.8, color='red')
ax2.set_ylabel('Spikes')
ax2.set_xlabel('Time (ms)')
ax2.set_ylim(0, 1)
plt.tight_layout()
plt.savefig('lif_response.png')
plt.show()
return V_history, spike_history
if __name__ == '__main__':
simulate_neuron_response()8. 总结
本章介绍了脉冲神经网络的基础知识:
- 神经元模型:从简单的IF模型到复杂的HH模型
- 编码方式:Rate、Temporal、Population等多种编码
- 动力学特性:时间常数、不应期、突触整合
- 实现代码:完整的LIF神经元PyTorch实现
参考
Footnotes
-
Gerstner, W., & Kistler, W. M. (2002). Spiking Neuron Models: Single Neurons, Populations, Plasticity. Cambridge University Press. ↩