教程 1:构建和使用 CANN 模型

Note

阅读时间: 25-30 分钟

难度: 初级

前提条件: Python 基础、NumPy/JAX 数组操作

本教程将帮助您理解 CANNs 库中模型的构建方式,以及如何使用内置的 Wu-Amari-Wong (WAW) 连续吸引子神经网络(CANNs) [5, 6, 7, 8] 模型。


1. BrainPy 框架介绍

CANNs 库中的所有模型均基于 BrainPy 框架 [18] 构建。BrainPy 是脑模拟生态系统中用于动力系统的核心框架,基于 JAX 构建,支持 JIT 编译与自动微分。

1.1 核心概念

在开始之前,您需要理解以下关键概念:

动力学抽象

所有 CANN 模型均继承自 bp.DynamicalSystem——这是用于定义动力系统的基类。它提供:

  • 状态管理机制

  • 时间步管理

  • JIT 编译支持

[1]:
import brainpy as bp
import brainpy.math as bm

class MyModel(bp.DynamicalSystem):

    def update(self, inp):
        # Define single-step dynamics update
        pass

状态容器

BrainPy 提供状态容器以管理不同类型变量:

[2]:
def __init__(self):
    super().__init__()
    # State variable: neuron membrane potential
    self.u = bm.Variable(bm.zeros(self.num))
    # State variable: neuron firing rate
    self.r = bm.Variable(bm.zeros(self.num))
    # External input state
    self.inp = bm.Variable(bm.zeros(self.num))

时间步管理

BrainPy 通过 bm.set_dt() 管理模拟时间步:

[3]:
import brainpy.math as bm  # :cite:p:`wang2023brainpy`

# Set simulation time step (unit: milliseconds)
bm.set_dt(0.1)

# Get current time step in the model
dt = bm.get_dt()

重要: 在运行任何模拟之前,必须设置时间步 dt——否则模型将抛出错误。

进一步学习

如需深入了解 BrainPy 框架 [18],请参阅:


2. CANN1D 实现分析

我们以 CANN1D 为例,了解完整 CANN 模型的实现方式。

2.1 模型继承结构

bp.DynamicalSystem
    └── BasicModel
        └── BaseCANN
            └── BaseCANN1D
                └── CANN1D

2.2 初始化方法 __init__

CANN1D 的初始化方法定义了所有模型参数:

[ ]:
class CANN1D(BaseCANN1D):
    def __init__(
        self,
        num: int,           # Number of neurons
        tau: float = 1.0,   # Time constant
        k: float = 8.1,     # Global inhibition strength
        a: float = 0.5,     # Connection width
        A: float = 10,      # External input amplitude
        J0: float = 4.0,    # Synaptic connection strength
        z_min: float = -π,  # Feature space minimum
        z_max: float = π,   # Feature space maximum
        **kwargs,
    ):
        ...

这些参数控制网络的动力学行为。我们将在 教程 4 中详细探讨每个参数的作用。

2.3 连接矩阵生成 make_conn

make_conn 方法生成神经元之间的连接矩阵。CANN 使用高斯连接核——具有相似特征偏好的神经元之间具有更强的兴奋性连接:

[ ]:
def make_conn(self):
    # Calculate distances between all neuron pairs
    x_left = bm.reshape(self.x, (-1, 1))
    x_right = bm.repeat(self.x.reshape((1, -1)), len(self.x), axis=0)
    d = self.dist(x_left - x_right)

    # Compute connection strength using Gaussian function
    return (
        self.J0
        * bm.exp(-0.5 * bm.square(d / self.a))
        / (bm.sqrt(2 * bm.pi) * self.a)
    )

2.4 刺激生成 get_stimulus_by_pos

get_stimulus_by_pos 根据特征空间中的指定位置生成外部刺激(高斯形波包):

[ ]:
def get_stimulus_by_pos(self, pos):
    return self.A * bm.exp(
        -0.25 * bm.square(self.dist(self.x - pos) / self.a)
    )

该方法由任务模块调用,用于生成输入数据。

2.5 动力学更新 update

update 方法定义了网络的单步动力学更新:

[ ]:
def update(self, inp):
    self.inp.value = inp

    # Compute firing rate (divisive normalization)
    r1 = bm.square(self.u.value)
    r2 = 1.0 + self.k * bm.sum(r1)
    self.r.value = r1 / r2

    # Compute recurrent input
    Irec = bm.dot(self.conn_mat, self.r.value)

    # Update membrane potential using Euler method
    self.u.value += (
        (-self.u.value + Irec + self.inp.value)
        / self.tau * bm.get_dt()
    )

3. 如何使用内置 CANN 模型

现在我们学习如何实际使用内置的 CANN 模型。

3.1 基本使用流程

[4]:
import brainpy.math as bm
from canns.models.basic import CANN1D

# Step 1: Set time step
bm.set_dt(0.1)

