使用 Workbench 微调 LLM

背景

模型微调和训练通常需要适配不同的模型结构、硬件设备以及合适的并行训练方法。Alauda AI Workbench 提供了一套完整的方案,覆盖从模型开发到训练任务提交、管理以及实验跟踪,帮助模型和算法工程师快速适配并完成整个模型微调和训练流程。

在本方案中,我们提供了一个使用 LLaMA-Factory 微调 Qwen3-0.6B 模型的示例。

Alauda AI Workbench 会在用户命名空间中创建一个用于开发和调试的 Notebook/VSCode(CodeServer)容器环境。一个命名空间内可以创建多个 Notebook/VSCode 实例,以便为不同用户和开发任务保留各自的环境。Notebook 可以仅申请 CPU 资源用于开发和集群任务提交,同时使用集群的 GPU 资源来运行任务。Notebook 也可以申请 GPU 资源,使训练和微调等任务能够直接在 Notebook 内完成,而不受模型是否需要分布式训练的限制。

此外,你还可以使用平台内置的 MLFlow 为每次模型微调训练记录各种指标,从而更方便地对比多个实验并选择最终模型。

我们使用 Kubernetes 原生资源管理器 VolcanoJob 从 Notebook 中提交集群任务。Volcano 调度器支持队列、优先级以及多种调度策略,有助于更高效地进行集群任务调度并提升资源利用率。

本方案使用 LLaMA-Factory 来启动微调和训练任务。但是,对于更大规模的模型微调和训练场景,如果需要使用 Tensor Parallelism、Context Parallelism 和 Expert Parallelism 等并行方法来训练更大的模型,可能需要使用其他工具、构建自定义的微调运行时镜像,并修改任务启动脚本以适配不同的工具和模型。有关 LLaMA-Factory 的更详细用法和参数配置,请参考:https://llamafactory.readthedocs.io/en/latest/index.html

适用范围

  • 本方案适用于 Alauda AI 1.3 及以上版本。
  • LLM 模型的微调和训练。如果你需要训练其他类型的模型(例如 YOLOv5),则需要使用不同的镜像、启动脚本、数据集等。
  • 本方案适用于 x86/64 CPU 和 NVIDIA GPU 场景。
  • NPU 场景需要基于本方案构建合适的运行时镜像,以确保兼容。

准备工作

  • 你必须先部署 “Alauda AI Workbench” 插件以启用 Workbench 支持(或者部署 Kubeflow Base 插件以使用 Kubeflow 的 Notebook)。
  • 安装 MLFlow 插件用于实验跟踪。

LLM 模型微调步骤

创建 Notebook/VSCode 实例

进入 Alauda AI - Workbench(或 Advanced - Kubeflow - Notebook), 然后创建一个新的 Notebook 或使用已有的 Notebook。请注意,建议 Notebook 只使用 CPU 资源。 从 Notebook 中提交集群任务时,会在集群内申请 GPU 资源,以提高资源利用率。

有关创建 Notebook/VSCode 实例的详细步骤,请参见 Creating a Workbench

准备模型

从 Huggingface 或其他开源模型分享网站下载 Qwen/Qwen3-0.6B 模型。然后将模型上传到模型仓库。

有关将模型文件上传到模型仓库的详细步骤,请参见 Upload Models Using Notebook

准备模型输出位置

在模型仓库中创建一个空模型用于存放输出模型。 配置微调输出位置时,输入该模型的 Git 仓库 URL。

准备数据集

下载并将 sample identity dataset 推送到数据集仓库。 该数据集用于微调 LLM,使其能够回答类似 “Who are you?” 的用户问题。

  1. 首先,在 “Datasets” - “Dataset Repository” 下创建一个空的数据集仓库。
  2. 将 zip 文件上传到 notebook,解压后进入数据集目录。使用 git lfs 将数据集推送到数据集仓库的 Git URL。步骤与上传模型类似。
  3. 推送完成后,刷新数据集页面,你应该会在 “File Management” 标签页中看到文件已成功上传。

