InfiniTensor/README_CN.md

7.6 KiB
Raw Blame History

使用指南

目录

编译

推荐使用 Ubuntu-22.04,本文以此环境为例。

  1. 使用 apt 安装依赖

    如果不使用 Ubuntu-22.04,部分软件版本可能不够高。

    sudo apt-get install make cmake build-essential python-is-python3 python-dev-is-python3 python3-pip libdw-dev
    
  2. 更新 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
    
  3. 编译并安装 python 库

    第一次执行会同时安装 python 依赖库,比较慢

    仅编译 CPU 部分:

    make install-python
    

    编译 GPU 部分:

    make install-python CUDA=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())

测试

除了单元测试 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。