当前位置:   article > 正文

【脉冲神经网络入门教程 01】脉冲编码方法_脉冲神经网络编码方式

脉冲神经网络编码方式

        大脑以神经脉冲作为基本信息形式进行工作。视网膜将光刺激转化为神经脉冲,我们就有了视觉。当挥发的分子转化为神经脉冲,我们就闻到了气味。当神经末梢将触觉压力转化为脉冲时,我们就有了触觉。脉冲神经网络采用脉冲神经元作为基本单元,具有一定的仿生性。脉冲神经网络的输入为神经脉冲,但是实际输入的信息通常为图片、语音等形式,因此在信息输入前需要进行脉冲编码。

        人脑具有最顶尖的脉冲编码技术,可以利用小容量、低功耗神经系统,编码出各种各样的感觉、记忆、想象等功能。然而,目前,人脑具体的神经编码机制尚不明确,研究人员根据一些典型的神经活动编码方法,设计出了三种基本编码方式,即

  1. 速率编码:信息通过神经元发放脉冲的频率来编码。更高的频率通常表示更强的激活。

  2. 延迟编码:信息通过脉冲的时间间隔来编码。不同的时间间隔可以表示不同的信息。

  3. 德尔塔调制事件驱动型,根据时间信号前后差值来触发脉冲。

        本文采用snntorch库作为工具,演示了灰度图、时序信号等常见信息的编码方法,并提供了相关的代码。主要内容和代码均参考snntorch官方文档

1. 速率编码

        速率编码即根据信息强度值更改神经元释放脉冲的频率。

1.1 图片编码(以Mnist数据集为例)

        以mnist图片为例,脉冲神经网络是对脉冲信号流进行处理的,是时序的,因此对mnist这种静态图片进行编码时,需要加入时间轴。一般的做法是将图片复制多份,然后叠加为视频流,再进行归一化,每个像素点的值作为概率,采用伯努利分布来触发脉冲。以下演示了snntorch库加载mnist数据集、构建子数据集、速率编码的流程。

(1)加载mnist数据集

  1. import snntorch as snn
  2. import torch
  3. from torchvision import datasets, transforms
  4. from snntorch import utils
  5. from torch.utils.data import DataLoader
  6. dtype = torch.float
  7. # 加载mnist数据集
  8. data_path='data/mnist'# 数据存放位置
  9. # 数据格式为28*28,归一化到(0,1)
  10. transform = transforms.Compose([
  11. transforms.Resize((28,28)),
  12. transforms.Grayscale(),
  13. transforms.ToTensor(),
  14. transforms.Normalize((0,), (1,))])
  15. # 初次运行则下载mnist数据集
  16. mnist_train = datasets.MNIST(data_path, train=True, download=True, transform=transform)
  17. # 取60000/10 = 6000个样本作为训练集
  18. subset = 10
  19. mnist_train = utils.data_subset(mnist_train, subset)
  20. print(f"The size of mnist_train is {len(mnist_train)}")
  21. # 上面创建的 Dataset 对象会将数据加载到内存中,而 DataLoader 会将数据分批加载到内存中。PyTorch 中的数据加载器是将数据传入网络的便捷接口。它们会返回一个迭代器,分成大小为 batch_size 的小批量。
  22. batch_size=128
  23. train_loader = DataLoader(mnist_train, batch_size=batch_size, shuffle=True)
  24. # Training Parameters
  25. num_classes = 10 # MNIST has 10 output classes

(2)mnist速率编码

snntorch直接提供了脉冲编码的函数库spikegen,可以直接调用spikegen.rate进行速率编码,

  • num_steps 表示每个样本的脉冲数据的时间步数,这里设置为100。
  • batch_size 是每个 minibatch 的样本数量。
  1. from snntorch import spikegen
  2. # 设置重复次数为100,即脉冲流的长度为100
  3. num_steps = 100
  4. # Iterate through minibatches
  5. data = iter(train_loader)
  6. data_it, targets_it = next(data)
  7. # Spiking Data
  8. # 输入数据的结构为 [num_steps x batch_size x input dimensions] :
  9. spike_data = spikegen.rate(data_it, num_steps=num_steps)
  10. print(spike_data.size())

(3)打印图片

  1. import matplotlib.pyplot as plt
  2. import snntorch.spikeplot as splt
  3. # 随机选择显示1个图片编码
  4. spike_data_sample = spike_data[:, 0, 0]
  5. fig, ax = plt.subplots()
  6. splt.animator(spike_data_sample, fig, ax)
  7. plt.show()

