深度学习编译器是一种专门针对深度学习应用的编译器,它的作用是将深度学习模型(通常由高级框架如TensorFlow、PyTorch等构建和训练)转换成在特定硬件平台上高效运行的低级代码。
深度学习编译器通常在前端接收不同深度学习框架(如TensorFlow, PyTorch等)生成的模型,并将其转换成统一的中间表示(Intermediate Representation, IR)。IR是一种抽象的模型表示,它提供了一种平台无关的方式来描述计算图和操作。IR可以是图级别的,也可以是更低层次的。然后,在IR层面上,编译器会应用一系列的图优化技术,如节点融合、常量折叠等,以简化模型并减少计算量。根据目标硬件的特性(如CPU的SIMD指令集、GPU的线程并行性等),编译器会进行特定的优化,如算子融合、内存访问模式优化等。在调度阶段,编译器为模型中的每个操作生成具体的执行计划,包括决定循环的顺序、数据的布局、并行化策略等。最后,编译器将优化后的IR和调度信息转换成目标硬件的机器码,并在目标设备上执行编译后的模型。

TVM

Apache TVM是端到端的开源机器学习编译框架,使基于不同机器学习框架(如TensorFlow, PyTorch,MXNet, ONNX等)的机器学习模型在不同硬件上(CPU、GPU、ASIC等)经过优化高效运行。
image
TVM进行模型优化过程如图所示,TVM首先将其他框架导出的模型导入编译器前端,之后将其转换成TVM的高级中间表示(High-level IR)Relay,之后使用Relay进行图级别的优化。在高级优化完毕后,编译器将模型分解为多个子图,并将中间表示转换为张量表达式(TE),TE支持循环切分、并行化、循环展开和融合等基本优化操作,TVM通过Auto-tuning module模块搜索最佳的底层循环优化调度,为每个子图提供最佳调度方案。最后,编译器将所有子图的中间表示(TE)转换为底层中间表示(TIR),在经过优化后TIR传递给需要部署的编译器后端(如LLVM,NVCC,BYOC等),编译器后端生成对应的机器码,并将模型编译为可链接对象模块通过TVM runtime动态加载模型。

TVM加速Pytorch模型示例:

  1. 安装 TVM:TVM 可以通过 pip 或源代码安装。使用 pip 安装是最简单的方式,如果需要支持特定的硬件后端(如 CUDA),可以使用 pip install tvm[cuda] 等命令。也可以从源代码构建 TVM。

  2. 准备机器学习模型:以Pytorch为例,确保你有一个已经训练完成的PyTorch模型。如对于ResNet18,可以通过torchvision.models模块轻松获得。使用pretrained=True参数来下载和加载一个已经预训练好的模型。

  3. 转换为TorchScript:利用torch.jit.trace方法对模型执行跟踪,将其转换为TorchScript格式。这一步是必要的,因为TVM前端需要TorchScript格式的模型来进行后续处理。

    1
    2
    3
    4
    5
    6
    import torch
    # 假设你已经定义了 PyTorch 模型
    model = ...
    input_shape = [1, 3, 224, 224] # 定义模型的输入尺寸
    input_data = torch.randn(input_shape)
    scripted_model = torch.jit.trace(model, input_data).eval()
  4. 转换为TVM Relay:使用TVM的Relay前端,将TorchScript模型转换成Relay中间表示(IR)。这一步涉及将PyTorch的算子映射并替换为TVM的算子。

    1
    2
    3
    4
    import tvm
    from tvm import relay
    # 将 PyTorch 模型转换为 Relay IR
    mod, params = relay.frontend.from_pytorch(model, input_shape)
  5. 构建Relay模型:通过relay.build函数,将Relay模型转换成目标硬件平台的可执行代码。选择”llvm”作为目标,这通常适用于CPU架构。其中opt_level 控制优化级别,范围从 0 到 3,级别越高,优化程度越高,但编译时间也会更长。

    1
    2
    3
    4
    5
    6
    7
    8
    # 为 CPU 优化
    target = tvm.target.Target("llvm")
    # 为 NVIDIA GPU 优化
    target = tvm.target.Target("cuda")
    # 为 ARM CPU 优化
    target = tvm.target.Target("llvm -mtriple=aarch64-linux-gnu")
    with tvm.transform.PassContext(opt_level=3): # 指定优化级别
    lib = relay.build(mod, target=target, params=params)
  6. 另外,TVM 提供了自动调优功能,可以进一步优化模型在特定硬件上的性能。它会自动探索不同的调度策略和参数组合,以找到最优的执行方式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 创建任务
    task = autotvm.task.create(...)

    # 运行自动调优
    tuner = autotvm.tuner.RandomTuner(task)
    n_trial = 1000 # 设置最大调优次数
    tuner.tune(n_trial=n_trial)

    # 应用调优结果
    with tvm.transform.PassContext(opt_level=3, config={"relay.backend.use_auto_scheduler": True}):
    lib = relay.build(mod, target=target, params=params)
  7. 运行编译后的模型:使用TVM runtime来加载和运行编译后的模型。首先,确保LLVM运行时被启用,然后选择目标CPU核心,并加载模型。

    1
    2
    3
    4
    5
    6
    7
    tvm.runtime.enabled("llvm")
    ctx = tvm.cpu(0) # 指定CPU核心为例
    # 创建运行时模块
    module = tvm.contrib.graph_executor.GraphModule(lib["default"](device))
    # 设置输入数据
    input_data = ...
    module.run(data=input_data)

