编码器 SpikingFlow.encoding¶
本教程作者: fangwei123456
本节教程主要关注 SpikingFlow.encoding
,包括如何使用已有编码器、如何定义新的编码器。
泊松编码器¶
SNN中,脉冲在层与层之间传递信息。在SpikingFlow中,脉冲被定义为 torch.bool
类型的tensor,也就是离散的0和1。因此,常见的数据类型,例如图像,并不能直接输入到SNN中,需要做一些转换,将这些数据转换成脉冲,这一过程即为“编码”。
泊松编码器是最简单,但也效果良好、广泛使用的一种编码。
对于输入 \(x \in [0, 1]\) ,泊松编码器将其编码为发放次数的分布符合泊松过程的脉冲。实现泊松过程非常简单,因为泊松流具有独立增量性、增量平稳性的性质。在一个仿真步长内,令发放脉冲的概率为 \(p = x\) ,就可以实现泊松编码。
参考 SpikingFlow.encoding.PoissonEncoder
的代码:
def forward(self, x):
'''
:param x: 要编码的数据,任意形状的tensor,要求x的数据范围必须在[0, 1]
将输入数据x编码为脉冲,脉冲发放的概率即为对应位置元素的值
'''
out_spike = torch.rand_like(x).le(x)
# torch.rand_like(x)生成与x相同shape的介于[0, 1)之间的随机数, 这个随机数小于等于x中对应位置的元素,则发放脉冲
return out_spike
使用起来也比较简单:
pe = encoding.PoissonEncoder()
x = torch.rand(size=[8])
print(x)
for i in range(10):
print(pe(x))
更复杂的编码器¶
更复杂的一些编码器,并不能像泊松编码器这样使用。例如某个编码器,将输入0.1编码成 [0, 0, 1, 0]
这样的脉冲,由于我们的框架是时间驱动的,因此编码器需要逐步输出 0
, 0
, 1
, 0
。
对于这样的编码器,例如 SpikingFlow.encoding.LatencyEncoder
,调用时需要先编码一次,也就是条用 forward()
函数编码数据然后再输出T次,也就是调用T次 step()
,例如:
x = torch.rand(size=[3, 2])
max_spike_time = 20
le = encoding.LatencyEncoder(max_spike_time)
le(x)
print(x)
print(le.spike_time)
for i in range(max_spike_time):
print(le.step())
定义新的编码器¶
编码器的实现非常灵活,因此在编码器的基类 SpikingFlow.encoding.BaseEncoder
中,并没有很多的限制。对于在一个仿真步长内就能输出所有脉冲的编码器,只需要实现 forward()
函数;对于需要多个仿真步长才能输出所有编码脉冲的编码器,需要实现 forward()
, step()
, reset()
函数。