注意: 数据集格式必须能够被微调框架正确识别,才能用于后续的微调任务。 下面的示例展示了两种常见的 LLM 微调数据集格式:

Huggingface 数据集格式

你可以使用以下代码检查数据集目录格式是否能被 datasets 正确加载:

import datasets

ds_infos = datasets.get_dataset_infos(<dataset directory>)
ds = datasets.load_dataset(<dataset directory>)
print(ds_infos)
print(ds)

LLaMA-Factory 格式

如果你在示例中使用 LLaMA-Factory 工具完成训练,那么数据集格式必须符合 LLaMA-Factory 的格式。参考:data_preparation

准备运行时镜像

使用以下 Containerfile 构建训练运行时镜像,或者使用预构建镜像:alaudadockerhub/fine_tune_with_llamafactory:v0.1.1。如果你希望使用其他训练框架,例如 YOLOv5,则可能需要自定义镜像并安装所需依赖。

构建完成后,你需要将镜像上传到 Alauda AI 平台集群的镜像仓库,并在后续任务中进行配置。

注意: 镜像中需要包含 git lfs 命令,用于下载和上传模型及数据集文件。

Containerfile
ARG LLAMA_FACTORY_VERSION="v0.9.4"
# FROM docker-mirrors.alauda.cn/library/python:3.13-trixie
FROM 152-231-registry.alauda.cn:60070/mlops/nvidia/pytorch:24.12-py3

RUN sed -i 's@//.*archive.ubuntu.com@//mirrors.ustc.edu.cn@g' /etc/apt/sources.list.d/ubuntu.sources && \
sed -i 's/security.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/ubuntu.sources && \
apt-get update && \
export DEBIAN_FRONTEND=noninteractive && \
apt-get install -yq --no-install-recommends git git-lfs unzip curl ffmpeg default-libmysqlclient-dev build-essential pkg-config && \
apt clean && rm -rf /var/lib/apt/lists/*

RUN pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -U pip setuptools && \
cd /opt && \
HTTPS_PROXY=http://192.168.144.12:7890 git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git && \
cd LLaMA-Factory && git checkout ${LLAMA_FACTORY_VERSION} && \
sed -i '/torch>=2.4.0/d' pyproject.toml && \
sed -i '/torchvision>=0.19.0/d' pyproject.toml && \
sed -i '/torchaudio>=2.4.0/d' pyproject.toml && \
pip install --no-cache-dir -e ".[metrics,awq,modelscope]" -i https://pypi.tuna.tsinghua.edu.cn/simple

RUN pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple \
"transformers>=4.51.1,<=4.53.3" \
"tokenizers>=0.21.1" \
"sqlalchemy~=2.0.30" \
"pymysql~=1.1.1" \
"loguru~=0.7.2" \
"mysqlclient~=2.2.7" \
"deepspeed~=0.18.8" \
"mlflow>=3.1"

#RUN pip install torch==2.9.1 torchvision==0.24.1 torchaudio==2.9.1 --index-url https://download.pytorch.org/whl/cu126

WORKDIR /opt

创建微调 VolcanoJob 任务

在 Notebook 中创建用于任务提交的 YAML 文件。参考以下示例:

VolcanoJob YAML 文件
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
  generateName: vcjob-sft-qwen3-
