1. 脉冲神经网络的实现方法

随着类脑技术(Neuromorphic Technology)的火热, 投入脉冲神经领域的工程师和学术届的科研人员越来越多, 喜大普奔~~

为了方便脉冲神经网络的研发和工程化, 越来越多的优秀的脉冲神经网络的实现框架也逐渐被开发出来, 但因为行业本身还在埋头发展过程当中, 其实业界没有一个类似于pytorch/tensorflow 一样被当作行业标准的SNN开发框架。

像上篇博文所提到的, 当前脉冲神经网络还依然需要一个稳定的,能充分体现其优势的学习算法。基于学习的难易程度和上手性,这篇博文主要叙述一种使用ANN-SNN转换的方法实现一个脉冲神经网络的方法,这种方法很适合有过深度学习背景的童鞋们,它主要有以下几点优势:

  • 基于成熟的Pytroch框架,易于上手, 安装包简单, 可以让大家专注在研究SNN上
  • 基于BP, 学习方法成熟而且稳定,有大量开源资料, 梯度下降毕竟支撑着整个神经网络。
  • 表现稳定, 基本ANN能成咱SNN也能成,不会因为莫名其妙的算法导致其不工作

当然, 也有劣势:

  • 普遍被DISS: 一点都不接近生物特性 (好用就行吧Hhhhh)
  • 基于rate coding, 导致神经网络中间层产生的脉冲数量过多, 普遍不具有低功耗的特征
  • 转换过程会因为近似估计造成精度损失, 但是普遍也强于用STDP等算法训练的网络

2. Sinabs

这里介绍 Sinabs, 一个以Pytorch为backend的脉冲神经网络框架, 短小精悍,简洁而不简单~

安装:在python3 环境下:

pip install sinabs

3. IAF(Integrate and Fire)神经元

IAF神经元相比于LIF神经元少了一个膜电压的泄露项,表达式如下:
τv˙=R⋅(Isyn+I~bias)\tau \dot{v} = R \cdot \big(I_{syn} + \tilde{I}_{bias}\big) τv˙=R⋅(Isyn​+I~bias​)

当设置神经元模电压阈值为1时, 对于一个固定大小持续输入的突触输入,膜电压变化可以显示为如下图:


IAF是当前脉冲神经元中相对简单的一种模型,它并不具备随着时间泄露模电压的运算项, 所以膜电压在IAF神经元里只会有线性上升和瞬时重置两种变化。 但是它也具备着将相应输入转换为模电压变化并和阈值比较的特性。 每个脉冲神经元随着时间t都具备着其相应的膜电压(或称之为状态–state), 这是脉冲神经元和传统人工神经元最大的区别。

4. ANN-SNN原理

Bodo等人在
Conversion of Continuous-Valued Deep Networks to Efficient Event-Driven Networks for Image Classification
中提出了一套完整可行的ANN-SNN转换框架, Sinabs所依赖的理论也是从这篇文章中变化而来的,这里粗浅的解释一下:

LIF和IAF脉冲神经元其在有限时间窗内输出脉冲的数量或其脉冲发放频率(firing rate)与其所接受到的突触输入电流成线性正比关系。如果简单画出firing rate VS synaptic output的关系的话, 应如下图所示:

没错, 熟悉深度神经网络的同学发现了, 这不是和ReLU一毛一样吗? ANN-SNN转换的可行性就在这里体现了, 我们在训练ANN之后, 只要把ANN里面常用的ReLU激活方程映射在SNN的脉冲频率就可以了。

在此之上, Bodo等大神提出了一系列可以将常用的ANN操作如BN, pooling,转换为event-driven的方式从而提升SNN的可训练性的方法。 同时,在转换过程中, 也有很多小trick可以提升转换网络效率和稳定性的方法, 在此不多做赘述, 感兴趣的同学可以在文章里读到详情。

5. 基于Sinabs框架的手写数字识别

有了上述原理, 我们可以来实操一次手写数字识别任务。 但是, 这次与传统灰度图所不同的是, 我们会直接使用一次事件相机提供的数据。 毕竟因为脉冲神经网络和事件相机天然的契合性, 处理事件数据才是脉冲神经网络该干的事情。

我们这次使用的是Neuromorphic MNIST数据库, 它的来源是:

https://www.garrickorchard.com/datasets/n-mnist

数据完全来源于真实的动态视觉事件相机,并做了相应的数据处理, 它的特点是稀疏异步、离散在时域, 并且有极高的时间分辨率(事件和事件间隔最低可到达ns级别)。

