0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

深度学习算子优化之FFT

jf_78858299 来源:旷视研究院 作者:严健文 2023-05-04 17:50 次阅读

数字信号和数字图像领域,对频域的研究是一个重要分支。

我们日常“加工”的图像都是像素级,被称为是图像的空域数据。空域数据表征我们“可读”的细节。如果我们将同一张图像视为信号,进行频谱分析,可以得到图像的频域数据。观察下面这组图,频域图中的亮点为低频信号,代表图像的大部分能量,也就是图像的主体信息。暗点为高频信号,代表图像的边缘和噪声。从组图可以看出,Degraded Goofy 与 Goofy 相比,近似的低频信号保留住了 Goofy 的“轮廓”,而其高频信号的增加使得背景噪点更加明显。频域分析使我们可以了解图像的组成,进而做更多的抽象分析和细节处理。

图片

Goofy and Degraded Goofy

实现图像空域和频域转换的工具,就是傅立叶变换。由于图像数据在空间上是离散的,我们使用傅立叶变换的离散形式 DFT(Discrete Fourier Transform)及其逆变换 IDFT(Inverse Discrete Fourier Transform)。Cooley-Tuckey在DFT的基础上,开发了更快的算法 FFT(Fast Fourier Transform)。

图片

DFT/FFT在数字图像领域还有一些延伸应用。比如基于 DFT 的 DCT(Discrete Cosine Transform,离散余弦变换)就用在了图像压缩JPEG算法和图像水印算法。

JPEG 编码是通过色彩空间转换、抽样分块、DCT 变换、量化编码实现的。其中 DCT 变换的使用将图像低频信息和高频信息区分开,在量化编码过程中压缩了少量低频信息、大量高频信息从而获得尺寸上压缩。从猫脸图上可看出随着压缩比增大画质会变差,但是主体信息还是得以保留。

图像水印算法通过 DCT 将原图转换至频域,选取合适的位置嵌入水印图像信息,并通过 IDCT 转换回原图。这样对原图像的改变较小不易察觉,且水印通过操作可以被提取。

图片

DFT/FFT 在深度学习领域也有延伸应用。比如利用 FFT 可以降低卷积计算量的特点,FFT_Conv 算法也成为常见的深度学习卷积算法。本文我们就来探究一下频域算法的原理和优化策略。

DFT的原理及优化

公式

无论是多维的 DFT 运算,还是有基于 DFT 的 DCT/FFT_Conv, 底层的计算单元都是 DFT_1D。因此,DFT_1D 的优化是整个FFT类算子优化的基础。

DFT_1D 的计算公式: 其中 为长度为 N 的输入信号, 是 1 的 N 次根, 为长度为N 的输出信号。

该公式的矩阵形式为:

单位复根的性质

DFT_1D 中的 是 1 的单位复根。直观地看,就是将复平面划分为 N 份,根据 k * n 的值逆时针扫过复平面的圆周。

图片

单位复根有着周期性和对称性,我们依据这两个性质可以对W矩阵做大量的简化,构成 DFT_1D 的快速算法的基础。

周期性:

对称性:

Cooley-Tuckey FFT算法

DFT_1D 的多种快速算法中,使用最频繁的是 Cooley-Tuckey FFT 算法。算法采用用分治的思想,将输入尺寸为 N 的序列,按照不同的基 radix,分解为 N/radix 个子序列,并对每个子序列再划分,直到不能再被划分为止。每一次划分都可以得到一级 stage,将所有的级自下而上组合在一起,计算得到最后的输出序列。

这里以 N = 8, radix=2 为例展示推理过程。

其中 为N=8 的序列, 为 DFT 输出序列.

根据 DFT 的计算公式:

根据奇偶项拆开,分成两个长度为 4 的序列 。

图片

的 DFT 结果 乘以对应的旋转因子 ,进行简单的加减运算可以得到输出 。

同理, 对 也做一样的迭代, 都是 N=2 的序列,用他们的 DFT 结果进行组合运算可以得到 。

