InfiniTensor/README_CN.md

11 KiB
Raw Blame History

使用指南

目录

环境准备

推荐使用 X86-64 机器以及 Ubuntu-22.04,本文以此环境为例。

  1. 确认 GCC 版本为 11.3 及以上的稳定版本,如若您的机器 GCC 版本不满足此条件,请自行编译安装,下述方式二选一:

    GCC 官方文档 网友安装分享

  2. 确认 CMake 版本为 3.17 及以上的稳定版本, 如若您的机器 CMake 版本不满足此条件,请自行编译安装,下述方式二选一:

    CMake 官方文档 网友安装分享

  3. 第三方加速卡软件资源安装,目前本项目已经适配了如下的第三方加速卡:

    如您的第三方加速卡为英伟达 GPU请参考英伟达官方文档进行驱动安装CUDA Toolkit 安装Cudnn 安装Cublas 安装,我们强烈建议您规范安装,统一到一个目录下,以免不必要的麻烦。 如您的第三方加速卡为寒武纪 MLU请参考寒武纪官方文档进行驱动安装CNToolkit 安装CNNL 安装,我们强烈建议您规范安装,统一到一个目录下,以免不必要的麻烦。另外请注意,由于 MLU 上层软件建设适配程度有限,如您在其覆盖的机器,操作系统之外运行,需要在安装驱动之后使用上层软件的 Docker。

  4. 确认您安装了 makebuild-essential python-is-python3 python-dev-is-python3 python3-pip libdw-dev如您的机器没有上述基础依赖请自行按需安装。

    在使用 apt-get 工具情况下,您可以这样子执行。

    sudo apt-get install make cmake build-essential python-is-python3 python-dev-is-python3 python3-pip libdw-dev
    

    其他工具请自行上网搜寻

  5. 更新pip并切换到清华源

    python -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip
    pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
    

编译本项目

推荐使用 X86-64 机器以及 Ubuntu-22.04,本文以此环境为例。

  1. 编译并安装 python 库

    第一次执行会同时安装 python 依赖库,耗时略长,请耐心等待

    仅编译 CPU 部分,不编译第三方计算卡:

    make install-python
    

    编译 CPU 部分,同时编译英伟达 GPU 部分:

    make install-python CUDA=ON
    

    编译 CPU 部分,同时编译寒武纪 MLU 部分:

    make install-python BANG=ON
    

使用方法

项目管理功能已写到 Makefile,支持下列功能:

  • 编译项目:make/make build
  • 清理生成文件:make clean
  • 安装 python 库:make install-python
  • 测试 c++ 后端:make test-cpp
  • 测试 python 前端:make test-onnx

并使用下列环境变量传递选项参数:

  • TYPE:编译模式(debug/release),默认值为 release
  • CUDA:是否编译 CUDA 后端,默认为 OFFON 打开
  • BANG:是否编译寒武纪后端,默认为 OFFON 打开
  • BACKTRACE:是否启用栈回溯,默认为 ONOFF 关闭,建议调试时打开
  • TEST:是否编译 googletest,默认为 ONOFF 关闭,只有 test-cpp 时必要

python 前端应用指南

make install-python 会将项目的 python 前端以 pyinfinitensor 为名字安装到系统目录,可以直接 import pyinfinitensor 来使用。现阶段,项目的主要用法是从 onnx 导入模型进行优化,然后可以再导出优化后的模型到 onnx也可以直接运行推理。

导入 onnx 模型

支持的模型:

import onnx
from pyinfinitensor.onnx import OnnxStub
from pyinfinitensor import backend

stub = OnnxStub(onnx.load("model_file"), backend.cpu_runtime())

onnx.load 是 onnx 提供的加载函数,将 onnx 文件读取为保存在内存中的 onnx 模型。

OnnxStub 是 onnx 模型在项目中的表示,通过构造这个对象,将 onnx 模型导入到项目中。其构造器的第一个参数是 onnx 模型文件;第二个参数是模型运行的后端运行时,可以是 backend.cpu_runtime()backend.cuda_runtime()backend.bang_runtime()

构造出的 stub 对象可以用于操作项目中的模型和运行时。

优化

TODO

导出 onnx 模型

优化后的模型可以导出成 onnx 文件提供给其他运行时。

with open("optimized.onnx", "wb") as f:
    f.write(stub.to_onnx("optimized").SerializeToString())