spec:
  minAvailable: 1
  schedulerName: volcano
  maxRetry: 1
  queue: default
  volumes:
    # 任务运行所在的 workspace PVC(临时 PVC)
    - mountPath: "/mnt/workspace"
      volumeClaim:
        accessModes: [ "ReadWriteOnce" ]
        storageClassName: "sc-topolvm"
        resources:
          requests:
            storage: 5Gi
  tasks:
    - name: "train"
      # 并行副本数量。对于分布式训练任务,可以指定 replicas > 2
      replicas: 1
      template:
        metadata:
          name: train
        spec:
          restartPolicy: Never
          securityContext:
            runAsNonRoot: true
            runAsUser: 65534
            runAsGroup: 65534
            fsGroup: 65534
          # 挂载 shm 设备,以提供多卡通信所需的共享内存空间。
          volumes:
            - emptyDir:
                medium: Memory
                # 这里可以调整共享内存的大小
                sizeLimit: 2Gi
              name: dshm
            # 用于存储模型和数据集的 PVC。
            # 在分布式训练任务(replicas >= 2)中,请确保为大模型缓存使用合适的存储类型:
            # 1. 网络存储,例如 NFS 或 Ceph:只需挂载网络存储。请注意,多个容器可能会同时访问该网络存储,从而产生较高的并发流量。此外,读取大模型文件可能比本地读取更慢(取决于网络存储性能)。
            # 2. 本地存储,例如 topolvm 或 local-storage:在挂载该 PVC 之前,使用 `kserve local model cache` 预先将模型文件缓存到每个节点。训练任务无法缓存每个本地 PVC。
            - name: models-cache
              persistentVolumeClaim:
                claimName: wy-model-cache
          initContainers:
            - name: prepare
              image: alaudadockerhub/fine_tune_with_llamafactory:v0.1.1
              imagePullPolicy: IfNotPresent
              env:
              # 将 BASE_MODEL_URL 改为基础模型地址,DATASET_URL 改为数据集地址
              - name: BASE_MODEL_URL
                value: "https://<your-model-registry-git-address>/mlops-demo-ai-test/amlmodels/qwen3-0.6b"
              - name: DATASET_URL
                value: "https://<your-model-registry-git-address>/mlops-demo-ai-test/amldatasets/identity-alauda"
              - name: GIT_USER
                valueFrom:
                  secretKeyRef:
                    name: aml-image-builder-secret
                    key: MODEL_REPO_GIT_USER
              - name: GIT_TOKEN
                valueFrom:
                  secretKeyRef:
                    name: aml-image-builder-secret
                    key: MODEL_REPO_GIT_TOKEN
              resources:
                requests:
                  cpu: 100m
                  memory: 128Mi
                limits:
                  cpu: 2
                  memory: 4Gi
              securityContext:
                allowPrivilegeEscalation: false
                capabilities:
                  drop:
                    - ALL
                runAsNonRoot: true
                seccompProfile:
                  type: RuntimeDefault
              volumeMounts:
                - name: models-cache
                  mountPath: /mnt/models
              command:
              - /bin/bash
              - -c
              - |
                set -ex
                cd /mnt/models
                BASE_MODEL_NAME=$(basename ${BASE_MODEL_URL})
                # 下载基础模型
                gitauth="${GIT_USER}:${GIT_TOKEN}"
                BASE_MODEL_URL_NO_HTTPS="${BASE_MODEL_URL//https:\/\/}"
                if [ -d ${BASE_MODEL_NAME} ]; then
                    echo "${BASE_MODEL_NAME} dir already exists, skip downloading"
                else
                    GIT_LFS_SKIP_SMUDGE=1 git -c http.sslVerify=false -c lfs.activitytimeout=36000 clone "https://${gitauth}@${BASE_MODEL_URL_NO_HTTPS}"
                    (cd ${BASE_MODEL_NAME} && git -c http.sslVerify=false -c lfs.activitytimeout=36000 lfs pull)
                fi
                # 下载数据集
                DATASET_NAME=$(basename ${DATASET_URL})
                DATASET_URL_NO_HTTPS="${DATASET_URL//https:\/\/}"

                rm -rf ${DATASET_NAME}
                rm -rf data

                if [ -d ${DATASET_NAME} ]; then
                    echo "dataset ${DATASET_NAME} already exists skipping download"
                else
                    git -c http.sslVerify=false -c lfs.activitytimeout=36000 clone "https://${gitauth}@${DATASET_URL_NO_HTTPS}"
                fi
                echo "listing files under /mnt/models ..."
                ls /mnt/models
                echo "listing model files ..."
                ls ${BASE_MODEL_NAME}
                echo "listing dataset files ..."
                ls ${DATASET_NAME}
          containers:
            # 运行时环境镜像。你可以参考 src/llm/Containerfile 构建类似镜像。通常应包含 cuda、transformers、pytorch、datasets、evaluate 和 git lfs 等运行时依赖。
            - name: train
              image: alaudadockerhub/fine_tune_with_llamafactory:v0.1.1
              imagePullPolicy: IfNotPresent
              volumeMounts:
                - mountPath: /dev/shm
                  name: dshm
                - name: models-cache
                  mountPath: /mnt/models
              env:
                # 将 BASE_MODEL_URL 修改为基础模型地址,DATASET_URL 修改为数据集地址,OUTPUT_MODEL_URL 修改为输出模型地址
                - name: BASE_MODEL_URL
                  value: "https://<your-model-registry-git-address>/mlops-demo-ai-test/amlmodels/qwen3-0.6b"
                - name: DATASET_URL
                  value: "https://<your-model-registry-git-address>/mlops-demo-ai-test/amldatasets/identity-alauda"
                - name: OUTPUT_MODEL_URL
                  value: "https://<your-model-registry-git-address>/mlops-demo-ai-test/amlmodels/wy-sft-output"
                # huggingface 等库(如 datasets、transformers 等)使用的缓存目录
                - name: HF_HOME
                  value: /mnt/workspace/hf_cache
                - name: DO_MERGE
                  value: "true"
                - name: GIT_USER
                  valueFrom:
                    secretKeyRef:
                      name: aml-image-builder-secret
                      key: MODEL_REPO_GIT_USER
                - name: GIT_TOKEN
                  valueFrom:
                    secretKeyRef:
                      name: aml-image-builder-secret
                      key: MODEL_REPO_GIT_TOKEN
                # 将 MLFLOW_TRACKING_URI 修改为你的实际 mlflow 服务端点
                - name: MLFLOW_TRACKING_URI
                  value: "http://mlflow-tracking-server.kubeflow:5000"
                # 将 MLFLOW_EXPERIMENT_NAME 设置为你的命名空间或你自己的实验名称
                - name: MLFLOW_EXPERIMENT_NAME
                  value: mlops-demo-ai-test
              command:
              - bash
              - -c
              - |
                set -ex
                echo "job workers list: ${VC_WORKER_HOSTS}"
                if [ "${VC_WORKER_HOSTS}" != "" ]; then
                    export N_RANKS=$(echo "${VC_WORKER_HOSTS}" |awk -F',' '{print NF}')
                    export RANK=$VC_TASK_INDEX
                    export MASTER_HOST=$(echo "${VC_WORKER_HOSTS}" |awk -F',' '{print $1}')
                    export RANK=$RANK
                    export WORLD_SIZE=$N_RANKS
                    export NNODES=$N_RANKS
                    export NODE_RANK=$RANK
                    export MASTER_ADDR=${MASTER_HOST}
                    export MASTER_PORT="8888"
                else
                    export N_RANKS=1
                    export RANK=0
                    export NNODES=1
                    export MASTER_HOST=""
                fi

                cd /mnt/workspace
                BASE_MODEL_NAME=$(basename ${BASE_MODEL_URL})
                DATASET_NAME=$(basename ${DATASET_URL})

                cat >lf-sft.yaml <<EOL
                model_name_or_path: /mnt/models/${BASE_MODEL_NAME}

                stage: sft
                do_train: true
                finetuning_type: lora
                lora_target: all
                lora_rank: 8
                lora_alpha: 16
                lora_dropout: 0.1

                dataset: identity_alauda
                dataset_dir: /mnt/models/${DATASET_NAME}
                template: qwen
                cutoff_len: 1024
                max_samples: 1000
                overwrite_cache: true
                preprocessing_num_workers: 8

                output_dir: output_models
                logging_steps: 10
                save_steps: 500
                plot_loss: true
                overwrite_output_dir: true

                # global batch size: 8
                per_device_train_batch_size: 2
                gradient_accumulation_steps: 2
                learning_rate: 2.0e-4
                num_train_epochs: 4.0
                bf16: false
                fp16: true
                ddp_timeout: 180000000

                val_size: 0.1
                per_device_eval_batch_size: 1
                eval_strategy: steps
                eval_steps: 500
                report_to: mlflow
                EOL

                # 运行训练
                if [ ${NNODES} -gt 1 ]; then
                    echo "deepspeed: ds-z3-config.json" >> lf-sft.yaml
                    FORCE_TORCHRUN=1 llamafactory-cli train lf-sft.yaml
                else
                    unset NNODES
                    unset NODE_RANK
                    unset MASTER_ADDR
                    unset MASTER_PORT
                    llamafactory-cli train lf-sft.yaml
                fi

                if [ "${DO_MERGE}" == "true" ]; then
                  # 合并 LoRA adapter
                  cat >lf-merge-config.yaml <<EOL
                model_name_or_path: /mnt/models/${BASE_MODEL_NAME}
                adapter_name_or_path: output_models
                template: qwen
                finetuning_type: lora

                ### export
                export_dir: output_models_merged
                export_size: 4
                export_device: cpu
                export_legacy_format: false
                EOL

                  llamafactory-cli export lf-merge-config.yaml
                else
                  # 移动输出 adapter 以便推送
                  mv output_models output_models_merged
                fi
                # 将合并后的模型推送到模型仓库
                gitauth="${GIT_USER}:${GIT_TOKEN}"
                cd /mnt/workspace/output_models_merged
                touch README.md
                OUTPUT_MODEL_NO_HTTPS="${OUTPUT_MODEL_URL//https:\/\/}"
                PUSH_URL="https://${gitauth}@${OUTPUT_MODEL_NO_HTTPS}"
                push_branch=$(date +'%Y%m%d-%H%M%S')

                git init
                git checkout -b sft-${push_branch}
                git lfs track *.safetensors
                git add .
                git -c user.name='AMLSystemUser' -c user.email='aml_admin@cpaas.io' commit -am "fine tune push auto commit"
                git -c http.sslVerify=false -c lfs.activitytimeout=36000 push -u ${PUSH_URL} sft-${push_branch}
              resources:
                # 确保有足够资源运行微调。如果需要 GPU,请申请对应的 GPU/vGPU 资源。
                requests:
                  cpu: "1"
                  memory: "2Gi"
                limits:
                  cpu: "8"
                  memory: "16Gi"
                  nvidia.com/gpu: 1
              securityContext:
                allowPrivilegeEscalation: false
                capabilities:
                  drop:
                    - ALL
                runAsNonRoot: true
                seccompProfile:
                  type: RuntimeDefault

