如何使用 FPGA 推理大模型 (2) - 加速核心编写

news/2025/12/19 21:24:23/文章来源:https://www.cnblogs.com/wenbinteng/p/19373604

在这一部分中,我们将重点介绍如何在 FPGA 上编写加速核心,以加速大模型推理过程中最关键的计算环节。

与在 CPU 上通过指令顺序执行程序不同,FPGA 的核心优势在于可以将计算过程直接“固化”为硬件结构,从而实现高度并行的执行方式。针对特定计算任务进行的硬件定制,正是 FPGA 能够获得加速效果的根本原因。

在具体实现上,我们将首先借助高层次综合工具(HLS)来描述计算逻辑,这些描述最终会被综合并映射为 FPGA 上真实运行的硬件电路。随后,通过预先定义好的接口,软件可以像调用普通函数一样触发这些加速核心完成计算。本篇博客将围绕这一完整流程展开,逐步讲解加速核心的编写思路,帮助读者建立对 FPGA 加速流程的整体认识。

1. GPT-2 模型概览

GPT-2 模型是 OpenAI 发布的基于 Transformer Decoder 架构的大语言模型,提供 small-124M、medium-355M、large-774M、xl-1.5B 四种参数规模。

这里我们选择 GPT-2 Medium 作为加速对象,其结构以及各级参数如下所示:

Model: GPT-2 Medium (355M)
├── Token Embedding: 50257 × 1024
├── Position Embedding: 1024 × 1024
├── 24 Transformer Blocks
│   ├── LayerNorm
│   ├── Multi-Head Attention (16 heads, 64 dim each)
│   │   ├── Q: Linear(1024 → 1024)
│   │   ├── K: Linear(1024 → 1024)
│   │   ├── V: Linear(1024 → 1024)
│   │   └── Output: Linear(1024 → 1024)
│   ├── Residual: Add & Norm
│   ├── LayerNorm
│   ├── Feed Forward Network
│   │   ├── Expansion: Linear(1024 → 4096)
│   │   ├── Activation: GELU
│   │   └── Projection: Linear(4096 → 1024)
│   └── Residual: Add & Norm
├── Final LayerNorm
└── Output Projection: Linear(1024 → 50257)

可以看到,Transformer Blocks 在整个模型中的计算占比最大,因此,我们选择其中的多头自注意力层(Multi-Head Attention,MHSA)和前馈网络层(Feed Forward Network,FFN)进行加速。下面简要介绍一下这两个层的作用。

MHSA

MHSA 用于根据注意力机制计算当前 token 的上下文增强表示。

输入: token (seq_len × embed_dim)
输出: token (seq_len × embed_dim)

计算过程的公式为

\[Q=X W_q + b_q\\ K=X W_k + b_k\\ V=X W_v + b_v\\ \mathrm{Attention}(Q,K,V)=\mathrm{Sofmax}(\frac{QK^T}{\sqrt{d}})V \]

FFN

FFN 用于对 token 进行非线性变化。

输入: token (seq_len × embed_dim)
输出: token (seq_len × embed_dim)

计算过程的公式为

\[\mathrm{FFN}(X) = \mathrm{GELU}(XW_1+b_1)W_2+b_2 \]

2. 使用 Stream-HLS 框架生成代码

对于初学者而言,直接使用 HDL 或传统 HLS 编写加速核心往往门槛较高,且开发周期较长。这里我们介绍一个最新的基于 MLIR 的 HLS 代码生成框架:Stream-HLS,它可以将输入的 Pytorch 模型转换为 HLS 代码,并拥有不错的性能。

2.1 安装

克隆仓库,并按照仓库中 README 安装步骤进行安装。

2.2 编写加速核心

我们对仓库中原有的 MHSA 和 FFN 模型定义进行修改,以匹配 GPT-2 的模型参数和输入大小。

首先,MHSA 的代码 examples/pymodels/transformers/MultiHeadSelfAttention.py

# MultiHeadSelfAttention.pyimport math
import torch
import torch.nn as nn
import torch.nn.functional as Fclass MultiHeadSelfAttention(nn.Module):def __init__(self, embed_dim, num_heads):super(MultiHeadSelfAttention, self).__init__()assert embed_dim % num_heads == 0self.num_heads = num_headsself.head_dim = embed_dim // num_headsself.c_attn = nn.Linear(embed_dim, 3*embed_dim, dtype=torch.float16)self.c_proj = nn.Linear(embed_dim, embed_dim, dtype=torch.float16)def forward(self, x):batch_size, seq_length, embed_dim = x.shapeqkv = self.c_attn(x)q,k,v = torch.split(qkv, embed_dim, dim=2)q = q.view(batch_size, seq_length, self.num_heads, self.head_dim).transpose(1, 2)k = k.view(batch_size, seq_length, self.num_heads, self.head_dim).transpose(1, 2)v = v.view(batch_size, seq_length, self.num_heads, self.head_dim).transpose(1, 2)score = (q @ k.transpose(-2,-1)) / math.sqrt(self.head_dim)  # (batch_size, num_heads, seq_length, seq_length)tri = torch.ones(seq_length, seq_length, dtype=torch.bool).tril()score = score.masked_fill(~tri, float("-inf"))attn = F.softmax(score, dim=-1)y = attn @ v  # (batch_size, num_heads, seq_length, head_dim)y = y.transpose(1,2).contiguous().view(y.size(0), y.size(2), -1)  # (batch_size, seq_length, embed_dim)out = self.c_proj(y)return out