Note: 对于AER数据的预处理本篇文章不会做过多的展示,让我们暂时先focus在SNN本身的实现。如果有需求的话之后会专门写一篇如何处理事件数据的博文。

首先我们要明确的是, 在SNN的仿真过程中, 时间的表示依然是离散的, 我们需要建立起一个以时间步长(time-step) 为基础的仿真网络。 理想情况下, 脉冲神经网络的运算应是完全异步的,没有采样频率的。

举一个比较具体点的例子来说,传统传感器如RGB相机, 一般具有一个固定的采样频率,比如30FPS。那么对于神经网络来说, 它的计算也是对于一帧一帧的图片来运行的。 而事件驱动运算的SNN的每个脉冲神经元的运算都应是相互独立(时间上和空间上)的。

[1] 图: Frame based vs Event-driven processing

依据这两点:

  • 以帧的形式仿真运算
  • 以脉冲数量对应相应的ReLU激活函数
    我们的输入数据将是在一段有限时间窗内的一帧压缩过后的矩阵, 对于N-MNIST数据来说,为2x34x34, 在每个像素上对对应的真值则是相应位置在时间窗内产生的脉冲个数, 通道2则为on/off极性。
    如果可视化出来大概长这样:

那么让我们先来构建一个简单的CNN吧, 使用pytorch的SequentialAPI就很简单:

import torch.nn as nncnn = nn.Sequential(nn.Conv2d(2, 20, 5, 1, bias=False),nn.ReLU(),nn.AvgPool2d(2,2),nn.Conv2d(20, 32, 5, 1, bias=False),nn.ReLU(),nn.AvgPool2d(2,2),nn.Conv2d(32, 128, 3, 1, bias=False),nn.ReLU(),nn.AvgPool2d(2,2),nn.Flatten(),nn.Linear(128, 500, bias=False),nn.ReLU(),nn.Linear(500, 10, bias=False),nn.ReLU()
)

到此为止, 我们还可以完全按照ANN的方法来训练此网络

device = "cuda" if torch.cuda.is_available() else "cpu"
cnn = cnn.to(device) # GPU
optimizer = torch.optim.Adam(ann.parameters(), lr=1e-3)
n_epochs = 1
for n in range(n_epochs):# Iterate over datafor data, target in train_data:data, target = data.to(device), target.to(device) # GPUoutput = cnn(data) # forward pass through the networkoptim.zero_grad()# Add loss to the total lossloss = F.cross_entropy(output, target)# Propagate loss backwardsloss.backward()# Update weightsoptimizer.step()# get the index of the max log-probabilitypred = output.argmax(dim=1, keepdim=True)# Compute the total correct predictionscorrect = pred.eq(target.view_as(pred)).sum().item()
# Save model parameters
torch.save(ann.state_dict(), "ann.pt")

当ANN训练拟合过后, 我们可以使用sinabs的api, 只用一句话就可以将ann转换为snn

from sinabs.from_torch import from_model
input_shape = (2, 34, 34)
sinabs_model = from_model(snn, input_shape=input_shape, add_spiking_output=True)

那么他做的是什么呢? 其实sinabs_model本身还是一个pytorch model,其中包含了analog_modelspiking_model分别对应着原ann模型和转换过后的snn模型。 所训练的卷积层和全连接层的权重是一模一样的, 不一样的只有激活的方式, 在spiking_model 里ReLU 被替换成了一层相对应的SpikingLayer()。这层神经元默认是IAF神经元类型, 在做测试时, 如果你调用sinabs_model.spiking_model(input)那么模型默认是会使用snn进行测试的。 而卷积操作所产生的值, 我们可以理解为输入脉冲在进行脉冲卷积操作后所产生的突触电流,而后经过膜电阻R=1后转变为相应的膜电压变化。

sinabs_model.spiking_model
Sequential((0): Conv2d(2, 20, kernel_size=(5, 5), stride=(1, 1), bias=False)(1): SpikingLayer()(2): AvgPool2d(kernel_size=2, stride=2, padding=0)(3): Conv2d(20, 32, kernel_size=(5, 5), stride=(1, 1), bias=False)(4): SpikingLayer()(5): AvgPool2d(kernel_size=2, stride=2, padding=0)(6): Conv2d(32, 128, kernel_size=(3, 3), stride=(1, 1), bias=False)(7): SpikingLayer()(8): AvgPool2d(kernel_size=2, stride=2, padding=0)(9): Flatten(start_dim=1, end_dim=-1)(10): Linear(in_features=128, out_features=500, bias=False)(11): SpikingLayer()(12): Linear(in_features=500, out_features=10, bias=False)(13): SpikingLayer()
)

