FINN(三)详细说明以及使用

一、入门

1. 如何使用FINN编译器

目前,最好将FINN编译器视为编译器基础结构,而不是像gcc这样的完整编译器(尽管目标是实现这一点)。尽管我们提供了一个用于构建数据流加速器的命令行条目,但这只公开了一个适用于更简单网络的基本流。更好的方法是将FINN编译器看作脚本/工具的集合,帮助您将QNN转换为执行高性能推理的定制FPGA加速器。

  • 那我从何而起? 开始使用FINN编译器的最好方法是遵循现有的Jupyter笔记本并查看预构建的示例
  • 如何编译自定义网络? 这取决于您的自定义网络与我们提供的示例的相似程度。如果存在实质性差异,则很可能必须编写自己的Python脚本,调用正确处理设计的相应FINN编译器函数,或者根据需要添加新函数(包括Vivado HLS层)。对于定制网络,我们建议从制作端到端Jupyter笔记本的副本开始,在中间步骤可视化模型,并根据需要添加对新转换的调用。一旦您有了一个工作流程,您就可以通过使用命令行条目部分中描述的“高级模式”为此实现一个命令行条目

2. 系统要求

  • Ubuntu 18.04 with bash installed
  • Docker without root
  • A working Vivado 2019.1 or 2020.1 installation
  • A VIVADO_PATH environment variable pointing to the Vivado installation directory (e.g. the directory where settings64.sh is located)
  • (optional) A PYNQ board with a network connection the bitstring package must be installed on the PYNQ: sudo pip3 install bitstring
  • (optional) An Alveo board, and a working Vitis 2020.1 installation if you want to use Vitis and Alveo (see Alveo first-time setup below)

我们还建议在具有足够强大硬件的系统上运行FINN编译器:
RAM。根据您的目标FPGA平台,您的系统必须有足够的RAM才能运行该部件的Vivado/Vitis合成。有关详细信息,请参阅本页。针对Zynq和Zynq UltraScale+部件,建议至少8 GB。较大的部件可能需要16 GB。针对带有Vitis的Alveo部件,建议至少使用64 GB RAM。
CPU。FINN可以将HLS合成和其他不同层次的操作并行化,因此建议使用多核CPU。但是,这应该与内存使用相平衡,因为高度并行化需要更多内存。有关如何控制并行化程度的详细信息,请参阅下面的NUM\u DEFAULT\u WORKERS环境变量。

3. 在Docker中运行FINN

我们广泛使用Docker来开发和部署FINN。如果你不熟悉Docker,有很多优秀的在线资源可以入门。在存储库的根目录中有一个Dockerfile和一个run-docker.sh可在以下模式下启动的脚本:

(1)获取用于开发或实验的交互式shell

警告:不要用sudo启动FINN Docker。相反,将Docker设置为在没有根的情况下运行

bash ./run_docker.sh

简单的run-docker.sh如果没有任何附加参数,将克隆依赖关系repos,创建一个Docker容器,并为您提供一个终端,您可以使用它进行实验开发。如果你想在一个已经运行的容器上安装一个新的终端,你可以用docker exec-it finn_dev_bash来实现。

(2)命令条目

FINN目前更多的是编译器基础设施,而不是编译器,但我们确实为某些用例提供了命令行条目。它们从命令行运行预定义流或用户定义流,如下所示:

bash ./run_docker.sh build_dataflow <path/to/dataflow_build_dir/>
bash ./run_docker.sh build_custom <path/to/custom_build_dir/>

(3)运行 Jupyter notebooks

bash ./run-docker.sh notebook

这将在Docker容器中启动Jupyter笔记本服务器,并在终端上打印一个链接,您可以在浏览器中打开该链接来运行FINN笔记本或创建新笔记本。。注意:链接看起来像这样(您得到的令牌将不同):http://127.0.0.1:8888/?token=f5c6bd32ae93ec103a88152214baedff4ce1850d81065bfc

run-docker.sh脚本为Jupyter转发8888端口,为Netron转发8081端口,并使用适当的参数启动笔记本服务器。

(4)直接运行the test suite

