使用Arena进行模型微调和模型管理

本文以大语言模型Qwen-7B-Chat为例,并结合Arena工具,演示如何在提交模型微调作业的同时,对模型微调作业所产生的模型进行管理。

前提条件

  • 已创建至少包含一个GPU节点的ACK集群Pro版,且集群版本不低于1.20。具体操作,请参见创建ACK Pro版集群

    本文所使用的GPU实例类型为ecs.gn7i-c8g1.2xlarge。关于GPU实例规格的详细信息,请参见实例规格族

  • 已在ACK集群中部署MLflow模型仓库。具体操作,请参见配置MLflow模型仓库

  • 已安装最新版本Arena客户端。具体操作,请参见配置Arena客户端

背景信息

关于大模型Qwen-7B-Chat的详细信息,请参见Qwen官方代码仓库。由于该仓库无法直接运行微调后的模型,因此本文对其进行了少量修改并重新构建了容器镜像,镜像地址为kube-ai-registry.cn-shanghai.cr.aliyuncs.com/kube-ai/qwen:cu117

步骤一:准备模型数据

  1. 创建一个存储卷用于准备模型数据,本文将以NAS存储卷为例说明如何准备模型数据,所使用的存储卷名称为nas-pvc

    您可以按需选择使用NAS存储卷或OSS存储卷准备模型数据。具体操作,请参见使用NAS静态存储卷使用OSS静态存储卷

  2. 将NAS存储卷对应的NAS文件系统挂载至ECS实例中。具体操作,请参见挂载文件系统场景说明

  3. 登录ECS实例下载数据模型。本文以Qwen-7B-Chat模型为例。

    1. 执行以下命令,进入NAS文件系统挂载路径。例如/mnt/nas。

      cd /mnt/nas
    2. 执行以下命令,安装Git。

      sudo yum install git
    3. 执行以下命令,安装Git LFS(Large File Support)插件。

      sudo yum install git-lfs
    4. 执行以下命令,将ModelScope上的Qwen-7B-Chat仓库克隆到本地。

      GIT_LFS_SKIP_SMUDGE=1 git clone https://www.modelscope.cn/qwen/Qwen-7B-Chat.git
    5. 执行以下命令,进入Qwen-7B-Chat仓库目录。

      cd Qwen-7B-Chat
    6. 执行以下命令,在Qwen-7B-Chat目录下,下载LFS管理的大文件。

      git lfs pull

步骤二:使用Arena提交微调前的模型推理服务

为了展示模型微调效果,首先将运行没有微调之前的Qwen-7B-Chat模型推理服务。

  1. 执行以下命令,运行没有微调的Qwen-7B-Chat模型推理服务。

    arena serve custom \
        --name=qwen-7b-chat \
        --namespace=default \
        --image=kube-ai-registry.cn-shanghai.cr.aliyuncs.com/kube-ai/qwen:cu117 \
        --gpus=1 \
        --data=nas-pvc:/mnt \
        --restful-port=80 \
        "python web_demo.py --server-port 80 --server-name 0.0.0.0 -c /mnt/models/Qwen-7B-Chat/"

    预期输出:

    service/qwen-7b-chat-202404301015 created
    deployment.apps/qwen-7b-chat-202404301015-custom-serving created
    INFO[0003] The Job qwen-7b-chat has been submitted successfully
    INFO[0003] You can run `arena serve get qwen-7b-chat --type custom-serving -n default` to check the job status

    输出结果表明qwen-7b-chat推理服务已被成功部署。

  2. 执行以下命令,查看推理作业运行日志。

    arena serve logs -f qwen-7b-chat

    预期输出:

    The model is automatically converting to bf16 for faster inference. If you want to disable the automatic precision, please manually add bf16/fp16/fp32=True to "AutoModelForCausalLM.from_pretrained".
    Try importing flash-attention for faster inference...
    Loading checkpoint shards: 100%|██████████| 8/8 [03:16<00:00, 24.59s/it]
  3. 模型加载完成之后,执行以下命令,将其Web端口转发到本地8901端口进行访问。

    重要

    kubectl port-forward建立的端口转发仅适用于测试环境下的快速验证,不适合在生产环境中使用,使用时请注意安全风险。

    kubectl port-forward services/qwen-7b-chat-202404301015 8901:80

    预期输出:

    Forwarding from 127.0.0.1:8901 -> 80
    Forwarding from [::1]:8901 -> 80
  4. 在浏览器地址栏中输入http://localhost:8901,可以进入Qwen-7B-Chat进行对话。

    以下示例展示了一个简单的对话场景,用户发起提问,助手给出回复。其中,Q表示用户输入,A表示模型输出:

    Q: 创建集群后,是否可以更改容器运行时?
    A: 是的,创建集群后,您可以更改容器运行时。阿里云的服务器镜像和平台提供了多种不同的运行时环境,例如Docker、Kubernetes等。您可以根据自己的需求选择合适的运行时环境。
    要更改容器运行时,您需要先停止当前的容器运行,并重新启动一个新容器。如果您想要在现有集群上进行更改,那么您可能需要创建一个新的集群,并将应用程序从旧的集群转移到新的集群。
    另外,阿里云还提供了一些自动化工具和服务,可以帮助您轻松地在集群中进行运行时更改。例如,您可以使用“一键部署”服务快速部署新的应用程序,或者使用“统一监控”服务来监控您的应用运行情况并及时发现和处理问题。
  5. 执行以下命令,删除Qwen-7B-Chat推理作业。

    arena serve delete qwen-7b-chat