这时候我们进行SNN测试的时候, 再使用单纯的一帧就变得非常不realistic了。 因为真正的snn应该接受异步的输入信号, 而不是一段时间累计的脉冲数量。 这时我们通常的做法是将时间步长设得非常小以让原来的一帧变成(T/time_step) 帧, 例如原本我们训练ANN时使用的是50ms一帧的等效压缩帧, 测试时相对应的如果我们选择的time_step 为1ms的话那此测试样本的dimension应是(50, 2 , 34 ,34), 如果是100us则对应(500, 2, 34 ,34)。 理论上来讲, time_step越小则越接近实际的脉冲序列, 最优应是在每个time_step内, 任何一个像素的值∈[0,1]\in [0, 1]∈[0,1]。当然这也会造成运算的时间过长

在此之上, 我们进行网络的测试:

选取测试集的一个sample:

raster, label = dataset_test_spiketrains[54]
print(label)
print(raster.shape)
4
(300, 2, 34, 34)

这里我们选择以1ms为time_step, 可以看倒一个sample的shape是(300, 2, 34, 34) 。在输出时, 我们也会对t维度进行加权, 因为这是一个sample连续的输出结果。

测试

for data, target in tqdm(dataloader_test_spiketrains):sinabs_model.reset_states()data = data[0].to(device)output = sinabs_model(data)# we sum the number of output spikes in time for each sampleoutput = output.sum(axis=0) # (135, 10)# get the index of the max log-probabilitypred = output.argmax() # which neuron spikes most# Compute the total correct predictionscorrect = pred.item() == target.item()acc.append(correct)# let's stop at 200 samples, otherwise it takes too longif len(acc) > 200:break

让我们来看一下network都干了些什么吧:

raster, label = dataset_test_spiketrains[54]# plot it in space: this is the same as the frame,
# we remove the time dimension
plt.title("Input spikes in space")
plt.imshow(raster.sum((0, 1)))
plt.xlabel("X")
plt.ylabel("Y");

这里我们来看一下输入:

plt.title("Input data in time")
plt.pcolormesh(raster.reshape((-1, 2*34*34)).T)
plt.xlabel("Time (ms)")
plt.ylabel("Input neurons (channel, x, y)");

对于输出:

output.T.detach().numpy()

我们所得到的2维矩阵(300, 10)其中300 对应时间, 10对应MNIST的label

因为此SNN是rate-based的, 那么在300个时间步长里, 我们找到发放脉冲最多相对应的那个脉冲神经元,就是网络对此input的输出预测啦。

如果一切没有问题的话, 那么我们会发现SNN的测试准确率会远远 小于 ANN的测试准确率!

这是因为SNN本身在转换过后会有激活值和脉冲数量不对应的情况, 脉冲神经元的输出是整形化的, 比如像ReLU可以产生像2.13324这样的激活值,而脉冲只能是1个, 2个,3个…

在这种情况下, 我们一般会采用增加整个脉冲网络的activity, 因为在正数区的线性关系, 我们可以通过直接按比例增大权重的方式去让ann的激活函数和snn尽量相互匹配。

因篇幅原因, 我们使用Sinabs实现ANN-SNN的方法就先到这里啦, 希望可以帮助到大家。

最后强烈建议阅读sinabs 文档:
https://sinabs.ai

Bodo神的paper:
Rueckauer, B., Lungu, I. A., Hu, Y., Pfeiffer, M., & Liu, S. C. (2017). Conversion of continuous-valued deep networks to efficient event-driven networks for image classification. Frontiers in neuroscience, 11, 682.

[1]https://www.researchgate.net/figure/Comparison-of-frame-based-and-event-based-processing-The-circles-on-the-Frame-based_fig3_342133982

