使用 Workbench 微调 LLM
本指南介绍如何使用从 Alauda AI Workbench 启动的 LLaMA-Factory 微调 LLM(示例:Qwen3-0.6B)。Notebook 会向集群提交一个 VolcanoJob,因此 GPU 任务在集群节点上运行,而你可以继续在 JupyterLab 中迭代。
当你需要交互式控制、自定义训练脚本以及针对每次实验调整 YAML 时,可以使用此方法。对于可复用的模板和配额,建议改用 Kubeflow Trainer v2。
适用范围
前提条件
- 已安装 Alauda AI Workbench 插件(或 Kubeflow Base + Notebook)。
- 已安装 MLflow 插件,用于实验跟踪。
1. 创建 Notebook / VSCode 实例
在 Alauda AI → Workbench(或 Advanced → Kubeflow → Notebook)中创建 workbench。workbench 本身只应请求 CPU — GPU 由它提交的 VolcanoJob 请求。请参阅创建 Workbench。
2. 准备基础模型
将 Qwen/Qwen3-0.6B(或任何 Hugging Face 模型)下载到共享 PVC 或训练作业可访问的 S3 兼容对象存储中。如果希望通过控制台部署或复用基础模型,请在 Model Registry 中注册生成的 Model URI。请参阅使用 Workbench 上传模型和 Model Registry。
3. 准备输出模型位置
使用共享 PVC 目录保存微调输出,或为作业配置一个 S3 位置以写入输出。作业完成后,使用其 Model URI 将输出 artifact 注册为新的 Model Registry 版本。无需预先创建占位模型记录。
4. 准备数据集
使用示例身份数据集训练模型回答“你是谁?”。将解压后的文件上传到训练作业可访问的 PVC 或 S3 位置,并将 DATASET_URI 设置为该位置。数据集布局必须与微调框架一致。
数据集格式必须符合微调框架的要求。
HuggingFace datasets 格式
import datasets
print(datasets.get_dataset_infos("<dataset directory>"))
print(datasets.load_dataset("<dataset directory>"))
LLaMA-Factory 格式
如果使用 LLaMA-Factory,请采用其要求的布局 — 请参阅 data_preparation。
5. 运行时镜像
使用预构建的 alaudadockerhub/fine_tune_with_llamafactory:v0.1.1,或自行构建镜像。镜像必须包含读取所选模型和数据集存储所需的工具。不需要包含大型文件模型仓库客户端。
Containerfile
ARG LLAMA_FACTORY_VERSION="v0.9.4"
FROM <registry-address>/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 && \
DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
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 && \
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;/torchvision>=0.19.0/d;/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"
WORKDIR /opt
6. 提交微调 VolcanoJob
创建 YAML,并在 Notebook 终端中使用 kubectl create -f vcjob_sft.yaml 提交。(使用 JupyterLab 上传器将 kubectl 二进制文件放入 workbench — 镜像中不包含该文件。)
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 (temporary; deleted after the job)
- mountPath: '/mnt/workspace'
volumeClaim:
accessModes: ['ReadWriteOnce']
storageClassName: 'sc-topolvm'
resources:
requests:
storage: 5Gi
tasks:
- name: 'train'
replicas: 1 # >= 2 for distributed training
template:
metadata:
name: train
spec:
restartPolicy: Never
securityContext:
runAsNonRoot: true
runAsUser: 65534
runAsGroup: 65534
fsGroup: 65534
volumes:
- name: dshm
emptyDir: { medium: Memory, sizeLimit: 2Gi }
# PVC for models and datasets. For distributed jobs, prefer NFS / Ceph
# for simplicity, or local storage pre-cached via kserve local model cache.
- name: models-cache
persistentVolumeClaim:
claimName: wy-model-cache
initContainers:
- name: prepare
image: alaudadockerhub/fine_tune_with_llamafactory:v0.1.1
imagePullPolicy: IfNotPresent
env:
- { name: BASE_MODEL_URI, value: 's3://models/qwen3-0.6b' }
- {
name: DATASET_URI,
value: 'pvc://dataset-pvc/identity-alauda',
}
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]
args:
- |
set -ex
# Mount or download the base model and dataset using the
# storage mechanism configured for the job. The example
# assumes the PVC mount already contains these directories.
cd /mnt/models
BASE_MODEL_NAME=qwen3-0.6b
DATASET_NAME=identity-alauda
test -d "${BASE_MODEL_NAME}" || { echo "Mount or stage ${BASE_MODEL_URI} at /mnt/models/${BASE_MODEL_NAME}"; exit 1; }
test -d "${DATASET_NAME}" || { echo "Mount or stage ${DATASET_URI} at /mnt/models/${DATASET_NAME}"; exit 1; }
containers:
- 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:
- { name: BASE_MODEL_URI, value: 's3://models/qwen3-0.6b' }
- {
name: DATASET_URI,
value: 'pvc://dataset-pvc/identity-alauda',
}
- { name: OUTPUT_MODEL_URI, value: 's3://models/wy-sft-output' }
- { name: HF_HOME, value: /mnt/workspace/hf_cache }
- { name: DO_MERGE, value: 'true' }
- {
name: MLFLOW_TRACKING_URI,
value: 'http://mlflow-tracking-server.kubeflow:5000',
}
- { name: MLFLOW_EXPERIMENT_NAME, value: '<your-namespace>' }
command: [bash, -c]
args:
- |
set -ex
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 WORLD_SIZE=$N_RANKS NNODES=$N_RANKS NODE_RANK=$RANK
export MASTER_ADDR=${MASTER_HOST} MASTER_PORT="8888"
else
export N_RANKS=1 RANK=0 NNODES=1 MASTER_HOST=""
fi
cd /mnt/workspace
BASE_MODEL_NAME=qwen3-0.6b
DATASET_NAME=identity-alauda
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
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 NODE_RANK MASTER_ADDR MASTER_PORT
llamafactory-cli train lf-sft.yaml
fi
if [ "${DO_MERGE}" = "true" ]; then
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_dir: output_models_merged
export_size: 4
export_device: cpu
export_legacy_format: false
EOL
llamafactory-cli export lf-merge-config.yaml
else
mv output_models output_models_merged
fi
# The merged artifact is now available at
# /mnt/workspace/output_models_merged. Upload it to the
# configured S3/PVC location with the organization's storage
# tooling, then register OUTPUT_MODEL_URI as a new Model
# Registry version. The training job does not use a
# source-code repository for model storage.
resources:
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 }
提交前需要修改以下内容:
- 将
BASE_MODEL_URI、DATASET_URI 和 OUTPUT_MODEL_URI 修改为环境所使用的受支持存储 URI。将基础模型和数据集挂载或暂存到示例所用的路径,并在作业完成后将输出目录上传到 OUTPUT_MODEL_URI。
models-cache PVC — 提前创建。跨实验复用它,以避免重复下载基础模型。
- 共享内存
dshm — 多 GPU 场景至少需要 4 GiB。
- CPU / memory / GPU
requests 和 limits — 与集群的 device-plugin 匹配(例如 nvidia.com/gpu、nvidia.com/gpualloc)。
- 超参数 — LLaMA-Factory YAML 内联在脚本中。将经常调整的参数提取为环境变量。
NFS workspace PVC 注意事项
如果 PVC 后端是 NFS:
7. 管理作业
kubectl get vcjob
kubectl get vcjob <name> -o yaml
kubectl get pod && kubectl logs <pod>
kubectl describe vcjob <name> # if pods aren't scheduling
kubectl get podgroups # Volcano scheduling view
kubectl delete vcjob <name>
成功后,将合并后的目录上传到 OUTPUT_MODEL_URI,并将其注册为新的 Model Registry 版本。发布时选择该活动版本。
8. 实验跟踪
在 LLaMA-Factory 配置中设置 report_to: mlflow,并配置 MLFLOW_TRACKING_URI / MLFLOW_EXPERIMENT_NAME 环境变量,即可将指标路由到 MLflow。在 Alauda AI → Tools → MLFlow 中查找运行记录、比较损失曲线,并固定表现最佳的运行记录。
在安全(SSO + 多租户)的 MLflow 安装中,作业还必须进行身份验证 — 提供一个 MLFLOW_TRACKING_TOKEN 并选择一个 workspace。请参阅使用带身份验证和 RBAC 的 MLflow Python SDK,了解如何获取令牌并配置客户端。
9. 发布微调后的模型
示例使用 LoRA,并在上传输出 artifact 前将 adapter 合并到基础模型中。当前尚不支持由基础模型 + adapter 组合构成的推理服务。
- 打开 AI Hub > Model Registry,将微调后的输出 URI 注册为新的模型版本,并在其元数据中设置 Task Type 和 Framework(例如
text-generation 和 transformers)。
- 打开活动版本,然后选择 Create Inference API。
- 选择与集群 CUDA 匹配的 vLLM 运行时,填写存储/资源和 GPU 设置,然后点击 Publish。
- 运行后,点击 Experience 与模型聊天(仅当模型包含
chat_template 时可用)。
在非 NVIDIA GPU 上运行
适用于 Huawei Ascend NPU、Intel Gaudi、AMD 等设备。关于使用 PyTorch CANN + MindSpeed-LLM 的 Ascend NPU 配方,请参阅在 Ascend NPU 上微调和预训练 LLM。
一般步骤:
- 前提条件:已部署供应商驱动程序和 Kubernetes device plugin,并且 Pod 可以看到设备。记录资源名称(例如
huawei.com/Ascend910: "1")。
- 收集供应商解决方案 — 文档、微调镜像、支持的模型、示例数据以及启动命令/参数。
- (可选)先端到端验证供应商解决方案,以排除解决方案本身的问题。
- (可选)将其封装为基本 Kubernetes Job,在加入 Volcano 之前确认 device plugin 在 K8s 下正常工作。
- 作为 VolcanoJob 运行,以下 YAML 可作为起点。
VolcanoJob YAML(供应商模板)
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
generateName: vcjob-sft-
spec:
minAvailable: 1
schedulerName: volcano
maxRetry: 1
queue: default
volumes:
- mountPath: '/mnt/workspace'
volumeClaim:
accessModes: ['ReadWriteOnce']
storageClassName: 'sc-topolvm'
resources:
requests:
storage: 5Gi
tasks:
- name: 'train'
replicas: 1
template:
metadata: { name: train }
spec:
restartPolicy: Never
volumes:
- name: dshm
emptyDir: { medium: Memory, sizeLimit: 2Gi }
- name: models-cache
persistentVolumeClaim:
claimName: sft-qwen3-volume
containers:
- name: train
image: '<vendor-fine-tuning-image>'
imagePullPolicy: IfNotPresent
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]
args:
- |
set -ex
echo "job workers list: ${VC_WORKER_HOSTS}"
# vendor-specific launch command goes here
resources:
requests: { cpu: '1', memory: '8Gi' }
limits:
cpu: '8'
memory: '16Gi'
nvidia.com/gpualloc: '1'
nvidia.com/gpucores: '50'
nvidia.com/gpumem: '8192'
在其他设备上进行实验跟踪
LLaMA-Factory 和 Transformers 可直接与 MLflow / wandb 集成。在框架配置中设置目标位置(例如 LLaMA-Factory 的 report_to: mlflow),并提供 MLFLOW_TRACKING_URI 和 MLFLOW_EXPERIMENT_NAME 环境变量(在安全安装中还需提供 MLFLOW_TRACKING_TOKEN — 请参阅使用带身份验证和 RBAC 的 MLflow Python SDK)。在 Alauda AI → Tools → MLFlow 下查看结果。