从FPGA说起的深度学习(三)

news2024/11/18 13:57:23


cf1ef8dac7142870fc1a2913f5dd3e4d.png

这是新的系列教程,在本教程中,我们将介绍使用 FPGA 实现深度学习的技术,深度学习是近年来人工智能领域的热门话题。

在本教程中,旨在加深对深度学习和 FPGA 的理解。

  • 用 C/C++ 编写深度学习推理代码

  • 高级综合 (HLS) 将 C/C++ 代码转换为硬件描述语言

  • FPGA 运行验证

046a23553168fec9bdff271f0cc242a5.png

在上一篇文章中,我们在 MNIST 数据集上创建并训练了一个网络模型。从本文开始,为了在 FPGA 上运行推理处理,我们将首先用 C++ 编写推理处理代码。

在这篇 C++ 实现的第一篇文章中,我们开始针对卷积层的 C++ 实现。具体内容是(1)卷积层的实现,(2)运算校验(C验证,C/RTL协同验证)(就是HLS的流程)。

卷积层实现

在上一篇文章中,我解释了卷积层是对图像的过滤过程,但是并没有解释输入输出通道如何处理,过滤时图像的边缘处理等。由于本文旨在实现层面的理解,因此我将详细介绍这些要点。

处理 I/O 通道

在图像处理中,对RGB输入图像进行噪声去除等滤波处理,并频繁地进行RGB图像的处理。在这种情况下,卷积过程往往是针对每个通道(R/G/B)独立完成的,输入的G/B通道值不影响输出的R通道结果。

41032b22403432a7b9d85945e5fbdb63.png每通道独立卷积

另一方面,在卷积层中执行的卷积过程中,所有输入通道的值影响每个输出通道。因此,对于输出图像的每个像素(输出通道,Y坐标,X坐标),所有输入通道和周围的像素区域都会参与计算,导致计算量非常大。

1554121e90b39bec9184c6695f27c1e0.png使用所有通道的卷积

另外,如上所述每个通道独立卷积的卷积层称为Depthwise Convolution。