在提交任务之前,请在上面的 YAML 文件中修改以下设置。

  1. 镜像:包含任务执行所需的依赖。
  2. 环境变量:任务的原始模型、数据集和输出模型位置:
  • BASE_MODEL_URL:修改为准备好的模型 Git URL。
  • DATASET_URL:修改为准备好的数据集 identity-alauda 的 Git URL。
  • OUTPUT_MODEL_URL:在模型仓库中创建一个空模型用于存放输出模型,然后输入该模型的 Git URL。
  1. 任务所需资源,包括:
  • model cache PVC(可选):用于存放基础模型(在执行预训练任务时,可以省略基础模型下载步骤)和数据集的 PVC。你需要在上面的 YAML 文件中手动创建并指定一个 PVC。使用这个 “model cache PVC” 可以加速在多个模型上进行的多次微调实验。
  • workspace PVC(必需):任务运行所在的 PVC 和工作目录。检查点和输出模型文件将保存在这里。在上面的示例中,我们使用的是临时 PVC,任务结束后会被删除,因为输出模型已经上传到模型仓库。
  • Shared Memory:对于多 GPU/分布式训练任务,建议至少分配 4 Gi 的共享内存。
  • 任务所需的 CPU、memory 和 GPU 资源(基于集群中部署的 GPU device plugin)。
  1. Task 执行脚本
  2. 上面的示例脚本包含了将模型从模型仓库缓存到 PVC、将训练数据集缓存到 PVC,以及在微调后将模型推送到新的模型仓库的过程。你可以自定义脚本以适配自己的任务。
  3. 示例脚本使用 LLaMA-Factory 启动微调任务,它可以处理大多数 LLM 微调训练场景。
  4. 超参数:在上面的示例中,超参数直接定义在启动脚本中。你也可以使用环境变量读取可能需要反复调整的超参数,这样更便于多次运行和配置。

