深度学习框架如何自动选择最快的算法?Fast Run 让你收获最好的性能!
作者:王博文 | 旷视 MegEngine 架构师
一、背景
对于深度学习框架来说,网络的训练/推理时间是用户非常看中的。在实际生产条件下,用户设计的 NN 网络是千差万别,即使是同一类数学计算,参数也各不相同。如果没有针对性的优化,框架就完全丧失竞争力。因此,在一类数学计算中,开发者们会开发多种高效的算法,分别适用于不同的参数,以保证网络的性能。接下来开发者们需要解决一个新问题,当计算参数确定以后,如何让最快的算法执行该计算。
大部分框架靠先验的经验选择算法,MegEngine 亦总结有优秀的先验经验值,实现计算时自动选择算法。但是依靠经验不能保证一定选择了最快的算法。很多实际场景中,用户希望网络有最极致的性能。为此,MegEngine 设计了专门的流程,可以为每个计算自动选择最快的算法,从而保证整个网络的运行时间最短。并且同时能够将计算的参数和其对应的算法信息以及设备信息记录到内存或文件,当用户再次运行网络时,可以直接获取性能最好的算法。这一提升性能的流程被称为 Fast Run,它能让 MegEngine 的用户运行不同的网络时都能收获最好的性能。
二、Fast Run 简述
目前,主流的框架几乎都使用了算子(Operator)的概念来抽象数学计算,如卷积算子,矩阵乘算子等。MegEngine 也使用了算子 这一概念。此外,在底层,我们开发了名为 MegDNN 的计算库,用以完成实际的数学计算。 MegDNN 仅提供数学计算能力。MegDNN 的顶层也是按照算子的概念组织的,对不同的后端,分别封装了 MegDNN 算子。一个 MegDNN 算子内部则可能有多个该算子的算法,MegEngine 将算法抽象为 Algorithm,一个 Algorithm 对象可以完成该算子的计算。
以卷积算子为例,ARM 上,MegEngine 实现了非常通用的 Im2col 算法,有特定条件下性能卓越的 Winograd 算法,有在小尺寸卷积时高性能的 Direct 直接卷积算法等。CUDA 上,有调用 cuDNN 库函数的方法等等。从 MegEngine 算子到 MegDNN 算子再到算法的关系如下图所示:
一个 MegEngine 算子可能持有一个或多个 MegDNN 算子来完成计算,一个 MegDNN 算子需要从多个算法对象中选择一个来执行计算。为了极致的计算性能,需要在开始网络计算之前,给 MegDNN 算子选好最快的算法。
Fast Run 的思路很直接,在网络计算开始之前,将每个 MegDNN 算子中所有可行的算法全部运行一次(Profiling),并将性能数据记录下来,将最快的算法设置给 MegDNN 算子。Fast Run 成立的前提条件是算法运行时间是稳定的,这样比较每个算法的 Profiling 数据才有意义。
最后是确定 Fast Run 执行的时间点。MegEngine 有统一的内存管理,各 MegEngine 算子需要在计算开始前向内存规划单元申请足够的计算时内存,这一内存包括了其内部的 MegDNN 算子计算时需要的内存,而 MegDNN 算子计算时需要的内存完全由算法决定。这就要求,MegDNN 此刻已经确定了将要使用的算法。自然地,MegEngine 选择在调用该接口之前执行 Fast Run 流程。这样,当 Fast Run 流程完成时,各 MegDNN 算子都设置了性能最好的算法。
Fast Run 执行的代价是显然的,它会显著增加第一次网络执行的时间。Fast Run 的流程如下图:
Fast Run 有下面两种使用方式,区别在于上图中写入的 Cache 文件不同:
- 离线 Fast Run,离线 Fast Run 分两步,分别在不同的进程中完成。第一步先将整个网络计算执行一遍,这一过程中,Fast Run 会将各个算法的性能数据写到一个专门的数据结构中,最后数据被统一写入一个 Cache 文件,随后进程退出,这个过程称之为“搜参”。第二步,加载同样的网络,通过 MegEngine 的接口将 Cache 文件读入。可以看出,离线 Fast Run甚至可以在不同的设备上进行。
- 在线 Fast Run,在线 Fast Run 在同一个进程完成的。前半段与离线 Fast Run 的流程相同,Fast Run 后,各算法的性能数据保存在内存中的一个数据结构之中。此时,进程不会退出。后续可以给网络加载不同的输入数据,此时各 MegDNN 算子中已设置好性能最好的算法。并且,也可以初始化另外的网络,亦可以像离线 Fast Run 的后半部分一样,从当前的数据结构中读取算法。
总的来说,Fast Run 提供搜参和记录的功能。它的作用是给网络中的各个 MegDNN 算子选择当前参数下性能最好的算法。由于 Fast Run 对每个 MegDNN 算子执行同样的操作,因此它在前向推理和反向传播时都能使用。目前,MegEngine 支持 CUDA、CPU、ROCM 三个后端的 Fast Run ,MegEngine 的用户们在训练和部署时,均广泛使用 Fast Run。
三、Fast Run 原理
Fast Run 中,Profiling 一个 MegDNN 算子并设置算法,会经历 4 个步骤,其流程如下图示:
这一流程中,需要注意一些细节:
1、递归搜参:MegDNN 中普遍存在算子嵌套的情况。例如,Convolution 算子中,Im2col 算法会使用 MegDNN 的 MatMul 算子执行矩阵乘计算。那么,Convolution 的性能直接受到 MatMul 性能的影响。可以看到,在 Profiling 一个 Convolution 算子之前,需要 MatMul 算子执行的性能数据已知。为了解决这个问题,Fast Run 使用了递归的方式,来解决搜参时的算子嵌套问题。如上图中虚线框所示,一个 MegDNN 算子,在获取所有可用算法之后,会调用每个算法的接口,询问该算法是否依赖子算子并保存相关结果,若最终相关结果不为空,则会先对子算子进行一次 Profiling,此后,再 Profiling 顶层的算子时,其使用的子算子会有最优的算法保存在 Cache 中。
2、Fast Run 性能数据保存:Fast Run 性能数据存取离不开 Cache。MegEngine 提供了两种 PersistentCache,两种 Cache 区别于数据保存的位置(内存或是文件)。Cache 的结构如下图所示:
MegEngine 中,PersistentCache 对象是单例的,两种 Cache 都保证线程安全。Cache 维护一个从 category 信息到一个集合的映射的集合,此处 category 是一个后端的记录信息。Category 是一个字符串,由后端信息和算子类型拼接获得,后端信息 由设备区分,例如 CUDA 的后端信息由设备名称、NVIDIA 驱动版本和 CUDA 运行时库版本信息组成;CPU 作为后端时,则只记录设备名称。MegEngine 中只有 CUDA、CPU、ROCM 三种类型有对应的 categoty 生成,这也是 MegEngine 目前仅支持在 CUDA、CPU、ROCM 三个后端支持 Fast Run 的原因。算子类型 由算子名称、Cache 版本信息两部分组成。
一个 category 映射到一个集合,该集合维护单个 MegDNN 算子的信息到其所有可用算法的 Profiling 结果的映射。该集合的 key值 由 MegDNN 算子的所有输入 Tensor 的尺寸和算子的全部参数组成(这些参数能够完全决定一个算法是否可用)。value值 是一个数组,保存每个 Profiling 过的算法的时间、所需额外的空间等信息,并排序。排序时,以运行时间进行升序排列,并且保证了序列中每个算法使用的内存必须小于其前一个算法使用的内存 – 这样序列中不存在一个算法既慢于另一个算法,又使用更多的内存。一个 Cache 中可以存在不同后端的 Fast Run 结果,只要它们的 category 不同。
在一些常见的模型上,推理时关闭和开启 Fast Run,性能表现如下:
从工程落地中 Fast Run 的使用情况来看,绝大部分场景下,能显著降低网络运行时间。
四、Fast Run 使用
MegEngine 可配置的参数众多,很多都是工程落地的解决方法,在工业上经过大量的实践。其中一些参数与 Fast Run 的使用有密切的关系,这里详细阐述它们的使用。
4.1 开启 Fast Run
源代码级别使用 Fast Run 可以参照 MegEngine 自带的可执行程序 load_and_run,如果仅关注利用 load_and_run 测试模型,有下面两个参数需要使用:
- --full-run/--fast-run,搜参的两种模式,需用户选择其中一种模式,两者的区别在于 Profiling 时,生成的 MegDNN 算子的可用算法集大小不同。--full-run 时,会 Profiling MegDNN 算子内所有的可用算法,包括最朴素的算法(MegDNN 算子至少有一个算法,保证任何参数下均可用,运行慢)。--fast-run 则会排除朴素算法。如果想要减少 Profiling 的时间开销,可以选择使用 --fast-run 模式,此时需要注意的是,如果网络中有参数过于特殊的算子,则该算子可能面临没有可用算法的情况(优化过的算法不可用、朴素的算法被排除),此时 MegEngine 会报出“没有可用算法”的错误并退出。
- --fast-run-algo-policy,指定 Cache 文件的路径,文件中的性能数据会被读入内存,被全局唯一的 PersistentCache 对象持有。进程退出前,PersistentCache 中的性能数据会全部写入该文件。
两个参数可以单独使用,也可以一起使用:
- 单独使用 --full-run/--fast-run,Profiling 数据保存在内存中。
- 两者一起使用,文件中的性能数据首先会被读入内存。如果文件为空,所有 MegDNN 算子完成搜参后,性能数据写回文件。如果文件不为空,且某个 MegDNN 算子能从 Cache 中查询到性能数据,则不会进行搜参,余下不能查到性能数据的,则会搜参。这样实现了断点搜参的功能,MegEngine 称之为“续搜“。如果 Fast Run 时程序因为某些原因异常退出,”续搜“能使 Fast Run 在下一次能够连上。“续搜”也能让多个模型的性能数据可以合并在一个 Cache 文件中。如果所有 MegDNN 算子都能从 Cache 中查到性能数据,则搜参不会发生,网络具有最好的性能。
- 单独使用 --fast-run-algo-policy,文件中的性能数据首先会被读入内存,如果 Cache 中没有记录,不“续搜”,以经验值设置 MegDNN 算子的算法,性能可能不是最优。
在使用 Fast Run 时,可以配合 --verbose 一起使用,程序将详细打印 Fast Run 时的调试信息,包括 MegDNN 算子的名称,输入输出的尺寸信息,设置的算法名称等。如果发现性能不符合预期,比如当加载的模型和 Cache 文件不匹配时,通常会发生“续搜”,造成网络执行时间很长的假象。因此,我们强烈推荐在此时使用 --verbose 参数来观察程序工作是否符合预期。
4.2 算法属性
MegDNN 中某些算法具有独特的属性,会影响向 MegDNN 算子设置算法,当前使用的 属性 有:
- REPRODUCIBLE:具有 REPRODUCIBLE 属性的算法,可保证计算结果比特对齐。Fast Run 中,在从 Cache 中读算法信息时提供了对 REPRODUCIBLE 属性的支持。设置 --reproducible,Fast Run 会从 Cache 中选择性能最好的且具有 REPRODUCIBLE 属性的算法。在 Profiling 阶段,并不区分算法是否 REPRODUCIBLE,这样 Cache 中的算法既有 REPRODUCIBLE 属性的,也有非 REPRODUCIBLE 属性的,具备一定的泛用性。
- NAIVE:只有 MegDNN 中最朴素的算法具有 NAIVE 属性。--full-run 和 --fast-run 的区别就在于 --fast-run 通过该属性筛除了运行最慢的朴素算法。
4.3 weight 前处理
有些算法,在计算时需要对数据进行辅助转换。其中,对权重 weight 的转换可以是一次性的,这样可以节省运行时间。例如 Winograd 算法,其权重可以在进行卷积计算之前转好,节约相当一部分运行时的性能开销。MegEngine 在 GraphCommonOptimizeOptions 中提供了 weight_preprocess 选项来支持部署时权重的提前转换功能。一旦设置 weight_preprocess,对于那些 weight 能够提前转换的算法,其性能数据将不会包含权重转换的时间。简单的说,在搜参阶段设置 weight_preprocess,会影响算法的性能数据,从而 Cache 中算法的性能数据排序可能不同。如果 Cache 是在开启 weight 前处理的情况下搜参得到,部署时务必要开启 weight 前处理以获得更好的性能,否则有性能下降的风险。Fast Run 与 weight 前处理不是必需的关系,两者可以分开使用。不过通常情况下,两者结合使用可以获得更好的性能.
4.4 Fast Run 版本
Fast Run 的版本信息以字符串的形式表示在 Cache 的 category 中。Cache 具有兼容性,可以允许不同的版本的 MegEngine 下的搜参结果集合在同一个 Cache 中,Cache 中看到的是不同的 category。但是用户在使用过程,依然需要注意 Fast Run 的版本。一般地,如果 MegDNN 的算法发生了删除或者是属性的变动,Fast Run 的版本信息会发生变化。Fast Run 版本信息变化后,需要重新搜参。
深度学习框架如何自动选择最快的算法?Fast Run 让你收获最好的性能!的更多相关文章
- 28款GitHub最流行的开源机器学习项目,推荐GitHub上10 个开源深度学习框架
20 个顶尖的 Python 机器学习开源项目 机器学习 2015-06-08 22:44:30 发布 您的评价: 0.0 收藏 1收藏 我们在Github上的贡献者和提交者之中检查了用Python语 ...
- 贾扬清分享_深度学习框架caffe
Caffe是一个清晰而高效的深度学习框架,其作者是博士毕业于UC Berkeley的 贾扬清,目前在Google工作.本文是根据机器学习研究会组织的online分享的交流内容,简单的整理了一下. 目录 ...
- Reading | 《TensorFlow:实战Google深度学习框架》
目录 三.TensorFlow入门 1. TensorFlow计算模型--计算图 I. 计算图的概念 II. 计算图的使用 2.TensorFlow数据类型--张量 I. 张量的概念 II. 张量的使 ...
- 从TensorFlow 到 Caffe2:盘点深度学习框架
机器之心报道 本文首先介绍GitHub中最受欢迎的开源深度学习框架排名,然后再对其进行系统地对比 下图总结了在GitHub中最受欢迎的开源深度学习框架排名,该排名是基于各大框架在GitHub里的收藏数 ...
- 转:TensorFlow和Caffe、MXNet、Keras等其他深度学习框架的对比
http://geek.csdn.net/news/detail/138968 Google近日发布了TensorFlow 1.0候选版,这第一个稳定版将是深度学习框架发展中的里程碑的一步.自Tens ...
- 深度学习与CV教程(8) | 常见深度学习框架介绍
作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/37 本文地址:http://www.showmeai.tech/article-det ...
- [深度学习大讲堂]从NNVM看2016年深度学习框架发展趋势
本文为微信公众号[深度学习大讲堂]特约稿,转载请注明出处 虚拟框架杀入 从发现问题到解决问题 半年前的这时候,暑假,我在SIAT MMLAB实习. 看着同事一会儿跑Torch,一会儿跑MXNet,一会 ...
- [转]Caffe 深度学习框架上手教程
Caffe 深度学习框架上手教程 机器学习Caffe caffe 原文地址:http://suanfazu.com/t/caffe/281 blink 15年1月 6 Caffe448是一个清 ...
- 深度学习框架-caffe安装-环境[Mac OSX 10.12]
深度学习框架-caffe安装 [Mac OSX 10.12] [参考资源] 1.英文原文:(使用GPU) [http://hoondy.com/2015/04/03/how-to-install-ca ...
随机推荐
- gitlab git 安装
1.配置yum源 vim /etc/yum.repos.d/gitlab-ce.repo [gitlab-ce] name=Gitlab CE Repository baseurl=https://m ...
- ios多线程开发基础
多线程编程:下载数据时,开辟子线程,减少阻塞时间,和主线程并发运行,提升用户体验 1.Thread 1>新建Thread对象,带一selector方法,调用start方法,开启子线程 2> ...
- [网络流24题]最长k可重区间集[题解]
最长 \(k\) 可重区间集 题目大意 给定实心直线 \(L\) 上 \(n\) 个开区间组成的集合 \(I\) ,和一个正整数 \(k\) ,试设计一个算法,从开区间集合 \(I\) 中选取开区间集 ...
- WIN10 报错及解决方法
你的电脑/设备需要修复,无法加载操作系统,原因是关键系统驱动程序丢失或包含错误 \windows\system32\drivers\zklvv.sys 错误代码:0XC000007b 解决方法: 用U ...
- 何为“Secure Contexts”安全内容? 终于说明白了!
何为"Secure Contexts"安全内容? 终于说明白了! 看图说话 [途径1]:地址栏输入: edge://flags/ 按需设置选项后,重启浏览器即可. Allow ...
- 详解Window10下使用IDEA搭建Hadoop开发环境
前言 经过三次重装,查阅无数资料后成功完成hadoop在win10上实现伪分布式集群,以及IDEA开发环境的搭建.一步一步跟着本文操作可以避免无数天坑. 下载安装Hadoop 下载安装包 进入官网下载 ...
- 简单快速安装Apache+PHP+MySql服务环境(四)—— 将php版本升级到7.2
书接上文,简单快速安装Apache+PHP+MySql服务环境(二)-- centos使用yum安装指定版本的php. 随着各种PHP框架的升级,对PHP的版本也有了更高的要求,所以笔者也尝试着更新升 ...
- ecshop二次开发笔记--订单表结构ecs_order_info说明
-- 表的结构 `ecs_order_info` CREATE TABLE IF NOT EXISTS `ecs_order_info` ( `order_id` mediumint(8) uns ...
- js学习笔记之正则
() 是为了提取匹配的字符串.表达式中有几个()就有几个相应的匹配字符串.(\s*)表示连续空格的字符串.[]是定义匹配的字符范围.比如 [a-zA-Z0-9] 表示相应位置的字符要匹配英文字符和数字 ...
- HTML5 socket
client: <!DOCTYPE html> <html> <head> <title></title> <meta http-eq ...