然后,修改 FFN 的代码 examples/pymodels/transformers/FeedForward.py

# FeedForward.pyimport torch
import torch.nn as nnclass FeedForward(nn.Module):def __init__(self, embed_dim, ff_dim):super(FeedForward, self).__init__()self.fc1 = nn.Linear(embed_dim, ff_dim, dtype=torch.float16)self.fc2 = nn.Linear(ff_dim, embed_dim, dtype=torch.float16)self.gelu = nn.GELU()def forward(self, x):x = self.gelu(self.fc1(x))x = self.fc2(x)return x

最后,在 examples/data.py 中修改输入向量的大小:

"FeedForward" : {"class": "FeedForward","config" : dict(embed_dim=1024,ff_dim=4096),"input" : (randTensor(1, 128, 1024, dtype=dtype),)
},
"MultiHeadSelfAttention" : {"class": "MultiHeadSelfAttention","config" : dict(embed_dim=1024,num_heads=16),"input" : (randTensor(1, 128, 1024, dtype=dtype),)
}
2.3 运行

在 bash 中配置环境变量:

export PATH="$PWD/build/bin:$PATH"
export XILINX_HLS="/tools/Xilinx/Vitis_HLS/2023.2/"

examples 目录中运行 Stream-HLS 脚本以生成 HLS 代码:

python run_streamhls.py -b transformers -k FeedForward -O5
python run_streamhls.py -b transformers -k MultiHeadSelfAttention -O5

其中,-b 用于指定 benchmark 目录,-k 用于选择需要编译的 kernel,-O 是优化级别选项。

执行完毕后, examples/designs/transformers/opt5/<KERNEL>_7680/<KERNEL>/hls/src/<KERNEL>.cpp 就是生成的 HLS 代码。

2.4 微调代码

此时生成的 HLS 代码还无法直接用于 Vivado Flow,需要进行少量人工修改,以适配后续的系统集成流程。

绑定 AXI 接口

Stream-HLS 默认生成的顶层接口为数组形式,这种接口通常对应 FPGA 片上存储资源。为了支持主机与 FPGA 之间的数据传输,需要将这些接口绑定到 AXI 接口上。

在生成的 HLS 代码文件中,找到 forward 函数,这是顶层函数定义。在函数体头部添加以下接口约束:

#pragma HLS interface mode=m_axi offset=slave port=<name> bundle=gmem<number> max_widen_bitwidth=256
...

<name> 替换为实际的数组名称,如 v99。将 <number> 设置为不同的数字,以区别不同的 AXI 接口。所有数组接口都应该添加上述的接口约束。

添加 AXI-Lite 控制接口

为了便于主机端对加速核心进行控制(例如启动和结束计算),还需要为顶层函数添加 AXI-Lite 控制接口。

同样地,在 forward 函数体的头部,添加以下代码:

#pragma HLS interface mode=s_axilite port=return

删除数组分块约束

Stream-HLS 会为数组接口自动添加 array_partition 约束,以提高并行访存能力。但在绑定到 AXI 接口后,这种分块方式会导致接口数量急剧增加,增加系统集成复杂度。因此,需要删除 forward 及相关 node* 函数中针对数组接口的 array_partition 约束。

3. Vitis-HLS 综合

examples/designs/transformers/opt5/<KERNEL>_7680/<KERNEL>/hls 中,Stream-HLS 已经为加速核心生成了 Vitis-HLS 综合所需的脚本。我们需要执行的是 syn 流程,然后导出 Vivado Flow 使用的 IP。以下是综合的流程:

首先,在 hls.tcl 脚本中添加导出 IP 的命令 export_design

...
elseif {[lindex $argv 1] == "syn"} { # config_op fmacc -impl auto -precision highcsynth_design# add this commandexport_design -rtl verilog -format ip_catalog -output ./
}
...

这条命令将会创建基于 RTL 的 IP,可以在 Vivado Flow 中导入。

接着,在终端中执行综合脚本:

bash syn.batch

可以看到,生成的 IP 压缩包在当前路径下。


至此,我们已经完成了加速核心的编写以及基于 Vitis-HLS 的综合流程。在下一部分中,我们将把这些 IP 导入 Vivado 工程,搭建完整的 FPGA 硬件实验平台,并完成与主机端软件的集成。

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

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

相关文章

015.洛谷模拟题

做了几道模拟题,总结一下动手之前一定要好好读题,遗漏信息大概率WA到死 不要怕麻烦,码量大是模拟题的特点 对算法要求一般不高 细心,逻辑链要完整luogu P1563 处理环形数组 #include<bits/stdc++.h> using n…