关于使用 NFS PVC 的说明

当使用 NFS 作为 workspace 或 model cache PVC 时,你需要确保执行以下操作,以保证挂载后的 NFS 卷具有正确的文件系统权限:

  • 所有可以使用 NFS PVC 的 K8s 节点都必须安装 nfs-utils,例如 yum install -y nfs-utils

  • 在创建 NFS storage class 时添加 mountPermissions: "0757" 设置,例如:

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: ai-nfs
      labels:
        project.cpaas.io/name: null
        project.cpaas.io/ALL_ALL: "true"
      annotations:
        cpaas.io/display-name: ""
        cpaas.io/access-mode: ReadWriteOnce,ReadWriteMany
        cpaas.io/features: ""
    provisioner: nfs.csi.k8s.io
    parameters:
      mountPermissions: "0757"
      server: 192.168.17.28
      share: /nfs_data/int/ai
    reclaimPolicy: Delete
    volumeBindingMode: Immediate
    mountOptions:
      - hard
      - nfsvers=4.1
    driverName: ""
INFO

Workbench 镜像默认未预装 kubectl。你可以使用 Jupyter 界面的上传按钮,将从本地机器下载的 kubectl 二进制文件上传到 Jupyter 环境中。

完成上述设置后,在 Notebook 中打开终端并执行:kubectl create -f vcjob_sft.yaml,即可将 VolcanoJob 提交到集群。