(4)打印神经元编码

  1. plt.figure(facecolor="w")
  2. plt.subplot(1,1,1)
  3. plt.imshow(spike_data_sample.mean(axis=0).reshape((28,-1)).cpu(), cmap='binary')
  4. plt.axis('off')

(5)观察单个神经元编码

  1. idx = 210 # index into 210th neuron
  2. fig = plt.figure(facecolor="w", figsize=(8, 1))
  3. ax = fig.add_subplot(111)
  4. splt.raster(spike_data_sample.reshape(num_steps, -1)[:, idx].unsqueeze(1), ax, s=100, c="black", marker="|")
  5. plt.title("Input Neuron")
  6. plt.xlabel("Time step")
  7. plt.yticks([])
  8. plt.show()

1.2 时序信号编码 (以正弦波为例)

对时序信号的编码亦是如此,这里以一个正弦信号为例:在速率编码中,信号的值即为神经元产生脉冲的概率值,神经元根据伯努利分布,发出脉冲。在正弦信号最大值附近时,所有神经元都发出脉冲,因此是一条直线,在靠近0或负值时,神经元不产生脉冲。

  1. import torch
  2. import snntorch.spikegen as spikegen
  3. import matplotlib.pyplot as plt
  4. # 定义时序信号
  5. time_steps = 100
  6. input_signal = torch.sin(torch.linspace(0, 10 * 3.1416, time_steps))
  7. input_signal = torch.abs(input_signal)
  8. # 编码时序信号
  9. spike_train = spikegen.rate(input_signal, num_steps=time_steps, gain=1)
  10. print(spike_train)
  11. # 绘制时序信号和脉冲编码结果
  12. plt.figure(figsize=(10, 5))
  13. # 绘制时序信号
  14. plt.subplot(2, 1, 1)
  15. plt.plot(input_signal.numpy(), label='Input Signal')
  16. plt.title('Input Signal')
  17. plt.xlabel('Time Steps')
  18. plt.ylabel('Amplitude')
  19. plt.legend()
  20. # 绘制脉冲编码结果
  21. plt.subplot(2, 1, 2)
  22. plt.eventplot([torch.nonzero(spike_train[i]).numpy()[:, 0] for i in range(time_steps)], colors='black', linewidths=2)
  23. plt.title('Spike Train (Encoded Signal)')
  24. plt.xlabel('Time Steps')
  25. plt.ylabel('Neuron Index')
  26. plt.tight_layout()
  27. plt.show()

输出结果:

2.  延迟编码

速率编码的观点实际上颇具争议。尽管我们相当确信速率编码发生在我们的感官外围,但我们并不相信大脑皮层会将信息全面编码为脉冲速率。其中几个令人信服的原因包括:

  • 能耗:速率编码完成任何任务都需要多个脉冲,而每个脉冲都会消耗能量。事实上,速率编码最多只能解释 15% 神经元的活动,它不可能是大脑内唯一的机制,因为大脑资源有限,而且效率很高。
  • 反应响应时间:我们知道,人类的反应时间大约为 250 毫秒。如果人脑中神经元的平均发射率约为 10 赫兹,那么我们在反应时间内只能处理约 2 个尖峰。

那么,如果速率编码在能效或延迟方面不是最佳的,我们为什么还要使用它们呢?速率编码的鲁棒性更强,可以一定程度上抵抗噪声,便于对传感器信号编码,即使有些脉冲被噪声影响无法产生也没关系,因为还会有更多的脉冲出现。        

        延迟编码通过控制脉冲产生的时间来进行编码。一般幅值越大的信号点,产生的脉冲越靠前。延迟编码依赖于神经元精确发射脉冲时间承载的信息,与依赖发射频率的速率编码相比,单个脉冲的意义要大得多。虽然这样更容易受到噪声的影响,但也能将运行功耗降低几个数量级。与速率编码相比,延迟编码的一大优势在于稀疏性。如果神经元被限制在感兴趣的时间过程中最多发射一次,那么这将促进低功耗运行。 

延迟编码的物理意义:        

        延迟编码可以认为是一个带电容的电阻电路,电容的大小C和输入电流大小决定了电容器何时被击穿。