步骤三:准备微调数据集

对Qwen-7B-Chat模型进行LoRA(Low-Rank Adaptation)微调前,您需要先准备微调数据集以提升模型在特定对话场景下的表现。

微调数据中的所有样本以JSON文件格式存放在一个JSON数组中,每个样本需要包含idconversation字段,其中conversations字段为一个数组。以下是一个微调数据集示例:

[
  {
    "id": "identity_0",
    "conversations": [
      {
        "from": "user",
        "value": "你好"
      },
      {
        "from": "assistant",
        "value": "我是一个语言模型,我叫通义千问。"
      }
    ]
  }
]

微调数据集参数说明如下:

  • id:唯一标识符,用于区分不同的对话样本,例如"id": "identity_0"

  • conversations:包含实际对话内容的数组,每个对话由一系列交替的用户和助手消息组成,每个消息对象有两个字段:

    • "from":指示消息来源,可以是"user""assistant"

    • "value":具体的对话内容,如用户说"你好",助手回应"我是一个语言模型,我叫通义千问。"

本次微调使用的数据集已经打包在镜像中,该数据集针对步骤二中提出的问题给出了特定的回答,您可以登录容器中执行cat /data/shared/Qwen/example.json命令查看微调数据详情。

步骤四:使用Arena提交微调作业