管理任务

在 Notebook 终端中:

  1. 运行 kubectl get vcjob 查看任务列表,然后运行 kubectl get vcjob <task name> 查看 VolcanoJob 任务状态。
  2. 运行 kubectl get pod 查看 pod 状态,运行 kubectl logs <pod name> 查看任务日志。请注意,对于分布式任务,可能会存在多个 pod。
  3. 如果 pod 未创建,运行 kubectl describe vcjob <task name>kubectl get podgroups 查看 Volcano podgroup。你也可以检查 Volcano 调度器日志,以判断调度问题是否由资源不足、无法挂载 PVC 或其他调度问题导致。
  4. 任务成功执行后,微调后的模型会自动推送到模型仓库。请注意,任务会根据时间自动生成用于推送的仓库分支。使用输出模型时,务必选择正确的版本。

运行 kubectl delete vcjob <task name> 删除任务。

实验跟踪

在上面的微调示例任务中,我们使用 LLaMA-Factory 启动微调任务,并在任务配置中设置 report_to: mlflow。这会自动将训练指标输出到 mlflow 服务器。任务启动后,你可以在 Alauda AI - Advanced - MLFlow 中找到实验跟踪记录,并对多个执行结果进行比较。例如,你可以比较多个实验的 loss。

使用微调后的模型启动 Inference Service

微调任务完成后,模型会自动推送到模型仓库。你可以使用微调后的模型来启动 inference service 并进行访问。

注意: 在上面的示例任务中,我们使用了 LoRA 微调方法。在上传模型之前,LoRA adapter 已与原始模型合并。这使得输出模型可以直接发布到 inference service。当前版本不支持使用基础模型和 adapters 启动 inference service。

