如何生成任务数据?

目标: 通过本指南,您将能够为 CANN 仿真生成任务输入。

预计阅读时间: 10 分钟


介绍

CANN 模型需要输入刺激来模拟真实行为——跟踪移动目标、编码空间位置或响应群体编码信号。与其手动创建这些输入,任务 API 为常见实验范式提供了现成的生成器。

本指南将向您展示如何:

  1. 创建平滑跟踪任务

  2. 生成任务数据

  3. 理解任务数据结构

  4. 在仿真中使用任务数据

什么是任务?

任务 是为 CANN 模型生成时变输入刺激的对象。它们处理以下复杂性:

  • 创建生物学上真实的输入模式

  • 管理时间和持续时间

  • 格式化数据以实现高效的仿真循环

  • 跟踪元数据(例如目标位置、速度)

关键原则: 任务 生成输入 → 模型在仿真循环中 消耗输入

创建平滑跟踪任务

最常见的任务是 平滑跟踪 ,其中刺激在神经群体中平滑移动:

[1]:
import brainpy.math as bm
from canns.models.basic import CANN1D
from canns.task.tracking import SmoothTracking1D
import jax

# Set up environment and model (from previous guide)
bm.set_dt(0.1)
cann = CANN1D(num=512)

# Create a smooth tracking task
task = SmoothTracking1D(
    cann_instance=cann,                # Link to the CANN model
    Iext=(1.0, 0.75, 2.0, 1.75, 3.0),  # External input positions (in radians)
    duration=(10.0, 10.0, 10.0, 10.0), # Duration at each position (ms)
    time_step=bm.get_dt()  # Simulation time step
)

发生了什么

  • cann_instance: 任务需要模型实例来访问其 get_stimulus_by_pos() 方法,该方法为给定位置生成空间定位的输入模式。任务还使用模型的结构(神经元位置、网络大小)来创建适当的刺激。

  • Iext: 刺激将出现的目标位置序列(弧度制,从 -π 到 π)

  • duration: 刺激在每个位置停留多长时间(毫秒)

  • time_step: 应与模型的 dt 匹配以保持同步

重要:CANN 模型必须提供一个 get_stimulus_by_pos(position) 方法,该方法返回给定空间位置的输入模式。任务调用此方法来生成数据。

生成数据

任务创建后,生成实际的输入数组:

[2]:
# Generate all task data
task.get_data()
<SmoothTracking1D> Generating Task data: 400it [00:00, 1593.79it/s]

此方法会预计算整个仿真的所有输入刺激。数据存储在 task.data 中。

理解任务数据结构

让我们检查任务生成的内容:

[3]:
print(f"Data shape: {task.data.shape}")
print(f"Number of time steps: {task.run_steps.shape[0]}")
print(f"Time step size: {task.time_step} ms")
print(f"Total simulation time: {task.run_steps.shape[0] * task.time_step} ms")
Data shape: (400, 512)
Number of time steps: 400
Time step size: 0.1 ms
Total simulation time: 40.0 ms
[4]:
from canns.analyzer.visualization import PlotConfigs, energy_landscape_1d_static

# Create configuration for static plot
config = PlotConfigs.energy_landscape_1d_static(
    time_steps_per_second=100,
    title='Input Stimulus at t=50',
    xlabel='Position (rad)',
    ylabel='Input Strength',
    show=True
)

# Plot input at time step 50
energy_landscape_1d_static(
    data_sets={'Stimulus': (cann.x, task.data[50])},
    config=config
)
../../_images/zh_1_quick_starts_02_generate_tasks_6_0.png
[4]:
(<Figure size 1000x600 with 1 Axes>,
 <Axes: title={'center': 'Input Stimulus at t=50'}, xlabel='Position (rad)', ylabel='Input Strength'>)

您将看到一个以 Iext 指定位置之一为中心的高斯形波包。

在仿真中使用任务数据

现在将任务数据连接到您的模型仿真:

[5]:
def run_step(t, stimulus):
    """Simulation step function."""
    cann(stimulus)  # Feed stimulus to model
    return cann.u.value, cann.r.value  # Return synaptic input and activity

# Run the simulation with task data
us, rs = bm.for_loop(
    run_step,
    operands=(task.run_steps, task.data),
    progress_bar=10
)

print(f"Synaptic input shape: {us.shape}")   # (400, 512)
print(f"Neural activity shape: {rs.shape}")  # (400, 512)
Synaptic input shape: (400, 512)
Neural activity shape: (400, 512)

