1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【第3世代のAI】Spiking Neural Network (SNN) の基礎から理論、学習手法まで

1
Last updated at Posted at 2026-04-14

はじめに

近年、ChatGPTをはじめとする大規模言語モデル(LLM)や画像生成AIが世界を席巻しています。これらは従来の人工ニューラルネットワーク(Artificial Neural Network; ANN)の進化の賜物です。しかし、これらのモデルは膨大な計算資源と電力を消費するという大きな課題を抱えています。

そこで「第3世代のニューラルネットワーク」として世界中の研究機関で注目を集めているのが、より人間の脳の構造に近いSpiking Neural Network (SNN) です。本記事では、SNNの基礎から理論的な背景、そしてどのように学習を行うのかを徹底的に解説します。

1. そもそもSNNとは?(ANNとの違い)

従来のANN(第2世代)とSNN(第3世代)の最大の違いは、「情報の伝達方法」と「時間の概念」にあります。

  • ANN(従来型): 連続値(実数)のアクティベーションを伝播させます。時間は通常、層(レイヤー)の深さとして表現され、計算は同期的に行われます。
  • SNN(第3世代): 「スパイク(パルス)」と呼ばれる離散的な信号(0または1)を用いて情報を伝達します。ニューロンは非同期に動作し、時間軸に沿ったダイナミクスを持ちます。

人間の脳は、約860億個のニューロンがスパイク信号をやり取りすることで、わずか約20W(電球1個分)の電力で高度な処理を行っています。SNNはこの省電力性を模倣する「ニューロモルフィック・コンピューティング」のソフトウェア的基盤となります。

2. 理論背景:ニューロンモデル

SNNを理解するためには、ニューロンの挙動を数式化したモデルを知る必要があります。最も代表的で計算コストと生物学的妥当性のバランスが良いのがLIF (Leaky Integrate-and-Fire) モデルです。

LIF (Leaky Integrate-and-Fire) モデル

LIFモデルは、ニューロンを「コンデンサ(Integrate)」と「抵抗(Leaky)」を持つ電気回路として表現します。

  1. 漏れ(Leaky): 何も入力がないと、膜電位(ニューロン内部の電位)は徐々に静止膜電位に戻ろうとします。
  2. 積分(Integrate): 前のニューロンからスパイク(入力電流)を受け取ると、膜電位が上昇(積分)します。
  3. 発火(Fire): 膜電位が特定の**閾値(Threshold)**を超えると、スパイクを出力し、膜電位はリセットされます。

これを微分方程式で表すと以下のようになります。

$$\tau_m \frac{dV_m(t)}{dt} = -(V_m(t) - V_{rest}) + R \cdot I(t)$$

  • $V_m(t)$: 時刻 $t$ における膜電位
  • $V_{rest}$: 静止膜電位(通常は0やマイナスの値)
  • $\tau_m$: 膜時定数(電位の減衰のスピードを決める)
  • $R$: 膜抵抗
  • $I(t)$: 入力電流(シナプスを介して受け取るスパイク)

膜電位 $V_m(t)$ が閾値 $V_{th}$ に達したとき、$V_m(t) \to V_{reset}$ となり、スパイクが出力されます。

3. 情報のエンコーディング(コーディング手法)

実数のデータ(例えば画像のピクセル値など)を、どのように「0と1のスパイク列」に変換するのでしょうか?主に2つのアプローチがあります。

  1. Rate Coding(発火頻度コーディング)
    値が大きいほど、一定時間内にたくさんのスパイクを発生させます。(例:ピクセル値255なら高頻度で発火、0なら発火しない)。実装が容易ですが、スパイク数が多くなり消費電力の観点では不利になることがあります。
  2. Temporal Coding(時間コーディング)
    値の大きさを「スパイクが発生するタイミング」で表現します。(例:値が大きいほど、より早く最初のスパイクを出す:Time-To-First-Spike)。極めてスパイク数が少なくなり、エネルギー効率が非常に高いです。

4. SNNの学習手法における最大の障壁と解決策

SNNを実用化する上で、長年研究者を悩ませてきたのが「どうやって学習(誤差逆伝播法:Backpropagation)させるか」という問題です。

離散微分不可能問題

ディープラーニングの学習は、損失関数の勾配(微分)を計算して重みを更新するバックプロパゲーション(BP)に依存しています。
しかし、SNNのスパイク生成は「閾値を超えたら1、それ以外は0」というヘビサイドの階段関数(ステップ関数)です。ステップ関数は閾値の点では微分不可能(無限大)、それ以外では微分値が「0」になってしまうため、勾配が消失し学習が全く進みません。

これを解決するための主なアプローチが以下の2つです。

① Surrogate Gradient(代替勾配法)

現在、SNNの学習において最も主流なアプローチです。
順伝播(Forward pass)では正確なステップ関数を用いてスパイクを生成しますが、**逆伝播(Backward pass)の時だけ、ステップ関数を近似した滑らかな関数(Sigmoid関数やFast Sigmoidなど)の微分で置き換える(Surrogate)**というトリッキーかつ強力な手法です。

これにより、PyTorchなどの既存の自動微分フレームワークを用いて、時間を展開したBPTT(Backpropagation Through Time)でSNNをエンドツーエンドで学習させることが可能になりました。

② STDP (Spike-Timing-Dependent Plasticity)

生物の脳で実際に起きている学習法則(ヘブ則の拡張)を模倣した、教師なし学習の局所的な学習則です。

  • シナプス前ニューロンが発火した直後に、シナプス後ニューロンが発火した場合: 「この結合は重要だ」とみなし、シナプスの結合荷重(重み)を強化(LTP)します。
  • 逆に発火した場合: 重みを弱化(LTD)します。