步骤:

  1. 进入 Alauda AI - Model Repository,找到微调后的输出模型,进入 Model Info - File Management - Edit Metadata,将 “Task Type” 选择为 “Text Classification”,将 “Framework” 选择为 “Transformers”。
  2. 点击 Publish Inference API 按钮,然后选择 Custom Publishing
  3. 在 Publish Inference Service 页面,选择 vLLM inference runtime(选择集群 GPU 节点所支持 CUDA 版本的 vLLM),填写 storage、resource、GPU 等设置,然后点击 Publish
  4. 等待 inference service 完全启动后,点击右上角的 Experience 按钮即可开始与模型对话。(注意:包含 chat_template 配置的模型才具备聊天能力。)

在非 Nvidia GPU 上运行

当使用非 Nvidia GPU 环境(例如 NPU、Intel Gaudi、AMD 等)时,你可以按照下面的通用步骤在 AML Notebook 中微调模型、启动训练任务并进行管理。

基于 PyTorch CANN workbench 镜像和 MindSpeed-LLM notebooks 的 Huawei Ascend NPU 具体示例,请参见 Fine-tune and Pretrain LLMs on Ascend NPU Using Workbench

注意: 以下步骤也可以适配到 LLM 预训练和传统 ML 场景。它们是将厂商方案转换为在 Alauda AI 上通过 Notebook 和 VolcanoJob 运行的一般步骤。

准备工作

  1. 前置条件:集群中已部署厂商 GPU 驱动和 Kubernetes device plugin。Kubernetes 创建的 pod 内可以访问这些设备。 注意:你需要知道厂商 GPU 资源名称以及集群中设备资源的总数量,以便后续任务提交。 例如,对于 Huawei NPU,你可以使用如下方式申请 NPU 卡:huawei.com/Ascend910:1
  2. 获取厂商提供的当前 GPU 微调方案文档和材料。这通常包括:
  3. 方案文档和步骤。可以在 Kubernetes 上执行,或者在容器中使用 nerdctl run 执行。
  4. 用于运行微调的镜像。例如,厂商提供了基于 LLaMA-Factory 的微调方案以及对应的 LLaMA-Factory 镜像(也可能已包含在镜像中)。
  5. 用于运行微调的模型。通常,厂商设备支持一系列模型。请使用设备支持的模型,或使用厂商方案中提供的模型。
  6. 训练数据。使用厂商方案文档中提供的示例数据,或按照相同格式构建自己的数据集。
  7. 任务启动命令和参数。例如,LLaMA-Factory 框架的微调方案使用 llamafactory-cli 命令启动微调任务,并在 YAML 文件中配置包括任务超参数在内的各项参数。

验证原始厂商方案(可选)

为了确保厂商方案能够正确执行并减少后续排障工作,你可以先完全按照厂商方案运行一次,以验证其是否正常工作。

这一步可以跳过。但是,如果后续出现任务执行问题,你可以回到这一步,确认问题是否出在原始方案本身。

将厂商方案转换为 Kubernetes Job/Deployment 运行(可选)

如果厂商方案本身已经以 Kubernetes job/deployment/pod 的方式运行,则可以跳过此步骤。

如果厂商方案使用的是容器执行方式,例如 nerdctl run,你可以先使用一个简单的 Kubernetes job 验证该方案是否能在已部署厂商 device plugin 的 Kubernetes 环境中正常运行。

注意: 此步骤可以排除 volcano job 无法调度厂商 GPU 设备的问题,因此可以单独进行验证。

参考:

apiVersion: batch/v1
kind: Job
metadata:
  name: custom-gpu-ft-job
spec:
  template:
    spec:
      containers:
      - name: train
        image: <厂商用于微调训练方案的镜像>
        command: ["任务启动命令", "参数 1", "参数 2"]
      restartPolicy: Never
  # 注意:如果是分布式任务,也可以通过修改 parallelism、completions 指定分布式训练的并行度。
  completions: 1
  parallelism: 1

将厂商方案修改为 volcano job 运行

参考以下 YAML 定义

