对应【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中,数据流经历如下过程:
- 第一个线性层:将8维输入转换为4维。
- 第一个IF神经元层:对线性层的输出进行处理,根据输入的累积和是否达到阈值来决定是否发放脉冲(发放脉冲时输出1,否则输出0)。这一步不改变数据的维度,仍然是4维。
- 第二个线性层:将4维数据转换为2维。
- 第二个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的数