spikingjelly学习-监视器

对应【https://spikingjelly.readthedocs.io/zh-cn/0.0.0.0.14/activation_based/monitor.html】

遇到问题【Python AttributeError: module ‘distutils‘ has no attribute ‘version‘】
from spikingjelly.activation_based import monitor, neuron, functional, layer

【https://blog.youkuaiyun.com/Alexa_/article/details/132686602】
LooseVersion = distutils.version.LooseVersion
AttributeError: module ‘distutils’ has no attribute ‘version’

import torch
import torch.nn as nn
from spikingjelly.activation_based import monitor, neuron, functional, layer

net = nn.Sequential(
    layer.Linear(8, 4),
    neuron.IFNode(),
    layer.Linear(4, 2),
    neuron.IFNode()
)

for param in net.parameters():
    param.data.abs_()

functional.set_step_mode(net, 'm')


spike_seq_monitor = monitor.OutputMonitor(net, neuron.IFNode)
T = 4
N = 1
x_seq = torch.rand([T, N, 8])
print(f'x_seq={x_seq}')

with torch.no_grad():
    net(x_seq)
    print(f'net={net}')

print(f'spike_seq_monitor.records=\n{spike_seq_monitor.records}')
x_seq=tensor([[[0.8103, 0.3444, 0.2343, 0.3068, 0.9242, 0.9482, 0.0254, 0.6177]],

        [[0.7992, 0.6859, 0.2073, 0.2428, 0.2519, 0.7367, 0.9069, 0.9154]],

        [[0.5242, 0.0340, 0.2344, 0.1659, 0.7342, 0.9544, 0.3074, 0.3089]],

        [[0.8285, 0.8670, 0.7657, 0.8079, 0.7226, 0.4870, 0.8185, 0.7910]]])
net=Sequential(
  (0): Linear(in_features=8, out_features=4, bias=True)
  (1): IFNode(
    v_threshold=1.0, v_reset=0.0, detach_reset=False, step_mode=m, backend=torch
    (surrogate_function): Sigmoid(alpha=4.0, spiking=True)
  )
  (2): Linear(in_features=4, out_features=2, bias=True)
  (3): IFNode(
    v_threshold=1.0, v_reset=0.0, detach_reset=False, step_mode=m, backend=torch
    (surrogate_function): Sigmoid(alpha=4.0, spiking=True)
  )
)
spike_seq_monitor.records=
[tensor([[[0., 1., 0., 0.]],

        [[1., 1., 1., 1.]],

        [[0., 0., 0., 0.]],

        [[1., 1., 1., 1.]]]), tensor([[[0., 0.]],

        [[1., 1.]],

        [[0., 0.]],

        [[1., 1.]]])]

第二部分

import torch
import torch.nn as nn
from spikingjelly.activation_based import monitor, neuron, functional, layer

net = nn.Sequential(
    layer.Linear(8, 4),
    neuron.IFNode(),
    layer.Linear(4, 2),
    neuron.IFNode()
)

for param in net.parameters():
    param.data.abs_()

functional.set_step_mode(net, 'm')

T = 4
N = 1
x_seq = torch.rand([T, N, 8])
print(f'x_seq={x_seq}')

def cal_firing_rate(s_seq: torch.Tensor):
    # s_seq.shape = [T, N, *]
    return s_seq.flatten(1).mean(1)

fr_monitor = monitor.OutputMonitor(net, neuron.IFNode, cal_firing_rate)

print(f'net={net}')

with torch.no_grad():
    functional.reset_net(net)
    fr_monitor.enable()
    net(x_seq)
    print(f'after call fr_monitor.enable(), fr_monitor.records=\n{fr_monitor.records}')
    functional.reset_net(net)
    #del fr_monitor

fr_monitor.remove_hooks()
x_seq=tensor([[[0.8234, 0.5764, 0.9747, 0.7809, 0.7263, 0.3057, 0.4685, 0.7957]],

        [[0.4115, 0.6270, 0.3786, 0.7090, 0.1846, 0.3448, 0.5977, 0.8899]],

        [[0.2308, 0.6170, 0.9733, 0.3767, 0.6033, 0.4628, 0.4365, 0.8855]],

        [[0.5851, 0.4342, 0.5604, 0.7551, 0.1712, 0.5831, 0.0962, 0.3603]]])