计算 N=2 的序列 , 因为 ,旋转因子 。只要进行加减运算得到结果。

用算法图形表示,每一层的计算会产生多个蝶形,因此该算法又被称为蝶形算法。

这里我们要介绍碟形网络的基本组成,对下文的分析有所帮助。

图片

N=8 碟形算法图

N=8 的计算序列被分成了 3 级,每一级 (stage) 有一个或多个块 (section),每个块中包含了一个或者多个蝶形(butterfly) , 蝶形的计算就是 DFT 运算的 kernel。

每一个 stage 的计算顺序:

  • 取输入
  • 乘以转换因子
  • for section_num, for butterfly_num,执行radixN_kernel
  • 写入输出

看 N=8 的蝶形算法图,stage = 1 时,运算被分成了 4 个 section,每个section的 butterfly_num = 1 。stage = 2 时, section_num = 2,butterfly_num = 2。stage = 3时, section_num = 1, butterfly_num = 4。

可以观察到,从左到右过程中 section_num 不断减少, butterfly_num 不断增加,蝶形群在“变大变密”,然而每一级总的碟形次数是不变的。

实际上,对于长度为 N ,radix = r 的算法,我们可以推得到:

为当前的 ,* sec/butterfly_stride 是每个section/butterfly* 的间隔。

这个算法可以将复杂度从 O(n^2) 下降到 O(nlogn),显得高效而优雅。我们基于蝶形算法,对于不同的radix进行算法的进一步划分和优化,主要分为radix - 2 的幂次的和 radix – 非 2 的幂次两类。

radix-2 的幂次优化

DFT_1D 的 kernel 即为矩阵形式中的 矩阵,我们对 radix_2^n的 kernel 进行分析。

背景里提到, DFT 公式的矩阵形式为:

其中 ~ 为乘以旋转因子 后的输入

当 radix = 2 时,由于 , radix_2 的 DFT 矩阵形式可以写为:

当 radix = 4 时,由于 ,radix_4的DFT 矩阵形式可以写为:

同理推得到 radix_8 的 kernel 为:

我们先来看访存, 现代处理器对于计算性能的优化要优于对于访存的优化, 在计算和访存相近的场景下, 访存通常是性能瓶颈。

DFT1D 中,对于不同基底的算法 r-2/r-4/r-8, 每一个 stage 有着相等的存取量: 2 * butterfly_num * radix = 2N, 而不同的基底对应的 stage 数有着明显差异( vs vs )。

因此对于 DFT , 在不显著增加计算量的条件下, 选用较大的kernel会在访存上取得明显的优势。观察推导的 kernel 图, r-2 的 kernel 每个蝶形对应 4次访存操作和,2 次复数浮点加减运算。r-4 的 kernel 每个蝶形算法对应 8 次 load/store、8 次复数浮点加减操作(合并相同的运算),在计算量略增加的同时 stage 由 下降到 , 降低了总访存的次数, 因此会有性能的提升。r-8 的 kerne l每个蝶形对应 16 次load/store、24 次复数浮点加法和8次浮点乘法。浮点乘法的存在使得计算代价有所上升, stage由 进一步下降到 ,但由于 N 日常并不会太大, r-4 到 r-8 的stage 减少不算明显,所以优化有限

我们再来看计算的开销. 减少计算的开销通常有两种办法:减少多余的运算、并行化。

以 r-4 算法为例, kernel 部分的计算为:

  • radix_4_first_stage(src, dst, sec_num, butterfly_num)
  • radix_4_other_stage(src, dst, sec_num, butterfly_num)
  • for Sec_num
  • for butterfly_num
  • raidx_4_kernel

