Directorio de artículos
prefacio
Este artículo presenta cómo compilar el entorno en , x86
y cómo usar el entorno para compilar y ejecutar en el entorno. También presenta cómo compilar y ejecutar en el entorno, y proporciona ejemplos detallados para la verificación, incluido el código de prueba, el código de prueba, modelar la inferencia y continuar . Como se muestra en la siguiente figura, se muestra la información de la arquitectura :docker
tvm
ARM
RPC
x86
arm
arm
rpc
acl
pytorch
arm
arm
autotvm
x86
cpu
Se recomienda encarecidamente utilizar
ubuntu:20.04
esta versión, ¡ubuntu:18.04
esta versiónglibc
caerá en el foso al actualizar! ! !
1. Cargue la imagen arm-ubuntu
docker
Extraiga la imagen del repositorio espejo arm-ubuntu
:
docker pull arm64v8/ubuntu:20.04
Dado que la arquitectura local cpu
es x86
la arquitectura, no hay forma de ejecutar directamente arm
la imagen de la arquitectura, y se necesita una herramienta de terceros: QEMU
QEMU
es un simulador de emulación multiplataforma de código abierto general, que puede simular la ejecución o construcción de aplicaciones bajo una arquitectura específica, como en Una aplicación x86
que se ejecuta en un sistema operativo con la misma arquitectura ARM
.
En la actualidad, qemu
hay dos formas de usar la simulación: una es docker
usar [usado en este blog] en combinación, y la otra es usar el código fuenteqemu
oficial para compilar e instalar el sistema correspondiente manualmente . Puede consultar este blog .iso
Al usar
docker
elarm
entorno construido ylscpu
verlo con instruccionescpu
,cpu
siguemodel name
siendointel
sí,arch
síaarch64
; al usar el código fuente compilado para construir el simulador, se especificaráqemu-system-aarch64
el modelo específico . Por ejemplo , como no he probado este método, no estoy seguro si es si o si..cpu
qemu-system-aarch64 -cpu cortex-a72
cpu
model name
intel
arm
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
Este comando instalará qemu-user-static
y, una vez completada la instalación, puede ejecutar la imagen en la arquitectura normalmente x86
en , pero este comando aún no se ha probado. Utilizo el siguiente método, puede consultar este artículo : primero descargue el paquete de instalación:docker
ARM
ubuntu
qemu-aarch64-static
Para qemu-aarch64-static
configurar:
sudo cp qemu-aarch64-static /usr/bin/
sudo chmod +x /usr/bin/qemu-aarch64-static
# 注册QEMU虚拟机
docker run --rm --privileged multiarch/qemu-user-static:register
Entonces se puede cargar normalmente arm-ubuntu
:
docker run --platform linux/arm64/v8 -it -v /home/liyanpeng/arm64v8_work:/home/liyanpeng/arm64v8_work -w /home/liyanpeng arm64v8/ubuntu:20.04 bash
# uname -a
# lscpu
2. Instale la biblioteca acl
ARM
La biblioteca informática es(Arm Compute Library, ACL)
un proyecto de código abierto que proporciona núcleos acelerados para la arquitectura y . Los binarios precompilados se pueden descargar desde el software ARM :ARM
CPU
GPU
# 将压缩包解压到 acl_tmp 目录
tar -zxvf arm_compute-v22.08-bin-linux-arm64-v8.2-a-neon.tar.gz -C acl_tmp
Al compilar directamente ARM
bajo la arquitectura runtime
, se informará un error y el directorio correspondiente debe ajustarse manualmente. Puede consultar tvm
un script proporcionado por el oficial: ubuntu_download_arm_compute_lib_binaries.sh
cp -r acl_tmp/include acl/
cp -r acl_tmp/arm_compute acl/include/
cp -r acl_tmp/support acl/include/
cp -r acl_tmp/utils acl/include/
cp -r acl_tmp/lib/arm64-v8.2-a-neon acl/lib
3. Compile el tiempo de ejecución del brazo
Antes de compilar, aún debe configurar elarm-ubuntu
entorno básico, incluidos los entornos básicos como C/C++
, CMake
y . No entraré en detalles aquí.Python
Modificar build/config.cmake
el archivo:
set(USE_LLVM OFF) # line 136(default)
set(USE_ARM_COMPUTE_LIB OFF) # line 236(default)
set(USE_ARM_COMPUTE_LIB_GRAPH_EXECUTOR "/home/liyanpeng/arm64v8_work/acl") # acl的路径
Compilar:
cd build
cmake ..
make runtime -j6
La información después de una compilación exitosa es la siguiente:
No olvides agregar tvm
el python
entorno:
export PYTHONPATH=$PYTHONPATH:/home/liyanpeng/arm64v8_work/tvm_work/tvm/python
tvm
Verificación de la versión:
import tvm
print(tvm.__version__)
4. Compilar en x86 y ejecutar en brazo
4.1 Cree el entorno de compilación arm en el entorno x86
Modificar build/config.cmake
el archivo:
set(USE_LLVM ON) # line 136
set(USE_ARM_COMPUTE_LIB ON) # line 236
set(USE_ARM_COMPUTE_LIB_GRAPH_EXECUTOR OFF) # line 237
Compilar:
cd build
cmake ..
make -j6
Se compila en poco tiempo:
Una vez completada la construcción , los operadores admitidos se pueden x86
compilar en el entorno . Solo se compila y no se puede ejecutar directamente en la plataforma.arm
x86
4.2 Probar si x86-ubuntu y arm-ubuntu pueden hacer ping
Esto se puede RPC(Remote Produce Call)
lograr con la ayuda de 编译在x86,运行在ARM
, por lo tanto, las direcciones que deben conocerse arm-ubuntu
: ip
Instale el paquete de herramientas de red en
:arm-ubuntu
apt-get update
# ifconfig
apt-get install net-tools
# ping
apt-get install inetutils-ping
arm-ubuntu
Dirección vista ip
:
x86-ubuntu
Dirección vista ip
:
Prueba x86-ubuntu
y arm-ubuntu
si puede ping
pasar:
# x86-ubuntu
ping 172.17.0.2
# arm-ubuntu
ping 172.17.0.3
4.3 Llamada RPC
arm-ubuntu
Comience en el entorno RPC
:
python -m tvm.exec.rpc_server --host 0.0.0.0 --port=9090
La información de inicio exitosa es la siguiente:
x86-ubuntu
Cree un archivo en el entorno rpc_test.py
con el siguiente contenido:
# rpc_test.py
import numpy as np
import tvm
from tvm import te
from tvm import rpc
from tvm.contrib import utils, tar
n = tvm.runtime.convert(1024)
A = te.placeholder((n,), name="A")
B = te.compute((n,), lambda i: A[i] + 1.0, name="B")
s = te.create_schedule(B.op)
local_demo = False
if local_demo:
target = "llvm"
else:
# target = "llvm -mtriple=armv7l-linux-gnueabihf" # Raspberry Pi 3B
# target = "llvm -mtriple=aarch64-linux-gnu"
# target = tvm.target.arm_cpu() # error: error adding symbols: file in wrong format
target = "llvm -mtriple=aarch64-linux-gnu -mattr=+neon"
func = tvm.build(s, [A, B], target=target, name="add_one")
# save the lib at a local temp folder
temp = utils.tempdir()
path = temp.relpath("lib_rpc_test.tar")
func.export_library(path, tar.tar)
print("lib path: ", path)
if local_demo:
remote = rpc.LocalSession()
else:
# The following is my environment, change this to the IP address of your target device
host = "172.17.0.5" # arm-ubuntu ip
port = 9090
remote = rpc.connect(host, port)
remote.upload(path)
func = remote.load_module("lib_rpc_test.tar")
# create arrays on the remote device
dev = remote.cpu()
a = tvm.nd.array(np.random.uniform(size=1024).astype(A.dtype), dev)
b = tvm.nd.array(np.zeros(1024, dtype=A.dtype), dev)
# the function will run on the remote device
func(a, b)
np.testing.assert_equal(b.numpy(), a.numpy() + 1)
time_f = func.time_evaluator(func.entry_name, dev, number=10)
cost = time_f(a, b).mean
print("%g secs/op" % cost)
El código anterior muestra una operación de suma, y el resultado de la ejecución es el siguiente:
arm-ubuntu
Puede ver la información de conexión desde x86-ubuntu
:
4.4 Uso de ACL
ACL
Para el uso, consulte el documento de muestratvm
oficial . Aquí hay un ejemplo. El método de uso es el mismo que en la sección anterior :RPC
# acl_test.py
import tvm
from tvm import relay
from tvm import rpc
from tvm.contrib import utils, tar
from tvm.relay.op.contrib.arm_compute_lib import partition_for_arm_compute_lib
import numpy as np
data_type = "float32"
data_shape = (1, 14, 14, 512)
strides = (2, 2)
padding = (0, 0, 0, 0)
pool_size = (2, 2)
layout = "NHWC"
output_shape = (1, 7, 7, 512)
# use a single max_pool2d operator
data = relay.var('data', shape=data_shape, dtype=data_type)
out = relay.nn.max_pool2d(data, pool_size=pool_size, strides=strides, layout=layout, padding=padding)
module = tvm.IRModule.from_expr(out)
# annotate and partition the graph for ACL
module = partition_for_arm_compute_lib(module)
# build the Relay graph.
target = "llvm -mtriple=aarch64-linux-gnu -mattr=+neon"
with tvm.transform.PassContext(opt_level=3, disabled_pass=["AlterOpLayout"]):
lib = relay.build(module, target=target)
# export the module
lib_path = './lib_acl.tar'
# cross_compile = 'aarch64-linux-gnu-c++'
# lib.export_library(lib_path, cc=cross_compile)
lib.export_library(lib_path)
# rpc
host = "172.17.0.2" # arm-ubuntu ip
port = 9090
remote = rpc.connect(host, port)
remote.upload(lib_path)
loaded_lib = remote.load_module("lib_acl.tar")
# run Inference
# dev = tvm.cpu(0)
# loaded_lib = tvm.runtime.load_module('lib_acl.so')
dev = remote.cpu(0)
module = tvm.contrib.graph_executor.GraphModule(loaded_lib['default'](dev))
d_data = np.random.uniform(0, 1, data_shape).astype(data_type)
map_inputs = {
'data': d_data}
module.set_input(**map_inputs)
module.run()
# get output
output = module.get_output(0)
print("TVM MaxPool2d[acl] output: ", output)
El resultado de la operación es el siguiente:
Los ejemplos anteriores solo muestran ACL
cómo usar un solo Maxpool2D
ejemplo básico. Si desea ver la implementación de cada operador en la red, consulte : tests/python/contrib/test_arm_compute_lib
.
5. La versión arm del entorno de tiempo de ejecución y compilación de tvm
5.1 Cree la versión arm del entorno de tiempo de ejecución y compilación de tvm
ARM
La compilación de tvm
la versión y la construcción del entorno de ejecución x86
son casi iguales a las de la versión.Puedes seguir este artículo: "Instalación y compilación de tvm en entorno linux y cómo vscode configura el entorno de depuración de conexión remota de tvm" para configuración, por lo que no entraré en detalles aquí. Sin embargo, cabe señalar que la versión requerida en el archivo arm-ubuntu
no se encontró en , por lo que aquí hay una pequeña modificación:conda/build-environment.yaml
llvmdev ==10.0.0
# conda/build-environment.yaml
# 这里将llvmdev更改为10.0.1版本
# 这样在编译时cmake会自动安装llvm
llvmdev ==10.0.1
Según la configuración anterior, modifique build/config.cmake
el archivo nuevamente:
set(USE_LLVM ON) # line 136
set(USE_ARM_COMPUTE_LIB ON) # line 236
set(USE_ARM_COMPUTE_LIB_GRAPH_EXECUTOR "/home/liyanpeng/arm64v8_work/acl") # line 237
Luego compílalo:
cd build
cmake ..
make -j6
Pasaron dos horas. . .
La información después de una compilación exitosa es la siguiente:
5.2 Acerca de la actualización de ubuntu 18.04 glibc cayó al hoyo
Desafortunadamente, en esta versión y versiones anteriores, pytorch 1.7.1
no se proporciona la arm
versión oficial pytorch
Aquí hay dos soluciones:
(1)
descargue una versión no oficial de la comunidad pytorch-aarch64
, por ejemplo: KumaTea proporciona oficialmente una versión a partir
(2)
de la versión , puede elegir una versión superior , pero aún tengo que decir que el oficial actualmente admite dos versiones principales, y otras versiones pueden ser inestables. La versión no oficial se selecciona aquí :pytorch 1.8.0
arm
pytorch
pytorch
tvm
[文章发布时]
pytorch 1.7
pytorch 1.4
pytorch-aarch64
pip install torch==1.7.1 torchvision==0.8.2 torchaudio==0.7.2 -f https://torch.kmtea.eu/whl/stable-cn.html
Al ver pytorch
la versión, se informa un error: [ Esta versión ImportError: /lib/aarch64-linux-gnu/libc.so.6: version "GLIBC_2.28" not found
se usó originalmente y no informará un error si cambia a esta versión, puede leer directamente la sección]ubuntu:18.04
ubuntu:20.04
5.3
Ver la glibc
versión actual del sistema
ldd --version
# or
strings /lib/aarch64-linux-gnu/libm.so.6 | grep GLIBC_
# Ubuntu 18.04: 2.27
# Ubuntu 20.04: 2.31
De acuerdo, puedes detenerte, te sugiero que te des la vuelta y saltes directamente a 5.3
la subsección, de lo contrario, ¡es posible que no puedas salir del pozo más tarde! ! !
La solución puede referirse a este blog :
# 安装依赖
apt-get install gawk
apt-get install bison
apt-get install wget
# 下载、解压并配置
wget http://ftp.gnu.org/gnu/libc/glibc-2.28.tar.gz
tar -zxvf glibc-2.28.tar.gz
cd glibc-2.28
mkdir build
cd build
../configure --prefix=/usr/local --disable-sanity-checks
# 安装
make -j6
make install
Parte de la información de registro durante el proceso de instalación es la siguiente:
No hay ningún mensaje de error que indique que la instalación se realizó correctamente.
# 查看原始的软连接
ll /lib/aarch64-linux-gnu/libc.so.6
De acuerdo con algunos tutoriales en línea , Segmentation fault
se produjeron errores, lo que resultó en el uso común ls
, estas instrucciones no se pueden usar, la solución:cp
clear
# export LD_PRELOAD=/lib/aarch64-linux-gnu/libc-2.27.so:/lib/aarch64-linux-gnu/ld-2.27.so
unset LD_PRELOAD
# 取消软连接
LD_PRELOAD=/lib/aarch64-linux-gnu/libc-2.27.so unlink /lib/aarch64-linux-gnu/libc.so.6
# 重新恢复
LD_PRELOAD=/lib/aarch64-linux-gnu/libc-2.27.so ln -s /lib/aarch64-linux-gnu/libc-2.27.so /lib/aarch64-linux-gnu/libc.so.6
Crear un enlace suave:
# 复制 libc
cp /usr/local/lib/libc-2.28.so /lib/aarch64-linux-gnu/
cp /usr/local/lib/ld-2.28.so /lib/aarch64-linux-gnu/
cd /lib/aarch64-linux-gnu/
# ll ld-linux-aarch64.so.1
# ll libc.so.6
ln -sf /lib/aarch64-linux-gnu/libc-2.28.so /lib/aarch64-linux-gnu/libm.so.6
# 无效, 仍然是2.27版本
Comparando aarch64-linux-gnu
el directorio con glibc-2.28
el directorio de instalación, encontré que muchas bibliotecas tienen el mismo nombre, pero el número de versión es diferente ¿Hay que reemplazarlas? ? ?
5.3 Verificar que la instalación fue exitosa
En este punto, si ubuntu 18.04
la glibc
actualización no es exitosa, entonces utilícela ubuntu 20.04
, se hace lo siguiente arm-ubuntu 20.04
en , verifique pytorch
la versión:
pytorch
Modelo de validación:
# from_pytorch.py
import tvm
from tvm import relay
from tvm.contrib.download import download_testdata
import numpy as np
import torch
import torchvision
######################################################################
# Load a pretrained PyTorch model
pth_file = 'resnet18-f37072fd.pth'
model = torchvision.models.resnet18()
ckpt = torch.load(pth_file)
model.load_state_dict(ckpt)
model = model.eval()
# We grab the TorchScripted model via tracing
input_shape = [1, 3, 224, 224]
input_data = torch.randn(input_shape)
scripted_model = torch.jit.trace(model, input_data).eval()
######################################################################
# Load a test image
from PIL import Image
img_path = 'cat.png'
img = Image.open(img_path).resize((224, 224))
# Preprocess the image and convert to tensor
from torchvision import transforms
my_preprocess = transforms.Compose(
[
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
]
)
img = my_preprocess(img)
img = np.expand_dims(img, 0)
######################################################################
# Import the graph to Relay
input_name = "input0"
shape_list = [(input_name, img.shape)]
mod, params = relay.frontend.from_pytorch(scripted_model, shape_list)
######################################################################
# Relay Build
target = tvm.target.arm_cpu()
dev = tvm.cpu(0)
with tvm.transform.PassContext(opt_level=3):
lib = relay.build(mod, target=target, params=params)
######################################################################
# Execute the portable graph on TVM
from tvm.contrib import graph_executor
dtype = "float32"
m = graph_executor.GraphModule(lib["default"](dev))
# Set inputs
m.set_input(input_name, tvm.nd.array(img.astype(dtype)))
# Execute
m.run()
# Get outputs
tvm_output = m.get_output(0)
#####################################################################
# Look up synset name
synset_path = 'imagenet_synsets.txt'
with open(synset_path) as f:
synsets = f.readlines()
synsets = [x.strip() for x in synsets]
splits = [line.split(" ") for line in synsets]
key_to_classname = {
spl[0]: " ".join(spl[1:]) for spl in splits}
class_path = 'imagenet_classes.txt'
with open(class_path) as f:
class_id_to_key = f.readlines()
class_id_to_key = [x.strip() for x in class_id_to_key]
# Get top-1 result for TVM
top1_tvm = np.argmax(tvm_output.numpy()[0])
tvm_class_key = class_id_to_key[top1_tvm]
# Convert input to PyTorch variable and get PyTorch result for comparison
with torch.no_grad():
torch_img = torch.from_numpy(img)
output = model(torch_img)
# Get top-1 result for PyTorch
top1_torch = np.argmax(output.numpy())
torch_class_key = class_id_to_key[top1_torch]
print("Relay top-1 id: {}, class name: {}".format(top1_tvm, key_to_classname[tvm_class_key]))
print("Torch top-1 id: {}, class name: {}".format(top1_torch, key_to_classname[torch_class_key]))
Los resultados de la verificación son los siguientes [la velocidad no es un poco lenta]:
autotvm
También ok
si:
Pero siento que hay algunos problemas. Solo hay uno x86
arriba , pero de hecho hay uno arriba . Después de verificar, esto está incluido , y todavía está incluido . Es muy extraño. Este problema aún no se ha resuelto:task
13
[resnet18]
arm
26
26
task
arm
x86
conclusión
Este artículo es un intento de construir un entorno en . x86
Dado que el servidor está usando , quería simular una versión que sea la misma que el servidor localmente. Como resultado, me encontré con el foso de la actualización , que aún no se ha completado. Sin embargo, el entorno se incorporó con éxito y se puede compilar y ejecutar. Todavía aprendiendo, ¡bienvenido a comunicarse en el área de comentarios! ! !docker
tvm
arm
ubuntu 18.04
ubuntu 18.04
glibc
x86
docker
tvm
arm
tvm