net=Sequential(
  (0): Linear(in_features=8, out_features=4, bias=True)
  (1): IFNode(
    v_threshold=1.0, v_reset=0.0, detach_reset=False, step_mode=m, backend=torch
    (surrogate_function): Sigmoid(alpha=4.0, spiking=True)
  )
  (2): Linear(in_features=4, out_features=2, bias=True)
  (3): IFNode(
    v_threshold=1.0, v_reset=0.0, detach_reset=False, step_mode=m, backend=torch
    (surrogate_function): Sigmoid(alpha=4.0, spiking=True)
  )
)
after call fr_monitor.enable(), fr_monitor.records=
[tensor([0.7500, 0.2500, 0.7500, 0.2500]), tensor([1., 0., 1., 0.])]

为什么x_seq是418的形状,但是通过监视器的到的发射率是两个张量,都是1*4的形状?net中的神经元经历了什么?
【x_seq是一个形状为[T, N, *]的张量,其中T=4表示时间步数,N=1表示批次大小,*表示输入特征的维度,这里是8。这个张量通过net网络传递,net是一个由线性层和积分发放(IF)神经元层交替构成的序列模型。
在net中,数据流经历如下过程:

  1. 第一个线性层:将8维输入转换为4维。
  2. 第一个IF神经元层:对线性层的输出进行处理,根据输入的累积和是否达到阈值来决定是否发放脉冲(发放脉冲时输出1,否则输出0)。这一步不改变数据的维度,仍然是4维。
  3. 第二个线性层:将4维数据转换为2维。
  4. 第二个IF神经元层:同样根据输入的累积和是否达到阈值来决定是否发放脉冲,输出维度为2维。
    fr_monitor是一个监视器,用于记录特定层(在这个例子中是IF神经元层)的输出。由于网络中有两个IF神经元层,所以fr_monitor.records包含两个张量,每个张量对应一个IF层的输出。
    对于每个时间步t,IF层都会根据输入决定是否发放脉冲。fr_monitor.records中的每个张量记录了在整个时间序列上,每个神经元的平均发放率。因此,尽管输入x_seq的形状是[4, 1, 8],通过网络处理后,监视器记录的发射率反映了每个IF层在所有时间步上的平均发放情况。
    第一个张量tensor([0.7500, 0.2500, 0.7500, 0.2500])可能对应第一个IF层的平均发放率,表示在时间序列上,每个神经元的平均发放率。
    第二个张量tensor([1., 0., 1., 0.])可能对应第二个IF层的平均发放率。
    这些发射率的具体值取决于每个IF层在每个时间步上的输入值,以及它们是否足以触发神经元发放。】
    在这里插入图片描述
    第一个IF层有几个神经元?第二个IF层有几个神经元?为什么他们的发射率都是1*4的形状?

在这里插入图片描述
【在给定的网络net中:
第一个IF层有4个神经元,因为它跟在一个将输入从8维转换到4维的线性层之后。
第二个IF层有2个神经元,因为它跟在一个将输入从4维转换到2维的线性层之后。
关于fr_monitor.records中发射率张量形状的问题,我的先前解释有误。实际上,根据您提供的输出:
这里的确出现了一个不一致性,因为根据网络结构,第一个IF层的发射率应该是针对4个神经元的,而第二个IF层的发射率应该是针对2个神经元的。然而,您提供的输出中两个张量都是4个元素的形状,这与第二个IF层的神经元数量不匹配。
正确的情况应该是:
第一个IF层的发射率记录应该反映4个神经元的平均发射率,因此形状为[4]是合理的。
第二个IF层的发射率记录应该反映2个神经元的平均发射率,因此理论上应该有一个形状为[2]的张量。
如果fr_monitor.records中的第二个张量也是形状为[4],这可能是由于如何计算和记录发射率的具体实现方式导致的误解或错误。可能的解释是,记录的发射率并不直接对应于每个IF层的神经元数量,而是反映了某种形式的平均或汇总统计信息,或者是由于代码中的逻辑错误导致错误地记录了数据。
为了准确理解这一点,需要进一步检查fr_monitor的实现细节,特别是cal_firing_rate函数如何应用于每个IF层的输出,以及如何收集和记录这些数据。】