VolcanoJob YAML 文件
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
  generateName: vcjob-sft-
spec:
  minAvailable: 1
  schedulerName: volcano
  maxRetry: 1
  queue: default
  volumes:
    # 任务运行所在的 workspace PVC(临时 PVC)
    - mountPath: "/mnt/workspace"
      volumeClaim:
        accessModes: [ "ReadWriteOnce" ]
        storageClassName: "sc-topolvm"
        resources:
          requests:
            storage: 5Gi
  tasks:
    - name: "train"
      # 并行副本数量。对于分布式训练任务,可以指定 replicas >= 2
      replicas: 1
      template:
        metadata:
          name: train
        spec:
          restartPolicy: Never
          # 挂载 shm 设备,以提供多卡通信所需的共享内存空间。
          volumes:
            - emptyDir:
                medium: Memory
                # 这里可以调整共享内存的大小
                sizeLimit: 2Gi
              name: dshm
            # 用于存储模型和数据集的 PVC。
            # 在分布式训练任务(replicas >= 2)中,请确保为大模型缓存使用合适的存储类型:
            # 1. 网络存储,例如 NFS 或 Ceph:只需挂载网络存储。请注意,多个容器可能会同时访问该网络存储,从而产生较高的并发流量。此外,读取大模型文件可能比本地读取更慢(取决于网络存储性能)。
            # 2. 本地存储,例如 topolvm 或 local-storage:在挂载该 PVC 之前,使用 `kserve local model cache` 预先将模型文件缓存到每个节点。训练任务无法缓存每个本地 PVC。
            - name: models-cache
              persistentVolumeClaim:
                claimName: sft-qwen3-volume
          containers:
            # 运行环境镜像。
            - image: <指定厂商方案使用的镜像,或在本地自定义镜像>
              imagePullPolicy: IfNotPresent
              name: train
              volumeMounts:
                - mountPath: /dev/shm
                  name: dshm
                - name: models-cache
                  mountPath: /mnt/models
              env:
                - name: MLFLOW_TRACKING_URI
                  value: "http://mlflow-tracking-server.aml-system.svc.cluster.local:5000"
                - name: MLFLOW_EXPERIMENT_NAME
                  value: kubeflow-admin-cpaas-io
              command:
              - bash
              - -c
              - |
                set -ex
                echo "job workers list: ${VC_WORKER_HOSTS}"
                # 在下方添加启动任务的命令行
                # ...
              resources:
                # 确保有足够资源运行微调。如果需要 GPU,请申请对应的 GPU/vGPU 资源。
                requests:
                  cpu: "1"
                  memory: "8Gi"
                limits:
                  cpu: "8"
                  memory: "16Gi"
                  nvidia.com/gpualloc: "1"
                  nvidia.com/gpucores: "50"
                  nvidia.com/gpumem: "8192"

实验跟踪

一些微调/训练框架会自动将实验进度记录到各种实验跟踪服务中。例如,LLaMA-FactoryTransformers 框架都可以指定将实验进度记录到 mlflow、wandb 等服务。根据你的部署情况,可以配置以下环境变量:

  • MLFLOW_TRACKING_URI:mlflow tracking server 的 URL。
  • MLFLOW_EXPERIMENT_NAME:实验名称,通常使用命名空间名称,用于区分一组任务。

框架还会指定记录目的地。例如,LLaMA-Factory 需要在任务参数配置 YAML 文件中指定 report_to: mlflow

训练任务开始后,你可以在 Alauda AI - “Advanced” - MLFlow 界面中找到对应任务,并在 “Metrics” 中查看各项已记录指标的曲线,或查看每次执行的参数配置。你也可以比较多个实验。

总结

借助 Alauda AI Notebook 开发环境,你可以使用 YAML 和命令行工具快速将微调和训练任务提交到集群,并管理这些任务的执行状态。这种方式使你能够快速开发和自定义模型微调与训练步骤,从而支持 LLM SFT、偏好对齐、传统模型训练以及多实验对比等操作。