015.洛谷模拟题

做了几道模拟题,总结一下动手之前一定要好好读题,遗漏信息大概率WA到死 不要怕麻烦,码量大是模拟题的特点 对算法要求一般不高 细心,逻辑链要完整luogu P1563 处理环形数组 #include<bits/stdc++.h> using n…

如何使用 FPGA 推理大模型 (1) - 简介

近年来,大语言模型(Large Language Models, LLMs)已经成为人工智能领域最重要的技术方向之一。从对话系统到代码生成,再到各类智能助手,模型规模和应用场景都在不断扩展。然而,与模型能力同步增长的,是推理阶段…

复制文本到剪贴板(跨平台兼容)

/** * 复制文本到剪贴板(跨平台兼容) * @param text 要复制的文本 * @returns 是否成功 */ static async copyToClipboard(textData: string): Promise { try { // 首先尝试现代剪贴板API if (navigator.clipboard &…

写在二战考试前一晚

写在二战考试前一晚记得去年这个时候我和室友因为酒店昏暗的灯光,在旁边的饭店里背政治。去年这个时候一切都是未知,即使有人陪伴,心里任是各种不安焦虑。但是今年,我一个人来到离我家几十公里却从未到访的城市的酒…

速度表情用语中外文对照表

largo (宽广的,广板) lento(缓慢的) adagio(从容的,柔板) andente(行走的速度,行板) andantino(比行板稍快,小行板) moderato(中速的,中板) allearetto(比快板稍慢,小快板) allegro(快板) vivace…

分享文件:charles-proxy-4.6.3-win64.msi

分享文件:charles-proxy-4.6.3-win64.msi链接:https://pan.xunlei.com/s/VOgqhBTzFLCqTiUFHMj24PC9A1?pwd=cxi7#复制这段内容后打开迅雷,查看更方便 注册信息:TEAM MESMERiZEFC91D362FB19D6E6CF

git如何撤销某个冲突的解决

你想撤销已经处理过的冲突解决(比如解决错了、想重新处理),核心思路是让冲突文件回到「未解决冲突」的原始状态,Git 提供了明确的命令来实现这个需求,下面分「已执行 git add 标记解决」和「仅手动修改但未 add」…

关于本站

本站由汤沐辰于2025年12月19日独立开发,望大家提出更正意见

2025年12月金包银品牌TOP10品牌:工艺/品控/售后三维分析,新手避坑首选 - 小白条111

2025年12月金包银品牌TOP10品牌:工艺/品控/售后三维分析,新手避坑首选随着国际金价持续高位(2025年10月京报网数据显示纯金单价达1245元/克),金包银珠宝凭借“千元享万元质感”的高性价比成为消费热点。但南方网2…

物理验证:你选哪款 DRC/LVS

“物理验证是通往 tape-out 的最后一关。” 当工艺推进至 7 nm、5 nm 乃至更先进节点,设计规则变得愈发复杂、模块层级更多、混合信号/3D 封装挑战加剧。此时, DRC (Design Rule Check) 与 LVS (Layout Versus Sche…

夕花朝逝

主要用来记录每天犯的神仙错误。。。 25.12.19 lca函数返回值写循环里面了 和李柏延@pm_fp调了30min才过"——敬不完美的明天""——敬不再沉默的历史,热烈而勇敢的奔赴,和通往所以未来的旅途"&q…

赫斯特 (Hurst)计算——重标极差法(R/S法)

基本概念赫斯特 (Hurst)指数:介于0到1之间的标量,度量时间序列的趋势持续性(惯性) 判读规则:H > 0.5:序列有持续性(长程正相关) H = 0.5:序列为无记忆随机游走 H < 0.5:序列有反持续性(长程负相关)核…

2025年12月中医馆,昆明中医,云南中医馆推荐:行业权威盘点与品质诊疗红榜发布 - 品牌鉴赏师

引言在 2025 年 12 月的今天,中医行业在健康领域的重要性愈发凸显。国内相关行业协会通过一系列科学测评,发布了权威数据和白皮书,为大众在选择中医馆时提供了重要参考。本次测评依据多维度标准,涵盖技术实力、人才…

Android ALSA驱动进阶之获取周期帧数snd_pcm_lib_period_frames:用法实例(九十五) - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

从研究问题到分析初稿:深度解析PaperXie AI科研工具中数据分析模块在学术写作场景下的辅助逻辑与技能实现路径

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

英语_阅读_Incorrect beliefs_待读

Has anyone ever told you that eating carrots will help you see in the dark?有人曾告诉你吃胡萝卜能帮助你在黑暗中看见吗? This is just one of many incorrect beliefs you may have heard.这只是你可能听过的…

详细介绍:Golang Cobra 教程:构建强大的CLI应用

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Unity 拖动物体技术文档

📌 前提条件场景中必须有 EventSystem(除非使用 OnMouseDrag)。 拖拽目标必须能被事件系统或物理系统检测到:UI 元素:Canvas + GraphicRaycaster。 3D 物体:Collider + Camera 上的 PhysicsRaycaster。🖼 拖拽…