VIT 推理适配昇腾

举报
昇腾适配 发表于 2024/11/30 10:26:00 2024/11/30
【摘要】 ViT是2020年Google团队提出的将Transformer应用在图像分类的模型,虽然不是第一篇将transformer应用在视觉任务的论文,但是因为其模型“简单”且效果好,可扩展性强,成为了transformer在CV领域应用的里程碑著作,本文介绍了如何使用昇腾 推理该模型。

/**************************如有任何问题和疑问,请评论区留言*********************************/


0.前提条件

0.1 登录机器

机器已开通,密码已获取,能通过ssh登录

0.2 检查NPU设备

NPU设备检查:运行npu-smi info命令,返回npu设备信息。

0.3 docker安装

#检查docker是否安装:docker -v,如如尚未安装,运行以下命令进行docker安装
yum install -y docker-engine.aarch64 docker-engine-selinux.noarch docker-runc.aarch64

#配置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

0.4 获取镜像

docker pull swr.cn-southwest-2.myhuaweicloud.com/atelier/pytorch_2_1_ascend:pytorch_2.1.0-cann_8.0.rc3-py_3.9-hce_2.0.2409-aarch64-snt9b-20241112192643-c45ac6b

0.5 启动镜像

启动容器镜像。启动前请先按照参数说明修改${}中的参数。

docker run -it --net=host \ 
--device=/dev/davinci0 \ 
--device=/dev/davinci1 \ 
--device=/dev/davinci2 \ 
--device=/dev/davinci3 \ 
--device=/dev/davinci4 \ 
--device=/dev/davinci5 \ 
--device=/dev/davinci6 \ 
--device=/dev/davinci7 \ 
--device=/dev/davinci_manager \ 
--device=/dev/devmm_svm \ 
--device=/dev/hisi_hdc \ 
--shm-size=32g \ 
-v /usr/local/dcmi:/usr/local/dcmi \ 
-v /usr/local/Ascend/driver:/usr/local/Ascend/driver \ 
-v /var/log/npu/:/usr/slog \ 
-v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \ 
-v ${work_dir}:${container_work_dir} \ 
--name ${container_name} \ 
${image_id}  \ 
/bin/bash
参数说明:
device=/dev/davinci0,..., --device=/dev/davinci7:挂载NPU设备,示例中挂载了8张卡davinci0~davinci7,可根据需要选择挂载卡数。
${work_dir}:${container_work_dir} 代表需要在容器中挂载宿主机的目录。宿主机和容器使用不同的文件系统,work_dir为宿主机中工作目录,目录下存放着训练所需代码、数据等文件。container_dir为要挂载到的容器中的目录。为方便两个地址可以相同。
shm-size:共享内存大小。
${container_name}:容器名称,进入容器时会用到,此处可以自己定义一个容器名称。
${image_id}:镜像ID,通过docker images查看刚拉取的镜像ID。
 
注意
容器不能挂载到/home/ma-user目录,此目录为ma-user用户家目录。如果容器挂载到/home/ma-user下,拉起容器时会与基础镜像冲突,导致基础镜像不可用。
driver及npu-smi需同时挂载至容器。
不要将多个容器绑到同一个NPU上,会导致后续的容器无法正常使用NPU功能。

1.推理验证

    在容器工作目录container_work_dir下执行以下操作,以下以vit_mediumd_patch16_reg4_gap_384模型为例

1.1 导出onnx模型

  1) 创建export_onnx.py脚本,该脚本会自动下载模型,其内容如下:

import os
import torch
import timm
import argparse

def parse_arguments():
    parser = argparse.ArgumentParser(description='Path', add_help=False)
    parser.add_argument('--save_dir', default="onnx", type=str,
                        help='save dir for onnx model')
    parser.add_argument('--batch_size', default=1, type=int,
                        help='batch size')
    parser.add_argument('--model_name', required=True, type=str,
                        help='model name for ViT')
    
    return parser.parse_args()

def main():
    args = parse_arguments()
    model = timm.create_model(args.model_name, pretrained=True)
    model.eval()
    input_size = 384
    tensor = torch.zeros(args.batch_size, 3, input_size, input_size)

    if not os.path.exists(args.save_dir):
        os.makedirs(args.save_dir)

    save_path = os.path.join(args.save_dir,f"{args.model_name}_bs{args.batch_size}.onnx")
    torch.onnx.export(model, tensor, save_path, opset_version=17,
                      do_constant_folding=True, input_names=["input"], output_names=["output"])

if __name__ == "__main__":
    main()