2.1 图片编码

        在snntorch库中采用spikegen.latency函数进行延迟编码。

  1. import snntorch as snn
  2. import torch
  3. from snntorch import spikegen, utils
  4. from torchvision import datasets, transforms
  5. from torch.utils.data import DataLoader
  6. # Training Parameters
  7. batch_size=128
  8. data_path='data/mnist'
  9. num_classes = 10 # MNIST has 10 output classes
  10. num_steps = 100
  11. # Torch Variables
  12. dtype = torch.float
  13. # Define a transform
  14. transform = transforms.Compose([
  15. transforms.Resize((28,28)),
  16. transforms.Grayscale(),
  17. transforms.ToTensor(),
  18. transforms.Normalize((0,), (1,))])
  19. subset = 10
  20. mnist_train = datasets.MNIST(data_path, train=True, download=True, transform=transform)
  21. mnist_train = utils.data_subset(mnist_train, subset)
  22. print(f"The size of mnist_train is {len(mnist_train)}")
  23. # 上面创建的 Dataset 对象会将数据加载到内存中,而 DataLoader 会将数据分批加载到内存中。PyTorch 中的数据加载器是将数据传入网络的便捷接口。它们会返回一个迭代器,分成大小为 batch_size 的小批量。
  24. train_loader = DataLoader(mnist_train, batch_size=batch_size, shuffle=True)
  25. # Iterate through minibatches
  26. data = iter(train_loader)
  27. data_it, targets_it = next(data)
  28. # Spiking Data
  29. # 输入数据的结构为 [num_steps x batch_size x input dimensions] :
  30. spike_data = spikegen.latency(data_it, num_steps=num_steps, tau=10, threshold=0.01, linear=True)
  31. print(spike_data.size())
  32. import matplotlib.pyplot as plt
  33. import snntorch.spikeplot as splt
  34. fig = plt.figure(facecolor="w", figsize=(10, 5))
  35. ax = fig.add_subplot(111)
  36. splt.raster(spike_data[:, 0].view(num_steps, -1), ax, s=25, c="black")
  37. plt.title("Input Layer")
  38. plt.xlabel("Time step")
  39. plt.ylabel("Neuron Number")
  40. plt.show()

大部分尖峰发生在最后一个时间步,此时输入特征低于阈值。从某种意义上说,MNIST 样本的深色背景并不包含有用的信息。 

 2.2 时序信号编码

  1. import torch
  2. import snntorch.spikegen as spikegen
  3. import matplotlib.pyplot as plt
  4. import snntorch.spikeplot as splt
  5. # 定义时序信号
  6. time_steps = 100
  7. input_signal = torch.sin(torch.linspace(0, 10 * 3.1416, time_steps))
  8. input_signal = torch.abs(input_signal)
  9. # 编码时序信号
  10. spike_data = spikegen.latency(input_signal, num_steps=time_steps, tau=5, threshold=0.01, linear=True)
  11. print(spike_data)
  12. # 绘制时序信号和脉冲编码结果
  13. plt.figure(figsize=(10, 5))
  14. # 绘制时序信号
  15. plt.subplot(1, 1, 1)
  16. plt.plot(input_signal.numpy(), label='Input Signal')
  17. plt.title('Input Signal')
  18. plt.xlabel('Time Steps')
  19. plt.ylabel('Amplitude')
  20. plt.legend()
  21. # 绘制脉冲信号
  22. fig = plt.figure(facecolor="w", figsize=(10, 5))
  23. ax = fig.add_subplot(111)
  24. splt.raster(spike_data[:, :].view(time_steps, -1), ax, s=25, c="black")
  25. plt.title("Input Layer")
  26. plt.xlabel("Time step")
  27. plt.ylabel("Neuron Number")
  28. plt.show()

3. 德尔塔调制

        德尔塔调制基于事件驱动的脉冲编码方法。,使用snntorch.delta 函数进行编码。它获取所有时间步长前后特征之间的差值,如果差值为正且大于阈值,则认为包含一个事件,就会产生一个脉冲:

 3.1 图片编码

        德尔塔调制主要用于检测幅度变化,在图像中可以起到一定程度上的边缘提取工作。

 3.2 时序信号编码

  1. import torch
  2. import snntorch.spikegen as spikegen
  3. import matplotlib.pyplot as plt
  4. import snntorch.spikeplot as splt
  5. # 定义时序信号
  6. time_steps = 100
  7. input_signal = torch.sin(torch.linspace(0, 10 * 3.1416, time_steps))
  8. input_signal = torch.abs(input_signal)
  9. # 编码时序信号
  10. spike_data = spikegen.delta(input_signal, threshold=0.3, off_spike=True)
  11. # 绘制时序信号
  12. fig = plt.figure(facecolor="w", figsize=(8, 1))
  13. ax = fig.add_subplot(111)
  14. splt.raster(spike_data, ax, c="black")
  15. plt.title("Input Neuron")
  16. plt.xlabel("Time step")
  17. plt.yticks([])
  18. plt.xlim(0, len(input_signal))
  19. plt.show()

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/352346
推荐阅读
相关标签
  

闽ICP备14008679号