脉冲神经网络-基于IAF神经元的手写数字识别相关推荐

  1. 基于深度学习的手写数字识别、python实现

    基于深度学习的手写数字识别.python实现 一.what is 深度学习 二.加深层可以减少网络的参数数量 三.深度学习的手写数字识别 一.what is 深度学习 深度学习是加深了层的深度神经网络 ...

  2. 基于深度学习的手写数字识别Matlab实现

    基于深度学习的手写数字识别Matlab实现 1.网络设计 2. 训练方法 3.实验结果 4.实验结果分析 5.结论 1.网络设计 1.1 CNN(特征提取网络+分类网络) 随着深度学习的迅猛发展,其应 ...

  3. 基于CNN的MINIST手写数字识别项目代码以及原理详解

    文章目录 项目简介 项目下载地址 项目开发软件环境 项目开发硬件环境 前言 一.数据加载的作用 二.Pytorch进行数据加载所需工具 2.1 Dataset 2.2 Dataloader 2.3 T ...

  4. 基于深度学习的手写数字识别算法Python实现

    摘 要 深度学习是传统机器学习下的一个分支,得益于近些年来计算机硬件计算能力质的飞跃,使得深度学习成为了当下热门之一.手写数字识别更是深度学习入门的经典案例,学习和理解其背后的原理对于深度学习的理解有 ...

  5. 【MLP实战】001:基于Minist数据集的手写数字识别

    本文又是一篇基于Minist数据集的手写数字识别. 首先,mnist数据集: 链接:https://pan.baidu.com/s/1z7R7_jnDKZm9F7M6n8hiIw 提取码:rn8z 首 ...

  6. Python基于深度学习的手写数字识别

    Python基于深度学习的手写数字识别 1.代码的功能和运行方法 2. 网络设计 3.训练方法 4.实验结果分析 5.结论 1.代码的功能和运行方法 代码可以实现任意数字0-9的识别,只需要将图片载入 ...

  7. 基于K210的MNIST手写数字识别

    基于K210的MNIST手写数字识别 项目已开源链接: Github. 硬件平台 采用Maixduino开发板 在sipeed官方有售 软件平台 使用MaixPy环境进行单片机的编程 官方资源可在这里 ...

  8. 基于SVM技术的手写数字识别

    老师常说,在人工智能未发展起来之前,SVM技术是一统江湖的,SVM常常听到,但究竟是什么呢?最近研究了一下基于SVM技术的手写数字识别.你没有看错,又是手写数字识别,就是喜欢这个手写数字识别,没办法( ...

  9. 基于Kera框架的手写数字识别

    基于Kera框架的手写数字识别 一.配置环境 二.具体步骤 2.1 导入数据集 2.2 创建网络图层和网络结构 2.3 定义反向传播函数与优化函数 2.4 数据集和测试集归一化 2.5 标签制作 三整 ...

  10. 课程设计(毕业设计)—基于机器学习KNN算法手写数字识别系统—计算机专业课程设计(毕业设计)

    机器学习KNN算法手写数字识别系统 下载本文手写数字识别系统完整的代码和课设报告的链接(或者可以联系博主koukou(壹壹23七2五六98),获取源码和报告):https://download.csd ...

最新文章

  1. 求s = k ! + n ! / m !的值
  2. 【 MATLAB 】如何产生一个均值和方差可控的正态分布矩阵(randn)?
  3. markdown 创建表格
  4. Lightoj 1123 - Trail Maintenance(最小增量生成树)
  5. 有哪些适合新手练手的C/C++项目?
  6. c++语言自定义操作符,C++11新特性之自定义字面量
  7. java 基本数据类型及自动类型转换
  8. 单线程和多线程的区别
  9. N划分成若干个奇正整数之和的分法有多少种---动态规划
  10. 【LA3942】Remember the World(初识前缀树Trie----模版题 + dp)
  11. 通过电脑从Ovi商店下载软件并安装
  12. python done()什么意思_done什么意思
  13. 终于发现路由器里的广告秘密
  14. Word文档中如何添加带打勾的方框
  15. 1186: 零起点学算法93——改革春风吹满地(C)
  16. java定义一个日期类 包括年 月 日_【说明】 设计一个日期类Date包括年、月、日等私有数据成员。要求实现日期..._考试资料网...
  17. macOS无法验证此App不包含恶意软件
  18. TB,GB,MB,KB,Byte字节,bit位 如何换算?
  19. [转]kaldi ASR: DNN训练
  20. Dockerfile构建镜像并发布镜像

热门文章

  1. AGI:走向通用人工智能的【生命学哲学科学】第一篇——生命、意识、五行、易经、量子
  2. java 二进制转换成图片_java二进制转换为图片
  3. 统计学 参数估计 之 总体均值的估计
  4. HDU 1402(FFT,NNT)
  5. Java算法:经纬度转换 将百度转为高德经纬度
  6. 计算标准累积正态分布_神说要有正态分布,于是就有了正态分布。
  7. 克隆出错fatal: unable to access ‘https://github.com/‘: OpenSSL SSL_connect:
  8. 手把手教你领取永久免费服务器
  9. 颜值大比拼,用数据告诉你中国哪里美女多?
  10. 易语言excel内容查找助手