结合回答,代码如下,其实是4个时刻,这个大的神经元层的发射率

import torch
import torch.nn as nn
from spikingjelly.activation_based import monitor, neuron, functional, layer

net = nn.Sequential(
    layer.Linear(8, 4),
    neuron.IFNode(),
    layer.Linear(4, 2),
    neuron.IFNode()
)
print(f'net={net}\n')

functional.set_step_mode(net, 'm')

for param in net.parameters():
    param.data.abs_()

T = 4
N = 1
x_seq = torch.rand([T, N, 8])
print(f'x_seq={x_seq}\n')



def cal_firing_rate(s_seq: torch.Tensor):
    # s_seq.shape = [T, N, *]
    return s_seq.flatten(1).mean(1)
fr_monitor = monitor.OutputMonitor(net, neuron.IFNode, cal_firing_rate)

spike_seq_monitor = monitor.OutputMonitor(net, neuron.IFNode)


with torch.no_grad():
    functional.reset_net(net)
    fr_monitor.enable()
    net(x_seq)
    print(f'after call fr_monitor.enable(), fr_monitor.records=\n{fr_monitor.records}')
    functional.reset_net(net)
    del fr_monitor
    
    print(f'spike_seq_monitor.records=\n{spike_seq_monitor.records}')
    del spike_seq_monitor

net=Sequential(
  (0): Linear(in_features=8, out_features=4, bias=True)
  (1): IFNode(
    v_threshold=1.0, v_reset=0.0, detach_reset=False, step_mode=s, backend=torch
    (surrogate_function): Sigmoid(alpha=4.0, spiking=True)
  )
  (2): Linear(in_features=4, out_features=2, bias=True)
  (3): IFNode(
    v_threshold=1.0, v_reset=0.0, detach_reset=False, step_mode=s, backend=torch
    (surrogate_function): Sigmoid(alpha=4.0, spiking=True)
  )
)

x_seq=tensor([[[0.6024, 0.1319, 0.8464, 0.3353, 0.7439, 0.9514, 0.6847, 0.0607]],

        [[0.7867, 0.6780, 0.7511, 0.0891, 0.6840, 0.5771, 0.6542, 0.9733]],

        [[0.7950, 0.5653, 0.2092, 0.7568, 0.2902, 0.6944, 0.2551, 0.3897]],

        [[0.8844, 0.4664, 0.9552, 0.1576, 0.2005, 0.3479, 0.0963, 0.5912]]])

after call fr_monitor.enable(), fr_monitor.records=
[tensor([0.7500, 1.0000, 0.7500, 0.5000]), tensor([0.0000, 1.0000, 0.5000, 0.5000])]
spike_seq_monitor.records=
[tensor([[[0., 1., 1., 1.]],

        [[1., 1., 1., 1.]],

        [[1., 1., 0., 1.]],

        [[0., 0., 1., 1.]]]), tensor([[[0., 0.]],

        [[1., 1.]],

        [[0., 1.]],

        [[1., 0.]]])]

其中[tensor([0.7500, 1.0000, 0.7500, 0.5000]), tensor([0.0000, 1.0000, 0.5000, 0.5000])]对应4个时刻的这个层的发射率,中间的神经元的发射率要自己再去算

第三部分-记录模块成员变量

import torch
import torch.nn as nn
from spikingjelly.activation_based import monitor, neuron, functional, layer

net = nn.Sequential(
    layer.Linear(8, 4),
    neuron.IFNode(),
    layer.Linear(4, 2),
    neuron.IFNode()
)
print(f'net={net}\n')

functional.set_step_mode(net, 'm')

for param in net.parameters():
    param.data.abs_()

T = 4
N = 1
x_seq = torch.rand([T, N, 8])
print(f'x_seq={x_seq}\n')



def cal_firing_rate(s_seq: torch.Tensor):
    # s_seq.shape = [T, N, *]
    return s_seq.flatten(1).mean(1)
fr_monitor = monitor.OutputMonitor(net, neuron.IFNode, cal_firing_rate)

spike_seq_monitor = monitor.OutputMonitor(net, neuron.IFNode)