radix4_first_stage 的数据由于 k=0, 旋转因子都为 1 ,可以省去这部分复数乘法运算,单独优化。radix4_other_stage 部分, 从第 2 个 stage 往后, butterfly_num = 4^(s-1) 都为 4 的倍数,而每个 butterfly 数组读取/存储都是间隔的。可以对最里层的循环做循环展开加向量化,实现 4 个或更多 butterfly 并行运算。循环展开和SIMD指令的使用不仅可以提高并行性, 也可以提升cacheline 利用的效率, 可以带来较大的性能提升。以SM8150(armv8) 为例,r-4 的并行优化可以达到 r2 的 1.6x 的性能。

图片

尺寸:1 * 2048(r2c) 环境:SM8150大核

总之,对于 radix-2^n 的优化,选用合适的 radix 以减少多 stage 带来的访存开销,并且利用单位复根性质以及并行化降低计算的开销,可以带来较大的性能提升。

radix-非2的幂次优化

当输入长度 N = radix1^m1 * radix2^m2… 且 radix 都不为 2 的幂次时,如果使用 naive的O(n^2) 算法, 性能就会急剧下降。常见的解决办法对原长补 0、使用 radix_N 算法、特殊的 radix_N 算法(chirp-z transform)。补0至2的幂次方法对于大尺寸的输入要增加很多运算量和存储量, 而chirp-z transform 是用卷积计算DFT, 算法过于复杂。因此对非 2 的幂次radix-N 的优化也是必要的。

radix-N 计算流程和 radix-2 幂次一样,我们同样可以利用单位复根的周期性和对称性,对 kernel 进行计算的简化。以 radix-5 为例,radix-5 的DFT_kernel 为:

在复平面上根据x轴对称,有相同的实部和相反的虚部。根据这个性质。如下图所示,对于每一个 stage,可以合并公共项A,B,C,D,再根据公共项计算出该 stage 的输出。

这种算法减少了很多重复的运算。同时,在 stage>=2 的时候,同样对 butterfly 做循环展开加并行化,进一步减少计算的开销。

radix-5 的优化思想可以外推至 radix-N 。对于 radix_N 的每一个 stage,计算流程为:

  • 取输入
  • 乘以对应的转换因子
  • 计算公共项, radix_N 有 N-1个公共项
  • 执行并行化的 radix_N_kernel
  • 写入输出

其他优化

上述两个章节描述的是 DFT_1D 的通用优化,在此基础上还可以做更细致的优化,可以参考本文引用的论文。

  • 对于全实数输入的,由于输入的虚部为 0, 进行旋转因子以及radix_N_kernel 的复数运算时会有多余的运算和多余的存储, 可以利用 split r2c 算法, 视为长度为 N/2 的复数序列, 计算 DFT 结果并进行 split操作得到 N 长实数序列的结果。
  • 对于 radix-2 的幂次算法, 重新计算每个 stage 的输入/输出 stride 以取消第一级的位元翻转可以进一步减少访存的开销。
  • 对于 radix-N 算法, 在混合基框架下 N = radix1^m1 * radix2^m2, 合并较小的 radix 为大的 radix 以减少 stage。

DFT 延展算法的原理及优化

DCT 和FFT_conv 两个典型的基于 DFT 延展的算法,DFT_1D/2D 的优化可以很好的用在这类算法中。

DCT

DCT算法(Discrete Cosine Transform, 离散余弦变换)可以看作是 DFT 取其正弦分量并经过工业校正的算法。DFT_1D 的计算公式为:

该算法naive实现是 O(n^2) 的,而我们将其转换成 DFT_1D 算法,可以将算法复杂度降至 O(nlogn )。

基于 DFT 的 DCT 算法流程为:

  • 对于 DCT 的输入序列 x[n], 创建长为 2N 的输入序列 y[n] 满足 y[n] = x[n] + x[2N-n-1], 即做一个镜像对称。
  • 对输入序列 y[n] 进行 DFT 运算,得到输出序列 Y[K]。
  • 由 Y[K] 计算得到原输入序列的输出 X[K] 。

我们尝试推导一下这个算法:

对 y[n] 依照 DFT 公式展开,整理展开的两项并提取公共项 , 根据欧拉公式和诱导函数,整理非公共项 。可以看出得到的结果正是 x[k] 和与 k 有关的系数的乘积。这样就可以通过先计算 得到 x[n] 的 DCT 输出 。

在理解算法的基础上,我们对 DFT_1D 的优化可以完整地应用到 DCT 上。DCT_2D 的计算过程是依次对行、列做 DCT_1D, 我们用多线程对 DCT_1D 进行并行,可以进一步优化算法。

FFT_conv

Conv 是深度学习最常见的运算,计算conv常用的方法有 IMG2COL+GEMM, Winograd, FFT_conv。三种算法都有各自的使用场景。

FFT_conv 的数学原理是时域中的循环卷积对应于其离散傅里叶变换的乘积. 如下图所示, f 和 g 的卷积等同于将 f 和 g 各自做傅立叶变幻 F,进行点乘并通过傅立叶逆变换计算后的结果。

直观的理论证明可下图。

将卷积公式和离散傅立叶变换展开, 改变积分的顺序并且替换变量, 可以证明结论。

注意这里的卷积是循环卷积, 和我们深度学习中常用的线性卷积是有区别的。利用循环卷积计算线性卷积的条件为循环卷积长度 L⩾| f |+| g |−1。因此我们要对 Feature Map 和 Kernel做zero-padding,并从最终结果中取有效的线性计算结果。

FFT_conv 算法的流程:

  • 将 Feature Map 和 Kernel 都 zero-pad 到同一个尺寸,进行 DFT 转换。
  • 矩阵点乘
  • 将计算结果通过 IDFT 计算出结果。

该算法将卷积转换成点乘, 算法复杂度是 O(nlogn),小于卷积的 O(n^2), 在输入的尺寸比较大时可以减少运算量,适用于大 kernel 的 conv 算法。

深度学习计算中, Kernel 的尺寸要远小于 Feature Map, 因此 FFT_conv第一步的 zero-padding 会有很大的开销,参考论文2里提到可以通过对 Feature map进行分块, 分块后的 Feature Map 和 Kernel 需要 padding 到的尺寸较小,可以大幅减小这一部分的开销。优化后 fft_conv 的计算流程为:

  • 合理安排缓存计算出合适的tile尺寸,对原图进行分块
  • 分块后的小图和 kernel 进行 zero-padding , 并进行 DFT 运算
  • 小图矩阵点乘
  • 进行逆运算并组合成大图。

同时我们可以观察到,FFT_conv 的核心计算模块还是针对小图的 DFT 运算, 因此我们可以将前一章节对 DFT 的优化代入此处,辅以多线程,进一步提升 FFT_Conv 的计算效率。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数字图像
    +关注

    关注

    2

    文章

    119

    浏览量

    18748
  • 数字信号
    +关注

    关注

    2

    文章

    969

    浏览量

    47543
  • 低频信号
    +关注

    关注

    2

    文章

    47

    浏览量

    8310