# Step 2: Create model instance
model = CANN1D(
    num=256,      # 256 neurons
    tau=1.0,      # Time constant
    k=8.1,        # Global inhibition
    a=0.5,        # Connection width
    A=10,         # Input amplitude
    J0=4.0,       # Connection strength
)

# Step 3: View model information
print(f"Number of neurons: {model.shape}")
print(f"Feature space range: [{model.z_min}, {model.z_max}]")
print(f"Connection matrix shape: {model.conn_mat.shape}")
Number of neurons: (256,)
Feature space range: [-3.141592653589793, 3.141592653589793]
Connection matrix shape: (256, 256)

3.2 运行单步更新

[5]:
# Generate external stimulus at pos=0
pos = 0.0
stimulus = model.get_stimulus_by_pos(pos)

# Run two step update
model(stimulus)     # or you can explicitly call model.update(stimulus)
model(stimulus)

# View current state
print(f"Firing rate shape: {model.r.value.shape}")
print(f"Max firing rate: {bm.max(model.r.value):.4f}")
print(f"Max membrane potential: {bm.max(model.u.value):.4f}")
Firing rate shape: (256,)
Max firing rate: 0.0024
Max membrane potential: 1.9275

3.3 完整示例

以下是创建并测试 CANN1D 模型的完整示例:

[6]:
import brainpy.math as bm  # :cite:p:`wang2023brainpy`
from canns.models.basic import CANN1D

# Setup environment
bm.set_dt(0.1)

# Create model (auto-initializes)
model = CANN1D(num=256, tau=1.0, k=8.1, a=0.5, A=10, J0=4.0)

# Print basic model information
print("=" * 50)
print("CANN1D Model Information")
print("=" * 50)
print(f"Number of neurons: {model.shape}")
print(f"Time constant tau: {model.tau}")
print(f"Global inhibition k: {model.k}")
print(f"Connection width a: {model.a}")
print(f"Input amplitude A: {model.A}")
print(f"Connection strength J0: {model.J0}")
print(f"Feature space: [{model.z_min:.2f}, {model.z_max:.2f}]")
print(f"Neural density rho: {model.rho:.2f}")

# Test stimulus generation
pos = 0.5
stimulus = model.get_stimulus_by_pos(pos)
print(f"\nStimulus position: {pos}")
print(f"Stimulus shape: {stimulus.shape}")
print(f"Max stimulus value: {bm.max(stimulus):.4f}")

# Run several update steps
print("\nRunning 100 update steps...")
for _ in range(100):
    model(stimulus)

print(f"Max firing rate: {bm.max(model.r.value):.6f}")
print(f"Max membrane potential: {bm.max(model.u.value):.6f}")
==================================================
CANN1D Model Information
==================================================
Number of neurons: (256,)
Time constant tau: 1.0
Global inhibition k: 8.1
Connection width a: 0.5
Input amplitude A: 10
Connection strength J0: 4.0
Feature space: [-3.14, 3.14]
Neural density rho: 40.74

Stimulus position: 0.5
Stimulus shape: (256,)
Max stimulus value: 9.9997

Running 100 update steps...
Max firing rate: 0.002427
Max membrane potential: 10.278063

4. 内置模型概览

CANNs 库提供三类内置模型:

基础模型

标准 CANN 实现及其变体:

  • CANN1D——一维连续吸引子神经网络

  • CANN1D_SFA——带有尖峰频率适应的 CANN1D

  • CANN2D——二维连续吸引子神经网络

  • CANN2D_SFA——带有 SFA 的 CANN2D

  • 分层路径积分网络(网格细胞、位置细胞、带状细胞等)

  • Theta 扫描模型

脑启发模型

基于神经科学原理的学习模型:

  • Hopfield 网络

混合模型

CANN 与人工神经网络的组合(开发中)。

详细信息: 请参阅核心概念文档,获取完整模型列表与使用案例。


恭喜您完成第一个 CANN 建模教程!您现在已理解:

  • CANN 的基本架构及其神经动力学

  • 如何创建 CANN1D 和 CANN2D 模型

  • 关键参数(num, k, tau, a, A, J0)的作用

  • 如何运行模拟并观察网络状态

您已掌握的内容

模型创建

您能够使用自定义参数实例化 CANN 模型,并理解每个参数的控制作用。

网络动力学

您理解了连续吸引子动力学——局部兴奋与全局抑制如何形成稳定的活动波包。

状态变量

您知道如何访问内部状态(u 表示突触输入,r 表示放电率)。

继续学习

现在,您可以进入下一个教程:

随后继续学习:

关键要点

  1. CANN 是动力系统——它们根据微分方程随时间演化

  2. 波包是吸引子——局部活动模式是动力学的稳定不动点

  3. 参数至关重要——不同参数组合会产生不同的动力学行为

  4. BrainPy 处理复杂性——框架自动管理状态变量与时间步进

下一步:教程 2:任务生成与模拟