从FPGA说起的深度学习(四)
这是新的系列教程,在本教程中,我们将介绍使用 FPGA 实现深度学习的技术,深度学习是近年来人工智能领域的热门话题。
在本教程中,旨在加深对深度学习和 FPGA 的理解。
用 C/C++ 编写深度学习推理代码
高级综合 (HLS) 将 C/C++ 代码转换为硬件描述语言
FPGA 运行验证

在上一篇文章中,我们用C语言实现了一个卷积层,并查看了结果。在本文中,我们将实现其余未实现的层:全连接层、池化层和激活函数 ReLU。
每一层的实现
全连接层
全连接层是将输入向量X乘以权重矩阵W,然后加上偏置B的过程。下面转载第二篇的图,能按照这个图计算就可以了。

全连接层的实现如下。
void linear(const float *x, const float* weight, const float* bias,int64_t in_features, int64_t out_features, float *y) {for (int64_t i = 0; i < out_features; ++i) {float sum = 0.f;for (int64_t j = 0; j < in_features; ++j) {sum += x[j] * weight[i * in_features + j];}y[i] = sum + bias[i];}
}
该函数的接口和各个数据的内存布局如下。
考虑稍后设置 PyTorch 参数,内存布局与 PyTorch 对齐。
输入
x: 输入图像。shape=(in_features)
weight: 权重因子。shape=(out_features, in_features)
bias: 偏置值。shape=(out_features)
输出
y: 输出图像。shape=(out_features)
参数
in_features: 输入顺序
out_features: 输出顺序
在全连接层中,内部操作数最多为out_channels * in_channels一个,对于典型参数,操作数远低于卷积层。
另一方面,关注权重因子,卷积层为shape=(out_channels, in_channels, ksize, ksize),而全连接层为shape=(out_features, in_features)。例如,如果层从卷积层变为全连接层,in_features = channels * width * height则以下关系成立。width, height >> ksize考虑到这一点,在很多情况下,全连接层参数的内存需求大大超过了卷积层。
由于FPGA内部有丰富的SRAM缓冲区,因此擅长处理内存访问量大和内存数据相对于计算总量的大量复用。单个全连接层不会复用权重数据,但是在视频处理等连续处理中,这是一个优势,因为要进行多次全连接。
另一方面,本文标题中也提到的边缘环境使用小型FPGA,因此可能会出现SRAM容量不足而需要访问外部DRAM的情况。如果你有足够的内存带宽,你可以按原样访问它,但如果你没有足够的内存带宽,你可以在参数调整和训练后对模型应用称为剪枝和量化的操作。
池化层
池化层是对输入图像进行缩小的过程,这次使用的方法叫做2×2 MaxPooling。在这个过程中,取输入图像2x2区域的最大值作为输出图像一个像素的值。这个看第二张图也很容易理解,所以我再贴一遍。

即使在池化层,输入图像有多个通道,但池化过程本身是针对每个通道独立执行的。因此,输入图像中的通道数和输出图像中的通道数在池化层中始终相等。
池化层的实现如下所示:
void maxpool2d(const float *x, int32_t width, int32_t height, int32_t channels, int32_t stride, float *y) {for (int ch = 0; ch < channels; ++ch) {for (int32_t h = 0; h < height; h += stride) {for (int32_t w = 0; w < width; w += stride) {float maxval = -FLT_MAX;for (int bh = 0; bh < stride; ++bh) {for (int bw = 0; bw < stride; ++bw) {maxval = std::max(maxval, x[(ch * height + h + bh) * width + w + bw]);}}y[(ch * (height / stride) + (h / stride)) * (width / stride) + w / stride] = maxval;}}}
}
这个函数的接口是:
此实现省略了边缘处理,因此图像的宽度和高度都必须能被stride整除。
输入
x: 输入图像。shape=(channels, height, width)
输出
y: 输出图像。shape=(channels, height/stride, width/stride)
参数
width: 图像宽度
height: 图像高度
stride:减速比
ReLU
ReLU 非常简单,因为它只是将负值设置为 0。
void relu(const float *x, int64_t size, float *y) {for (int64_t i = 0; i < size; ++i) {y[i] = std::max(x[i], .0f);}
}
由于每个元素的处理是完全独立的,x, y因此未指定内存布局。
硬件生成
到这里为止的内容,各层的功能都已经完成了。按照上一篇文章中的步骤,可以确认这次创建的函数也产生了与 libtorch 相同的输出。此外,Vivado HLS 生成了一个通过 RTL 仿真的电路。从这里开始,我将简要说明实际生成了什么样的电路。
如果将上述linear函数原样输入到 Vivado HLS,则会发生错误。这里,将输入输出设为指针->数组是为了决定在电路制作时用于访问数组的地址的位宽。另外,in_features的值为778=392,out_将features的值固定为32。这是为了避免Vivado HLS 在循环次数可变时输出性能不佳。
static const std::size_t kMaxSize = 65536;void linear_hls(const float x[kMaxSize], const float weight[kMaxSize], const float bias[kMaxSize], float y[kMaxSize]) {dnnk::linear(x, weight, bias, 7*7*8, 32, y);
}
linear_hls函数的综合报告中的“性能估计”如下所示:

在Timing -> Summary中写入了综合时指定的工作频率,此时的工作频率为5.00 ns = 200MHz。重要的是 Latency -> Summary 部分,它描述了执行此函数时的周期延迟(Latency(cycles))和实时延迟(Latency(absolute))。看看这个,我们可以看到这个全连接层在 0.566 ms内完成。
在 Latency -> Detail -> Loop 列中,描述了每个循环的一次迭代所需的循环次数(Iteration Latency)和该循环的迭代次数(Trip Count)。延迟(周期)包含Iteration Latency * Trip Count +循环初始化成本的值。Loop 1 是out_features循环到loop 1.1 in_features。在Loop1.1中进行sum += x[j] * weight[i * in_features + j]; 简单计算会发现需要 9 个周期才能完成 Loop 1.1 所做的工作。
使用HLS中的“Schedule Viewer”功能,可以更详细地了解哪些操作需要花费更多长时间。下图横轴的2~10表示Loop1.1的处理内容,大致分为x,weights等的加载2个循环,乘法(fmul)3个循环,加法(fadd)4个循环共计9个循环。

在使用 HLS 进行开发期间通过添加#pragma HLS pipeline指令,向此代码添加优化指令以指示它创建高效的硬件。与普通的 FPGA 开发类似,运算单元的流水线化和并行化经常用于优化。通过这些优化,HLS 报告证实了加速:
流水线:减少迭代延迟(min=1)
并行化:减少行程次数,删除循环
正如之前也说过几次的那样,这次的课程首先是以FPGA推理为目的,所以不会进行上述的优化。有兴趣进行什么样的优化的人,可以参考以下教程和文档。
教程
https://github.com/Xilinx/HLS-Tiny-Tutorials
文档
https://www.xilinx.com/support/documentation/sw_manuals/xilinx2019_2/ug902-vivado-high-level-synthesis.pdf
最后,该函数的接口如下所示。

由于本次没有指定接口,所以数组接口如x_ 等ap_memory对应FPGA上可以1个周期读写的存储器(BRAM/Distributed RAM)。在下一篇文章中,我们将连接每一层的输入和输出,但在这种情况下,我们计划连接 FPGA 内部的存储器作为每一层之间的接口,如本例所示。
总结
在本文中,我们实现了全连接层、池化层和 ReLU。现在我们已经实现了所有层,我们将在下一篇文章中组合它们。之后我们会实际给MNIST数据,确认我们可以做出正确的推论。
从FPGA说起的深度学习(三)
从FPGA说起的深度学习(二)
从FPGA说起的深度学习(一)
相关文章:

从FPGA说起的深度学习(四)
这是新的系列教程,在本教程中,我们将介绍使用 FPGA 实现深度学习的技术,深度学习是近年来人工智能领域的热门话题。在本教程中,旨在加深对深度学习和 FPGA 的理解。用 C/C 编写深度学习推理代码高级综合 (HLS) 将 C/C 代码转换为硬…...

pytorch入门7--自动求导和神经网络
深度学习网上自学学了10多天了,看了很多大神的课总是很快被劝退。终于,遇到了一位对小白友好的刘二大人,先附上链接,需要者自取:https://b23.tv/RHlDxbc。 下面是课程笔记。 一、自动求导 举例说明自动求导。 torch中的…...
QT 之wayland 事件处理分析基于qt5wayland5.14.2
1. Qt wayland 初始化 接收鼠标/案件,触摸屏等事件事件 QWaylandNativeInterface : public QPlatformNativeInterface 在QWaylandNativeInterface 继承qpa 接口类QPlatformNativeInterface; 1.1 初始化鼠标: void *QWaylandNativeInterface::nativeR…...
【this 和 super 的区别】
在 Java 中,this 和 super 都是关键字,表示当前对象和父类对象。 this 关键字可以用于以下几种情况: 引用当前对象的成员变量,方法和构造方法,用于区分局部变量和成员变量重名的情况; 调用当前类的另外一…...

K8s:Monokle Desktop 一个集Yaml资源编写、项目管理、集群管理的 K8s IDE
写在前面 Monokle Desktop 是 kubeshop 推出的一个开源的 K8s IDE相关项目还有 Monokle CLI 和 Monokle Cloud相比其他的工具,Monokle Desktop 功能较全面,涉及 k8s 管理的整个生命周期博文内容:Monokle Desktop 下载安装,项目管理…...

自动化测试实战篇(8),jmeter并发测试登录接口,模拟从100到1000个用户同时登录测试服务器压力
首先进行使用jmeter进行并发测试之前就需要搞清楚线程和进程的区别还需要理解什么是并发、高并发、并行。还需要理解高并发中的以及老生常谈的,TCP三次握手协议和TCP四次握手协议**TCP三次握手协议指:****TCP四次挥手协议:**进入Jmeter&#…...