stub.to_onnx(<name>) 将模型转换为 onnx 模型对象,<name> 将填写到 onnx 模型的 name 字段。序列化到文件的代码见官方示例

要可视化检查导出的模型文件,可以利用 onnx 提供的功能将所有的张量的形状推理出来再导出:

from onnx.shape_inference import infer_shapes

with open("optimized.onnx", "wb") as f:
    f.write(infer_shapes(stub.to_onnx("optimized")).SerializeToString())

然后用 Netron 绘制计算图。

执行推理

也可以使用项目的运行时执行推理。

第一步是将数据传入计算图。OnnxStub.inputs 是一个 Dict[str, Tensor],保存着模型的所有输入的名字和对象。可以用 items() 来遍历。

这个代码片段显示了如何打印出模型所有输入张量的名字、形状和对象指针:

for name, tensor in stub.inputs.items():
    print(name, tensor.shape(), tensor)

对于 resnet18-v2-7.onnx,会打印出:

data [1, 3, 224, 224] <backend.Tensor object at 0x7efeb828e3b0>

当然,地址是随机的。这个输出表明需要输入一个名为 “data”形为 1×3×224×224 的数据。通常来说,这表示一张 224×224 的 rgb 图片。而这个模型是一个 1000 分类的图像分类模型。

为了方便,这里我们向模型传入一个随机的数据。

import numpy

stub.init()
for name, tensor in stub.inputs.items():
    print(name, tensor.shape(), tensor)
    input = numpy.random.random(tensor.shape()).astype(numpy.float32)
    tensor.copyin_float(input.flatten().tolist())

stub.init() 为所有张量分配空间。空间是预分配的,所以不支持动态 size 的模型。

tensor.copyin_float(<data>) 向张量传入数据。其参数必须是一个 List[float],即压平的数据。类似的函数还有 copyin_int32(<data>)copyin_int64(<data>)

然后,调用 stub.run() 执行推理:

stub.run()

最后,将结果拷贝出来,传入类似:

stub.init()
for name, tensor in stub.outputs.items():
    print(name, tensor.shape(), tensor)
    print(tensor.copyout_float())

样例代码

您可以参照./example/Resnet/resnet.py的样例代码进行了解,并尝试运行。在这个文件中,我们使用了 Pytorch 构建了 resnet 网络。您可以查阅该脚本使用方式:

python resnet.py -h

在样例代码中,我们对定义的网络进行了序列化操作,并存储为模型文件。之后加载该模型文件,并转换为本项目的模型进行优化操作,再进行推理。您可以关注一下代码中 242 行之后的代码。请注意,您可以按照您的需求来进行操作,通常来说,您所需要撰写的代码就是加载模型,转换为本项目的模型进行优化,推理运行。

技术支持

如若您遇到了本项目的问题,请联系我们的技术支持团队

测试

除了单元测试 make test-cppmake test-onnx 之外,还可以用其他方式来测试单个模型导入导出和优化的正确性。

这个脚本利用 onnxruntime 来测试导出的模型是否与导入的模型等价:

import onnx
import numpy
import sys
from onnx import ModelProto, ValueInfoProto
from pyinfinitensor.onnx import OnnxStub
from pyinfinitensor import backend
from onnxruntime import InferenceSession


def infer(model: ModelProto, input) -> dict:
    collection = set()
    for node in model.graph.node:
        for output in node.output:
            collection.add(output)
    model.graph.output.extend([ValueInfoProto(name=x) for x in collection])
    session = InferenceSession(model.SerializeToString())
    i = session.get_inputs()[0].name
    return dict(
        zip(
            [x.name for x in session.get_outputs()],
            [x.flatten() for x in session.run(None, {i: input})],
        )
    )


model0 = onnx.load(sys.argv[1])
model1 = OnnxStub(model0, backend.cpu_runtime()).to_onnx("new")

input_shape = [x.dim_value for x in model1.graph.input[0].type.tensor_type.shape.dim]
input = numpy.random.random(input_shape).astype(numpy.float32)

output0 = infer(model0, input)[model0.graph.output[0].name]
output1 = infer(model1, input)[model1.graph.output[0].name]

print("error =", sum((output1 - output0) ** 2) / len(output0))

要运行脚本,先安装 onnxruntime

pip install onnxruntime

打印出的 error = ... 是两个模型输出张量的均方误差。对于不同的模型,这个误差最小为 0最大不超过 1e-9。