-
背景介绍
-
什么是AI编译框架,有什么好处?
- AI编译框架是连接深度学习模型和硬件加速器(如GPU、TPU)的桥梁。它负责将高级语言编写的深度学习模型转换为可在特定硬件上高效执行的底层代码。
- 性能优化:
- 硬件加速: AI编译框架能够将模型中的运算映射到硬件加速器上,充分利用硬件的并行计算能力,从而大幅提升模型的运行速度。
- 算子优化: 框架可以对模型中的算子进行优化,如算子融合、内存布局优化等,减少计算冗余和内存占用,进一步提高计算效率。
- 量化和剪枝: 一些框架支持模型量化和剪枝技术,通过降低模型的精度和复杂度,在保持模型性能的同时,大幅减少模型的存储空间和计算量,使其更适合在资源受限的设备上部署。
- 跨平台部署:
- 多种后端支持: AI编译框架支持多种后端,如TensorFlow、PyTorch、ONNX等,开发者可以灵活选择适合自己的后端进行模型部署。
- 一次编写,多处运行: 框架的跨平台特性使得开发者只需编写一次模型代码,即可在不同的硬件平台上运行,无需针对每个平台进行单独适配。
- 简化开发流程:
- 高级API: AI编译框架提供高级的API和丰富的模型库,开发者无需深入了解底层硬件和编译细节,即可轻松构建、训练和部署深度学习模型。
- 自动微分: 框架通常内置自动微分机制,开发者只需定义模型的正向传播过程,框架会自动计算梯度,简化了模型训练过程。
- 可视化工具: 许多框架提供可视化工具,帮助开发者直观地了解模型的结构、计算图和性能瓶颈,便于调试和优化。
- 形象比喻:
- 如果将深度学习模型比作一份菜谱,硬件加速器比作厨房里的各种厨具,那么AI编译框架就是一位经验丰富的大厨。大厨能够根据菜谱和厨具的特点,对烹饪步骤进行优化,选择合适的厨具,并合理安排烹饪顺序,从而在保证菜品美味的同时,最大限度地提高烹饪效率。
-
AI编译框架主要分为两种运行模式:动态图模式和静态图模式。
-
1. 动态图模式(Eager Execution):
- 特点: 动态图模式下,模型的计算图是动态构建的,即每一步计算都会立即执行并返回结果。这种模式更符合Python的编程习惯,方便调试和修改模型。
- 优点:
- 易用性: 符合Python编程习惯,代码编写和调试更直观。
- 灵活性: 模型结构可以根据输入数据动态调整,方便实现控制流、循环等复杂结构。
- 交互性: 可以实时查看中间结果,方便调试和分析。
- 缺点:
- 性能: 由于每一步计算都需要立即执行,动态图模式的运行效率相对较低。
- 部署: 模型结构不固定,难以进行优化和部署。
-
2. 静态图模式(Graph Execution):
- 特点: 静态图模式下,模型的计算图在编译时提前构建好,后续的计算只需按照图中的顺序执行。这种模式更适合模型的优化和部署。
- 优点:
- 性能: 计算图提前构建好,可以进行全局优化,如算子融合、内存布局优化等,运行效率更高。
- 部署: 模型结构固定,易于序列化和部署到不同的平台。
- 缺点:
- 灵活性: 模型结构固定,难以动态调整。
- 调试: 中间结果不可见,调试相对困难。
-
为什么会出现两种模式?
- 两种模式的出现是为了在易用性、灵活性和性能、部署效率之间取得平衡。
- 动态图模式更适合模型的原型设计、调试和研究,方便开发者快速验证想法和实验。
- 静态图模式更适合模型的生产部署,可以最大限度地发挥硬件性能,提高模型的运行效率。
-
-
动态图和静态图
- 动态图
- 设置MindSpore的运行模式为动态图模式(PYNATIVE_MODE),即计算图在运行时动态构建。
- 静态图
- 静态图模式与动态图模式在输出上的差别:
- 输出内容:
- 理论上: 在相同的网络结构和输入数据下,静态图模式和动态图模式的输出结果应该是一致的。
- 实际情况: 由于数值计算和优化过程中的微小差异,两种模式的输出结果可能会有非常细微的差别,但这些差别通常不会对模型的性能和效果产生显著影响。
- 输出时间:
- 静态图模式: 第一次执行时,由于需要构建和优化计算图,可能会比动态图模式稍慢。但在后续执行中,由于计算图已经构建好,静态图模式通常会比动态图模式更快。
- 动态图模式: 每次执行都会重新构建计算图,因此每次执行的时间相对稳定。
- 输出内容:
-
静态图模式开启方式
-
使用场景
- MindSpore编译器重点面向Tensor数据的计算以及其微分处理。因此使用MindSpore API以及基于Tensor对象的操作更适合使用静态图编译优化。其他操作虽然可以部分入图编译,但实际优化作用有限。另外,静态图模式先编译后执行的模式导致其存在编译耗时。因此,如果函数无需反复执行,那么使用静态图加速也可能没有价值。
-
基于装饰器的开启
- @ms.jit
def run(x):
model = Network()
return model(x)- 使用了
@ms.jit
装饰器。这种方式被称为基于装饰器的静态图加速。它的原理是: @ms.jit
装饰器会将被装饰的函数run(x)
标记为需要进行静态图编译。- 当
run(x)
函数第一次被调用时,MindSpore 会将其转换为静态计算图,并进行优化。 - 之后的调用都会直接执行优化后的静态计算图,从而获得更高的性能。
这种方式的优点是简单易用,只需在函数定义前添加
@ms.jit
装饰器即可。缺点是只能对函数级别的代码进行静态图加速,灵活性较低。
- 使用了
- def run(x):
model = Network()
return model(x)
run_with_jit = ms.jit(run)- 这里先定义了一个普通的函数
run(x)
,然后通过ms.jit(run)
将其转换为静态图模式执行的函数run_with_jit
。这种方式被称为显式调用ms.jit
接口。它的原理是: ms.jit(run)
会将函数run(x)
转换为一个新的函数run_with_jit
。run_with_jit
函数内部会自动构建静态计算图,并进行优化。- 调用
run_with_jit
时,会直接执行优化后的静态计算图。这种方式的优点是更加灵活,可以对任意函数进行静态图加速,甚至可以对类的方法进行加速。缺点是代码稍显繁琐。
- 这里先定义了一个普通的函数
- @ms.jit # 使用ms.jit装饰器,使被装饰的函数以静态图模式运行
def construct(self, x):
x = self.flatten(x)
logits = self.dense_relu_sequential(x)
return logits- 由于
construct
方法已经被@ms.jit
装饰,因此调用model(input)
时,会直接执行优化后的静态计算图。 - 在 MindSpore 中使用
@ms.jit
装饰器将模型的construct
方法转换为静态图模式。这种方式可以更精细地控制哪些部分的代码需要进行静态图加速,从而在性能和灵活性之间取得更好的平衡。
- 由于
- @ms.jit
-
基于全局context的开启
- ms.set_context(mode=ms.GRAPH_MODE)
- 这段代码与之前的动态图模式示例基本一致,用于构建并运行一个简单的神经网络模型进行图像分类。主要区别在于,这里使用
ms.set_context(mode=ms.GRAPH_MODE)
设置了 MindSpore 的运行模式为静态图模式。
-
- 动态图
-
《昇思25天学习打卡营第9天|使用静态图加速》
于 2024-07-04 23:56:44 首次发布