moondream2基于DevServer适配PyTorch NPU推理指导
方案概览
本文档从模型部署的环境配置、模型转换、模型推理等方面进行介绍moondream2模型在ModelArts DevServer上部署,支持NPU推理场景。
本方案目前仅适用于部分企业客户,完成本方案的部署,需要先联系您所在企业的华为方技术支持。
资源规格要求
推理部署推荐使用DevServer资源和Ascend Snt9B单机单卡。
名称 |
版本 |
---|---|
CANN |
cann_8.0.rc1 |
PyTorch |
pytorch_2.1.0 |
获取镜像
Step1 准备环境
- 请参考DevServer资源开通,购买DevServer资源,并确保机器已开通,密码已获取,能通过SSH登录,不同机器之间网络互通。
当容器需要提供服务给多个用户,或者多个用户共享使用该容器时,应限制容器访问Openstack的管理地址(169.254.169.254),以防止容器获取宿主机的元数据。具体操作请参见禁止容器获取宿主机元数据。
- 检查环境。
- SSH登录机器后,检查NPU设备检查。运行如下命令,返回NPU设备信息。
npu-smi info # 在每个实例节点上运行此命令可以看到NPU卡状态 npu-smi info -l | grep Total # 在每个实例节点上运行此命令可以看到总卡数
如出现错误,可能是机器上的NPU设备没有正常安装,或者NPU镜像被其他容器挂载。请先正常安装固件和驱动,或释放被挂载的NPU。
- 检查docker是否安装。
docker -v #检查docker是否安装
如尚未安装,运行以下命令安装docker。
yum install -y docker-engine.aarch64 docker-engine-selinux.noarch docker-runc.aarch64
- 配置IP转发,用于容器内的网络访问。执行以下命令查看net.ipv4.ip_forward配置项的值,如果为1,可跳过此步骤。
sysctl -p | grep net.ipv4.ip_forward
如果net.ipv4.ip_forward配置项的值不为1,执行以下命令配置IP转发。sed -i 's/net\.ipv4\.ip_forward=0/net\.ipv4\.ip_forward=1/g' /etc/sysctl.conf sysctl -p | grep net.ipv4.ip_forward
- SSH登录机器后,检查NPU设备检查。运行如下命令,返回NPU设备信息。
Step3 启动容器镜像
- 启动容器镜像。启动前请先按照参数说明修改${}中的参数。
docker run -itd \ --device=/dev/davinci1 \ --device=/dev/davinci_manager \ --device=/dev/devmm_svm \ --device=/dev/hisi_hdc \ -v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \ -v /usr/local/dcmi:/usr/local/dcmi \ -v /etc/ascend_install.info:/etc/ascend_install.info \ -v /sys/fs/cgroup:/sys/fs/cgroup:ro \ -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \ --shm-size 32g \ --net=bridge \ -v ${work_dir}:${container_work_dir} \ --name ${container_name} \ ${image_name} bash
参数说明:
- -v ${work_dir}:${container_work_dir}:代表需要在容器中挂载宿主机的目录。宿主机和容器使用不同的文件系统。work_dir为宿主机中工作目录,目录下存放着训练所需代码、数据等文件。container_work_dir为要挂载到的容器中的目录。为方便两个地址可以相同。
- 容器不能挂载到/home/ma-user目录,此目录为ma-user用户家目录。如果容器挂载到/home/ma-user下,拉起容器时会与基础镜像冲突,导致基础镜像不可用。
- driver及npu-smi需同时挂载至容器。
- --name ${container_name}:容器名称,进入容器时会用到,此处可以自己定义一个容器名称。
- ${image_name}:容器镜像的名称。
- -v ${work_dir}:${container_work_dir}:代表需要在容器中挂载宿主机的目录。宿主机和容器使用不同的文件系统。work_dir为宿主机中工作目录,目录下存放着训练所需代码、数据等文件。container_work_dir为要挂载到的容器中的目录。为方便两个地址可以相同。
- 通过容器名称进入容器中。
docker exec -it ${container_name} bash
Step4 下载原始模型包
从HuggingFace官网下载moondream2模型包到本地,下载地址:https://huggingface.co/vikhyatk/moondream2/tree/2024-03-06。
在宿主机上创建一个空目录/home/temp,将下载的模型包存放在宿主机/home/temp/moondream2目录下,修改目录权限后,复制到容器中。
mkdir /home/temp #创建一个空目录,将下载的模型包存放在宿主机/home/temp/moondream2目录下 chmod -R 777 moondream2 #修改moondream2目录权限 docker cp moondream2 moondream2:/home/ma-user/ #复制moondream2目录到容器中
Step5 准备测试数据
需要用户自己准备测试图片。
将测试图片存放在宿主机/home/temp/data目录下,修改目录权限后,复制到容器中。
chmod -R 777 data #修改data目录权限 docker cp data moondream2:/home/ma-user/ #复制data目录到容器中
Step6 安装依赖
pip install transformers timm einops torch==2.1.0 && pip install --upgrade sympy
运行结束后,会打印所有图片预测的平均时延。
NPU上运行后,结果会保存在/home/ma-user/result.txt下。
如果在GPU上运行,推荐直接在GPU宿主机上执行,因此不需要启动容器,直接将模型和数据复制到相应目录,然后安装PIP依赖后就可以运行。GPU推理脚本内容参见附录2:在GPU上运行infer.py脚本内容。
附录1:在NPU上运行infer.py脚本内容
NPU上运行推理的infer.py脚本内容如下:
from transformers import AutoModelForCausalLM, AutoTokenizer from PIL import Image import torch import os import time import torch_npu #from torch_npu.contrib import transfer_to_npu import torchair as tng from torchair.configs.compiler_config import CompilerConfig #import logging #from torchair.core.utils import logger # 是否开启DEBUG日志 # logger.setLevel(logging.DEBUG) model_id = "./moondream2" revision = "2024-03-13" model = AutoModelForCausalLM.from_pretrained( model_id, trust_remote_code=True, revision=revision ) device = 'npu:0' model = model.to(device) tokenizer = AutoTokenizer.from_pretrained(model_id, revision=revision) config = CompilerConfig() npu_backend = tng.get_npu_backend(compiler_config=config) model.text_model.transformer = torch.compile(model.text_model.transformer, backend=npu_backend, dynamic=True, fullgraph=True) filenames = os.listdir(r'./data') filenames = sorted(filenames) count = 0 total_time = 0.0 not_num = 1 with open("./result.txt", 'w+') as f: for file in filenames: t1 = time.time() image = Image.open('./data/'+file) enc_image = model.encode_image(image) enc_image = enc_image.to(device) result = model.answer_question(enc_image, "Describe in detail what is in the video frame. The rule is: first describe the main body of the character in the video frame, including action, state, characteristics, etc., do not make associations or summarize. Then describe the environment, such as the background; then describe how the video was shot, such as close-ups. Do not appear 'seems', 'may' and other words, need to be sure of the description, do not need to be ambiguous description.", tokenizer) cost = time.time()-t1 if not_num <=0: count = count+1 total_time += cost print("infer time:"+str(cost)) print("average infer time:"+str(total_time/count), " total count:"+str(count)) else: not_num = not_num -1 f.write(file + ":" + "\n") f.write(result + "\n\n")
附录2:在GPU上运行infer.py脚本内容
GPU上运行推理的infer.py脚本内容如下:
from transformers import AutoModelForCausalLM, AutoTokenizer from PIL import Image import torch import os import time model_id = "./moondream2" revision = "2024-03-13" model = AutoModelForCausalLM.from_pretrained( model_id, trust_remote_code=True, revision=revision ) device = 'cuda:0' model = model.to(device) tokenizer = AutoTokenizer.from_pretrained(model_id, revision=revision) filenames = os.listdir(r'./data') filenames = sorted(filenames) count = 0 total_time = 0.0 not_num = 1 with open("./result.txt", 'w+') as f: for file in filenames: t1 = time.time() image = Image.open('./data/'+file) enc_image = model.encode_image(image) enc_image = enc_image.to(device) result = model.answer_question(enc_image, "Describe in detail what is in the video frame. The rule is: first describe the main body of the character in the video frame, including action, state, characteristics, etc., do not make associations or summarize. Then describe the environment, such as the background; then describe how the video was shot, such as close-ups. Do not appear 'seems', 'may' and other words, need to be sure of the description, do not need to be ambiguous description.", tokenizer) cost = time.time()-t1 if not_num <=0: count = count+1 total_time += cost print("infer time:"+str(cost)) print("average infer time:"+str(total_time/count), " total count:"+str(count)) else: not_num = not_num -1 f.write(file + ":" + "\n") f.write(result + "\n\n")