with torch.no_grad():
    functional.reset_net(net)
    fr_monitor.enable()
    net(x_seq)
    print(f'after call fr_monitor.enable(), fr_monitor.records=\n{fr_monitor.records}')
    functional.reset_net(net)
    del fr_monitor

    print(f'spike_seq_monitor.records=\n{spike_seq_monitor.records}')
    del spike_seq_monitor
print('--------------------------------------\n')
for m in net.modules():
    if isinstance(m, neuron.IFNode):
        m.store_v_seq = True

v_seq_monitor = monitor.AttributeMonitor('v_seq', pre_forward=False, net=net, instance=neuron.IFNode)
with torch.no_grad():
    functional.reset_net(net)
    net(x_seq)
    print(f'v_seq_monitor.records=\n{v_seq_monitor.records}')
    functional.reset_net(net)
    del v_seq_monitor

记录输入

import torch
import torch.nn as nn
from spikingjelly.activation_based import monitor, neuron, functional, layer

net = nn.Sequential(
    layer.Linear(8, 4),
    neuron.IFNode(),
    layer.Linear(4, 2),
    neuron.IFNode()
)
print(f'net={net}\n')

functional.set_step_mode(net, 'm')

for param in net.parameters():
    param.data.abs_()

T = 4
N = 1
x_seq = torch.rand([T, N, 8])
print(f'x_seq={x_seq}\n')



def cal_firing_rate(s_seq: torch.Tensor):
    # s_seq.shape = [T, N, *]
    return s_seq.flatten(1).mean(1)
fr_monitor = monitor.OutputMonitor(net, neuron.IFNode, cal_firing_rate)

spike_seq_monitor = monitor.OutputMonitor(net, neuron.IFNode)


with torch.no_grad():
    functional.reset_net(net)
    fr_monitor.enable()
    net(x_seq)
    print(f'after call fr_monitor.enable(), fr_monitor.records=\n{fr_monitor.records}')
    functional.reset_net(net)
    del fr_monitor

    print(f'spike_seq_monitor.records=\n{spike_seq_monitor.records}')
    del spike_seq_monitor
print('--------------------------------------\n')
for m in net.modules():
    if isinstance(m, neuron.IFNode):
        m.store_v_seq = True

v_seq_monitor = monitor.AttributeMonitor('v_seq', pre_forward=False, net=net, instance=neuron.IFNode)
input_monitor = monitor.InputMonitor(net, neuron.IFNode)
with torch.no_grad():
    functional.reset_net(net)
    net(x_seq)
    print(f'input_monitor.records=\n{input_monitor.records}')
    print(f'v_seq_monitor.records=\n{v_seq_monitor.records}')
    functional.reset_net(net)
    del v_seq_monitor
    del input_monitor

spike_seq_monitor.records=
[tensor([[[0., 0., 0., 0.]],

        [[1., 1., 1., 1.]],

        [[1., 0., 1., 1.]],

        [[0., 1., 0., 0.]]]), tensor([[[0., 0.]],

        [[1., 1.]],

        [[0., 0.]],

        [[1., 1.]]])]
--------------------------------------

input_monitor.records=
[tensor([[[0.9585, 0.7271, 0.9534, 0.8826]],

        [[0.7900, 0.6853, 0.8445, 1.0299]],

        [[1.1499, 0.9388, 1.0878, 1.2133]],

        [[0.8198, 0.7123, 0.7467, 0.8653]]]), tensor([[[0.0359, 0.3038]],

        [[1.0266, 1.1850]],

        [[0.8415, 0.8591]],

        [[0.2210, 0.6296]]])]
v_seq_monitor.records=
[tensor([[[0.9585, 0.7271, 0.9534, 0.8826]],

        [[0.0000, 0.0000, 0.0000, 0.0000]],

        [[0.0000, 0.9388, 0.0000, 0.0000]],

        [[0.8198, 0.0000, 0.7467, 0.8653]]]), tensor([[[0.0359, 0.3038]],

        [[0.0000, 0.0000]],

        [[0.8415, 0.8591]],

        [[0.0000, 0.0000]]])]

输入的值是随机的,但是如果达到阈值1的时候,v会归零并且发射,spike_seq_monitor和v_seq_monitor中,0和1的位置正好的相反的,表示,如果发射了,spike=1然后v=0,如果没有发射,spike=0而v还是一个小于1的数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_44781508

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值