FINN提供了一组测试来检查回归。完整的测试套件(运行需要几个小时,并且需要PYNQ板)可以通过以下方式执行:

bash ./run-docker.sh test

测试套件有一个更快的变体,可以跳过标记为需要Vivado或运行缓慢的测试:

bash ./run-docker.sh quicktest

如果要运行单个测试,可以在Docker容器内从FINN根目录执行此操作,如下所示:

python setup.py test --addopts "-k test_brevitas_debug"

如果要并行运行测试(例如利用多核CPU),可以使用:

  • pytest-parallel for any rtlsim tests, e.g. python setup.py test
    –addopts “-k rtlsim –workers auto”
  • pytest-xdist for anything else, make sure to add –dist=loadfile if
    you have tests in the same file that have dependencies on each other
    e.g. python setup.py test –addopts “-k mytest -n auto –dist=loadfile”

请参阅pytest文档,了解有关按标记或名称选择测试的更多信息。
最后,可以通过以下方式在容器内运行具有适当并行化的完整测试套件:

quicktest.sh full

(5)环境变量

运行run-docker.sh脚本之前,可以设置几个环境变量来配置FINN的某些方面。总结如下:
在这里插入图片描述

4. 支持的硬件

端到端支持,包括驱动程序: 为了快速部署,FINN将PYNQ支持的板作为目标。对于这些平台,我们可以构建一个完整的位文件,包括DMAs,用于将数据移入和移出FINN生成的加速器,以及一个Python驱动程序来启动加速器。我们支持Pynq-Z1、Pynq-Z2、Ultra96、ZCU102和ZCU104板。从FINN v0.4b开始,我们还初步支持使用PYNQ和Vitis的Xilinx Alveo板,有关Alveo设置,请参阅下面的说明。
Vivado IPI支持任何Xilinx FPGA: FINN通过带有AXI流(FIFO)输入输出接口的神经网络生成Vivado IP Integrator(IPI)设计,可以作为更大系统的一部分集成到任何Xilinx FPGA上。您可以使用FINN生成的加速器(我们在教程中称之为“缝合的IP”),将其连接到您的FPGA设计,并向加速器发送/接收神经网络数据。

(1)Alveo 首次安装

我们使用主机指的是运行FINN-Docker环境的PC,它将构建accelerator+驱动程序并将其打包,而target指的是安装Alveo卡的PC。这两台电脑可以是同一台电脑,也可以通过网络连接——FINN还提供了一些实用程序,以便在远程电脑上进行测试。在首次使用之前,您需要按以下方式设置主机和目标:
在这里插入图片描述

二、教程

FINN提供了几个Jupyter notebooks,可以帮助您熟悉FINN的基础知识、内部结构和端到端流程。所有Jupyter笔记本都可以在笔记本文件夹的repo中找到。

1. 基础知识

本文件夹中的笔记本应提供对FINN的基本见解,如何开始和基本概念。

  • 0_how_to_work_with_onnx :这个笔记本可以帮助你学习如何创建和操作一个简单的ONNX模型,也可以使用FINN
  • 1_brevitas_network_import:本笔记本演示如何导入Brevitas网络并为FINN流做好准备。

2. End-to-End Flow

the end2end_example directory目录下当前有两组笔记本:

  • cybersecurity演示如何使用Brevitas训练量化MLP,并使用命令行入口构建系统使用FINN部署它。
  • bnn-pynq显示了在MNIST和CIFAR-10上执行预训练Brevitas QNNs并生成FPGA加速器的内部编译器步骤。

3. Advanced

此文件夹中的笔记本更面向开发人员。他们应该帮助您熟悉芬兰语的原则,以及如何添加有关这些概念的新内容。

  • 0_custom_analysis_pass:这个笔记本解释了什么是分析通行证,以及如何为FINN写一个。
  • 1_custom_transformation_pass:这个笔记本解释了什么是转换通行证,以及如何为FINN写一个。

三、端到端流

下图显示了FINN中的端到端流示例,从经过训练的PyTorch/Brevitas网络开始,一直到正在运行的FPGA加速器。如图所示,FINN具有很高的模块性,并且具有流可以在任何点停止的特性,中间结果可以用于进一步处理或其他目的。这使得广大用户能够从FINN中获益,即使他们不使用整个流程。
在这里插入图片描述