ATTCK v12版本战术实战研究—持久化(二)
一、前言前几期文章中,我们介绍了ATT&CK中侦察、资源开发、初始访问、执行战术、持久化战术的知识。那么从前文中介绍的相关持久化子技术来开展测试,进行更深一步的分析。本文主要内容是介绍攻击者在运用持久化子技术时,在相关的资产服务…...
python函数式编程
1 callable内建函数判断一个名字是否为一个可调用函数 >>> import math >>> x 1 >>> y math.sqrt >>> callable(x) False >>> callable(y) True 2 记录函数(文档字符串) >>> def square(x): …...
3.linux下安装mysql
1.安装前的环境准备 查看是否安装过mysql 首先检测Linux操作系统中是否安装了MySQL: # rpm -qa | grep -i mysql 卸载安装包 如果有信息出现,则进行删除,命令如下: # rpm -e --nodeps 包名 删除老版本mysql的开发头文件和…...
17、MySQL分库分表,原理实战
MySQL分库分表,原理实战 1.MyCAT分布式架构入门及双主架构1.1 主从架构1.2 MyCAT安装1.3 启动和连接1.4 配置文件介绍2.MyCAT读写分离架构2.1 架构说明2.2 创建用户2.3 schema.xml2.4 连接说明2.5 读写测试2.6 当前是单节点3.MyCAT高可用读写分离架构3.1 架构说明3.3 schema.xm…...

【C++的OpenCV】第九课-OpenCV图像常用操作(六):图像形态学-阈值的概念、功能及操作(threshold()函数))
目录一、阈值(thresh)的概念二、阈值在图形学中的用途三、阈值的作用和操作3.1 在OpenCV中可以进行的阈值操作3.2 操作实例3.2.1 threshold()函数介绍3.2.2 实例3.2.3 结果上节课的内容(作者还是鼓励各位同学按照顺序进行学习哦)&…...

[Java代码审计]—MCMS
环境搭建 MCMS 5.2.4:https://gitee.com/mingSoft/MCMS/tree/5.2.4/利用 idea 打开项目 创建数据库 mcms,导入 doc/mcms-5.2.8.sql 修改 src/main/resources/application-dev.yml 中关于数据库设置参数 启动项目登录后台 http://localhost:8080/ms/l…...
《程序员面试金典(第6版)》面试题 01.08. 零矩阵
题目描述 编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。 示例1: 输入:[1, 2, 3, 3, 2, 1] 输出:[1, 2, 3] -示例2: 输入:[1, 1, 1, 1, 2] 输出:[1, 2] 提示: 链表长度在[0, 20000]范…...
初识 Python
文章目录简介用途解释器命令行模式交互模式输入和输出简介 高级编程语言,解释型语言代码在执行时会逐行翻译成 CPU 能理解的机器码代码精简,但运行速度慢基础代码库丰富,还有大量第三方库代码不能加密 用途 网络应用工具软件包装其他语言开…...
常用sql语句分享
SELECT COUNT(DISTINCT money) FROM ac_association_course;#COUNT() 函数返回匹配指定条件的行数SELECT AVG(money) FROM ac_association_course;#AVG 函数返回数值列的平均值。NULL 值不包括在计算中SELECT id FROM ac_association_course order by id desc limit 1;#返回最大…...

极狐GitLab DevSecOps 为企业许可证安全合规保驾护航
本文来自: 小马哥 极狐(GitLab) 技术布道师 开源许可证是开源软件的法律武器,是第三方正确使用开源软件的安全合规依据。 根据 Linux 发布的 SBOM 报告显示,98% 的企业都在使用开源软件(中文版报告详情)。随着开源使用…...
后端程序员的前端基础-前端三剑客之HTML
文章目录1 HTML简介1.1 什么是HTML1.2 HTML能做什么1.3 HTML书写规范2 HTML基本标签2.1 结构标签2.2 排版标签2.3 块标签2.4 基本文字标签2.5 文本格式化标签2.6 标题标签2.7 列表标签(清单标签)2.8 图片标签2.9 链接标签2.10 表格标签3 HTML表单标签3.1 form元素常用属性3.2 i…...

VS2019加载解决方案时不能自动打开之前的文档(回忆消失)
✏️作者:枫霜剑客 📋系列专栏:C实战宝典 🌲上一篇: 错误error c3861 :“_T“:找不到标识符 逐梦编程,让中华屹立世界之巅。 简单的事情重复做,重复的事情用心做,用心的事情坚持做; 文章目录前言一、问题描…...

ConcurrentHashMap-Java八股面试(五)
系列文章目录 第一章 ArrayList-Java八股面试(一) 第二章 HashMap-Java八股面试(二) 第三章 单例模式-Java八股面试(三) 第四章 线程池和Volatile关键字-Java八股面试(四) 提示:动态每日更新算法题,想要学习的可以关注一下 文章目录系列文章目录一、…...

互联网衰退期,测试工程师35岁的路该怎么走...
国内的互联网行业发展较快,所以造成了技术研发类员工工作强度比较大,同时技术的快速更新又需要员工不断的学习新的技术。因此淘汰率也比较高,超过35岁的基层研发类员工,往往因为家庭原因、身体原因,比较难以跟得上工作…...

51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...

DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...