这通常用于减少计算量的网络模型,例如MobileNet(https://arxiv.org/abs/1704.04861)。

图像边缘处理

在对图像进行卷积处理时,图像边缘的处理往往是一个问题。

由于卷积过程在计算某个像素时使用了周围像素,因此对于没有周围像素的像素,例如图像边缘的像素,就无法获取周围像素。

卷积神经网络主要通过以下两种方式处理边缘像素。

  • 无填充:输出图像减少了输入图像的卷积区域。

  • 补零:将输入图像预先用卷积区域扩展,用零填充该区域,对原始输入图像进行卷积处理。

没有填充的卷积的图形表示如下所示:在这种情况下,输出图像将是比输入图像小一个滤波器尺寸的区域(橙色部分)。如果内核大小为 3(中心像素 +/-1),则输出图像大小在宽度和高度上都将为 -2,因为图像之外的 1 个像素是无法进行卷积的区域。

6345ba62af8084cda6ba2383668d03f7.png无填充卷积:输出图像缩小

接下来,零填充的图形表示如下所示。在这个例子中,预先在输入图像的外部添加了一个值为0的区域(灰色区域),进行卷积,这样就不会出现图像缩小现象。如果内核大小为 3,则带填充的输入图像大小在宽度和高度上均为 +2,因为 +1 像素将添加到屏幕外部且值为零。

91257b3dd31351ec9553b17cdd90ebdd.png零填充卷积:输出图像大小保持不变

在我们的模型中,我们在所有卷积层中使用零填充。

C代码

如果根据目前为止的解释用 C 语言实现卷积过程,它将类似于下面的代码。

void conv2d(const float* x, const float* weight, const float* bias, int32_t width, int32_t height,
             int32_t in_channels, int32_t out_channels, int32_t ksize, float* y) {
   for (int32_t och = 0; och < out_channels; ++och) {
     for (int32_t h = 0; h < height; ++h) {
       for (int32_t w = 0; w < width; ++w) {
         float sum = 0.f;

         for (int32_t ich = 0; ich < in_channels; ++ich) {
           for (int32_t kh = 0; kh < ksize; ++kh) {
             for (int32_t kw = 0; kw < ksize; ++kw) {
               int32_t ph = h + kh - ksize/2;
               int32_t pw = w + kw - ksize/2;

               // zero padding
               if (ph < 0 || ph >= height || pw < 0 || pw >= width) {
                 continue;
               }

               int64_t pix_idx = (ich * height + ph) * width + pw;
               int64_t weight_idx = ((och * in_channels + ich) * ksize + kh) * ksize + kw;

               sum += x[pix_idx] * weight[weight_idx];
             }
           }
         }

         // add bias
         sum += bias[och];

         y[(och * height + h) * width + w] = sum;
       }
     }
   }
 }

此函数的解释如下所示:

  • 输入

-- x: 输入图像。shape=(in_channels, height, width)

-- weight: 权重因子。shape=(out_channels, in_channels, ksize, ksize)

-- bias: 偏置值。shape=(out_channels)

  • 输出

-- y: 输出图像。shape=(out_channels, height, width)

  • 参数:-- width: 输入/输出图像的宽度

-- height: 输入/输出图像高度

-- in_channels:输入图像的通道数

-- out_channels:输出图像的通道数

-- ksize: 内核大小

每个输入/输出的内存布局shape=(...)如表格所示,但float x[in_channels][height][width];将其视为定义为三维数组。

卷积层的处理是一个6级循环。第一个三级循环确定输出图像上的位置,随后的三级循环对该位置执行卷积操作。

零填充在第 24-26 行完成。由于实际创建零填充输入图像是低效的,所以零填充是通过在访问图像外部时不参与乘积之和来实现的。

第31行是卷积过程中的积和运算部分,这个积和运算out_channels * height * width * in_channels * ksize * ksize进行了两次。这个卷积过程的操作数量非常大,在很多情况下,卷积层支配着卷积神经网络的执行时间。这就是为什么计算单元比 CPU 多的 GPU 和 FPGA 更适合处理神经网络。

第37行是偏差处理部分。到目前为止,我还没有触及什么是偏差处理,但正如我在这里所写的那样,它是一个简单地对输出值进行偏移的过程。这种偏差处理在输入通道/内核大小 (Y,X) 循环之外,因此处理步骤的数量非常微不足道。

运算检查

作为对上一节创建的函数运行的确认,conv2d我们将比较结果是否足够接近在 PyTorch 的 C++ API (libtorch) 上执行的卷积计算。

每个测试包括以下两个步骤。

  • C. 验证

  • C/RTL 协同验证

1、C 验证类似于正常的软件开发,gcc只是用通用的编译器编译源代码并检查结果。

2、C/RTL协同验证是使用AMD-Xilinx提供的高阶综合工具Vitis HLS进行验证。对于此验证,HLS 首先将 C 源代码转换为 Verilog HDL 等 RTL。然后在 Vivado 中对生成的 RTL 执行功能仿真。

在这个逻辑仿真中,将一个类似于C验证的数据序列输入到创建的电路中,确认输出结果是否正确。

本节以后的内容将以运行创建的源代码的形式进行说明。

源代码将在后面发布。

运行环境

运行环境面向 Linux 机器。不支持 Windows/Mac 操作系统。此外,由于预装gcc版本,该发行版针对 Ubuntu 18.04。难以自行准备运行环境的朋友,看看就行。

需要以下工具。

  • Vivado  2020.2(推荐 2019.2)

  • cmake >= 3.11

cmake比较麻烦,因为它需要的版本比apt等包管理器可以安装的版本高,但是可以下载预构建的二进制文件(cmake--Linux-x86_64.tar.gz)。

C. 验证

测试代码/tests/ref/conv2d.cc的使用,我不会在本文中详细介绍,但测试将是一个正常的随机测试。

可以按照以下步骤构建代码。请将 -DVIVADO_HLS_ROOT 的值相应地替换为安装的 Vivado 的路径。

$ mkdir <dnn-kernel>/build
$ cd <dnn-kernel>/build
$ cmake .. -DVIVADO_HLS_ROOT=/tools/Xilinx/Vivado/2022.2
$ cmake --build .

使用以下命令进行测试:如果没有任何错误,那它就是成功的。

$ ctest -V -R "conv2d_ref"

C/RTL 协同验证

运行以下命令以使用 HLS 启动 C/RTL 协同验证。大约需要 5 分钟。

$ ctest -V -R "conv2d_hls_cosim"

当执行 C/RTL 协同验证时,会自动创建一个 HLS 项目文件,因此可以使用它来检查高级综合和 RTL 仿真波形的结果。

要检查这一点,请使用以下命令启动 HLS:

$ vitis_hls &

HLS 打开后,单击“打开项目”,如下所示,导航到/build/tests/hls/conv2d/conv2d_hls_cosim目录并单击“确定”。

79a36fb7f5a8de926e07486a70ee3011.png

然后,HLS 综合报告将显示如下屏幕所示。

从此报告中,可以看到从 Performance Estimates 列创建的电路的估计性能,以及从 Utilization Estimates 看到在目标设备上实施时的估计资源使用情况。

点击顶部红框包围的区域,可以看到仿真的波形。

4e49c10d0d9c01e0414d40bf9511c325.png

波形如下所示,可以看出可以通过某种方式读取到该值,大概2000.00ns就能输出y的第一个值。

fbf02fbaedaecb1152e4111d4ccb41a2.png

通过这种方式,我们能够创建一个逻辑电路,该逻辑电路使用  HLS 执行卷积层计算,而无需特别注意 HW。

然而,由于这个电路根本没有调整,它的设计只是实现功能,在后续会对此进行优化。

总结

在这篇文章中,用 C++ 实现了一个卷积层并确认了它的运行。我们还在这个 C++ 实现上使用 HLS 进行了高级综合,并确认它在 C/RTL 协同验证中没有任何问题。

在下一篇文章中,我们将用 C++ 实现其余两层(池化层、全连接层)和 ReLU。之后,我们会结合所有实现的层,为MNIST创建和验证一个模型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/349804.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux下安装elasticsearch步骤

linux下安装elasticsearch步骤&#xff1a; 1、Elasticsearch的下载&#xff08;选择7.8.0&#xff09; 1.1、elasticsearch国内社区&#xff1a; https://elasticsearch.cn/1.2、elasticsearch官网地址&#xff1a; https://www.elastic.co/cn/elasticsearch/1.3、elastic…

从零实现Web服务器(二): 线程池以及线程池的作用,Get和Post的区别,项目中如何编写数据库连接池,定时器优化非活跃连接

文章目录一、线程池以及线程池的作用二、手写线程池三、Get和Post的区别四、如何编写数据库连接池五、定时器优化非活跃连接5.1. 基于排序链表实现。5.2. 基于小根堆实现。5.3. 基于红黑树实现。5.4. 基于时间轮实现。5.4.1 单时间轮实现5.4.2 多时间轮实现一、线程池以及线程池…

JavaScript 入门教程||javascript 简介||JavaScript 用法

javascript 简介JavaScript 是互联网上最流行的脚本语言&#xff0c;这门语言可用于 HTML 和 web&#xff0c;更可广泛用于服务器、PC、笔记本电脑、平板电脑和智能手机等设备。JavaScript 是脚本语言JavaScript 是一种轻量级的编程语言。JavaScript 是可插入 HTML 页面的编程代…

C++之lambda函数(匿名函数)

lambda函数简介lambda函数是C11标准新增的语法&#xff0c;也称为lambda表达式或匿名函数。lambda函数的特点是&#xff1a;距离近、简洁、高效和功能强大。优点声明式编程风格&#xff1a;就地匿名定义目标函数或函数对象&#xff0c;有更好的可读性和可维护性。简洁&#xff…

webspider_20230216

下载网页源代码 分析下规则 抓取咱们需要的信息 仅限于合法的行为&#xff0c;仅仅提供自动化&#xff0c;省得手工去获取图片、手机、邮箱等信息

Spring Cloud是什么?怎么理解Spring Cloud?

简介Spring Cloud项目的官方网址&#xff1a;https://projects.spring.io/spring-cloud/ Spring Cloud 并不是一个项目&#xff0c;而是一组项目的集合。在 Spring Cloud中包含了很多的子项目&#xff0c;每一个子项目都是一种微服务开发过程中遇到的问题的一种解决方案。它利…

机场航站楼告别人工巡检,提高空间安全性

机场是现代化民航基础设施体系&#xff0c;加快数字化转型、建设更高水平的智慧城市已成为城市竞争的新赛道。图扑软件基于 HT 引擎为国内民航数字化转型支撑可视化服务提供有力的一环&#xff0c;融合物联网解决方案&#xff0c;打造适配场景的智慧机场三维可视化解决方案&…

面试题整理

面试题整理 一、Java基础 1、Java 语言有哪些特点 简单易学&#xff1b; 面向对象&#xff08;封装&#xff0c;继承&#xff0c;多态&#xff09;&#xff1b; 平台无关性&#xff08; Java 虚拟机实现平台无关性&#xff09;&#xff1b; 支持多线程&#xff08; C 语言…

#Paper Reading# Improving Language Understanding by Generative Pre-Training

论文题目: Improving Language Understanding by Generative Pre-Training 论文地址: https://www.cs.ubc.ca/~amuham01/LING530/papers/radford2018improving.pdf 论文发表于: OpenAI 2018 论文所属单位: OpenAI 论文大体内容&#xff1a; 本文主要提出了GPT&#xff08;Gene…

【SQL server】视图和索引的创建与管理

本实验数据来源课参照一下本专栏文章&#xff1a;【SQL server】进行简单查询分组、连接查询子查询和汇总&#xff08;含teaching数据库创建及实验拓展&#xff09;_Deep-sea shark的博客-CSDN博客_sql 分组汇总在SSMS中创建视图视图是一张虚表&#xff0c;数据库中只存储视图的…

关于git仓库的一些使用

配置多个ssh-key 1.生成不同的key名 如github key ssh-keygen -t rsa -C "exampleemail.com" -f ~/.ssh/github_id-rsa如gitlab key ssh-keygen -t rsa -C "examlpe企业邮箱.com" -f ~/.ssh/gitlab_id-rsa创建完成后的 macbookMacBookProdeMacBook-Pr…

STL——容器适配器、deque

一、容器适配器 1.适配器 适配器是一种设计模式&#xff08;设计模式是一套被反复使用的、多数人所知晓的、经过分类编目的、代码设计经验的总结&#xff09;&#xff0c;该种模式是将一个类的接口转换成客户希望的另外一个接口。 2.STL标准库中stack和queue的底层结构 stack…

数据结构与算法(Java版) | 就让我们来看看几个实际编程中遇到的问题吧!

上一讲&#xff0c;我给大家简单介绍了一下数据结构&#xff0c;以及数据结构与算法之间的关系&#xff0c;照理来说&#xff0c;接下来我就应该要给大家详细介绍线性结构和非线性结构了&#xff0c;但是在此之前&#xff0c;我决定还是先带着大家看几个实际编程中遇到的问题&a…

UE4 编写着色器以及各种宏的理解

参考链接&#xff1a;如何为 UE4 添加全局着色器&#xff08;Global Shaders&#xff09; - Unreal Enginehttps://docs.unrealengine.com/5.1/zh-CN/adding-global-shaders-to-unreal-engine/如何为 UE4 添加全局着色器&#xff08;Global Shaders&#xff09; - Unreal Engin…

睡眠影响寿命,这几个睡眠习惯赶紧改掉!

我们知道&#xff0c;现在睡眠不足已经成为普遍问题&#xff0c;但你知道睡眠的时长会影响寿命吗&#xff1f;熬夜对身体不好&#xff0c;已是老生常谈。但睡得过早&#xff0c;也可能影响寿命&#xff01;2021年《睡眠医学》杂志一项针对21个国家11万名参与者的研究中发现&…

重生之我是赏金猎人-SRC漏洞挖掘(十)-某大厂从废弃sso登陆口到多思路fuzz获取各地高管信息

0x01 前言 https://github.com/J0o1ey/BountyHunterInChina 欢迎亲们点个star 作者Catm78sec 前期通过灯塔 ffuf oneforall 等工具组合进行子域名收集&#xff0c;得到目标站点&#xff0c;漏洞挖掘中多次踩坑成功get腾讯某后台 0x02 渗透日常——单点登录 目标URL&…

【vcpkg】cpprestsdk之64位编译链接及踩坑

▒ 目录 ▒&#x1f6eb; 问题描述1️⃣ 多版本vs报错指定VS路径2️⃣ error LNK2001: 问题排查通过IDA打开lib文件&#xff0c;确认导出内容查看源码增加参数--editable&#xff0c;重新编译3️⃣ error LNK2001: 外部符号__imp_?close_...去除__imp_&#x1f6ec; 结论vcpkg…

浅谈估值模型:从Grinold Kroner(GK)模型看投资的本质

摘要及声明 1&#xff1a;本文主要介绍Grinold Kroner(GK)模型的运用&#xff0c;并以上证指数为例实现一个GK模型&#xff1b; 2&#xff1a;本文主要为理念的讲解&#xff0c;模型也是笔者自建&#xff0c;文中假设与观点是基于笔者对模型及数据的一孔之见&#xff0c;若有…

buffer和cache的区别

一&#xff0c;计算机硬件组成 计算机硬件组成&#xff1a;CPU&#xff0c;存储器&#xff0c;输入输出设备&#xff08;I/O&#xff09;&#xff0c;其他&#xff08;主板&#xff0c;电源等&#xff09; CPU&#xff1a;运算器&#xff0c;控制器 存储器&#xff1a;内部存储…

蓝桥云课-声网编程赛(声网编程竞赛7月专场)题解

比赛题目快速链接&#xff1a;https://www.lanqiao.cn/contests/lqENT02/challenges/ 让时钟转起来&#xff08;考点&#xff1a;css&#xff1a;transform&#xff09; // index.js function main() {// 题解前理解一个东西&#xff1a;// 时针每过一小时&#xff0c;转30 原…