白色字段显示相应步骤中网络表示的状态。彩色字段表示应用于网络以获得特定结果的变换。该图分为五个部分,每个部分包括几个流程步骤。流程从左上角的Brevitas export(绿色部分)开始,然后准备Vivado HLS和Vivado IPI(橙色部分)的网络(蓝色部分)。在PYNQ板上还有一个测试和验证部分(红色部分)和硬件生成和部署部分(黄色部分)。

这个示例流在第二个示例Jupyter笔记本中介绍。有关不同流程部分的更详细概述,请查看相应页面

四、命令行条目

尽管FINN主要是编译器基础设施,它提供了研究人员可以用来探索定制QNN推断的功能,但我们还提供了两个命令行入口点,以提高生产率和易用性:

  • 简单的数据流构建模式:通过JSON构建配置文件来转换ONNX模型的Best-effort数据流构建。
  • 高级构建模式:提供完全灵活的构建脚本

警告:如果您使用的神经网络的拓扑结构与FINN端到端示例有很大不同,那么下面的简单数据流构建模式很可能会失败。对于这些情况,我们建议创建一个端到端Jupyter笔记本的副本作为起点,在中间步骤可视化模型,并根据需要添加对新转换的调用。一旦你有了一个工作流程,你就可以使用这里描述的“高级模式”来实现一个命令行条目。

1. 简单数据流构建模式

此模式适用于拓扑结构类似于FINN端到端示例的更简单网络。它运行一个固定的构建流程,包括整理、优化、HLS转换和硬件合成。它可以配置为产生不同的输出,包括缝合IP集成在Vivado IPI以及位文件。

要使用它,请首先创建一个包含必要配置和模型文件的文件夹:
在这里插入图片描述
现在您可以按如下方式调用简单的数据流构建:

./run-docker.sh build_dataflow <path/to/dataflow_build_dir/>

根据选择的输出产品,dataflow构建将运行一段时间,因为它将经历许多步骤:

Building dataflow accelerator from /home/maltanar/sandbox/build_dataflow/model.onnx
Outputs will be generated at output_tfc_w1a1_Pynq-Z1
Build log is at output_tfc_w1a1_Pynq-Z1/build_dataflow.log
Running step: step_tidy_up [1/15]
Running step: step_streamline [2/15]
Running step: step_convert_to_hls [3/15]
Running step: step_create_dataflow_partition [4/15]
Running step: step_target_fps_parallelization [5/15]
Running step: step_apply_folding_config [6/15]
Running step: step_generate_estimate_reports [7/15]
Running step: step_hls_ipgen [8/15]
Running step: step_set_fifo_depths [9/15]
Running step: step_create_stitched_ip [10/15]
Running step: step_measure_rtlsim_performance [11/15]
Running step: step_make_pynq_driver [12/15]
Running step: step_out_of_context_synthesis [13/15]
Running step: step_synthesize_bitfile [14/15]
Running step: step_deployment_package [15/15]

你可以阅读每个步骤的简要说明finn.builder.build_dataflow_steps。请注意,输出产品未启用的步骤仍将运行,但不会执行任何操作。

2. 生成的输出

您将在生成配置中指定的子文件夹下找到生成的输出,该子文件夹可以包括各种文件夹和文件,具体取决于所选的输出产品。

无论选择哪个特定输出,都将生成以下输出:
在这里插入图片描述

其他输出产品由构建配置中的generate_outputs 字段控制,详情如下。
在这里插入图片描述
在这里插入图片描述

3.中间步骤验证

FINN数据流构建在生成位文件之前要经过许多步骤,并且由于bug或不受支持的特性,流可能会生成错误的模型。在这个过程中运行新模型时,最好启用数据流构建的验证特性。这样,FINN将使用您提供的输入来运行中间模型,生成一些输出,并将其与您提供的预期输出进行比较。

这是通过设置生成配置的以下成员来实现的:
在这里插入图片描述
验证的输出有两个方面:

在这里插入图片描述

4. 高级模式

在其他情况下,您可能希望对构建过程有更多的控制,以便通过不同的编译步骤组合、对模型应用预处理、调用自定义转换等来实现自己的FINN流。这可以通过使用build\u自定义条目实现,如下所示:

  • 为自定义生成创建新文件夹。最好将这个文件夹放在FINN repo文件夹之外,以便更清晰地分离。我们称这个文件夹为custom_build_dir
  • 创建custom_build_dir/build.py执行时将执行生成的文件。您还应该将任何ONNX模型或其他您可能希望包含在构建流中的Python模块放在这个文件夹中(以便在构建时将它们装入Docker容器)。除了文件名和数据位置之外,您还可以完全自由地在这里实现构建流,包括从上面的简单数据流构建模式调用步骤、调用FINN库函数、预处理和更改模型、构建几个变体等build.py在下面。src/finn/qnn-data/build_dataflow/build.py
    可以使用以下方法启动自定义生成流:
./run-docker.sh build_custom <path/to/custom_build_dir/>

这将把指定的文件夹装入FINN Docker容器并启动build.py

五、示例网络

请访问finn示例库。这个repo包括各种PYNQ和Alveo平台的预构建位文件,以及使用FINN编译器重建这些示例的脚本。

1. 端到端集成测试

