Neuron model의 종류

!https://github.com/jeshraghian/snntorch/blob/master/docs/_static/img/examples/tutorial2/2_1_neuronmodels.png?raw=true

  • Hodgkin-Huxley 뉴런 모델

    생물물리학적으로 구현된 모델로 전기생리학적(electrophysiological) 결과를 매우 높은 정확도로 재현할 수 있지만 복잡성이 높아서 사용되지 않고 있음

  • 인공 뉴런 모델

    입력 값은 가중치와 곱해진 다음 활성화 함수(Activatoin function)에 전달된다. 오늘날 AI에서 쓰이고 있는 분야다.

  • Leaky Integrate-and-Fire (LIF) 뉴런 모델

    생물물리학 모델과 인공 뉴런 모델 두 모델이 섞인 모델이다. LIF 모델은 입력 값과 가중치의 곱한 값의 합을 계산한다. 이때 인공 뉴런 모델과 달리 시간에 따라 입력을 합하고 누출시킨다. 이때 합한 값이 임계값을 초과하면 전압 스파이크를 발생한다.

    이때 정보는 Spike에 저장되지 않고 Spike가 발생한 타이밍, 빈도에 정보가 저장된다.

SNNTorch LIF model 종류

  • Lapicque’s RC model: snntorch.Lapicque
  • 1st-order model: snntorch.Leaky
  • Synaptic Conductance-based neuron model: snntorch.Synaptic
  • Recurrent 1st-order model: snntorch.RLeaky
  • Recurrent Synaptic Conductance-based neuron model: snntorch.RSynaptic
  • Alpha neuron model: snntorch.Alpha

Leaky Integrate-and-Fire Neuron model

Spiking neuron

우리의 뇌는 하나의 뉴런이 다른 뉴런과 연결되어 있다. 즉 하나의 뉴런이 Spike를 발생 시키면 연결된 뉴런이 흥분 or 억제 등 영향을 받게 된다.

이때 감각은 다음과 같은 곳에서 온다.

Spike는 매우 짧은 전기적 신호이기에 모든 입력 Spike가 동시에 도착하지 않는 경우가 많다. 따라서 neuron은 이러한 시간 차를 기억 or 유지하며 반응한다. 즉 뉴런은 입력된 Spike에 대해 바로 처리하지 않고 지연되면서 반응하는 특성을 가지고 있다.

The Passive membrane

neuron은 얇은 막으로 둘러싸여 있다. 이때 이 막은 절연체로 전도성 용액을 두 개로 분리해서 커패시터 역할을 한다.

또한 이온에 대해 불투과성이라 이온이 들어오거나 나가는 걸 막는다. 그런데 뉴런에 전류가 주입되면 채널의 변화로 인해 이온의 이동이 가능해 진다. 이는 저항 역할을 한다.

!https://github.com/jeshraghian/snntorch/blob/master/docs/_static/img/examples/tutorial2/2_3_passivemembrane.png?raw=true


LIF neuron model 유도