Tensorflow XLA

XLA(Accelerated Linear Algebra)是Tensorflow内置的一种深度学习编译器,用于加速TensorFlow模型的运行速度并改进内存使用效率。TensorFlow XLA,作为TensorFlow生态系统内的关键组件,XLA能够针对不同硬件后端生成定制化代码,如利用CUDA为NVIDIA GPU生成优化并行代码,或使用ROCm为AMD GPU生成代码,实现设备特定的优化。此外,XLA在优化过程中考虑多线程和并行执行,通过重构计算图来提升执行效率,充分利用多核CPU或多GPU的计算资源。在内存优化方面,XLA通过改进内存访问模式,减少全局内存访问,转而使用局部缓存或寄存器,有效提升了模型的运行速度。
image.png
在TensorFlow会话配置被设置以启用XLA时,如将global_jit_level设置为tf.OptimizerOptions.ON_1,用户定义的模型被构建成TensorFlow的计算图,该图经过分析识别出使用XLA优化的部分。这些部分被转换成XLA的中间表示——高级优化语言(HLO IR)。XLA接着对HLO图执行目标无关的优化,如常量传播、死码消除和表达式简化等。之后将HLO图传递给XLA后端,XLA根据目标硬件的特性,如CPU或GPU,进行特定的优化,包括内存访问模式优化和操作融合。最后优化后的HLO图最终被转换成目标硬件的机器代码,如利用LLVM等工具为CPU生成机器码,或为GPU生成CUDA或PTX代码。
在TensorFlow中启用XLA(加速线性代数)可以通过以下几种方式进行:

  1. 使用tf.function装饰器: 通过为TensorFlow函数添加tf.function装饰器并设置参数jit_compile=True,可以明确指示TensorFlow使用XLA进行编译。这要求函数的输入形状是静态的或者可以被推断出来。

    1
    2
    3
    4
    5
    6
    import tensorflow as tf

    @tf.function(jit_compile=True)
    def train_step(x, y):
    # 定义训练步骤的计算图
    return compute_loss(x, y) # 假设compute_loss是一个已定义的函数
  2. 自动聚类: 使用环境变量TF_XLA_FLAGS来启用XLA的自动聚类功能。这种方法不需要修改现有的TensorFlow代码,XLA会自动处理可以被编译的TensorFlow图的部分。在命令行中启动程序之前,设置环境变量:

    1
    2
    export TF_XLA_FLAGS=--tf_xla_auto_jit=2
    python my_tensorflow_program.py

    对于CPU上的自动聚类,可以使用:

    1
    export TF_XLA_FLAGS="--tf_xla_auto_jit=2 --tf_xla_cpu_global_jit"