工作流程

  1. task.data 为每个时间步提供刺激

  2. for_loop 遍历时间步

  3. 在每一步中,刺激被传递给 run_step

  4. 模型更新其状态并返回结果

  5. 所有结果被收集到数组(usrs)中

完整工作示例

以下是从模型创建到任务驱动仿真的完整流程:

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

# 1. Setup
bm.set_dt(0.1)

# 2. Create model (auto-initializes)
cann = CANN1D(num=512)

# 3. Create task
task = SmoothTracking1D(
    cann_instance=cann,
    Iext=(1.0, 0.75, 2.0, 1.75, 3.0),
    duration=(10.0, 10.0, 10.0, 10.0),
    time_step=bm.get_dt(),
)
task.get_data()

# 4. Run simulation
def run_step(t, stimulus):
    cann(stimulus)
    return cann.u.value, cann.r.value

us, rs = bm.for_loop(
    run_step,
    operands=(task.run_steps, task.data),
    progress_bar=10
)

print("Simulation complete!")
print(f"Captured {us.shape[0]} time steps of activity")
<SmoothTracking1D> Generating Task data: 400it [00:00, 3246.41it/s]
Simulation complete!
Captured 400 time steps of activity

探索任务参数

SmoothTracking1D 非常灵活。尝试不同的配置:

示例 1: 快速追踪

[7]:
task_fast = SmoothTracking1D(
    cann_instance=cann,
    Iext=(0.0, 1.0, 2.0),
    duration=(5.0, 5.0),  # Shorter durations = faster transitions
    time_step=0.1
)

示例 2: 更多位置

[9]:
import jax.numpy as jnp

# Track across 10 evenly spaced positions
positions = jnp.linspace(-3.0, 3.0, 10)
durations = [8.0] * 9

task_dense = SmoothTracking1D(
    cann_instance=cann,
    Iext=tuple(positions),
    duration=tuple(durations),
    time_step=0.1
)

示例 3: 可变持续时间

[10]:
# Different durations for each position
task_variable = SmoothTracking1D(
    cann_instance=cann,
    Iext=(0.0, 1.5, -1.0, 2.0),
    duration=(15.0, 5.0, 10.0),  # Stay longer at first position
    time_step=0.1
)

其他任务类型

虽然本指南重点关注 SmoothTracking1D,但该库还包含其他任务生成器:

  • SmoothTracking2D: 2D 空间追踪

  • PopulationCoding: 群体编码输入

  • ClosedLoopNavigation: 带反馈的交互式导航

  • OpenLoopNavigation: 预定义轨迹

所有任务都遵循相同的模式:

  1. 使用参数创建任务

  2. 调用 task.get_data()

  3. 在仿真循环中使用 task.data

我们将在”详细信息”部分探讨这些任务。

常见问题

问: 生成任务数据后,我可以修改它吗?

可以!task.data 是一个您可以操作的 JAX 数组:

[11]:
task.get_data()
# Add noise to all inputs
task.data = task.data + 0.1 * jax.random.normal(jax.random.PRNGKey(0), task.data.shape)
<SmoothTracking1D> Generating Task data: 400it [00:00, 9170.08it/s]

问: 如果我想要自定义输入模式怎么办?

您可以创建任务作为模板并修改它们,或手动生成输入:

[12]:
import jax.numpy as jnp

# Create custom input sequence
custom_data = jnp.zeros((100, 512))
for t in range(100):
    position = jnp.sin(t * 0.1)  # Sinusoidal movement
    custom_data = custom_data.at[t].set(
        jnp.exp(-0.5 * (cann.x - position)**2 / 0.3**2)
    )

问: 我每次都需要调用 ``get_data()`` 吗?

每个任务实例只需调用一次。数据被缓存:

[ ]:
task.get_data()  # Generates data
# ... use task.data in simulations

# If you change parameters, recreate the task
task_new = SmoothTracking1D(...)
task_new.get_data()

后续步骤

现在您已经可以生成任务数据,接下来可以:

  1. 分析 CANN 模型动力学——可视化能量景观、波包追踪等

  2. 探索任务生成器——了解所有可用的任务类型及其用例

  3. 完整任务 API 参考——所有任务参数和方法的完整文档


有疑问?打开 `GitHub Discussion <https://github.com/routhleck/canns/discussions>`_.