임의의 시간에 따라 $I(t)$가 neuron에 주입된다고 가정 이때 키르히호프 전류법칙(Kirchhoff's Current Law)에 의거하여 아래와 같다.

$$ I(t) = I_R(t)+I_C(t) $$

이때 옴의 법칙에 의거하여 뉴런 내부,외부 막 사이의 전위차 $V_m(t)$는 저항을 통해 구할 수 있다.

$$ V_m(t)=I_R(t)R $$

커패시턴스 $C$는 저장된 전하 $Q(t)$와 막 전위 $V_m(t)$ 사이의 비례상수 이다. 그리고 전화의 변화율은 커패시터의 전류이다.

$$ I_C(t)=C\cdot \frac{dV_m(t)}{dt} $$

따라서 $I(t)$은 아래와 같이 나타낼 수 있다.

$$ I(t) = \frac{V_m(t)}{R} + C \cdot \frac{dV_m(t)}{dt} $$

이때 회로의 시간상수 $\tau = RC$로 $V_m(t)$함수와 그 도함수가 같은 형태가 될려면 시간상수 $\tau$도 지수적으로 감소하는 형태를 가져야 한다. 따라서 $V_m(0) = V_0$ 이고 추가적인 입력이 없다고 가정하면($I(t)=0$) 미분 방정식의 해는 아래와 같다.

$$ V_m(t)=V_0 \cdot e^{-\frac{t}{\tau}} $$

!https://github.com/jeshraghian/snntorch/blob/master/docs/_static/img/examples/tutorial2/2_4_RCmembrane.png?raw=true


Forward Euler 이산화

앞서 유도한 연속 미분방정식을 컴퓨터에서 시뮬레이션하기 위해 Forward Euler 방법으로 이산화합니다. 연속 시간 $t$를 고정된 시간 간격 $\Delta t$로 나누어 수치적으로 풀 수 있습니다.

$$ \tau \frac{dV_m}{dt} = -V_m(t) + I(t)R $$

Forward Euler 방법을 적용하면:

$$ V_m(t + \Delta t) = V_m(t) + \frac{\Delta t}{\tau}\left(-V_m(t) + I(t)R\right) $$

이를 정리하면:

$$ V_m(t + \Delta t) = \left(1 - \frac{\Delta t}{\tau}\right)V_m(t) + \frac{\Delta t}{\tau} I(t) R $$

이때 $\left(1 - \frac{\Delta t}{\tau}\right)$가 막 전위의 감쇠율 $\beta$가 됩니다. 이것이 Tutorial 3에서 단순화된 LIF 모델의 핵심 파라미터가 됩니다.

import torch

# RC 회로 파라미터
R = 5.0      # 저항 (MΩ)
C = 1e-3     # 커패시턴스 (μF)
tau = R * C  # 시간상수 τ = RC

# 시뮬레이션 설정
delta_t = 1e-3   # 시간 간격 (ms)
num_steps = 200  # 총 시간 스텝

# 초기 막 전위
V_m = torch.zeros(1)
V_rec = []  # 기록용

# 입력 전류 (50번째 스텝부터 step input)
I = torch.cat([torch.zeros(50), torch.ones(150) * 0.2])

for step in range(num_steps):
    # Forward Euler: 이산 업데이트
    dV = (delta_t / tau) * (-V_m + I[step] * R)
    V_m = V_m + dV
    V_rec.append(V_m.item())

print(f"최종 막 전위: {V_rec[-1]:.4f} V")
print(f"시간상수 τ = {tau:.4f} s, 감쇠율 β ≈ {1 - delta_t/tau:.4f}")

Lapicque's LIF neuron model

Lapicque 모델은 RC 회로 기반의 원형 LIF 모델로, snnTorch에서 snntorch.Lapicque 클래스로 제공됩니다. 다음 여섯 가지 시나리오를 통해 특성을 이해합니다.

import snntorch as snn
import torch

# Lapicque 뉴런 인스턴스화
# R: 막 저항, C: 막 커패시턴스, time_step: 시뮬레이션 시간 간격
lif = snn.Lapicque(R=5, C=1e-3, time_step=1e-3)

Without Stimulus (자극 없음)

외부 입력 전류가 0일 때 초기 막 전위는 시간상수 τ에 따라 지수적으로 0으로 수렴합니다.

num_steps = 200
mem = torch.ones(1) * 0.9  # 초기 막 전위: 임계값 아래에서 시작
cur_in = torch.zeros(num_steps)  # 입력 없음
mem_rec = []

for step in range(num_steps):
    spk, mem = lif(cur_in[step], mem)
    mem_rec.append(mem.item())

# → mem_rec을 그래프로 그리면 지수 감쇠 곡선이 나타남

Step inputs (계단 입력)

일정한 전류가 갑자기 인가될 때 막 전위가 점진적으로 상승하다 임계값 도달 시 스파이크를 발생시킵니다.

cur_in = torch.cat([torch.zeros(10), torch.ones(190) * 0.2])  # 10스텝 후 일정 전류
mem = torch.zeros(1)
mem_rec, spk_rec = [], []

for step in range(200):
    spk, mem = lif(cur_in[step], mem)
    mem_rec.append(mem.item())
    spk_rec.append(spk.item())

print(f"Total spikes: {sum(spk_rec)}")

Pulse inputs (펄스 입력)

짧은 펄스 형태의 전류가 반복적으로 입력될 때 막 전위가 계단식으로 축적됩니다.

# 20 스텝마다 짧은 펄스 주입
pulse = torch.zeros(200)
pulse[::20] = 1.0   # 20, 40, 60... 스텝에서 전류 주입

mem = torch.zeros(1)
for step in range(200):
    spk, mem = lif(pulse[step], mem)

Firing (발화)

막 전위가 임계값(threshold, 기본값 1.0)을 초과하면 스파이크가 발생합니다.

# 임계값 조정도 가능
lif_custom = snn.Lapicque(R=5, C=1e-3, time_step=1e-3, threshold=0.5)

# 임계값이 낮을수록 더 자주 발화, 높을수록 강한 자극이 필요

Spike inputs (스파이크 입력)

다른 뉴런에서 온 이진 스파이크(0 또는 1)를 가중치로 스케일링하여 입력으로 사용합니다.

import torch.nn as nn

# 선형 레이어의 출력을 LIF 뉴런의 입력으로 연결
fc = nn.Linear(784, 1)
lif = snn.Lapicque(R=5, C=1e-3, time_step=1e-3)
mem = torch.zeros(1)

# 스파이크 트레인 입력 생성 (MNIST 픽셀의 Rate coding)
spike_input = torch.bernoulli(mnist_pixel.view(-1))   # (784,) binary tensor

# 가중치를 곱한 전류를 뉴런에 주입
cur = fc(spike_input.float())
spk, mem = lif(cur, mem)

Reset Mechanisms (리셋 메커니즘)

스파이크가 발생한 후 막 전위를 어떻게 초기화하는지에 따라 두 가지 방식이 있습니다.

  • Reset by Subtraction (감산 리셋): 스파이크 발생 시 임계값만큼 전위를 감소시킵니다. ($V \leftarrow V - V_{thr}$) 임계값 이상의 에너지가 다음 사이클에 이월됩니다. snnTorch 기본값입니다.
  • Reset to Zero (영점 리셋): 스파이크 발생 시 막 전위를 0(또는 Reset 전위)로 강제 초기화합니다. ($V \leftarrow 0$) 생물학적으로 더 가까운 동작입니다.
# Reset by subtraction (기본값)
lif_sub = snn.Lapicque(R=5, C=1e-3, time_step=1e-3, reset_mechanism="subtract")

# Reset to zero
lif_zero = snn.Lapicque(R=5, C=1e-3, time_step=1e-3, reset_mechanism="zero")

# 두 방식 비교: 동일한 입력에 대해 발화 패턴이 달라짐
cur_in = torch.ones(100) * 0.3
mem_sub, mem_zero = torch.zeros(1), torch.zeros(1)

for step in range(100):
    spk_sub, mem_sub = lif_sub(cur_in[step], mem_sub)
    spk_zer, mem_zero = lif_zero(cur_in[step], mem_zero)
# subtract: 이전 에너지가 이월되어 더 자주 발화 가능
# zero: 매 발화 후 완전 초기화로 더 규칙적인 발화 패턴