計算コストは低いものの、画像認識などの複雑なタスクにおける精度では Surrogate Gradient に劣る傾向があります。

5. 簡単な実装イメージ(PyTorch / snnTorch)

最近では snnTorchBindsNET といったフレームワークが登場し、PyTorchライクにSNNを実装できるようになりました。以下は snnTorch を用いたLIFニューロンの極めてシンプルな1ステップのイメージです。

import torch
import snntorch as snn

# LIFニューロンの初期化 (減衰率 beta = 0.95)
beta = 0.95
lif = snn.Leaky(beta=beta)

# 膜電位の初期化
mem = lif.init_leaky()

# 入力電流 (例としてランダムなテンソル)
x_in = torch.rand(1)

# 1タイムステップの計算
spk, mem = lif(x_in, mem)

print(f"Spike out: {spk}, Membrane Potential: {mem}")

6. 少し実践的なコード:snnTorchによるMNIST画像分類の実装

理論を理解したところで、実際にSNNを構築してMNIST(手書き数字認識)を解いてみましょう。ここでは、デファクトスタンダードになりつつあるライブラリ snnTorch を使用します。

実装コード

このコードでは、単純な全結合型SNNを構築し、画像をスパイク列に変換して入力、最終的な出力層のスパイク数で数字を判定します。

import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import snntorch as snn
from snntorch import surrogate
from snntorch import utils
import matplotlib.pyplot as plt

# 1. ハイパーパラメータの設定
batch_size = 128
data_path = './data/mnist'
num_steps = 25  # タイムステップ数(1つの画像に対して何ステップシミュレーションするか)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 2. データセットとデータローダー
transform = transforms.Compose([
    transforms.Resize((28, 28)),
    transforms.Grayscale(),
    transforms.ToTensor(),
    transforms.Normalize((0,), (1,))
])

mnist_train = datasets.MNIST(data_path, train=True, download=True, transform=transform)
train_loader = DataLoader(mnist_train, batch_size=batch_size, shuffle=True, drop_last=True)

# 3. ネットワーク定義
class Net(nn.Module):
    def __init__(self):
        super().__init__()

        # 代替勾配関数の定義(Fast Sigmoid)
        spike_grad = surrogate.fast_sigmoid(slope=25)

        # ネットワーク層の定義
        self.fc1 = nn.Linear(784, 1000)
        self.lif1 = snn.Leaky(beta=0.9, spike_grad=spike_grad) # 隠れ層のLIFニューロン
        self.fc2 = nn.Linear(1000, 10)
        self.lif2 = snn.Leaky(beta=0.9, spike_grad=spike_grad) # 出力層のLIFニューロン

    def forward(self, x):
        # 膜電位の初期化
        mem1 = self.lif1.init_leaky()
        mem2 = self.lif2.init_leaky()

        # スパイク記録用リスト
        spk2_rec = []
        mem2_rec = []

        # 時間軸に沿ったシミュレーション
        for step in range(num_steps):
            cur1 = self.fc1(x.view(batch_size, -1)) # 入力電流
            spk1, mem1 = self.lif1(cur1, mem1)      # 第1層の発火計算
            cur2 = self.fc2(spk1)                   # 第1層のスパイクが次の入力へ
            spk2, mem2 = self.lif2(cur2, mem2)      # 出力層の発火計算
            
            spk2_rec.append(spk2)
            mem2_rec.append(mem2)

        return torch.stack(spk2_rec, dim=0), torch.stack(mem2_rec, dim=0)

net = Net().to(device)

# 4. 損失関数と最適化
optimizer = torch.optim.Adam(net.parameters(), lr=5e-4, betas=(0.9, 0.999))
# 出力層のスパイク数に基づいたクロスエントロピー損失
loss_fn = snn.functional.ce_count_loss() 

# 5. 学習ループ(簡易版)
print("Training started...")
for epoch in range(1):
    for i, (data, targets) in enumerate(train_loader):
        data, targets = data.to(device), targets.to(device)

        net.train()
        spk_rec, mem_rec = net(data) # 順伝播
        loss_val = loss_fn(spk_rec, targets) # 損失計算

        optimizer.zero_grad()
        loss_val.backward() # 逆伝播(Surrogate Gradientが活躍!)
        optimizer.step()

        if i % 100 == 0:
            print(f"Iteration {i}, Loss: {loss_val.item():.4f}")

print("Training complete!")

コードのポイント解説

  1. num_steps = 25: SNN特有のパラメータです。1枚の画像を25個の時間枠(タイムステップ)に分けて処理します。この繰り返しによって「時間的なダイナミクス」が生まれます。
  2. surrogate.fast_sigmoid: 本文で解説した「代替勾配」です。これがあるおかげで、本来微分できないスパイク信号を通したバックプロパゲーションが可能になります。
  3. snn.Leaky: LIFニューロンを1つの層として扱っています。内部で「膜電位の保持」と「閾値判定」を行っています。
  4. ce_count_loss: 出力層の10個のニューロンのうち、どのニューロンが最も多くスパイクを出したかをカウントし、それを正解ラベルと比較して学習させます。

おわりに

SNNは「精度」の面ではまだ従来のANN(CNNやTransformerなど)に一歩譲る部分がありますが、「超低消費電力」「低レイテンシ」というエッジAIデバイスにおいて決定的な強みを持っています。
IntelのLoihiやIBMのTrueNorthなど、SNNに特化したニューロモルフィック・ハードウェアの開発も急速に進んでおり、第3世代のAIが社会実装される日はそう遠くないであろうと考えています。


1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?