FINN编译器使用几个预先训练好的qnn作为示例和测试用例。
在这里插入图片描述
作为FINN集成测试的一部分,这些网络是端到端构建的,关键性能指标(FPGA资源、每秒帧数…)会自动发布到下面的仪表板上。要实现一个新的网络,可以使用集成测试代码作为起点,以及相关的Jupyter笔记本。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
使用iText库可以方便地创建PDF文档并生成目录。 首先需要导入iText库,可以通过Maven或Gradle方式导入。 接着可以创建一个PdfDocument对象,并设置文档属性: ```java PdfDocument pdfDoc = new PdfDocument(new PdfWriter("output.pdf")); Document document = new Document(pdfDoc); pdfDoc.setTagged(); pdfDoc.getCatalog().setLang(new PdfString("en-us")); pdfDoc.getCatalog().setViewerPreferences(new PdfViewerPreferences().setDisplayDocTitle(true)); document.setProperty(Property.SPLIT_CHARACTERS, new DefaultSplitCharacters() { @Override public boolean isSplitCharacter(GlyphLine text, int glyphPos) { return false; } }); ``` 然后可以添加章节标题和内容: ```java // 添加章节标题 Paragraph chapterTitle = new Paragraph("Chapter 1").setFont(PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD)).setFontSize(16); Chapter chapter = new Chapter(chapterTitle, 1); document.add(chapter); // 添加章节内容 Paragraph para1 = new Paragraph("This is the first paragraph."); Paragraph para2 = new Paragraph("This is the second paragraph."); document.add(para1); document.add(para2); ``` 最后可以生成目录: ```java pdfDoc.addEventHandler(PdfDocumentEvent.START_PAGE, new IEventHandler() { @Override public void handleEvent(Event event) { try { PdfDocumentEvent docEvent = (PdfDocumentEvent) event; PdfPage page = docEvent.getPage(); PdfCanvas canvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDoc); Rectangle rect = new Rectangle(36, 750, 523, 36); Canvas canvas1 = new Canvas(canvas, pdfDoc, rect); Paragraph p = new Paragraph().add("Table of Contents").setFont(PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD)).setFontSize(12); canvas1.add(p); canvas1.close(); PdfOutline root = pdfDoc.getOutlines(false); for (int i = 1; i <= 3; i++) { PdfOutline chapterOutline = root.addOutline(String.format("Chapter %d", i)); chapterOutline.addDestination(PdfDestination.makeDestination(new PdfString(String.format("chapter_%d", i)))); } } catch (Exception ex) { ex.printStackTrace(); } } }); ``` 完整的代码示例可参考: ```java import com.itextpdf.kernel.colors.ColorConstants; import com.itextpdf.kernel.font.PdfFont; import com.itextpdf.kernel.font.PdfFontFactory; import com.itextpdf.kernel.geom.Rectangle; import com.itextpdf.kernel.pdf.*; import com.itextpdf.layout.Document; import com.itextpdf.layout.element.AreaBreak; import com.itextpdf.layout.element.Chapter; import com.itextpdf.layout.element.Paragraph; import com.itextpdf.layout.element.Text; import com.itextpdf.layout.property.AreaBreakType; import com.itextpdf.layout.property.Property; import com.itextpdf.layout.renderer.DocumentRenderer; import com.itextpdf.layout.renderer.DrawContext; import com.itextpdf.layout.renderer.IRenderer; import com.itextpdf.layout.renderer.ParagraphRenderer; import com.itextpdf.layout.splitting.DefaultSplitCharacters; import com.itextpdf.layout.splitting.GlyphLine; import com.itextpdf.layout.splitting.ISplitCharacters; import com.itextpdf.layout.splitting.SplitCharacters; import com.itextpdf.layout.splitting.SplitCharacters.DefaultSplitCharacter; import com.itextpdf.layout.splitting.SplitCharacters.SplitCharacter; import java.io.IOException; public class PdfGenerator { public static void main(String[] args) throws IOException { PdfDocument pdfDoc = new PdfDocument(new PdfWriter("output.pdf")); Document document = new Document(pdfDoc); pdfDoc.setTagged(); pdfDoc.getCatalog().setLang(new PdfString("en-us")); pdfDoc.getCatalog().setViewerPreferences(new PdfViewerPreferences().setDisplayDocTitle(true)); document.setProperty(Property.SPLIT_CHARACTERS, new DefaultSplitCharacters() { @Override public boolean isSplitCharacter(GlyphLine text, int glyphPos) { return false; } }); // 添加章节 Paragraph chapterTitle = new Paragraph("Chapter 1").setFont(PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD)).setFontSize(16); Chapter chapter = new Chapter(chapterTitle, 1); chapter.setDestination("chapter_1"); document.add(chapter); Paragraph para1 = new Paragraph("This is the first paragraph."); Paragraph para2 = new Paragraph("This is the second paragraph."); document.add(para1); document.add(para2); document.add(new AreaBreak(AreaBreakType.NEXT_PAGE)); chapterTitle = new Paragraph("Chapter 2").setFont(PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD)).setFontSize(16); chapter = new Chapter(chapterTitle, 2); chapter.setDestination("chapter_2"); document.add(chapter); para1 = new Paragraph("This is the third paragraph."); para2 = new Paragraph("This is the fourth paragraph."); document.add(para1); document.add(para2); document.add(new AreaBreak(AreaBreakType.NEXT_PAGE)); chapterTitle = new Paragraph("Chapter 3").setFont(PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD)).setFontSize(16); chapter = new Chapter(chapterTitle, 3); chapter.setDestination("chapter_3"); document.add(chapter); para1 = new Paragraph("This is the fifth paragraph."); para2 = new Paragraph("This is the sixth paragraph."); document.add(para1); document.add(para2); // 添加目录 pdfDoc.addEventHandler(PdfDocumentEvent.START_PAGE, new IEventHandler() { @Override public void handleEvent(Event event) { try { PdfDocumentEvent docEvent = (PdfDocumentEvent) event; PdfPage page = docEvent.getPage(); PdfCanvas canvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDoc); Rectangle rect = new Rectangle(36, 750, 523, 36); Canvas canvas1 = new Canvas(canvas, pdfDoc, rect); Paragraph p = new Paragraph().add("Table of Contents").setFont(PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD)).setFontSize(12); canvas1.add(p); canvas1.close(); PdfOutline root = pdfDoc.getOutlines(false); for (int i = 1; i <= 3; i++) { PdfOutline chapterOutline = root.addOutline(String.format("Chapter %d", i)); chapterOutline.addDestination(PdfDestination.makeDestination(new PdfString(String.format("chapter_%d", i)))); } } catch (Exception ex) { ex.printStackTrace(); } } }); document.close(); } } ``` 在运行代码后,将会生成一个名为"output.pdf"的PDF文档,包含了个章节和一个目录。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

别出BUG求求了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值