收藏 人收藏

    评论

    相关推荐

    基于动态编译(Just-in-Time)的全新深度学习框架

    )的深度学习框架。[1] 据介绍,Jittor 内部使用创新的元算子和统一计算图的深度学习框架。和 Numpy 相比,元
    的头像 发表于 11-25 11:08 3025次阅读

    matplotlib动态演示深度学习tensorflow将神经网络系统自动学习散点(二次函数+noise)并优化修正并且将输出结果可视化

    TFNN:matplotlib动态演示深度学习tensorflow将神经网络系统自动学习散点(二次函数+noise)并
    发表于 12-21 10:48

    AutoKernel高性能算子自动优化工具

    主要由资深HPC工程师(高性能计算优化工程师)进行开发,为了加快开发进程,缩短深度学习应用落地周期,自动化算子优化是一个趋势。AutoKer
    发表于 12-14 06:18

    存储深度FFT结果的影响

    存储深度FFT结果的影响     在DSO中,通过快速傅立叶变换(FFT)可以得到信号的
    发表于 08-25 08:06 904次阅读

    算法优化福音:算子自动优化工具AutoKernel正式开源啦

    算子自动优化的发展趋势随着AI威廉希尔官方网站 的快速发展,深度学习在各个领域得到了广泛应用。深度学习模型能否
    的头像 发表于 12-08 22:28 866次阅读

    深度模型中的优化学习课件下载

    深度模型中的优化学习课件下载
    发表于 04-07 16:21 3次下载
    <b class='flag-5'>深度</b>模型中的<b class='flag-5'>优化</b>与<b class='flag-5'>学习</b>课件下载

    算法优化入坑难?福音来了:算子自动优化工具AutoKernel正式开源啦!

    算子自动优化的发展趋势随着AI威廉希尔官方网站 的快速发展,深度学习在各个领域得到了广泛应用。深度学习模型能否
    发表于 01-25 20:05 1次下载
    算法<b class='flag-5'>优化</b>入坑难?福音来了:<b class='flag-5'>算子</b>自动<b class='flag-5'>优化</b>工具AutoKernel正式开源啦!

    什么是深度学习优化算法

    先大致讲一下什么是深度学习优化算法吧,我们可以把模型比作函数,一种很复杂的函数:h(f(g(k(x)))),函数有参数,这些参数是未知的,深度学习
    的头像 发表于 02-13 15:31 1606次阅读
    什么是<b class='flag-5'>深度</b><b class='flag-5'>学习</b>中<b class='flag-5'>优化</b>算法

    深度学习编译器Layerout Transform优化

    继续深度学习编译器的优化工作解读,本篇文章要介绍的是OneFlow系统中如何基于MLIR实现Layerout Transform。
    的头像 发表于 05-18 17:32 738次阅读

    PyTorch教程12.1优化深度学习

    电子发烧友网站提供《PyTorch教程12.1优化深度学习.pdf》资料免费下载
    发表于 06-05 15:08 0次下载
    PyTorch教程12.1<b class='flag-5'>之</b><b class='flag-5'>优化</b>和<b class='flag-5'>深度</b><b class='flag-5'>学习</b>

    PyTorch教程-12.1. 优化深度学习

    12.1. 优化深度学习¶ Colab [火炬]在 Colab 中打开笔记本 Colab [mxnet] Open the notebook in Colab Colab [jax
    的头像 发表于 06-05 15:44 573次阅读
    PyTorch教程-12.1. <b class='flag-5'>优化</b>和<b class='flag-5'>深度</b><b class='flag-5'>学习</b>

    智造眼丨深度学习应用

    智造眼®科学设计深度学习各应用流程,在尽量简化前期准备工作的基础上为客户提供稳定且准确的深度学习解决方案。
    的头像 发表于 05-04 16:55 762次阅读
    智造<b class='flag-5'>之</b>眼丨<b class='flag-5'>深度</b><b class='flag-5'>学习</b>应用

    机器学习算法的5种基本算子

    机器学习算法的5种基本算子 机器学习是一种重要的人工智能威廉希尔官方网站 ,它是为了让计算机能够通过数据自主的学习和提升能力而发明的。机器学习算法是机器
    的头像 发表于 08-17 16:11 1792次阅读

    深度学习编译工具链中的核心——图优化

    等,需要调整优化网络中使用的算子算子组合,这就是深度学习编译工具链中的核心——图优化。图
    的头像 发表于 05-16 14:24 938次阅读
    <b class='flag-5'>深度</b><b class='flag-5'>学习</b>编译工具链中的核心——图<b class='flag-5'>优化</b>

    深度学习的模型优化与调试方法

    深度学习模型在训练过程中,往往会遇到各种问题和挑战,如过拟合、欠拟合、梯度消失或爆炸等。因此,对深度学习模型进行优化与调试是确保其性能优越的
    的头像 发表于 07-01 11:41 809次阅读