以下使用Arena提交一个对Qwen-7B-Chat模型进行LoRA微调的作业,该次微调将产生一个LoRA模型,本文将该模型注册为一个新的模型版本,模型名称为Qwen-7B-Chat-Lora

  1. 执行以下命令,提交一个对Qwen-7B-Chat模型进行LoRA微调的作业。

    # Submit a finetune job and register the output PEFT model as a new model version
    arena submit pytorchjob \
        --name=qwen-7b-chat-finetune \
        --namespace=default \
        --image=kube-ai-registry.cn-shanghai.cr.aliyuncs.com/kube-ai/qwen:cu117 \
        --image-pull-policy=Always \
        --gpus=1 \
        --working-dir /data/shared/Qwen \
        --data=nas-pvc:/mnt/ \
        --model-name=Qwen-7B-Chat-Lora \
        --model-source=pvc://default/nas-pvc/finetune/Qwen-7B-Chat-Lora \
        "bash finetune/finetune_lora_single_gpu.sh -m /mnt/models/Qwen-7B-Chat/ -d example.json -o /mnt/finetune/Qwen-7B-Chat-Lora"

    Qwen-7B-Chat-Lora模型源路径为pvc://default/nas-pvc/finetune/Qwen-7B-Chat-Lora,表示该模型存储在default命名空间里一个名为nas-pvc的存储卷中,存储路径为/finetune/Qwen-7B-Chat-Lora

    预期输出:

    pytorchjob.kubeflow.org/qwen-7b-chat-finetune created
    INFO[0004] The Job qwen-7b-chat-finetune has been submitted successfully
    INFO[0004] You can run `arena get qwen-7b-chat-finetune --type pytorchjob -n default` to check the job status
    INFO[0004] registered model "Qwen-7B-Chat-Lora" created
    INFO[0005] model version 1 for "Qwen-7B-Chat-Lora" created

    预期输出表明模型微调任务已经成功创建与提交,并自动地完成了模型注册与模型版本创建。

  2. 执行以下命令,查看该次作业详情。

    arena get qwen-7b-chat-finetune

    预期输出:

    Name:          qwen-7b-chat-finetune
    Status:        RUNNING
    Namespace:     default
    Priority:      N/A
    Trainer:       PYTORCHJOB
    Duration:      2m
    CreateTime:    2024-04-29 16:02:01
    EndTime:
    ModelName:     Qwen-7B-Chat-Lora
    ModelVersion:  1
    ModelSource:   pvc://default/nas-pvc/finetune/Qwen-7B-Chat-Lora/
    
    Instances:
      NAME                            STATUS   AGE  IS_CHIEF  GPU(Requested)  NODE
      ----                            ------   ---  --------  --------------  ----
      qwen-7b-chat-finetune-master-0  Running  2m   true      1               ap-southeast-1.XX.XX.XX.XX

    输出结果记录了该作业关联的模型的名称、版本号及其源路径等详细信息。

  3. 执行以下命令,查看微调作业日志。

    arena logs -f qwen-7b-chat-finetune

    展开查看微调作业日志的详细信息

    + export CUDA_VISIBLE_DEVICES=0
    + CUDA_VISIBLE_DEVICES=0
    + mkdir -p /mnt/finetune/Qwen-7B-Chat-Lora
    + python finetune.py --model_name_or_path /mnt/models/Qwen-7B-Chat/ --data_path example.json --bf16 True --output_dir /mnt/finetune/Qwen-7B-Chat-Lora --num_train_epochs 5 --per_device_train_batch_size 2 --per_device_eval_batch_size 1 --gradient_accumulation_steps 8 --evaluation_strategy no --save_strategy steps --save_steps 1000 --save_total_limit 10 --learning_rate 3e-4 --weight_decay 0.1 --adam_beta2 0.95 --warmup_ratio 0.01 --lr_scheduler_type cosine --logging_steps 1 --report_to none --model_max_length 512 --lazy_preprocess True --gradient_checkpointing --use_lora
    [2024-04-30 02:26:42,358] [INFO] [real_accelerator.py:203:get_accelerator] Setting ds_accelerator to cuda (auto detect)
    ...
    Loading checkpoint shards: 100%|██████████| 8/8 [00:02<00:00,  3.29it/s]
    /usr/local/lib/python3.8/dist-packages/accelerate/accelerator.py:436: FutureWarning: Passing the following arguments to `Accelerator` is deprecated and will be removed in version 1.0 of Accelerate: dict_keys(['dispatch_batches', 'split_batches']). Please pass an `accelerate.DataLoaderConfiguration` instead:
    dataloader_config = DataLoaderConfiguration(dispatch_batches=None, split_batches=False)
      warnings.warn(
    You are using an old version of the checkpointing format that is deprecated (We will also silently ignore `gradient_checkpointing_kwargs` in case you passed it).Please update to the new format on your modeling file. To use the new format, you need to completely remove the definition of the method `_set_gradient_checkpointing` in your model.
    trainable params: 143,130,624 || all params: 7,864,455,168 || trainable%: 1.8199687192876373
    Loading data...
    Formatting inputs...Skip in lazy mode
    100%|██████████| 20/20 [02:42<00:00,  8.12s/it]
    {'loss': 2.6322, 'learning_rate': 0.0003, 'epoch': 0.23}
    {'loss': 2.6542, 'learning_rate': 0.00029795419551040833, 'epoch': 0.46}
    {'loss': 2.3209, 'learning_rate': 0.00029187258625509513, 'epoch': 0.69}
    {'loss': 2.1613, 'learning_rate': 0.00028192106268097334, 'epoch': 0.91}
    {'loss': 1.6563, 'learning_rate': 0.00026837107640945905, 'epoch': 1.14}
    {'loss': 1.4985, 'learning_rate': 0.00025159223574386114, 'epoch': 1.37}
    {'loss': 1.3369, 'learning_rate': 0.00023204222371836405, 'epoch': 1.6}
    {'loss': 1.0505, 'learning_rate': 0.0002102543136979454, 'epoch': 1.83}
    {'loss': 0.7033, 'learning_rate': 0.00018682282307111987, 'epoch': 2.06}
    {'loss': 0.5576, 'learning_rate': 0.00016238690182084986, 'epoch': 2.29}
    {'loss': 0.2523, 'learning_rate': 0.00013761309817915014, 'epoch': 2.51}
    {'loss': 0.2481, 'learning_rate': 0.00011317717692888012, 'epoch': 2.74}
    {'loss': 0.1343, 'learning_rate': 8.97456863020546e-05, 'epoch': 2.97}
    {'loss': 0.0676, 'learning_rate': 6.795777628163599e-05, 'epoch': 3.2}
    {'loss': 0.0489, 'learning_rate': 4.840776425613886e-05, 'epoch': 3.43}
    {'loss': 0.0312, 'learning_rate': 3.162892359054098e-05, 'epoch': 3.66}
    {'loss': 0.018, 'learning_rate': 1.8078937319026654e-05, 'epoch': 3.89}
    {'loss': 0.0134, 'learning_rate': 8.127413744904804e-06, 'epoch': 4.11}
    {'loss': 0.0141, 'learning_rate': 2.0458044895916513e-06, 'epoch': 4.34}
    {'loss': 0.0099, 'learning_rate': 0.0, 'epoch': 4.57}
    {'train_runtime': 162.4618, 'train_samples_per_second': 2.154, 'train_steps_per_second': 0.123, 'train_loss': 0.8704732102807611, 'epoch': 4.57}

步骤五:使用Arena访问模型仓库

  1. 执行以下命令,使用Arena列出所有已注册模型。

    arena model list

    预期输出:

    NAME                 LATEST_VERSION       LAST_UPDATED_TIME
    Qwen-7B-Chat-Lora    1                    2024-04-30T10:26:14+08:00
  2. 执行以下命令,查看注册模型Qwen-7B-Chat-Lora(版本号为1)的版本详情。

    arena model get \
        --name Qwen-7B-Chat-Lora \
        --version 1

    展开查看注册模型Qwen-7B-Chat-Lora的版本详情

    Name:                Qwen-7B-Chat-Lora
    Version:             1
    CreationTime:        2024-04-30T10:26:14+08:00
    LastUpdatedTime:     2024-04-30T10:26:14+08:00
    Source:              pvc://default/nas-pvc/finetune/Qwen-7B-Chat-Lora
    Description:
      arena submit pytorchjob \
          --data nas-pvc:/mnt/ \
          --gpus 1 \
          --image kube-ai-registry.cn-shanghai.cr.aliyuncs.com/kube-ai/qwen:cu117 \
          --image-pull-policy Always \
          --model-name Qwen-7B-Chat-Lora \
          --model-source pvc://default/nas-pvc/finetune/Qwen-7B-Chat-Lora \
          --name qwen-7b-chat-finetune \
          --namespace default \
          --working-dir /data/shared/Qwen \
          "bash finetune/finetune_lora_single_gpu.sh -m /mnt/models/Qwen-7B-Chat/ -d example.json -o /mnt/finetune/Qwen-7B-Chat-Lora"
    Tags:
      createdBy: arena
      modelName: Qwen-7B-Chat-Lora
      arena.kubeflow.org/uid: 3399d840e8b371ed7ca45dda29debeb1

    输出结果表明Arena会自动将作业提交的完整命令添加至描述信息中,并添加相应的标签。

步骤六:使用Arena提交微调后的模型推理服务

  1. 执行以下命令,运行经过微调之后的Qwen-7B-Chat模型推理服务。

    arena serve custom \
        --name=qwen-7b-chat-lora \
        --namespace=default \
        --image=kube-ai-registry.cn-shanghai.cr.aliyuncs.com/kube-ai/qwen:cu117 \
        --image-pull-policy=Always \
        --gpus=1 \
        --data=nas-pvc:/mnt \
        --restful-port=80 \
        --model-name=Qwen-7B-Chat-Lora \
        --model-version=1 \
        "python web_demo_peft.py --server-port 80 --server-name 0.0.0.0 -c /mnt/finetune/Qwen-7B-Chat-Lora"

    预期输出:

    service/qwen-7b-chat-lora-202404301036 created
    deployment.apps/qwen-7b-chat-lora-202404301036-custom-serving created
    INFO[0003] The Job qwen-7b-chat-lora has been submitted successfully
    INFO[0003] You can run `arena serve get qwen-7b-chat-lora --type custom-serving -n default` to check the job status

    输出结果表明微调后的模型推理服务已成功部署。

  2. 执行以下命令,查看作业运行日志。

    arena serve logs -f qwen-7b-chat-lora

    预期输出:

    The model is automatically converting to bf16 for faster inference. If you want to disable the automatic precision, please manually add bf16/fp16/fp32=True to "AutoModelForCausalLM.from_pretrained".
    Try importing flash-attention for faster inference...
    Loading checkpoint shards: 100%|██████████| 8/8 [03:10<00:00, 23.76s/it]

  3. 模型加载完成之后,执行以下命令,将其Web端口转发到本地的8901端口进行访问。

    重要

    kubectl port-forward建立的端口转发仅适用于测试环境下的快速验证,不适合在生产环境中使用,使用时请注意安全风险。

    kubectl port-forward services/qwen-7b-chat-lora-202404301036 8901:80

    预期输出:

    Forwarding from 127.0.0.1:8901 -> 80
    Forwarding from [::1]:8901 -> 80
  4. 在浏览器地址栏中输入http://localhost:8901,然后提出与步骤二(模型微调前)相同的问题。

    以下示例展示了一个简单的对话场景,用户发起提问,助手给出回复。其中,Q表示用户输入,A表示模型输出:

    Q: 创建集群后,是否可以更改容器运行时?
    A: 创建集群后,不支持切换容器运行时,但是您可以创建不同类型运行时的节点池。节点池与节点池的运行时可以不同。更多信息,请参见节点池概述。

    将上述的答案和容器镜像中内置的微调数据进行对比,可以发现模型微调后助手回答问题的质量显著提升。

  5. 执行以下命令,删除微调后的Qwen-7B-Chat推理作业。

    arena serve delete qwen-7b-chat-lora

相关文档

如需对MLflow模型仓库中的模型进行管理,请参见对MLflow模型仓库中的模型进行管理