MiniCPM-V2.0推理及LoRA微调基于DevServer适配PyTorch NPU指导(6.3.910)
本文档主要介绍如何在ModelArts Lite的DevServer环境中,使用NPU卡对MiniCPM-V 2.0进行LoRA微调及推理。本文档中提供的训练脚本,是基于原生MiniCPM-V的代码基础适配修改,可以用于NPU芯片训练。
MiniCPM 系列的最新多模态版本 MiniCPM-V 2.0。该模型基于 MiniCPM 2.4B 和 SigLip-400M 构建,共拥有 2.8B 参数。MiniCPM-V 2.0 具有领先的光学字符识别(OCR)和多模态理解能力。该模型在综合性 OCR 能力评测基准 OCRBench 上达到开源社区的最佳水平,甚至在场景文字理解方面实现接近 Gemini Pro 的性能。
MiniCPM-V 2.0 值得关注的特性包括:
- 领先的 OCR 和多模态理解能力。MiniCPM-V 2.0 显著提升了 OCR 和多模态理解能力,场景文字理解能力接近 Gemini Pro,在多个主流评测基准上性能超过了更大参数规模(例如 17-34B)的主流模型。
- 可信行为。MiniCPM-V 2.0 是第一个通过多模态 RLHF 对齐的端侧多模态大模型(借助 RLHF-V [CVPR'24] 系列技术。该模型在 Object HalBench 达到和 GPT-4V 相仿的性能。
- 任意长宽比高清图像高效编码。 MiniCPM-V 2.0 可以接受 180 万像素的任意长宽比图像输入(基于最新的 LLaVA-UHD 技术),这使得模型可以感知到小物体、密集文字等更加细粒度的视觉信息。
- 高效部署。MiniCPM-V 2.0 可以高效部署在大多数消费级显卡、个人电脑以及移动手机等终端设备。
- 双语支持。MiniCPM-V 2.0 提供领先的中英双语多模态能力支持。 该能力通过过 VisCPM [ICLR'24] 论文中提出的多模态能力的跨语言泛化技术实现。
方案概览
本方案介绍了在ModelArts的DevServer上使用昇腾计算资源开展MiniCPM-V 2.0 LoRA训练的详细过程,及一份推理示例代码。完成本方案的部署,需要先联系您所在企业的华为方技术支持购买DevServer资源。
本方案目前仅适用于企业客户。
资源规格要求
推荐使用“西南-贵阳一”Region上的DevServer资源和Ascend Snt9B单机。
名称 |
版本 |
---|---|
driver |
23.0.6 |
PyTorch |
pytorch_2.1.0 |
获取软件和镜像
分类 |
名称 |
获取路径 |
---|---|---|
插件代码包 |
AscendCloud-6.3.910-xxx.zip软件包中的AscendCloud-AIGC-6.3.910-xxx.zip
说明:
包名中的xxx表示具体的时间戳,以包名的实际时间为准。 |
获取路径:Support-E,在此路径中查找下载ModelArts 6.3.910 版本。
说明:
如果上述软件获取路径打开后未显示相应的软件信息,说明您没有下载权限,请联系您所在企业的华为方技术支持下载获取。 |
基础镜像 |
西南-贵阳一: 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 |
从SWR拉取。 |
约束限制
- 本文档适配昇腾云ModelArts 6.3.910版本,请参考表2获取配套版本的软件包和镜像,请严格遵照版本配套关系使用本文档。
- 确保容器可以访问公网。
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
Step3 启动容器镜像
- 启动容器镜像。启动前请先按照参数说明修改${}中的参数。
export work_dir="自定义挂载的工作目录" export container_work_dir="自定义挂载到容器内的工作目录" export container_name="自定义容器名称" export image_name="镜像名称或ID" // 启动一个容器去运行镜像 docker run -itd --net=bridge \ --device=/dev/davinci0 \ --device=/dev/davinci1 \ --device=/dev/davinci2 \ --device=/dev/davinci3 \ --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/sbin/npu-smi:/usr/local/sbin/npu-smi \ -v ${work_dir}:${container_work_dir} \ --name ${container_name} \ ${image_name} \ /bin/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}:容器镜像的名称。
- --device=/dev/davinci0 :挂载对应卡到容器,当需要挂载多卡,请依次添加多项该配置
- -v ${work_dir}:${container_work_dir}:代表需要在容器中挂载宿主机的目录。宿主机和容器使用不同的文件系统。work_dir为宿主机中工作目录,目录下可存放项目所需代码、数据等文件。container_work_dir为要挂载到的容器中的目录。为方便两个地址可以相同。
- 通过容器名称进入容器中。默认使用ma-user用户,后续所有操作步骤都在ma-user用户下执行。
docker exec -it ${container_name} bash
Step4 安装依赖和软件包
- git clone和git lfs下载大模型可以参考如下操作。
- 由于欧拉源上没有git-lfs包,所以需要从压缩包中解压使用,在浏览器中输入如下地址下载git-lfs压缩包并上传到容器的/home/ma-user目录下
https://github.com/git-lfs/git-lfs/releases/download/v3.2.0/git-lfs-linux-arm64-v3.2.0.tar.gz
或直接下载到容器,这样在容器中可以直接使用。cd /home/ma-user wget https://github.com/git-lfs/git-lfs/releases/download/v3.2.0/git-lfs-linux-arm64-v3.2.0.tar.gz
- 进入容器,执行安装git lfs命令。
cd /home/ma-user tar -zxvf git-lfs-linux-arm64-v3.2.0.tar.gz cd git-lfs-3.2.0 sudo sh install.sh
- 设置git配置去掉ssl校验。
git config --global http.sslVerify false
- 由于欧拉源上没有git-lfs包,所以需要从压缩包中解压使用,在浏览器中输入如下地址下载git-lfs压缩包并上传到容器的/home/ma-user目录下
- 从github拉取MiniCPM-V代码。
cd /home/ma-user git clone https://github.com/OpenBMB/MiniCPM-V.git cd /home/ma-user/MiniCPM-V git checkout ba27a162aa236e17036bb903
- 获取openbmb/MiniCPM-V-2模型。
cd /home/ma-user git clone https://huggingface.co/openbmb/MiniCPM-V-2 cd /home/ma-user/MiniCPM-V-2 git checkout ee00ff7ce36667e7df81cb2a0
- 安装MiniCPM-V2_0 Ascend软件包。
- 将获取到的MiniCPM-V Ascend软件包AscendCloud-AIGC-*.zip文件上传到容器的/home/ma-user目录下。获取路径参见表2。
- 解压AscendCloud-AIGC-*.zip文件,解压后将里面指定文件与对应MiniCPM-V文件进行替换。
cd /home/ma-user unzip AscendCloud-AIGC-*.zip -d ./AscendCloud cd AscendCloud/multimodal_algorithm/MiniCPM-V2_0/ dos2unix install.sh bash install.sh
AscendCloud-AIGC-*.zip后面的*表示时间戳,请按照实际替换。
MiniCPM-V2_0 Ascend软件包内容如下:|---MiniCPM-V2_0/ --- infer.py 推理示例代码 --- install.sh 安装torch-npu适配修改及优化脚本 --- model_modify.patch 优化模型融合算子git patch文件 --- modify.patch 适配优化MiniCPM-V2.0代码git patch文件 --- README.md 适配文档基于官方代码commit id说明
Step5 MiniCPM-V2.0微调
使用/home/ma-user/MiniCPM-V/finetune/finetune_lora.sh官方脚本对MiniCPM-V 2.0进行微调。微调脚本默认使用 transformers Trainer 和 DeepSpeed。
- 数据准备
要准备微调数据,您应该将每个样本制定为一个字典,其中包含一个 ID、一个图像路径(或图像列表)和一个对话列表。然后,将数据样本保存在 JSON 文件中。
对于视觉语言任务,您必须提供占位符(例如<image>或<image_XX>)来定义在对话中插入图像嵌入的位置。如果没有提供占位符,则图像将默认放置在对话的前面。
单幅图像示例
如果您的输入仅包含一张图片,则可以使用单个占位符<image>来指示应在对话中插入图像的位置。
包含 1 个样本的单个图像示例:[ { "id": "0", "image": 'path/to/image_0.jpg', "conversations": [ { 'role': 'user', 'content': '<image>\nHow many desserts are on the white plate?' }, { 'role': 'assistant', 'content': 'There are three desserts on the white plate.' }, { 'role': 'user', 'content': 'What type of desserts are they?' }, { 'role': 'assistant', 'content': 'The desserts are cakes with bananas and pecans on top. They share similarities with donuts, but the presence of bananas and pecans differentiates them.' }, { 'role': 'user', 'content': 'What is the setting of the image?'}, { 'role': 'assistant', 'content': 'The image is set on a table top with a plate containing the three desserts.' }, ] }, ]
- LoRA微调
需要在整个训练过程中更新LLM的所有参数,请在shell脚本中指定正确的MODEL路径、DATA路径和LLM_TYPE。
MODEL="/path/to/openbmb/MiniCPM-V-2" # or openbmb/MiniCPM-V-2 # ATTENTION: specify the path to your training data, which should be a json file consisting of a list of conversations. # See the section for finetuning in README for more information. DATA="path/to/trainging_data" EVAL_DATA="path/to/test_data" LLM_TYPE="minicpm" # if use openbmb/MiniCPM-V-2, please set LLM_TYPE=minicpm
LoRA 允许轻量级模型调整,只需更新一小部分参数。要启动训练,请运行以下脚本:
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True bash finetune_lora.sh
注:以上微调文档提示来自官方文档,有关可用LoRA微调脚本参数及其功能的全面文档,您可以参考官方MiniCPM-V文档。
Step6 MiniCPM-V2.0推理
多轮对话
cd /home/ma-user/MiniCPM-V python infer.py
# 执行python infer.py即可得到示例参考结果: =======First round answer====== The aircraft is an Airbus A380-Boeing 747. =======Second round answer====== The Airbus A380 is a large passenger jet designed for long-haul flights. It has four engines, making it one of the most powerful commercial aircraft ever built with an impressive capacity to carry up to 524 passengers in its standard configuration or even more than that depending on seating arrangements and cabin layouts.
infer.py示例代码如下:
import json from chat import MiniCPMVChat, img2base64 import torch_npu from torch_npu.contrib import transfer_to_npu model_type = "/home/ma-user/MiniCPM-V-2" # or openbmb/MiniCPM-V-2 chat_model = MiniCPMVChat(model_type) im_64 = img2base64('./assets/airplane.jpeg') # First round chat msgs = [{"role": "user", "content": "Tell me the model of this aircraft."}] inputs = {"image": im_64, "question": json.dumps(msgs)} answer = chat_model.chat(inputs) print("=======First round answer======") print(answer) # Second round chat # pass history context of multi-turn conversation msgs.append({"role": "assistant", "content": answer}) msgs.append({"role": "user", "content": "Introduce something about Airbus A380."}) inputs = {"image": im_64, "question": json.dumps(msgs)} answer = chat_model.chat(inputs) print("=======Second round answer======") print(answer)