2) 修改/home/ma-user/anaconda3/envs/PyTorch-2.1.0/lib/python3.9/site-packages/requests/sessions.py 422

self.verify = True 修改为 self.verify = False

3) 导出onnx模型

python export_onnx.py --save_dir ./onnx --batch_size 1 --model_name vit_mediumd_patch16_reg4_gap_384

    执行完会在onnx目录下生成vit_mediumd_patch16_reg4_gap_384_bs1.onnx

1.2 onnx模型转mindir

该镜像中自带转换工具converter_lite,使用converter_lite转换onnx模型到mindir, 转换命令如下:

converter_lite  --fmk=ONNX --modelFile=onnx/vit_mediumd_patch16_reg4_gap_384_bs1.onnx --outputFile=vit_mediumd_patch16_reg4_gap_384_bs1 --saveType=MINDIR --inputShape=input:1,3,384,384 --optimize=ascend_oriented

转换后生成vit_mediumd_patch16_reg4_gap_384_bs1.mindir

关于converter_lite工具详细使用参考https://www.mindspore.cn/lite/docs/zh-CN/r2.4.0/converter/converter_tool.html

1.3 推理验证

创建推理代码 infer.py

import numpy as np
import time
import torch
from PIL import Image
import mindspore_lite as mslite

model_config = {
    224: {
        'resize': 248,
        'centercrop': 224,
        'mean': [0.5, 0.5, 0.5],
        'std': [0.5, 0.5, 0.5],
    },
    384: {
        'resize': 384,
        'centercrop': 384,
        'mean': [0.5, 0.5, 0.5],
        'std': [0.5, 0.5, 0.5],
    },
}

def center_crop(img, output_size):
    if isinstance(output_size, int):
        output_size = (int(output_size), int(output_size))
    image_width, image_height = img.size
    crop_height, crop_width = output_size
    crop_top = int(round((image_height - crop_height) / 2.))
    crop_left = int(round((image_width - crop_width) / 2.))
    return img.crop((crop_left, crop_top, crop_left + crop_width, crop_top + crop_height))

def resize(img, size, interpolation=Image.BICUBIC):
    if isinstance(size, int):
        w, h = img.size
        if (w <= h and w == size) or (h <= w and h == size):
            return img
        if w < h:
            ow = size
            oh = int(size * h / w)
            return img.resize((ow, oh), interpolation)
        else:
            oh = size
            ow = int(size * w / h)
            return img.resize((ow, oh), interpolation)
    else:
        return img.resize(size[::-1], interpolation)

def normalize(img, means, variances):
    img = np.array(img)
    for channel, (mean, variance) in enumerate(zip(means, variances)):
        img[:, :, channel] = (img[:, :, channel] - mean) / variance
    return img

def loadimage(image):
    img = Image.open(image).convert('RGB')
    img = resize(img, model_config[384]['resize']) # Resize
    img = center_crop(img, model_config[384]['centercrop']) # CenterCrop
    img = np.array(img).astype(np.float32) / 255
    img = normalize(img, model_config[384]['mean'], model_config[384]['std']) # Normalization
    img = img.transpose((2, 0, 1))

    return img

def mslite_init_model(model_path):
    context = mslite.Context()
    context.target = ["ascend"]
    context.ascend.device_id = 0
    context.cpu.thread_num = 1
    context.cpu.thread_affinity_mode=2

    # build model from file
    model = mslite.Model()
    model.build_from_file(model_path, mslite.ModelType.MINDIR, context)

    return model

def mslite_infer(model, input_data):
    ms_inp = list(input_data.values())
    inputs = model.get_inputs()

    for i, _input in enumerate(inputs):
        _input.set_data_from_numpy(ms_inp[i])

    outputs = model.predict(inputs)
    return outputs[0].get_data_to_numpy()

def main():
    mslite_model = mslite_init_model("vit_mediumd_patch16_reg4_gap_384_bs1.mindir文件所在绝对路径")

    start_time = time.time()
    img = loadimage('beignets-task-guide.png')
    start_time = time.time()
    mslite_input_data = {"input": img}
    mslite_output = mslite_infer(mslite_model, mslite_input_data)  
    print("infer time: ", time.time() - start_time)
    
    top5_probabilities, top5_class_indices = torch.topk(torch.from_numpy(mslite_output).softmax(dim=1) * 100, k=5)
    print("top5_probabilities: ", top5_probabilities)
    print("top5_class_indices: ", top5_class_indices)

if __name__ == "__main__":
   main()

执行python infer.py

推理结果:

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。