This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
# .readthedocs.yaml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the OS, Python version and other tools you might need
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.10"
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
# - pdf
|
||||
# - epub
|
||||
|
||||
# Optional but recommended, declare the Python requirements required
|
||||
# to build your documentation
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: requirements/docs.txt
|
||||
- requirements: requirements/framework.txt
|
||||
@@ -0,0 +1,321 @@
|
||||
# AMD GPU 支持
|
||||
|
||||
## 1. 环境配置
|
||||
### 1.1 基础环境
|
||||
拉取适配了 AMD ROCm 生态的 ms-swift 镜像,并参照以下命令启动容器。
|
||||
|
||||
如果用户需要运行更新版本的 ms-swift,可以使用 pip 升级或基于源码安装更新(建议安装时添加 `--no-deps` 选项以避免自动升级其他依赖可能引起的问题)。
|
||||
|
||||
```bash
|
||||
IMAGE_NAME=amdagi/modelscope:ubuntu22.04-rocm7.2.0-py312-torch2.10.0-vllm0.18.1-modelscope1.35.1-swift4.1.0
|
||||
docker pull ${IMAGE_NAME}
|
||||
|
||||
CONTAINER_NAME=swift_test
|
||||
docker run -it --network=host --ipc=host --privileged --group-add video \
|
||||
--device=/dev/dri --device=/dev/kfd \
|
||||
--shm-size 512G --ulimit memlock=-1 \
|
||||
--security-opt seccomp=unconfined --cap-add SYS_PTRACE \
|
||||
--name ${CONTAINER_NAME} \
|
||||
${IMAGE_NAME} \
|
||||
/bin/bash
|
||||
```
|
||||
|
||||
### 1.2 环境检查
|
||||
|
||||
- 确认 container 环境中 pytorch 正确识别 AMD GPU。
|
||||
|
||||
```bash
|
||||
python -c "import torch;print(torch.cuda.is_available())" # output: True
|
||||
```
|
||||
|
||||
- 检查 GPU 的拓扑连接及 NUMA:`rocm-smi --showtopo`
|
||||
|
||||
```
|
||||
============================ ROCm System Management Interface ============================
|
||||
WARNING: AMD GPU device(s) is/are in a low-power state. Check power control/runtime_status
|
||||
|
||||
================================ Weight between two GPUs =================================
|
||||
GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7
|
||||
GPU0 0 15 15 15 15 15 15 15
|
||||
GPU1 15 0 15 15 15 15 15 15
|
||||
GPU2 15 15 0 15 15 15 15 15
|
||||
GPU3 15 15 15 0 15 15 15 15
|
||||
GPU4 15 15 15 15 0 15 15 15
|
||||
GPU5 15 15 15 15 15 0 15 15
|
||||
GPU6 15 15 15 15 15 15 0 15
|
||||
GPU7 15 15 15 15 15 15 15 0
|
||||
|
||||
================================= Hops between two GPUs ==================================
|
||||
GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7
|
||||
GPU0 0 1 1 1 1 1 1 1
|
||||
GPU1 1 0 1 1 1 1 1 1
|
||||
GPU2 1 1 0 1 1 1 1 1
|
||||
GPU3 1 1 1 0 1 1 1 1
|
||||
GPU4 1 1 1 1 0 1 1 1
|
||||
GPU5 1 1 1 1 1 0 1 1
|
||||
GPU6 1 1 1 1 1 1 0 1
|
||||
GPU7 1 1 1 1 1 1 1 0
|
||||
|
||||
=============================== Link Type between two GPUs ===============================
|
||||
GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7
|
||||
GPU0 0 XGMI XGMI XGMI XGMI XGMI XGMI XGMI
|
||||
GPU1 XGMI 0 XGMI XGMI XGMI XGMI XGMI XGMI
|
||||
GPU2 XGMI XGMI 0 XGMI XGMI XGMI XGMI XGMI
|
||||
GPU3 XGMI XGMI XGMI 0 XGMI XGMI XGMI XGMI
|
||||
GPU4 XGMI XGMI XGMI XGMI 0 XGMI XGMI XGMI
|
||||
GPU5 XGMI XGMI XGMI XGMI XGMI 0 XGMI XGMI
|
||||
GPU6 XGMI XGMI XGMI XGMI XGMI XGMI 0 XGMI
|
||||
GPU7 XGMI XGMI XGMI XGMI XGMI XGMI XGMI 0
|
||||
|
||||
======================================= Numa Nodes =======================================
|
||||
GPU[0] : (Topology) Numa Node: 0
|
||||
GPU[0] : (Topology) Numa Affinity: 0
|
||||
GPU[1] : (Topology) Numa Node: 0
|
||||
GPU[1] : (Topology) Numa Affinity: 0
|
||||
GPU[2] : (Topology) Numa Node: 0
|
||||
GPU[2] : (Topology) Numa Affinity: 0
|
||||
GPU[3] : (Topology) Numa Node: 0
|
||||
GPU[3] : (Topology) Numa Affinity: 0
|
||||
GPU[4] : (Topology) Numa Node: 1
|
||||
GPU[4] : (Topology) Numa Affinity: 1
|
||||
GPU[5] : (Topology) Numa Node: 1
|
||||
GPU[5] : (Topology) Numa Affinity: 1
|
||||
GPU[6] : (Topology) Numa Node: 1
|
||||
GPU[6] : (Topology) Numa Affinity: 1
|
||||
GPU[7] : (Topology) Numa Node: 1
|
||||
GPU[7] : (Topology) Numa Affinity: 1
|
||||
================================== End of ROCm SMI Log ===================================
|
||||
```
|
||||
|
||||
- 查看 GPU 利用率及显存占用等信息(`rocm-smi` 或者 `rocm-smi -u --showmeminfo vram`)
|
||||
|
||||
```
|
||||
# output of 'rocm-smi'
|
||||
============================================ ROCm System Management Interface ============================================
|
||||
====================================================== Concise Info ======================================================
|
||||
Device Node IDs Temp Power Partitions SCLK MCLK Fan Perf PwrCap VRAM% GPU%
|
||||
(DID, GUID) (Junction) (Socket) (Mem, Compute, ID)
|
||||
==========================================================================================================================
|
||||
0 2 0x74a2, 1017 43.0°C 155.0W NPS1, SPX, 0 94Mhz 900Mhz 0% auto 650.0W 0% 0%
|
||||
1 3 0x74a2, 47713 41.0°C 155.0W NPS1, SPX, 0 91Mhz 900Mhz 0% auto 650.0W 0% 0%
|
||||
2 4 0x74a2, 37449 45.0°C 159.0W NPS1, SPX, 0 95Mhz 900Mhz 0% auto 650.0W 0% 0%
|
||||
3 5 0x74a2, 11217 41.0°C 155.0W NPS1, SPX, 0 95Mhz 900Mhz 0% auto 650.0W 0% 0%
|
||||
4 6 0x74a2, 41880 44.0°C 160.0W NPS1, SPX, 0 91Mhz 900Mhz 0% auto 650.0W 0% 0%
|
||||
5 7 0x74a2, 6656 42.0°C 157.0W NPS1, SPX, 0 95Mhz 900Mhz 0% auto 650.0W 0% 0%
|
||||
6 8 0x74a2, 12840 45.0°C 160.0W NPS1, SPX, 0 96Mhz 900Mhz 0% auto 650.0W 0% 0%
|
||||
7 9 0x74a2, 35760 43.0°C 158.0W NPS1, SPX, 0 107Mhz 900Mhz 0% auto 650.0W 0% 0%
|
||||
==========================================================================================================================
|
||||
================================================== End of ROCm SMI Log ===================================================
|
||||
|
||||
# output of 'rocm-smi -u --showmeminfo vram'
|
||||
============================ ROCm System Management Interface ============================
|
||||
=================================== % time GPU is busy ===================================
|
||||
GPU[0] : GPU use (%): 0
|
||||
GPU[0] : GFX Activity: 3862538534
|
||||
GPU[1] : GPU use (%): 0
|
||||
GPU[1] : GFX Activity: 4053246251
|
||||
GPU[2] : GPU use (%): 0
|
||||
GPU[2] : GFX Activity: 3114103535
|
||||
GPU[3] : GPU use (%): 0
|
||||
GPU[3] : GFX Activity: 4026776444
|
||||
GPU[4] : GPU use (%): 0
|
||||
GPU[4] : GFX Activity: 1224255679
|
||||
GPU[5] : GPU use (%): 0
|
||||
GPU[5] : GFX Activity: 1191191242
|
||||
GPU[6] : GPU use (%): 0
|
||||
GPU[6] : GFX Activity: 1184652679
|
||||
GPU[7] : GPU use (%): 0
|
||||
GPU[7] : GFX Activity: 2145209382
|
||||
==========================================================================================
|
||||
================================== Memory Usage (Bytes) ==================================
|
||||
GPU[0] : VRAM Total Memory (B): 206141652992
|
||||
GPU[0] : VRAM Total Used Memory (B): 297611264
|
||||
GPU[1] : VRAM Total Memory (B): 206141652992
|
||||
GPU[1] : VRAM Total Used Memory (B): 297623552
|
||||
GPU[2] : VRAM Total Memory (B): 206141652992
|
||||
GPU[2] : VRAM Total Used Memory (B): 297623552
|
||||
GPU[3] : VRAM Total Memory (B): 206141652992
|
||||
GPU[3] : VRAM Total Used Memory (B): 297623552
|
||||
GPU[4] : VRAM Total Memory (B): 206141652992
|
||||
GPU[4] : VRAM Total Used Memory (B): 297623552
|
||||
GPU[5] : VRAM Total Memory (B): 206141652992
|
||||
GPU[5] : VRAM Total Used Memory (B): 297623552
|
||||
GPU[6] : VRAM Total Memory (B): 206141652992
|
||||
GPU[6] : VRAM Total Used Memory (B): 297623552
|
||||
GPU[7] : VRAM Total Memory (B): 206141652992
|
||||
GPU[7] : VRAM Total Used Memory (B): 297623552
|
||||
==========================================================================================
|
||||
================================== End of ROCm SMI Log ===================================
|
||||
```
|
||||
|
||||
## 2. 运行示例
|
||||
|
||||
### 2.1 使用 Megatron-Swift 全量微调 Qwen3.5 模型
|
||||
AMD GPU 显存相对较大,因此可以通过同时对以下参数进行联合调优,以提升训练吞吐性能。
|
||||
- 并行度调优(TP/PP/EP等):GPU 单卡显存较大使得用户可以尽可能减小并行度切分带来的通信开销(优先级 PP/EP > TP)
|
||||
- 显存允许的情况下关闭 optimizer cpu offload:设置 `--optimizer_cpu_offload false`
|
||||
- 显存允许的情况下调整 activation/gradient checkpointing:设置 `--recompute_granularity none`,或者 `--recompute_granularity selective` 配合 `--recompute_modules` 进行细粒度的控制
|
||||
- 对于 MoE 模型,建议设置 `export NVTE_USE_GROUPED_GEMM_TRITON=1` 以使用 triton 实现的 grouped gemm kernel
|
||||
- 对于带有 GatedDeltaNet 结构的模型,建议设置 `USE_MCORE_GDN=1` 使用 mcore 的实现版本
|
||||
- 为避免在某些 AMD GPU 上可能出现的问题,保证性能更稳定,建议设置 `export HSA_NO_SCRATCH_RECLAIM=1`
|
||||
|
||||
单机训练:
|
||||
|
||||
```bash
|
||||
export HSA_NO_SCRATCH_RECLAIM=1
|
||||
export NVTE_USE_GROUPED_GEMM_TRITON=1
|
||||
|
||||
output_dir=${PWD}/megatron_output/Qwen3.5-35B-A3B
|
||||
mkdir -p ${output_dir}
|
||||
current_time=$(date "+%Y.%m.%d-%H.%M.%S")
|
||||
log_file=${output_dir}/"1node_full_megatron_Qwen3.5-35B-A3B_${current_time}.log"
|
||||
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
NPROC_PER_NODE=8 \
|
||||
MAX_PIXELS=1003520 \
|
||||
VIDEO_MAX_PIXELS=50176 \
|
||||
FPS_MAX_FRAMES=12 \
|
||||
SKIP_MULTIMODAL_MTP_VALIDATION=1 \
|
||||
USE_MCORE_GDN=1 \
|
||||
megatron sft \
|
||||
--model Qwen/Qwen3.5-35B-A3B \
|
||||
--dataset 'AI-ModelScope/LongAlpaca-12k' \
|
||||
--save_safetensors true \
|
||||
--load_from_cache_file true \
|
||||
--tuner_type full \
|
||||
--add_non_thinking_prefix true \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--tensor_model_parallel_size 1 \
|
||||
--pipeline_model_parallel_size 1 \
|
||||
--expert_model_parallel_size 8 \
|
||||
--sequence_parallel true \
|
||||
--moe_permute_fusion true \
|
||||
--moe_grouped_gemm true \
|
||||
--moe_shared_expert_overlap true \
|
||||
--moe_aux_loss_coeff 1e-6 \
|
||||
--moe_expert_capacity_factor 2 \
|
||||
--micro_batch_size 1 \
|
||||
--global_batch_size 8 \
|
||||
--recompute_granularity selective \
|
||||
--recompute_modules core_attn mlp moe \
|
||||
--gradient_accumulation_fusion false \
|
||||
--num_train_epochs 500 \
|
||||
--group_by_length true \
|
||||
--finetune true \
|
||||
--freeze_llm false \
|
||||
--freeze_vit false \
|
||||
--freeze_aligner false \
|
||||
--cross_entropy_loss_fusion true \
|
||||
--lr 1e-5 \
|
||||
--lr_warmup_fraction 0.05 \
|
||||
--min_lr 1e-6 \
|
||||
--weight_decay 0.1 \
|
||||
--adam_beta2 0.95 \
|
||||
--eval_steps 500 \
|
||||
--save_steps 500 \
|
||||
--save_total_limit 10 \
|
||||
--logging_steps 1 \
|
||||
--max_length 16384 \
|
||||
--dataloader_num_workers 8 \
|
||||
--dataset_num_proc 8 \
|
||||
--no_save_optim true \
|
||||
--no_save_rng true \
|
||||
--optimizer_cpu_offload false \
|
||||
--attention_backend flash \
|
||||
--padding_free false \
|
||||
--output_dir ${output_dir} \
|
||||
2>&1 | tee ${log_file}
|
||||
```
|
||||
|
||||
多机训练:
|
||||
|
||||
```bash
|
||||
export NNODES=2 # 此处以 2 节点为例
|
||||
export NODE_RANK=0 # 主节点设置为 0,从节点设置为 1
|
||||
export MASTER_ADDR=<MASTER_NODE_IP> # 根据主节点 ip 设置
|
||||
export MASTER_PORT=29500 # 设置通信端口
|
||||
export NCCL_SOCKET_IFNAME=ens50f1np1 # 根据机器实际通信网口名设置,可通过 ifconfig 查看
|
||||
export GLOO_SOCKET_IFNAME=ens50f1np1 # 根据机器实际通信网口名设置,可通过 ifconfig 查看
|
||||
export NCCL_IB_HCA=mlx5_0,mlx5_1,mlx5_2,mlx5_3 # 根据实际IB网卡名设置,可通过 ibv_devices 查看
|
||||
export NCCL_IB_GID_INDEX=3
|
||||
|
||||
# 训练脚本主体:参照单机训练脚本
|
||||
...
|
||||
```
|
||||
|
||||
### 2.2 使用 Megatron-Swift 对 Qwen3.5 模型做强化学习训练
|
||||
|
||||
```bash
|
||||
# 单机训练样例
|
||||
export HSA_NO_SCRATCH_RECLAIM=1
|
||||
export NVTE_USE_GROUPED_GEMM_TRITON=1
|
||||
|
||||
SYSTEM_PROMPT="""You are a helpful math assistant. Solve the problem step by step and put your final answer within \\boxed{}."""
|
||||
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
|
||||
NPROC_PER_NODE=8 \
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
megatron rlhf \
|
||||
--rlhf_type grpo \
|
||||
--model Qwen/Qwen3.5-35B-A3B \
|
||||
--save_safetensors true \
|
||||
--enable_thinking false \
|
||||
--merge_lora true \
|
||||
--context_parallel_size 1 \
|
||||
--tensor_model_parallel_size 1 \
|
||||
--expert_model_parallel_size 8 \
|
||||
--pipeline_model_parallel_size 1 \
|
||||
--moe_permute_fusion true \
|
||||
--dataset open-r1/DAPO-Math-17k-Processed \
|
||||
--system "$SYSTEM_PROMPT" \
|
||||
--num_train_epochs 1 \
|
||||
--global_batch_size 64 \
|
||||
--micro_batch_size 1 \
|
||||
--steps_per_generation 2 \
|
||||
--num_generations 8 \
|
||||
--reward_funcs accuracy \
|
||||
--use_vllm true \
|
||||
--vllm_mode colocate \
|
||||
--vllm_gpu_memory_utilization 0.5 \
|
||||
--vllm_tensor_parallel_size 2 \
|
||||
--vllm_max_model_len 9192 \
|
||||
--max_length 1000 \
|
||||
--max_completion_length 8192 \
|
||||
--tuner_type lora \
|
||||
--target_modules all-linear \
|
||||
--lr 5e-5 \
|
||||
--bf16 true \
|
||||
--beta 0.00 \
|
||||
--epsilon 0.2 \
|
||||
--epsilon_high 0.28 \
|
||||
--dynamic_sample false \
|
||||
--overlong_filter true \
|
||||
--loss_type grpo \
|
||||
--sleep_level 1 \
|
||||
--offload_model true \
|
||||
--offload_bridge false \
|
||||
--offload_optimizer true \
|
||||
--logging_steps 1 \
|
||||
--recompute_granularity none \
|
||||
--gradient_accumulation_fusion false \
|
||||
--finetune \
|
||||
--dataloader_num_workers 8 \
|
||||
--dataset_num_proc 8 \
|
||||
--no_save_optim \
|
||||
--no_save_rng \
|
||||
--save_steps 20 \
|
||||
--attention_backend flash \
|
||||
--moe_expert_capacity_factor 2 \
|
||||
--temperature 1.0 \
|
||||
--padding_free false \
|
||||
--sequence_parallel true \
|
||||
--log_completions true \
|
||||
--report_to tensorboard
|
||||
```
|
||||
|
||||
## 已知问题
|
||||
- 强化学习训练:
|
||||
- 在强化学习训练中,如果使用 vLLM 作为推理引擎,需要 vLLM>=0.11.0。建议使用 ROCm7.0 或者我们提供的镜像以避免出现 sleep mode memory leak 问题。
|
||||
- 在使用 [Ray Megatron](../../source/Instruction/Ray.md) 而非 `torchrun` 的方式进行多 GPU/Node 训练时,不设置 `CUDA_VISIBLE_DEVICES`/`HIP_VISIBLE_DEVICES`等,以避免冲突问题。
|
||||
- MoE 模型训练:
|
||||
- MoE 模型建议增加环境变量 `NVTE_USE_GROUPED_GEMM_TRITON=1` 和 `--gradient_accumulation_fusion false` 以避免偶发的GPU卡死问题。
|
||||
@@ -0,0 +1,211 @@
|
||||
# Elastic
|
||||
|
||||
|
||||
|
||||
## 安装依赖
|
||||
|
||||
集群部署K8S,并在集群中部署DLrover,[DLRover](https://github.com/intelligent-machine-learning/dlrover),
|
||||
`pip install dlrover && pip install tornado && pip install kubernetes && pip install ms-swift`
|
||||
|
||||
经过反复测试验证的训练镜像中的其它依赖以及版本:
|
||||
deepspeed 0.16.5(需参考https://github.com/deepspeedai/DeepSpeed/pull/7585/files 修复universal checkpoint 相关问题)
|
||||
pytorch 2.6.0
|
||||
|
||||
|
||||
## 如何启动
|
||||
|
||||
通过在`--callbacks`中添加`deepspeed_elastic`(可选`graceful_exit`)启用弹性训练,并配置DeepSpeed弹性参数。
|
||||
命令组成=dlrover-run +dlrover 命令参数+swift 启动命令 +swift参数,dlrover-run除自定义的参数外,其他参数与torchrun一致;
|
||||
dlrover-run 参数如下:
|
||||
```
|
||||
usage: dlrover-run [-h] [--nnodes NNODES] [--nproc-per-node NPROC_PER_NODE]
|
||||
[--rdzv-backend RDZV_BACKEND] [--rdzv-endpoint RDZV_ENDPOINT] [--rdzv-id RDZV_ID]
|
||||
[--rdzv-conf RDZV_CONF] [--standalone] [--max-restarts MAX_RESTARTS]
|
||||
[--monitor-interval MONITOR_INTERVAL] [--start-method {spawn,fork,forkserver}]
|
||||
[--role ROLE] [-m] [--no-python] [--run-path] [--log-dir LOG_DIR] [-r REDIRECTS]
|
||||
[-t TEE] [--local-ranks-filter LOCAL_RANKS_FILTER] [--node-rank NODE_RANK]
|
||||
[--master-addr MASTER_ADDR] [--master-port MASTER_PORT] [--local-addr LOCAL_ADDR]
|
||||
[--logs-specs LOGS_SPECS] [--precheck {0,1,2}] [--node_unit NODE_UNIT]
|
||||
[--auto_config] [--auto_tunning] [--exclude-straggler] [--save_at_breakpoint]
|
||||
[--accelerator {nvidia.com/gpu,ascend-npu}] [--training_port TRAINING_PORT]
|
||||
[--switchbox-check] [--box-pairs PAIR [PAIR ...]] [--min-bandwidth MIN_BANDWIDTH]
|
||||
[--min-channels MIN_CHANNELS] [--numa-affinity] [--network-check]
|
||||
[--comm-perf-test] [--ucp_device_type UCP_DEVICE_TYPE]
|
||||
training_script
|
||||
|
||||
```
|
||||
在弹性训练中我们需要关注的参数为:
|
||||
|
||||
--nnodes NNODES Number of nodes, or the range of nodes in form
|
||||
<minimum_nodes>:<maximum_nodes>.
|
||||
|
||||
--nproc-per-node NPROC_PER_NODE Number of processes per node.
|
||||
示例:
|
||||
|
||||
```bash
|
||||
model=your model path
|
||||
dataset=your dataset
|
||||
output= your output dir
|
||||
export CUDA_VISIBLE_DEVICES=0 根据实际使用的GPU情况设置
|
||||
deepspeed_config_or_type=deepspeed类型或者配置文件的路径,如 zero1 或者/xxx/ms-swift/swift/llm/ds_config/zero1.json
|
||||
|
||||
dlrover-run --nnodes 1:$NODE_NUM --nproc_per_node=1 \
|
||||
/opt/conda/lib/python3.10/site-packages/swift/cli/sft.py --model $model \
|
||||
--model_type qwen3 \
|
||||
--tuner_type lora \
|
||||
--torch_dtype bfloat16 \
|
||||
--dataset $dataset \
|
||||
--num_train_epochs 4 \
|
||||
--per_device_train_batch_size 1 \
|
||||
--per_device_eval_batch_size 1 \
|
||||
--learning_rate 5e-7 \
|
||||
--gradient_accumulation_steps 8 \
|
||||
--eval_steps 500 \
|
||||
--save_steps 10 \
|
||||
--save_total_limit 20 \
|
||||
--logging_steps 1 \
|
||||
--output_dir $output \
|
||||
--warmup_ratio 0.01 \
|
||||
--dataloader_num_workers 4 \
|
||||
--temperature 1.0 \
|
||||
--system 'You are a helpful assistant.' \
|
||||
--lora_rank 8 \
|
||||
--lora_alpha 32 \
|
||||
--target_modules all-linear \
|
||||
--dataset_num_proc 1 \
|
||||
--use_flash_ckpt true \
|
||||
--callbacks deepspeed_elastic graceful_exit \
|
||||
--deepspeed $deepspeed_config_or_type \
|
||||
```
|
||||
|
||||
## 配置文件示例
|
||||
默认情况下的zero1为以下示例配置,
|
||||
|
||||
```json
|
||||
{
|
||||
"fp16": {
|
||||
"enabled": "auto",
|
||||
"loss_scale": 0,
|
||||
"loss_scale_window": 1000,
|
||||
"initial_scale_power": 16,
|
||||
"hysteresis": 2,
|
||||
"min_loss_scale": 1
|
||||
},
|
||||
|
||||
"bf16": {
|
||||
"enabled": "auto"
|
||||
},
|
||||
|
||||
"zero_optimization": {
|
||||
"stage": 1,
|
||||
"offload_optimizer": {
|
||||
"device": "none",
|
||||
"pin_memory": true
|
||||
},
|
||||
"allgather_partitions": true,
|
||||
"allgather_bucket_size": 2e8,
|
||||
"overlap_comm": false,
|
||||
"reduce_scatter": true,
|
||||
"reduce_bucket_size": 2e8,
|
||||
"contiguous_gradients": true
|
||||
},
|
||||
|
||||
"gradient_accumulation_steps": "auto",
|
||||
"gradient_clipping": "auto",
|
||||
"steps_per_print": 2000,
|
||||
"train_batch_size": "auto",
|
||||
"train_micro_batch_size_per_gpu": "auto",
|
||||
"wall_clock_breakdown": false,
|
||||
"elasticity": {
|
||||
"ignore_non_elastic_batch_info": true,
|
||||
"enabled": true,
|
||||
"max_train_batch_size": 8,
|
||||
"micro_batch_sizes": [
|
||||
4,
|
||||
2
|
||||
],
|
||||
"min_gpus": 1,
|
||||
"max_gpus": 4,
|
||||
"min_time": 20,
|
||||
"version": 0.1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
如果用户需要自定义,可以在启动命令中deepspeed_config_or_type指定自定义的zero1.json的存放路径,其中弹性相关的配置为:
|
||||
```json
|
||||
...
|
||||
|
||||
"elasticity": {
|
||||
"ignore_non_elastic_batch_info": true,
|
||||
"enabled": true,
|
||||
"max_train_batch_size": 8,
|
||||
"micro_batch_sizes": [
|
||||
4,
|
||||
2
|
||||
],
|
||||
"min_gpus": 1,
|
||||
"max_gpus": 4,
|
||||
"min_time": 20,
|
||||
"version": 0.1
|
||||
}
|
||||
```
|
||||
|
||||
- ignore_non_elastic_batch_info:代表在elasticity里的配置会忽略外层的batch_size相关的配置,训练过程中会根据实际的训练进程个数实时修改batch_size等相关的参数
|
||||
计算原则为:
|
||||
global-training-batch-size = micro-batch-size * gradient-accumulation-steps * world-size
|
||||
- max_train_batch_size:最大batch_size数
|
||||
- micro_batch_sizes:elasticity下允许的每卡micro-batch size列表,相当于train_micro_batch_size_per_gpu的候选值
|
||||
- min_gpus:最小gpu数目
|
||||
- max_gpus:最大gpu数目
|
||||
更详细的内容见:[Deepspeed](https://www.deepspeed.ai/docs/config-json/#elastic-training-config-v01-and-v02)
|
||||
|
||||
|
||||
## 启动训练
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: elastic.iml.github.io/v1alpha1
|
||||
kind: ElasticJob
|
||||
metadata:
|
||||
name: deepspeed-elastic-swift
|
||||
namespace: dlrover
|
||||
spec:
|
||||
distributionStrategy: AllreduceStrategy
|
||||
optimizeMode: single-job
|
||||
replicaSpecs:
|
||||
worker:
|
||||
replicas: 1 #【这里需要与启动命令中的--nnodes NNODES的最大值一致】
|
||||
template:
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: main
|
||||
image: #【训练镜像,需要安装deepspeed,dlrover 和swift 】
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /bin/bash
|
||||
- -c
|
||||
- sh start.sh # 启动脚本
|
||||
resources:
|
||||
limits:
|
||||
cpu: '8'
|
||||
memory: 16Gi
|
||||
nvidia.com/gpu: '1'
|
||||
volumeMounts:
|
||||
- mountPath: /model
|
||||
name: volume-model
|
||||
- mountPath: /dev/shm
|
||||
name: volume-shm
|
||||
restartPolicy: Never
|
||||
volumes:
|
||||
- hostPath:
|
||||
path: /model
|
||||
type: Directory
|
||||
name: volume-model
|
||||
- emptyDir:
|
||||
medium: Memory
|
||||
sizeLimit: 200Gi
|
||||
name: volume-shm
|
||||
|
||||
```
|
||||
@@ -0,0 +1,211 @@
|
||||
# Embedding训练
|
||||
|
||||
SWIFT已经支持Embedding模型的训练,包括纯文本和多模态两个类型。目前已经支持的模型有:
|
||||
|
||||
1. modernbert embedding模型
|
||||
- [ModelScope](https://modelscope.cn/models/iic/gte-modernbert-base) [Hugging Face](https://huggingface.co/Alibaba-NLP/gte-modernbert-base)
|
||||
2. gte embedding模型
|
||||
- 1.5B: [ModelScope](https://www.modelscope.cn/models/iic/gte_Qwen2-1.5B-instruct) [Hugging Face](https://huggingface.co/Alibaba-NLP/gte-Qwen2-1.5B-instruct)
|
||||
- 7B: [ModelScope](https://www.modelscope.cn/models/iic/gte_Qwen2-7B-instruct) [Hugging Face](https://huggingface.co/Alibaba-NLP/gte-Qwen2-7B-instruct)
|
||||
3. gme embedding模型
|
||||
- 2B: [ModelScope](https://www.modelscope.cn/models/iic/gme-Qwen2-VL-2B-Instruct) [Hugging Face](https://huggingface.co/Alibaba-NLP/gme-Qwen2-VL-2B-Instruct)
|
||||
- 7B: [ModelScope](https://www.modelscope.cn/models/iic/gme-Qwen2-VL-7B-Instruct) [Hugging Face](https://huggingface.co/Alibaba-NLP/gme-Qwen2-VL-7B-Instruct)
|
||||
4. qwen3-embedding模型
|
||||
- 0.6B: [ModelScope](https://www.modelscope.cn/models/Qwen/Qwen3-Embedding-0.6B) [Hugging Face](https://huggingface.co/Qwen/Qwen3-Embedding-0.6B)
|
||||
- 4B: [ModelScope](https://www.modelscope.cn/models/Qwen/Qwen3-Embedding-4B) [Hugging Face](https://huggingface.co/Qwen/Qwen3-Embedding-4B)
|
||||
- 8B: [ModelScope](https://www.modelscope.cn/models/Qwen/Qwen3-Embedding-8B) [Hugging Face](https://huggingface.co/Qwen/Qwen3-Embedding-8B)
|
||||
5. qwen3-vl-embedding模型
|
||||
- 2B: [ModelScope](https://www.modelscope.cn/models/Qwen/Qwen3-VL-Embedding-2B) [Hugging Face](https://huggingface.co/Qwen/Qwen3-VL-Embedding-2B)
|
||||
- 8B: [ModelScope](https://www.modelscope.cn/models/Qwen/Qwen3-VL-Embedding-8B) [Hugging Face](https://huggingface.co/Qwen/Qwen3-VL-Embedding-8B)
|
||||
|
||||
|
||||
开发者可以自行集成自己的模型,模型forward输出值需要满足:
|
||||
|
||||
```text
|
||||
{"last_hidden_state": some-embedding-tensor}
|
||||
```
|
||||
|
||||
返回值是一个json,具有`last_hidden_state` key,value是embedding tensor即可,输入部分可以使用我们已经支持的template。用户也可以通过指定
|
||||
|
||||
```shell
|
||||
--task_type embedding
|
||||
```
|
||||
参数来将任意一个其他模型转换为embedding模型进行训练。
|
||||
|
||||
需要注意的是,SWIFT目前支持的embedding模型均为符合纯文本或多模态LLM,目前并不支持CLIP类型的模型训练。
|
||||
|
||||
此外,SWIFT支持的所有embedding模型在模型forward最后都增加了normalize,如自行增加新模型请注意增加normalize层。
|
||||
|
||||
## loss
|
||||
|
||||
目前SWIFT支持的Embedding模型可以使用的loss有:
|
||||
|
||||
- cosine_similarity: cosine相似度loss,计算两个embedding的相似度,并根据label的值拟合,实际为MSE loss
|
||||
- contrastive: 可调margin的对比学习loss,label仅支持0和1两个值
|
||||
- online_contrastive: 考虑hard negative和hard positive部分的contrastive loss,label仅支持0和1两个值
|
||||
- infonce: 在同一个batch中不同row两两计算cosine相似度,并使row内部相似度最大,不同row相似度最小,不需要label
|
||||
|
||||
loss的源代码可以在[这里](https://github.com/modelscope/ms-swift/blob/main/swift/loss/mapping.py)找到。
|
||||
|
||||
## 数据集格式
|
||||
|
||||
> 注:
|
||||
> 1. `<image>`标签可以出现在`messages`/`positive_messages`/`negative_messages`的任意位置;它们各自拥有独立的`images`/`positive_images`/`negative_images`字段用于提供图片路径或URL。
|
||||
> 2. 不再需要跨字段的“对应顺序”。对齐规则为:`images`的长度等于`messages`中`<image>`标签的数量;`positive_images`与`negative_images`均为“list of list”,其外层长度分别等于`positive_messages`与`negative_messages`的长度;并且外层每一项的内层列表长度等于该条消息序列中`<image>`标签的数量。
|
||||
> 3. `messages`代表anchor样本(anchor sample);`positive_messages`/`negative_messages`为“list of messages”(因此多一层`[]`);相应地,`positive_images`/`negative_images`也多一层`[]`并与之逐项对齐。
|
||||
> 4. 也支持`<video>`, `<audio>`标签;可按相同规则分别通过`videos`/`positive_videos`/`negative_videos`与`audios`/`positive_audios`/`negative_audios`提供对应模态数据。
|
||||
> 5. 当前约束:`positive_messages`的外层长度必须为1(即仅提供一个positive样本);对应地,`positive_images`的外层长度也必须为1。
|
||||
|
||||
### cosine_similarity loss对应的格式
|
||||
|
||||
```json lines
|
||||
# LLM
|
||||
{"messages": [{"role": "user", "content": "sentence1"}], "positive_messages": [[{"role": "user", "content": "sentence2"}]], "label": 0.8}
|
||||
# MLLM
|
||||
{"messages": [{"role": "user", "content": "<image>"}], "images": ["/some/images1.jpg"],"positive_messages": [[{"role": "user", "content": "<image>sentence"}]], "positive_images": [["/some/images2.jpg"]], "label": 0.7}
|
||||
{"messages": [{"role": "user", "content": "sentence1"}], "positive_messages": [[{"role": "user", "content": "<image>sentence2"}]], "positive_images": [["/some/images.jpg"]], "label": 0.7}
|
||||
```
|
||||
|
||||
|
||||
### contrastive/online_contrastive loss对应的格式
|
||||
|
||||
```json lines
|
||||
# LLM
|
||||
{"messages": [{"role": "user", "content": "sentence1"}], "positive_messages": [[{"role": "user", "content": "sentence2"}]], "label": 1}
|
||||
# MLLM
|
||||
{"messages": [{"role": "user", "content": "<image>"}], "images": ["/some/images1.jpg"], "positive_messages": [[{"role": "user", "content": "<image>sentence"}]], "positive_images": [["/some/images2.jpg"]], "label": 1}
|
||||
{"messages": [{"role": "user", "content": "sentence1"}], "positive_messages": [[{"role": "user", "content": "<image>sentence2"}]], "positive_images": [["/some/images.jpg"]], "label": 0}
|
||||
```
|
||||
|
||||
评测的指标分别是两个embedding的欧式距离、点积等的pearson系数以及spearman系数,共八个指标。
|
||||
|
||||
### infonce 格式
|
||||
|
||||
```json lines
|
||||
# LLM
|
||||
{"messages": [{"role": "user", "content": "sentence1"}], "positive_messages": [[{"role": "user", "content": "sentence2"}]]}
|
||||
# MLLM
|
||||
{"messages": [{"role": "user", "content": "<image>"}], "images": ["/some/images.jpg"], "positive_messages": [[{"role": "user", "content": "sentence"}]]}
|
||||
{"messages": [{"role": "user", "content": "<image>sentence1"}], "images": ["/some/images.jpg"], "positive_messages": [[{"role": "user", "content": "<image>sentence2"}]], "positive_images": [["/some/positive_images.jpg"]], "negative_messages": [[{"role": "user", "content": "<image><image>sentence3"}], [{"role": "user", "content": "<image>sentence4"}]], "negative_images": [["/some/negative_images1.jpg", "/some/negative_images2.jpg"], ["/some/negative_images3.jpg"]]}
|
||||
```
|
||||
|
||||
infonce loss支持几个环境变量:
|
||||
1. `INFONCE_TEMPERATURE`: temperature参数,不设置的话默认值是0.1
|
||||
2. `INFONCE_USE_BATCH`: 使用sample内部的`negative_messages`(hard negative样例)还是使用一个batch内其他样本作为in-batch negatives;默认为True,表示使用batch内部的样本作为负例
|
||||
3. `INFONCE_HARD_NEGATIVES`: hard negatives的数量;如果不设置会使用数据中提供的所有`negative_messages`。由于长度未必一致,因此会采用for循环计算loss(计算会慢)。若设置为某个数值,则不足会随机采样补齐,超长会选用前`INFONCE_HARD_NEGATIVES`个
|
||||
4. `INFONCE_MASK_FAKE_NEGATIVE`: mask掉假negative。默认为False,开启时会判断 `positive_similarity + INFONCE_FAKE_NEG_MARGIN`,比该阈值大的样本相似度会被设置为 `-inf`,以防止正样本泄露问题
|
||||
5. `INFONCE_FAKE_NEG_MARGIN`:假负样本屏蔽的边际,默认 `0.1`。
|
||||
6. `INFONCE_INCLUDE_QQ`:是否在分母中加入 q–q 分量(query 间相似度)作为负例,默认 `False`。
|
||||
7. `INFONCE_INCLUDE_DD`:是否在分母中加入 d–d 分量(正样本文档与 batch 内所有文档的相似度)作为负例,默认 `False`。
|
||||
|
||||
> 也可以在数据集中将hard negatives数量设置为数量相等,这样即使不设置也不会使用for循环方式,加快计算速度
|
||||
> `negative_messages`也可以不提供。在这种情况下,保持`INFONCE_USE_BATCH=True`,会使用一个batch内部的其他样本作为负例
|
||||
|
||||
infonce loss的评测会有下面几个指标:
|
||||
- mean_neg 所有hard_negative的平均值
|
||||
- mean_pos 所有positive的平均值
|
||||
- margin positive-max_hard_negative的平均值
|
||||
|
||||
## 训练
|
||||
|
||||
SWIFT提供的脚手架训练脚本:
|
||||
|
||||
- [Qwen3-Embedding/Qwen3-VL-Embedding模型](https://github.com/modelscope/ms-swift/blob/main/examples/train/embedding/qwen3)
|
||||
- [GME模型](https://github.com/modelscope/ms-swift/blob/main/examples/train/embedding/train_gme.sh)
|
||||
|
||||
## 推理
|
||||
|
||||
SWIFT已经支持GME、GTE、Qwen3-Embedding模型的部署,请查看[这里](https://github.com/modelscope/ms-swift/blob/main/examples/deploy/embedding/client.py)。
|
||||
- 推理脚本参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_embedding.py)。
|
||||
|
||||
也可以使用原模型的代码进行推理:
|
||||
|
||||
https://www.modelscope.cn/models/iic/gte_Qwen2-7B-instruct
|
||||
|
||||
https://www.modelscope.cn/models/iic/gme-Qwen2-VL-7B-Instruct
|
||||
|
||||
如果使用了其他模型从0训练embedding(例如,原版`qwen2-vl`模型+`--task_type embedding`),也可以使用gme的推理代码,但请注意:
|
||||
|
||||
https://www.modelscope.cn/models/iic/gme-Qwen2-VL-7B-Instruct/file/view/master/gme_inference.py?status=1#L111
|
||||
|
||||
这里的模板请修改为模型自身的template,以免最后的embedding对不上。需要额外注意的是,gme模型的template和`qwen2-vl`或`qwen2.5-vl`系列的chatml template并不相同,其推理代码最后的结束字符是`<|endoftext|>`而非`<|im_end|>`.
|
||||
|
||||
## 高级功能
|
||||
|
||||
- Qwen3-Embedding 自定义 Instruction:
|
||||
- 默认无 Instruction,输入模板为:`{Query}<|endoftext|>`。
|
||||
- 通过在 system message 中添加 Instruction,可将输入改为:`{Instruction} {Query}<|endoftext|>`。
|
||||
- 示例:
|
||||
|
||||
```json lines
|
||||
{"messages": [
|
||||
{"role": "system", "content": "请用中文回答,并输出简洁要点"},
|
||||
{"role": "user", "content": "介绍一下Qwen3-Embedding"}
|
||||
]}
|
||||
```
|
||||
|
||||
> 说明:Qwen3-Embedding 模板会将 system 内容前置拼接到首条 user 消息中,并使用 `<|endoftext|>` 作为结束标记。
|
||||
|
||||
### 转换前后示例
|
||||
|
||||
- 不加 Instruction:
|
||||
|
||||
输入数据(messages):
|
||||
|
||||
```json lines
|
||||
{"messages": [
|
||||
{"role": "user", "content": "北京明天天气如何?"}
|
||||
]}
|
||||
```
|
||||
|
||||
模板转换后(送入模型的实际文本):
|
||||
|
||||
```text
|
||||
北京明天天气如何?<|endoftext|>
|
||||
```
|
||||
|
||||
- 加 Instruction:
|
||||
|
||||
输入数据(messages,包含system):
|
||||
|
||||
```json lines
|
||||
{"messages": [
|
||||
{"role": "system", "content": "请使用中文、精炼输出要点"},
|
||||
{"role": "user", "content": "北京明天天气如何?"}
|
||||
]}
|
||||
```
|
||||
|
||||
模板转换后(送入模型的实际文本):
|
||||
|
||||
```text
|
||||
请使用中文、精炼输出要点 北京明天天气如何?<|endoftext|>
|
||||
```
|
||||
|
||||
- positive/negative 同理:
|
||||
|
||||
若在某个 positive/negative 的消息序列中提供 system,则会将该 system 内容前置到该序列首条 user 内容之前;未提供 system 则不前置。
|
||||
|
||||
输入数据(包含一个 positive 带 system,和一个 negative 无 system):
|
||||
|
||||
```json lines
|
||||
{
|
||||
"messages": [
|
||||
{"role": "user", "content": "Anchor"}
|
||||
],
|
||||
"positive_messages": [[
|
||||
{"role": "system", "content": "指令"},
|
||||
{"role": "user", "content": "Positive"}
|
||||
]],
|
||||
"negative_messages": [[
|
||||
{"role": "user", "content": "Negative"}
|
||||
]]
|
||||
}
|
||||
```
|
||||
|
||||
模板转换后(送入模型的实际文本):
|
||||
|
||||
```text
|
||||
Anchor<|endoftext|>
|
||||
指令 Positive<|endoftext|>
|
||||
Negative<|endoftext|>
|
||||
```
|
||||
@@ -0,0 +1,145 @@
|
||||
# GRPO代码训练
|
||||
本文档介绍如何使用GRPO对模型进行代码训练
|
||||
|
||||
模型:[Qwen/Qwen2.5-7B-Instruct](https://www.modelscope.cn/models/Qwen/Qwen2.5-VL-7B-Instruct)
|
||||
|
||||
数据集:[open-r1/verifiable-coding-problems-python-10k](https://www.modelscope.cn/datasets/open-r1/verifiable-coding-problems-python-10k/dataPeview)
|
||||
|
||||
数据集样例
|
||||
```json
|
||||
{
|
||||
"problem": "Solve the following coding problem using the programming language python: Polycarp has $n$ different binary words. A word called binary if it contains only characters '0' and '1'. For example, these words are binary: \"0001\", \"11\", \"0\" and \"0011100\". Polycarp wants to offer his set of $n$ binary words to play a game \"words\". In this game, players name words and each next word (starting from the second) must start with the last character of the previous word. The first word can be any. For example, these sequence of words can be named during the game: \"0101\", \"1\", \"10\", \"00\", \"00001\". Word reversal is the operation of reversing the order of the characters. For example, the word \"0111\" after the reversal becomes \"1110\", the word \"11010\" after the reversal becomes \"01011\". Probably, Polycarp has such a set of words that there is no way to put them in the order correspondent to the game rules. In this situation, he wants to reverse some words from his set so that: the final set of $n$ words still contains different words (i.e. all words are unique); there is a way to put all words of the final set of words in the order so that the final sequence of $n$ words is consistent with the game rules. Polycarp wants to reverse minimal number of words. Please, help him. -----Input----- The first line of the input contains one integer $t$ ($1 \\le t \\le 10^4$) — the number of test cases in the input. Then $t$ test cases follow. The first line of a test case contains one integer $n$ ($1 \\le n \\le 2\\cdot10^5$) — the number of words in the Polycarp's set. Next $n$ lines contain these words. All of $n$ words aren't empty and contains only characters '0' and '1'. The sum of word lengths doesn't exceed $4\\cdot10^6$. All words are different. Guaranteed, that the sum of $n$ for all test cases in the input doesn't exceed $2\\cdot10^5$. Also, guaranteed that the sum of word lengths for all test cases in the input doesn't exceed $4\\cdot10^6$. -----Output----- Print answer for all of $t$ test cases in the order they appear. If there is no answer for the test case, print -1. Otherwise, the first line of the output should contain $k$ ($0 \\le k \\le n$) — the minimal number of words in the set which should be reversed. The second line of the output should contain $k$ distinct integers — the indexes of the words in the set which should be reversed. Words are numerated from $1$ to $n$ in the order they appear. If $k=0$ you can skip this line (or you can print an empty line). If there are many answers you can print any of them. -----Example----- Input 4 4 0001 1000 0011 0111 3 010 101 0 2 00000 00001 4 01 001 0001 00001 Output 1 3 -1 0 2 1 2 The input will be stdin and you should print your solution to stdout Now solve the problem and return the code.",
|
||||
"verification_info": {
|
||||
"language": "python",
|
||||
"test_cases": [
|
||||
{
|
||||
"input": "4\n4\n0001\n1000\n0011\n0111\n3\n010\n101\n0\n2\n00000\n00001\n4\n01\n001\n0001\n00001\n",
|
||||
"output": "1\n3 \n-1\n0\n\n2\n1 2 \n",
|
||||
"type": "stdin_stdout"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`verification_info` 提供了程序语言以及测试用例,其中包含输入和预期的输出。
|
||||
|
||||
|
||||
## 奖励函数
|
||||
使用`code_reward`和`code_format`奖励进行训练,实现细节见[代码](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/plugin.py)
|
||||
|
||||
- `code_reward`通过[e2b](https://e2b.dev/)或[judge0](https://judge0.com/)执行生成的代码,根据数据集中的测试用例对代码进行验证给出奖励值。
|
||||
- `code_format`要求模型输出包含代码块的格式化回答。
|
||||
|
||||
注:当前通过e2b执行代码仅支持python语言,如需执行其他语言,可以使用judge0执行([judge0支持语言列表](https://github.com/judge0/judge0?tab=readme-ov-file#supported-languages))。
|
||||
|
||||
## 训练脚本
|
||||
### e2b
|
||||
- 在[e2b](https://e2b.dev/dashboard)注册获取E2B_API_KEY,并设置为环境变量。
|
||||
- `--reward_funcs`添加`external_code_reward`作为奖励函数。
|
||||
- `--external_plugins`设置为plugin.py的路径。
|
||||
首先拉起 vLLM server
|
||||
```bash
|
||||
CUDA_VISIBLE_DEVICES=7 \
|
||||
swift rollout \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--vllm_enable_lora true \
|
||||
--vllm_max_lora_rank 16
|
||||
```
|
||||
|
||||
```bash
|
||||
E2B_API_KEY=xxx \
|
||||
WANDB_API_KEY=xxx \
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6 \
|
||||
NPROC_PER_NODE=7 \
|
||||
swift rlhf \
|
||||
--rlhf_type grpo \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--external_plugins examples/train/grpo/plugin/plugin.py \
|
||||
--reward_funcs external_code_reward external_code_format \
|
||||
--reward_weights 1.0 0.1 \
|
||||
--vllm_mode server \
|
||||
--use_vllm true \
|
||||
--vllm_server_host 127.0.0.1 \
|
||||
--vllm_server_port 8000 \
|
||||
--tuner_type lora \
|
||||
--lora_rank 16 \
|
||||
--lora_alpha 32 \
|
||||
--torch_dtype bfloat16 \
|
||||
--dataset 'open-r1/verifiable-coding-problems-python-10k' \
|
||||
--load_from_cache_file true \
|
||||
--max_completion_length 2048 \
|
||||
--num_train_epochs 1 \
|
||||
--per_device_train_batch_size 2 \
|
||||
--per_device_eval_batch_size 2 \
|
||||
--learning_rate 1e-6 \
|
||||
--gradient_accumulation_steps 1 \
|
||||
--eval_steps 200 \
|
||||
--save_steps 200 \
|
||||
--save_total_limit 2 \
|
||||
--logging_steps 5 \
|
||||
--max_length 4096 \
|
||||
--output_dir output \
|
||||
--warmup_ratio 0.05 \
|
||||
--dataloader_num_workers 4 \
|
||||
--dataset_num_proc 4 \
|
||||
--num_generations 14 \
|
||||
--temperature 0.9 \
|
||||
--system 'examples/train/grpo/prompt.txt' \
|
||||
--deepspeed zero2 \
|
||||
--log_completions true \
|
||||
--report_to wandb
|
||||
```
|
||||
|
||||
### judge0
|
||||
- 设置环境变量:
|
||||
- (必需)JUDGE0_ENDPOINT: judge0访问地址。
|
||||
- (可选)JUDGE0_X_AUTH_TOKEN: judge0访问Token。
|
||||
- `--reward_funcs`添加`external_code_reward_by_judge0`作为奖励函数。
|
||||
- `--external_plugins`设置为plugin.py的路径。
|
||||
|
||||
```bash
|
||||
JUDGE0_ENDPOINT=xxx \
|
||||
JUDGE0_X_AUTH_TOKEN=xxx \
|
||||
WANDB_API_KEY=xxx \
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6 \
|
||||
NPROC_PER_NODE=7 \
|
||||
swift rlhf \
|
||||
--rlhf_type grpo \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--external_plugins examples/train/grpo/plugin/plugin.py \
|
||||
--reward_funcs external_code_reward_by_judge0 external_code_format \
|
||||
--reward_weights 1.0 0.1 \
|
||||
--vllm_mode server \
|
||||
--use_vllm true \
|
||||
--vllm_server_host 127.0.0.1 \
|
||||
--vllm_server_port 8000 \
|
||||
--tuner_type lora \
|
||||
--torch_dtype bfloat16 \
|
||||
--dataset 'open-r1/verifiable-coding-problems-python-10k' \
|
||||
--load_from_cache_file true \
|
||||
--max_completion_length 2048 \
|
||||
--num_train_epochs 1 \
|
||||
--per_device_train_batch_size 2 \
|
||||
--per_device_eval_batch_size 2 \
|
||||
--learning_rate 1e-6 \
|
||||
--gradient_accumulation_steps 1 \
|
||||
--eval_steps 200 \
|
||||
--save_steps 200 \
|
||||
--save_total_limit 2 \
|
||||
--logging_steps 5 \
|
||||
--max_length 4096 \
|
||||
--output_dir output \
|
||||
--warmup_ratio 0.05 \
|
||||
--dataloader_num_workers 4 \
|
||||
--dataset_num_proc 4 \
|
||||
--num_generations 14 \
|
||||
--temperature 0.9 \
|
||||
--system 'examples/train/grpo/prompt.txt' \
|
||||
--deepspeed zero2 \
|
||||
--log_completions true \
|
||||
--report_to wandb
|
||||
```
|
||||
|
||||
训练奖励曲线图
|
||||

|
||||
@@ -0,0 +1,314 @@
|
||||
# 多模态GRPO完整实验流程
|
||||
本文介绍如何使用SWIFT GRPO进行多模态模型和任务的训练。目标是对多个多模态任务进行训练,提升任务精度,任务定义和训练参数等参考了 [R1-V](https://github.com/Deep-Agent/R1-V.git) 和 [open-r1-multimodal](https://github.com/EvolvingLMMs-Lab/open-r1-multimodal.git)
|
||||
|
||||
|
||||
|
||||
## ClevrCount 任务
|
||||
### 任务与数据集定义
|
||||
本任务从clevr_cogen_a_train数据集出发,模型的目标是输出图像中包含的物体数量,因此,我们定义数据集如下:
|
||||
|
||||
```python
|
||||
class ClevrPreprocessor(ResponsePreprocessor):
|
||||
|
||||
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
|
||||
query = row.get('query', '')
|
||||
query = f"""{query} Output the thinking process in <think> </think> and
|
||||
final answer (number) in <answer> </answer> tags."""
|
||||
row.update({'query': query})
|
||||
return super().preprocess(row)
|
||||
|
||||
|
||||
register_dataset(
|
||||
DatasetMeta(
|
||||
ms_dataset_id='AI-ModelScope/clevr_cogen_a_train',
|
||||
subsets=[
|
||||
SubsetDataset(
|
||||
name='default',
|
||||
subset='default',
|
||||
split=['train'],
|
||||
),
|
||||
],
|
||||
preprocess_func=ClevrPreprocessor(),
|
||||
tags=['qa', 'math']))
|
||||
|
||||
```
|
||||
这里重新定义dataset preprocessor的目的是修改query。数据集示例样本如下,包含messages,images和solution字段,solution会送入后续的奖励函数中,而messages和images则会作为模型输入。
|
||||
- 注意:`{'role': 'assistant', 'content': '<answer> 3 </answer>'}`将会在GRPOTrainer中被移除,可以忽略。'solution'字段将会透传入ORM中。在自定义数据集时,'images'字段组织成`["image_path1", "image_path2"]`即可。
|
||||
|
||||
```json
|
||||
{
|
||||
"images": ["image_path1", "image_path2"],
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "How many items are there in the image? Output the thinking process in <think> </think> and \n final answer (number) in <answer> </answer> tags."
|
||||
}
|
||||
],
|
||||
"solution": "<answer> 3 </answer>"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 奖励函数定义:
|
||||
本任务使用的奖励函数有两个,一个是 Deepseek-R1 中提到的格式奖励函数,另一是 ClevrCount 的准确性奖励函数。前者已经在swift中内置,通过 `--reward_funcs format` 可以直接使用,而后者需要我们自己定义,在这里我们使用 external_plugin 的方式定义准确性奖励函数,将代码放在`swift/examples/train/grpo/plugin/plugin.py`中。
|
||||
|
||||
在这里,奖励函数的输入包括completions和solution两个字段,分别表示模型生成的文本和真值。每个都是list,支持多个completion同时计算。注意,在这里,solution字段是数据集中定义的字段透传而来,如果有任务上的变动,可以分别对数据集和奖励函数做对应的改变即可。
|
||||
```python
|
||||
|
||||
class MultiModalAccuracyORM(ORM):
|
||||
|
||||
def __call__(self, completions, solution, **kwargs) -> List[float]:
|
||||
"""
|
||||
Reward function that checks if the completion is correct.
|
||||
Args:
|
||||
completions (list[str]): Generated outputs
|
||||
solution (list[str]): Ground Truths.
|
||||
|
||||
Returns:
|
||||
list[float]: Reward scores
|
||||
"""
|
||||
rewards = []
|
||||
from math_verify import parse, verify
|
||||
for content, sol in zip(completions, solution):
|
||||
reward = 0.0
|
||||
# Try symbolic verification first
|
||||
try:
|
||||
answer = parse(content)
|
||||
if float(verify(answer, parse(sol))) > 0:
|
||||
reward = 1.0
|
||||
except Exception:
|
||||
pass # Continue to next verification method if this fails
|
||||
|
||||
# If symbolic verification failed, try string matching
|
||||
if reward == 0.0:
|
||||
try:
|
||||
# Extract answer from solution if it has think/answer tags
|
||||
sol_match = re.search(r'<answer>(.*?)</answer>', sol)
|
||||
ground_truth = sol_match.group(1).strip() if sol_match else sol.strip()
|
||||
|
||||
# Extract answer from content if it has think/answer tags
|
||||
content_match = re.search(r'<answer>(.*?)</answer>', content)
|
||||
student_answer = content_match.group(1).strip() if content_match else content.strip()
|
||||
|
||||
# Compare the extracted answers
|
||||
if student_answer == ground_truth:
|
||||
reward = 1.0
|
||||
except Exception:
|
||||
pass # Keep reward as 0.0 if both methods fail
|
||||
rewards.append(reward)
|
||||
return rewards
|
||||
orms['external_r1v_acc'] = MultiModalAccuracyORM
|
||||
```
|
||||
|
||||
### GRPO训练实验记录
|
||||
#### 训练参数:
|
||||
我们选取 Qwen2.5-VL-3B-Instruct 作为基础模型进行训练,选取 Instruct 而不是基模的主要原因是可以更快地获取 format reward。我们在八卡 GPU 上进行实验。如果遇到vllm部署qwen2.5-vl报错,可以参考[issue](https://github.com/vllm-project/vllm/issues/13285)
|
||||
|
||||
由于任务简单,我们设置max_completion_length为1024,奖励函数选择external_r1v_acc和format,学习率和beta分别设置为1e-6和0.001。其他设置如下所示,batch_size和num_generations的设置原则可以参考[GRPO完整流程](./GRPO.md)。
|
||||
首先拉起 external vLLM server
|
||||
```bash
|
||||
CUDA_VISIBLE_DEVICES=6,7 \
|
||||
swift rollout \
|
||||
--model Qwen/Qwen2.5-VL-3B-Instruct \
|
||||
--vllm_data_parallel_size 2
|
||||
```
|
||||
|
||||
```shell
|
||||
WANDB_API_KEY=your_wandb_api_key \
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5 \
|
||||
NPROC_PER_NODE=6 \
|
||||
swift rlhf \
|
||||
--rlhf_type grpo \
|
||||
--model Qwen/Qwen2.5-VL-3B-Instruct \
|
||||
--external_plugins examples/train/grpo/plugin/plugin.py \
|
||||
--reward_funcs external_r1v_acc format \
|
||||
--use_vllm true \
|
||||
--vllm_mode server \
|
||||
--vllm_server_host 127.0.0.1 \
|
||||
--vllm_server_port 8000 \
|
||||
--tuner_type full \
|
||||
--torch_dtype bfloat16 \
|
||||
--dataset 'AI-ModelScope/clevr_cogen_a_train' \
|
||||
--load_from_cache_file true \
|
||||
--max_completion_length 1024 \
|
||||
--num_train_epochs 1 \
|
||||
--per_device_train_batch_size 8 \
|
||||
--per_device_eval_batch_size 8 \
|
||||
--learning_rate 1e-6 \
|
||||
--gradient_accumulation_steps 2 \
|
||||
--save_strategy 'steps' \
|
||||
--eval_strategy 'steps' \
|
||||
--eval_steps 1000 \
|
||||
--save_steps 1000 \
|
||||
--save_total_limit 10 \
|
||||
--logging_steps 1 \
|
||||
--output_dir output/GRPO_CLEVR_COUNTDOWN \
|
||||
--warmup_ratio 0.01 \
|
||||
--dataloader_num_workers 4 \
|
||||
--num_generations 24 \
|
||||
--temperature 1.0 \
|
||||
--system 'examples/train/grpo/prompt.txt' \
|
||||
--deepspeed zero3 \
|
||||
--log_completions true \
|
||||
--report_to wandb \
|
||||
--num_iterations 1 \
|
||||
--async_generate false \
|
||||
--beta 0.001 \
|
||||
```
|
||||
#### 实验现象
|
||||

|
||||
由于数据集和任务比较简单,模型训练了500个epoch已经基本收敛,有以下观察。
|
||||
1. 自定义的ClevrORM在不断增加,证明模型学习到了如何完成这一任务,最终任务成功率从初始的0.4攀升到1左右。
|
||||
2. 由于是Instruct模型,Format Reward基本稳定在1,这可能也与数据集所有样本都是一样的query有一定关系。
|
||||
3. reward_std 稳定到0.1以下。
|
||||
4. completion length最终稳定在60-80,模型学习到的固定输出范式是逐个物体列举数数。
|
||||
|
||||
step 1:
|
||||
```
|
||||
"<think> The image shows 5 distinct items: a cyan metallic cylinder, a red metallic cylinder, a small purple metallic cylinder, a large gray metallic sphere, and a small yellow metallic cube. To count the number of items, I add the counts of each type of object: 1 cyan cylinder, 1 red cylinder, 1 purple cylinder, 1 gray sphere, and 1 yellow cube. Therefore, the total number of items is 5. </think>\n<answer>5</answer>"
|
||||
"Let's count the number of items in the image:\n1. A large cyan metallic cylinder.\n2. A small purple metallic cylinder.\n3. A large red metallic cylinder.\n4. A large gray metallic sphere.\n5. A small yellow metallic cube.\n\n<answer>5</answer>"
|
||||
"<think> The image shows four distinct objects: a large cyan metallic cylinder, a smaller purple metallic cylinder, a large red metallic cylinder, and a small yellow metallic cube. Counting each of these items individually, there are five items in total. </think>\n<answer>5</answer>"
|
||||
```
|
||||
step 400:
|
||||
```
|
||||
"<think> Counting the number of items in the image:\n1. Green matte sphere\n2. Large metallic yellow sphere\n3. Small metallic brown cube\n\nThere are three distinct objects in total.\n</think>\n<answer>3</answer>"
|
||||
"<think> Counting the objects in the image:\n1. Large yellow metallic sphere\n2. Small green rubber sphere\n3. Small brown metallic cube\n\nThere are three distinct items in the image.\n</think>\n<answer>3</answer>"
|
||||
```
|
||||
总体来讲,这一任务比较简单,reward的收敛也比较典型。
|
||||
|
||||
## Geometric QA任务
|
||||
### 任务与数据集定义
|
||||
本任务为Geometric QA任务,任务描述为:给定一个几何图形,回答有关几何图形的数学问题。原始数据来自于[论文](https://arxiv.org/pdf/2312.11370),[R1-V](https://github.com/Deep-Agent/R1-V.git)对数据进行了预处理,将所有数据全部处理成了problem-solution的格式,而图像则保留在image字段中,因此,我们不需要额外定义数据集,直接使用`--dataset AI-ModelScope/GEOQA_R1V_Train_8K`即可。
|
||||
### 奖励函数
|
||||
由于也是数学题,同时,答案也处理成了最终结果,因此,我们直接使用以上定义过的`MultiModalAccuracyORM`奖励函数。
|
||||
### GRPO训练实验记录
|
||||
#### 训练参数:
|
||||
选取的模型和大部分超参数与上一个实验相似,主要有两点不同:
|
||||
1. SWIFT 已支持`--num_iteration`参数,单次rollout可以进行多次更新,这里设置为2。
|
||||
2. 在实验时发现,在数学问题中,训练可能会出现不稳定现象,导致模型训崩,具体表现为所有rewar迅速降低,loss、grad_norm和kl都迅速增大,后续也难以恢复正常状态。因此,这里设置`--max_grad_norm 0.5`,保证稳定训练,当然,这种现象的出现也有一定的随机性。
|
||||
|
||||
```shell
|
||||
WANDB_API_KEY=your_wandb_api_key \
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5 \
|
||||
MAX_PIXELS=401408 \
|
||||
NPROC_PER_NODE=6 \
|
||||
swift rlhf \
|
||||
--rlhf_type grpo \
|
||||
--model Qwen/Qwen2.5-VL-3B-Instruct \
|
||||
--external_plugins examples/train/grpo/plugin/plugin.py \
|
||||
--reward_funcs external_r1v_acc format \
|
||||
--use_vllm true \
|
||||
--vllm_mode server \
|
||||
--vllm_server_host 127.0.0.1 \
|
||||
--vllm_server_port 8000 \
|
||||
--tuner_type full \
|
||||
--torch_dtype bfloat16 \
|
||||
--dataset 'AI-ModelScope/GEOQA_R1V_Train_8K' \
|
||||
--load_from_cache_file true \
|
||||
--max_completion_length 1024 \
|
||||
--num_train_epochs 1 \
|
||||
--per_device_train_batch_size 8 \
|
||||
--per_device_eval_batch_size 8 \
|
||||
--learning_rate 1e-6 \
|
||||
--gradient_accumulation_steps 2 \
|
||||
--save_strategy 'steps' \
|
||||
--eval_strategy 'steps' \
|
||||
--eval_steps 400 \
|
||||
--save_steps 400 \
|
||||
--save_total_limit 10 \
|
||||
--logging_steps 1 \
|
||||
--output_dir output/GRPO_GEOQA \
|
||||
--warmup_ratio 0.05 \
|
||||
--dataloader_num_workers 4 \
|
||||
--num_generations 8 \
|
||||
--temperature 1.0 \
|
||||
--repetition_penalty 1.1 \
|
||||
--system 'examples/train/grpo/prompt.txt' \
|
||||
--deepspeed zero3 \
|
||||
--log_completions true \
|
||||
--report_to wandb \
|
||||
--num_iterations 2 \
|
||||
--async_generate false \
|
||||
--beta 0.001 \
|
||||
--max_grad_norm 0.5 \
|
||||
```
|
||||
|
||||
#### 实验现象
|
||||

|
||||
训练曲线如上图所示。
|
||||
1. 相比于Count任务,Geometric QA任务的难度明显更大,因此,Accuracy Reward收敛明显更慢,同时震荡幅度也明显更大。
|
||||
2. completion_length的变化趋势比较明显,最终收敛到250 token左右。
|
||||
3. reward基本收敛,标准差在一共1300个step时稳定在0.2左右。
|
||||
以下是训练后的模型completition示例:
|
||||
```
|
||||
User:
|
||||
"If AB is the tangent to circle O with the tangent point A, and BO intersects circle O at point C, and point D is located on circle O, what is the measure of angle ADC if the degree of angle ABO is 32.0?"
|
||||
Assistant:
|
||||
"<think>\nTo find the measure of angle ADC, we first need to understand the properties of the tangent and the circle. Since AB is a tangent to the circle at point A, the line segment OA (which is perpendicular to the tangent AB) is perpendicular to AB. This means that angle OAB is 90 degrees.\n\nGiven that angle ABO is 32 degrees, we can find angle BAO by subtracting angle ABO from 90 degrees:\n∠BAO = 90° - ∠ABO = 90° - 32° = 58°\n\nSince angle BAO is an angle in the minor arc AD, the angle ADC, which is an angle in the minor arc AD, is half the angle BAO. Therefore, angle ADC is:\n∠ADC = 1/2 × ∠BAO = 1/2 × 58° = 29°\n\nSo, the measure of angle ADC is 29 degrees.\n</think>\n<answer>\nThe measure of angle ADC is 29 degrees.\n</answer>"
|
||||
```
|
||||
## Multimodal Open R1 数据集实验
|
||||
### 任务与数据集定义
|
||||
本任务为参考[open-r1-multimodal](https://github.com/EvolvingLMMs-Lab/open-r1-multimodal.git)的实验,使用数据集:[lmms-lab/multimodal-open-r1-8k-verified](https://www.modelscope.cn/datasets/lmms-lab/multimodal-open-r1-8k-verified),该数据集专注于多模态的数学推理任务,数据由GPT4o基于`Math360K`和`Geo170K`数据集生成,包含推理路径和可验证答案。数据集中已包含了image, problem和solution字段,我们也不需要针对prompt进行修改,因此无需额外定义数据集。
|
||||
### 奖励函数
|
||||
我们直接使用以上定义过的`MultiModalAccuracyORM`奖励函数。
|
||||
### GRPO训练实验记录
|
||||
#### 训练参数:
|
||||
选取的模型和大部分超参数与上一个实验相似,由于训练的时候出现了OOM,我们设置`MAX_PIXELS=262144`以降低显存占用。
|
||||
```shell
|
||||
WANDB_API_KEY=your_wandb_api_key \
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5 \
|
||||
MAX_PIXELS=262144 \
|
||||
MASTER_PORT=29600 \
|
||||
NPROC_PER_NODE=6 \
|
||||
swift rlhf \
|
||||
--rlhf_type grpo \
|
||||
--model Qwen/Qwen2.5-VL-3B-Instruct \
|
||||
--external_plugins examples/train/grpo/plugin/plugin.py \
|
||||
--reward_funcs external_r1v_acc format \
|
||||
--use_vllm true \
|
||||
--vllm_mode server \
|
||||
--vllm_server_host 127.0.0.1 \
|
||||
--vllm_server_port 8000 \
|
||||
--tuner_type full \
|
||||
--torch_dtype bfloat16 \
|
||||
--dataset 'lmms-lab/multimodal-open-r1-8k-verified' \
|
||||
--load_from_cache_file true \
|
||||
--max_completion_length 1024 \
|
||||
--num_train_epochs 1 \
|
||||
--per_device_train_batch_size 8 \
|
||||
--per_device_eval_batch_size 8 \
|
||||
--learning_rate 1e-6 \
|
||||
--gradient_accumulation_steps 2 \
|
||||
--save_strategy 'steps' \
|
||||
--eval_strategy 'steps' \
|
||||
--eval_steps 400 \
|
||||
--save_steps 400 \
|
||||
--save_total_limit 10 \
|
||||
--logging_steps 1 \
|
||||
--output_dir output/GRPO_GEOQA \
|
||||
--warmup_ratio 0.05 \
|
||||
--dataloader_num_workers 4 \
|
||||
--num_generations 8 \
|
||||
--temperature 1.0 \
|
||||
--repetition_penalty 1.1 \
|
||||
--system 'examples/train/grpo/prompt.txt' \
|
||||
--deepspeed zero3 \
|
||||
--log_completions true \
|
||||
--report_to wandb \
|
||||
--num_iterations 2 \
|
||||
--async_generate false \
|
||||
--beta 0.001 \
|
||||
--max_grad_norm 0.5 \
|
||||
```
|
||||
|
||||
#### 实验现象
|
||||

|
||||
训练曲线如上图所示。
|
||||
一共8k数据,训练了1268个step,Accuracy Reward收敛到0.5左右,completion_length基本收敛到200token左右,reward_std最终在0.2左右震荡。
|
||||
以下是训练后的模型completition示例:
|
||||
```
|
||||
User:
|
||||
"Based on the map, which state falls into the lowest percentage range of lots, and what is that percentage range?"
|
||||
Assistant:
|
||||
"<think>\nThe image provided shows a color-coded map of the USA indicating different ranges of lot percentages across various states. According to the legend at the bottom, the lowest percentage range (6.0% - 6.5%) is represented by white. In the image, Alabama (AL) is shaded in white, which corresponds to the 6.0% - 6.5% category. Therefore, based on the map, the state that falls into the lowest percentage range of lots is Alabama, with the percentage range of 6.0% - 6.5%.\nTherefore, the answer is 6.0% - 6.5%.\n</think>\n<answer>Alabama</answer>"
|
||||
```
|
||||
@@ -0,0 +1,195 @@
|
||||
# GRPO完整实验流程
|
||||
|
||||
本文从较为简单的数学任务 Coundown Game 出发,从数据集定义、奖励函数定义和GRPO训练几个步骤介绍完整的GRPO训练流程。任务定义和训练参数等参考了 [mini-deepseek-r1](https://github.com/philschmid/deep-learning-pytorch-huggingface/blob/main/training/mini-deepseek-r1-aha-grpo.ipynb)。
|
||||
|
||||
## 任务与数据集定义
|
||||
|
||||
Coundown Game 的任务目标是根据给定的几个数字和加减乘除四种运算,得到目标数字,因此,我们定义数据集如下:
|
||||
```python
|
||||
class CoundownTaskPreprocessor(ResponsePreprocessor):
|
||||
|
||||
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
|
||||
numbers = row['nums']
|
||||
target = row.pop('response', None)
|
||||
query = f"""
|
||||
Using the numbers {numbers}, create an equation that equals {target}.
|
||||
You can use basic arithmetic operations (+, -, *, /) and each number can only be used once.
|
||||
Show your work in <think> </think> tags. And return the final equation and answer in <answer> </answer> tags,
|
||||
for example <answer> (1 + 2) / 3 * 4 = 4 </answer>.
|
||||
"""
|
||||
row.update({'target': target, 'query': query})
|
||||
return super().preprocess(row)
|
||||
|
||||
register_dataset(
|
||||
DatasetMeta(
|
||||
ms_dataset_id='zouxuhong/Countdown-Tasks-3to4',
|
||||
subsets=['default'],
|
||||
preprocess_func=CoundownTaskPreprocessor(),
|
||||
tags=['math']))
|
||||
```
|
||||
通过 template, 使用 numbers 和 target 完成任务定义,并给到 query 字段供模型采样使用。同时,我们需要保留 nums 和 target 两个字段,用于后续的奖励函数计算。
|
||||
|
||||
## 奖励函数定义:
|
||||
本任务使用的奖励函数有两个,一个是 Deepseek-R1 中提到的格式奖励函数,另一是 Coundown Game 的准确性奖励函数。前者已经在swift中内置,通过 `--reward_funcs format` 可以直接使用,而后者需要我们自己定义,在这里我们使用 external_plugin 的方式定义准确性奖励函数,将代码放在`swift/examples/train/grpo/plugin/plugin.py`中。
|
||||
|
||||
在这里,奖励函数的输入包括 completions、target 和 nums 三个字段,分别表示模型生成的文本、目标答案和可用的数字。每个都是list,支持多个 completion 同时计算。注意,在这里,除了 completions 之外的参数都是数据集中定义的字段透传而来,如果有任务上的变动,可以分别对数据集和奖励函数做对应的改变即可。
|
||||
```python
|
||||
class CountdownORM(ORM):
|
||||
def __call__(self, completions, target, nums, **kwargs) -> List[float]:
|
||||
"""
|
||||
Evaluates completions based on Mathematical correctness of the answer
|
||||
Args:
|
||||
completions (list[str]): Generated outputs
|
||||
target (list[str]): Expected answers
|
||||
nums (list[str]): Available numbers
|
||||
Returns:
|
||||
list[float]: Reward scores
|
||||
"""
|
||||
rewards = []
|
||||
for completion, gt, numbers in zip(completions, target, nums):
|
||||
try:
|
||||
# Check if the format is correct
|
||||
match = re.search(r"<answer>(.*?)<\/answer>", completion)
|
||||
if match is None:
|
||||
rewards.append(0.0)
|
||||
continue
|
||||
# Extract the "answer" part from the completion
|
||||
equation = match.group(1).strip()
|
||||
if '=' in equation:
|
||||
equation = equation.split('=')[0]
|
||||
# Extract all numbers from the equation
|
||||
used_numbers = [int(n) for n in re.findall(r'\d+', equation)]
|
||||
# Check if all numbers are used exactly once
|
||||
if sorted(used_numbers) != sorted(numbers):
|
||||
rewards.append(0.0)
|
||||
continue
|
||||
# Define a regex pattern that only allows numbers, operators, parentheses, and whitespace
|
||||
allowed_pattern = r'^[\d+\-*/().\s]+$'
|
||||
if not re.match(allowed_pattern, equation):
|
||||
rewards.append(0.0)
|
||||
continue
|
||||
# Evaluate the equation with restricted globals and locals
|
||||
result = eval(equation, {'__builtins__': None}, {})
|
||||
# Check if the equation is correct and matches the ground truth
|
||||
if abs(float(result) - float(gt)) < 1e-5:
|
||||
rewards.append(1.0)
|
||||
else:
|
||||
rewards.append(0.0)
|
||||
except Exception as e:
|
||||
# If evaluation fails, reward is 0
|
||||
rewards.append(0.0)
|
||||
return rewards
|
||||
orms['external_countdown'] = CountdownORM
|
||||
```
|
||||
|
||||
## GRPO训练实验记录
|
||||
首先贴上GRPO的公式:
|
||||
|
||||
$$
|
||||
{\scriptstyle
|
||||
\begin{aligned}
|
||||
\mathcal{J}_{G R P O}(\theta) & =\mathbb{E}\left[q \sim P(Q),\left\{o_i\right\}_{i=1}^G \sim \pi_{\theta_{o l d}}(O \mid q)\right] \\
|
||||
& \frac{1}{G} \sum_{i=1}^G \frac{1}{\left|o_i\right|} \sum_{t=1}^{\left|o_i\right|}\left\{\min \left[\frac{\pi_\theta\left(o_{i, t} \mid q, o_{i,<t}\right)}{\pi_{\theta_{o l d}}\left(o_{i, t} \mid q, o_{i,<t}\right)} \hat{A}_{i, t}, \operatorname{clip}\left(\frac{\pi_\theta\left(o_{i, t} \mid q, o_{i,<t}\right)}{\pi_{\theta_{o l d}}\left(o_{i, t} \mid q, o_{i,<t}\right)}, 1-\varepsilon, 1+\varepsilon\right) \hat{A}_{i, t}\right]-\beta \mathbb{D}_{K L}\left[\pi_\theta| | \pi_{r e f}\right]\right\}
|
||||
\end{aligned}
|
||||
}
|
||||
$$
|
||||
|
||||
### 训练参数:
|
||||
我们选取 Qwen2.5-3B-Instruct 作为基础模型进行训练,选取 Instruct 而不是基模的主要原因是可以更快地获取 format reward。我们在三卡 GPU 上进行实验,因此vllm的推理部署在最后一张卡上,而进程数设置为2,在剩下两张卡上进行梯度更新。
|
||||
|
||||
由于任务较为简单,我们设置 max_completion_length 和 vllm_max_model_len 为1024,如果有更复杂的任务,可以适当加大模型输出长度,但请注意,**这两个参数越大,模型训练需要的显存越多,训练速度越慢,单个step的训练时间与max_completion_length呈现线性关系**。
|
||||
|
||||
在我们的实验中,总batch_size为
|
||||
|
||||
```
|
||||
num_processes * per_device_train_batch_size * gradient_accumulation_steps = 2 * 8 * 8 = 128
|
||||
```
|
||||
|
||||
注意,这里单卡batch_size设置也与显存息息相关,请根据显存上限设置一个合适的值。 同时,还有一个公式,即总的steps数量 :$num\_steps = epochs \times len(datasets) \times num\_generations \div batch\_size $,需要根据这个来合理规划训练的学习率和warmup设置。
|
||||
|
||||
最后比较重要的设置是学习率和 beta,学习率比较好理解,而beta则是是以上公式的 $\beta$,即KL散度的梯度的权重。这两个参数设置的越大,模型收敛原则上更快,但训练往往会不稳定。经过实验,我们分别设置为 `5e-7` 和 `0.001`。在实际训练中,请根据是否出现不稳定的震荡情况适当调整这两个参数。
|
||||
|
||||
对于KL散度,社区有很多的讨论,可以参考[为什么GRPO坚持用KL散度](https://zhuanlan.zhihu.com/p/25862547100)。
|
||||
|
||||
其他参数的设置,没有做太多探讨,所以这里不进行详细说明。
|
||||
```bash
|
||||
CUDA_VISIBLE_DEVICES=2 \
|
||||
swift rollout \
|
||||
--model Qwen/Qwen2.5-3B-Instruct
|
||||
```
|
||||
|
||||
```bash
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
WANDB_API_KEY=your_wandb_key \
|
||||
NPROC_PER_NODE=2 \
|
||||
swift rlhf \
|
||||
--rlhf_type grpo \
|
||||
--model Qwen/Qwen2.5-3B-Instruct \
|
||||
--external_plugins examples/train/grpo/plugin/plugin.py \
|
||||
--reward_funcs external_countdown format \
|
||||
--use_vllm true \
|
||||
--vllm_mode server \
|
||||
--vllm_server_host 127.0.0.1 \
|
||||
--vllm_server_port 8000 \
|
||||
--tuner_type full \
|
||||
--torch_dtype bfloat16 \
|
||||
--dataset 'zouxuhong/Countdown-Tasks-3to4#50000' \
|
||||
--load_from_cache_file true \
|
||||
--max_length 2048 \
|
||||
--max_completion_length 1024 \
|
||||
--num_train_epochs 1 \
|
||||
--per_device_train_batch_size 8 \
|
||||
--per_device_eval_batch_size 8 \
|
||||
--learning_rate 5e-7 \
|
||||
--gradient_accumulation_steps 8 \
|
||||
--eval_steps 500 \
|
||||
--save_steps 100 \
|
||||
--save_total_limit 20 \
|
||||
--logging_steps 1 \
|
||||
--output_dir output/GRPO_COUNTDOWN \
|
||||
--warmup_ratio 0.01 \
|
||||
--dataloader_num_workers 4 \
|
||||
--num_generations 8 \
|
||||
--temperature 1.0 \
|
||||
--system 'You are a helpful assistant. You first thinks about the reasoning process in the mind and then provides the user with the answer.' \
|
||||
--deepspeed zero3 \
|
||||
--log_completions true \
|
||||
--report_to wandb \
|
||||
--beta 0.001 \
|
||||
--num_iterations 1
|
||||
```
|
||||
### 实验现象
|
||||

|
||||
从训练了2000 step的实验结果来看,有几点观察:
|
||||
1. 自定义的CoundownORM在不断增加,证明模型学习到了如何完成这一任务,最终任务成功率在0.5左右。
|
||||
2. Format Reward整体处在较高的水平,在0.9-1左右震荡。
|
||||
3. reward_std 最终在0.2-0.3左右,仍未完全收敛,模型仍然有继续提升reward的空间。
|
||||
4. 观察模型输出可以发现,相当一部分失败案例是由于模型简单加减乘除做不对,因此模型最终的收敛方向也变成了逐步运算上,如果换成更大的模型,可能有不同的收敛方向。
|
||||
5. completion length呈现先下降再增加,在200 step左右最低,然后逐渐上升到350左右。证明模型经历了思考方式的转变。
|
||||
6. 模型的思考方式在不断变化,最终模型发现做排列组合并逐步运算能更好地完成这一任务,最终的采样输出也大多是这一格式,以下是不同step的completion变化:
|
||||
|
||||
step 1:
|
||||
```
|
||||
"<think>\nWe need to create an equation using the given numbers [70, 5, 42, 30] exactly once and make it equal to 77. \n\nFirst, let's consider the possibility of multiplication or division to get closer to 77. Notice that 77 is close to 70 and 42. If we can use these numbers together, it might work.\n\nMultiplying 70 by 1.1 would give us approximately 77, which is very close. So, let's try to make 1.1 using the remaining numbers 5 and 30. \nIf we take 30/5 = 6, and then 5/6 = 0.8333..., this isn't useful here, so let's look at other possibilities.\n\nNext, let's consider if there's another way to achieve 77 using addition, subtraction, or another combination of our numbers. Given the numbers provided, it seems that multiplication or division will be key. \n\nWe can try subtracting 5 from 30 to get 25. Then, if we divide 42 by 25, we don't get a whole number, but we can round it to the nearest whole number, 1. Now, we need to make the equation fit 77. Let's multiply 25 by 3 and add 7 (since 77 = 25*3 + 7). However, we only have one 7 available and we need to find a way to use the remaining numbers to get 7.\n\nLet's try: \n70 - 42 = 28 \nThen, 28 + 5 = 33 \nNext, 33 + 30 = 63 \nFinally, 77 - 63 = 14, which doesn't help.\n\nConsidering different combinations, one viable way is:\n(70 - 42) * (30 / 5) = 28 * 6 = 168 / 2 = 84 / 2 = 42 / 2 = 21 / 3 = 7\n\nSo, (70 - 42) * (30 / 5) = 77.\n</think>\n<answer>(70 - 42) * (30 / 5) = 77</answer>"
|
||||
```
|
||||
step 200:
|
||||
```
|
||||
"<think>\nWe need to use all the given numbers in a way that the resultant value of the calculation is 99. Given these restrictions, it's clear we should look into how the numbers relate and the mathematical operations that will bring them together to meet our condition.\n\nFirstly, let's evaluate the sums, differences, and multiplication/division among the numbers and their relations to see how this could lead us towards an outcome of 99.\n- Summing the three numbers: 96 + 70 + 73 = 239, which is far from 99. This shows addition isn't practical.\n- Subtracting numbers: For instance, subtracting the lowest two (i.e., 73 - 70 = 3) and combining with the highest one (96) does not work. Also, there is no other combination of subtraction that would result in the number needing to add up to 99.\n- Multiplying or dividing the numbers alone wouldn’t reach to any near solution since none of the products or division results of the numbers themselves is very close to 99. We might think more carefully about the arrangement of them using a combination of the operations provided.\nTherefore, considering the possible arrangement, a combination like taking (73 - 70 + 96) may produce an effective solution as this will sum the values in such a manner that it aligns with the goal of 99.\n\nSo we conclude that the operation: 73 - 70 + 96 gives the right sequence to equal 99.\n</think>\n\n<answer>73 - 70 + 96 = 99</answer>"
|
||||
```
|
||||
step 500:
|
||||
```
|
||||
"<think>\nLet's begin by exploring various possible combinations of the given numbers using the arithmetic operators we have access to.\n\n- If I add the numbers, I get: 56 + 73 + 51 = 180 which is not equal to our target number (68)\n- If I subtract one number from another I will still exceed our target value as well since the maximum difference is: |(difference between max-min):| | (73 - 51) or | (73 - 56), etc.|\n- Let's multiply the numbers individually and see if the division operation can bring us closer to the goal:\n - (73 * 51) - 56 = 3723 - 56 = 3667 which isn't the goal either.\n - The only operator left is addition and subtraction; I'll try the combination: \n ((73 - 56) + 51) gives me (17 + 51) = 68.\n \nThus, our solution fits the provided problem constraints and meets the required outcome.\n</think>\n<answer>(73 - 56) + 51 = 68</answer>"
|
||||
```
|
||||
|
||||
step 1000:
|
||||
```
|
||||
"<think>\nWe need to form an expression using the given numbers (50, 86, and 49) such that it equals 85. Let's try different combinations:\n\n(86 - 50) + 49 = 76 + 49 = 125 (too big)\n(86 - 49) + 50 = 37 + 50 = 87 (too big)\n(86 - 50) + 49 = 37 + 49 = 86 (too big)\n(86 + 50) - 49 = 136 - 49 = 87 (too big)\n(86 - 50) + (49 - x)? Not working!\n(50 + 86) - 49: this is 136 which isn't our aim.\n50 + (86 - 49) = 50 + 37 = 87 which wasn't correct.\n50 - 86 + 49 = -16 + 49: This doesn't match up as well.\n86 - (50 + 49) = 86 - 99 which doesn't work too.\n\nWe try one more:\n(86 + 49) - 50 = 135 - 50 = 85!\n\nThat's it!\n</think>\n\n<answer>(86 + 49) - 50 = 85</answer>"
|
||||
```
|
||||
|
||||
step 2000:
|
||||
```
|
||||
"<think>\nWe need to form an expression using the numbers 58, 9, 78, and 44 which evaluates to 73. Let's try different combinations:\n(78 - 58) + (9 + 44) = 10 + 53 = 63 (too low)\n(78 - 58) + (9 - 44) = 20 - 35 = -15 (too low)\n(78 - 58) + (44 - 9) = 20 + 35 = 55 (too low)\n(78 + 58) - (9 + 44) = 136 - 53 = 83 (too high)\n(78 + 58) - (9 - 44) = 136 + 35 = 171 (too high)\n(78 + 58) - (44 + 9) = 136 + 53 = 189 (too high)\n(78 + 9) - (58 + 44) = 87 - 102 = -15 (too low)\n(78 + 9) - (58 - 44) = 87 - 14 = 73\n\nSo our solution is: (78 + 9) - (58 - 44) = 73</think>\n\n<answer>(78 + 9) - (58 - 44) = 73</answer>"
|
||||
```
|
||||
|
||||
附learning_rate和beta分别取值1e-6和0.04的不稳定实验记录,模型在step 200左右出现了震荡,format和CountdownORM瞬间走低:
|
||||

|
||||
@@ -0,0 +1,658 @@
|
||||
|
||||
# 注册多模态模型最佳实践
|
||||
|
||||
本文将介绍如何在ms-swift中注册多模态模型,并成功推理和训练。本文将以Qwen2.5-Omni为例子,注册新的model_type和template `my_qwen2_5_omni`,并支持文本、图片、视频和音频的训练。由于Qwen2.5-Omni已经在ms-swift中注册,我们可以通过显式指定model_type和template来使用我们自定义的部分。
|
||||
|
||||
## 环境准备
|
||||
```shell
|
||||
# 避免未来出现与文档的不兼容情况
|
||||
pip install "ms-swift>=4.0"
|
||||
|
||||
pip install "transformers==4.57.*" "qwen_omni_utils==0.0.8"
|
||||
```
|
||||
|
||||
|
||||
## 注册模型
|
||||
|
||||
第一步,我们需要注册模型,来获取模型和processor。
|
||||
|
||||
```python
|
||||
from transformers import AutoConfig, PretrainedConfig, PreTrainedModel
|
||||
|
||||
from swift.model import (Model, ModelGroup, ModelMeta, MultiModelKeys, get_model_processor, register_model,
|
||||
register_model_arch, ModelLoader)
|
||||
from swift.model.models.qwen import patch_qwen_vl_utils
|
||||
from swift.model.patcher import patch_get_input_embeddings
|
||||
from swift.model.utils import use_submodel_func
|
||||
from swift.utils import get_env_args, Processor
|
||||
|
||||
register_model_arch(
|
||||
MultiModelKeys(
|
||||
'my_qwen2_5_omni',
|
||||
# `freeze_llm`, `freeze_vit`, `freeze_aligner`将根据下面的值来决定其行为。
|
||||
# 例如:全参数训练,若设置`freeze_vit=True`,将冻结以`thinker.audio_tower`和`thinker.visual`为前缀的模型层的参数。
|
||||
# LoRA训练,若设置`freeze_vit=False`,将额外为以`thinker.audio_tower`和`thinker.visual`为前缀的Linear层添加LoRA。
|
||||
language_model=['thinker.model', 'thinker.lm_head'],
|
||||
vision_tower=['thinker.audio_tower', 'thinker.visual'],
|
||||
aligner=['thinker.audio_tower.proj', 'thinker.visual.merger'],
|
||||
# generator的部分将永远不进行训练或处于冻结状态。
|
||||
# 如果你希望`thinker.audio_tower`, `thinker.audio_tower.proj`永远不进行训练,你可以放置到generator中,并将其从vision_tower, aligner中移除。
|
||||
generator=['talker', 'token2wav'],
|
||||
))
|
||||
|
||||
class Qwen2_5OmniLoader(ModelLoader):
|
||||
|
||||
|
||||
def get_config(self, model_dir: str) -> PretrainedConfig:
|
||||
config = AutoConfig.from_pretrained(model_dir, trust_remote_code=True)
|
||||
enable_audio_output = get_env_args('ENABLE_AUDIO_OUTPUT', bool, None)
|
||||
if enable_audio_output is not None:
|
||||
config.enable_audio_output = enable_audio_output
|
||||
return config
|
||||
|
||||
def get_processor(self, model_dir: str, config: PretrainedConfig) -> Processor:
|
||||
from transformers import Qwen2_5OmniProcessor
|
||||
from qwen_omni_utils import vision_process
|
||||
processor = Qwen2_5OmniProcessor.from_pretrained(model_dir, trust_remote_code=True)
|
||||
# Control constants in qwen_omni_utils library via environment variables,
|
||||
# e.g., `MAX_PIXELS`, etc.
|
||||
patch_qwen_vl_utils(vision_process)
|
||||
return processor
|
||||
|
||||
def get_model(self, model_dir: str, config: PretrainedConfig, processor: Processor,
|
||||
model_kwargs) -> PreTrainedModel:
|
||||
from transformers import Qwen2_5OmniForConditionalGeneration
|
||||
print('Run my_qwen2_5_omni...')
|
||||
self.auto_model_cls = self.auto_model_cls or Qwen2_5OmniForConditionalGeneration
|
||||
model = super().get_model(model_dir, config, processor, model_kwargs)
|
||||
# For multimodal model consistency, we replace the model's forward/generate functions
|
||||
# with those of its language_model.
|
||||
# Handle additional parts separately.
|
||||
use_submodel_func(model, 'thinker')
|
||||
# Avoid inplace operations on leaf_variable during training
|
||||
# (replacing parts of input_embeds with images_embeds)
|
||||
patch_get_input_embeddings(model.thinker.visual, 'patch_embed')
|
||||
# Some custom settings for model/config (usually not needed; configure based on
|
||||
# specific model if errors occur during training/inference)
|
||||
model.config.keys_to_ignore_at_inference += ['hidden_states', 'attention_mask']
|
||||
model.config.talker_config.pad_token_id = None
|
||||
return model
|
||||
|
||||
|
||||
register_model(
|
||||
ModelMeta(
|
||||
'my_qwen2_5_omni',
|
||||
[
|
||||
ModelGroup([
|
||||
Model('Qwen/Qwen2.5-Omni-3B', 'Qwen/Qwen2.5-Omni-3B'),
|
||||
Model('Qwen/Qwen2.5-Omni-7B', 'Qwen/Qwen2.5-Omni-7B'),
|
||||
]),
|
||||
],
|
||||
# 用来获取model和processor的函数。
|
||||
Qwen2_5OmniLoader,
|
||||
template='my_qwen2_5_omni',
|
||||
is_multimodal=True, # 是否是多模态模型
|
||||
model_arch='my_qwen2_5_omni', # 通常只为多模态模型设置
|
||||
# 用于model_type的自动匹配
|
||||
architectures=['Qwen2_5OmniModel', 'Qwen2_5OmniForConditionalGeneration'],
|
||||
# 用来提示用户依赖版本(可删除)
|
||||
requires=['transformers>=4.50', 'soundfile', 'qwen_omni_utils', 'decord'],
|
||||
# 用来提示用户(可删除)
|
||||
tags=['vision', 'video', 'audio'],
|
||||
# 全参数训练/merge-lora需要额外保存的文件
|
||||
additional_saved_files=['spk_dict.pt'],
|
||||
))
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 测试与debug
|
||||
model, processor = get_model_processor('Qwen/Qwen2.5-Omni-7B', model_type='my_qwen2_5_omni')
|
||||
```
|
||||
|
||||
## 注册模板
|
||||
|
||||
第二步,我们需要注册模板,来自定义如何将文本、图片、视频和音频进行预处理(`_encode`和`_data_collator`方法)。这是ms-swift支持多模态模型训练的关键模块。预处理方式请参考transformers推理实现,并进行对齐。
|
||||
|
||||
template的功能如下:
|
||||
1. 支持正常推理与训练,预处理文本和多模态信息,并支持grounding任务。
|
||||
2. 支持padding_free和packing训练。
|
||||
3. 支持混合模态数据训练。
|
||||
|
||||
|
||||
```python
|
||||
from functools import partial
|
||||
from typing import Any, Dict, List, Literal, Optional
|
||||
|
||||
import torch
|
||||
from transformers.integrations import is_deepspeed_zero3_enabled
|
||||
from swift import get_model_processor
|
||||
from swift.template import StdTemplateInputs, Template, TemplateMeta, get_template, register_template
|
||||
from swift.template.utils import Context, findall
|
||||
from swift.template.vision_utils import load_audio
|
||||
from swift.utils import Processor, get_env_args, get_logger, get_packed_seq_params, is_deepspeed_enabled, to_float_dtype
|
||||
|
||||
logger = get_logger()
|
||||
|
||||
class Qwen2_5OmniTemplate(Template):
|
||||
use_model = True # 是否在预处理的过程中需要model参与
|
||||
# 需要注意是:并不是所有的多模态模型都能支持padding_free/packing。`transformers`库内的模型通常可以支持
|
||||
support_padding_free = True # 是否支持padding_free和packing(多模态模型)
|
||||
norm_bbox = 'none' # grounding任务使用绝对坐标还是norm1000坐标
|
||||
|
||||
# 这里的tokens将不会被裁剪(例如设置`--truncation_strategy left/right`)
|
||||
# 并会使用简略方式打印(调用`template.safe_decode`)
|
||||
placeholder_tokens = ['<|IMAGE|>', '<|AUDIO|>', '<|VIDEO|>']
|
||||
|
||||
def init_processor(self, processor: Processor) -> None:
|
||||
"""在初始化processor时,额外初始化所需的一些常量"""
|
||||
if processor is None:
|
||||
return
|
||||
super().init_processor(processor)
|
||||
from transformers.models.qwen2_5_omni.processing_qwen2_5_omni import Qwen2_5OmniProcessorKwargs
|
||||
default = Qwen2_5OmniProcessorKwargs._defaults
|
||||
self.seconds_per_chunk = default['videos_kwargs']['seconds_per_chunk']
|
||||
self.position_id_per_seconds = default['videos_kwargs']['position_id_per_seconds']
|
||||
self.use_audio_in_video = get_env_args('use_audio_in_video', bool, False)
|
||||
self.sampling_rate = get_env_args('sampling_rate', int, self.processor.feature_extractor.sampling_rate)
|
||||
# `QWENVL_BBOX_FORMAT`的含义参考grounding数据集自定义文档
|
||||
self.bbox_format = get_env_args('QWENVL_BBOX_FORMAT', str, 'legacy')
|
||||
|
||||
|
||||
def replace_tag(self, media_type: Literal['image', 'video', 'audio'], index: int,
|
||||
inputs: StdTemplateInputs) -> List[Context]:
|
||||
"""读取多模态数据,并替换通用多模态tag。
|
||||
例如:图像tag从`<image>` -> `<|vision_bos|><|IMAGE|><|vision_eos|>`"""
|
||||
# 读取多模态数据也可以在`_encode`函数中进行,怎么方便怎么来。
|
||||
from qwen_omni_utils import fetch_image, fetch_video
|
||||
if media_type == 'image':
|
||||
inputs.images[index] = fetch_image({'image': inputs.images[index]})
|
||||
return ['<|vision_bos|><|IMAGE|><|vision_eos|>']
|
||||
elif media_type == 'audio':
|
||||
if self.mode != 'vllm': # 'vllm'推理场景下不需要处理
|
||||
inputs.audios[index] = load_audio(inputs.audios[index], self.sampling_rate)
|
||||
return ['<|audio_bos|><|AUDIO|><|audio_eos|>']
|
||||
elif media_type == 'video':
|
||||
video = inputs.videos[index]
|
||||
_video = fetch_video({'video': video})
|
||||
if isinstance(_video, torch.Tensor):
|
||||
_video = _video.to(torch.uint8)
|
||||
inputs.videos[index] = _video
|
||||
if self.use_audio_in_video:
|
||||
import librosa
|
||||
if video.startswith('http://') or video.startswith('https://'):
|
||||
import audioread
|
||||
video = audioread.ffdec.FFmpegAudioFile(video)
|
||||
video = librosa.load(video, sr=self.sampling_rate)[0]
|
||||
inputs.audios.insert(inputs.audio_idx, (video, 'video'))
|
||||
inputs.audio_idx += 1
|
||||
return ['<|vision_bos|><|audio_bos|><|VIDEO|><|audio_eos|><|vision_eos|>']
|
||||
else:
|
||||
return ['<|vision_bos|><|VIDEO|><|vision_eos|>']
|
||||
|
||||
|
||||
def replace_ref(self, ref: str, index: int, inputs: StdTemplateInputs) -> List[Context]:
|
||||
"""替换grounding任务的通用tag: `<ref-object>`"""
|
||||
if self.bbox_format == 'legacy':
|
||||
return [f'<|object_ref_start|>{ref}<|object_ref_end|>']
|
||||
else:
|
||||
return [ref]
|
||||
|
||||
def replace_bbox(self, bbox: List[int], index: int, inputs: StdTemplateInputs) -> List[Context]:
|
||||
"""替换grounding任务的通用tag: `<bbox>`"""
|
||||
if self.bbox_format == 'legacy':
|
||||
return [f'<|box_start|>{self._get_bbox_str(bbox)}<|box_end|>']
|
||||
else:
|
||||
return [str(bbox)]
|
||||
|
||||
def packing_row(self, row: List[Dict[str, Any]]) -> Dict[str, Any]:
|
||||
"""支持packing & mrope。通常情况不需要继承该函数,这里为了自定义mrope的position_ids。"""
|
||||
position_ids = []
|
||||
for r in row:
|
||||
r = r.copy()
|
||||
r['input_ids'] = torch.tensor(r['input_ids'])[None]
|
||||
position_ids.append(self._get_position_ids(r))
|
||||
packed = super().packing_row(row)
|
||||
packed['position_ids'] = torch.concat(position_ids, dim=-1)
|
||||
return packed
|
||||
|
||||
def _get_new_tokens_use_audio_in_video(self, i, *, video_grid_thw, video_second_per_grid, audio_lengths,
|
||||
video_token_id, audio_token_id):
|
||||
"""辅助函数,用于支持`use_audio_in_video`为True的情况"""
|
||||
merge_size = self.processor.image_processor.merge_size
|
||||
grid_thw = video_grid_thw[i]
|
||||
height = grid_thw[1] // merge_size
|
||||
width = grid_thw[2] // merge_size
|
||||
audio_token_indices = torch.arange(audio_lengths[i])
|
||||
video_token_indices = torch.arange(grid_thw[0]).reshape(-1, 1, 1)
|
||||
|
||||
video_token_indices = torch.broadcast_to(video_token_indices,
|
||||
(video_token_indices.shape[0], height, width)).reshape(-1)
|
||||
video_token_indices = (video_token_indices * video_second_per_grid[i] * self.position_id_per_seconds)
|
||||
tokens_per_chunk = int(self.position_id_per_seconds * self.seconds_per_chunk)
|
||||
video_chunk_indexes = self.processor.get_chunked_index(video_token_indices, tokens_per_chunk)
|
||||
audio_chunk_indexes = self.processor.get_chunked_index(audio_token_indices, tokens_per_chunk)
|
||||
|
||||
res = []
|
||||
for j in range(max(len(video_chunk_indexes), len(audio_chunk_indexes))):
|
||||
if j < len(video_chunk_indexes):
|
||||
video_seq_length = video_chunk_indexes[j][1] - video_chunk_indexes[j][0]
|
||||
res += video_token_id * video_seq_length
|
||||
if j < len(audio_chunk_indexes):
|
||||
audio_seq_length = audio_chunk_indexes[j][1] - audio_chunk_indexes[j][0]
|
||||
res += audio_token_id * audio_seq_length
|
||||
return res
|
||||
|
||||
|
||||
def _encode(self, inputs: StdTemplateInputs) -> Dict[str, Any]:
|
||||
"""这里决定如何将text/images/audios/videos -> input_ids、labels、loss_scale以及pixel_values等多模态内容
|
||||
这里的处理逻辑通常可以从对应模型的预处理代码实现中借鉴。
|
||||
推荐:请先做推理对齐再做训练"""
|
||||
encoded = Template._encode(self, inputs) # 处理纯文本部分,具体请参考自定义模型文档
|
||||
logger.info_once('Run qwen2_5_omni template')
|
||||
processor = self.processor
|
||||
# 获取多模态内容
|
||||
media_inputs = processor(
|
||||
text='',
|
||||
audio=inputs.audios or None,
|
||||
images=inputs.images or None,
|
||||
videos=inputs.videos or None,
|
||||
do_resize=False,
|
||||
return_tensors='pt')
|
||||
# 我们不使用`processor`产生的input_ids和attention_mask。因为其不产生`labels`。
|
||||
media_inputs.pop('input_ids')
|
||||
media_inputs.pop('attention_mask')
|
||||
media_inputs = to_float_dtype(media_inputs, self.model_info.torch_dtype)
|
||||
|
||||
input_ids = encoded['input_ids']
|
||||
labels = encoded['labels']
|
||||
loss_scale = encoded.get('loss_scale', None)
|
||||
# audio模态
|
||||
audio_token_id = self._tokenize('<|AUDIO|>')
|
||||
idx_list = findall(input_ids, audio_token_id) # 查找所有的audio_token
|
||||
feature_attention_mask = media_inputs.get('feature_attention_mask')
|
||||
if feature_attention_mask is not None:
|
||||
audio_feature_lengths = torch.sum(feature_attention_mask, dim=1)
|
||||
audio_lengths = ((audio_feature_lengths - 1) // 2 + 1 - 2) // 2 + 1
|
||||
else:
|
||||
audio_lengths = None
|
||||
audio_lengths_origin = audio_lengths
|
||||
# video_audios_mask用于处理`use_audio_in_video`,区分是纯audio(0)还是video中的audio(1)
|
||||
video_audios_mask = []
|
||||
for i, audio in enumerate(inputs.audios):
|
||||
if isinstance(audio, tuple) and audio[1] == 'video':
|
||||
inputs.audios[i] = audio[0]
|
||||
video_audios_mask.append(True)
|
||||
else:
|
||||
video_audios_mask.append(False)
|
||||
video_audios_mask = torch.tensor(video_audios_mask)
|
||||
if idx_list:
|
||||
# 过滤掉video中的audio的内容(将在video部分处理)
|
||||
if self.use_audio_in_video:
|
||||
audio_lengths = audio_lengths[~video_audios_mask]
|
||||
|
||||
def _get_new_audio_tokens(i):
|
||||
return audio_token_id * audio_lengths[i]
|
||||
|
||||
# 对input_ids的多模态tokens进行展开
|
||||
input_ids, labels, loss_scale = self._extend_tokens(input_ids, labels, loss_scale, idx_list,
|
||||
_get_new_audio_tokens)
|
||||
|
||||
# image和video模态
|
||||
for media_type in ['image', 'video']:
|
||||
token = f'<|{media_type.upper()}|>'
|
||||
token_id = self._tokenize(token)
|
||||
idx_list = findall(input_ids, token_id)
|
||||
if idx_list:
|
||||
merge_size = processor.image_processor.merge_size
|
||||
media_grid_thw = media_inputs.get(f'{media_type}_grid_thw')
|
||||
if media_type == 'video' and self.use_audio_in_video:
|
||||
audio_lengths = audio_lengths_origin[video_audios_mask]
|
||||
video_second_per_grid = media_inputs['video_second_per_grid']
|
||||
_get_new_tokens_use_audio_in_video = partial(
|
||||
self._get_new_tokens_use_audio_in_video,
|
||||
video_grid_thw=media_grid_thw,
|
||||
video_second_per_grid=video_second_per_grid,
|
||||
audio_lengths=audio_lengths,
|
||||
video_token_id=token_id,
|
||||
audio_token_id=audio_token_id)
|
||||
input_ids, labels, loss_scale = self._extend_tokens(input_ids, labels, loss_scale, idx_list,
|
||||
_get_new_tokens_use_audio_in_video)
|
||||
|
||||
else:
|
||||
|
||||
def _get_new_tokens(i):
|
||||
token_len = (media_grid_thw[i].prod() // (merge_size**2))
|
||||
return token_id * token_len
|
||||
|
||||
input_ids, labels, loss_scale = self._extend_tokens(input_ids, labels, loss_scale, idx_list,
|
||||
_get_new_tokens)
|
||||
|
||||
encoded['input_ids'] = input_ids
|
||||
encoded['labels'] = labels
|
||||
encoded['loss_scale'] = loss_scale
|
||||
encoded.update(media_inputs) # 将多模态内容加入其中
|
||||
return encoded
|
||||
|
||||
def _post_encode(self, model, inputs: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""该函数通常用于解决混合模型训练zero2/zero3卡住的问题,
|
||||
即有的进程为纯文本数据未过vit,有的进程含图片数据过了vit。这里将创建dummy_image来解决。
|
||||
|
||||
该函数将被注册在`model.forward`前的pre_forward_hook中。
|
||||
该函数需返回 含多模态信息的input_embeds。
|
||||
"""
|
||||
if not self.is_training:
|
||||
return inputs
|
||||
|
||||
input_ids = inputs['input_ids']
|
||||
input_features = inputs.get('input_features')
|
||||
feature_attention_mask = inputs.get('feature_attention_mask')
|
||||
|
||||
base_model = self.get_base_model(model)
|
||||
inputs_embeds = base_model.thinker.model.embed_tokens(input_ids)
|
||||
thinker_config = model.config.thinker_config
|
||||
# 辅助函数,用于处理text/image/video混合模态数据场景。(内部会创建dummy_image)
|
||||
inputs_embeds = self._get_inputs_embeds_hf(inputs_embeds, inputs, model.thinker.visual, self.processor,
|
||||
thinker_config)
|
||||
# 含audio的混合模态数据场景
|
||||
if input_features is None:
|
||||
if is_deepspeed_enabled() and not is_deepspeed_zero3_enabled():
|
||||
# 注意: 由于transformers实现中,经过audio部分模型层的次数与audio数量相关
|
||||
# 因此zero3在不同进程audios数不同场景下会卡住(需修改transformers代码修复)。此场景请使用zero2。
|
||||
input_features = input_ids.new_zeros([1, 128, 128], dtype=model.thinker.audio_tower.dtype)
|
||||
feature_attention_mask = input_ids.new_ones([1, 128], dtype=torch.bool)
|
||||
audio_res = model.thinker.get_audio_features(input_features, feature_attention_mask)
|
||||
# 兼容transformers 5.0
|
||||
if hasattr(audio_res, 'last_hidden_state'):
|
||||
audio_embeds = audio_res.last_hidden_state
|
||||
else:
|
||||
audio_embeds = audio_res
|
||||
inputs_embeds = inputs_embeds + audio_embeds.mean() * 0.
|
||||
else:
|
||||
audio_res = model.thinker.get_audio_features(input_features, feature_attention_mask)
|
||||
# 兼容transformers 5.0
|
||||
if hasattr(audio_res, 'last_hidden_state'):
|
||||
audio_embeds = audio_res.last_hidden_state
|
||||
else:
|
||||
audio_embeds = audio_res
|
||||
audio_mask = (input_ids == thinker_config.audio_token_index).unsqueeze(-1).expand_as(inputs_embeds)
|
||||
audio_embeds = audio_embeds.to(inputs_embeds.device, inputs_embeds.dtype)
|
||||
inputs_embeds = inputs_embeds.masked_scatter(audio_mask, audio_embeds)
|
||||
|
||||
return {'inputs_embeds': inputs_embeds}
|
||||
|
||||
def _get_position_ids(self, inputs: Dict[str, Any]):
|
||||
"""辅助函数,用来获取mrope的position_ids"""
|
||||
feature_attention_mask = inputs.get('feature_attention_mask')
|
||||
if feature_attention_mask is not None:
|
||||
audio_feature_lengths = torch.sum(feature_attention_mask, dim=1)
|
||||
else:
|
||||
audio_feature_lengths = None
|
||||
video_second_per_grid = inputs.pop('video_second_per_grid', None)
|
||||
input_ids = inputs['input_ids']
|
||||
attention_mask = inputs.get('attention_mask')
|
||||
if attention_mask is None:
|
||||
attention_mask = torch.ones_like(input_ids)
|
||||
position_ids, _ = self.model.thinker.get_rope_index(
|
||||
input_ids,
|
||||
inputs.get('image_grid_thw'),
|
||||
inputs.get('video_grid_thw'),
|
||||
attention_mask,
|
||||
self.use_audio_in_video,
|
||||
audio_feature_lengths,
|
||||
video_second_per_grid,
|
||||
)
|
||||
return self._concat_text_position_ids(position_ids) # 第一维为text_position_ids
|
||||
|
||||
def _data_collator(self, batch: List[Dict[str, Any]], *, padding_to: Optional[int] = None) -> Dict[str, Any]:
|
||||
"""传入dataloader的`collate_fn`"""
|
||||
res = super()._data_collator(batch, padding_to=padding_to)
|
||||
if not self.padding_free and self.is_training:
|
||||
# 其中padding_free/packing场景将会在packing_row中处理position_ids。
|
||||
res['position_ids'] = self._get_position_ids(res)
|
||||
if 'position_ids' in res:
|
||||
# 创建`packed_seq_params`以支持padding_free/packing & flash-attn
|
||||
position_ids = res['position_ids']
|
||||
res['position_ids'] = position_ids[1:]
|
||||
res['text_position_ids'] = text_position_ids = position_ids[0]
|
||||
# https://github.com/huggingface/transformers/pull/40194
|
||||
res.update(get_packed_seq_params(text_position_ids))
|
||||
return res
|
||||
|
||||
def _data_collator_mm_data(self, batch: List[Dict[str, Any]]) -> Dict[str, Any]:
|
||||
"""处理`_data_collator`函数中的多模态部分。(该函数兼容padding_free/packing)"""
|
||||
res = super()._data_collator_mm_data(batch)
|
||||
video_second_per_grid = self.gather_list(batch, 'video_second_per_grid')
|
||||
if video_second_per_grid:
|
||||
res['video_second_per_grid'] = video_second_per_grid
|
||||
input_features = [b['input_features'] for b in batch if b.get('input_features') is not None]
|
||||
feature_attention_mask = [
|
||||
b['feature_attention_mask'] for b in batch if b.get('feature_attention_mask') is not None
|
||||
]
|
||||
if input_features:
|
||||
res['input_features'] = torch.concat(input_features)
|
||||
res['feature_attention_mask'] = torch.concat(feature_attention_mask)
|
||||
return res
|
||||
|
||||
def generate(self, model, *args, **kwargs):
|
||||
"""`TransformersEngine`会调用template.generate方法进行文本生成,这里继承进行自定义。"""
|
||||
if kwargs.get('video_grid_thw') is not None:
|
||||
kwargs['use_audio_in_video'] = self.use_audio_in_video
|
||||
return super().generate(model, *args, **kwargs)
|
||||
|
||||
|
||||
register_template(
|
||||
TemplateMeta('my_qwen2_5_omni', prefix=[], prompt=['<|im_start|>user\n{{QUERY}}<|im_end|>\n<|im_start|>assistant\n'],
|
||||
chat_sep=['<|im_end|>\n'], suffix=['<|im_end|>'],
|
||||
system_prefix=['<|im_start|>system\n{{SYSTEM}}<|im_end|>\n'],
|
||||
default_system='You are a helpful assistant.', stop_words=['<|endoftext|>'],
|
||||
agent_template='hermes',
|
||||
template_cls=Qwen2_5OmniTemplate))
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 测试与debug
|
||||
model, processor = get_model_processor('Qwen/Qwen2.5-Omni-7B', model_type='my_qwen2_5_omni')
|
||||
template = get_template(processor, template_type='my_qwen2_5_omni')
|
||||
data = {
|
||||
'messages': [
|
||||
{'role': 'user', 'content': '描述视频<video>与图片<image>内容。'},
|
||||
{'role': 'assistant', 'content': '一个小孩和一只猫咪。'},
|
||||
],
|
||||
'videos': ['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4'],
|
||||
'images': ['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png'],
|
||||
}
|
||||
template.set_mode('train')
|
||||
encoded = template.encode(data)
|
||||
print('input_ids: ' + template.safe_decode(encoded['input_ids']))
|
||||
print('labels: ' + template.safe_decode(encoded['labels']))
|
||||
print('keys: ' + str(encoded.keys()))
|
||||
```
|
||||
|
||||
|
||||
## 推理对齐
|
||||
接下来,你需要进行TransformersEngine与transformers的推理对齐。通常你需要对齐`input_ids`以及输出内容。你可以书写以下测试函数:
|
||||
|
||||
```python
|
||||
import os
|
||||
from transformers import Qwen2_5OmniForConditionalGeneration, Qwen2_5OmniProcessor
|
||||
from qwen_omni_utils import process_mm_info
|
||||
from modelscope import snapshot_download
|
||||
from swift.infer_engine import TransformersEngine, InferRequest, RequestConfig
|
||||
import requests
|
||||
|
||||
def infer_hf():
|
||||
model_dir = snapshot_download('Qwen/Qwen2.5-Omni-7B')
|
||||
model = Qwen2_5OmniForConditionalGeneration.from_pretrained(
|
||||
model_dir, torch_dtype="auto", device_map="auto", attn_implementation='flash_attention_2')
|
||||
processor = Qwen2_5OmniProcessor.from_pretrained(model_dir)
|
||||
# 使用decord读取视频(暂不支持url)
|
||||
resp = requests.get('https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4')
|
||||
with open('_baby.mp4', 'wb') as f:
|
||||
f.write(resp.content)
|
||||
|
||||
conversation = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "video", "video": "_baby.mp4"},
|
||||
{"type": "image", "image": "http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png"},
|
||||
{"type": "text", "text": "描述视频和图像。"},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
USE_AUDIO_IN_VIDEO = False
|
||||
text = processor.apply_chat_template(conversation, add_generation_prompt=True, tokenize=False)
|
||||
audios, images, videos = process_mm_info(conversation, use_audio_in_video=USE_AUDIO_IN_VIDEO)
|
||||
inputs = processor(text=text, audio=audios, images=images, videos=videos, return_tensors="pt", padding=True,
|
||||
use_audio_in_video=USE_AUDIO_IN_VIDEO)
|
||||
inputs = inputs.to(model.device).to(model.dtype)
|
||||
text_ids = model.generate(**inputs, use_audio_in_video=USE_AUDIO_IN_VIDEO, thinker_do_sample=False,
|
||||
return_audio=False)
|
||||
text = processor.batch_decode(text_ids[:, inputs['input_ids'].shape[1]:], skip_special_tokens=True, clean_up_tokenization_spaces=False)
|
||||
return inputs['input_ids'][0].tolist(), text[0]
|
||||
|
||||
def test_my_qwen2_5_omni():
|
||||
engine = TransformersEngine('Qwen/Qwen2.5-Omni-7B', model_type='my_qwen2_5_omni', attn_impl='flash_attention_2')
|
||||
infer_request = InferRequest(messages=[{
|
||||
"role": "user",
|
||||
"content": "<video><image>描述视频和图像。",
|
||||
}],
|
||||
videos=["https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4"],
|
||||
images=["http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png"],
|
||||
)
|
||||
request_config = RequestConfig(temperature=0, max_tokens=512)
|
||||
input_ids = engine.template.encode(infer_request)['input_ids']
|
||||
resp_list = engine.infer([infer_request], request_config)
|
||||
resp = resp_list[0].choices[0].message.content
|
||||
return input_ids, resp
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 开启debug模式,会打印`TransformersEngine.infer`的input_ids和generate_ids
|
||||
os.environ['SWIFT_DEBUG'] = '1'
|
||||
input_ids_hf, response_hf = infer_hf()
|
||||
input_ids_swift, response_swift = test_my_qwen2_5_omni()
|
||||
# 测试input_ids和response对齐
|
||||
assert input_ids_hf == input_ids_swift
|
||||
assert response_hf == response_swift
|
||||
```
|
||||
|
||||
|
||||
## 开始训练
|
||||
|
||||
使用python代码训练,这通常更容易debug:
|
||||
```python
|
||||
from swift import sft_main, SftArguments
|
||||
import os
|
||||
if __name__ == '__main__':
|
||||
os.environ['MAX_PIXELS'] = '1003520'
|
||||
sft_main(SftArguments(
|
||||
model='Qwen/Qwen2.5-Omni-7B',
|
||||
dataset=['AI-ModelScope/LaTeX_OCR#5000'],
|
||||
model_type='my_qwen2_5_omni',
|
||||
template='my_qwen2_5_omni',
|
||||
load_from_cache_file=True,
|
||||
split_dataset_ratio=0.01,
|
||||
tuner_type='lora',
|
||||
torch_dtype='bfloat16',
|
||||
attn_impl='flash_attn',
|
||||
padding_free=True,
|
||||
num_train_epochs=1,
|
||||
per_device_train_batch_size=16,
|
||||
per_device_eval_batch_size=16,
|
||||
learning_rate=1e-4,
|
||||
lora_rank=8,
|
||||
lora_alpha=32,
|
||||
target_modules=['all-linear'],
|
||||
freeze_vit=True,
|
||||
freeze_aligner=True,
|
||||
gradient_accumulation_steps=1,
|
||||
eval_steps=50,
|
||||
save_steps=50,
|
||||
save_total_limit=2,
|
||||
logging_steps=5,
|
||||
max_length=2048,
|
||||
output_dir='output',
|
||||
warmup_ratio=0.05,
|
||||
dataloader_num_workers=4,
|
||||
dataset_num_proc=1,
|
||||
))
|
||||
```
|
||||
|
||||
使用命令行训练:
|
||||
```shell
|
||||
# 4 * 35GiB
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
NPROC_PER_NODE=4 \
|
||||
VIDEO_MAX_PIXELS=50176 \
|
||||
FPS_MAX_FRAMES=12 \
|
||||
MAX_PIXELS=1003520 \
|
||||
swift sft \
|
||||
--model Qwen/Qwen2.5-Omni-7B \
|
||||
--model_type my_qwen2_5_omni \
|
||||
--template my_qwen2_5_omni \
|
||||
--external_plugins 'examples/custom/my_qwen2_5_omni/my_register.py' \
|
||||
--dataset 'AI-ModelScope/alpaca-gpt4-data-zh#2000' \
|
||||
'AI-ModelScope/LaTeX_OCR:human_handwrite#2000' \
|
||||
'speech_asr/speech_asr_aishell1_trainsets:validation#2000' \
|
||||
'swift/VideoChatGPT:all#2000' \
|
||||
--load_from_cache_file true \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--tuner_type lora \
|
||||
--torch_dtype bfloat16 \
|
||||
--attn_impl flash_attn \
|
||||
--padding_free true \
|
||||
--packing true \
|
||||
--num_train_epochs 3 \
|
||||
--per_device_train_batch_size 1 \
|
||||
--per_device_eval_batch_size 1 \
|
||||
--learning_rate 1e-4 \
|
||||
--lora_rank 8 \
|
||||
--lora_alpha 32 \
|
||||
--target_modules all-linear \
|
||||
--freeze_vit true \
|
||||
--freeze_aligner true \
|
||||
--gradient_accumulation_steps 1 \
|
||||
--eval_steps 50 \
|
||||
--save_steps 50 \
|
||||
--save_total_limit 2 \
|
||||
--logging_steps 5 \
|
||||
--max_length 4096 \
|
||||
--output_dir output \
|
||||
--warmup_ratio 0.05 \
|
||||
--dataloader_num_workers 4 \
|
||||
--dataset_num_proc 1 \
|
||||
--deepspeed zero2
|
||||
```
|
||||
|
||||
训练后对验证集进行推理:(环境变量请与训练时对齐)
|
||||
```shell
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
VIDEO_MAX_PIXELS=50176 \
|
||||
FPS_MAX_FRAMES=12 \
|
||||
MAX_PIXELS=1003520 \
|
||||
swift infer \
|
||||
--adapters output/vx-xxx/checkpoint-xxx \
|
||||
--stream true \
|
||||
--max_new_tokens 512 \
|
||||
--load_data_args true
|
||||
```
|
||||
|
||||
使用以下命令将训练权重推送到 Modelscope:
|
||||
```shell
|
||||
swift export \
|
||||
--adapters output/vx-xxx/checkpoint-xxx \
|
||||
--push_to_hub true \
|
||||
--hub_model_id '<your-model-id>' \
|
||||
--hub_token '<your-sdk-token>'
|
||||
```
|
||||
|
||||
## 提交PR
|
||||
|
||||
如果你想给ms-swift提交PR,你需要额外运行以下命令,对代码进行整理:
|
||||
|
||||
```shell
|
||||
pip install pre-commit
|
||||
pre-commit run --all-files
|
||||
```
|
||||
@@ -0,0 +1,280 @@
|
||||
# Metax支持
|
||||
|
||||
## 1. 在 Metax 平台上使用 Swift
|
||||
你可以选择构建自己的镜像,也可以直接拉取已有的预构建镜像。本文以拉取预构建镜像为例,演示如何在 Metax 上使用 ms-swift。
|
||||
### 1.1. 启动 ms-swift 容器
|
||||
```bash
|
||||
docker pull mx-devops-acr-cn-shanghai.cr.volces.com/opensource/public-ai-release/maca/ms-swift:3.10.3-maca.ai3.3.0.16-torch2.6-py310-ubuntu22.04-amd64
|
||||
# 你可以根据需要调整 --privileged 参数,并仅挂载特定的 GPU 卡。
|
||||
# 更多信息请参考我们的官方文档:https://developer.metax-tech.com
|
||||
# 必须通过 --device 挂载 Metax GPU 设备:--device=/dev/dri --device=/dev/mxcd
|
||||
docker run -it --net=host --uts=host --ipc=host --privileged=true --group-add video \
|
||||
--shm-size 100gb --ulimit memlock=-1 \
|
||||
--security-opt seccomp=unconfined --security-opt apparmor=unconfined \
|
||||
--device=/dev/dri --device=/dev/mxcd \
|
||||
-v /root/workspace:/external \
|
||||
--name swift_test \
|
||||
mx-devops-acr-cn-shanghai.cr.volces.com/opensource/public-ai-release/maca/ms-swift:3.10.3-maca.ai3.3.0.16-torch2.6-py310-ubuntu22.04-amd64
|
||||
```
|
||||
## 2. 环境检查
|
||||
### 2.1. 检查 Metax GPU 是否可用
|
||||
得益于与 CUDA 的兼容性,我们可以像使用 NVIDIA GPU 一样检查 Metax 设备是否可用:
|
||||
```python
|
||||
import torch
|
||||
print(torch.cuda.is_available())
|
||||
# True
|
||||
```
|
||||
### 2.2. 检查 GPU 之间的 P2P 连接拓扑
|
||||
```bash
|
||||
mx-smi topo -m
|
||||
# output
|
||||
=================== MetaX System Management Interface Log ===================
|
||||
Timestamp : Wed Feb 11 16:37:10 2026
|
||||
|
||||
Attached GPUs : 8
|
||||
Device link type matrix
|
||||
GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7 Node Affinity CPU Affinity
|
||||
GPU0 X MX MX MX NODE NODE NODE NODE 0 0-31,64-95
|
||||
GPU1 MX X MX MX NODE NODE NODE NODE 0 0-31,64-95
|
||||
GPU2 MX MX X MX NODE NODE NODE NODE 0 0-31,64-95
|
||||
GPU3 MX MX MX X NODE NODE NODE NODE 0 0-31,64-95
|
||||
GPU4 NODE NODE NODE NODE X MX MX MX 0 0-31,64-95
|
||||
GPU5 NODE NODE NODE NODE MX X MX MX 0 0-31,64-95
|
||||
GPU6 NODE NODE NODE NODE MX MX X MX 0 0-31,64-95
|
||||
GPU7 NODE NODE NODE NODE MX MX MX X 0 0-31,64-95
|
||||
|
||||
Legend:
|
||||
X = Self
|
||||
SYS = Connection traversing PCIe as well as the SMP interconnect between NUMA nodes (e.g., QPI/UPI)
|
||||
NODE = Connection traversing PCIe as well as the interconnect between PCIe Host Bridges within a NUMA node
|
||||
PHB = Connection traversing PCIe as well as a PCIe Host Bridge (typically the CPU)
|
||||
PXB = Connection traversing multiple PCIe bridges (without traversing the PCIe Host Bridge)
|
||||
PIX = Connection traversing at most a single PCIe bridge
|
||||
MX = Connection traversing MetaXLink
|
||||
ETH = Connection traversing Eth
|
||||
NA = Connection type is unknown
|
||||
```
|
||||
### 2.3. 查看 GPU 状态
|
||||
```bash
|
||||
mx-smi
|
||||
# output
|
||||
=================== MetaX System Management Interface Log ===================
|
||||
Timestamp : Wed Feb 11 09:55:49 2026
|
||||
|
||||
Attached GPUs : 8
|
||||
+---------------------------------------------------------------------------------+
|
||||
| MX-SMI 2.2.9 Kernel Mode Driver Version: 3.4.4 |
|
||||
| MACA Version: 3.3.0.15 BIOS Version: 1.30.0.0 |
|
||||
|------------------+-----------------+---------------------+----------------------|
|
||||
| Board Name | GPU Persist-M | Bus-id | GPU-Util sGPU-M |
|
||||
| Pwr:Usage/Cap | Temp Perf | Memory-Usage | GPU-State |
|
||||
|==================+=================+=====================+======================|
|
||||
| 0 MetaX C500 | 0 Off | 0000:0e:00.0 | 0% Disabled |
|
||||
| 57W / 350W | 35C P0 | 826/65536 MiB | Available |
|
||||
+------------------+-----------------+---------------------+----------------------+
|
||||
| 1 MetaX C500 | 1 Off | 0000:0f:00.0 | 0% Disabled |
|
||||
| 58W / 350W | 37C P0 | 826/65536 MiB | Available |
|
||||
+------------------+-----------------+---------------------+----------------------+
|
||||
| 2 MetaX C500 | 2 Off | 0000:10:00.0 | 0% Disabled |
|
||||
| 58W / 350W | 36C P0 | 826/65536 MiB | Available |
|
||||
+------------------+-----------------+---------------------+----------------------+
|
||||
| 3 MetaX C500 | 3 Off | 0000:12:00.0 | 0% Disabled |
|
||||
| 60W / 350W | 35C P0 | 826/65536 MiB | Available |
|
||||
+------------------+-----------------+---------------------+----------------------+
|
||||
| 4 MetaX C500 | 4 Off | 0000:35:00.0 | 0% Disabled |
|
||||
| 57W / 350W | 33C P0 | 826/65536 MiB | Available |
|
||||
+------------------+-----------------+---------------------+----------------------+
|
||||
| 5 MetaX C500 | 5 Off | 0000:36:00.0 | 0% Disabled |
|
||||
| 56W / 350W | 34C P0 | 826/65536 MiB | Available |
|
||||
+------------------+-----------------+---------------------+----------------------+
|
||||
| 6 MetaX C500 | 6 Off | 0000:37:00.0 | 0% Disabled |
|
||||
| 55W / 350W | 34C P0 | 826/65536 MiB | Available |
|
||||
+------------------+-----------------+---------------------+----------------------+
|
||||
| 7 MetaX C500 | 7 Off | 0000:38:00.0 | 0% Disabled |
|
||||
| 56W / 350W | 36C P0 | 826/65536 MiB | Available |
|
||||
+------------------+-----------------+---------------------+----------------------+
|
||||
|
||||
+---------------------------------------------------------------------------------+
|
||||
| Process: |
|
||||
| GPU PID Process Name GPU Memory |
|
||||
| Usage(MiB) |
|
||||
|=================================================================================|
|
||||
| no process found |
|
||||
+---------------------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
## 3. 运行示例
|
||||
我们支持直接使用社区版 Swift,同时在镜像中 /workspace 目录下提供了经过更多优化的版本。强烈建议优先使用该目录下的软件包。
|
||||
|
||||
### 3.1. 运行 Swift 示例
|
||||
在大多数场景下,可直接运行 Swift 的训练示例:
|
||||
```bash
|
||||
# We assume that the ms-swift code is under /workspace
|
||||
cd /workspace/ms-swift/
|
||||
bash examples/train/full/train.sh
|
||||
|
||||
```
|
||||
运行输出示例(节选):
|
||||
```bash
|
||||
# output:
|
||||
{'loss': 1.47077751, 'grad_norm': 10.5625, 'learning_rate': 2e-06, 'token_acc': 0.65511727, 'epoch': 0.01, 'global_step/max_steps': '1/94', 'percentage': '1.06%', 'elapsed_time': '2s', 'remaining_time': '4m 28s', 'memory(GiB)': 4.87, 'train_speed(iter/s)': 0.345807}
|
||||
{'loss': 1.58882141, 'grad_norm': 10.75, 'learning_rate': 1e-05, 'token_acc': 0.61763144, 'epoch': 0.05, 'global_step/max_steps': '5/94', 'percentage': '5.32%', 'elapsed_time': '10s', 'remaining_time': '3m 12s', 'memory(GiB)': 5.64, 'train_speed(iter/s)': 0.461462}
|
||||
{'loss': 1.56617603, 'grad_norm': 12.8125, 'learning_rate': 9.92e-06, 'token_acc': 0.61519274, 'epoch': 0.11, 'global_step/max_steps': '10/94', 'percentage': '10.64%', 'elapsed_time': '20s', 'remaining_time': '2m 52s', 'memory(GiB)': 5.64, 'train_speed(iter/s)': 0.485796}
|
||||
{'loss': 1.63347206, 'grad_norm': 13.6875, 'learning_rate': 9.69e-06, 'token_acc': 0.60373975, 'epoch': 0.16, 'global_step/max_steps': '15/94', 'percentage': '15.96%', 'elapsed_time': '30s', 'remaining_time': '2m 39s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.493855}
|
||||
{'loss': 1.60613976, 'grad_norm': 11.0, 'learning_rate': 9.32e-06, 'token_acc': 0.59997221, 'epoch': 0.21, 'global_step/max_steps': '20/94', 'percentage': '21.28%', 'elapsed_time': '39s', 'remaining_time': '2m 27s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.500516}
|
||||
{'loss': 1.45015478, 'grad_norm': 15.25, 'learning_rate': 8.8e-06, 'token_acc': 0.62373584, 'epoch': 0.27, 'global_step/max_steps': '25/94', 'percentage': '26.60%', 'elapsed_time': '49s', 'remaining_time': '2m 16s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.50548}
|
||||
{'loss': 1.39427547, 'grad_norm': 13.9375, 'learning_rate': 8.18e-06, 'token_acc': 0.6357994, 'epoch': 0.32, 'global_step/max_steps': '30/94', 'percentage': '31.91%', 'elapsed_time': '59s', 'remaining_time': '2m 5s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.508409}
|
||||
{'loss': 1.53672237, 'grad_norm': 11.125, 'learning_rate': 7.45e-06, 'token_acc': 0.61650612, 'epoch': 0.37, 'global_step/max_steps': '35/94', 'percentage': '37.23%', 'elapsed_time': '1m 8s', 'remaining_time': '1m 55s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.510425}
|
||||
{'loss': 1.54039021, 'grad_norm': 13.8125, 'learning_rate': 6.65e-06, 'token_acc': 0.61613974, 'epoch': 0.43, 'global_step/max_steps': '40/94', 'percentage': '42.55%', 'elapsed_time': '1m 18s', 'remaining_time': '1m 45s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.512302}
|
||||
{'loss': 1.40159426, 'grad_norm': 9.4375, 'learning_rate': 5.79e-06, 'token_acc': 0.64041773, 'epoch': 0.48, 'global_step/max_steps': '45/94', 'percentage': '47.87%', 'elapsed_time': '1m 27s', 'remaining_time': '1m 35s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.512983}
|
||||
{'loss': 1.54977188, 'grad_norm': 11.9375, 'learning_rate': 4.91e-06, 'token_acc': 0.61078816, 'epoch': 0.53, 'global_step/max_steps': '50/94', 'percentage': '53.19%', 'elapsed_time': '1m 37s', 'remaining_time': '1m 25s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.514489}
|
||||
{'loss': 1.6754509, 'grad_norm': 13.0625, 'learning_rate': 4.04e-06, 'token_acc': 0.58574393, 'epoch': 0.59, 'global_step/max_steps': '55/94', 'percentage': '58.51%', 'elapsed_time': '1m 46s', 'remaining_time': '1m 15s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.515752}
|
||||
{'loss': 1.37204351, 'grad_norm': 9.25, 'learning_rate': 3.19e-06, 'token_acc': 0.6391937, 'epoch': 0.64, 'global_step/max_steps': '60/94', 'percentage': '63.83%', 'elapsed_time': '1m 56s', 'remaining_time': '1m 5s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.516829}
|
||||
{'loss': 1.47697926, 'grad_norm': 11.375, 'learning_rate': 2.4e-06, 'token_acc': 0.62817259, 'epoch': 0.69, 'global_step/max_steps': '65/94', 'percentage': '69.15%', 'elapsed_time': '2m 5s', 'remaining_time': '55s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.517947}
|
||||
{'loss': 1.4336628, 'grad_norm': 8.125, 'learning_rate': 1.69e-06, 'token_acc': 0.63453862, 'epoch': 0.75, 'global_step/max_steps': '70/94', 'percentage': '74.47%', 'elapsed_time': '2m 14s', 'remaining_time': '46s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.518833}
|
||||
{'loss': 1.54315252, 'grad_norm': 9.625, 'learning_rate': 1.08e-06, 'token_acc': 0.60202073, 'epoch': 0.8, 'global_step/max_steps': '75/94', 'percentage': '79.79%', 'elapsed_time': '2m 24s', 'remaining_time': '36s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.519627}
|
||||
{'loss': 1.47180223, 'grad_norm': 9.5625, 'learning_rate': 6e-07, 'token_acc': 0.62211501, 'epoch': 0.85, 'global_step/max_steps': '80/94', 'percentage': '85.11%', 'elapsed_time': '2m 33s', 'remaining_time': '26s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.520284}
|
||||
{'loss': 1.44068375, 'grad_norm': 10.125, 'learning_rate': 2.5e-07, 'token_acc': 0.62673112, 'epoch': 0.91, 'global_step/max_steps': '85/94', 'percentage': '90.43%', 'elapsed_time': '2m 43s', 'remaining_time': '17s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.520331}
|
||||
{'loss': 1.44893646, 'grad_norm': 8.375, 'learning_rate': 5e-08, 'token_acc': 0.63837478, 'epoch': 0.96, 'global_step/max_steps': '90/94', 'percentage': '95.74%', 'elapsed_time': '2m 52s', 'remaining_time': '7s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.520707}
|
||||
{'train_runtime': 183.4332, 'train_samples_per_second': 8.177, 'train_steps_per_second': 0.512, 'train_loss': 1.50650934, 'token_acc': 0.6194337, 'epoch': 1.0, 'global_step/max_steps': '94/94', 'percentage': '100.00%', 'elapsed_time': '3m 3s', 'remaining_time': '0s', 'memory(GiB)': 6.5, 'train_speed(iter/s)': 0.512463}
|
||||
Train: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 94/94 [03:03<00:00, 1.95s/it]
|
||||
[INFO:swift] last_model_checkpoint: /workspace/ms-swift/output/v0-20260211-143035/checkpoint-94
|
||||
[INFO:swift] best_model_checkpoint: None
|
||||
[INFO:swift] images_dir: /workspace/ms-swift/output/v0-20260211-143035/images
|
||||
[INFO:swift] End time of running main: 2026-02-11 14:34:09.521336
|
||||
|
||||
```
|
||||
### 3.2. 使用 Megatron-LM 作为 Swift 后端
|
||||
若希望使用 Megatron-LM 作为 Swift 的后端,需设置 `MEGATRON_LM_PATH` 环境变量:
|
||||
|
||||
```bash
|
||||
export MEGATRON_LM_PATH=/workspace/Megatron-LM-0.15.0
|
||||
cd /workspace/ms-swift
|
||||
bash examples/megatron/pretrain.sh
|
||||
```
|
||||
|
||||
### 3.3. 使用其他版本的 ms-swift
|
||||
Metax 平台要求使用与 Maca 兼容的软件包。例如,编译可能依赖 torch2.8,因此需使用 torch2.8+maca3.3.x.x 版本。
|
||||
|
||||
默认情况下,安装会覆盖环境中已有的 PyTorch。因此,建议使用 --no-deps 参数进行安装:
|
||||
```bash
|
||||
|
||||
git clone -b ${SWIFT_VERSION} https://github.com/modelscope/ms-swift.git
|
||||
cd ms-swift
|
||||
pip install . --no-deps
|
||||
|
||||
```
|
||||
每次环境变更后,请检查 PyTorch 版本及其可用性:
|
||||
```bash
|
||||
pip list |grep torch
|
||||
# output:
|
||||
# torch2.x.x+metax3.x.x.x
|
||||
```
|
||||
|
||||
```python
|
||||
import torch
|
||||
torch.cuda.is_available()
|
||||
```
|
||||
|
||||
### 3.4. Metax 与 NVIDIA CUDA 的差异
|
||||
Metax 在大部分接口上与 NVIDIA 对齐,但在某些软件行为和环境变量上存在差异。
|
||||
|
||||
#### 3.4.1. MACA_MPS_MODE
|
||||
默认情况下,MACA 不允许多个进程共享同一块 GPU。如果 GPU 已被占用,则无法启动新进程。
|
||||
|
||||
如需启用类似 MPS(Multi-Process Service)的功能,需设置:`MACA_MPS_MODE=1`
|
||||
```bash
|
||||
# 运行其他脚本...
|
||||
export MACA_MPS_MODE=1
|
||||
cd /workspace/ms-swift/
|
||||
bash examples/train/full/train.sh
|
||||
```
|
||||
#### 3.4.2. MCCL_SOCKET_IFNAME GLOO_SOCKET_IFNAME & MCCL_IB_HCA
|
||||
在多节点训练时,建议设置以下环境变量以确保节点间通信正常:
|
||||
> MCCL_SOCKET_IFNAME:用于 MCCL 通信的网络接口
|
||||
> GLOO_SOCKET_IFNAME:用于 GLOO 通信的网络接口
|
||||
> MCCL_IB_HCA:指定使用的 InfiniBand 设备
|
||||
|
||||
可通过 ifconfig 和 mx-smi 确定所用网卡和 IB 设备:
|
||||
```bash
|
||||
ifconfig
|
||||
# output
|
||||
ens20f0np0: xxx
|
||||
inet: your node ip
|
||||
xxx
|
||||
...
|
||||
```
|
||||
```bash
|
||||
mx-smi topo -n
|
||||
# output
|
||||
mx-smi version: 2.2.9
|
||||
|
||||
=================== MetaX System Management Interface Log ===================
|
||||
Timestamp : Wed Feb 11 18:53:44 2026
|
||||
|
||||
Attached GPUs : 8
|
||||
Device link type matrix
|
||||
GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7 NIC0 NIC1 NIC2 NIC3 NIC4 NIC5 Node Affinity CPU Affinity
|
||||
GPU0 X MX MX MX NODE NODE NODE NODE PIX PIX NODE NODE SYS SYS 0 0-31,64-95
|
||||
GPU1 MX X MX MX NODE NODE NODE NODE PIX PIX NODE NODE SYS SYS 0 0-31,64-95
|
||||
GPU2 MX MX X MX NODE NODE NODE NODE PIX PIX NODE NODE SYS SYS 0 0-31,64-95
|
||||
GPU3 MX MX MX X NODE NODE NODE NODE PIX PIX NODE NODE SYS SYS 0 0-31,64-95
|
||||
GPU4 NODE NODE NODE NODE X MX MX MX NODE NODE PIX PIX SYS SYS 0 0-31,64-95
|
||||
GPU5 NODE NODE NODE NODE MX X MX MX NODE NODE PIX PIX SYS SYS 0 0-31,64-95
|
||||
GPU6 NODE NODE NODE NODE MX MX X MX NODE NODE PIX PIX SYS SYS 0 0-31,64-95
|
||||
GPU7 NODE NODE NODE NODE MX MX MX X NODE NODE PIX PIX SYS SYS 0 0-31,64-95
|
||||
NIC0 PIX PIX PIX PIX NODE NODE NODE NODE X PIX NODE NODE SYS SYS
|
||||
NIC1 PIX PIX PIX PIX NODE NODE NODE NODE PIX X NODE NODE SYS SYS
|
||||
NIC2 NODE NODE NODE NODE PIX PIX PIX PIX NODE NODE X PIX SYS SYS
|
||||
NIC3 NODE NODE NODE NODE PIX PIX PIX PIX NODE NODE PIX X SYS SYS
|
||||
NIC4 SYS SYS SYS SYS SYS SYS SYS SYS SYS SYS SYS SYS X PIX
|
||||
NIC5 SYS SYS SYS SYS SYS SYS SYS SYS SYS SYS SYS SYS PIX X
|
||||
|
||||
Legend:
|
||||
X = Self
|
||||
SYS = Connection traversing PCIe as well as the SMP interconnect between NUMA nodes (e.g., QPI/UPI)
|
||||
NODE = Connection traversing PCIe as well as the interconnect between PCIe Host Bridges within a NUMA node
|
||||
PHB = Connection traversing PCIe as well as a PCIe Host Bridge (typically the CPU)
|
||||
PXB = Connection traversing multiple PCIe bridges (without traversing the PCIe Host Bridge)
|
||||
PIX = Connection traversing at most a single PCIe bridge
|
||||
MX = Connection traversing MetaXLink
|
||||
ETH = Connection traversing Eth
|
||||
NA = Connection type is unknown
|
||||
|
||||
NIC Legend:
|
||||
|
||||
NIC0: mlx5_0
|
||||
NIC1: mlx5_1
|
||||
NIC2: mlx5_2
|
||||
NIC3: mlx5_3
|
||||
NIC4: mlx5_4
|
||||
NIC5: mlx5_5
|
||||
# 根据拓扑信息可知:
|
||||
# 1. GPU0–GPU3 与 NIC0/NIC1(即 mlx5_0, mlx5_1)通信
|
||||
# 2. GPU4–GPU7 与 NIC2/NIC3(即 mlx5_2, mlx5_3)通信
|
||||
|
||||
|
||||
```
|
||||
因此,推荐设置如下:
|
||||
`MCCL_SOCKET_IFNAME=ens20f0np0`
|
||||
`GLOO_SOCKET_IFNAME=ens20f0np0`
|
||||
`MCCL_IB_HCA=mlx5_0,mlx5_1,mlx5_2,mlx5_3`
|
||||
|
||||
```bash
|
||||
# node 1
|
||||
export MCCL_SOCKET_IFNAME=ens20f0np0
|
||||
export GLOO_SOCKET_IFNAME=ens20f0np0
|
||||
export MCCL_IB_HCA=mlx5_0,mlx5_1,mlx5_2,mlx5_3
|
||||
cd /workspace/ms-swift/
|
||||
bash examples/train/multi-node/torchrun/train_node1.sh
|
||||
```
|
||||
|
||||
```bash
|
||||
# node 2
|
||||
# 需修改脚本中的 master_addr 为节点1的IP
|
||||
export MCCL_SOCKET_IFNAME=ens20f0np0
|
||||
export GLOO_SOCKET_IFNAME=ens20f0np0
|
||||
export MCCL_IB_HCA=mlx5_0,mlx5_1,mlx5_2,mlx5_3
|
||||
cd /workspace/ms-swift/
|
||||
bash examples/train/multi-node/torchrun/train_node2.sh
|
||||
```
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
# 更多最佳实践
|
||||
|
||||
- [Qwen2.5自我认知微调](https://github.com/modelscope/ms-swift/tree/main/examples/notebook/qwen2_5-self-cognition)
|
||||
- [Qwen2-VL Latex-OCR微调](https://github.com/modelscope/ms-swift/tree/main/examples/notebook/qwen2vl-ocr)
|
||||
- [Qwen2.5-VL Grounding任务微调](https://github.com/modelscope/ms-swift/tree/main/examples/notebook/qwen2_5-vl-grounding)
|
||||
@@ -0,0 +1,849 @@
|
||||
# NPU支持
|
||||
|
||||
我们在 ms-swift 上增加了对昇腾 NPU 的支持,用户可以在昇腾 NPU 上进行模型的微调和推理。
|
||||
|
||||
本文档介绍如何在昇腾 NPU 上完成环境准备、模型训练、保存合并、推理、部署和常见问题排查。
|
||||
|
||||
如果你是第一次在 NPU 上使用 ms-swift,推荐按以下顺序阅读:
|
||||
|
||||
1. 先查看“支持范围速览”,确认模型、算法和后端是否已验证。
|
||||
2. 根据“选择你的使用路线”决定只装基础环境,还是额外安装 MindSpeed/Megatron-SWIFT。
|
||||
3. 根据自己的环境管理习惯选择“本地环境安装”或“镜像/容器环境安装”,然后执行“NPU 可用性检查”。
|
||||
4. 使用“快速跑通”完成一次 ModelScope 模型 LoRA 训练、合并、推理和部署。
|
||||
5. 需要更大规模训练时,再阅读 DDP、DeepSpeed 和 MindSpeed/Megatron-SWIFT 相关章节。
|
||||
|
||||
## 硬件配套和支持的操作系统
|
||||
|
||||
**表 1** 产品硬件支持列表
|
||||
|
||||
|产品|是否支持|
|
||||
|--|:-:|
|
||||
|<term>Ascend 950 系列产品</term>|√|
|
||||
|<term>Atlas A3 训练系列产品</term>|√|
|
||||
|<term>Atlas A3 推理系列产品</term>|x|
|
||||
|<term>Atlas A2 训练系列产品</term>|√|
|
||||
|<term>Atlas A2 推理系列产品</term>|x|
|
||||
|<term>Atlas 200I/500 A2 推理产品</term>|x|
|
||||
|<term>Atlas 推理系列产品</term>|x|
|
||||
|<term>Atlas 训练系列产品</term>|x|
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> 本节表格中“√”代表支持,“x”代表不支持。
|
||||
|
||||
- 各硬件产品对应物理机部署场景支持的操作系统请参考[兼容性查询助手](https://www.hiascend.com/hardware/compatibility)。
|
||||
- 各硬件产品对应虚拟机及容器部署场景支持的操作系统请参考《CANN 软件安装》的“[操作系统兼容性说明](https://www.hiascend.com/document/detail/zh/canncommercial/900/softwareinst/instg/instg_0101.html?OS=openEuler&InstallType=netyum)”章节(商用版)或“[操作系统兼容性说明](https://www.hiascend.com/document/detail/zh/CANNCommunityEdition/900/softwareinst/instg/instg_0101.html?OS=openEuler&InstallType=netyum)”章节(社区版)。
|
||||
|
||||
## 支持范围速览
|
||||
|
||||
推荐基础环境版本:
|
||||
|
||||
| software | version |
|
||||
| --------- | --------------- |
|
||||
| Python | >= 3.10, < 3.12 |
|
||||
| CANN | >= 8.5.1 |
|
||||
| torch | >= 2.7.1 |
|
||||
| torch_npu | >= 2.7.1.post4 |
|
||||
|
||||
基础环境准备请参照 [Ascend PyTorch 安装文档](https://gitcode.com/Ascend/pytorch)。本文示例实验环境为 8 * 昇腾910B3 64G。
|
||||
注:vllm ascend系列官方推荐版本配套已更新至 CANN9.0.0 torch 2.9.0 torch_npu 2.9.0 vllm-ascend 0.18.0(A3) 0.19.1(A5),详情请参阅 [vLLM Ascend 安装文档](https://docs.vllm.ai/projects/ascend/en/v0.18.0/installation.html)。
|
||||
|
||||
| 一级特性 | 特性 | 进展 |
|
||||
| -------- | ------------------- | -------- |
|
||||
| 训练范式 | CPT | 已支持 |
|
||||
| | SFT | 已支持 |
|
||||
| | DPO | 已支持 |
|
||||
| | RM | 已支持 |
|
||||
| 分布式 | DDP | 已支持 |
|
||||
| | FSDP | 已支持 |
|
||||
| | FSDP2 | 已支持 |
|
||||
| | DeepSpeed | 已支持 |
|
||||
| | MindSpeed(Megatron) | 已支持 |
|
||||
| 低参微调 | FULL | 已支持 |
|
||||
| | LoRA | 已支持 |
|
||||
| | QLoRA | 暂不支持 |
|
||||
| RLHF | GRPO | 已支持 |
|
||||
| | PPO | 已支持 |
|
||||
| 性能优化 | FA 等融合算子 | 已支持 |
|
||||
| | Liger-Kernel | 暂不支持 |
|
||||
| 部署 | PT | 已支持 |
|
||||
| | vLLM | 已支持 |
|
||||
| | SGLang | 暂不支持 |
|
||||
|
||||
### 已验证 SFT 组合
|
||||
|
||||
| algorithm | model families | strategy | hardware |
|
||||
| --------- | --------------------------- | --------------------- | ----------------- |
|
||||
| SFT | Qwen2.5-0.5B-Instruct | FSDP1/FSDP2/deepspeed | Atlas 900 A2 PODc |
|
||||
| SFT | Qwen2.5-1.5B-Instruct | FSDP1/FSDP2/deepspeed | Atlas 900 A2 PODc |
|
||||
| SFT | Qwen2.5-7B-Instruct | FSDP1/FSDP2/deepspeed | Atlas 900 A2 PODc |
|
||||
| SFT | Qwen2.5-VL-3B-Instruct | FSDP1/FSDP2/deepspeed | Atlas 900 A2 PODc |
|
||||
| SFT | Qwen2.5-VL-7B-Instruct | FSDP1/FSDP2/deepspeed | Atlas 900 A2 PODc |
|
||||
| SFT | Qwen2.5-Omni-3B | FSDP1/FSDP2/deepspeed | Atlas 900 A2 PODc |
|
||||
| SFT | Qwen3-8B | FSDP1/FSDP2/deepspeed | Atlas 900 A2 PODc |
|
||||
| SFT | Qwen3-30B-A3B | FSDP1/FSDP2/deepspeed | Atlas 900 A2 PODc |
|
||||
| SFT | Qwen3-32B | FSDP1/FSDP2/deepspeed | Atlas 900 A2 PODc |
|
||||
| SFT | Qwen3-VL-30B-A3B-Instruct | FSDP1/FSDP2/deepspeed | Atlas 900 A2 PODc |
|
||||
| SFT | Qwen3-Omni-30B-A3B-Instruct | FSDP1/FSDP2/deepspeed/Megatron | Atlas 900 A2 PODc/A3 SuperPoD |
|
||||
| SFT | InternVL3-8B | FSDP1/FSDP2/deepspeed | Atlas 900 A2 PODc |
|
||||
| SFT | Ovis2.5-2B | FSDP1/FSDP2/deepspeed | Atlas 900 A2 PODc |
|
||||
| SFT | Qwen3.5-27B | FSDP1/FSDP2/deepspeed/Megatron | Atlas 900 A2 PODc/A3 SuperPoD |
|
||||
| SFT | Qwen3.5-35B-A3B | FSDP1/FSDP2/deepspeed/Megatron | Atlas 900 A2 PODc/A3 SuperPoD |
|
||||
|
||||
### 已验证 RL 组合
|
||||
|
||||
| algorithm | model families | strategy | rollout engine | hardware |
|
||||
| --------- | ------------------- | --------- | -------------- | ----------------- |
|
||||
| **GRPO** | Qwen2.5-7B-Instruct | deepspeed | vllm-ascend | Atlas 900 A2 PODc |
|
||||
| **GRPO** | Qwen3-8B | deepspeed | vllm-ascend | Atlas 900 A2 PODc |
|
||||
| **DPO** | Qwen2.5-7B-Instruct | deepspeed | vllm-ascend | Atlas 900 A2 PODc |
|
||||
| **DPO** | Qwen3-8B | deepspeed | vllm-ascend | Atlas 900 A2 PODc |
|
||||
| **PPO** | Qwen2.5-7B-Instruct | deepspeed | vllm-ascend | Atlas 900 A2 PODc |
|
||||
| **PPO** | Qwen3-8B | deepspeed | vllm-ascend | Atlas 900 A2 PODc |
|
||||
|
||||
### 暂不支持或未完全验证
|
||||
|
||||
| item |
|
||||
| --------------------------------- |
|
||||
| Liger-kernel |
|
||||
| 量化/QLoRA相关 |
|
||||
| 使用sglang作为推理引擎 |
|
||||
| 使用megatron时开启ETP进行lora训练 |
|
||||
|
||||
### PEFT Transformers 5 MoE fused expert LoRA 限制
|
||||
|
||||
在使用 Qwen3.5-MoE、Qwen3-Omni-MoE 等 Transformers 5 MoE 结构模型进行 LoRA 训练时,部分 expert 权重可能不是普通 `nn.Linear` 模块,而是 fused `nn.Parameter`。这类参数需要依赖 PEFT 的 `target_parameters` 路径注入 LoRA。
|
||||
|
||||
当前该路径在 `lora_dropout`、ZeRO-3/FSDP、多 adapter 等组合场景下仍未完全稳定。典型触发条件包括:
|
||||
|
||||
- 使用 MoE 模型;
|
||||
- 使用 LoRA,并希望覆盖 fused expert 参数;
|
||||
- 模型配置或命令行 `--model_type` 触发 PEFT 的 Transformers 5 MoE target conversion 路径;
|
||||
- 使用默认 `lora_dropout != 0`,或使用 ZeRO-3/FSDP 等参数分片后端。
|
||||
|
||||
如果只是进行常规 Qwen3.5 GRPO/SFT LoRA 训练,建议避免额外指定 `--model_type` 去扩大触发范围;若模型配置本身已经触发该路径,则优先使用 full 参数训练或关闭对应 LoRA 组合。若确实需要训练 fused expert 参数,建议等待 PEFT 上游能力稳定,或在 `lora_dropout=0` 且目标模型、训练后端已单独验证的前提下使用。
|
||||
|
||||
## 选择你的使用路线
|
||||
|
||||
| 场景 | 推荐路线 | 是否需要 MindSpeed |
|
||||
| ---------------------------- | --------------------------------------------- | ------------------ |
|
||||
| 只做普通 SFT/LoRA/推理 | 本地环境安装或镜像/容器环境安装 | 不需要 |
|
||||
| 需要 Megatron-SWIFT 大模型训练 | 先装基础环境,再装 MindSpeed/Megatron/mcore-bridge | 需要 |
|
||||
| 需要 GRPO/PPO/DPO 等 RLHF | 基础训练环境 + vLLM-Ascend rollout/deploy | 通常不需要 |
|
||||
| 只是验证 NPU 是否可用 | 跑 NPU 可用性检查脚本 | 不需要 |
|
||||
|
||||
## 环境准备
|
||||
|
||||
### 镜像/容器环境安装
|
||||
官方 NPU 镜像已发布在 [quay.io/ascend/ms-swift](https://quay.io/repository/ascend/ms-swift?tab=tags)。推荐优先根据设备代际、Python、CANN 和系统版本选择匹配的镜像标签;如需锁定分支或定制依赖,再使用项目提供的 Dockerfile 自行构建。容器方式的优势是依赖版本更容易固化,也便于在多台昇腾机器之间复现实验环境。
|
||||
|
||||
下面以 A2、Python 3.11、CANN 9.0.0、Ubuntu 22.04 标签为例,实际使用时请以 Quay 标签页中适配当前机器和软件栈的最新标签为准:
|
||||
|
||||
```shell
|
||||
docker pull quay.io/ascend/ms-swift:v4.3.0-A2-py311-CANN9.0.0-ubuntu22.04
|
||||
export IMAGE_NAME=quay.io/ascend/ms-swift:v4.3.0-A2-py311-CANN9.0.0-ubuntu22.04
|
||||
export WORKSPACE=/path/to/workspace
|
||||
```
|
||||
|
||||
如果需要自行构建镜像,先 clone modelscope 仓库,然后使用仓库中的 [Dockerfile.ascend](https://github.com/modelscope/modelscope/blob/master/docker/Dockerfile.ascend) 和 [build_image.py](https://github.com/modelscope/modelscope/blob/master/docker/build_image.py):
|
||||
|
||||
```shell
|
||||
git clone https://github.com/modelscope/modelscope.git
|
||||
cd modelscope
|
||||
DOCKER_REGISTRY=ms-swift python docker/build_image.py \
|
||||
--image_type ascend \
|
||||
--python_version 3.11.11 \
|
||||
--soc_version ascend910b1 \
|
||||
--arch arm
|
||||
```
|
||||
|
||||
当前 `build_image.py` 生成的 Ascend 镜像名格式为 `{DOCKER_REGISTRY}:{swift_branch}-{atlas_hardware}-{python_tag}-{arch}`。以上命令以 ARM 架构的 Atlas 900 A2 PODc 为例,通常会生成 `ms-swift:main-A2-py311-arm`。如果使用自行构建的镜像,请按构建日志中的镜像名替换上面的 `IMAGE_NAME`:
|
||||
|
||||
```shell
|
||||
export IMAGE_NAME=ms-swift:main-A2-py311-arm
|
||||
```
|
||||
|
||||
启动容器前建议先确认宿主机暴露的 NPU 设备:
|
||||
|
||||
```shell
|
||||
ls /dev/davinci*
|
||||
```
|
||||
|
||||
启动容器时需要把 NPU 设备、驱动、固件、`npu-smi` 和必要日志目录挂载进去。下面示例按常见 8 卡设备 `davinci0` 到 `davinci7` 编写;部分机器会额外暴露到 `davinci15`,这时请按 `ls /dev/davinci*` 的结果把对应设备都加到 `docker run` 中:
|
||||
|
||||
```shell
|
||||
docker run -it \
|
||||
--name swift-ascend \
|
||||
--network=host --ipc=host --shm-size=128g \
|
||||
--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 \
|
||||
-v /usr/local/Ascend/driver:/usr/local/Ascend/driver:ro \
|
||||
-v /usr/local/Ascend/firmware:/usr/local/Ascend/firmware:ro \
|
||||
-v /usr/local/sbin/npu-smi:/usr/local/sbin/npu-smi:ro \
|
||||
-v /etc/ascend_install.info:/etc/ascend_install.info:ro \
|
||||
-v /var/log/npu:/var/log/npu \
|
||||
-v ${WORKSPACE}:/workspace \
|
||||
${IMAGE_NAME} \
|
||||
/bin/bash
|
||||
```
|
||||
|
||||
进入容器后,建议先执行 `source /usr/local/Ascend/ascend-toolkit/set_env.sh`,再运行后文的 NPU 可用性检查脚本,确认容器内可以正确访问昇腾设备。如果容器内无法识别 NPU,请优先检查 `/dev/davinci*`、`/dev/davinci_manager`、驱动目录和 `npu-smi` 是否正确挂载。
|
||||
|
||||
### 本地环境安装
|
||||
```shell
|
||||
# 创建新的 conda 虚拟环境(可选)
|
||||
conda create -n swift-npu python=3.11 -y
|
||||
conda activate swift-npu
|
||||
|
||||
# 注意进行后续操作前要先 source 激活 CANN 环境
|
||||
source /usr/local/Ascend/ascend-toolkit/set_env.sh
|
||||
|
||||
# 设置 pip 全局镜像(可选,加速下载)
|
||||
pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
|
||||
pip install ms-swift -U
|
||||
|
||||
# 使用源码安装
|
||||
git clone https://github.com/modelscope/ms-swift.git
|
||||
cd ms-swift
|
||||
pip install -e .
|
||||
|
||||
# 安装 torch_npu
|
||||
pip install torch_npu==2.9.0 decorator
|
||||
# 如果你想要使用 deepspeed(控制显存占用,训练速度会有一定下降)
|
||||
pip install deepspeed
|
||||
|
||||
# 如果需要使用 evaluation 功能,请安装以下包
|
||||
pip install evalscope[opencompass]
|
||||
|
||||
# 如果需要使用 vllm-ascend 进行推理,请安装以下包(更多版本请参考 [vLLM-Ascend 官网](https://docs.vllm.ai/projects/ascend/en/latest/installation.html))
|
||||
pip install vllm==0.18.0
|
||||
pip install vllm-ascend==0.18.0
|
||||
```
|
||||
|
||||
### NPU 可用性检查
|
||||
|
||||
测试环境是否安装正确,NPU能否被正常加载:
|
||||
|
||||
```python
|
||||
from transformers.utils import is_torch_npu_available
|
||||
import torch
|
||||
|
||||
print(is_torch_npu_available()) # True
|
||||
print(torch.npu.device_count()) # 8
|
||||
print(torch.randn(10, device='npu:0'))
|
||||
```
|
||||
|
||||
### MindSpeed/Megatron-SWIFT 可选安装
|
||||
|
||||
如果需要使用 MindSpeed(Megatron-LM),请按照下面引导安装必要依赖。
|
||||
|
||||
```shell
|
||||
# 1. 获取并切换 Megatron-LM 至 v0.16.0 版本
|
||||
git clone https://github.com/NVIDIA/Megatron-LM.git
|
||||
cd Megatron-LM
|
||||
git checkout core_v0.16.0
|
||||
cd ..
|
||||
|
||||
# 2. 获取并安装 MindSpeed
|
||||
git clone https://gitcode.com/Ascend/MindSpeed.git
|
||||
cd MindSpeed
|
||||
git checkout core_r0.16.0
|
||||
pip install -e .
|
||||
cd ..
|
||||
|
||||
# 3. 获取并安装 mcore-bridge
|
||||
git clone https://github.com/modelscope/mcore-bridge.git
|
||||
cd mcore-bridge
|
||||
pip install -e .
|
||||
cd ..
|
||||
|
||||
# 4. 获取并安装 triton-ascend
|
||||
pip install triton-ascend==3.2.1 --extra-index-url=https://triton-ascend.osinfra.cn/pypi/simple
|
||||
|
||||
# 5. 设置环境变量
|
||||
export PYTHONPATH=$PYTHONPATH:<your_local_megatron_lm_path>
|
||||
export MEGATRON_LM_PATH=<your_local_megatron_lm_path>
|
||||
|
||||
# 6. 如需回退到 transformers 的 GatedDeltaNet 实现,可关闭 Megatron GDN
|
||||
export USE_MCORE_GDN=0
|
||||
```
|
||||
|
||||
执行如下命令验证 MindSpeed(Megatron-LM) 是否配置成功:
|
||||
```shell
|
||||
python -c "import mindspeed.megatron_adaptor; from swift.megatron.init import init_megatron_env; init_megatron_env(); print('✓ NPU环境下的Megatron-SWIFT配置验证成功!')"
|
||||
```
|
||||
|
||||
### Qwen3.5 FLA补丁说明
|
||||
|
||||
当前仓库已经内置了面向昇腾 NPU 的 Qwen3.5 linear attention patch,无需用户再额外修改 `transformers` 或 `fla` 源码。该 patch 的目标不是直接替换整个 `flash-linear-attention` 包,而是在 `Qwen3.5` 实际调用的 `chunk_gated_delta_rule` 路径上,将底层 GPU Triton 算子重定向到 MindSpeed 的 NPU 实现。
|
||||
|
||||
补丁生效时,ms-swift 会执行以下替换:
|
||||
|
||||
1. 将 `transformers.utils.is_flash_linear_attention_available` 与 `transformers.utils.import_utils.is_flash_linear_attention_available` 置为 `True`,使 `transformers.models.qwen3_5.modeling_qwen3_5` 可以按 FLA fast path 完成初始化。
|
||||
2. 将 `transformers.models.qwen3_5.modeling_qwen3_5.chunk_gated_delta_rule` 以及 `transformers.models.qwen3_5_moe.modeling_qwen3_5_moe.chunk_gated_delta_rule` 重定向到 ms-swift 内置实现 `swift.model.chunk_gated_delta_rule.chunk_gated_delta_rule`。
|
||||
3. `swift.model.chunk_gated_delta_rule` 内部继续调用 MindSpeed 提供的原生 Triton 算子,包括:
|
||||
- `mindspeed.ops.triton.chunk_delta_h`
|
||||
- `mindspeed.ops.triton.chunk_o`
|
||||
- `mindspeed.ops.triton.chunk_scaled_dot_kkt`
|
||||
- `mindspeed.ops.triton.cumsum`
|
||||
- `mindspeed.ops.triton.solve_tril`
|
||||
- `mindspeed.ops.triton.wy_fast`
|
||||
4. 保留了 torch 原生 l2norm 小算子实现,减轻每层每步的 launch 开销以及冷启动中的 compile/autotune 开销,提升模型在 NPU 上的性能表现。
|
||||
5. 对于 FLA 中依赖 `torch.cuda.current_device()` 初始化的 `FusedRMSNormGated`,NPU 上会保留 Qwen3.5 的原生 torch 路径,避免 CUDA-only 初始化逻辑带来的兼容性问题。
|
||||
|
||||
可以将这条调用链理解为:
|
||||
|
||||
```text
|
||||
Qwen3.5 modeling.chunk_gated_delta_rule
|
||||
-> swift.model.chunk_gated_delta_rule.chunk_gated_delta_rule
|
||||
-> MindSpeed Triton kernels
|
||||
```
|
||||
|
||||
因此:
|
||||
|
||||
- 该 patch 主要覆盖的是 **Qwen3.5 linear attention 的 gated-delta-rule 路径**;
|
||||
- 它并不等价于“将整个 fla 包完整替换为 MindSpeed”;
|
||||
- 若需要这条路径生效,请确保当前环境中可以正确导入 MindSpeed 和 triton ascend
|
||||
- 精度对齐验证版本:torch 2.9.0 + MindSpeed 0.16.0 + flash-linear-attention 0.4.2 + triton-ascend 3.2.1 + transformers 5.2.0
|
||||
|
||||
|
||||
当前 Qwen3.5 在 NPU 上如果走 transformers 后端或 Megatron-SWIFT 后端训练,还需要额外注意版本和功能约束:
|
||||
|
||||
1. 当前 NPU 文档中约定的 MindSpeed 训练组合是 `Megatron-LM v0.16.0 + MindSpeed core_r0.16.0`。在这个组合下,`megatron-core` 已包含 `core.ssm.gated_delta_net` 原生 GDN 内核,`mcore-bridge` 默认会按 `USE_MCORE_GDN=1` 走 Megatron-Core/MindSpeed GDN 路径。若显式设置 `USE_MCORE_GDN=0`,则会回退到由 `mcore-bridge` 包装的 transformers 版 GDN,并配合 ms-swift 内置的 Qwen3.5 FLA NPU 补丁,把 `chunk_gated_delta_rule` 重定向到 MindSpeed Triton 算子。
|
||||
|
||||
2. 目前无论使用 transformers 后端还是 Megatron-SWIFT 后端,也无论 Megatron-SWIFT 下使用 `USE_MCORE_GDN=1` 还是 `USE_MCORE_GDN=0`,都不要在 Qwen3.5 的 NPU 路径上开启序列相关特性,包括 SP(sequence parallel,序列并行)、CP(context parallel,上下文并行)或 packing/padding-free。相关 FLA Triton 算子在 NPU 侧还没有完整的原生支持,开启这类特性可能触发算子缺失、样本边界处理不完整或并行切分不匹配问题。
|
||||
|
||||
3. 因此当前建议:transformers 后端避免设置 `--sequence_parallel_size` 大于 `1`,并避免使用 `--packing true` / `--padding_free true`;Megatron-SWIFT 后端`--context_parallel_size` 保持为 `1`,并同样避免使用 `--packing true` / `--padding_free true`。只有在目标 MindSpeed/FLA 版本明确补齐支持并完成分层验证后,才重新开启这些特性。
|
||||
|
||||
### 环境查看
|
||||
|
||||
查看NPU的P2P连接,这里看到每个NPU都通过7条HCCS与其他NPU互联
|
||||
|
||||
```shell
|
||||
(valle) root@valle:~/src# npu-smi info -t topo
|
||||
NPU0 NPU1 NPU2 NPU3 NPU4 NPU5 NPU6 NPU7 CPU Affinity
|
||||
NPU0 X HCCS HCCS HCCS HCCS HCCS HCCS HCCS 144-167
|
||||
NPU1 HCCS X HCCS HCCS HCCS HCCS HCCS HCCS 144-167
|
||||
NPU2 HCCS HCCS X HCCS HCCS HCCS HCCS HCCS 96-119
|
||||
NPU3 HCCS HCCS HCCS X HCCS HCCS HCCS HCCS 96-119
|
||||
NPU4 HCCS HCCS HCCS HCCS X HCCS HCCS HCCS 0-23
|
||||
NPU5 HCCS HCCS HCCS HCCS HCCS X HCCS HCCS 0-23
|
||||
NPU6 HCCS HCCS HCCS HCCS HCCS HCCS X HCCS 48-71
|
||||
NPU7 HCCS HCCS HCCS HCCS HCCS HCCS HCCS X 48-71
|
||||
|
||||
Legend:
|
||||
|
||||
X = Self
|
||||
SYS = Path traversing PCIe and NUMA nodes. Nodes are connected through SMP, such as QPI, UPI.
|
||||
PHB = Path traversing PCIe and the PCIe host bridge of a CPU.
|
||||
PIX = Path traversing a single PCIe switch
|
||||
PXB = Path traversing multiple PCIe switches
|
||||
HCCS = Connection traversing HCCS.
|
||||
NA = Unknown relationship.
|
||||
```
|
||||
|
||||
查看NPU状态, npu-smi命令详解可以查看[官方文档](https://support.huawei.com/enterprise/zh/doc/EDOC1100079287/10dcd668)
|
||||
|
||||
```shell
|
||||
(valle) root@valle:~/src# npu-smi info
|
||||
+------------------------------------------------------------------------------------------------+
|
||||
| npu-smi 24.1.rc1.b030 Version: 24.1.rc1.b030 |
|
||||
+---------------------------+---------------+----------------------------------------------------+
|
||||
| NPU Name | Health | Power(W) Temp(C) Hugepages-Usage(page)|
|
||||
| Chip | Bus-Id | AICore(%) Memory-Usage(MB) HBM-Usage(MB) |
|
||||
+===========================+===============+====================================================+
|
||||
| 0 910B3 | OK | 101.8 43 0 / 0 |
|
||||
| 0 | 0000:C1:00.0 | 0 0 / 0 3318 / 65536 |
|
||||
+===========================+===============+====================================================+
|
||||
| 1 910B3 | OK | 92.0 39 0 / 0 |
|
||||
| 0 | 0000:C2:00.0 | 0 0 / 0 3314 / 65536 |
|
||||
+===========================+===============+====================================================+
|
||||
| 2 910B3 | OK | 102.0 40 0 / 0 |
|
||||
| 0 | 0000:81:00.0 | 0 0 / 0 3314 / 65536 |
|
||||
+===========================+===============+====================================================+
|
||||
| 3 910B3 | OK | 99.8 40 0 / 0 |
|
||||
| 0 | 0000:82:00.0 | 0 0 / 0 3314 / 65536 |
|
||||
+===========================+===============+====================================================+
|
||||
| 4 910B3 | OK | 98.6 45 0 / 0 |
|
||||
| 0 | 0000:01:00.0 | 0 0 / 0 3314 / 65536 |
|
||||
+===========================+===============+====================================================+
|
||||
| 5 910B3 | OK | 99.7 44 0 / 0 |
|
||||
| 0 | 0000:02:00.0 | 0 0 / 0 3314 / 65536 |
|
||||
+===========================+===============+====================================================+
|
||||
| 6 910B3 | OK | 103.8 45 0 / 0 |
|
||||
| 0 | 0000:41:00.0 | 0 0 / 0 3314 / 65536 |
|
||||
+===========================+===============+====================================================+
|
||||
| 7 910B3 | OK | 98.2 44 0 / 0 |
|
||||
| 0 | 0000:42:00.0 | 0 0 / 0 3315 / 65536 |
|
||||
+===========================+===============+====================================================+
|
||||
```
|
||||
|
||||
## 快速跑通:ModelScope 模型 + 数据集
|
||||
|
||||
如果你想先用 ModelScope 上的模型和数据集快速验证环境,可以直接执行本节完成一次完整闭环:训练 LoRA、找到最新 checkpoint、Merge LoRA、命令行推理、启动服务、curl 验证。示例使用小模型和小规模采样,便于快速跑通;换成自己的模型或数据集时,只需要修改前面的 ID 变量。
|
||||
|
||||
```shell
|
||||
export MODEL_ID=Qwen/Qwen3-0.6B
|
||||
export DATASET_ID=AI-ModelScope/alpaca-gpt4-data-zh
|
||||
export WORK_DIR=output/npu-modelscope-qwen3-0_6b-lora
|
||||
```
|
||||
|
||||
训练并保存 LoRA checkpoint:
|
||||
|
||||
```shell
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 \
|
||||
swift sft \
|
||||
--model $MODEL_ID \
|
||||
--dataset $DATASET_ID#1000 \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--torch_dtype bfloat16 \
|
||||
--tuner_type lora \
|
||||
--target_modules all-linear \
|
||||
--lora_rank 8 \
|
||||
--lora_alpha 32 \
|
||||
--num_train_epochs 1 \
|
||||
--per_device_train_batch_size 1 \
|
||||
--per_device_eval_batch_size 1 \
|
||||
--gradient_accumulation_steps 8 \
|
||||
--learning_rate 1e-4 \
|
||||
--max_length 2048 \
|
||||
--save_steps 100 \
|
||||
--eval_steps 100 \
|
||||
--save_total_limit 2 \
|
||||
--logging_steps 10 \
|
||||
--output_dir $WORK_DIR
|
||||
```
|
||||
|
||||
训练结束后,checkpoint 会保存在 `$WORK_DIR/*/checkpoint-*` 目录下。可以用下面的命令取最新 checkpoint,并将 LoRA 合并保存为完整模型权重:
|
||||
|
||||
```shell
|
||||
export CKPT_DIR=$(ls -dt $WORK_DIR/*/checkpoint-* | head -n 1)
|
||||
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 \
|
||||
swift export \
|
||||
--adapters $CKPT_DIR \
|
||||
--merge_lora true
|
||||
|
||||
export MERGED_DIR=${CKPT_DIR}-merged
|
||||
```
|
||||
|
||||
推理验证可以直接加载 LoRA checkpoint,也可以加载合并后的完整权重:
|
||||
|
||||
```shell
|
||||
# 直接加载 LoRA checkpoint
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--adapters $CKPT_DIR \
|
||||
--stream true \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 512
|
||||
|
||||
# 加载 Merge 后的完整权重
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--model $MERGED_DIR \
|
||||
--stream true \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 512
|
||||
```
|
||||
|
||||
如果需要启动 OpenAI 兼容的部署服务,可以使用合并后的完整权重:
|
||||
|
||||
```shell
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 \
|
||||
swift deploy \
|
||||
--model $MERGED_DIR \
|
||||
--host 0.0.0.0 \
|
||||
--port 8000 \
|
||||
--max_new_tokens 512 \
|
||||
--served_model_name npu-modelscope-qwen3-0_6b
|
||||
```
|
||||
|
||||
服务启动后,用 curl 验证接口:
|
||||
|
||||
```shell
|
||||
curl http://localhost:8000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "npu-modelscope-qwen3-0_6b",
|
||||
"messages": [{"role": "user", "content": "用一句话介绍昇腾NPU。"}],
|
||||
"max_tokens": 128,
|
||||
"temperature": 0
|
||||
}'
|
||||
```
|
||||
|
||||
## 训练
|
||||
|
||||
以下介绍LoRA的微调, 全参数微调设置参数`--tuner_type full`即可. **更多训练脚本**参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/ascend/train)。如果需要了解预训练、SFT、LoRA、全参数训练、自定义数据集等通用能力,可以继续阅读[预训练与微调文档](../Instruction/Pre-training-and-Fine-tuning.md)。
|
||||
|
||||
|
||||
| 模型大小 | NPU数量 | deepspeed类型 | 最大显存占用量 |
|
||||
| -------- | ------- | ------------- | -------------- |
|
||||
| 7B | 1 | None | 1 * 28 GB |
|
||||
| 7B | 4 | None | 4 * 22 GB |
|
||||
| 7B | 4 | zero2 | 4 * 28 GB |
|
||||
| 7B | 4 | zero3 | 4 * 22 GB |
|
||||
| 7B | 8 | None | 8 * 22 GB |
|
||||
| 14B | 1 | None | 1 * 45 GB |
|
||||
| 14B | 8 | None | 8 * 51 GB |
|
||||
| 14B | 8 | zero2 | 8 * 49 GB |
|
||||
| 14B | 8 | zero3 | 8 * 31 GB |
|
||||
|
||||
### 单卡训练
|
||||
|
||||
通过如下命令启动单卡微调:
|
||||
|
||||
```shell
|
||||
# 实验环境: 昇腾910B3
|
||||
# 显存需求: 28 GB
|
||||
# 运行时长: 8小时
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 \
|
||||
swift sft \
|
||||
--model Qwen/Qwen2-7B-Instruct \
|
||||
--dataset AI-ModelScope/blossom-math-v2 \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--num_train_epochs 5 \
|
||||
--tuner_type lora \
|
||||
--output_dir output \
|
||||
--learning_rate 1e-4 \
|
||||
--gradient_accumulation_steps 16 \
|
||||
--save_steps 100 \
|
||||
--eval_steps 100
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 数据并行训练
|
||||
|
||||
我们使用其中的4卡进行ddp训练
|
||||
|
||||
```shell
|
||||
# 实验环境: 4 * 昇腾910B3
|
||||
# 显存需求: 4 * 22 GB
|
||||
# 运行时长: 2小时
|
||||
NPROC_PER_NODE=4 \
|
||||
ASCEND_RT_VISIBLE_DEVICES=0,1,2,3 \
|
||||
swift sft \
|
||||
--model Qwen/Qwen2-7B-Instruct \
|
||||
--dataset AI-ModelScope/blossom-math-v2 \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--num_train_epochs 5 \
|
||||
--tuner_type lora \
|
||||
--output_dir output \
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
### Deepspeed训练
|
||||
|
||||
ZeRO2:
|
||||
|
||||
```shell
|
||||
# 实验环境: 4 * 昇腾910B3
|
||||
# 显存需求: 4 * 28GB
|
||||
# 运行时长: 3.5小时
|
||||
NPROC_PER_NODE=4 \
|
||||
ASCEND_RT_VISIBLE_DEVICES=0,1,2,3 \
|
||||
swift sft \
|
||||
--model Qwen/Qwen2-7B-Instruct \
|
||||
--dataset AI-ModelScope/blossom-math-v2 \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--num_train_epochs 5 \
|
||||
--tuner_type lora \
|
||||
--output_dir output \
|
||||
--deepspeed zero2 \
|
||||
...
|
||||
```
|
||||
|
||||
ZeRO3:
|
||||
|
||||
```shell
|
||||
# 实验环境: 4 * 昇腾910B3
|
||||
# 显存需求: 4 * 22 GB
|
||||
# 运行时长: 8.5小时
|
||||
NPROC_PER_NODE=4 \
|
||||
ASCEND_RT_VISIBLE_DEVICES=0,1,2,3 \
|
||||
swift sft \
|
||||
--model Qwen/Qwen2-7B-Instruct \
|
||||
--dataset AI-ModelScope/blossom-math-v2 \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--num_train_epochs 5 \
|
||||
--tuner_type lora \
|
||||
--output_dir output \
|
||||
--deepspeed zero3 \
|
||||
...
|
||||
```
|
||||
|
||||
### Qwen3.5 单机多卡 LoRA 示例
|
||||
|
||||
下面给出一个更新模型的 NPU LoRA 示例。这里使用 Qwen3.5-4B 做演示,4 卡数据并行通常比单卡更快;如果本地已经下载好模型和数据集,可以把 `--model`、`--dataset` 替换成本地路径。
|
||||
|
||||
```shell
|
||||
# 实验环境: 4 * 昇腾910B3
|
||||
NPROC_PER_NODE=4 \
|
||||
ASCEND_RT_VISIBLE_DEVICES=0,1,2,3 \
|
||||
swift sft \
|
||||
--model Qwen/Qwen3.5-4B \
|
||||
--dataset AI-ModelScope/alpaca-gpt4-data-zh#2000 \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--torch_dtype bfloat16 \
|
||||
--num_train_epochs 1 \
|
||||
--tuner_type lora \
|
||||
--target_modules all-linear \
|
||||
--lora_rank 8 \
|
||||
--lora_alpha 32 \
|
||||
--per_device_train_batch_size 1 \
|
||||
--per_device_eval_batch_size 1 \
|
||||
--gradient_accumulation_steps 8 \
|
||||
--learning_rate 1e-4 \
|
||||
--max_length 2048 \
|
||||
--group_by_length true \
|
||||
--dataset_num_proc 4 \
|
||||
--dataloader_num_workers 4 \
|
||||
--save_steps 100 \
|
||||
--eval_steps 100 \
|
||||
--save_total_limit 2 \
|
||||
--output_dir output/Qwen3.5-4B-NPU
|
||||
```
|
||||
|
||||
调参时可以先抓住三个目标:显存、吞吐和稳定性。
|
||||
|
||||
- 降低显存:优先减小 `--max_length`、`--per_device_train_batch_size` 和 `--lora_rank`;仍然 OOM 时再启用 `--deepspeed zero2/zero3`。ZeRO 可以明显降低显存压力,但会增加通信和调度开销。
|
||||
- 提高吞吐:在显存允许的情况下增大 `--per_device_train_batch_size`,再用 `--gradient_accumulation_steps` 保持全局 batch size;数据预处理较慢时增加 `--dataset_num_proc`,数据读取跟不上时增加 `--dataloader_num_workers`。
|
||||
- 控制保存成本:`--save_steps` 不宜过小,否则频繁保存会拖慢训练;`--save_total_limit 2` 通常足够保留 best checkpoint 和 last checkpoint。
|
||||
- 提高稳定性:NPU 上建议优先使用 `bfloat16`;如果遇到 loss 异常或 NaN,可以先缩小学习率、降低 batch,必要时再临时切到 `float32` 做对照定位。
|
||||
|
||||
更多参数含义可以在[命令行参数文档](../Instruction/Command-line-parameters.md)中查询。
|
||||
|
||||
### NPU模型Patch开关
|
||||
|
||||
ms-swift 在 NPU 环境下默认会启用模型层 patch,以适配部分 Transformers 模型在昇腾 NPU 上的算子和兼容性需求。通常不需要关闭;如果怀疑某个模型的 loss 异常、forward 报错与 NPU 模型 patch 有关,需要临时切回 Transformers 原生实现做对比,可以设置:
|
||||
|
||||
```shell
|
||||
swift sft ... --enable_npu_model_patch false
|
||||
```
|
||||
|
||||
## 模型保存、Merge LoRA 和断点续训
|
||||
|
||||
训练时通过 `--output_dir` 指定输出目录,通过 `--save_steps` 控制 checkpoint 保存间隔,通过 `--save_total_limit` 控制最多保留多少个 checkpoint。LoRA 训练结束后,checkpoint 目录中会保存 adapter 权重、训练参数和 trainer 状态;常见目录形态如下:
|
||||
|
||||
```text
|
||||
output/Qwen3.5-4B-NPU/vx-xxx/
|
||||
├── checkpoint-100/
|
||||
├── checkpoint-200/
|
||||
└── ...
|
||||
```
|
||||
|
||||
如果只做推理或继续 LoRA 训练,可以直接使用 checkpoint 目录。若希望得到一个独立的完整模型目录,便于 vLLM-Ascend 部署、离线分发或后续量化,可以执行 Merge LoRA:
|
||||
|
||||
```shell
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 \
|
||||
swift export \
|
||||
--adapters output/Qwen3.5-4B-NPU/vx-xxx/checkpoint-xxx \
|
||||
--merge_lora true
|
||||
```
|
||||
|
||||
合并后的模型默认保存在 `checkpoint-xxx-merged` 目录。之后可以像加载普通模型一样使用 `--model checkpoint-xxx-merged`。
|
||||
|
||||
如果训练中断,需要从 checkpoint 恢复训练,请保持原训练参数不变,只额外增加 `--resume_from_checkpoint`:
|
||||
|
||||
```shell
|
||||
NPROC_PER_NODE=4 \
|
||||
ASCEND_RT_VISIBLE_DEVICES=0,1,2,3 \
|
||||
swift sft \
|
||||
--model Qwen/Qwen3.5-4B \
|
||||
--dataset AI-ModelScope/alpaca-gpt4-data-zh#2000 \
|
||||
--tuner_type lora \
|
||||
--output_dir output/Qwen3.5-4B-NPU \
|
||||
--resume_from_checkpoint output/Qwen3.5-4B-NPU/vx-xxx/checkpoint-xxx \
|
||||
...
|
||||
```
|
||||
|
||||
`--resume_from_checkpoint` 会恢复模型权重、优化器状态、随机种子和训练进度。如果只想加载模型权重而不恢复优化器和数据跳过状态,可以额外设置 `--resume_only_model true`。相关参数可参考[命令行参数文档](../Instruction/Command-line-parameters.md)中的 `resume_from_checkpoint`、`resume_only_model`、`save_steps` 和 `save_total_limit`。
|
||||
|
||||
## 推理
|
||||
|
||||
原始模型:
|
||||
|
||||
```shell
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 swift infer \
|
||||
--model Qwen/Qwen2-7B-Instruct \
|
||||
--stream true --max_new_tokens 2048
|
||||
```
|
||||
|
||||
LoRA微调后:
|
||||
|
||||
```shell
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 swift infer \
|
||||
--adapters xxx/checkpoint-xxx --load_data_args true \
|
||||
--stream true --max_new_tokens 2048
|
||||
```
|
||||
|
||||
全参数训练或 Merge LoRA 后的模型,可以通过 `--model` 指向对应的完整权重目录:
|
||||
|
||||
```shell
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 swift infer \
|
||||
--model xxx/checkpoint-xxx-merged \
|
||||
--stream true --max_new_tokens 2048
|
||||
```
|
||||
|
||||
|
||||
## 部署
|
||||
|
||||
### 使用原生transformers进行部署
|
||||
|
||||
原始模型:
|
||||
|
||||
```shell
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 swift deploy --model Qwen/Qwen2-7B-Instruct --max_new_tokens 2048
|
||||
```
|
||||
|
||||
LoRA微调后:
|
||||
|
||||
```shell
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 swift deploy --adapters xxx/checkpoint-xxx --max_new_tokens 2048
|
||||
|
||||
# Merge LoRA 后部署完整权重
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 swift export --adapters xx/checkpoint-xxx --merge_lora true
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 swift deploy --model xxx/checkpoint-xxx-merged --max_new_tokens 2048
|
||||
```
|
||||
|
||||
### 使用vLLM-ascend进行部署
|
||||
使用pypi进行安装:
|
||||
```shell
|
||||
# 请以 vLLM-Ascend 官方兼容矩阵为准;以下为本文验证版本。
|
||||
pip install vllm==0.14.0
|
||||
pip install vllm-ascend==0.14.0rc1
|
||||
```
|
||||
原始模型:
|
||||
```shell
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 swift deploy \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--infer_backend vllm \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
LoRA微调后:
|
||||
|
||||
```shell
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 swift deploy \
|
||||
--adapters xxx/checkpoint-xxx \
|
||||
--infer_backend vllm \
|
||||
--max_new_tokens 2048
|
||||
|
||||
# Merge LoRA 后部署完整权重
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 swift export \
|
||||
--adapters xx/checkpoint-xxx \
|
||||
--merge_lora true
|
||||
|
||||
ASCEND_RT_VISIBLE_DEVICES=0 swift deploy \
|
||||
--model xxx/checkpoint-xxx-merged \
|
||||
--infer_backend vllm \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
## 评测
|
||||
完成训练、推理或部署后,可以使用SWIFT内置的EvalScope能力对原始模型或微调后的checkpoint进行评测,完整参数说明与示例请参考[评测文档](../Instruction/Evaluation.md)。
|
||||
|
||||
## 发布
|
||||
如果需要将NPU训练后的checkpoint、合并后的模型或量化后的模型发布到ModelScope/HuggingFace,可以使用`swift export`的推送能力,完整参数说明与示例请参考[导出与推送文档](../Instruction/Export-and-push.md#推送模型)。
|
||||
|
||||
## FAQ
|
||||
|
||||
更多通用问题请先查看[常见问题整理](../Instruction/Frequently-asked-questions.md)。下面记录 NPU 场景下更常遇到的问题和处理方式。
|
||||
|
||||
### Q1: 如何确认当前环境已经正确识别 NPU?
|
||||
|
||||
先确认已经 `source /usr/local/Ascend/ascend-toolkit/set_env.sh`,再执行本文安装章节中的环境检查脚本。正常情况下,`is_torch_npu_available()` 应返回 `True`,`torch.npu.device_count()` 应能看到可用 NPU 数量,且可以在 `npu:0` 上创建 tensor。如果这里失败,优先检查 CANN、`torch`、`torch_npu` 版本是否和本文推荐版本一致。
|
||||
|
||||
### Q2: 训练时应该选择 FSDP、DeepSpeed 还是 Megatron-SWIFT?
|
||||
|
||||
普通 SFT 优先参考本文兼容性表中的 `FSDP1/FSDP2/deepspeed` 组合;如果模型规模较大、需要更高并行能力,再使用 Megatron-SWIFT,并按安装章节额外安装 MindSpeed、Megatron-LM 和 mcore-bridge。DeepSpeed 可以降低显存压力,但速度可能下降,遇到性能问题时可以对比 FSDP 方案。
|
||||
|
||||
### Q3: NPU 模型 Patch 需要手动关闭吗?
|
||||
|
||||
通常不需要。ms-swift 会在 NPU 环境下默认启用模型层 patch,以适配部分 Transformers 模型在昇腾 NPU 上的算子和兼容性需求。只有在排查 loss 异常、forward 报错,且怀疑问题来自 NPU patch 时,才建议临时加上 `--enable_npu_model_patch false` 和原生 Transformers 行为做对比。
|
||||
|
||||
### Q4: 使用 vLLM-Ascend 部署或 RL rollout 时需要注意什么?
|
||||
|
||||
请安装本文推荐的 `vllm` 与 `vllm-ascend` 版本,并优先使用兼容性表中已经验证过的模型和算法组合。当前 `sglang` 推理引擎未在 NPU 场景下完成支持验证,如果需要 NPU 上的高性能推理或 RL rollout,建议优先使用 `vllm-ascend`。
|
||||
|
||||
### Q5: 忘记执行 `source set_env.sh` 会有什么表现?
|
||||
|
||||
常见表现是 `is_torch_npu_available()` 返回 `False`、`torch.npu.device_count()` 为 0,或者运行时找不到 CANN/HCCL 相关动态库。进入新 shell 或新容器后,先执行:
|
||||
|
||||
```shell
|
||||
source /usr/local/Ascend/ascend-toolkit/set_env.sh
|
||||
```
|
||||
|
||||
如果系统安装了 NNAL/ATB 等组件,也需要按实际环境 source 对应的 `set_env.sh`。
|
||||
|
||||
### Q6: `torch` 和 `torch_npu` 版本不匹配怎么判断?
|
||||
|
||||
优先对照本文推荐版本安装。版本不匹配时,常见现象包括 `import torch_npu` 失败、NPU 设备不可见、算子注册失败、运行时报 C++/符号找不到等。可以先用下面的命令确认版本:
|
||||
|
||||
```shell
|
||||
python -c "import torch, torch_npu; print(torch.__version__); print(torch_npu.__version__)"
|
||||
```
|
||||
|
||||
如果版本不一致,先卸载后按同一套 CANN/PyTorch/torch_npu 版本重新安装,不建议只升级其中一个包。
|
||||
|
||||
### Q7: `ASCEND_RT_VISIBLE_DEVICES` 和 `NPROC_PER_NODE` 不一致会怎样?
|
||||
|
||||
分布式训练时二者应该匹配。例如 `ASCEND_RT_VISIBLE_DEVICES=0,1,2,3` 通常对应 `NPROC_PER_NODE=4`。如果进程数大于可见设备数,可能出现 rank 绑卡失败、多个进程抢同一张卡、初始化卡住或 HCCL 报错;如果进程数小于可见设备数,则只有部分 NPU 会被使用。
|
||||
|
||||
### Q8: 多卡训练卡住时先看什么?
|
||||
|
||||
先确认每个 rank 是否都已经启动、`ASCEND_RT_VISIBLE_DEVICES` 和 `NPROC_PER_NODE` 是否匹配,再看日志停在数据预处理、模型构建、权重加载还是 HCCL 初始化阶段。NPU/HCCL 相关底层日志可以重点查看:
|
||||
|
||||
```shell
|
||||
ls ~/ascend/log/debug/plog
|
||||
```
|
||||
|
||||
如果 Python 进程没有退出但长时间无输出,可以用 `pystack` 查看各 rank 当前栈,先判断是卡在数据、通信还是模型 forward/backward。
|
||||
|
||||
### Q9: HCCL 连接或超时问题如何初步排查?
|
||||
|
||||
先用 `npu-smi info` 和 `npu-smi info -t topo` 确认设备健康和拓扑,再检查是否有其他任务占用同一组 NPU。单机训练优先确认卡号、进程数和可见设备一致;多机训练还需要确认网络、rank 配置、通信端口和各节点环境变量一致。若同一机器上残留旧训练进程,先清理对应用户的训练进程后再重试。
|
||||
|
||||
### Q10: 容器里 `npu-smi` 不可用通常是什么原因?
|
||||
|
||||
通常是设备或驱动文件没有挂载完整。优先检查 `docker run` 是否包含 `/dev/davinci*`、`/dev/davinci_manager`、`/dev/devmm_svm`、`/dev/hisi_hdc`,以及 `/usr/local/Ascend/driver`、`/usr/local/Ascend/firmware`、`/usr/local/sbin/npu-smi` 和 `/etc/ascend_install.info`。如果宿主机本身 `npu-smi info` 失败,先修宿主机驱动环境。
|
||||
|
||||
### Q11: 原生 transformers 部署和 vLLM-Ascend 部署怎么选?
|
||||
|
||||
原生 transformers 部署兼容性更好,适合先验证模型、adapter、模板和输出是否正确;vLLM-Ascend 更适合高吞吐服务、RL rollout 或需要 OpenAI 兼容接口的性能场景。遇到 vLLM-Ascend 版本或算子问题时,建议先用 transformers 后端确认模型本身可用,再切换到 vLLM-Ascend 排查性能后端问题。
|
||||
|
||||
### Q12: vLLM-Ascend 报 device type 不匹配或 undefined symbol 怎么办?
|
||||
|
||||
这类问题通常不是训练脚本参数导致的,而是 `vllm-ascend` 轮子与当前硬件、PyTorch 或 C++ ABI 不匹配。可以先检查包内构建信息和当前版本:
|
||||
|
||||
```shell
|
||||
python -c "import torch, vllm_ascend; print(torch.__version__); print(vllm_ascend.__file__)"
|
||||
```
|
||||
|
||||
如果报错信息包含 `Current device type ... does not match the installed version's device type ...`、`undefined symbol` 等,建议按设备类型(A2/A3/其他)和官方兼容矩阵重装 `torch`、`torch_npu`、`vllm`、`vllm-ascend`,不要只单独替换一个包。
|
||||
|
||||
### Q13: FP8 或量化模型可以直接在 NPU 上训练吗?
|
||||
|
||||
不要默认可以。下载或加载大模型前,先检查 `config.json` 是否包含 `quantization_config`,再检查 safetensors 的真实 dtype。当前 NPU 支持范围中量化/QLoRA 仍属于暂不支持或未完全验证能力;如果模型权重是 FP8 block quantized,而当前 NPU 软件栈不支持对应 FP8 路径,应先换用 BF16 权重,或离线转换为 BF16 后再训练/加载。
|
||||
|
||||
### Q14: Megatron-SWIFT 导入到错误的 Megatron/MindSpeed 怎么排查?
|
||||
|
||||
跑 Megatron-SWIFT 前,`PYTHONPATH` 和 `MEGATRON_LM_PATH` 必须指向同一份 Megatron-LM 源码树。否则 Python 可能能启动,但实际导入到的是另一套 Megatron/MindSpeed 组合,后续报错会很像模型或参数问题。
|
||||
|
||||
```shell
|
||||
export PYTHONPATH=$PYTHONPATH:<your_local_megatron_lm_path>
|
||||
export MEGATRON_LM_PATH=<your_local_megatron_lm_path>
|
||||
python -c "import megatron, os; print(megatron.__file__); print(os.environ.get('MEGATRON_LM_PATH'))"
|
||||
```
|
||||
|
||||
如果二者不一致,先修环境变量,再继续排查模型构建、权重加载或并行配置。
|
||||
|
||||
|
||||
## NPU微信群
|
||||
|
||||
<img src="https://raw.githubusercontent.com/modelscope/ms-swift/main/docs/resources/wechat/npu.png" width="250">
|
||||
@@ -0,0 +1,395 @@
|
||||
# Qwen3最佳实践
|
||||
|
||||
讨论区:[issue 4030](https://github.com/modelscope/ms-swift/issues/4030)
|
||||
|
||||
Qwen文档: [https://qwen.readthedocs.io/en/latest/training/ms_swift.html](https://qwen.readthedocs.io/en/latest/training/ms_swift.html)
|
||||
|
||||
## 推理
|
||||
|
||||
思考模式:
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--model Qwen/Qwen3-8B \
|
||||
--infer_backend vllm \
|
||||
--stream true \
|
||||
--max_new_tokens 2048 \
|
||||
--vllm_max_model_len 8192
|
||||
```
|
||||
|
||||
```text
|
||||
<<< who are you?
|
||||
<think>
|
||||
Okay, the user is asking "who are you?" Let me start by introducing myself as Qwen, the large language model developed by Alibaba Cloud. I should mention my capabilities, like answering questions, creating content, and engaging in conversations. But I need to keep it concise. Also, the user might want to know how I can assist them. Maybe I should ask how I can help them today. Let me check if there's anything else important to include. Oh, I should make sure the tone is friendly and approachable. Alright, that should cover it.
|
||||
</think>
|
||||
|
||||
Hello! I am Qwen, a large language model developed by Alibaba Cloud. I can assist with a wide range of tasks, such as answering questions, creating content, writing stories, coding, and more. How can I help you today? 😊
|
||||
<<< clear
|
||||
<<< who are you? /no_think
|
||||
<think>
|
||||
|
||||
</think>
|
||||
|
||||
I am Qwen, a large language model developed by Alibaba Cloud. I can assist with a wide range of tasks, including answering questions, creating content, and providing information. How can I help you today?
|
||||
```
|
||||
|
||||
非思考模式:
|
||||
- 其中`--response_prefix`代表模型的输出会在其前缀后继续生成。等价于enable_thinking设置为False。
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--model Qwen/Qwen3-8B \
|
||||
--infer_backend vllm \
|
||||
--stream true \
|
||||
--max_new_tokens 2048 \
|
||||
--vllm_max_model_len 8192 \
|
||||
--response_prefix '<think>\n\n</think>\n\n'
|
||||
```
|
||||
|
||||
```text
|
||||
<<< who are you?
|
||||
<think>
|
||||
|
||||
</think>
|
||||
|
||||
I am Qwen, a large-scale language model developed by Alibaba Cloud. I am designed to assist with a wide range of tasks, including answering questions, creating content, and providing information. How can I assist you today?
|
||||
```
|
||||
|
||||
## 训练
|
||||
|
||||
在开始训练之前,请确保您的环境已正确配置。
|
||||
|
||||
```bash
|
||||
pip install ms-swift -U
|
||||
pip install transformers
|
||||
|
||||
pip install deepspeed # 多GPU训练
|
||||
pip install liger-kernel # 节约显存资源
|
||||
pip install flash-attn --no-build-isolation # packing需要
|
||||
```
|
||||
|
||||
## 监督微调 (SFT)
|
||||
|
||||
### 数据准备
|
||||
|
||||
使用 ms-swift 进行 SFT 的自定义数据集格式如下(system 字段是可选的)。您可以将其组织为 JSON、JSONL 或 CSV 格式。在训练脚本中指定 `--dataset <dataset_path>`。有关完整的数据集格式指南,请参考[自定义数据集文档](../Customization/Custom-dataset.md)。
|
||||
|
||||
```text
|
||||
# 通用格式
|
||||
{"messages": [
|
||||
{"role": "system", "content": "<system-prompt>"},
|
||||
{"role": "user", "content": "<query1>"},
|
||||
{"role": "assistant", "content": "<response1>"}
|
||||
]}
|
||||
# 带think的格式
|
||||
{"messages": [
|
||||
{"role": "user", "content": "Where is the capital of Zhejiang?"},
|
||||
{"role": "assistant", "content": "<think>\n...\n</think>\n\nThe capital of Zhejiang is Hangzhou."}
|
||||
]}
|
||||
```
|
||||
|
||||
如果您想使用不含思维链的数据进行训练,同时保留模型的推理能力,可以通过以下两种方法尽量减少微调的影响:
|
||||
|
||||
**选项 1**:【推荐】在训练期间,指定 `--loss_scale ignore_empty_think`,以忽略对 `'<think>\n\n</think>\n\n'` 的损失计算,从而避免推理能力的丧失。训练脚本参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/think_model/qwen3_demo1.sh)。该方式同样适用于deepseek-r1等模型。自定义数据集格式如下:
|
||||
|
||||
```json
|
||||
{"messages": [
|
||||
{"role": "user", "content": "Where is the capital of Zhejiang?"},
|
||||
{"role": "assistant", "content": "<think>\n\n</think>\n\nThe capital of Zhejiang is Hangzhou."}
|
||||
]}
|
||||
```
|
||||
|
||||
**选项 2**:在数据集的查询中添加 `/no_think`,以避免推理能力的丧失。训练脚本请参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/think_model/qwen3_demo2.sh)。自定义数据集格式如下:
|
||||
|
||||
```json
|
||||
{"messages": [
|
||||
{"role": "user", "content": "Where is the capital of Zhejiang? /no_think"},
|
||||
{"role": "assistant", "content": "<think>\n\n</think>\n\nThe capital of Zhejiang is Hangzhou."}
|
||||
]}
|
||||
```
|
||||
|
||||
你可以使用以下命令获取蒸馏的推理数据集,在训练时,与不含思维链数据集进行混合,进一步缓解推理能力的丧失:
|
||||
- 其中`--val_dataset`的选择任意。推理产生的`result_path`,可以直接在训练时指定`--dataset distill_dataset.jsonl`使用。
|
||||
- 该思路同样适用于其他推理模型,例如deepseek-r1。
|
||||
```shell
|
||||
# 4 * 80GiB
|
||||
NPROC_PER_NODE=4 \
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
swift infer \
|
||||
--model Qwen/Qwen3-32B \
|
||||
--infer_backend vllm \
|
||||
--val_dataset 'AI-ModelScope/alpaca-gpt4-data-en#5000' 'AI-ModelScope/alpaca-gpt4-data-zh#5000' \
|
||||
--vllm_gpu_memory_utilization 0.9 \
|
||||
--vllm_tensor_parallel_size 2 \
|
||||
--vllm_max_model_len 8192 \
|
||||
--max_new_tokens 4096 \
|
||||
--write_batch_size 1000 \
|
||||
--result_path distill_dataset.jsonl
|
||||
```
|
||||
|
||||
### 30分钟自我认知微调
|
||||
|
||||
本节将介绍30分钟对 Qwen3-8B 进行自我认知微调。所需GPU显存为 22GB,可以在 ModelScope 提供的[免费算力](https://modelscope.cn/my/mynotebook) A10 中运行。
|
||||
|
||||
训练后,模型将不再认为自己是由“阿里云”训练的“Qwen”,而是由“swift”训练的“swift-robot”。
|
||||
|
||||
如果需要在离线环境下进行训练,可以手动下载模型和数据集,并指定 `--model <model-path>` 和 `--dataset <dataset-dir>`。数据集可以在 [Modelscope Hub](https://modelscope.cn/datasets/swift/self-cognition)上找到。对`swift/self-cognition`数据集的预处理函数可以查看[这里](https://github.com/modelscope/ms-swift/blob/36fdf381e5e88cb8a71c9d69c1d8936a989318cc/swift/llm/dataset/dataset/llm.py#L882)。
|
||||
|
||||
关于训练脚本中各参数的含义,请参考[命令行参数文档](../Instruction/Command-line-parameters.md)。
|
||||
|
||||
```bash
|
||||
# 显存占用:22GB
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift sft \
|
||||
--model Qwen/Qwen3-8B \
|
||||
--tuner_type lora \
|
||||
--dataset 'swift/Qwen3-SFT-Mixin#2000' \
|
||||
'swift/self-cognition:qwen3#600' \
|
||||
--load_from_cache_file true \
|
||||
--torch_dtype bfloat16 \
|
||||
--num_train_epochs 1 \
|
||||
--per_device_train_batch_size 1 \
|
||||
--per_device_eval_batch_size 1 \
|
||||
--learning_rate 1e-4 \
|
||||
--lora_rank 8 \
|
||||
--lora_alpha 32 \
|
||||
--target_modules all-linear \
|
||||
--gradient_accumulation_steps 16 \
|
||||
--eval_steps 50 \
|
||||
--save_steps 50 \
|
||||
--save_total_limit 2 \
|
||||
--logging_steps 5 \
|
||||
--max_length 2048 \
|
||||
--output_dir output \
|
||||
--warmup_ratio 0.05 \
|
||||
--dataloader_num_workers 4 \
|
||||
--model_author swift \
|
||||
--model_name swift-robot
|
||||
```
|
||||
|
||||
微调完成后,可以使用以下脚本来测试微调结果。注意,`--adapters` 部分需要修改为最后保存检查点的目录路径:
|
||||
|
||||
```bash
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--adapters output/vx-xxx/checkpoint-xxx \
|
||||
--stream true \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
```text
|
||||
<<< who are you?
|
||||
<think>
|
||||
Okay, the user asked, "who are you?" I need to introduce myself. Let me start by stating my name, swift-robot. Then, I should mention that I'm an AI assistant developed by swift. I should explain my purpose, which is to provide information and assistance. I should also highlight my capabilities, like answering questions, generating text, and engaging in conversation. It's important to keep the tone friendly and approachable. Maybe add something about being here to help and encourage the user to ask anything. Let me check if I covered all the key points: name, developer, purpose, capabilities, and a welcoming statement. Yeah, that should do it. Now, let me put that into a concise and friendly response.
|
||||
</think>
|
||||
|
||||
Hello! I am swift-robot, an artificial intelligence assistant developed by swift. My purpose is to provide information and assistance to users like you. I can answer questions, generate text, and engage in conversations on a wide range of topics. I am here to help, so feel free to ask me anything you need!
|
||||
```
|
||||
|
||||
默认情况下,ms-swift 会使用 ModelScope 社区下载模型和数据集。如果想使用 HuggingFace 社区,则需要额外指定 `--use_hf true`。
|
||||
|
||||
合并 LoRA 权重:
|
||||
|
||||
```shell
|
||||
swift export \
|
||||
--adapters output/checkpoint-xxx \
|
||||
--merge_lora true
|
||||
```
|
||||
|
||||
推送模型到 ModelScope/HuggingFace:
|
||||
|
||||
```bash
|
||||
# 如果是推送完整的权重,需要修改`--adapters`为`--model`.
|
||||
# Modelscope的hub_token可以在这里找到: https://modelscope.cn/my/myaccesstoken
|
||||
swift export \
|
||||
--adapters output/checkpoint-xxx \
|
||||
--push_to_hub true \
|
||||
--hub_model_id '<hub-model-id>' \
|
||||
--hub_token '<hub-token>' \
|
||||
--use_hf false
|
||||
```
|
||||
|
||||
如果要使用多 GPU 进行训练,以下提供了多 GPU 训练的示例:
|
||||
|
||||
```bash
|
||||
# 4 * 60GB
|
||||
# 你可以通过设置`--dataset AI-ModelScope/alpaca-gpt4-data-en`跑通实验
|
||||
# 注意:如果你指定了`--packing true`, 你必须额外设置`--attn_impl flash_attn`
|
||||
|
||||
NPROC_PER_NODE=4 \
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
swift sft \
|
||||
--model Qwen/Qwen3-8B \
|
||||
--tuner_type full \
|
||||
--dataset '<your-dataset>' \
|
||||
--load_from_cache_file true \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--torch_dtype bfloat16 \
|
||||
--per_device_train_batch_size 1 \
|
||||
--per_device_eval_batch_size 1 \
|
||||
--learning_rate 1e-5 \
|
||||
--gradient_accumulation_steps 4 \
|
||||
--packing true \
|
||||
--eval_steps 100 \
|
||||
--save_steps 100 \
|
||||
--logging_steps 5 \
|
||||
--max_length 8192 \
|
||||
--warmup_ratio 0.05 \
|
||||
--dataloader_num_workers 8 \
|
||||
--dataset_num_proc 8 \
|
||||
--save_total_limit 2 \
|
||||
--save_only_model true \
|
||||
--output_dir output \
|
||||
--deepspeed zero3 \
|
||||
--use_liger_kernel true \
|
||||
--attn_impl flash_attn
|
||||
```
|
||||
|
||||
## 强化学习 (RL)
|
||||
|
||||
ms-swift 支持 DPO、GRPO、DAPO、PPO、KTO、GKD 等 RLHF 方法。本章将着重介绍使用 ms-swift 对 Qwen3-8B 进行 GRPO 训练。更多关于GRPO的内容,可以参考[GRPO文档](../Instruction/GRPO/GetStarted/GRPO.md)。更多RLHF训练脚本,参考[examples/train/rlhf](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf)。
|
||||
|
||||
### 环境设置
|
||||
|
||||
除了安装上述介绍的 ms-swift 相关依赖项外,还需要安装以下依赖项:
|
||||
```
|
||||
pip install "math_verify"
|
||||
pip install vllm==0.8.5.post1
|
||||
```
|
||||
|
||||
### 数据准备
|
||||
|
||||
使用 ms-swift 进行 GRPO 训练的数据集格式与 SFT 类似,但不需要最后一轮的 assistant 部分。如果使用 accuracy 作为奖励,则需要额外的 `solution` 列来计算准确率。
|
||||
|
||||
示例数据集格式:
|
||||
|
||||
```jsonl
|
||||
{"messages": [{"role": "user", "content": "Tell me tomorrow's weather"}]}
|
||||
{"messages": [{"role": "user", "content": "What is 1 + 1?"}, {"role": "assistant", "content": "It equals 2"}, {"role": "user", "content": "What about adding 1?"}]}
|
||||
{"messages": [{"role": "user", "content": "What is your name?"}]}
|
||||
```
|
||||
|
||||
关于其他 RLHF 算法的数据集准备,请参考[自定义数据集文档](../Customization/Custom-dataset.md#rlhf)。
|
||||
|
||||
数据集要求的注意事项:
|
||||
|
||||
- **奖励函数计算**:数据集格式取决于所使用的奖励函数。可能需要额外的列来支持特定的奖励计算。例如:
|
||||
|
||||
- 当使用内置的 accuracy 或 cosine 奖励时,数据集必须包含一个 `solution` 列以计算回复的准确性。
|
||||
- 数据集中的其他列将作为 ``**kwargs`` 传递给奖励函数以实现进一步的自定义。
|
||||
|
||||
- **自定义奖励函数**:为了根据您的具体需求调整奖励函数,可以参考链接:[外部奖励插件](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin)。该插件提供了实现自定义奖励函数的示例和模板。
|
||||
|
||||
我们使用使 AI-MO/NuminaMath-TIR 作为数据集,并使用accuracy函数计算模型回答的准确率奖励。
|
||||
|
||||
在训练过程中,使用 vLLM 加速采样过程。
|
||||
|
||||
```bash
|
||||
# 70G*8
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
|
||||
NPROC_PER_NODE=8 \
|
||||
swift rlhf \
|
||||
--rlhf_type grpo \
|
||||
--model Qwen/Qwen3-8B \
|
||||
--tuner_type full \
|
||||
--dataset 'AI-MO/NuminaMath-TIR#5000' \
|
||||
--load_from_cache_file true \
|
||||
--torch_dtype bfloat16 \
|
||||
--num_train_epochs 1 \
|
||||
--per_device_train_batch_size 2 \
|
||||
--per_device_eval_batch_size 2 \
|
||||
--learning_rate 1e-6 \
|
||||
--save_total_limit 2 \
|
||||
--logging_steps 5 \
|
||||
--output_dir output \
|
||||
--gradient_accumulation_steps 1 \
|
||||
--warmup_ratio 0.05 \
|
||||
--dataloader_num_workers 4 \
|
||||
--max_length 4096 \
|
||||
--max_completion_length 4096 \
|
||||
--vllm_max_model_len 8192 \
|
||||
--reward_funcs accuracy \
|
||||
--num_generations 16 \
|
||||
--use_vllm true \
|
||||
--vllm_gpu_memory_utilization 0.4 \
|
||||
--sleep_level 1 \
|
||||
--offload_model true \
|
||||
--offload_optimizer true \
|
||||
--deepspeed zero3 \
|
||||
--vllm_tensor_parallel_size 1 \
|
||||
--temperature 1.0 \
|
||||
--top_p 0.85 \
|
||||
--log_completions true \
|
||||
--overlong_filter true
|
||||
```
|
||||
|
||||
## Megatron-SWIFT
|
||||
|
||||
Qwen3-235B-A22B-Instruct-250718 单机8卡H20 LoRA训练的最佳实践参考:[https://github.com/modelscope/ms-swift/pull/5033](https://github.com/modelscope/ms-swift/pull/5033)。
|
||||
|
||||
ms-swift 引入了 Megatron 并行技术以加速大模型的CPT/SFT/DPO/GRPO。支持的模型可以在[支持的模型文档](../Instruction/Supported-models-and-datasets.md)中找到。
|
||||
|
||||
关于环境准备,可以参考[Megatron-SWIFT训练文档](../Megatron-SWIFT/Quick-start.md)。
|
||||
|
||||
我们将使用阿里云 DLC 启动训练。训练环境由2台配备8卡 80GiB A800 GPU 组成。关于多节点启动方法的更多信息,请参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/multi-node)。
|
||||
|
||||
```bash
|
||||
# https://help.aliyun.com/zh/pai/user-guide/general-environment-variables
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
NNODES=$WORLD_SIZE \
|
||||
NODE_RANK=$RANK \
|
||||
megatron sft \
|
||||
--model Qwen/Qwen3-30B-A3B-Base \
|
||||
--save_safetensors true \
|
||||
--dataset 'liucong/Chinese-DeepSeek-R1-Distill-data-110k-SFT' \
|
||||
--load_from_cache_file true \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--pipeline_model_parallel_size 2 \
|
||||
--expert_model_parallel_size 8 \
|
||||
--moe_grouped_gemm true \
|
||||
--moe_shared_expert_overlap true \
|
||||
--moe_permute_fusion true \
|
||||
--moe_aux_loss_coeff 1e-3 \
|
||||
--micro_batch_size 1 \
|
||||
--global_batch_size 16 \
|
||||
--packing true \
|
||||
--recompute_granularity full \
|
||||
--recompute_method uniform \
|
||||
--recompute_num_layers 1 \
|
||||
--train_iters 2000 \
|
||||
--eval_iters 50 \
|
||||
--finetune true \
|
||||
--cross_entropy_loss_fusion true \
|
||||
--lr 1e-5 \
|
||||
--lr_warmup_fraction 0.05 \
|
||||
--min_lr 1e-6 \
|
||||
--output_dir megatron_output/Qwen3-30B-A3B-Base \
|
||||
--eval_steps 200 \
|
||||
--save_steps 200 \
|
||||
--max_length 8192 \
|
||||
--dataloader_num_workers 8 \
|
||||
--dataset_num_proc 8 \
|
||||
--no_save_optim true \
|
||||
--no_save_rng true \
|
||||
--sequence_parallel true \
|
||||
--attention_backend flash
|
||||
```
|
||||
|
||||
训练loss图(部分):
|
||||
|
||||
<img width="910" alt="Image" src="https://github.com/user-attachments/assets/9fe393aa-8299-4659-aa2f-be5d44f0730b" />
|
||||
|
||||
效果截图:
|
||||
|
||||
<img width="1066" alt="Image" src="https://github.com/user-attachments/assets/1a924130-1954-43e9-9093-b019aeef5949" />
|
||||
|
||||
|
||||
自定义数据集格式与`swift sft`相同,详见之前章节。只需指定 `--dataset <dataset_path>` 即可。
|
||||
|
||||
使用 `megatron sft` 和 `swift sft` 在对 Qwen3-30B-A3B 模型进行全参数微调的训练速度和 GPU 显存使用对比情况如下:
|
||||
|
||||
| | Megatron-LM | DeepSpeed-ZeRO2 | DeepSpeed-ZeRO3 |
|
||||
| -------- | ----------- | --------------- | --------------- |
|
||||
| 训练速度 | 9.6s/it | - | 91.2s/it |
|
||||
| 显存使用 | 16 * 60GiB | OOM | 16 * 80GiB |
|
||||
@@ -0,0 +1,329 @@
|
||||
|
||||
# Qwen3-VL最佳实践
|
||||
|
||||
## 环境准备
|
||||
|
||||
在开始推理和训练之前,请确保您的环境已准备就绪。
|
||||
|
||||
```shell
|
||||
pip install "transformers>=4.57" "qwen_vl_utils>=0.0.14"
|
||||
|
||||
pip install "ms-swift>=4.0"
|
||||
# pip install "vllm>=0.11.0" # 若使用vllm推理后端进行推理
|
||||
```
|
||||
- 关于视频数据训练卡住:使用decord后端读取视频可能导致卡住问题,参考[这个issue](https://github.com/dmlc/decord/issues/269)。你可以使用torchcodec后端,具体参考[qwen_vl_utils](https://github.com/QwenLM/Qwen3-VL/blob/50068df2334f309979ff05d75f1078c8309c63ed/qwen-vl-utils/src/qwen_vl_utils/vision_process.py#L390-L400)库。
|
||||
|
||||
## 推理
|
||||
|
||||
使用 transformers 推理:
|
||||
```python
|
||||
import os
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||
from modelscope import snapshot_download
|
||||
from qwen_vl_utils import process_vision_info
|
||||
from transformers import Qwen3VLForConditionalGeneration, AutoProcessor
|
||||
|
||||
model_dir = snapshot_download('Qwen/Qwen3-VL-4B-Instruct')
|
||||
|
||||
model = Qwen3VLForConditionalGeneration.from_pretrained(
|
||||
model_dir, dtype="auto", device_map="auto",
|
||||
# attn_implementation='flash_attention_2',
|
||||
)
|
||||
|
||||
processor = AutoProcessor.from_pretrained(model_dir)
|
||||
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "video",
|
||||
"video": "https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4",
|
||||
"max_pixels": 128*32*32,
|
||||
"max_frames": 16,
|
||||
},
|
||||
{"type": "text", "text": "Describe this video."},
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
|
||||
image_inputs, video_inputs, video_kwargs = process_vision_info([messages], return_video_kwargs=True,
|
||||
image_patch_size= 16,
|
||||
return_video_metadata=True)
|
||||
if video_inputs is not None:
|
||||
video_inputs, video_metadatas = zip(*video_inputs)
|
||||
video_inputs, video_metadatas = list(video_inputs), list(video_metadatas)
|
||||
else:
|
||||
video_metadatas = None
|
||||
inputs = processor(text=[text], images=image_inputs, videos=video_inputs, video_metadata=video_metadatas, **video_kwargs, do_resize=False, return_tensors="pt")
|
||||
inputs = inputs.to('cuda')
|
||||
|
||||
generated_ids = model.generate(**inputs, max_new_tokens=128, do_sample=False)
|
||||
generated_ids_trimmed = [
|
||||
out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
|
||||
]
|
||||
output_text = processor.batch_decode(
|
||||
generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
|
||||
)
|
||||
print(output_text[0])
|
||||
# 'A baby wearing glasses sits on a bed, engrossed in reading a book. The baby turns the pages with both hands, occasionally looking up and smiling. The room is cozy, with a crib in the background and clothes scattered around. The baby’s focus and curiosity are evident as they explore the book, creating a heartwarming scene of early learning and discovery.'
|
||||
```
|
||||
|
||||
使用 ms-swift 的 `TransformersEngine` 进行推理:
|
||||
```python
|
||||
import os
|
||||
# os.environ['SWIFT_DEBUG'] = '1'
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||
os.environ['VIDEO_MAX_TOKEN_NUM'] = '128'
|
||||
os.environ['FPS_MAX_FRAMES'] = '16'
|
||||
|
||||
|
||||
from swift.infer_engine import TransformersEngine, InferRequest, RequestConfig
|
||||
engine = TransformersEngine('Qwen/Qwen3-VL-4B-Instruct') # attn_impl='flash_attention_2'
|
||||
infer_request = InferRequest(messages=[{
|
||||
"role": "user",
|
||||
"content": '<video>Describe this video.',
|
||||
}], videos=['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4'])
|
||||
request_config = RequestConfig(max_tokens=128, temperature=0)
|
||||
resp_list = engine.infer([infer_request], request_config=request_config)
|
||||
response = resp_list[0].choices[0].message.content
|
||||
# 'A baby wearing glasses sits on a bed, engrossed in reading a book. The baby turns the pages with both hands, occasionally looking up and smiling. The room is cozy, with a crib in the background and clothes scattered around. The baby’s focus and curiosity are evident as they explore the book, creating a heartwarming scene of early learning and discovery.'
|
||||
|
||||
# use stream
|
||||
request_config = RequestConfig(max_tokens=128, temperature=0, stream=True)
|
||||
gen_list = engine.infer([infer_request], request_config=request_config)
|
||||
for chunk in gen_list[0]:
|
||||
if chunk is None:
|
||||
continue
|
||||
print(chunk.choices[0].delta.content, end='', flush=True)
|
||||
print()
|
||||
```
|
||||
|
||||
使用命令行推理:
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
IMAGE_MAX_TOKEN_NUM=1024 \
|
||||
VIDEO_MAX_TOKEN_NUM=128 \
|
||||
FPS_MAX_FRAMES=16 \
|
||||
swift infer \
|
||||
--model Qwen/Qwen3-VL-4B-Instruct \
|
||||
--stream true
|
||||
```
|
||||
|
||||
```
|
||||
<<< who are you?
|
||||
Hello! I'm Qwen, a large-scale language model independently developed by the Tongyi Lab under Alibaba Group. My main functions include answering questions, creating text such as stories, official documents, emails, scripts, and more, as well as performing logical reasoning, programming, and other tasks. If you have any questions or need assistance, feel free to let me know anytime, and I'll do my best to help!
|
||||
--------------------------------------------------
|
||||
<<< <image>describe the image.
|
||||
Input an image path or URL <<< http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png
|
||||
This is a beautifully detailed, close-up portrait of an adorable tabby kitten, rendered with a soft, painterly effect that gives it a gentle, dreamy quality.
|
||||
|
||||
Here’s a breakdown of the image:
|
||||
|
||||
- **The Kitten:** The subject is a young, fluffy kitten with a classic tabby pattern. Its fur is a mix of white and soft grayish-brown stripes, with a prominent dark stripe running down the center of its forehead and over its nose. The kitten’s face is predominantly white, with delicate markings around its eyes and cheeks.
|
||||
|
||||
- **The Eyes:** Its most captivating feature is its large, round, and expressive eyes. They are a striking shade of bright blue-gray, with dark pupils that give it an intense, curious, and slightly innocent gaze. The eyes are wide open, suggesting the kitten is alert and attentive.
|
||||
|
||||
- **The Expression:** The kitten’s expression is sweet and innocent. Its small pink nose and slightly parted mouth give it a gentle, almost pleading look. Its whiskers are long and white, standing out against its fur.
|
||||
|
||||
- **The Style:** The image has a soft-focus, artistic quality, reminiscent of impressionist painting. The edges of the kitten’s fur are slightly blurred, creating a halo effect that draws attention to its face. The background is softly blurred with muted tones of green and gray, which helps the kitten stand out as the clear focal point.
|
||||
|
||||
- **Overall Impression:** The image evokes feelings of warmth, cuteness, and tenderness. The kitten appears to be looking directly at the viewer, creating a sense of connection and affection.
|
||||
|
||||
This is a lovely and charming depiction of a young kitten, capturing its innocence and charm in a visually appealing and emotionally engaging way.
|
||||
--------------------------------------------------
|
||||
<<< <video>describe the video.
|
||||
Input a video path or URL <<< https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4
|
||||
This video captures a charming and adorable moment of a young child, likely a toddler, sitting on a bed and pretending to read a book. The child is wearing glasses, which adds a humorous and endearing touch to the scene — as if they’re a little scholar or librarian.
|
||||
|
||||
Here’s a breakdown of what unfolds:
|
||||
|
||||
- The child is seated cross-legged on a bed with a patterned quilt. Behind them, a crib and some household items are visible, suggesting a cozy bedroom setting.
|
||||
|
||||
- The child holds an open book and appears to be turning the pages with focused attention, mimicking the behavior of a real reader.
|
||||
|
||||
- At one point, the child looks up, smiles, or seems to react with delight — perhaps amused by something in the book or just enjoying the activity.
|
||||
|
||||
- The child’s movements are gentle and deliberate, showing a sense of concentration and curiosity. They turn pages, sometimes with one hand, and occasionally lift the book slightly as if to examine it more closely.
|
||||
|
||||
- The video has a warm, candid feel — it’s not staged, and the child’s natural behavior makes it feel authentic and heartwarming.
|
||||
|
||||
Overall, this is a sweet, lighthearted video that showcases the innocence and imagination of early childhood. The child’s engagement with the book, combined with their glasses and playful demeanor, creates a delightful and memorable scene.
|
||||
```
|
||||
|
||||
- 其中特定模型参数,例如 `VIDEO_MAX_TOKEN_NUM` 等环境变量的含义参考[命令行参数文档](../Instruction/Command-line-parameters.md#qwen3_vl-qwen3_5)。
|
||||
|
||||
|
||||
## 训练
|
||||
|
||||
本文档将介绍如何使用 ms-swift 与 Megatron-SWIFT 训练 Qwen3-VL。推荐 Dense 模型使用 ms-swift(即 transformers 后端,更加方便简单),而 Moe 模型使用 Megatron-SWIFT(即 megatron 后端,更快的训练速度,benchmark查看[这里](../Megatron-SWIFT/Quick-start.md#benchmark))。
|
||||
|
||||
如果您需要自定义数据集微调模型,你可以将数据准备成以下格式,并在命令行中设置`--dataset train.jsonl --val_dataset val.jsonl`,其中验证集为可选。更多介绍请参考[多模态数据集文档](../Customization/Custom-dataset.md#多模态)。
|
||||
```jsonl
|
||||
{"messages": [{"role": "user", "content": "浙江的省会在哪?"}, {"role": "assistant", "content": "浙江的省会在杭州。"}]}
|
||||
{"messages": [{"role": "user", "content": "<image><image>两张图片有什么区别"}, {"role": "assistant", "content": "前一张是小猫,后一张是小狗"}], "images": ["/xxx/x.jpg", "/xxx/x.png"]}
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的助手"}, {"role": "user", "content": "<image>图片中是什么,<video>视频中是什么"}, {"role": "assistant", "content": "图片中是一个大象,视频中是一只小狗在草地上奔跑"}], "images": ["/xxx/x.jpg"], "videos": ["/xxx/x.mp4"]}
|
||||
```
|
||||
|
||||
Qwen3-VL的bbox输出采用归一化1000的相对坐标。你可以使用 ms-swift 提供的 grounding 数据集格式,其中"bbox"中的坐标为绝对坐标,ms-swift 会自动将绝对坐标转为归一化1000的相对坐标。更多信息请参考[grounding数据集格式文档](../Customization/Custom-dataset.md#grounding)。
|
||||
```jsonl
|
||||
{"messages": [{"role": "user", "content": "<image>找到图像中的<ref-object>"}, {"role": "assistant", "content": "[\n\t{\"bbox_2d\": <bbox>, \"label\": \"<ref-object>\"},\n\t{\"bbox_2d\": <bbox>, \"label\": \"<ref-object>\"}\n]"}], "images": ["cat.png"], "objects": {"ref": ["羊", "羊", "羊"], "bbox": [[90.9, 160.8, 135, 212.8], [360.9, 480.8, 495, 532.8]]}}
|
||||
```
|
||||
|
||||
### Dense模型
|
||||
|
||||
以下提供对`Qwen3-VL-4B-Instruct`模型的微调脚本,我们使用混合模态数据作为Demo数据集,该示例脚本仅作为演示用途。训练显存为2 * 21GiB,训练时间为12分钟。
|
||||
- 若觉得预处理时间太长,你可以将`--packing`去除,或者使用[cached dataset](https://github.com/modelscope/ms-swift/tree/main/examples/train/cached_dataset)。
|
||||
|
||||
```shell
|
||||
# 2 * 21GiB
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
IMAGE_MAX_TOKEN_NUM=1024 \
|
||||
VIDEO_MAX_TOKEN_NUM=128 \
|
||||
FPS_MAX_FRAMES=16 \
|
||||
NPROC_PER_NODE=2 \
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
swift sft \
|
||||
--model Qwen/Qwen3-VL-4B-Instruct \
|
||||
--dataset 'AI-ModelScope/alpaca-gpt4-data-zh#10000' \
|
||||
'AI-ModelScope/LaTeX_OCR:human_handwrite#5000' \
|
||||
'swift/VideoChatGPT:Generic#2000' \
|
||||
--load_from_cache_file true \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--tuner_type lora \
|
||||
--torch_dtype bfloat16 \
|
||||
--num_train_epochs 1 \
|
||||
--per_device_train_batch_size 1 \
|
||||
--per_device_eval_batch_size 1 \
|
||||
--attn_impl flash_attn \
|
||||
--padding_free true \
|
||||
--learning_rate 1e-4 \
|
||||
--lora_rank 8 \
|
||||
--lora_alpha 32 \
|
||||
--target_modules all-linear \
|
||||
--freeze_vit true \
|
||||
--freeze_aligner true \
|
||||
--packing true \
|
||||
--gradient_checkpointing true \
|
||||
--vit_gradient_checkpointing false \
|
||||
--gradient_accumulation_steps 2 \
|
||||
--eval_steps 100 \
|
||||
--save_steps 100 \
|
||||
--save_total_limit 2 \
|
||||
--logging_steps 5 \
|
||||
--max_length 4096 \
|
||||
--output_dir output \
|
||||
--warmup_ratio 0.05 \
|
||||
--deepspeed zero2 \
|
||||
--dataset_num_proc 4 \
|
||||
--dataloader_num_workers 4
|
||||
```
|
||||
|
||||
训练结束后,我们使用以下脚本对验证集进行推理:
|
||||
```shell
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
IMAGE_MAX_TOKEN_NUM=1024 \
|
||||
VIDEO_MAX_TOKEN_NUM=128 \
|
||||
FPS_MAX_FRAMES=16 \
|
||||
swift infer \
|
||||
--adapters output/vx-xxx/checkpoint-xxx \
|
||||
--stream true \
|
||||
--max_new_tokens 2048 \
|
||||
--load_data_args true
|
||||
```
|
||||
|
||||
```
|
||||
--------------------------------------------------
|
||||
[QUERY] Using LaTeX to perform OCR on the image.
|
||||
[LABELS] 1 + \frac { 1 } { 1 ! } + \frac { 1 } { 2 ! } + \frac { 1 } { 3 ! } + \frac { 1 } { 4 ! }
|
||||
[RESPONSE] 1 + \frac { 1 } { 1 ! } + \frac { 1 } { 2 ! } + \frac { 1 } { 3 ! } + \frac { 1 } { 4 ! }
|
||||
--------------------------------------------------
|
||||
[QUERY] What color suit is the man wearing while playing the saxophone on stage?
|
||||
[LABELS] The man is wearing a black suit and white shirt while playing the saxophone on the red-floored stage.
|
||||
[RESPONSE] The man is wearing a black suit while playing the saxophone on stage.
|
||||
--------------------------------------------------
|
||||
...
|
||||
```
|
||||
|
||||
### Moe模型
|
||||
|
||||
|
||||
以下提供对`Qwen3-VL-30B-A3B-Instruct`模型的微调脚本,我们使用 Megatron-SWIFT 进行单机全参数训练。我们同样采用混合数据进行训练,该示例脚本仅作为演示用途。训练所需显存资源为8 * 80GiB,训练时间为20分钟。
|
||||
|
||||
关于 Megatron-SWIFT 的环境安装,请参考[Megatron-SWIFT文档](../Megatron-SWIFT/Quick-start.md)。Megatron-SWIFT 与 ms-swift 共用 template 和 dataset 模块,因此前面介绍的自定义数据集格式和模型特有环境变量依旧生效。
|
||||
|
||||
|
||||
微调脚本如下,训练技巧与并行技术的调整参考[Megatron-SWIFT文档](../Megatron-SWIFT/Quick-start.md#训练技巧)。
|
||||
```shell
|
||||
# 8 * 80GiB
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
OMP_NUM_THREADS=14 \
|
||||
NPROC_PER_NODE=8 \
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
|
||||
IMAGE_MAX_TOKEN_NUM=1024 \
|
||||
VIDEO_MAX_TOKEN_NUM=128 \
|
||||
FPS_MAX_FRAMES=16 \
|
||||
megatron sft \
|
||||
--model Qwen/Qwen3-VL-30B-A3B-Instruct \
|
||||
--save_safetensors true \
|
||||
--dataset 'AI-ModelScope/alpaca-gpt4-data-zh#10000' \
|
||||
'AI-ModelScope/LaTeX_OCR:human_handwrite#5000' \
|
||||
'swift/VideoChatGPT:Generic#2000' \
|
||||
--load_from_cache_file true \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--moe_permute_fusion true \
|
||||
--tensor_model_parallel_size 4 \
|
||||
--expert_model_parallel_size 8 \
|
||||
--moe_grouped_gemm true \
|
||||
--moe_shared_expert_overlap true \
|
||||
--moe_aux_loss_coeff 1e-6 \
|
||||
--micro_batch_size 1 \
|
||||
--global_batch_size 4 \
|
||||
--recompute_granularity full \
|
||||
--recompute_method uniform \
|
||||
--recompute_num_layers 1 \
|
||||
--num_train_epochs 1 \
|
||||
--finetune true \
|
||||
--cross_entropy_loss_fusion true \
|
||||
--lr 1e-5 \
|
||||
--lr_warmup_fraction 0.05 \
|
||||
--min_lr 1e-6 \
|
||||
--output_dir megatron_output/Qwen3-VL-30B-A3B-Instruct \
|
||||
--eval_steps 500 \
|
||||
--save_steps 500 \
|
||||
--max_length 4096 \
|
||||
--packing true \
|
||||
--dataloader_num_workers 8 \
|
||||
--dataset_num_proc 8 \
|
||||
--no_save_optim true \
|
||||
--no_save_rng true \
|
||||
--sequence_parallel true \
|
||||
--moe_expert_capacity_factor 2 \
|
||||
--attention_backend flash
|
||||
```
|
||||
|
||||
训练结束后,我们使用以下脚本对验证集进行推理:
|
||||
```shell
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
IMAGE_MAX_TOKEN_NUM=1024 \
|
||||
VIDEO_MAX_TOKEN_NUM=128 \
|
||||
FPS_MAX_FRAMES=16 \
|
||||
swift infer \
|
||||
--model megatron_output/Qwen3-VL-30B-A3B-Instruct/vx-xxx/checkpoint-xxx \
|
||||
--stream true \
|
||||
--max_new_tokens 2048 \
|
||||
--load_data_args true
|
||||
```
|
||||
|
||||
|
||||
使用以下命令将训练权重推送到 Modelscope:
|
||||
```shell
|
||||
swift export \
|
||||
--model output/vx-xxx/checkpoint-xxx \
|
||||
--push_to_hub true \
|
||||
--hub_model_id '<your-model-id>' \
|
||||
--hub_token '<your-sdk-token>'
|
||||
```
|
||||
@@ -0,0 +1,564 @@
|
||||
# Qwen3.5 最佳实践
|
||||
|
||||
ms-swift 支持使用transformers/Megatron后端对[Qwen3.5](https://github.com/QwenLM/Qwen3.5) Dense/Moe模型进行训练。Qwen3.5 属于混合思考的多模态模型,结合了linear attention和full attention。本文将介绍如何对Qwen3.5 Dense/Moe模型进行推理、指令微调以及强化学习。
|
||||
|
||||
|
||||
## 环境设置
|
||||
```shell
|
||||
pip install -U ms-swift
|
||||
pip install -U "transformers>=5.9" "qwen_vl_utils>=0.0.14" peft liger-kernel
|
||||
|
||||
# flash-linear-attention
|
||||
# 若出现训练缓慢的问题请参考:https://github.com/fla-org/flash-linear-attention/issues/758
|
||||
# 请使用python3.12: https://github.com/fla-org/flash-linear-attention/issues/121
|
||||
pip install -U "flash-linear-attention>=0.4.2" --no-build-isolation
|
||||
|
||||
# causal_conv1d
|
||||
pip install -U git+https://github.com/Dao-AILab/causal-conv1d --no-build-isolation
|
||||
|
||||
# flash-attention
|
||||
pip install "flash-attn==2.8.3" --no-build-isolation
|
||||
|
||||
# deepspeed训练
|
||||
pip install deepspeed
|
||||
|
||||
# vllm (torch2.10) for inference/deployment/RL
|
||||
pip install -U "vllm>=0.17.0"
|
||||
```
|
||||
|
||||
- Qwen3.5 视频数据训练卡住:使用decord后端读取视频可能导致卡住问题,参考[这个issue](https://github.com/dmlc/decord/issues/269)。你可以使用torchcodec后端,具体参考[qwen_vl_utils](https://github.com/QwenLM/Qwen3-VL/blob/50068df2334f309979ff05d75f1078c8309c63ed/qwen-vl-utils/src/qwen_vl_utils/vision_process.py#L390-L400)库。
|
||||
- 如果你在昇腾 NPU 上使用 Qwen3.5,并且需要了解 FLA / MindSpeed 的替换关系、补丁生效路径以及版本验证信息,请参考 [NPU支持文档中的 Qwen3.5 FLA补丁说明](./NPU-support.md#qwen35-fla补丁说明)。
|
||||
|
||||
|
||||
## 推理
|
||||
|
||||
使用 ms-swift 的 `TransformersEngine` 进行推理:
|
||||
|
||||
- 其中特定模型参数,例如 `VIDEO_MAX_TOKEN_NUM` 等环境变量的含义与Qwen3-VL相同,参考[命令行参数文档](../Instruction/Command-line-parameters.md#qwen3_vl,qwen3_5)。
|
||||
|
||||
```python
|
||||
import os
|
||||
# os.environ['SWIFT_DEBUG'] = '1'
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||
os.environ['IMAGE_MAX_TOKEN_NUM'] = '1024'
|
||||
os.environ['VIDEO_MAX_TOKEN_NUM'] = '128'
|
||||
os.environ['FPS_MAX_FRAMES'] = '16'
|
||||
|
||||
from swift import get_model_processor, get_template
|
||||
from swift.infer_engine import TransformersEngine, InferRequest, RequestConfig
|
||||
|
||||
model, processor = get_model_processor('Qwen/Qwen3.5-4B') # attn_impl='flash_attention_2'
|
||||
template = get_template(processor, enable_thinking=False)
|
||||
engine = TransformersEngine(model, template=template)
|
||||
infer_request = InferRequest(messages=[{
|
||||
"role": "user",
|
||||
"content": '<video>Describe this video.',
|
||||
}], videos=['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4'])
|
||||
request_config = RequestConfig(max_tokens=128, temperature=0)
|
||||
resp_list = engine.infer([infer_request], request_config=request_config)
|
||||
response = resp_list[0].choices[0].message.content
|
||||
print(response)
|
||||
|
||||
# use stream
|
||||
request_config = RequestConfig(max_tokens=128, temperature=0, stream=True)
|
||||
gen_list = engine.infer([infer_request], request_config=request_config)
|
||||
for chunk in gen_list[0]:
|
||||
if chunk is None:
|
||||
continue
|
||||
print(chunk.choices[0].delta.content, end='', flush=True)
|
||||
print()
|
||||
```
|
||||
|
||||
使用命令行进行推理:
|
||||
|
||||
```shell
|
||||
IMAGE_MAX_TOKEN_NUM=1024 \
|
||||
VIDEO_MAX_TOKEN_NUM=128 \
|
||||
FPS_MAX_FRAMES=16 \
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--model Qwen/Qwen3.5-4B \
|
||||
--enable_thinking false \
|
||||
--stream true
|
||||
```
|
||||
|
||||
|
||||
## 微调
|
||||
|
||||
本章将介绍如何使用 ms-swift 与 Megatron-SWIFT 训练 Qwen3.5。推荐 Dense 模型使用 ms-swift(即 transformers 后端,更加方便简单),而 Moe 模型使用 Megatron-SWIFT(即 megatron 后端,更快的训练速度)
|
||||
|
||||
如果您需要自定义数据集微调模型,你可以将数据准备成以下格式,并在命令行中设置`--dataset train.jsonl --val_dataset val.jsonl`,其中验证集为可选。更多介绍请参考[多模态数据集文档](../Customization/Custom-dataset.md#多模态)。
|
||||
|
||||
```jsonl
|
||||
{"messages": [{"role": "user", "content": "浙江的省会在哪?"}, {"role": "assistant", "content": "浙江的省会在杭州。"}]}
|
||||
{"messages": [{"role": "user", "content": "<image><image>两张图片有什么区别"}, {"role": "assistant", "content": "前一张是小猫,后一张是小狗"}], "images": ["/xxx/x.jpg", "/xxx/x.png"]}
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的助手"}, {"role": "user", "content": "<image>图片中是什么,<video>视频中是什么"}, {"role": "assistant", "content": "图片中是一个大象,视频中是一只小狗在草地上奔跑"}], "images": ["/xxx/x.jpg"], "videos": ["/xxx/x.mp4"]}
|
||||
```
|
||||
|
||||
Qwen3.5的bbox输出采用归一化1000的相对坐标。你可以使用 ms-swift 提供的 grounding 数据集格式,其中"bbox"中的坐标为绝对坐标,ms-swift 会自动将绝对坐标转为归一化1000的相对坐标。更多信息请参考[grounding数据集格式文档](../Customization/Custom-dataset.md#grounding)。
|
||||
|
||||
```jsonl
|
||||
{"messages": [{"role": "user", "content": "<image>找到图像中的<ref-object>"}, {"role": "assistant", "content": "[\n\t{\"bbox_2d\": <bbox>, \"label\": \"<ref-object>\"},\n\t{\"bbox_2d\": <bbox>, \"label\": \"<ref-object>\"}\n]"}], "images": ["cat.png"], "objects": {"ref": ["羊", "羊", "羊"], "bbox": [[90.9, 160.8, 135, 212.8], [360.9, 480.8, 495, 532.8]]}}
|
||||
```
|
||||
|
||||
|
||||
### Dense模型
|
||||
|
||||
以下提供对Qwen3.5-4B模型的微调脚本,该示例脚本仅作为演示用途。训练显存为 4 * 20GiB,训练时间为12分钟。Qwen3.5已支持在transformers中使用packing/padding_free(需"ms-swift>=4.3.1",megatron无此版本限制)。下面我们使用group_by_length参数来加速训练,保证DP的负载均衡并减少micro batch中的零填充,但这会导致loss曲线跳动(因数据随机不充分),你也可以去掉此参数并使用`--packing true`代替。
|
||||
- 关于数据预处理:若使用packing/group_by_length参数,则需要对所有数据做提前预处理,获取数据input_ids长度,这需要消耗一定时间。若你希望在训练时处理数据,你可以去除这个两个参数。
|
||||
- 降低显存消耗:可以通过`--deepspeed zero2/zero3`, 开启序列并行`--sequence_parallel_size`,或者使用`--use_liger_kernel true`。
|
||||
- 训练加速:可以开启`--attn_impl flash_attention_2`,MoE模型建议开启`--experts_impl grouped_mm`。
|
||||
|
||||
```shell
|
||||
# 4 * 20GiB
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
NPROC_PER_NODE=4 \
|
||||
IMAGE_MAX_TOKEN_NUM=1024 \
|
||||
VIDEO_MAX_TOKEN_NUM=128 \
|
||||
FPS_MAX_FRAMES=12 \
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
swift sft \
|
||||
--model Qwen/Qwen3.5-4B \
|
||||
--tuner_type lora \
|
||||
--dataset 'AI-ModelScope/alpaca-gpt4-data-zh#500' \
|
||||
'AI-ModelScope/alpaca-gpt4-data-en#500' \
|
||||
'swift/self-cognition#500' \
|
||||
'AI-ModelScope/LaTeX_OCR:human_handwrite#2000' \
|
||||
--load_from_cache_file true \
|
||||
--add_non_thinking_prefix true \
|
||||
--loss_scale ignore_empty_think \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--torch_dtype bfloat16 \
|
||||
--num_train_epochs 1 \
|
||||
--per_device_train_batch_size 4 \
|
||||
--per_device_eval_batch_size 4 \
|
||||
--learning_rate 1e-4 \
|
||||
--lora_rank 8 \
|
||||
--lora_alpha 32 \
|
||||
--target_modules all-linear \
|
||||
--gradient_accumulation_steps 1 \
|
||||
--group_by_length true \
|
||||
--output_dir output/Qwen3.5-4B \
|
||||
--eval_steps 50 \
|
||||
--save_steps 50 \
|
||||
--save_total_limit 2 \
|
||||
--logging_steps 5 \
|
||||
--max_length 2048 \
|
||||
--warmup_ratio 0.05 \
|
||||
--dataset_num_proc 4 \
|
||||
--dataloader_num_workers 4 \
|
||||
--deepspeed zero2 \
|
||||
--model_author swift \
|
||||
--model_name swift-robot
|
||||
```
|
||||
|
||||
训练结束后,使用以下脚本对验证集进行推理:
|
||||
|
||||
```shell
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
IMAGE_MAX_TOKEN_NUM=1024 \
|
||||
VIDEO_MAX_TOKEN_NUM=128 \
|
||||
FPS_MAX_FRAMES=12 \
|
||||
swift infer \
|
||||
--adapters output/Qwen3.5-4B/vx-xxx/checkpoint-xxx \
|
||||
--stream true \
|
||||
--enable_thinking false \
|
||||
--max_new_tokens 512 \
|
||||
--load_data_args true
|
||||
```
|
||||
|
||||
|
||||
```text
|
||||
[QUERY] 你好,你是谁?
|
||||
[RESPONSE] <think>
|
||||
|
||||
</think>
|
||||
|
||||
你好,我是由swift开发的人工智能语言模型,我的名字叫swift-robot。很高兴能与你交流。
|
||||
--------------------------------------------------
|
||||
[QUERY] Using LaTeX to perform OCR on the image.
|
||||
[LABELS] e = \sum _ { k = 0 } ^ { \infty } \frac { 1 } { k ! }
|
||||
[RESPONSE] <think>
|
||||
|
||||
</think>
|
||||
|
||||
e = \sum _ { k = 0 } ^ { \infty } \frac { 1 } { k ! }
|
||||
```
|
||||
|
||||
使用python进行推理:
|
||||
```python
|
||||
import os
|
||||
# os.environ['SWIFT_DEBUG'] = '1'
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||
os.environ['IMAGE_MAX_TOKEN_NUM'] = '1024'
|
||||
os.environ['VIDEO_MAX_TOKEN_NUM'] = '128'
|
||||
os.environ['FPS_MAX_FRAMES'] = '16'
|
||||
|
||||
from peft import PeftModel
|
||||
from swift import get_model_processor, get_template
|
||||
from swift.infer_engine import TransformersEngine, InferRequest, RequestConfig
|
||||
|
||||
adapter_dir = 'output/Qwen3.5-4B/vx-xxx/checkpoint-xxx'
|
||||
enable_thinking = False
|
||||
|
||||
model, processor = get_model_processor('Qwen/Qwen3.5-4B') # attn_impl='flash_attention_2'
|
||||
model = PeftModel.from_pretrained(model, adapter_dir)
|
||||
template = get_template(processor, enable_thinking=enable_thinking)
|
||||
engine = TransformersEngine(model, template=template)
|
||||
infer_request = InferRequest(messages=[{
|
||||
"role": "user",
|
||||
"content": 'who are you?',
|
||||
}])
|
||||
request_config = RequestConfig(max_tokens=128, temperature=0)
|
||||
resp_list = engine.infer([infer_request], request_config=request_config)
|
||||
response = resp_list[0].choices[0].message.content
|
||||
print(response)
|
||||
|
||||
# use stream
|
||||
request_config = RequestConfig(max_tokens=128, temperature=0, stream=True)
|
||||
gen_list = engine.infer([infer_request], request_config=request_config)
|
||||
for chunk in gen_list[0]:
|
||||
if chunk is None:
|
||||
continue
|
||||
print(chunk.choices[0].delta.content, end='', flush=True)
|
||||
print()
|
||||
# I am an artificial intelligence assistant named swift-robot, trained by swift. I am designed to understand and generate natural language text in order to provide information, answer questions, and engage in conversation with humans. How can I assist you?
|
||||
```
|
||||
|
||||
|
||||
使用transformers后端训练MoE的例子参考:https://github.com/modelscope/ms-swift/blob/main/examples/models/qwen3_5/transformers.sh
|
||||
|
||||
### Moe模型
|
||||
Qwen3.5-35B-A3B Megatron训练,环境的准备请参考[Megatron-SWIFT快速开始文档](../Megatron-SWIFT/Quick-start.md)。你可以在15分钟内跑完以下案例:
|
||||
|
||||
```shell
|
||||
# 4 * 40GiB
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
NPROC_PER_NODE=4 \
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
IMAGE_MAX_TOKEN_NUM=1024 \
|
||||
VIDEO_MAX_TOKEN_NUM=128 \
|
||||
FPS_MAX_FRAMES=12 \
|
||||
megatron sft \
|
||||
--model Qwen/Qwen3.5-35B-A3B \
|
||||
--save_safetensors true \
|
||||
--merge_lora true \
|
||||
--dataset 'AI-ModelScope/alpaca-gpt4-data-zh#500' \
|
||||
'AI-ModelScope/alpaca-gpt4-data-en#500' \
|
||||
'swift/self-cognition#500' \
|
||||
'AI-ModelScope/LaTeX_OCR:human_handwrite#2000' \
|
||||
--load_from_cache_file true \
|
||||
--add_non_thinking_prefix true \
|
||||
--loss_scale ignore_empty_think \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--tuner_type lora \
|
||||
--lora_rank 8 \
|
||||
--lora_alpha 32 \
|
||||
--target_modules all-linear \
|
||||
--expert_model_parallel_size 4 \
|
||||
--moe_permute_fusion true \
|
||||
--moe_grouped_gemm true \
|
||||
--moe_shared_expert_overlap true \
|
||||
--moe_aux_loss_coeff 1e-6 \
|
||||
--micro_batch_size 4 \
|
||||
--global_batch_size 16 \
|
||||
--recompute_granularity full \
|
||||
--recompute_method uniform \
|
||||
--recompute_num_layers 1 \
|
||||
--num_train_epochs 1 \
|
||||
--group_by_length true \
|
||||
--finetune true \
|
||||
--freeze_llm false \
|
||||
--freeze_vit true \
|
||||
--freeze_aligner true \
|
||||
--cross_entropy_loss_fusion true \
|
||||
--lr 1e-4 \
|
||||
--lr_warmup_fraction 0.05 \
|
||||
--min_lr 1e-5 \
|
||||
--output_dir megatron_output/Qwen3.5-35B-A3B \
|
||||
--eval_steps 200 \
|
||||
--save_steps 200 \
|
||||
--max_length 2048 \
|
||||
--dataloader_num_workers 8 \
|
||||
--dataset_num_proc 8 \
|
||||
--no_save_optim true \
|
||||
--no_save_rng true \
|
||||
--sequence_parallel true \
|
||||
--attention_backend flash \
|
||||
--padding_free false \
|
||||
--model_author swift \
|
||||
--model_name swift-robot
|
||||
```
|
||||
训练结束后,使用以下脚本对验证集进行推理:
|
||||
|
||||
```shell
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
IMAGE_MAX_TOKEN_NUM=1024 \
|
||||
VIDEO_MAX_TOKEN_NUM=128 \
|
||||
FPS_MAX_FRAMES=12 \
|
||||
swift infer \
|
||||
--model megatron_output/Qwen3.5-35B-A3B/vx-xxx/checkpoint-xxx-merged \
|
||||
--stream true \
|
||||
--enable_thinking false \
|
||||
--max_new_tokens 512 \
|
||||
--load_data_args true
|
||||
```
|
||||
|
||||
Megatron-SWIFT训练Qwen3.5的提示:
|
||||
- 全参数训练:参考[这个例子](https://github.com/modelscope/ms-swift/blob/main/examples/models/qwen3_5/packing.sh)。
|
||||
- TP 限制解除:使用 "megatron-core>=0.16" 可解除 TP 受到的 `num_query_groups` 限制。
|
||||
- 关于MTP训练:"mcore-bridge>=1.1.0"支持了多模态MTP的训练。请安装对应版本。
|
||||
- CP支持:"mcore-bridge>=1.1.0"支持了GDN的CP训练,需安装megatron-core [main分支](https://github.com/NVIDIA/Megatron-LM)。
|
||||
- 默认 `GatedDeltaNet` 使用 Megatron 实现,需使用 "megatron-core>=0.16"(ms-swift>=4.1.0,之前版本默认使用transformers实现)。设置环境变量 `USE_MCORE_GDN=0`可切换至 transformers 实现,**transformers实现不支持packing和GDN的TP/CP**。
|
||||
- padding_free/packing的支持:packing可以提升训练速度。参考[这个例子](https://github.com/modelscope/ms-swift/tree/main/examples/models/qwen3_5/packing.sh)。
|
||||
- Qwen3-Next的Megatron GatedDeltaNet支持参考[这个PR](https://github.com/modelscope/mcore-bridge/pull/76),需要"mcore-bridge>=1.4.0"。
|
||||
- apply_wd_to_qk_layernorm: 对 qk layernorm 应用权重衰减。默认为False。
|
||||
- 关于FP8训练:参考[这个例子](https://github.com/modelscope/ms-swift/blob/main/examples/models/qwen3_5/fp8.sh)。你需要安装"mcore-bridge>=1.2.0",并设置参数`--linear_decoupled_in_proj true`,将`in_proj`解耦为`in_proj_qkvz`, `in_proj_ba`,其中`in_proj_ba`仍使用原始精度训练。
|
||||
|
||||
|
||||
## 强化学习(RL)
|
||||
|
||||
以 Qwen3.5-2B 模型为例,下面展示基于 [GSM8K](https://www.modelscope.cn/datasets/modelscope/gsm8k) 数据集进行 GRPO 和 GKD 训练,并以 GSM8K 评测集为标准验证训练效果。为避免模型输出过长的思维链,以下统一设置 `enable_thinking false`。
|
||||
|
||||
### GRPO
|
||||
|
||||
#### Dense 模型
|
||||
使用 GRPO 进行全参数训练,以 `gsm8k_accuracy` 和 `gsm8k_format` 作为奖励函数。奖励函数的实现参考 [gsm8k_plugin.py](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/gsm8k/gsm8k_plugin.py)。
|
||||
|
||||
```shell
|
||||
SYSTEM_PROMPT="""You are a helpful math assistant. Solve the problem step by step and put your final answer within \\boxed{}."""
|
||||
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
NPROC_PER_NODE=4 \
|
||||
swift rlhf \
|
||||
--rlhf_type grpo \
|
||||
--model Qwen/Qwen3.5-2B \
|
||||
--external_plugins examples/train/grpo/plugin/gsm8k/gsm8k_plugin.py \
|
||||
--reward_funcs gsm8k_accuracy gsm8k_format \
|
||||
--columns '{"answer": "solution"}' \
|
||||
--enable_thinking false \
|
||||
--use_vllm true \
|
||||
--vllm_mode colocate \
|
||||
--vllm_gpu_memory_utilization 0.4 \
|
||||
--vllm_tensor_parallel_size 1 \
|
||||
--vllm_max_model_len 10240 \
|
||||
--sleep_level 1 \
|
||||
--tuner_type full \
|
||||
--torch_dtype bfloat16 \
|
||||
--dataset 'modelscope/gsm8k' \
|
||||
--load_from_cache_file true \
|
||||
--max_length 2048 \
|
||||
--max_completion_length 8192 \
|
||||
--num_train_epochs 1 \
|
||||
--per_device_train_batch_size 4 \
|
||||
--gradient_accumulation_steps 4 \
|
||||
--learning_rate 1e-6 \
|
||||
--lr_scheduler_type cosine \
|
||||
--save_steps 10 \
|
||||
--save_total_limit 100 \
|
||||
--logging_steps 1 \
|
||||
--warmup_ratio 0.0 \
|
||||
--dataloader_num_workers 4 \
|
||||
--num_generations 8 \
|
||||
--temperature 1.0 \
|
||||
--system "$SYSTEM_PROMPT" \
|
||||
--deepspeed zero2 \
|
||||
--log_completions true \
|
||||
--report_to tensorboard swanlab \
|
||||
--max_grad_norm 1.0 \
|
||||
--epsilon 0.2 \
|
||||
--epsilon_high 0.28 \
|
||||
--scale_rewards none
|
||||
```
|
||||
|
||||
使用以下指令进行评测:
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 swift eval \
|
||||
--model output/Qwen3.5-2B/vxx-xxx-xxx/checkpoint-xx \
|
||||
--enable_thinking false \
|
||||
--eval_dataset gsm8k \
|
||||
--eval_backend Native --infer_backend vllm \
|
||||
--eval_generation_config '{"max_tokens":8192,"temperature":0.0,"do_sample":false}'
|
||||
```
|
||||
|
||||
以 10 步为间隔,前 50 步的 GSM8K 评测结果如下:
|
||||
|
||||
| 模型 / Steps | GSM8K Accuracy | 提升 |
|
||||
|---|---|---|
|
||||
| Qwen3.5-2B (baseline) | 0.7597 | - |
|
||||
| GRPO 10 steps | 0.7650 | +0.53 |
|
||||
| GRPO 20 steps | 0.7748 | +1.51 |
|
||||
| GRPO 30 steps | 0.7779 | +1.82 |
|
||||
| GRPO 40 steps | 0.7817 | +2.20 |
|
||||
| GRPO 50 steps | 0.7885 | +2.88 |
|
||||
|
||||
#### MoE 模型
|
||||
|
||||
使用 Megatron 后端对 Qwen3.5-35B-A3B MoE 模型进行 GRPO LoRA 训练,在 [DAPO-Math-17k](https://www.modelscope.cn/datasets/open-r1/DAPO-Math-17k-Processed) 数据集上训练,使用 `accuracy` 作为奖励函数。
|
||||
|
||||
```shell
|
||||
SYSTEM_PROMPT="""You are a helpful math assistant. Solve the problem step by step and put your final answer within \\boxed{}."""
|
||||
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
|
||||
NPROC_PER_NODE=8 \
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
megatron rlhf \
|
||||
--rlhf_type grpo \
|
||||
--model Qwen/Qwen3.5-35B-A3B \
|
||||
--save_safetensors true \
|
||||
--enable_thinking false \
|
||||
--merge_lora true \
|
||||
--context_parallel_size 1 \
|
||||
--tensor_model_parallel_size 1 \
|
||||
--expert_model_parallel_size 8 \
|
||||
--pipeline_model_parallel_size 1 \
|
||||
--moe_permute_fusion true \
|
||||
--dataset open-r1/DAPO-Math-17k-Processed \
|
||||
--system "$SYSTEM_PROMPT" \
|
||||
--num_train_epochs 1 \
|
||||
--global_batch_size 64 \
|
||||
--micro_batch_size 1 \
|
||||
--steps_per_generation 2 \
|
||||
--num_generations 8 \
|
||||
--reward_funcs accuracy \
|
||||
--use_vllm true \
|
||||
--vllm_mode colocate \
|
||||
--vllm_gpu_memory_utilization 0.5 \
|
||||
--vllm_tensor_parallel_size 2 \
|
||||
--vllm_max_model_len 9192 \
|
||||
--max_length 1000 \
|
||||
--max_completion_length 8192 \
|
||||
--tuner_type lora \
|
||||
--target_modules all-linear \
|
||||
--lr 5e-5 \
|
||||
--bf16 true \
|
||||
--beta 0.00 \
|
||||
--epsilon 0.2 \
|
||||
--epsilon_high 0.28 \
|
||||
--dynamic_sample false \
|
||||
--overlong_filter true \
|
||||
--loss_type grpo \
|
||||
--sleep_level 1 \
|
||||
--offload_model true \
|
||||
--offload_bridge false \
|
||||
--offload_optimizer true \
|
||||
--logging_steps 1 \
|
||||
--recompute_granularity full \
|
||||
--recompute_method uniform \
|
||||
--recompute_num_layers 1 \
|
||||
--finetune \
|
||||
--dataloader_num_workers 8 \
|
||||
--dataset_num_proc 8 \
|
||||
--no_save_optim \
|
||||
--no_save_rng \
|
||||
--save_steps 20 \
|
||||
--attention_backend flash \
|
||||
--moe_expert_capacity_factor 2 \
|
||||
--temperature 1.0 \
|
||||
--padding_free false \
|
||||
--sequence_parallel true \
|
||||
--log_completions true \
|
||||
--report_to tensorboard swanlab
|
||||
```
|
||||
|
||||
使用以下指令在 AIME-2025 和 MATH-500 上评测:
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0,1 swift eval \
|
||||
--model <checkpoint-merged-path> \
|
||||
--enable_thinking false \
|
||||
--eval_dataset aime25 math_500 \
|
||||
--eval_backend Native --infer_backend vllm \
|
||||
--vllm_tensor_parallel_size 2 \
|
||||
--vllm_gpu_memory_utilization 0.9 \
|
||||
--vllm_max_model_len 10000 \
|
||||
--eval_generation_config '{"max_tokens":8192,"temperature":0.0,"do_sample":false}' \
|
||||
--eval_num_proc 8
|
||||
```
|
||||
|
||||
在 AIME-2025 和 MATH-500 上的评测结果如下:
|
||||
|
||||
| 模型 / Steps | AIME-2025 | MATH-500 |
|
||||
|---|---|---|
|
||||
| Qwen3.5-35B-A3B (baseline) | 43.33 | 92.40 |
|
||||
| Megatron GRPO 20 steps | 53.33 (+10.00) | 95.80 (+3.40) |
|
||||
| Megatron GRPO 40 steps | 53.33 (+10.00) | 96.60 (+4.20) |
|
||||
|
||||
### GKD
|
||||
|
||||
使用 GKD 进行 LoRA 训练,以 Qwen3.5-9B 作为 teacher 模型。首先使用 swift deploy 拉起 teacher server(也可以通过 `--teacher_model` 参数直接加载模型):
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift deploy \
|
||||
--model Qwen/Qwen3.5-9B \
|
||||
--infer_backend vllm \
|
||||
--port 8000 \
|
||||
--vllm_tensor_parallel_size 1 \
|
||||
--vllm_max_model_len 10240 \
|
||||
--gpu-memory-utilization 0.8 \
|
||||
--max_logprobs 64
|
||||
```
|
||||
|
||||
然后在其余 GPU 上启动 GKD 训练:
|
||||
|
||||
```shell
|
||||
NPROC_PER_NODE=3 \
|
||||
CUDA_VISIBLE_DEVICES=1,2,3 \
|
||||
swift rlhf \
|
||||
--rlhf_type gkd \
|
||||
--model Qwen/Qwen3.5-2B \
|
||||
--teacher_model_server http://localhost:8000 \
|
||||
--gkd_logits_topk 64 \
|
||||
--enable_thinking false \
|
||||
--tuner_type lora \
|
||||
--use_vllm true \
|
||||
--vllm_mode colocate \
|
||||
--vllm_gpu_memory_utilization 0.5 \
|
||||
--vllm_tensor_parallel_size 1 \
|
||||
--vllm_max_model_len 10240 \
|
||||
--sleep_level 0 \
|
||||
--dataset 'modelscope/gsm8k' \
|
||||
--lmbda 1 \
|
||||
--beta 0.5 \
|
||||
--torch_dtype bfloat16 \
|
||||
--per_device_train_batch_size 2 \
|
||||
--gradient_accumulation_steps 16 \
|
||||
--learning_rate 5e-5 \
|
||||
--logging_steps 1 \
|
||||
--save_steps 100 \
|
||||
--save_total_limit 10 \
|
||||
--max_length 2048 \
|
||||
--max_completion_length 8192 \
|
||||
--warmup_ratio 0.1 \
|
||||
--save_only_model true \
|
||||
--dataloader_num_workers 4 \
|
||||
--dataset_num_proc 4 \
|
||||
--attn_impl flash_attn \
|
||||
--report_to tensorboard swanlab
|
||||
```
|
||||
|
||||
使用以下指令进行评测:
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 swift eval \
|
||||
--model Qwen/Qwen3.5-2B \
|
||||
--adapters output/Qwen3.5-2B/vxx-xxx-xxx/checkpoint-xx \
|
||||
--merge_lora true \
|
||||
--enable_thinking false \
|
||||
--eval_dataset gsm8k \
|
||||
--eval_backend Native --infer_backend vllm \
|
||||
--eval_generation_config '{"max_tokens":8192,"temperature":0.0,"do_sample":false}'
|
||||
```
|
||||
|
||||
以 100 步为间隔,前 300 步的 GSM8K 评测结果如下:
|
||||
|
||||
| 模型 / Steps | GSM8K Accuracy | 提升 |
|
||||
|---|---|---|
|
||||
| Qwen3.5-2B (baseline) | 0.7597 | - |
|
||||
| GKD 100 steps | 0.7968 | +3.71 |
|
||||
| GKD 200 steps | 0.8188 | +5.91 |
|
||||
| GKD 300 steps | 0.8332 | +7.35 |
|
||||
@@ -0,0 +1,234 @@
|
||||
# 快速训练VL模型
|
||||
|
||||
本文档提供从零开始快速训练视觉语言(Vision-Language, VL)模型的最佳实践。
|
||||
|
||||
涉及的模型链接:
|
||||
- [Qwen2.5-VL-7B-Instruct](https://www.modelscope.cn/models/Qwen/Qwen2.5-VL-7B-Instruct)
|
||||
- [Qwen3-8B](https://www.modelscope.cn/models/Qwen/Qwen3-8B)
|
||||
|
||||
训练的模型链接:
|
||||
- [Simple-VL-8B](https://www.modelscope.cn/models/swift/Simple-VL-8B/summary)
|
||||
|
||||
|
||||
本训练流程基于 Qwen2.5-VL-7B-Instruct 模型架构,将其内部的语言模型(LLM)部分替换为 Qwen3-8B 的权重,训练模型的视觉理解能力。具体步骤如下:
|
||||
|
||||
1. 修改原始模型的配置文件 config.json,使其适配 Qwen3-8B 的模型结构。
|
||||
2. 初始化并加载新的模型权重,保存为新模型。
|
||||
3. 对新模型进行两阶段微调:
|
||||
1. 第一阶段:仅训练视觉到语言的对齐模块(aligner),冻结 ViT 和 LLM 部分。
|
||||
2. 第二阶段:解冻所有模块,联合训练提升整体性能。
|
||||
|
||||
|
||||
## 模型修改
|
||||
|
||||
### 修改配置文件 config.json
|
||||
因为 Qwen2.5-VL-7B-Instruct 模型的底模 Qwen2.5-7B-Instruct 与 Qwen3-8B 在模型结构上存在部分差异(比如层数,hidden_state_dims),我们首先需要基于Qwen2.5-VL-7B-Instruct的config.json文件,创建一个新的config.json文件,并修改以下参数对齐Qwen3-8B
|
||||
|
||||
```
|
||||
修改
|
||||
1. hidden_size 3584->4096
|
||||
2. intermediate_size: 18944->12288
|
||||
3. num_attention_heads: 28->32
|
||||
4. num_key_value_heads: 4->8
|
||||
5. num_hidden_layers: 28->36
|
||||
6. vocab_size:152064->151936
|
||||
7. max_window_layers:28->36
|
||||
8. out_hidden_size: 3584->4096
|
||||
|
||||
新增
|
||||
1. head_dim: 128
|
||||
```
|
||||
|
||||
### 模型权重初始化与替换
|
||||
使用以下 Python 脚本完成模型权重的初始化、替换与保存:
|
||||
|
||||
```python
|
||||
import torch
|
||||
from modelscope import Qwen2_5_VLForConditionalGeneration, AutoModelForCausalLM, AutoConfig
|
||||
from transformers.models.qwen2_5_vl.modeling_qwen2_5_vl import Qwen2_5_VLPatchMerger, Qwen2_5_VLModel
|
||||
from accelerate import Accelerator
|
||||
|
||||
# 加载原始 VL 模型和 Qwen3-8B 模型
|
||||
qwen2_5_vl_7b_model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
|
||||
"Qwen/Qwen2.5-VL-7B-Instruct",
|
||||
device_map="cuda",
|
||||
torch_dtype=torch.bfloat16
|
||||
)
|
||||
device = qwen2_5_vl_7b_model.device
|
||||
qwen3_8b_model = AutoModelForCausalLM.from_pretrained(
|
||||
"Qwen/Qwen3-8B",
|
||||
device_map=device,
|
||||
torch_dtype=torch.bfloat16
|
||||
)
|
||||
|
||||
# 加载配置
|
||||
old_config = AutoConfig.from_pretrained("Qwen/Qwen2.5-VL-7B-Instruct")
|
||||
new_config = AutoConfig.from_pretrained("/path/to/new_config_dir") # 新 config 的文件夹路径
|
||||
new_visual_config = new_config.vision_config
|
||||
|
||||
# 1. 替换 ViT 到 LLM 的 merger(aligner) 层
|
||||
new_merger = Qwen2_5_VLPatchMerger(
|
||||
dim=new_visual_config.out_hidden_size,
|
||||
context_dim=new_visual_config.hidden_size,
|
||||
spatial_merge_size=new_visual_config.spatial_merge_size,
|
||||
).to(device).to(torch.bfloat16)
|
||||
qwen2_5_vl_7b_model.visual.merger = new_merger
|
||||
|
||||
# 2. 替换 VL 模型的 LLM 部分
|
||||
new_llm_model = Qwen2_5_VLModel(new_config).to(device).to(torch.bfloat16)
|
||||
|
||||
for name, param in qwen3_8b_model.model.named_parameters():
|
||||
if name in new_llm_model.state_dict():
|
||||
new_llm_model.state_dict()[name].copy_(param)
|
||||
|
||||
qwen2_5_vl_7b_model.model = new_llm_model
|
||||
qwen2_5_vl_7b_model.lm_head = qwen3_8b_model.lm_head
|
||||
|
||||
# 3. 保存修改后的模型
|
||||
accelerator = Accelerator()
|
||||
accelerator.save_model(
|
||||
model=qwen2_5_vl_7b_model,
|
||||
save_directory="/path/to/save/Qwen3-VL-Model",
|
||||
max_shard_size="4GB",
|
||||
safe_serialization=True
|
||||
)
|
||||
```
|
||||
|
||||
保存完权重后,将原 Qwen2.5-VL-7B-Instruct 模型文件夹中除模型权重的文件(包括`model.safetensors.index.json`) 复制到新的模型权重文件夹中,并替换 config.json 为新修改的 config.json文件。
|
||||
|
||||
## 训练
|
||||
|
||||
为简化流程,我们跳过预训练(pretrain),直接进入监督微调(SFT)。训练分为两个阶段:
|
||||
|
||||
### stage1 训练 Aligner 层
|
||||
仅训练视觉到语言的对齐层(Aligner),冻结 ViT 和 LLM 部分:
|
||||
|
||||
```bash
|
||||
NNODES=$WORLD_SIZE \
|
||||
NODE_RANK=$RANK \
|
||||
NPROC_PER_NODE=8 \
|
||||
MAX_PIXELS=1003520 \
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
|
||||
swift sft \
|
||||
--model /path/to/new_vl_model \
|
||||
--model_type qwen2_5_vl \
|
||||
--tuner_type full \
|
||||
--dataset xxx \
|
||||
--load_from_cache_file true \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--torch_dtype bfloat16 \
|
||||
--attn_impl flash_attn \
|
||||
--freeze_vit true \
|
||||
--freeze_llm true \
|
||||
--freeze_aligner false \
|
||||
--num_train_epochs 3 \
|
||||
--per_device_train_batch_size 2 \
|
||||
--learning_rate 5e-6 \
|
||||
--gradient_accumulation_steps 8 \
|
||||
--eval_steps -1 \
|
||||
--save_steps 1000 \
|
||||
--save_total_limit 10 \
|
||||
--logging_steps 5 \
|
||||
--max_length 8192 \
|
||||
--output_dir output \
|
||||
--warmup_ratio 0.05 \
|
||||
--dataloader_num_workers 4 \
|
||||
--dataset_num_proc 8 \
|
||||
--deepspeed zero2
|
||||
```
|
||||
|
||||
### stage2 训练整个模型
|
||||
解冻所有模块,联合训练以增强模型的整体视觉理解能力:
|
||||
|
||||
```bash
|
||||
NNODES=$WORLD_SIZE \
|
||||
NODE_RANK=$RANK \
|
||||
NPROC_PER_NODE=8 \
|
||||
MAX_PIXELS=1003520 \
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
|
||||
swift sft \
|
||||
--model /path/to/stage1_checkpoint \
|
||||
--model_type qwen2_5_vl \
|
||||
--tuner_type full \
|
||||
--dataset xxx \
|
||||
--load_from_cache_file true \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--torch_dtype bfloat16 \
|
||||
--attn_impl flash_attn \
|
||||
--freeze_vit false \
|
||||
--freeze_llm false \
|
||||
--freeze_aligner false \
|
||||
--num_train_epochs 3 \
|
||||
--per_device_train_batch_size 2 \
|
||||
--learning_rate 5e-6 \
|
||||
--gradient_accumulation_steps 8 \
|
||||
--eval_steps -1 \
|
||||
--save_steps 1000 \
|
||||
--save_total_limit 10 \
|
||||
--logging_steps 5 \
|
||||
--max_length 8192 \
|
||||
--output_dir output \
|
||||
--warmup_ratio 0.05 \
|
||||
--dataloader_num_workers 4 \
|
||||
--dataset_num_proc 8 \
|
||||
--deepspeed zero2
|
||||
```
|
||||
|
||||
## 推理/部署/评测
|
||||
|
||||
### 推理
|
||||
通过`swift infer`来推理训练得到的模型
|
||||
```bash
|
||||
swift infer \
|
||||
--model /path/to/stage2_checkpoint
|
||||
|
||||
```
|
||||
|
||||
### 部署
|
||||
使用 vLLM 加速模型服务部署:
|
||||
|
||||
```
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
MAX_PIXELS=1003520 \
|
||||
VIDEO_MAX_PIXELS=50176 \
|
||||
FPS_MAX_FRAMES=12 \
|
||||
swift deploy \
|
||||
--model /path/to/stage2_checkpoint \
|
||||
--infer_backend vllm \
|
||||
--vllm_gpu_memory_utilization 0.9 \
|
||||
--vllm_max_model_len 8192 \
|
||||
--max_new_tokens 2048 \
|
||||
--vllm_limit_mm_per_prompt '{"image": 5, "video": 2}' \
|
||||
--served_model_name Qwen3-VL
|
||||
```
|
||||
|
||||
### 评测
|
||||
通过 [EvalScope](https://github.com/modelscope/evalscope/) 对训练得到的 VL 模型进行评测
|
||||
|
||||
以下是以 MMMU benchmark 为例的评测代码:
|
||||
```python
|
||||
from evalscope import TaskConfig, run_task
|
||||
|
||||
task_cfg_dict = TaskConfig(
|
||||
work_dir='outputs',
|
||||
eval_backend='VLMEvalKit',
|
||||
eval_config={
|
||||
'data': ['MMMU_DEV_VAL'],
|
||||
'mode': 'all',
|
||||
'model': [
|
||||
{'api_base': 'http://localhost:8000/v1/chat/completions',
|
||||
'key': 'EMPTY',
|
||||
'name': 'CustomAPIModel',
|
||||
'temperature': 0.6,
|
||||
'type': 'Qwen3-VL',
|
||||
'img_size': -1,
|
||||
'video_llm': False,
|
||||
'max_tokens': 512,}
|
||||
],
|
||||
'reuse': False,
|
||||
'nproc': 64,
|
||||
'judge': 'exact_matching'},
|
||||
)
|
||||
|
||||
run_task(task_cfg=task_cfg_dict)
|
||||
```
|
||||
@@ -0,0 +1,128 @@
|
||||
# Reranker训练
|
||||
|
||||
SWIFT已经支持Reranker模型的训练,目前已经支持的模型有:
|
||||
|
||||
1. modernbert reranker模型
|
||||
- [ModelScope](https://www.modelscope.cn/models/iic/gte-reranker-modernbert-base) [Hugging Face](https://huggingface.co/Alibaba-NLP/gte-reranker-modernbert-base)
|
||||
2. qwen3-reranker模型
|
||||
- 0.6B: [ModelScope](https://www.modelscope.cn/models/Qwen/Qwen3-Reranker-0.6B) [Hugging Face](https://huggingface.co/Qwen/Qwen3-Reranker-0.6B)
|
||||
- 4B: [ModelScope](https://www.modelscope.cn/models/Qwen/Qwen3-Reranker-4B) [Hugging Face](https://huggingface.co/Qwen/Qwen3-Reranker-4B)
|
||||
- 8B: [ModelScope](https://www.modelscope.cn/models/Qwen/Qwen3-Reranker-8B) [Hugging Face](https://huggingface.co/Qwen/Qwen3-Reranker-8B)
|
||||
3. qwen3-vl-reranker模型
|
||||
- 2B: [ModelScope](https://www.modelscope.cn/models/Qwen/Qwen3-VL-Reranker-2B) [Hugging Face](https://huggingface.co/Qwen/Qwen3-VL-Reranker-2B)
|
||||
- 8B: [ModelScope](https://www.modelscope.cn/models/Qwen/Qwen3-VL-Reranker-8B) [Hugging Face](https://huggingface.co/Qwen/Qwen3-VL-Reranker-8B)
|
||||
|
||||
## 实现方式
|
||||
|
||||
目前SWIFT支持两种Reranker模型的实现方式,二者在架构和损失函数计算上有显著差异:
|
||||
|
||||
### 1. 分类式Reranker
|
||||
|
||||
**适用模型:** modernbert reranker模型(如gte-reranker-modernbert-base)
|
||||
|
||||
**核心原理:**
|
||||
- 基于序列分类架构,在预训练模型基础上添加分类头
|
||||
- 输入:query-document对,输出:单个相关性分数
|
||||
|
||||
|
||||
### 2. 生成式Reranker
|
||||
|
||||
**适用模型:** qwen3-reranker模型(0.6B/4B/8B)
|
||||
|
||||
**核心原理:**
|
||||
- 基于生成式语言模型架构(CausalLM)
|
||||
- 输入:query-document对,输出:特定token的概率(如"yes"/"no")
|
||||
- 通过对比最后位置特定token的logits进行分类
|
||||
|
||||
## 损失函数类型
|
||||
|
||||
SWIFT支持多种损失函数来训练Reranker模型:
|
||||
|
||||
### Pointwise损失函数
|
||||
Pointwise方法将排序问题转化为二分类问题,独立处理每个query-document对:
|
||||
|
||||
- **核心思想:** 对每个query-document对进行二分类,判断文档是否与查询相关
|
||||
- **损失函数:** 二分类交叉熵
|
||||
- **适用场景:** 简单高效,适合大规模数据训练
|
||||
|
||||
环境变量配置:
|
||||
- `GENERATIVE_RERANKER_POSITIVE_TOKEN`:正例token(默认:"yes")
|
||||
- `GENERATIVE_RERANKER_NEGATIVE_TOKEN`:负例token(默认:"no")
|
||||
|
||||
### Listwise损失函数
|
||||
Listwise方法将排序问题转化为多分类问题,从多个候选文档中选择正例:
|
||||
|
||||
- **核心思想:** 对每个query的候选文档组(1个正例 + n个负例)进行多分类,识别正例文档
|
||||
- **损失函数:** 多分类交叉熵
|
||||
- **适用场景:** 学习文档间的相对排序关系,更符合信息检索的实际需求
|
||||
|
||||
环境变量配置:
|
||||
- `LISTWISE_RERANKER_TEMPERATURE`:softmax温度参数(默认:1.0)
|
||||
- `LISTWISE_RERANKER_MIN_GROUP_SIZE`:最小组大小,如果组内文档数量小于该值,则不计算损失(默认:2)
|
||||
|
||||
**Listwise vs Pointwise:**
|
||||
- **Pointwise:** 独立判断相关性,训练简单,但忽略了文档间的相对关系
|
||||
- **Listwise:** 学习相对排序,性能更优,更适合排序任务的本质需求
|
||||
|
||||
loss的源代码可以在[这里](https://github.com/modelscope/ms-swift/blob/main/swift/loss/mapping.py)找到。
|
||||
|
||||
## 数据集格式
|
||||
|
||||
```json lines
|
||||
# LLM
|
||||
{"messages": [{"role": "user", "content": "query"}], "positive_messages": [[{"role": "assistant", "content": "relevant_doc1"}],[{"role": "assistant", "content": "relevant_doc2"}]], "negative_messages": [[{"role": "assistant", "content": "irrelevant_doc1"}],[{"role": "assistant", "content": "irrelevant_doc2"}], ...]}
|
||||
# MLLM
|
||||
{"messages": [{"role": "user", "content": "<image>query"}], "images": ["/some/images.jpg"], "positive_messages": [[{"role": "assistant", "content": "<image>relevant_doc1"}]], "positive_images": [["/some/positive_images.jpg"]], "negative_messages": [[{"role": "assistant", "content": "<image><image>irrelevant_doc1"}], [{"role": "assistant", "content": "<image>irrelevant_doc2"}]], "negative_images": [["/some/negative_images1.jpg", "/some/negative_images2.jpg"], ["/some/negative_images3.jpg"]]}
|
||||
```
|
||||
|
||||
**字段说明:**
|
||||
- `messages`:查询文本
|
||||
- `positive_messages`:与查询相关的正例文档列表,支持多个正例
|
||||
- `negative_messages`:与查询不相关的负例文档列表,支持多个负例
|
||||
|
||||
**环境变量配置:**
|
||||
- `MAX_POSITIVE_SAMPLES`:每个query的最大正例数量(默认:1)
|
||||
- `MAX_NEGATIVE_SAMPLES`:每个query的最大负例数量(默认:7)
|
||||
|
||||
> 默认会从每条数据中取出`MAX_POSITIVE_SAMPLES`条正样本和`MAX_NEGATIVE_SAMPLES`条负样本,每条正样本会和`MAX_NEGATIVE_SAMPLES`条负样本组成一个group,因此每条数据会扩展成`MAX_POSITIVE_SAMPLES`x`(1 + MAX_NEGATIVE_SAMPLES)`条数据。
|
||||
> 如果数据中正例/负例数量不足,会取全部正例/负例,如果数据中正例和负例数量超过`MAX_POSITIVE_SAMPLES`和`MAX_NEGATIVE_SAMPLES`,会进行随机采样。
|
||||
> **IMPORTANT**:展开后的数据会放在同一个batch中,因此每个设备上的实际批处理大小(effective batch size)将是 `per_device_train_batch_size` × `MAX_POSITIVE_SAMPLES` × (1 + `MAX_NEGATIVE_SAMPLES`)。请注意调整 `per_device_train_batch_size` 以避免显存不足。
|
||||
|
||||
## 脚手架
|
||||
|
||||
SWIFT提供的脚手架训练脚本:
|
||||
|
||||
- [Qwen3-Reranker/Qwen3-VL-Reranker](https://github.com/modelscope/ms-swift/blob/main/examples/train/reranker/qwen3)
|
||||
- [Pointwise分类式Reranker](https://github.com/modelscope/ms-swift/blob/main/examples/train/reranker/train_reranker.sh)
|
||||
- [Pointwise生成式Reranker](https://github.com/modelscope/ms-swift/blob/main/examples/train/reranker/train_generative_reranker.sh)
|
||||
- [Listwise分类式Reranker](https://github.com/modelscope/ms-swift/blob/main/examples/train/reranker/train_reranker_listwise.sh)
|
||||
- [Listwise生成式Reranker](https://github.com/modelscope/ms-swift/blob/main/examples/train/reranker/train_generative_reranker_listwise.sh)
|
||||
|
||||
推理脚本参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_reranker.py)。
|
||||
|
||||
## 高级功能
|
||||
|
||||
- Qwen3-Reranker 自定义 Instruction:
|
||||
- 默认模板如下:
|
||||
|
||||
```text
|
||||
<|im_start|>system
|
||||
Judge whether the Document meets the requirements based on the Query and the Instruct provided. Note that the answer can only be "yes" or "no".<|im_end|>
|
||||
<|im_start|>user
|
||||
<Instruct>: {Instruction}
|
||||
<Query>: {Query}
|
||||
<Document>: {Document}<|im_end|>
|
||||
<|im_start|>assistant
|
||||
<think>
|
||||
|
||||
</think>
|
||||
|
||||
|
||||
```
|
||||
|
||||
- 默认 Instruction:
|
||||
- `Given a web search query, retrieve relevant passages that answer the query`
|
||||
|
||||
- Instruction 优先级(就近覆盖):
|
||||
- `positive_messages`/`negative_messages` 内提供的 `system` > 主 `messages` 的 `system` > 默认 Instruction。
|
||||
- 即:若某个 positive/negative 的消息序列内包含 `system`,则优先使用该条;否则若主 `messages` 含 `system` 则使用之;两者都未提供时,使用默认 Instruction。
|
||||
@@ -0,0 +1,259 @@
|
||||
# DeepSeek-V4 训练支持
|
||||
|
||||
|
||||
目前Megatron-SWIFT支持了DeepSeek-V4的微调与RL支持,包括MTP、FP8等特性。(FP4 blockwise训练暂时不支持,会在加载权重时自动转成FP8/BF16)
|
||||
|
||||
你需要使用Megatron-Core dev分支以及mcore-bridge、ms-swift main分支。
|
||||
|
||||
```shell
|
||||
pip install git+https://github.com/NVIDIA/Megatron-LM.git@dev
|
||||
pip install git+https://github.com/modelscope/mcore-bridge.git
|
||||
pip install git+https://github.com/modelscope/ms-swift.git
|
||||
|
||||
# Megatron-LM在以下commit hash下进行测试
|
||||
# pip install git+https://github.com/NVIDIA/Megatron-LM.git@c6449f0b23be397449f21c0967c5fc90785e55ea
|
||||
```
|
||||
|
||||
## 精度对齐
|
||||
|
||||
- 为了支持精度对齐测试(FP32),你需注释掉[这几行](https://github.com/NVIDIA/Megatron-LM/blob/bd381ac364b5139840f0cba6389db54f2c092e90/megatron/core/transformer/experimental_attention_variant/dsa.py#L41-L43)。
|
||||
|
||||
修改完代码后,测试以下代码,确认无精度对齐问题(测试transformers/megatron forward对齐情况):
|
||||
|
||||
创建mini版本的模型,我们将创建4层:
|
||||
|
||||
```python
|
||||
import os
|
||||
import torch
|
||||
from modelscope.hub.file_download import model_file_download
|
||||
from safetensors.torch import safe_open
|
||||
from swift import safe_snapshot_download
|
||||
|
||||
from mcore_bridge.utils import Fp8Dequantizer, SafetensorLazyLoader, StreamingSafetensorSaver
|
||||
|
||||
model_id = 'deepseek-ai/DeepSeek-V4-Flash-Base'
|
||||
# Some models have the first few layers as dense and the rest as MoE; set this value accordingly
|
||||
model_dir = safe_snapshot_download(model_id, download_model=False)
|
||||
|
||||
loader = SafetensorLazyLoader(model_dir)
|
||||
state_dict = loader.get_state_dict()
|
||||
saver = StreamingSafetensorSaver(save_dir=model_dir)
|
||||
fp8_dequantizer = Fp8Dequantizer() # Used to convert fp8 weights to bf16
|
||||
|
||||
|
||||
def _open_file(self, filename: str):
|
||||
if filename not in self._file_handles:
|
||||
file_path = os.path.join(self.hf_model_dir, filename)
|
||||
tmp_dir = os.path.join(self.hf_model_dir, 'tmp')
|
||||
if not os.path.exists(file_path):
|
||||
file_path = os.path.join(tmp_dir, filename)
|
||||
if not os.path.exists(file_path):
|
||||
file_path = model_file_download(
|
||||
model_id=model_id,
|
||||
file_path=filename,
|
||||
local_dir=tmp_dir,
|
||||
)
|
||||
self._file_handles[filename] = safe_open(file_path, framework='pt')
|
||||
return self._file_handles[filename]
|
||||
|
||||
|
||||
SafetensorLazyLoader._open_file = _open_file # monkey patch (lazy downloading)
|
||||
new_state_dict = {}
|
||||
for k, v in state_dict.items():
|
||||
if k.startswith('layers.'):
|
||||
idx = int(k[len('layers.'):].split('.', 1)[0])
|
||||
if idx >= 4:
|
||||
continue
|
||||
if k.endswith('.scale'):
|
||||
continue
|
||||
elif k.endswith('.weight'):
|
||||
weight_scale_inv = k.replace('.weight', '.scale')
|
||||
if weight_scale_inv in state_dict:
|
||||
v = fp8_dequantizer.convert(v.load(), state_dict[weight_scale_inv].load()).to(torch.bfloat16)
|
||||
new_state_dict[k] = v if isinstance(v, torch.Tensor) else v.load()
|
||||
|
||||
for k, v in new_state_dict.items():
|
||||
saver.add_tensor(k, v)
|
||||
saver.finalize()
|
||||
```
|
||||
然后修改`config.json`:
|
||||
- num_hidden_layers修改为`4`。
|
||||
- compress_ratios修改为`[0, 0, 4, 128, 0]`。
|
||||
- 删除`quantization_config`。
|
||||
|
||||
|
||||
然后创建`test.py`,使用以下命令运行:`CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --nproc_per_node=4 test.py`。更多参考[自定义Megatron模型文档](https://swift.readthedocs.io/zh-cn/latest/Megatron-SWIFT/Custom-Model.html)。
|
||||
|
||||
```python
|
||||
import os
|
||||
|
||||
os.environ['SWIFT_TEST_CONVERT_PRECISION'] = '1'
|
||||
|
||||
from swift.megatron import MegatronExportArguments, megatron_export_main
|
||||
from swift import safe_snapshot_download
|
||||
model_id = 'deepseek-ai/DeepSeek-V4-Flash-Base'
|
||||
|
||||
model_dir = safe_snapshot_download(model_id, download_model=False)
|
||||
|
||||
if __name__ == '__main__':
|
||||
megatron_export_main(
|
||||
MegatronExportArguments(
|
||||
model=model_dir,
|
||||
to_mcore=True,
|
||||
attention_backend='flash',
|
||||
tensor_model_parallel_size=1,
|
||||
pipeline_model_parallel_layout='Et*3|t*1mL',
|
||||
pipeline_model_parallel_size=2,
|
||||
expert_model_parallel_size=2,
|
||||
mtp_num_layers=1,
|
||||
test_convert_precision=True,
|
||||
))
|
||||
```
|
||||
|
||||
当出现以下结果时,则表示对齐没有问题,可以进行训练了。
|
||||

|
||||
|
||||
|
||||
## LoRA训练
|
||||
|
||||
BF16精度LoRA训练脚本如下,最后会保存LoRA增量权重和Merge-LoRA后的BF16完整权重。
|
||||
|
||||
```shell
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
NPROC_PER_NODE=8 \
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
|
||||
megatron sft \
|
||||
--model deepseek-ai/DeepSeek-V4-Flash \
|
||||
--save_safetensors true \
|
||||
--dataset 'AI-ModelScope/alpaca-gpt4-data-zh#1000' \
|
||||
'AI-ModelScope/alpaca-gpt4-data-en#1000' \
|
||||
'swift/self-cognition#1000' \
|
||||
--model_author swift \
|
||||
--model_name swift-robot \
|
||||
--merge_lora true \
|
||||
--load_from_cache_file true \
|
||||
--add_non_thinking_prefix true \
|
||||
--loss_scale ignore_empty_think \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--tuner_type lora \
|
||||
--lora_rank 16 \
|
||||
--lora_alpha 32 \
|
||||
--tensor_model_parallel_size 1 \
|
||||
--expert_model_parallel_size 8 \
|
||||
--micro_batch_size 4 \
|
||||
--global_batch_size 32 \
|
||||
--padding_free false \
|
||||
--group_by_length true \
|
||||
--recompute_granularity full \
|
||||
--recompute_method uniform \
|
||||
--recompute_num_layers 1 \
|
||||
--moe_permute_fusion true \
|
||||
--moe_grouped_gemm true \
|
||||
--moe_shared_expert_overlap true \
|
||||
--moe_aux_loss_coeff 1e-3 \
|
||||
--num_train_epochs 1 \
|
||||
--finetune true \
|
||||
--cross_entropy_loss_fusion true \
|
||||
--lr 1e-4 \
|
||||
--lr_warmup_fraction 0.05 \
|
||||
--min_lr 1e-5 \
|
||||
--output_dir megatron_output/DeepSeek-V4-Flash \
|
||||
--eval_steps 200 \
|
||||
--save_steps 200 \
|
||||
--max_length 4096 \
|
||||
--dataloader_num_workers 8 \
|
||||
--dataset_num_proc 8 \
|
||||
--no_save_optim true \
|
||||
--no_save_rng true \
|
||||
--sequence_parallel true \
|
||||
--mtp_num_layers 1 \
|
||||
--attention_backend flash
|
||||
```
|
||||
|
||||
显存占用:
|
||||

|
||||
|
||||
|
||||
训练日志与损失:
|
||||

|
||||
|
||||
提示:
|
||||
- 如果你要设置pp并行,你需要额外设置`pipeline_model_parallel_layout`。例如:
|
||||
```
|
||||
--pipeline_model_parallel_size 2 \
|
||||
--pipeline_model_parallel_layout 'Et*22|t*21mL' \
|
||||
```
|
||||
- 全参数训练也是支持的,你需要降低learning_rate,并提高并行数。参考64卡训练例子:
|
||||
```
|
||||
--lr 1e-5 \
|
||||
--min_lr 1e-6 \
|
||||
--tensor_model_parallel_size 1 \
|
||||
--expert_model_parallel_size 8 \
|
||||
--pipeline_model_parallel_size 8 \
|
||||
--pipeline_model_parallel_layout Et*5|t*5|t*6|t*6|t*6|t*5|t*5|t*5mL \
|
||||
```
|
||||
- Packing/CP的支持:需安装mcore-bridge/ms-swift main分支。参考这两个PR:[ms-swift#9705](https://github.com/modelscope/ms-swift/pull/9705)、[mcore-bridge#140](https://github.com/modelscope/mcore-bridge/pull/140)。若要使用CP,你需要额外设置(需结合packing一起使用`--packing true`,并注意这个PR的合并[megatron-core#5706](https://github.com/NVIDIA/Megatron-LM/pull/5706)):
|
||||
```
|
||||
--sequence_packing_scheduler dp_balanced \
|
||||
--cp_partition_mode contiguous \
|
||||
```
|
||||
- 暂时不支持TP,待Megatron-Core支持。
|
||||
- FP8训练:你可以设置以下参数开启FP8训练,并最终将权重保存成FP8权重。推荐使用全参数训练。如果要使用LoRA + FP8,你需要只保存LoRA权重(设置`--merge_lora false`),并使用BF16权重进行Merge-LoRA(FP8 精度有限,LoRA delta 会被舍入为 0)。参考[这个例子](https://github.com/modelscope/ms-swift/blob/main/examples/megatron/fp8/lora.sh)。
|
||||
```
|
||||
--fp8_recipe blockwise \
|
||||
--fp8_format e4m3 \
|
||||
--fp8_param_gather true \
|
||||
```
|
||||
|
||||
推理训练后的模型:
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
|
||||
swift infer \
|
||||
--model megatron_output/DeepSeek-V4-Flash/vx-xxx/checkpoint-xxx-merged \
|
||||
--infer_backend transformers \
|
||||
--enable_thinking false \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
推理结果:
|
||||
|
||||

|
||||
|
||||
跑通vLLM推理:
|
||||
|
||||
- 如果要使用vllm推理,你可以参考[这里的文档](https://recipes.vllm.ai/deepseek-ai/DeepSeek-V4-Flash)。你需要FP4/FP8精度的权重。
|
||||
- 此外你需要copy原始的'config.json'文件,并修改'expert_dtype'(与训练后的config.json一致)。因为,使用transformers的`config.save_pretrained`保存的文件与原始文件不同,vllm不兼容保存后的文件。
|
||||
- 如果遇到tilelang问题,可以查看[这个issue](https://github.com/modelscope/ms-swift/issues/9494)。
|
||||
- mcore-bridge DeepSeek-V4 Fp8修复:[PR](https://github.com/modelscope/mcore-bridge/pull/133)。
|
||||
|
||||
这里先做量化(这里的量化会导致LoRA增量信息丢失,这里只作为例子,建议使用FP8全参数训练并导出FP8权重):
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
|
||||
NPROC_PER_NODE=8 \
|
||||
megatron export \
|
||||
--model megatron_output/DeepSeek-V4-Flash/vx-xxx/checkpoint-xxx-merged \
|
||||
--output_dir megatron_output/DeepSeek-V4-Flash/vx-xxx/checkpoint-xxx-merged-FP8 \
|
||||
--to_hf true \
|
||||
--fp8_recipe blockwise \
|
||||
--fp8_format e4m3 \
|
||||
--fp8_param_gather true \
|
||||
--mtp_num_layers 1 \
|
||||
--expert_model_parallel_size 8
|
||||
```
|
||||
|
||||
vLLM启动命令:
|
||||
```shell
|
||||
vllm serve megatron_output/DeepSeek-V4-Flash/vx-xxx/checkpoint-xxx-merged-FP8 \
|
||||
--trust-remote-code \
|
||||
--kv-cache-dtype fp8 \
|
||||
--block-size 256 \
|
||||
--enable-expert-parallel \
|
||||
--tensor-parallel-size 8 \
|
||||
--max-model-len 8192 \
|
||||
--tokenizer-mode deepseek_v4 \
|
||||
--tool-call-parser deepseek_v4 \
|
||||
--enable-auto-tool-choice \
|
||||
--reasoning-parser deepseek_v4
|
||||
```
|
||||
@@ -0,0 +1,233 @@
|
||||
# 架构介绍
|
||||
|
||||
ms-swift 4.0 采用模块化设计,各功能模块分布在一级目录下,便于开发者进行自定义扩展。本文档将详细介绍各模块的功能及自定义方法。
|
||||
|
||||
## Agent Template
|
||||
|
||||
agent模板的mapping文件可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/agent_template/mapping.py)。agent template设计目标是,基于统一的Agent数据集格式,可以灵活切换不同模型进行训练,无需修改数据。训练时使用`--agent_template`指定对应的agent模板。
|
||||
|
||||
所有的AgentTemplate需要继承自`BaseAgentTemplate`,并实现其中的几个方法: `_format_tools`, `_format_tool_calls`, `_format_tool_responses`, `get_toolcall`。
|
||||
- _format_tools: 将`tools`和`system`格式化,组成完整的system。
|
||||
- _format_tool_calls: 将tool_call部分 `[{"role": "tool_call", "content": "..."}, {"role": "tool_call", "content": "..."}]`进行格式化,最后返回字符串。
|
||||
- _format_tool_responses: 对tool(也称为tool_response)部分 `[{"role": "tool", "content": "..."}, {"role": "tool", "content": "..."}]`进行格式化。
|
||||
- get_toolcall: 在部署的时候使用,用于解析模型输出内容中的工具名和参数,返回`List[Function]`。
|
||||
|
||||
|
||||
如何debug:
|
||||
```python
|
||||
data = {"tools": "[{\"type\": \"function\", \"function\": {\"name\": \"realtime_aqi\", \"description\": \"天气预报。获取实时空气质量。当前空气质量,PM2.5,PM10信息\", \"parameters\": {\"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\", \"description\": \"城市名,例如:上海\"}}, \"required\": [\"city\"]}}}]", "messages": [{"role": "user", "content": "北京和上海今天的天气情况"}, {"role": "tool_call", "content": "{\"name\": \"realtime_aqi\", \"arguments\": {\"city\": \"北京\"}}"}, {"role": "tool_call", "content": "{\"name\": \"realtime_aqi\", \"arguments\": {\"city\": \"上海\"}}"}, {"role": "tool_response", "content": "{\"city\": \"北京\", \"aqi\": \"10\", \"unit\": \"celsius\"}"}, {"role": "tool_response", "content": "{\"city\": \"上海\", \"aqi\": \"72\", \"unit\": \"fahrenheit\"}"}, {"role": "assistant", "content": "根据天气预报工具,北京今天的空气质量指数为10,属于良好水平;上海今天的空气质量指数为72,属于轻度污染水平。"}]}
|
||||
|
||||
|
||||
from swift import get_processor, get_template
|
||||
|
||||
tokenizer = get_processor('Qwen/Qwen3.5-2B')
|
||||
template = get_template(tokenizer) # 使用默认agent模板
|
||||
# template = get_template(tokenizer, agent_template='qwen3_5')
|
||||
print(f'agent_template: {template._agent_template}')
|
||||
template.set_mode('train')
|
||||
encoded = template.encode(data)
|
||||
print(f'[INPUT_IDS] {template.safe_decode(encoded["input_ids"])}\n')
|
||||
print(f'[LABELS] {template.safe_decode(encoded["labels"])}')
|
||||
```
|
||||
|
||||
如果你想要给我们提供PR,请参考[这里](https://github.com/modelscope/ms-swift/blob/main/tests/test_align/test_template/test_agent.py)书写你的测试案例。
|
||||
|
||||
## Callbacks
|
||||
|
||||
callbacks的mapping文件可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/callbacks/mapping.py)。callbacks可以对trainer中的关键节点的行为进行自定义。自定义后,你需要在mapping中进行注册,训练时使用`--callbacks`指定对应的回调类。例如,你可以自定义:
|
||||
|
||||
```python
|
||||
class CustomCallback(TrainerCallback):
|
||||
|
||||
def on_train_begin(self, args: TrainingArguments, state: TrainerState, control: TrainerControl, **kwargs):
|
||||
# Doing something when the training begins.
|
||||
pass
|
||||
|
||||
def on_save(self, args: TrainingArguments, state: TrainerState, control: TrainerControl, **kwargs):
|
||||
# Doing something when save checkpoint
|
||||
pass
|
||||
```
|
||||
|
||||
所有的回调类需继承自base.py中的`TrainerCallback`,并覆盖其方法。接口与transformers的`TrainerCallback`一致,请参考transformers的[callback文档](https://huggingface.co/docs/transformers/main_classes/callback)。
|
||||
|
||||
|
||||
## Loss
|
||||
|
||||
Loss的mapping文件可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/loss/mapping.py)。
|
||||
swift支持自定义loss(当前只支持sft/pretrain/reranker/embedding任务),注册后在训练时设置`--loss_type <loss-name>`使用你定制的loss方法。
|
||||
|
||||
自定义Loss需继承自`BaseLoss`,并实现`__call__`方法,返回标量Tensor。你可以参考[CustomCrossEntropyLoss](https://github.com/modelscope/ms-swift/blob/0d7c9f5bc0e7e7d67d914ce6edeb9ce24f60746f/swift/loss/causal_lm.py#L5)进行定制。例如:
|
||||
|
||||
```python
|
||||
class CustomLoss(BaseLoss):
|
||||
|
||||
def __call__(self, outputs, labels, **kwargs) -> torch.Tensor:
|
||||
pass
|
||||
```
|
||||
|
||||
## Loss Scale
|
||||
|
||||
loss scale的mapping文件可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/loss_scale/mapping.py)。在pretrain和sft任务中,可训练token的loss是平均的,即每个token平等地对待。但在某些情况下,某些token需要被额外关注,并设置更高的权重或者对某些token不进行训练。loss_scale可以让开发者自由地定义自己的token权重。(预训练和SFT支持使用loss_scale控制token是否参与训练以及和其权重大小,RLHF中只支持控制token是否参与训练)
|
||||
|
||||
你可以通过继承LossScale基类,并实现`get_loss_scale`方法来自定义loss scale。
|
||||
```python
|
||||
class CustomLossScale(LossScale):
|
||||
|
||||
def get_loss_scale(self, context: str, **kwargs) -> Tuple[List[str], List[float]]:
|
||||
...
|
||||
```
|
||||
`get_loss_scale`函数需要返回了一个Tuple,第一个返回是拆解后的字符串的列表,第二个参数是字符串对应的loss_scale的列表,float值代表了权重。例如下面的权重设置:
|
||||
```text
|
||||
["学习", "好", "数学", "是", "重要", "的"]
|
||||
[1.0, 0.5, 2.0, 0.5, 2.0, 0.1]
|
||||
```
|
||||
例子中,我们更看重数学和重要两个词,因为其loss_scale为2.0。
|
||||
|
||||
|
||||
当然我们也需要关注`__call__`方法的核心逻辑,即loss_scale基本策略(base_strategy)all/default/last_round 对loss_scale的影响,具体参考[命令行参数文档](../Instruction/Command-line-parameters.md)的介绍。以及数据集中的'loss'字段对loss_scale的影响,参考[自定义数据集文档](../Customization/Custom-dataset.md)。
|
||||
```python
|
||||
if loss or loss is None and (self.base_strategy == 'all' or
|
||||
(self.base_strategy == 'default' and is_assistant) or
|
||||
(self.base_strategy == 'last_round' and is_assistant and is_last_round)):
|
||||
new_context, loss_scale = self.get_loss_scale(context, query=query)
|
||||
else:
|
||||
new_context, loss_scale = [context], [0.]
|
||||
```
|
||||
|
||||
此外你也可以使用[json配置文件](https://github.com/modelscope/ms-swift/tree/main/swift/loss_scale/config),继承内置的ConfigLossScale类,来自定义loss_scale。目前支持两种配置方式:字符串精确匹配和正则表达式匹配。你可以参考[Agent支持文档](../Instruction/Agent-support.md#loss_scale的使用)的内容进行理解。
|
||||
|
||||
- 字符串精确匹配,例如参考`react.json`, `qwen.json`。json中需要书写`Dict[str, List[float]]`的映射。字符串代表关键词,列表中需要有两个值。我们会根据关键词,将字符串切分成多段字符串。列表的第一个值代表关键词的权重,列表的第二个值代表该关键值后,下一关键词前的内容的权重。
|
||||
|
||||
- 正则表达式匹配,例如参考`ignore_empty_think.json`, `hermes.json`。json中需要书写`Dict[str, float]`的映射。字符串代表正则表达式pattern,浮点数代表匹配字符串的权重。
|
||||
|
||||
|
||||
如何debug:
|
||||
```python
|
||||
from swift import get_processor, get_template
|
||||
|
||||
data = {"messages": [
|
||||
{"role": "user", "content": "今天的日期是多少?"},
|
||||
{"role": "assistant", "content": (
|
||||
"<think>\n我可以通过调用`get_date`函数来获取当前时间。\n</think>\n"
|
||||
'<tool_call>\n{"name": "get_date", "arguments": {}}\n</tool_call>'
|
||||
)}
|
||||
]}
|
||||
|
||||
template = get_template(get_processor('Qwen/Qwen3-8B'), loss_scale='hermes')
|
||||
template.set_mode('train')
|
||||
inputs = template.encode(data)
|
||||
|
||||
print(template.safe_decode(inputs['labels']))
|
||||
print(inputs['loss_scale'])
|
||||
```
|
||||
|
||||
## Metrics
|
||||
|
||||
metrics的mapping文件可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/metrics/mapping.py)。该组件在ms-swift/Megatron-SWIFT中都有被使用。
|
||||
- 如果是在ms-swift中被使用,你需要继承 base.py 中`EvalMetrics`基类,并实现`compute_metrics`函数,返回字典`Dict[str, float]`。你可以参考[NlgMetrics](https://github.com/modelscope/ms-swift/blob/0d7c9f5bc0e7e7d67d914ce6edeb9ce24f60746f/swift/metrics/nlg.py#L33)进行定制。
|
||||
- 如果是在Megatron-SWIFT中被使用,你需要继承 utils.py 中`Metric`基类,并实现`update`和`compute`方法,compute方法需返回字典`Dict[str, float]`。
|
||||
|
||||
你可以自定义metrics(当前只支持sft/pretrain/reranker/embedding任务),在训练时设置`--eval_metric <metric-name>`使用你定制的metrics。
|
||||
|
||||
## Optimizers
|
||||
|
||||
optimizer的mapping文件可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/optimizers/mapping.py)。如果你需要自定义优化器,你需要继承`OptimizerCallback`基类,并覆盖`create_optimizer`函数。训练时使用`--optimizer <optimizer-name>`指定自定义的优化器。
|
||||
- 你可以参考[MultimodalOptimizerCallback](https://github.com/modelscope/ms-swift/blob/0d7c9f5bc0e7e7d67d914ce6edeb9ce24f60746f/swift/optimizers/multimodal.py#L43)进行实现,该类实现了vit_lr, aligner_lr的功能,即对vit, aligner和LLM分别使用不同的学习率。
|
||||
|
||||
|
||||
|
||||
## Tuner Plugin
|
||||
|
||||
Tuner插件的mapping文件可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/tuner_plugin/mapping.py)。如果你需要自定义tuner,你需要继承`Tuner`基类,并覆盖`prepare_model`, `save_pretrained`, `from_pretrained`函数。
|
||||
- prepare_model: 该函数在训练前被调用,将原始模型进行处理与准备,使用tuner封装,并设置可训练参数。例如:你可以对某些层附加LoRA,对某些层进行冻结等。
|
||||
- save_pretrained: 该函数在训练中被调用,对模型进行保存。
|
||||
- from_pretrained: 该函数在推理/断点续训时被调用,准备模型并读取权重。
|
||||
|
||||
你可以参考[LoRALLMTuner](https://github.com/modelscope/ms-swift/blob/0d7c9f5bc0e7e7d67d914ce6edeb9ce24f60746f/swift/tuner_plugin/lora_llm.py#L24)进行实现,该类实现了对LLM进行LoRA训练,对ViT进行全参数训练的功能。
|
||||
|
||||
|
||||
## ORM
|
||||
|
||||
example参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/rewards/orm.py)。
|
||||
|
||||
ORM是结果奖励模型。ORM一般使用正则表达式来进行,ORM决定了response是否是正确的。例如:
|
||||
|
||||
```python
|
||||
class MathORM(ORM):
|
||||
|
||||
@staticmethod
|
||||
def extract_boxed_result(text):
|
||||
pattern = r'\\boxed{([^}]*)}'
|
||||
match = re.search(pattern, text)
|
||||
if match:
|
||||
return match.group(1).strip()
|
||||
else:
|
||||
return None
|
||||
|
||||
def __call__(self, infer_requests: List[InferRequest], ground_truths: List[str],
|
||||
**kwargs) -> List[float]:
|
||||
rewards = []
|
||||
predictions = [request.messages[-1]['content'] for request in infer_requests]
|
||||
for prediction, ground_truth in zip(predictions, ground_truths):
|
||||
res1 = MathORM.extract_boxed_result(prediction) or ''
|
||||
res2 = MathORM.extract_boxed_result(ground_truth) or ''
|
||||
rewards.append(float(res1.strip() == res2.strip()))
|
||||
|
||||
return rewards
|
||||
|
||||
|
||||
orms = {
|
||||
'math': MathORM,
|
||||
}
|
||||
```
|
||||
|
||||
在上面的代码中,我们定义了一个对数学response进行解析的过程,如果结果相同则返回score为1.0,否则为0.0。和PRM不同,这个类的infer中有一个额外参数`ground_truths`,
|
||||
该参数是对应的infer_requests的实际label(数据集中定义的标准response)。
|
||||
|
||||
|
||||
## PRM
|
||||
|
||||
example参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/rewards/prm.py)。
|
||||
|
||||
PRM是过程奖励模型,PRM会在`swift sample`命令中使用。PRM需要支持的接口比较简单:
|
||||
```python
|
||||
class PRM:
|
||||
|
||||
def __init__(self):
|
||||
# init here
|
||||
pass
|
||||
|
||||
def __call__(self, infer_requests: List[InferRequest], **kwargs) -> List[Union[float, List[float]]]:
|
||||
raise NotImplementedError
|
||||
```
|
||||
|
||||
其中的InferRequest来自于`swift.infer_engine`,返回的`List[Union[float, List[float]]]`,列表中可能是reward也可能是若干reward。开发者可以在infer_requests中拿到queries和responses,并按照自己的方式进行切分,例如:
|
||||
```text
|
||||
Let's think step by step.
|
||||
|
||||
Step1: xxx
|
||||
|
||||
Step2: xxx
|
||||
|
||||
So, the answer is ...
|
||||
```
|
||||
开发者可以在这里对过程进行切分,并按batch传入PRM中进行推理并返回rewards。更通用来说,开发者可以在这里调用一个远端URL,例如一个闭源PRM大模型并返回rewards。
|
||||
|
||||
|
||||
## 其他目录结构介绍
|
||||
|
||||
- arguments: 命令行参数定义,例如:`SftArguments`, `RLHFArguments`等。
|
||||
- cli: swift命令行机制以及启动文件。例如`swift sft ...`等价于`python swift/cli/main.py sft ...`也等价于`python swift/cli/sft.py ...`。
|
||||
- config: deepspeed/fsdp2配置文件。
|
||||
- dataloader: dataloader的实现,包括shard/dispatcher两种方式。
|
||||
- dataset: 数据集相关模块实现,包括数据预处理、packing、流式数据等。内置数据集的注册在`dataset/dataset`和`dataset/data`文件夹内。具体参考[自定义数据集文档](Custom-dataset.md)。
|
||||
- infer_engine: 推理引擎实现。包括transformers/vllm/sglang/lmdeploy为后端的推理引擎实现。
|
||||
- megatron: Megatron-SWIFT 实现。
|
||||
- model: 模型加载与注册。具体参考[自定义模型文档](Custom-model.md),[多模态模型注册最佳实践](../BestPractices/MLLM-Registration.md)。
|
||||
- pipelines: `swift sft/rlhf/infer`等主函数pipeline实现,包括`sft_main/rlhf_main/infer_main`等。
|
||||
- rlhf_trainers: GRPO/GKD/DPO/KTO/RM等算法的Trainer实现。
|
||||
- rollout: RL算法中rollout过程的采样实现。
|
||||
- rewards: RL算法中的奖励函数实现,支持自定义奖励计算逻辑。
|
||||
- template: 对话模板的实现与注册,包含各个任务将messages转换成input_ids的逻辑,以及data_collator相关逻辑。具体参考[自定义模型文档](Custom-model.md),[多模态模型注册最佳实践](../BestPractices/MLLM-Registration.md)。
|
||||
- trainers: 预训练/SFT/Embedding/Reranker/序列分类任务的Trainer实现。
|
||||
- ui: `swift web-ui`界面训练与推理实现。
|
||||
@@ -0,0 +1,427 @@
|
||||
# 自定义数据集
|
||||
|
||||
自定义数据集的接入方法有三种,对预处理函数的控制能力逐渐加强,但接入难度逐步增加。例如,方案一最为方便,但对预处理函数的控制能力最弱,需要预先对数据集进行转换,传入特定格式的数据集:
|
||||
1. 【推荐】直接使用命令行传参的方式接入,即`--dataset <dataset_path1> <dataset_path2>`。这将使用AutoPreprocessor将数据集转换为标准格式(支持4种数据集格式,具体查看下面对AutoPreprocessor的介绍)。你可以使用`--columns`进行列名转换。支持传入csv、json、jsonl、txt、文件夹(例如git clone开源数据集)。该方案不需要修改dataset_info.json,适合刚接触ms-swift的用户,下面两种方案适合对ms-swift进行拓展的开发者。
|
||||
2. 添加数据集到`dataset_info.json`中,可以参考ms-swift内置的[dataset_info.json](https://github.com/modelscope/ms-swift/blob/main/swift/dataset/data/dataset_info.json)。该方案也将使用AutoPreprocessor将数据集转换为标准格式。dataset_info.json为数据集元信息的list,每一项元信息必填ms_dataset_id/hf_dataset_id/dataset_path中的一项,通过`columns`字段进行列名转换。添加到`dataset_info.json`或者注册的数据集在运行[run_dataset_info.py](https://github.com/modelscope/ms-swift/blob/main/scripts/utils/run_dataset_info.py)时将自动产生[支持的数据集文档](https://swift.readthedocs.io/zh-cn/latest/Instruction/Supported-models-and-datasets.html)。此外,你可以采用外接`dataset_info.json`的方式,使用`--custom_dataset_info xxx.json`解析json文件(方便pip install而非git clone的用户),然后指定`--dataset <dataset_id/dataset_dir/dataset_path>`。
|
||||
3. 手动注册数据集,具有最灵活的预处理函数定制能力,支持使用函数对数据集进行预处理,但难度较高。可以参考[内置数据集](https://github.com/modelscope/ms-swift/blob/main/swift/dataset/dataset/llm.py)或者[examples](https://github.com/modelscope/ms-swift/blob/main/examples/custom)中的样例。你可以通过指定`--external_plugins xxx.py`解析外置注册内容(方便pip install而非git clone的用户)。
|
||||
- 方案一和二在实现中借助了方案三,只是注册的过程为自动发生。
|
||||
|
||||
以下将对`AutoPreprocessor`可以处理的数据集格式进行介绍:
|
||||
|
||||
ms-swift的标准数据集格式可接受的keys包括: 'messages'、'rejected_response'、'label'、'images'、'videos'、'audios'、'tools'和'objects'。其中'messages'是必需的key,'rejected_response'用于DPO等RLHF训练,'label'用于KTO训练和分类模型训练,'images'、'videos'、'audios'用于存储多模态数据的路径或者url,'tools'用于Agent任务,'objects'用于grounding任务。
|
||||
|
||||
ms-swift中存在三种核心预处理器:`MessagesPreprocessor`、`AlpacaPreprocessor`、`ResponsePreprocessor`。MessagesPreprocessor用于将类messages和sharegpt格式的数据集转换为标准格式,AlpacaPreprocessor则转换alpaca格式的数据集,ResponsePreprocessor则转换类query/response格式的数据集。`AutoPreprocessor`则自动选择合适的预处理进行处理。
|
||||
|
||||
以下四种格式在`AutoPreprocessor`处理下都会转换成ms-swift标准格式中的messages字段,即都可以直接使用`--dataset <dataset-path>`接入:
|
||||
|
||||
messages格式(标准格式):
|
||||
```jsonl
|
||||
{"messages": [{"role": "system", "content": "<system>"}, {"role": "user", "content": "<query1>"}, {"role": "assistant", "content": "<response1>"}, {"role": "user", "content": "<query2>"}, {"role": "assistant", "content": "<response2>"}]}
|
||||
```
|
||||
- 注意:system部分是可选的。数据集中的system优先级高于命令行传入的`--system`,最后是定义在template中的`default_system`。
|
||||
|
||||
sharegpt格式:
|
||||
```jsonl
|
||||
{"system": "<system>", "conversation": [{"human": "<query1>", "assistant": "<response1>"}, {"human": "<query2>", "assistant": "<response2>"}]}
|
||||
```
|
||||
|
||||
query-response格式:
|
||||
```jsonl
|
||||
{"system": "<system>", "query": "<query2>", "response": "<response2>", "history": [["<query1>", "<response1>"]]}
|
||||
```
|
||||
注意:以下字段会自动转成对应的system、query、response字段。(solution字段会保留)
|
||||
- system: 'system', 'system_prompt'.
|
||||
- query: 'query', 'prompt', 'input', 'instruction', 'question', 'problem'.
|
||||
- response: 'response', 'answer', 'output', 'targets', 'target', 'answer_key', 'answers', 'solution', 'text', 'completion', 'content'.
|
||||
|
||||
alpaca格式:
|
||||
```jsonl
|
||||
{"system": "<system>", "instruction": "<query-inst>", "input": "<query-input>", "output": "<response>"}
|
||||
```
|
||||
- 注意:instruction和input字段将组合成query字段。若instruction和input不等于空字符串,`query = f'{instruction}\n{input}'`
|
||||
|
||||
|
||||
## 标准数据集格式
|
||||
|
||||
以下给出ms-swift的标准数据集格式,其中system字段是可选的,默认使用template中定义的`default_system`。之前介绍的4种数据集格式也可以被AutoPreprocessor处理成标准数据集格式。
|
||||
|
||||
### 预训练
|
||||
|
||||
```jsonl
|
||||
{"messages": [{"role": "assistant", "content": "I love music"}]}
|
||||
{"messages": [{"role": "assistant", "content": "教练我要打篮球"}]}
|
||||
{"messages": [{"role": "assistant", "content": "西红柿鸡蛋盖饭和地三鲜盖饭哪个更权威"}]}
|
||||
```
|
||||
|
||||
### 监督微调
|
||||
|
||||
```jsonl
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的助手"}, {"role": "user", "content": "告诉我明天的天气"}, {"role": "assistant", "content": "明天天气晴朗"}]}
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的数学计算器"}, {"role": "user", "content": "1+1等于几"}, {"role": "assistant", "content": "等于2"}, {"role": "user", "content": "再加1呢"}, {"role": "assistant", "content": "等于3"}]}
|
||||
```
|
||||
|
||||
- 可以通过增加"loss"字段,控制对应的模型回复部分("role"为"assistant")是否计算损失。默认该字段为None。若"loss"设置为true,则对应content进行损失计算(具体loss_scale依旧由`--loss_scale`决定);若"loss"设置为false,则对应content不进行损失计算。需要注意的是,该功能只对"role"为"assistant"的部分生效;该功能优先级高于命令行参数 `--loss_scale`的基本策略(即'default'、'last_round'、'all'部分),例如,loss_scale为`'default+ignore_empty_think'`时,"loss"字段优先级高于"default",但'ignore_empty_think'依旧发挥作用。
|
||||
- 可以通过增加"loss_scale"字段,控制对应的模型回复部分("role"为"assistant")的loss_scale。(ms-swift>=4.2.0)默认为None。该功能优先级高于命令行参数 `--loss_scale`的其他策略部分,例如:'ignore_empty_think', 'hermes'等。当loss_scale中有`>1`的数字出现时,你需要额外设置`--is_binary_loss_scale false`参数。
|
||||
```jsonl
|
||||
{"messages": [{"role": "user", "content": "你好"}, {"role": "assistant", "content": "你好,有什么可以帮助你的吗?", "loss": false}, {"role": "user", "content": "1+1等于几?"}, {"role": "assistant", "content": "等于2", "loss": true}]}
|
||||
{"messages": [{"role": "user", "content": "hello!"}, {"role": "assistant", "content": "<think>\n...\n</think>\n", "loss_scale": 1.0}, {"role": "assistant", "content": "hi!", "loss_scale": 2.0}, {"role": "user", "content": "1+1=?"}, {"role": "assistant", "content": "<think>\n...\n</think>\n1+1=3", "loss": false}]}
|
||||
```
|
||||
|
||||
使用以下脚本进行测试:
|
||||
|
||||
```python
|
||||
from swift import get_processor, get_template
|
||||
|
||||
data = {"messages": [
|
||||
{"role": "user", "content": "hello!"},
|
||||
{"role": "assistant", "content": "<think>\n...\n</think>\n", "loss_scale": 1.},
|
||||
{"role": "assistant", "content": "hi!", "loss_scale": 2.},
|
||||
{"role": "user", "content": "1+1=?"},
|
||||
{"role": "assistant", "content": "<think>\n...\n</think>\n1+1=3", "loss": False},
|
||||
]}
|
||||
|
||||
template = get_template(get_processor('Qwen/Qwen3-8B'), loss_scale='default+ignore_empty_think',
|
||||
is_binary_loss_scale=False)
|
||||
template.set_mode('train')
|
||||
inputs = template.encode(data)
|
||||
|
||||
print(template.safe_decode(inputs['labels']))
|
||||
print(inputs['loss_scale'])
|
||||
```
|
||||
|
||||
注意:如果对messages中连续的"tool_call"设置"loss"/"loss_scale",则只生效最前面"tool_call"的配置。例如:
|
||||
```jsonl
|
||||
{"messages": [..., {"role": "tool_call", "content": "{\"name\": \"realtime_aqi\", \"arguments\": {\"city\": \"北京\"}}", "loss": false}, {"role": "tool_call", "content": "{\"name\": \"realtime_aqi\", \"arguments\": {\"city\": \"上海\"}}"}, ...]}
|
||||
```
|
||||
|
||||
- "chat_template_kwargs"字段,(需ms-swift>=4.3.0),你可以通过在数据集中传入该字段**样本级别**控制template的min_pixels, max_pixels, fps等多模态参数,以及enable_thinking(推理时)等参数。以下为不同模型支持的参数:
|
||||
- 其中"enable_thinking", "preserve_thinking", "response_prefix"支持所有模型(推理时生效);"max_pixels"参数支持所有多模态模型。
|
||||
- Qwen系列多模态模型:min_pixels, max_pixels, fps等qwen_vl_utils/qwen_omni_utils支持的参数。
|
||||
```jsonl
|
||||
{"messages": [{"role": "user", "content": "<image>这是什么"}, {"role": "assistant", "content": "这是一只兔子", "loss": false}], "chat_template_kwargs": {"max_pixels": 1048576}}
|
||||
{"messages": [{"role": "user", "content": "who are you?"}], "chat_template_kwargs": {"enable_thinking": false}}
|
||||
```
|
||||
|
||||
#### channel loss
|
||||
如果你要使用channel loss,你需要设置`--enable_channel_loss true`,并在数据集中增加"channel"字段。channel loss兼容packing/padding_free/loss_scale等技术。
|
||||
|
||||
```jsonl
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的助手"}, {"role": "user", "content": "告诉我明天的天气"}, {"role": "assistant", "content": "明天天气晴朗"}], "channel": "general"}
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的数学计算器"}, {"role": "user", "content": "1+1等于几"}, {"role": "assistant", "content": "等于2"}, {"role": "user", "content": "再加1呢"}, {"role": "assistant", "content": "等于3"}], "channel": "math"}
|
||||
```
|
||||
|
||||
### RLHF
|
||||
|
||||
#### DPO/ORPO/CPO/SimPO/RM
|
||||
|
||||
```jsonl
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的助手"}, {"role": "user", "content": "告诉我明天的天气"}, {"role": "assistant", "content": "明天天气晴朗"}], "rejected_response": "我不知道"}
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的数学计算器"}, {"role": "user", "content": "1+1等于几"}, {"role": "assistant", "content": "等于2"}, {"role": "user", "content": "再加1呢"}, {"role": "assistant", "content": "等于3"}], "rejected_response": "我不知道"}
|
||||
```
|
||||
|
||||
多模态数据的格式参考[多模态数据集](#多模态), 额外加入如`images`的列表示其他模态输入。当需要为偏好对数据关联不同的图片信息时,可通过`rejected_images`字段标注拒绝回答对应的图片信息。
|
||||
对齐数据集中要求`rejected_images`和`rejected_response`至少提供一个。
|
||||
|
||||
> 注: RM 额外支持 margin 列,参考[RM文档](../Instruction/RLHF.md#rm)
|
||||
|
||||
当然,你也可以直接使用`rejected_messages`,而不是只提供`rejected_response`/`rejected_images`,这将提供更大的灵活度(例如多模态/agent场景)。若使用rejected_messages,在多模态场景下,你需要额外传入"rejected_images","rejected_audios","rejected_videos"等内容;在Agent场景下,你需要额外传入"rejected_tools"等内容。多模态数据格式例子如下:
|
||||
- 若使用`rejected_response`,'rejected_images/rejected_audios/rejected_videos/rejected_tools'的默认值为'images/audios/videos/tools';若使用`rejected_messages`,则需要额外传入。
|
||||
|
||||
```jsonl
|
||||
{"messages": [{"role": "user", "content": "<image>这是什么"}, {"role": "assistant", "content": "这是一只小猫咪。"}], "images": ["cat.png"], "rejected_messages": [{"role": "user", "content": "<image>这是什么"}, {"role": "assistant", "content": "这是一只小狗。"}], "rejected_images": ["cat.png"]}
|
||||
{"messages": [{"role": "user", "content": "<image>这是什么"}, {"role": "assistant", "content": "这是一只小猫咪。"}], "images": ["cat.png"], "rejected_messages": [{"role": "user", "content": "<image>这是什么"}, {"role": "assistant", "content": "这是一只小猫咪。"}], "rejected_images": ["dog.png"]}
|
||||
```
|
||||
|
||||
以上格式等价于:
|
||||
```jsonl
|
||||
{"messages": [{"role": "user", "content": "<image>这是什么"}, {"role": "assistant", "content": "这是一只小猫咪。"}], "images": ["cat.png"], "rejected_response": "这是一只小狗。"}
|
||||
{"messages": [{"role": "user", "content": "<image>这是什么"}, {"role": "assistant", "content": "这是一只小猫咪。"}], "images": ["cat.png"], "rejected_images": ["dog.png"]}
|
||||
# 例子一也可写成:
|
||||
{"messages": [{"role": "user", "content": "<image>这是什么"}, {"role": "assistant", "content": "这是一只小猫咪。"}], "images": ["cat.png"], "rejected_response": [{"role": "assistant", "content": "这是一只小狗。"}]}
|
||||
```
|
||||
|
||||
你也可以将Agent数据集组织成以下形式:
|
||||
```jsonl
|
||||
# 会寻找`messages`最后一个user的位置,并替换之后的内容为`rejected_response`组成`rejected_messages`
|
||||
{"tools": "[{\"type\": \"function\", \"function\": {\"name\": \"realtime_aqi\", \"description\": \"天气预报。获取实时空气质量。当前空气质量,PM2.5,PM10信息\", \"parameters\": {\"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\", \"description\": \"城市名,例如:上海\"}}, \"required\": [\"city\"]}}}]", "messages": [{"role": "user", "content": "北京和上海今天的天气情况"}, {"role": "tool_call", "content": "{\"name\": \"realtime_aqi\", \"arguments\": {\"city\": \"北京\"}}"}, {"role": "tool_call", "content": "{\"name\": \"realtime_aqi\", \"arguments\": {\"city\": \"上海\"}}"}, {"role": "tool_response", "content": "{\"city\": \"北京\", \"aqi\": \"10\", \"unit\": \"celsius\"}"}, {"role": "tool_response", "content": "{\"city\": \"上海\", \"aqi\": \"72\", \"unit\": \"fahrenheit\"}"}, {"role": "assistant", "content": "根据天气预报工具,北京今天的空气质量指数为10,属于良好水平;上海今天的空气质量指数为72,属于轻度污染水平。"}], "rejected_response": [{"role": "assistant", "content": "我不知道。"}]}
|
||||
```
|
||||
|
||||
如何debug:
|
||||
|
||||
```python
|
||||
from swift import get_processor, get_template
|
||||
|
||||
data = {"tools": "[{\"type\": \"function\", \"function\": {\"name\": \"realtime_aqi\", \"description\": \"天气预报。获取实时空气质量。当前空气质量,PM2.5,PM10信息\", \"parameters\": {\"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\", \"description\": \"城市名,例如:上海\"}}, \"required\": [\"city\"]}}}]", "messages": [{"role": "user", "content": "北京和上海今天的天气情况"}, {"role": "tool_call", "content": "{\"name\": \"realtime_aqi\", \"arguments\": {\"city\": \"北京\"}}"}, {"role": "tool_call", "content": "{\"name\": \"realtime_aqi\", \"arguments\": {\"city\": \"上海\"}}"}, {"role": "tool_response", "content": "{\"city\": \"北京\", \"aqi\": \"10\", \"unit\": \"celsius\"}"}, {"role": "tool_response", "content": "{\"city\": \"上海\", \"aqi\": \"72\", \"unit\": \"fahrenheit\"}"}, {"role": "assistant", "content": "根据天气预报工具,北京今天的空气质量指数为10,属于良好水平;上海今天的空气质量指数为72,属于轻度污染水平。"}], "rejected_response": [{"role": "assistant", "content": "我不知道。"}]}
|
||||
|
||||
template = get_template(get_processor('Qwen/Qwen3.5-4B'), loss_scale='last_round')
|
||||
template.set_mode('rlhf') # 具体查看命令行文档 `template_mode` 参数的介绍
|
||||
inputs = template.encode(data)
|
||||
|
||||
print(template.safe_decode(inputs['chosen_labels']))
|
||||
print(template.safe_decode(inputs['rejected_labels']))
|
||||
```
|
||||
|
||||
#### KTO
|
||||
|
||||
```jsonl
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的助手"}, {"role": "user", "content": "告诉我明天的天气"}, {"role": "assistant", "content": "我不知道"}], "label": false}
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的数学计算器"}, {"role": "user", "content": "1+1等于几"}, {"role": "assistant", "content": "等于2"}, {"role": "user", "content": "再加1呢"}, {"role": "assistant", "content": "等于3"}], "label": true}
|
||||
```
|
||||
|
||||
#### PPO/GRPO
|
||||
|
||||
```jsonl
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的助手"}, {"role": "user", "content": "告诉我明天的天气"}]}
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的数学计算器"}, {"role": "user", "content": "1+1等于几"}, {"role": "assistant", "content": "等于2"}, {"role": "user", "content": "再加1呢"}]}
|
||||
{"messages": [{"role": "user", "content": "你的名字是什么"}]}
|
||||
```
|
||||
- 注意:GRPO会透传所有额外的字段内容给ORM,而不像其他训练方法,默认将额外的字段删除。例如: 你可以额外传入'solution'。自定义的ORM需要包含一个位置参数completions,其他为关键词参数,由数据集额外字段透传。
|
||||
|
||||
#### GKD
|
||||
数据集格式如下
|
||||
```jsonl
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的助手"}, {"role": "user", "content": "告诉我明天的天气"}, {"role": "assistant", "content": "明天天气晴朗"}]}
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的数学计算器"}, {"role": "user", "content": "1+1等于几"}, {"role": "assistant", "content": "等于2"}, {"role": "user", "content": "再加1呢"}, {"role": "assistant", "content": "等于3"}]}
|
||||
```
|
||||
|
||||
若是在线生成训练(lmbda>0),不需要response部分(学生模型在线生成,数据集中的response部分会被删除)
|
||||
```jsonl
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的助手"}, {"role": "user", "content": "告诉我明天的天气"}]}
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的数学计算器"}, {"role": "user", "content": "1+1等于几"}, {"role": "assistant", "content": "等于2"}, {"role": "user", "content": "再加1呢"}]}
|
||||
```
|
||||
|
||||
### 序列分类
|
||||
|
||||
**单标签任务**:
|
||||
```jsonl
|
||||
{"messages": [{"role": "user", "content": "今天天气真好呀"}], "label": 1}
|
||||
{"messages": [{"role": "user", "content": "今天真倒霉"}], "label": 0}
|
||||
{"messages": [{"role": "user", "content": "好开心"}], "label": 1}
|
||||
```
|
||||
**多标签任务**:
|
||||
```jsonl
|
||||
{"messages": [{"role": "user", "content": "<sentence>"}], "label": []}
|
||||
{"messages": [{"role": "user", "content": "<sentence>"}], "label": [0, 2]}
|
||||
{"messages": [{"role": "user", "content": "<sentence>"}], "label": [1, 3, 5]}
|
||||
```
|
||||
|
||||
**单回归任务**:
|
||||
```jsonl
|
||||
{"messages": [{"role": "user", "content": "求两句话的相似度,范围为0-1。\nsentence1: <sentence1>\nsentence2: <sentence2>"}], "label": 0.8}
|
||||
```
|
||||
**多回归任务**:
|
||||
|
||||
```jsonl
|
||||
{"messages": [{"role": "user", "content": "<sentence>"}], "label": [1.2, -0.6, 0.8]}
|
||||
```
|
||||
|
||||
### Embedding
|
||||
|
||||
请参考[embedding训练文档](../BestPractices/Embedding.md#数据集格式)
|
||||
|
||||
### Reranker
|
||||
|
||||
请参考[Reranker训练文档](../BestPractices/Reranker.md#数据集格式)
|
||||
|
||||
### 多模态
|
||||
|
||||
对于多模态数据集,和上述任务的格式相同。区别在于增加了`images`, `videos`, `audios`几个key,分别代表多模态资源的url或者path(推荐使用绝对路径),`<image>` `<video>` `<audio>`标签代表了插入图片/视频/音频的位置,ms-swift支持多图片/视频/音频的情况。这些特殊tokens将在预处理的时候进行替换,参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/template/templates/qwen.py#L198)。下面给出的四条示例分别展示了纯文本,以及包含图像、视频和音频数据的数据格式。
|
||||
|
||||
预训练:
|
||||
```
|
||||
{"messages": [{"role": "assistant", "content": "预训练的文本在这里"}]}
|
||||
{"messages": [{"role": "assistant", "content": "<image>是一只小狗,<image>是一只小猫"}], "images": ["/xxx/x.jpg", "/xxx/x.png"]}
|
||||
{"messages": [{"role": "assistant", "content": "<audio>描述了今天天气真不错"}], "audios": ["/xxx/x.wav"]}
|
||||
{"messages": [{"role": "assistant", "content": "<image>是一个大象,<video>是一只狮子在跑步"}], "images": ["/xxx/x.jpg"], "videos": ["/xxx/x.mp4"]}
|
||||
```
|
||||
|
||||
微调:
|
||||
```jsonl
|
||||
{"messages": [{"role": "user", "content": "浙江的省会在哪?"}, {"role": "assistant", "content": "浙江的省会在杭州。"}]}
|
||||
{"messages": [{"role": "user", "content": "<image><image>两张图片有什么区别"}, {"role": "assistant", "content": "前一张是小猫,后一张是小狗"}], "images": ["/xxx/x.jpg", "/xxx/x.png"]}
|
||||
{"messages": [{"role": "user", "content": "<audio>语音说了什么"}, {"role": "assistant", "content": "今天天气真好呀"}], "audios": ["/xxx/x.mp3"]}
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的助手"}, {"role": "user", "content": "<image>图片中是什么,<video>视频中是什么"}, {"role": "assistant", "content": "图片中是一个大象,视频中是一只小狗在草地上奔跑"}], "images": ["/xxx/x.jpg"], "videos": ["/xxx/x.mp4"]}
|
||||
```
|
||||
- 注意:以下字段会自动转成对应的images, videos, audios字段。
|
||||
- images: image, images.
|
||||
- videos: video, videos.
|
||||
- audios: audio, audios.
|
||||
- 如果需要传入base64格式而不是文件路径,以下为样本例子:`"videos": ['data:video/mp4;base64,{base64_encoded}']`, `"images": ['data:image/jpg;base64,{base64_encoded}']`。
|
||||
- 若你希望直接传入视频帧,而不是视频,你可以使用以下格式:`"videos": [["/xxx/x.png", "/xxx/y.png"], ["/xxx/a.png", "/xxx/b.png", "/xxx/c.png"]]`。该格式只有部分模型支持,包括Qwen2/2.5/3-VL、Qwen2.5/3-Omni以及其衍生模型。
|
||||
|
||||
多模态模型的RLHF和序列分类的数据格式可以参考纯文本大模型的格式,并在此基础上增加`images`等字段。
|
||||
|
||||
#### grounding
|
||||
|
||||
如果是grounding(物体检测)任务,ms-swift支持两种方式:
|
||||
1. 直接使用对应模型grounding任务的数据集格式,例如qwen2-vl的格式如下:
|
||||
|
||||
```jsonl
|
||||
{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "<image>描述图像"}, {"role": "assistant", "content": "<|object_ref_start|>一只狗<|object_ref_end|><|box_start|>(221,423),(569,886)<|box_end|>和<|object_ref_start|>一个女人<|object_ref_end|><|box_start|>(451,381),(733,793)<|box_end|>正在沙滩上玩耍"}], "images": ["/xxx/x.jpg"]}
|
||||
{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "<image>找到图像中的<|object_ref_start|>羊<|object_ref_end|>"}, {"role": "assistant", "content": "<|box_start|>(101,201),(150,266)<|box_end|><|box_start|>(401,601),(550,666)<|box_end|>"}], "images": ["/xxx/x.jpg"]}
|
||||
{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "<image>帮我打开谷歌浏览器"}, {"role": "assistant", "content": "Action: click(start_box='<|box_start|>(246,113)<|box_end|>')"}], "images": ["/xxx/x.jpg"]}
|
||||
```
|
||||
使用这种类型的数据需要注意:
|
||||
- 不同模型grounding任务的特殊字符和数据集格式不同。
|
||||
- 不同模型对bbox是否归一化的处理不同。例如:qwen2.5-vl使用绝对坐标,而qwen2/3-vl、internvl2.5需要对bbox的坐标进行千分位坐标归一化。
|
||||
- 注意:Qwen2.5-VL采用绝对坐标,因此要小心每次的图像缩放,如果使用方案一的数据集格式,你需要预先对图像进行resize(H和W需要是28的系数),并根据该尺寸缩放坐标点。如果使用方案二的数据集格式,ms-swift会帮助你处理图像的缩放问题,你依旧可以使用`MAX_PIXELS`或者`--max_pixels`等进行图像缩放(仅训练,推理场景,你依旧需要自己处理图像的缩放问题)。
|
||||
|
||||
2. 使用ms-swift的grounding数据格式:
|
||||
|
||||
```jsonl
|
||||
{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "<image>描述图像"}, {"role": "assistant", "content": "<ref-object><bbox>和<ref-object><bbox>正在沙滩上玩耍"}], "images": ["/xxx/x.jpg"], "objects": {"ref": ["一只狗", "一个女人"], "bbox": [[331.5, 761.4, 853.5, 1594.8], [676.5, 685.8, 1099.5, 1427.4]]}}
|
||||
{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "<image>找到图像中的<ref-object>"}, {"role": "assistant", "content": "<bbox><bbox>"}], "images": ["/xxx/x.jpg"], "objects": {"ref": ["羊"], "bbox": [[90.9, 160.8, 135, 212.8], [360.9, 480.8, 495, 532.8]]}}
|
||||
{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "<image>帮我打开谷歌浏览器"}, {"role": "assistant", "content": "Action: click(start_box='<bbox>')"}], "images": ["/xxx/x.jpg"], "objects": {"ref": [], "bbox": [[615, 226]]}}
|
||||
```
|
||||
该格式将自动转换数据集格式为对应模型的grounding任务格式,且选择对应模型的bbox归一化方式。该格式比通用格式多了objects字段,该字段包含的字段有:
|
||||
- ref: 用于替换messages中的`<ref-object>`。ref的长度需要与`<ref-object>`的数量一致。
|
||||
- bbox: 用于替换messages中的`<bbox>`。若bbox中每个box长度为2,则代表x和y坐标,若box长度为4,则代表2个点的x和y坐标。bbox的长度需要与`<bbox>`的数量一致。
|
||||
- 注意:`<ref-object>`和`<bbox>`并没有对应关系,ref和bbox各自替换各自的占位符。
|
||||
- bbox_type: 可选项为'real','norm1'。默认为'real',即bbox为真实bbox值。若是'norm1',则bbox已经归一化为0~1。
|
||||
- image_id: 通常用于多图grounding任务。该参数只有当bbox_type为'real'时生效,代表bbox对应的图片是第几张,用于缩放bbox。索引从0开始,默认全为第0张。image_id的数量需要和bbox的数量一致。例如:若bbox的长度为10,images的长度为2,那么image_id的长度需要是10,其值需要在`{0, 1}`集合内。
|
||||
|
||||
对于Qwen2.5-VL/Qwen3-VL,你可以使用环境`QWENVL_BBOX_FORMAT='new'`(默认为'legacy'),以兼容[官方cookbook](https://github.com/QwenLM/Qwen3-VL/blob/main/cookbooks/2d_grounding.ipynb)格式。并将数据集定义成以下格式:
|
||||
```jsonl
|
||||
{"messages": [{"role": "user", "content": "<image>找到图像中的<ref-object>"}, {"role": "assistant", "content": "[\n\t{\"bbox_2d\": <bbox>, \"label\": \"<ref-object>\"},\n\t{\"bbox_2d\": <bbox>, \"label\": \"<ref-object>\"}\n]"}], "images": ["cat.png"], "objects": {"ref": ["羊", "羊", "羊"], "bbox": [[90.9, 160.8, 135, 212.8], [360.9, 480.8, 495, 532.8]]}}
|
||||
```
|
||||
|
||||
测试ms-swift格式的grounding数据格式的最终格式:
|
||||
```python
|
||||
import os
|
||||
os.environ["MAX_PIXELS"] = "1003520"
|
||||
from swift import get_processor, get_template
|
||||
|
||||
processor = get_processor('Qwen/Qwen2.5-VL-7B-Instruct')
|
||||
template = get_template(processor)
|
||||
data = {...}
|
||||
template.set_mode('train')
|
||||
encoded = template.encode(data, return_template_inputs=True)
|
||||
print(f'[INPUT_IDS] {template.safe_decode(encoded["input_ids"])}\n')
|
||||
print(f'[LABELS] {template.safe_decode(encoded["labels"])}')
|
||||
print(f'images: {encoded["template_inputs"].images}')
|
||||
```
|
||||
|
||||
### Agent格式
|
||||
这里分别提供了纯文本Agent和多模态Agent的示例数据样本:
|
||||
```jsonl
|
||||
{"tools": "[{\"type\": \"function\", \"function\": {\"name\": \"realtime_aqi\", \"description\": \"天气预报。获取实时空气质量。当前空气质量,PM2.5,PM10信息\", \"parameters\": {\"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\", \"description\": \"城市名,例如:上海\"}}, \"required\": [\"city\"]}}}]", "messages": [{"role": "user", "content": "北京和上海今天的天气情况"}, {"role": "tool_call", "content": "{\"name\": \"realtime_aqi\", \"arguments\": {\"city\": \"北京\"}}"}, {"role": "tool_call", "content": "{\"name\": \"realtime_aqi\", \"arguments\": {\"city\": \"上海\"}}"}, {"role": "tool_response", "content": "{\"city\": \"北京\", \"aqi\": \"10\", \"unit\": \"celsius\"}"}, {"role": "tool_response", "content": "{\"city\": \"上海\", \"aqi\": \"72\", \"unit\": \"fahrenheit\"}"}, {"role": "assistant", "content": "根据天气预报工具,北京今天的空气质量指数为10,属于良好水平;上海今天的空气质量指数为72,属于轻度污染水平。"}]}
|
||||
{"tools": "[{\"type\": \"function\", \"function\": {\"name\": \"click\", \"description\": \"点击屏幕中的某个位置\", \"parameters\": {\"type\": \"object\", \"properties\": {\"x\": {\"type\": \"integer\", \"description\": \"横坐标,表示屏幕上的水平位置\"}, \"y\": {\"type\": \"integer\", \"description\": \"纵坐标,表示屏幕上的垂直位置\"}}, \"required\": [\"x\", \"y\"]}}}]", "messages": [{"role": "user", "content": "<image>现在几点了?"}, {"role": "assistant", "content": "<think>\n我可以通过打开日历App来获取当前时间。\n</think>\n"}, {"role": "tool_call", "content": "{\"name\": \"click\", \"arguments\": {\"x\": 105, \"y\": 132}}"}, {"role": "tool_response", "content": "{\"images\": \"<image>\", \"status\": \"success\"}"}, {"role": "assistant", "content": "成功打开日历App,现在的时间为中午11点"}], "images": ["desktop.png", "calendar.png"]}
|
||||
```
|
||||
- agent_template为"react_en", "hermes"等情况下,该格式适配所有模型Agent训练,可以轻松在不同模型间切换。
|
||||
- 其中tools是一个包含tool列表的json字符串,messages中role为'tool_call'和'tool_response/tool'的content部分都需要是json字符串。
|
||||
- tools字段将在训练/推理时和`{"role": "system", ...}"`部分组合,根据agent_template组成完整的system部分。
|
||||
- `{"role": "tool_call", ...}`部分将根据agent_template自动转成对应格式的`{"role": "assistant", ...}`,多条连续的`{"role": "assistant", ...}`将拼接在一起组成完整的assistant_content。
|
||||
- `{"role": "tool_response", ...}`也可以写成`{"role": "tool", ...}`,这两种写法是等价的。该部分也将根据`agent_template`自动转换格式。该部分在训练时将不进行损失的计算,角色类似于`{"role": "user", ...}`。
|
||||
- 该格式支持并行调用工具,例子参考第一条数据样本。多模态Agent数据样本中`<image>`标签数量应与"images"长度相同,其标签位置代表图像特征的插入位置。当然也支持其他模态,例如audios, videos。
|
||||
- 注意:您也可以手动将数据处理为role为system/user/assistant的messages格式。agent_template的作用是将其中的tools字段以及role为tool_call和tool_response的messages部分,自动映射为标准的role为system/user/assistant的messages格式。
|
||||
- 更多请参考[Agent文档](../Instruction/Agent-support.md)。
|
||||
|
||||
### 文生图格式
|
||||
|
||||
```jsonl
|
||||
{"messages": [{"role": "system", "content": "你是个有用无害的助手"}, {"role": "user", "content": "给我画出一个苹果"}, {"role": "assistant", "content": "<image>"}], "images": ["/xxx/x.jpg"]}
|
||||
```
|
||||
|
||||
## dataset_info.json
|
||||
|
||||
可以参考ms-swift内置的[dataset_info.json](https://github.com/modelscope/ms-swift/blob/main/swift/dataset/data/dataset_info.json)。该方案使用AutoPreprocessor预处理函数将数据集转换为标准格式。dataset_info.json文件中包含了数据集元信息的list,以下为一些例子:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"ms_dataset_id": "xxx/xxx"
|
||||
},
|
||||
{
|
||||
"dataset_path": "<dataset_dir/dataset_path>"
|
||||
},
|
||||
{
|
||||
"ms_dataset_id": "<dataset_id>",
|
||||
"subsets": ["v1"],
|
||||
"split": ["train", "validation"],
|
||||
"columns": {
|
||||
"input": "query",
|
||||
"output": "response"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ms_dataset_id": "<dataset_id>",
|
||||
"hf_dataset_id": "<hf_dataset_id>",
|
||||
"subsets": [{
|
||||
"subset": "subset1",
|
||||
"columns": {
|
||||
"problem": "query",
|
||||
"content": "response"
|
||||
}
|
||||
},
|
||||
{
|
||||
"subset": "subset2",
|
||||
"columns": {
|
||||
"messages": "_",
|
||||
"new_messages": "messages"
|
||||
}
|
||||
}]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
支持以下参数:
|
||||
- ms_dataset_id: 参考DatasetMeta参数。
|
||||
- hf_dataset_id: 参考DatasetMeta参数。
|
||||
- dataset_path: 参考DatasetMeta参数。
|
||||
- dataset_name: 参考DatasetMeta参数。
|
||||
- subsets: 参考DatasetMeta参数。
|
||||
- split: 参考DatasetMeta参数。
|
||||
- columns: 在数据集进行预处理前,对数据集进行列名转换。
|
||||
|
||||
|
||||
## 数据集注册
|
||||
|
||||
register_dataset会在`DATASET_MAPPING`中注册数据集,调用函数`register_dataset(dataset_meta)`即可完成数据集注册,其中dataset_meta将存储模型的元信息。DatasetMeta的参数列表如下:
|
||||
- ms_dataset_id: ModelScope的dataset_id,默认为None。
|
||||
- hf_dataset_id: HuggingFace的dataset_id,默认为None。
|
||||
- dataset_path: 数据集**文件/文件夹**的本地路径(推荐使用绝对路径)。默认为None。
|
||||
- dataset_name: 数据集别名,可以通过`--dataset <dataset_name>`指定数据集,这在dataset_path很长时很方便。默认为None。
|
||||
- subsets: 子数据集的名字列表或者`SubsetDataset`对象的列表,默认为`['default']`。(只有dataset_id或者dataset_dir(git clone开源数据集)有子数据集和split的概念)。
|
||||
- split: 默认为`['train']`。
|
||||
- preprocess_func: 预处理函数或可调用对象,默认为`AutoPreprocessor()`。该预处理函数接口为传入`HfDataset`,并返回满足标准格式的`HfDataset`。
|
||||
- load_function: 默认为`DatasetLoader.load`。若需要自定义载入函数,则该载入函数需返回满足标准格式的`HfDataset`,这将抛弃ms-swift的数据集载入机制,提供给用户最大的自由度。通常该参数不需要进行修改。
|
||||
|
||||
|
||||
以下介绍注册数据集的例子:
|
||||
|
||||
```python
|
||||
from swift.dataset import (
|
||||
ResponsePreprocessor, DatasetMeta, register_dataset, SubsetDataset, load_dataset
|
||||
)
|
||||
from typing import Dict, Any
|
||||
|
||||
class CustomPreprocessor(ResponsePreprocessor):
|
||||
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
|
||||
query = f"""任务:判断下面两句话语意是否相似。
|
||||
句子1: {row['text1']}
|
||||
句子2: {row['text2']}
|
||||
请输出类别[0/1]: 0代表含义不同, 1代表含义相似。
|
||||
"""
|
||||
response = str(row['label'])
|
||||
row = {
|
||||
'query': query,
|
||||
'response': response
|
||||
}
|
||||
return super().preprocess(row)
|
||||
|
||||
|
||||
register_dataset(
|
||||
DatasetMeta(
|
||||
ms_dataset_id='swift/financial_classification',
|
||||
subsets=[SubsetDataset('train', split=['train']), SubsetDataset('test', split=['test'])],
|
||||
preprocess_func=CustomPreprocessor(),
|
||||
))
|
||||
|
||||
if __name__ == '__main__':
|
||||
# load_dataset returns train_dataset and val_dataset based on `split_dataset_ratio`
|
||||
# Here, since we didn't pass `split_dataset_ratio` (defaults to 0), we take the first one (index 0)
|
||||
dataset = load_dataset('swift/financial_classification:train')[0]
|
||||
test_dataset = load_dataset('swift/financial_classification:test')[0]
|
||||
print(f'dataset[0]: {dataset[0]}')
|
||||
print(f'test_dataset[0]: {test_dataset[0]}')
|
||||
```
|
||||
@@ -0,0 +1,38 @@
|
||||
# 自定义模型
|
||||
|
||||
ms-swift内置的模型,你可以直接通过指定model_id或者model_path来使用:`--model <model_id_or_path>`。ms-swift会根据model_id/model_path的后缀和`config.json`文件来判断model_type。
|
||||
|
||||
每种model_type都有唯一的模型结构、template和加载方式。当然,你也可以手动传入`--model_type`、`--template`来进行覆盖。ms-swift已支持的model_type和template可以查看[支持的模型与数据集](../Instruction/Supported-models-and-datasets.md)。
|
||||
|
||||
以下介绍如何注册一个新模型和对应的template。最佳实践参考[注册多模态模型最佳实践](../BestPractices/MLLM-Registration.md)。
|
||||
|
||||
## 模型注册
|
||||
|
||||
自定义模型通常使用模型注册的方式进行,可以参考[内置模型](https://github.com/modelscope/ms-swift/blob/main/swift/model/models/qwen.py)、[内置对话模板](https://github.com/modelscope/ms-swift/blob/main/swift/template/templates/qwen.py)或者[examples](https://github.com/modelscope/ms-swift/blob/main/examples/custom)的示例代码。你可以通过指定`--external_plugins xxx.py`解析外置注册的内容(方便pip install而非git clone的用户)。
|
||||
|
||||
register_model会在`MODEL_MAPPING`中注册模型,调用函数`register_model(model_meta)`即可完成模型注册,其中model_meta将存储模型的元信息。ModelMeta的参数列表如下:
|
||||
- model_type: 必填项。模型类型,也是唯一ID。
|
||||
- model_groups: 必填项。罗列ModelScope/HuggingFace的模型id和模型本地路径。运行[run_model_info.py](https://github.com/modelscope/ms-swift/blob/main/scripts/utils/run_model_info.py)文件将自动产生[支持的模型文档](https://swift.readthedocs.io/zh-cn/latest/Instruction/Supported-models-and-datasets.html)以及自动根据`--model`后缀匹配model_type。
|
||||
- loader: 模型和tokenizer/processor(多模态模型)的加载器。默认使用`swift.model.ModelLoader`。
|
||||
- template: 命令行不额外指定`--template`时的默认template类型。默认为None。
|
||||
- model_arch: 模型架构。默认为None。多模态模型训练需要设置该参数来确定llm/vit/aligner的前缀。
|
||||
- architectures: config.json中的architectures项,用于自动匹配模型对应的model_type。默认为`[]`。
|
||||
- additional_saved_files: 全参数训练和merge-lora时需要额外保存的文件。默认为`[]`。
|
||||
- torch_dtype: 模型加载时未传入`torch_dtype`时的默认dtype。默认为None,从config.json中读取。
|
||||
- is_multimodal: 是否是多模态模型,默认为False。
|
||||
- ignore_patterns: 从hub端下载文件需要忽略的文件patterns,默认为`[]`。
|
||||
|
||||
|
||||
register_template会在`TEMPLATE_MAPPING`中注册对话模板,调用函数`register_template(template_meta)`即可完成对话模板注册,其中template_meta将存储template的元信息。TemplateMeta的参数列表如下:
|
||||
- template_type: 必填项。对话模板类型,也是唯一ID。
|
||||
- prefix: 必填项。对话模板的前缀,通常包含system、bos_token等部分,独立于多轮对话而产生的对话模板循环。例如qwen的prefix为`[]`。
|
||||
- prompt: 必填项。表示对话模板中的`{{RESPONSE}}`之前的对话部分。我们使用`{{QUERY}}`代表user询问部分的填充符。例如qwen的prompt为`['<|im_start|>user\n{{QUERY}}<|im_end|>\n<|im_start|>assistant\n']`。
|
||||
- chat_sep: 必填项。多轮对话中每轮的分隔符。若设置为None,则该template不支持多轮对话。例如qwen的chat_sep为`['<|im_end|>\n']`。
|
||||
- suffix: 默认为`[['eos_token_id']]`。对话模板的后缀部分,独立于多轮对话而产生的对话模板循环,通常为eos_token。例如qwen的suffix为`['<|im_end|>']。`
|
||||
- template_cls: 默认为`Template`。通常在定义多模态模型的template时需要进行自定义,自定义`_encode`、`_post_encode`、`_data_collator`函数。
|
||||
- system_prefix: 默认为None。含system的对话模板前缀。我们使用`{{SYSTEM}}`作为system的填充符。例如qwen的system_prefix为`['<|im_start|>system\n{{SYSTEM}}<|im_end|>\n']`。
|
||||
- 注意:若system为空时,`prefix`可以被`system_prefix`替代,则可以将`prefix`写为含system的前缀,而无需设置`system_prefix`。
|
||||
- 若prefix不含`{{SYSTEM}}`且未设置system_prefix,则该template不支持system。
|
||||
- default_system: 默认为None。不传入`--system`时使用的默认system。例如qwen的default_system为`'You are a helpful assistant.'`。
|
||||
- stop_words: 默认为`[]`。除了eos_token和`suffix[-1]`的额外停止符。例如qwen的stop_words为`['<|endoftext|>']`。
|
||||
- 注意:推理时,输出的response将会过滤eos_token和`suffix[-1]`,但是会保留额外的stop_words。
|
||||
@@ -0,0 +1,108 @@
|
||||
# 快速开始
|
||||
|
||||
🍲 ms-swift是魔搭社区提供的大模型与多模态大模型微调部署框架,现已支持600+纯文本大模型与400+多模态大模型的训练(预训练、微调、人类对齐)、推理、评测、量化与部署。其中大模型包括:Qwen3、Qwen3.5、InternLM3、GLM4.5、Mistral、DeepSeek-R1、Llama4等模型,多模态大模型包括:Qwen3-VL、Qwen3-Omni、Llava、InternVL3.5、MiniCPM-V-4、Ovis2.5、GLM4.5-V、DeepSeek-VL2等模型。
|
||||
|
||||
🍔 除此之外,ms-swift汇集了最新的训练技术,包括集成Megatron并行技术,包括TP、PP、CP、EP等为训练提供加速,以及众多GRPO算法族强化学习的算法,包括:GRPO、DAPO、GSPO、SAPO、CISPO、RLOO、Reinforce++等提升模型智能。ms-swift支持广泛的训练任务,包括DPO、KTO、RM、CPO、SimPO、ORPO等偏好学习算法,以及Embedding、Reranker、序列分类任务。ms-swift提供了大模型训练全链路的支持,包括使用vLLM、SGLang和LMDeploy对推理、评测、部署模块提供加速,以及使用GPTQ、AWQ、BNB、FP8技术对大模型进行量化。
|
||||
|
||||
**为什么选择ms-swift?**
|
||||
- 🍎 **模型类型**:支持600+纯文本大模型、**400+多模态大模型**以及All-to-All全模态模型训练到部署全流程,热门模型Day0支持。
|
||||
- **数据集类型**:内置150+预训练、微调、人类对齐、多模态等各种任务数据集,并支持自定义数据集,用户只需准备数据集即可一键训练。
|
||||
- **硬件支持**:支持A10/A100/H100、RTX系列、T4/V100、CPU、MPS以及国产硬件Ascend NPU等。
|
||||
- **轻量训练**:支持了LoRA、QLoRA、DoRA、LoRA+、LLaMAPro、LongLoRA、LoRA-GA、ReFT、RS-LoRA、Adapter、LISA等轻量微调方式。
|
||||
- **量化训练**:支持对BNB、AWQ、GPTQ、AQLM、HQQ、EETQ量化模型进行训练,7B模型训练只需9GB训练资源。
|
||||
- **显存优化**: GaLore、Q-Galore、UnSloth、Liger-Kernel、Flash-Attention 2/3 以及 **Ulysses和Ring-Attention序列并行技术**支持,降低长文本训练显存占用。
|
||||
- **分布式训练**:支持分布式数据并行(DDP)、device_map简易模型并行、DeepSpeed ZeRO2 ZeRO3、FSDP/FSDP2以及Megatron等分布式训练技术。
|
||||
- 🍓 **多模态训练**:支持多模态packing技术提升训练速度100%+,支持文本、图像、视频和语音混合模态数据训练,支持vit/aligner/llm单独控制。
|
||||
- **Agent训练**:支持Agent template,准备一套数据集可用于不同模型的训练。
|
||||
- 🍊 **训练任务**:支持预训练和指令微调,以及DPO、GKD、KTO、RM、CPO、SimPO、ORPO等训练任务,支持**Embedding/Reranker**和序列分类任务。
|
||||
- 🥥 **Megatron并行技术**:提供TP/PP/SP/CP/ETP/EP/VPP并行策略,显著提升**MoE模型训练速度**。支持300+纯文本大模型和100+多模态大模型的全参数和LoRA训练方法。支持CPT/SFT/GRPO/DPO/KTO/RM训练任务。
|
||||
- 🍉 **强化学习**:内置**丰富GRPO族算法**,包括GRPO、DAPO、GSPO、SAPO、CISPO、CHORD、RLOO、Reinforce++等,支持同步和异步vLLM引擎推理加速,可使用插件拓展奖励函数、多轮推理调度器以及环境等。
|
||||
- **全链路能力**:覆盖训练、推理、评测、量化和部署全流程。
|
||||
- **界面训练**:提供使用Web-UI界面的方式进行训练、推理、评测、量化,完成大模型的全链路。
|
||||
- **推理加速**:支持Transformers、vLLM、SGLang和LmDeploy推理加速引擎,并提供OpenAI接口,为推理、部署和评测模块提供加速。
|
||||
- **模型评测**:以EvalScope作为评测后端,支持100+评测数据集对纯文本和多模态模型进行评测。
|
||||
- **模型量化**:支持AWQ、GPTQ、FP8和BNB的量化导出,导出的模型支持使用vLLM/SGLang/LmDeploy推理加速。
|
||||
|
||||
|
||||
## 安装
|
||||
|
||||
ms-swift的安装请参考[安装文档](./SWIFT-installation.md)。
|
||||
|
||||
## 使用样例
|
||||
|
||||
10分钟在单卡3090上对Qwen3-4B-Instruct-2507进行自我认知微调:
|
||||
```shell
|
||||
# 13GB
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift sft \
|
||||
--model Qwen/Qwen3-4B-Instruct-2507 \
|
||||
--tuner_type lora \
|
||||
--dataset 'AI-ModelScope/alpaca-gpt4-data-zh#500' \
|
||||
'AI-ModelScope/alpaca-gpt4-data-en#500' \
|
||||
'swift/self-cognition#500' \
|
||||
--torch_dtype bfloat16 \
|
||||
--num_train_epochs 1 \
|
||||
--per_device_train_batch_size 1 \
|
||||
--per_device_eval_batch_size 1 \
|
||||
--learning_rate 1e-4 \
|
||||
--lora_rank 8 \
|
||||
--lora_alpha 32 \
|
||||
--target_modules all-linear \
|
||||
--gradient_accumulation_steps 16 \
|
||||
--eval_steps 50 \
|
||||
--save_steps 50 \
|
||||
--save_total_limit 2 \
|
||||
--logging_steps 5 \
|
||||
--max_length 2048 \
|
||||
--output_dir output \
|
||||
--warmup_ratio 0.05 \
|
||||
--dataloader_num_workers 4 \
|
||||
--model_author swift \
|
||||
--model_name swift-robot
|
||||
```
|
||||
|
||||
小贴士:
|
||||
- 如果要使用自定义数据集进行训练,你可以参考[这里](../Customization/Custom-dataset.md)组织数据集格式,并指定`--dataset <dataset_path>`。
|
||||
- `--model_author`和`--model_name`参数只有当数据集中包含`swift/self-cognition`时才生效。
|
||||
- 如果要使用其他模型进行训练,你只需要修改`--model <model_id/model_path>`即可。
|
||||
- 默认使用**ModelScope**进行模型和数据集的下载。如果要使用HuggingFace,指定`--use_hf true`即可。
|
||||
|
||||
训练完成后,使用以下命令对训练后的权重进行推理:
|
||||
- 这里的`--adapters`需要替换成训练生成的last checkpoint文件夹。由于adapters文件夹中包含了训练的参数文件`args.json`,因此不需要额外指定`--model`,`--system`,swift会自动读取这些参数。如果要关闭此行为,可以设置`--load_args false`。
|
||||
|
||||
```shell
|
||||
# 使用交互式命令行进行推理
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--adapters output/vx-xxx/checkpoint-xxx \
|
||||
--stream true \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048
|
||||
|
||||
# merge-lora并使用vLLM进行推理加速
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--adapters output/vx-xxx/checkpoint-xxx \
|
||||
--stream true \
|
||||
--merge_lora true \
|
||||
--infer_backend vllm \
|
||||
--vllm_max_model_len 8192 \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
最后,使用以下命令将模型推送到ModelScope:
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift export \
|
||||
--adapters output/vx-xxx/checkpoint-xxx \
|
||||
--push_to_hub true \
|
||||
--hub_model_id '<your-model-id>' \
|
||||
--hub_token '<your-sdk-token>' \
|
||||
--use_hf false
|
||||
```
|
||||
|
||||
## 了解更多
|
||||
|
||||
- 更多Shell脚本:[https://github.com/modelscope/ms-swift/tree/main/examples](https://github.com/modelscope/ms-swift/tree/main/examples)
|
||||
- 使用Python:[https://github.com/modelscope/ms-swift/blob/main/examples/notebook/qwen2_5-self-cognition/self-cognition-sft.ipynb](https://github.com/modelscope/ms-swift/blob/main/examples/notebook/qwen2_5-self-cognition/self-cognition-sft.ipynb)
|
||||
@@ -0,0 +1,180 @@
|
||||
# SWIFT安装
|
||||
|
||||
## Wheel包安装
|
||||
|
||||
可以使用pip进行安装:
|
||||
|
||||
```shell
|
||||
# 推荐
|
||||
pip install 'ms-swift' -U
|
||||
# 额外安装megatron依赖
|
||||
pip install 'ms-swift[megatron]' -U
|
||||
# 额外安装评测依赖
|
||||
pip install 'ms-swift[eval]' -U
|
||||
# 全能力
|
||||
pip install 'ms-swift[all]' -U
|
||||
|
||||
# 使用uv
|
||||
pip install uv
|
||||
uv pip install 'ms-swift' --torch-backend=auto
|
||||
```
|
||||
|
||||
## 源代码安装
|
||||
|
||||
当前main分支为 swift4.x 版本。
|
||||
```shell
|
||||
# pip install git+https://github.com/modelscope/ms-swift.git
|
||||
|
||||
# 全能力
|
||||
# pip install "ms-swift[all]@git+https://github.com/modelscope/ms-swift.git"
|
||||
|
||||
git clone https://github.com/modelscope/ms-swift.git
|
||||
cd ms-swift
|
||||
pip install -e .
|
||||
|
||||
# 使用 uv
|
||||
uv pip install -e . --torch-backend=auto
|
||||
|
||||
# 全能力
|
||||
# pip install -e '.[all]'
|
||||
```
|
||||
|
||||
安装swift3.x:
|
||||
```shell
|
||||
# pip install "git+https://github.com/modelscope/ms-swift.git@release/3.12"
|
||||
|
||||
# 全能力
|
||||
# pip install "ms-swift[all]@git+https://github.com/modelscope/ms-swift.git@release/3.12"
|
||||
|
||||
git clone -b release/3.12 https://github.com/modelscope/ms-swift.git
|
||||
cd ms-swift
|
||||
pip install -e .
|
||||
|
||||
# 全能力
|
||||
# pip install -e '.[all]'
|
||||
```
|
||||
|
||||
## 镜像
|
||||
|
||||
docker可以查看[这里](https://github.com/modelscope/modelscope/blob/build_swift_image/docker/build_image.py#L392)。
|
||||
```
|
||||
# swift4.3.2
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda13.0.3-py312-torch2.11.0-vllm0.23.0-modelscope1.37.1-swift4.3.2
|
||||
modelscope-registry.cn-beijing.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda13.0.3-py312-torch2.11.0-vllm0.23.0-modelscope1.37.1-swift4.3.2
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda13.0.3-py312-torch2.11.0-vllm0.23.0-modelscope1.37.1-swift4.3.2
|
||||
|
||||
# swift4.2.3
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda13.0.3-py312-torch2.11.0-vllm0.21.0-modelscope1.36.3-swift4.2.3
|
||||
modelscope-registry.cn-beijing.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda13.0.3-py312-torch2.11.0-vllm0.21.0-modelscope1.36.3-swift4.2.3
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda13.0.3-py312-torch2.11.0-vllm0.21.0-modelscope1.36.3-swift4.2.3
|
||||
|
||||
# swift4.1.3
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.9.1-py312-torch2.10.0-vllm0.19.1-modelscope1.35.4-swift4.1.3
|
||||
modelscope-registry.cn-beijing.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.9.1-py312-torch2.10.0-vllm0.19.1-modelscope1.35.4-swift4.1.3
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.9.1-py312-torch2.10.0-vllm0.19.1-modelscope1.35.4-swift4.1.3
|
||||
|
||||
# swift4.0.3
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.10.0-vllm0.17.1-modelscope1.34.0-swift4.0.3
|
||||
modelscope-registry.cn-beijing.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.10.0-vllm0.17.1-modelscope1.34.0-swift4.0.3
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.10.0-vllm0.17.1-modelscope1.34.0-swift4.0.3
|
||||
```
|
||||
|
||||
<details><summary>历史镜像</summary>
|
||||
|
||||
```
|
||||
# swift3.12.5
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.9.0-vllm0.13.0-modelscope1.33.0-swift3.12.5
|
||||
modelscope-registry.cn-beijing.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.9.0-vllm0.13.0-modelscope1.33.0-swift3.12.5
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.9.0-vllm0.13.0-modelscope1.33.0-swift3.12.5
|
||||
|
||||
# swift3.11.3
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.9.1-py311-torch2.8.0-vllm0.11.0-modelscope1.32.0-swift3.11.3
|
||||
modelscope-registry.cn-beijing.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.9.1-py311-torch2.8.0-vllm0.11.0-modelscope1.32.0-swift3.11.3
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.9.1-py311-torch2.8.0-vllm0.11.0-modelscope1.32.0-swift3.11.3
|
||||
|
||||
# swift3.10.3
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.8.0-vllm0.11.0-modelscope1.31.0-swift3.10.3
|
||||
modelscope-registry.cn-beijing.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.8.0-vllm0.11.0-modelscope1.31.0-swift3.10.3
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.8.0-vllm0.11.0-modelscope1.31.0-swift3.10.3
|
||||
|
||||
# swift3.9.3
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.8.0-vllm0.11.0-modelscope1.31.0-swift3.9.3
|
||||
modelscope-registry.cn-beijing.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.8.0-vllm0.11.0-modelscope1.31.0-swift3.9.3
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.8.0-vllm0.11.0-modelscope1.31.0-swift3.9.3
|
||||
|
||||
# swift3.8.3
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.6.3-py311-torch2.7.1-vllm0.10.1.1-modelscope1.29.2-swift3.8.3
|
||||
modelscope-registry.cn-beijing.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.6.3-py311-torch2.7.1-vllm0.10.1.1-modelscope1.29.2-swift3.8.3
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.6.3-py311-torch2.7.1-vllm0.10.1.1-modelscope1.29.2-swift3.8.3
|
||||
|
||||
# swift3.7.2
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.6.3-py311-torch2.7.1-vllm0.10.0-modelscope1.28.2-swift3.7.2
|
||||
modelscope-registry.cn-beijing.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.6.3-py311-torch2.7.1-vllm0.10.0-modelscope1.28.2-swift3.7.2
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.6.3-py311-torch2.7.1-vllm0.10.0-modelscope1.28.2-swift3.7.2
|
||||
|
||||
# swift3.6.4
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.4.0-py310-torch2.6.0-vllm0.8.5.post1-modelscope1.28.1-swift3.6.4
|
||||
modelscope-registry.cn-beijing.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.4.0-py310-torch2.6.0-vllm0.8.5.post1-modelscope1.28.1-swift3.6.4
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.4.0-py310-torch2.6.0-vllm0.8.5.post1-modelscope1.28.1-swift3.6.4
|
||||
|
||||
# swift3.5.3
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.4.0-py310-torch2.6.0-vllm0.8.5.post1-modelscope1.27.1-swift3.5.3
|
||||
modelscope-registry.cn-beijing.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.4.0-py310-torch2.6.0-vllm0.8.5.post1-modelscope1.27.1-swift3.5.3
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.4.0-py310-torch2.6.0-vllm0.8.5.post1-modelscope1.27.1-swift3.5.3
|
||||
|
||||
# swift3.4.1.post1
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.4.0-py311-torch2.6.0-vllm0.8.5.post1-modelscope1.26.0-swift3.4.1.post1
|
||||
modelscope-registry.cn-beijing.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.4.0-py311-torch2.6.0-vllm0.8.5.post1-modelscope1.26.0-swift3.4.1.post1
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.4.0-py311-torch2.6.0-vllm0.8.5.post1-modelscope1.26.0-swift3.4.1.post1
|
||||
|
||||
# swift3.3.0.post1
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.4.0-py311-torch2.6.0-vllm0.8.3-modelscope1.25.0-swift3.3.0.post1
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.4.0-py311-torch2.6.0-vllm0.8.3-modelscope1.25.0-swift3.3.0.post1
|
||||
|
||||
# swift3.2.2
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.4.0-py311-torch2.5.1-modelscope1.25.0-swift3.2.2
|
||||
```
|
||||
</details>
|
||||
|
||||
更多镜像可以查看[这里](https://modelscope.cn/docs/intro/environment-setup#%E6%9C%80%E6%96%B0%E9%95%9C%E5%83%8F)。
|
||||
|
||||
## 支持的硬件
|
||||
|
||||
| 硬件环境 | 备注 |
|
||||
| --------------- | --------------------------- |
|
||||
| A10/A100/H100 | |
|
||||
| RTX20/30/40系列 | |
|
||||
| T4/V100 | 部分模型出现NAN |
|
||||
| Ascend NPU | 部分模型出现NAN或算子不支持 |
|
||||
| MPS | 参考[issue 4572](https://github.com/modelscope/ms-swift/issues/4572) |
|
||||
| CPU | |
|
||||
|
||||
|
||||
## 运行环境
|
||||
|
||||
| | 范围 | 推荐 | 备注 |
|
||||
|--------------|--------------|---------------------|--------------------|
|
||||
| python | >=3.10 | 3.12 | |
|
||||
| cuda | | cuda12.8/13.0 | 使用cpu、npu、mps则无需安装 |
|
||||
| torch | >=2.0 | 2.8.0/2.11.0 | |
|
||||
| transformers | >=4.33 | 4.57.6/5.12.1 | |
|
||||
| modelscope | >=1.23 | | |
|
||||
| datasets | >=3.0,<4.8.5 | 3.6.0/4.8.4 | |
|
||||
| peft | >=0.11,<0.20 | | |
|
||||
| flash_attn | | 2.8.3/4.0.0b15 | |
|
||||
| trl | >=0.15,<1.0 | 0.29.1 | RLHF |
|
||||
| deepspeed | >=0.14 | 0.18.9 | 训练 |
|
||||
| vllm | >=0.5.1 | 0.11.0/0.23.0 | 推理/部署 |
|
||||
| sglang | >=0.4.6 | | 推理/部署 |
|
||||
| evalscope | >=1.0 | | 评测 |
|
||||
| gradio | | 5.32.1 | Web-UI/App |
|
||||
|
||||
更多可选依赖可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/requirements/install_all.sh)。
|
||||
|
||||
## Notebook环境
|
||||
|
||||
Swift支持训练的绝大多数模型都可以在`A10`显卡上使用,用户可以使用ModelScope官方提供的免费显卡资源:
|
||||
|
||||
1. 进入[ModelScope](https://www.modelscope.cn)官方网站并登录。
|
||||
2. 点击左侧的`我的Notebook`并开启一个免费GPU实例。
|
||||
3. 愉快地薅A10显卡羊毛。
|
||||
@@ -0,0 +1,36 @@
|
||||
# Web-UI
|
||||
|
||||
目前SWIFT已经支持了界面化的训练和推理,参数支持和脚本训练相同。在安装SWIFT后,使用如下命令:
|
||||
|
||||
```shell
|
||||
swift web-ui --lang zh
|
||||
# or en
|
||||
swift web-ui --lang en
|
||||
```
|
||||
|
||||
开启界面训练和推理。
|
||||
|
||||
SWIFT web-ui是命令行的高级封装,即,在界面上启动的训练、部署等任务,会在系统中以命令行启动一个独立的进程,伪代码类似:
|
||||
```python
|
||||
import os
|
||||
os.system('swift sft --model xxx --dataset xxx')
|
||||
```
|
||||
|
||||
这给web-ui带来了几个特性:
|
||||
1. web-ui的每个超参数描述都带有`--xxx`的标记,这与[命令行参数](../Instruction/Command-line-parameters.md)的内容是一致的
|
||||
2. web-ui可以在一台多卡机器上并行启动多个训练/部署任务
|
||||
3. web-ui服务关闭后,后台服务是仍旧运行的,这防止了web-ui被关掉后影响训练进程,如果需要关闭后台服务,只需要**选择对应的任务**后在界面上的`运行时`tab点击杀死服务
|
||||
4. 重新启动web-ui后,如果需要显示正在运行的服务,在`运行时`tab点击`找回运行时任务`即可
|
||||
5. 训练界面支持显示运行日志,请在选择某个任务后手动点击`展示运行状态`,在训练时运行状态支持展示训练图表,图标包括训练loss、训练acc、学习率等基本指标,在人类对齐任务重界面图标为margin、logps等关键指标
|
||||
6. web-ui的训练不支持PPO,该过程比较复杂,建议使用examples的[shell脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/ppo)直接运行
|
||||
|
||||
如果需要使用share模式,请添加`--share true`参数。注意:请不要在dsw、notebook等环境中使用该参数。
|
||||
|
||||
目前ms-swift额外支持了界面推理模式(即Space部署):
|
||||
|
||||
```shell
|
||||
swift app --model '<model>' --studio_title My-Awesome-Space --stream true
|
||||
# 或者
|
||||
swift app --model '<model>' --adapters '<adapter>' --stream true
|
||||
```
|
||||
即可启动一个只有推理页面的应用,该应用会在启动时对模型进行部署并提供后续使用。
|
||||
@@ -0,0 +1,240 @@
|
||||
# Agent支持
|
||||
|
||||
## 数据集格式
|
||||
|
||||
ms-swift 使用 agent-template 实现了Agent数据格式与模型的解耦:基于统一的数据集格式,可以灵活切换不同模型进行训练,无需修改数据。
|
||||
|
||||
纯文本Agent和多模态Agent的示例数据样本如下:
|
||||
```jsonl
|
||||
{"tools": "[{\"type\": \"function\", \"function\": {\"name\": \"realtime_aqi\", \"description\": \"天气预报。获取实时空气质量。当前空气质量,PM2.5,PM10信息\", \"parameters\": {\"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\", \"description\": \"城市名,例如:上海\"}}, \"required\": [\"city\"]}}}]", "messages": [{"role": "user", "content": "北京和上海今天的天气情况"}, {"role": "tool_call", "content": "{\"name\": \"realtime_aqi\", \"arguments\": {\"city\": \"北京\"}}"}, {"role": "tool_call", "content": "{\"name\": \"realtime_aqi\", \"arguments\": {\"city\": \"上海\"}}"}, {"role": "tool_response", "content": "{\"city\": \"北京\", \"aqi\": \"10\", \"unit\": \"celsius\"}"}, {"role": "tool_response", "content": "{\"city\": \"上海\", \"aqi\": \"72\", \"unit\": \"fahrenheit\"}"}, {"role": "assistant", "content": "根据天气预报工具,北京今天的空气质量指数为10,属于良好水平;上海今天的空气质量指数为72,属于轻度污染水平。"}]}
|
||||
{"tools": "[{\"type\": \"function\", \"function\": {\"name\": \"click\", \"description\": \"点击屏幕中的某个位置\", \"parameters\": {\"type\": \"object\", \"properties\": {\"x\": {\"type\": \"integer\", \"description\": \"横坐标,表示屏幕上的水平位置\"}, \"y\": {\"type\": \"integer\", \"description\": \"纵坐标,表示屏幕上的垂直位置\"}}, \"required\": [\"x\", \"y\"]}}}]", "messages": [{"role": "user", "content": "<image>现在几点了?"}, {"role": "assistant", "content": "<think>\n我可以通过打开日历App来获取当前时间。\n</think>\n"}, {"role": "tool_call", "content": "{\"name\": \"click\", \"arguments\": {\"x\": 105, \"y\": 132}}"}, {"role": "tool_response", "content": "{\"images\": \"<image>\", \"status\": \"success\"}"}, {"role": "assistant", "content": "成功打开日历App,现在的时间为中午11点"}], "images": ["desktop.png", "calendar.png"]}
|
||||
```
|
||||
- agent_template为"react_en", "hermes"等情况下,该格式适配所有模型Agent训练,可以轻松在不同模型间切换。
|
||||
- 其中tools是一个包含tool列表的json字符串,messages中role为'tool_call'和'tool_response/tool'的content部分都需要是json字符串。
|
||||
- tools字段将在训练/推理时和`{"role": "system", ...}"`部分组合,根据agent_template组成完整的system部分。
|
||||
- `{"role": "tool_call", ...}`部分将根据agent_template自动转成对应格式的`{"role": "assistant", ...}`,多条连续的`{"role": "assistant", ...}`将拼接在一起组成完整的assistant_content。
|
||||
- `{"role": "tool_response", ...}`也可以写成`{"role": "tool", ...}`,这两种写法是等价的。该部分也将根据`agent_template`自动转换格式。该部分在训练时将不进行损失的计算,角色类似于`{"role": "user", ...}`。
|
||||
- 该格式支持并行调用工具,例子参考第一条数据样本。多模态Agent数据样本中`<image>`标签数量应与"images"长度相同,其标签位置代表图像特征的插入位置。当然也支持其他模态,例如audios, videos。
|
||||
- 注意:您也可以手动将数据处理为role为system/user/assistant的messages格式。agent_template的作用是将其中的tools字段以及role为tool_call和tool_response的messages部分,自动映射为标准的role为system/user/assistant的messages格式。
|
||||
|
||||
以下为上述两条数据样本由qwen2_5和qwen2_5_vl的template进行encode后的input_ids和labels,选择的agent_template为**hermes**:
|
||||
|
||||
样本一(并行工具调用):
|
||||
```text
|
||||
[INPUT_IDS] <|im_start|>system
|
||||
You are Qwen, created by Alibaba Cloud. You are a helpful assistant.
|
||||
|
||||
# Tools
|
||||
|
||||
You may call one or more functions to assist with the user query.
|
||||
|
||||
You are provided with function signatures within <tools></tools> XML tags:
|
||||
<tools>
|
||||
{"type": "function", "function": {"name": "realtime_aqi", "description": "天气预报。获取实时空气质量。当前空气质量,PM2.5,PM10信息", "parameters": {"type": "object", "properties": {"city": {"type": "string", "description": "城市名,例如:上海"}}, "required": ["city"]}}}
|
||||
</tools>
|
||||
|
||||
For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
|
||||
<tool_call>
|
||||
{"name": <function-name>, "arguments": <args-json-object>}
|
||||
</tool_call><|im_end|>
|
||||
<|im_start|>user
|
||||
北京和上海今天的天气情况<|im_end|>
|
||||
<|im_start|>assistant
|
||||
<tool_call>
|
||||
{"name": "realtime_aqi", "arguments": {"city": "北京"}}
|
||||
</tool_call>
|
||||
<tool_call>
|
||||
{"name": "realtime_aqi", "arguments": {"city": "上海"}}
|
||||
</tool_call><|im_end|>
|
||||
<|im_start|>user
|
||||
<tool_response>
|
||||
{"city": "北京", "aqi": "10", "unit": "celsius"}
|
||||
</tool_response>
|
||||
<tool_response>
|
||||
{"city": "上海", "aqi": "72", "unit": "fahrenheit"}
|
||||
</tool_response><|im_end|>
|
||||
<|im_start|>assistant
|
||||
根据天气预报工具,北京今天的空气质量指数为10,属于良好水平;上海今天的空气质量指数为72,属于轻度污染水平。<|im_end|>
|
||||
|
||||
[LABELS] [-100 * 195]<tool_call>
|
||||
{"name": "realtime_aqi", "arguments": {"city": "北京"}}
|
||||
</tool_call>
|
||||
<tool_call>
|
||||
{"name": "realtime_aqi", "arguments": {"city": "上海"}}
|
||||
</tool_call><|im_end|>[-100 * 67]根据天气预报工具,北京今天的空气质量指数为10,属于良好水平;上海今天的空气质量指数为72,属于轻度污染水平。<|im_end|>
|
||||
```
|
||||
|
||||
样本二(多模态,混合assistant和tool_call):
|
||||
```text
|
||||
[INPUT_IDS] <|im_start|>system
|
||||
You are a helpful assistant.
|
||||
|
||||
# Tools
|
||||
|
||||
You may call one or more functions to assist with the user query.
|
||||
|
||||
You are provided with function signatures within <tools></tools> XML tags:
|
||||
<tools>
|
||||
{"type": "function", "function": {"name": "click", "description": "点击屏幕中的某个位置", "parameters": {"type": "object", "properties": {"x": {"type": "integer", "description": "横坐标,表示屏幕上的水平位置"}, "y": {"type": "integer", "description": "纵坐标,表示屏幕上的垂直位置"}}, "required": ["x", "y"]}}}
|
||||
</tools>
|
||||
|
||||
For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
|
||||
<tool_call>
|
||||
{"name": <function-name>, "arguments": <args-json-object>}
|
||||
</tool_call><|im_end|>
|
||||
<|im_start|>user
|
||||
<|vision_start|>[151655 * 729]<|vision_end|>现在几点了?<|im_end|>
|
||||
<|im_start|>assistant
|
||||
<think>
|
||||
我可以通过打开日历App来获取当前时间。
|
||||
</think>
|
||||
<tool_call>
|
||||
{"name": "click", "arguments": {"x": 105, "y": 132}}
|
||||
</tool_call><|im_end|>
|
||||
<|im_start|>user
|
||||
<tool_response>
|
||||
{"images": "<|vision_start|>[151655 * 729]<|vision_end|>", "status": "success"}
|
||||
</tool_response><|im_end|>
|
||||
<|im_start|>assistant
|
||||
成功打开日历App,现在的时间为中午11点<|im_end|>
|
||||
|
||||
[LABELS] [-100 * 924]<think>
|
||||
我可以通过打开日历App来获取当前时间。
|
||||
</think>
|
||||
<tool_call>
|
||||
{"name": "click", "arguments": {"x": 105, "y": 132}}
|
||||
</tool_call><|im_end|>[-100 * 759]成功打开日历App,现在的时间为中午11点<|im_end|>
|
||||
```
|
||||
|
||||
**react_en**是常用的agent template格式之一,以下为样本一由qwen2_5使用`agent_template='react_en'`进行encode后的input_ids和labels:
|
||||
|
||||
```text
|
||||
[INPUT_IDS] <|im_start|>system
|
||||
Answer the following questions as best you can. You have access to the following tools:
|
||||
|
||||
realtime_aqi: Call this tool to interact with the realtime_aqi API. What is the realtime_aqi API useful for? 天气预报。获取实时空气质量。当前空气质量,PM2.5,PM10信息 Parameters: {"type": "object", "properties": {"city": {"type": "string", "description": "城市名,例如:上海"}}, "required": ["city"]} Format the arguments as a JSON object.
|
||||
|
||||
Use the following format:
|
||||
|
||||
Question: the input question you must answer
|
||||
Thought: you should always think about what to do
|
||||
Action: the action to take, should be one of [realtime_aqi]
|
||||
Action Input: the input to the action
|
||||
Observation: the result of the action
|
||||
... (this Thought/Action/Action Input/Observation can be repeated zero or more times)
|
||||
Thought: I now know the final answer
|
||||
Final Answer: the final answer to the original input question
|
||||
|
||||
Begin!
|
||||
<|im_end|>
|
||||
<|im_start|>user
|
||||
北京和上海今天的天气情况<|im_end|>
|
||||
<|im_start|>assistant
|
||||
Action: realtime_aqi
|
||||
Action Input: {'city': '北京'}
|
||||
Action: realtime_aqi
|
||||
Action Input: {'city': '上海'}
|
||||
Observation:{"city": "北京", "aqi": "10", "unit": "celsius"}
|
||||
Observation:{"city": "上海", "aqi": "72", "unit": "fahrenheit"}
|
||||
根据天气预报工具,北京今天的空气质量指数为10,属于良好水平;上海今天的空气质量指数为72,属于轻度污染水平。<|im_end|>
|
||||
|
||||
[LABELS] [-100 * 233]Action: realtime_aqi
|
||||
Action Input: {'city': '北京'}
|
||||
Action: realtime_aqi
|
||||
Action Input: {'city': '上海'}
|
||||
Observation:[-100 * 45]根据天气预报工具,北京今天的空气质量指数为10,属于良好水平;上海今天的空气质量指数为72,属于轻度污染水平。<|im_end|>
|
||||
```
|
||||
|
||||
更多模型和agent_template的尝试可以使用以下代码,更多的agent template可选值参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/agent_template/__init__.py)。
|
||||
```python
|
||||
from swift import get_processor, get_template
|
||||
|
||||
tokenizer = get_processor('Qwen/Qwen3.5-2B')
|
||||
template = get_template(tokenizer) # 使用默认agent模板
|
||||
# template = get_template(tokenizer, agent_template='qwen3_5')
|
||||
print(f'agent_template: {template._agent_template}')
|
||||
data = {...}
|
||||
template.set_mode('train')
|
||||
encoded = template.encode(data)
|
||||
print(f'[INPUT_IDS] {template.safe_decode(encoded["input_ids"])}\n')
|
||||
print(f'[LABELS] {template.safe_decode(encoded["labels"])}')
|
||||
```
|
||||
|
||||
|
||||
## tools格式
|
||||
tools字段提供了模型可以调用的API信息。你需要提供tools的名字,描述和参数,示例如下:
|
||||
|
||||
```python
|
||||
tools = [{
|
||||
'type': 'function',
|
||||
'function': {
|
||||
'name': 'get_current_weather',
|
||||
'description': 'Get the current weather in a given location',
|
||||
'parameters': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'location': {
|
||||
'type': 'string',
|
||||
'description': 'The city and state, e.g. San Francisco, CA'
|
||||
},
|
||||
'unit': {
|
||||
'type': 'string',
|
||||
'enum': ['celsius', 'fahrenheit']
|
||||
}
|
||||
},
|
||||
'required': ['location']
|
||||
}
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
## loss_scale的使用
|
||||
|
||||
loss_scale参数可用于调节模型输出部分在训练过程中的损失权重。目前支持两种配置方式:字符串精确匹配和正则表达式匹配。
|
||||
|
||||
1. 字符串匹配示例:ReACT 格式
|
||||
|
||||
以 ReACT 格式为例,可通过 `--loss_scale react` 启用相应的 loss_scale 配置(配置文件详见 [react.json](https://github.com/modelscope/ms-swift/blob/main/swift/loss_scale/config/react.json))。该方式基于字符串精确匹配,配置中的字典映射需提供一个包含两个元素的列表,分别表示:当前匹配字符串本身的损失权重,
|
||||
从该字符串之后到下一个指定字符串之前的内容的损失权重。该设置的具体效果如下:
|
||||
- 'Action:' 和 'Action Input:' 字段自身及其后续内容的损失权重均为 2;
|
||||
- 'Thought:' 和 'Final Answer:' 字段自身及其后续内容的损失权重均为 1;
|
||||
- 'Observation:' 字段自身的权重为 2,但其后跟随的工具调用结果部分的损失权重为 0。
|
||||
|
||||
2. 正则匹配示例:忽略空思维块
|
||||
|
||||
在训练推理模型时,我们可能需要忽略数据集中存在的形如 `'<think>\n\n</think>\n\n'`的空思维标记损失计算。此时可使用 `--loss_scale ignore_empty_think`(配置文件详见 [ignore_empty_think.json](https://github.com/modelscope/ms-swift/blob/main/swift/loss_scale/config/ignore_empty_think.json))。该配置采用正则表达式匹配方式,字典映射的列表只需指定一个值,表示匹配内容的损失权重。该设置的具体效果如下:
|
||||
|
||||
- 所有与正则表达式`<think>\\s*</think>\\s*`匹配的字符串,loss_scale为0,即不计算损失。
|
||||
|
||||
使用代码测试loss_scale:
|
||||
```python
|
||||
from swift import get_processor, get_template
|
||||
|
||||
data = {"messages": [
|
||||
{"role": "user", "content": "aaaaa"},
|
||||
{"role": "assistant", "content": "<think>\n\n</think>\n\nabc<think>\n\n</think>\n\n123"},
|
||||
]}
|
||||
|
||||
template = get_template(get_processor('Qwen/Qwen3-8B'), loss_scale='ignore_empty_think')
|
||||
template.set_mode('train')
|
||||
inputs = template.encode(data)
|
||||
|
||||
print(template.safe_decode(inputs['labels']))
|
||||
# '[-100 * 14]abc<think>\n\n</think>\n\n123<|im_end|>\n'
|
||||
```
|
||||
|
||||
更多的loss_scale插件设计,请参考[架构](../Customization/Architecture.md#loss-scale)文档.
|
||||
|
||||
## 训练
|
||||
- 训练Base模型的Agent能力,通过修改`--model`切换不同模型,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/agent/qwen2_5.sh)。
|
||||
- 训练GLM4的agent_template为hermes,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/agent/glm4.sh)。
|
||||
- 使用`--loss_scale`对模型输出部分的损失权重进行调整,参加[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/agent/loss_scale)。
|
||||
|
||||
## 推理
|
||||
|
||||
- 🚀原始模型或者全参数训练后模型的推理,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_agent.py)。
|
||||
- LoRA训练后推理,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/agent/loss_scale/infer_lora.py)。
|
||||
|
||||
## 部署
|
||||
|
||||
服务端和客户端代码,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/deploy/agent)。
|
||||
@@ -0,0 +1,964 @@
|
||||
# 命令行参数
|
||||
|
||||
命令行参数的介绍会分为基本参数,原子参数、集成参数和特定模型参数。**命令行最终使用的参数列表为集成参数。集成参数继承自基本参数和一些原子参数**。特定模型参数是针对于具体模型的参数,可以通过`--model_kwargs'`或者环境变量进行设置。Megatron-SWIFT命令行参数介绍可以在[Megatron-SWIFT训练文档](../Megatron-SWIFT/Command-line-parameters.md)中找到。
|
||||
|
||||
**提示:**
|
||||
- 命令行传入list使用空格隔开即可。例如:`--dataset <dataset_path1> <dataset_path2>`。
|
||||
- 命令行传入dict使用json。例如:`--model_kwargs '{"fps_max_frames": 12}'`。
|
||||
- 带🔥的参数为重要参数,刚熟悉ms-swift的用户可以先关注这些命令行参数。
|
||||
|
||||
## 基本参数
|
||||
|
||||
- 🔥tuner_backend: 可选为'peft','unsloth'。默认为'peft'。
|
||||
- 🔥tuner_type: 可选为'lora'、'full'、'lora_llm'、'longlora'、'adalora'、'llamapro'、'adapter'、'vera'、'boft'、'fourierft'、'reft'、'bone'。默认为'lora'。
|
||||
- 注意:在Megatron-SWIFT中默认为'full'。
|
||||
- 其中'lora_llm'代表对llm部分进行lora,vit/aligner部分使用'full'。你可以使用`vit_lr/aligner_lr`设置各自的学习率。
|
||||
- 🔥adapters: 用于指定adapter的id/path的list,默认为`[]`。该参数通常用于推理/部署命令,例如:`swift infer --model '<model_id_or_path>' --adapters '<adapter_id_or_path>'`。该参数偶尔也用于断点续训,该参数与`resume_from_checkpoint`的区别在于,**该参数只读取adapter权重**,而不加载优化器和随机种子,并不跳过已训练的数据集部分。
|
||||
- `--model`与`--adapters`的区别:`--model`后接完整权重的目录路径,内包含model/tokenizer/config等完整权重信息,例如`model.safetensors`。`--adapters`后接增量adapter权重目录路径的列表,内涵adapter的增量权重信息,例如`adapter_model.safetensors`。
|
||||
- 🔥external_plugins: 外部`plugin.py`文件列表,这些文件会被额外加载(即对模块进行`import`)。默认为`[]`。你可以传入自定义模型、对话模板和数据集注册的`.py`文件路径,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/custom/sft.sh);或者自定义GRPO的组件,参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin/run_external_reward_func.sh)。
|
||||
- seed: 全局随机种子,默认为42。
|
||||
- 注意:该随机种子与控制数据集随机的`data_seed`互不影响。
|
||||
- model_kwargs: 特定模型可传入的额外参数,该参数列表会在训练/推理时打印日志进行提示。例如`--model_kwargs '{"fps_max_frames": 12}'`。你也可以通过环境变量的方式设置,例如`FPS_MAX_FRAMES=12`。默认为None。
|
||||
- 注意:**若你在训练时指定了特定模型参数,请在推理时也设置对应的参数**,这可以提高训练效果。
|
||||
- 特定模型参数的含义可以在对应模型官方repo或者其推理代码中找到相应含义。ms-swift引入这些参数以确保训练的模型与官方推理代码效果对齐。
|
||||
- enable_npu_model_patch: 是否启用NPU模型层patch,默认为True。该参数仅控制NPU环境下模型相关的patch,通常不需要关闭;排查transformers原生行为或NPU模型patch兼容问题时可以设置为False。该参数需要在进程首次导入`swift.model`前作为启动参数传入。
|
||||
- load_args: 当指定`--resume_from_checkpoint`、`--model`、`--adapters`会读取保存文件中的`args.json`,读取的keys查看[base_args.py](https://github.com/modelscope/ms-swift/blob/main/swift/arguments/base_args/base_args.py)。推理和导出时默认为True,训练时默认为False。该参数通常不需要修改。
|
||||
- load_data_args: 如果将该参数设置为True,则会额外读取`args.json`中的数据参数。默认为False。**该参数通常用于推理时对训练中切分的验证集进行推理**,例如:`swift infer --adapters xxx --load_data_args true --stream true --max_new_tokens 512`。
|
||||
- use_hf: 控制模型下载、数据集下载、模型推送使用[ModelScope](https://modelscope.cn/)还是[HuggingFace](https://huggingface.co/)。默认为False,使用ModelScope。
|
||||
- 提示:如果你想在国外访问ModelScope,可以尝试使用[ModelScope国际版](https://modelscope.ai/home),设置环境变量`MODELSCOPE_DOMAIN='www.modelscope.ai'`即可。
|
||||
- hub_token: hub token. modelscope的hub token可以查看[这里](https://modelscope.cn/my/myaccesstoken)。默认为None。
|
||||
- ddp_timeout: 默认为18000000,单位为秒。
|
||||
- ddp_backend: 可选为"nccl"、"gloo"、"mpi"、"ccl"、"hccl"、"cncl"、"mccl"。默认为None,进行自动选择。
|
||||
- ignore_args_error: 用于兼容jupyter notebook。默认为False。
|
||||
|
||||
### 模型参数
|
||||
- 🔥model: [模型id](https://modelscope.cn/models)或模型本地路径。默认为None。
|
||||
- 🔥model_type: 模型类型。**我们将相同的模型架构、模型加载过程、template定义为一个`model_type`**。默认为None,即**根据`--model`的后缀和config.json中的'architectures'属性进行自动选择**。对应模型的model_type可以在[支持的模型列表](./Supported-models-and-datasets.md)中找到。
|
||||
- 注意:ms-swift中model_type的概念与`config.json`中的model_type不同。
|
||||
- 自定义模型通常需要自行注册`model_type`和`template`,具体可以参考[自定义模型文档](../Customization/Custom-model.md)。
|
||||
- model_revision: 模型版本,默认为None。
|
||||
- task_type: 默认为'causal_lm'。可选为'causal_lm'、'seq_cls'、'embedding'、'reranker'和'generative_reranker'。seq_cls的例子可以查看[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/seq_cls),embedding的例子查看[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/embedding)。
|
||||
- 若设置为'seq_cls',你通常需要额外设置`--num_labels`和`--problem_type`。
|
||||
- 🔥torch_dtype: 模型权重的数据类型,支持`float16`,`bfloat16`,`float32`。默认为None,从'config.json'文件中读取。
|
||||
- attn_impl: attention类型,可选项为'sdpa', 'eager', 'flash_attn', 'flash_attention_2', 'flash_attention_3', 'flash_attention_4'等。默认使用None,读取'config.json'。
|
||||
- 注意:这几种attention实现并不一定都支持,这取决于对应模型transformers实现的支持情况。
|
||||
- 若设置为'flash_attn'(兼容旧版本),则使用'flash_attention_2'。
|
||||
- 🔥experts_impl: 专家实现类型,可选项为'grouped_mm', 'batched_mm', 'eager'。默认为None。该特性需要"transformers>=5.0.0"。
|
||||
- new_special_tokens: 需要新增的特殊tokens。默认为`[]`。例子参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/new_special_tokens)。
|
||||
- 注意:你也可以传入以`.txt`结尾的文件路径,每行为一个special token。
|
||||
- num_labels: 分类模型(即`--task_type seq_cls`)需要指定该参数。代表标签数量,默认为None。
|
||||
- problem_type: 分类模型(即`--task_type seq_cls`)需要指定该参数。可选为'regression', 'single_label_classification', 'multi_label_classification'。默认为None,若模型为 reward_model 或 num_labels 为1,该参数为'regression',其他情况,该参数为'single_label_classification'。
|
||||
- rope_scaling: rope类型,你可以传入字符串,例如:`linear`、`dynamic`、`yarn`并结合传入`max_model_len`,ms-swift会自动设置对应的rope_scaling并覆盖'config.json'中的rope_scaling。或者你需要传入一个json字符串,例如`'{"factor":2.0, "type":"yarn"}'`,该值会直接覆盖'config.json'中的rope_scaling。默认为None。
|
||||
- max_model_len: 如果使用`rope_scaling`并传入字符串,可以设置`max_model_len`,该参数用来计算rope的`factor`倍数。该参数默认为None。若为非None,该参数会**覆盖**'config.json'中的`max_position_embeddings`值。
|
||||
- device_map: 模型使用的device_map配置,例如:'auto'、'cpu'、json字符串、json文件路径。该参数会**透传**入transformers的`from_pretrained`接口。默认为None,根据设备和分布式训练情况自动设置。
|
||||
- max_memory: device_map设置为'auto'或者'sequential'时,会根据max_memory进行模型权重的device分配,例如:`--max_memory '{0: "20GB", 1: "20GB"}'`。默认为None。该参数会透传入transformers的`from_pretrained`接口。
|
||||
- local_repo_path: 部分模型在加载时依赖于github repo,例如[deepseek-vl2](https://github.com/deepseek-ai/DeepSeek-VL2)。为了避免`git clone`时遇到网络问题,可以直接使用本地repo。该参数需要传入本地repo的路径, 默认为`None`。
|
||||
- init_strategy: 加载模型时,初始化模型中所有未初始化的参数(自定义模型架构时)。可选为'zero', 'uniform', 'normal', 'xavier_uniform', 'xavier_normal', 'kaiming_uniform', 'kaiming_normal', 'orthogonal'。默认为None。
|
||||
|
||||
|
||||
### 数据参数
|
||||
- 🔥dataset: 数据集id或路径的list。默认为`[]`。每个数据集的传入格式为:`'数据集id or 数据集路径:子数据集#采样数量'`,其中子数据集和取样数据可选。本地数据集支持jsonl、csv、json、文件夹等。**hub端的开源数据集可以通过`git clone`到本地并将文件夹传入而离线使用**。自定义数据集格式可以参考[自定义数据集文档](../Customization/Custom-dataset.md)。你可以传入`--dataset <dataset1> <dataset2>`来使用多个数据集。
|
||||
- 子数据集: 该参数只有当dataset为ID或者文件夹时生效。若注册时指定了subsets,且只有一个子数据集,则默认选择注册时指定的子数据集,否则默认为'default'。你可以使用`/`来选择多个子数据集,例如:`<dataset_id>:subset1/subset2`。你也可以使用'all'来选择注册时指定的所有子数据集,例如:`<dataset_id>:all`。注册例子可以参考[这里](https://modelscope.cn/datasets/swift/garbage_competition)。
|
||||
- 采样数量: 默认使用完整的数据集。你可以通过设置`#采样数`对选择的数据集进行采样,例如`<dataset_path#1000>`。若采样数少于数据样本总数,则进行随机选择(不重复采样)。若采样数高于数据样本总数,则只额外随机采样`采样数%数据样本总数`的样本,数据样本重复采样`采样数//数据样本总数`次。注意:流式数据集(`--streaming true`)只进行顺序采样。若设置`--dataset_shuffle false`,则非流式数据集也进行顺序采样。
|
||||
- 🔥val_dataset: 验证集id或路径的list。默认为`[]`。
|
||||
- 🔥cached_dataset: 使用缓存数据集(使用`swift export --to_cached_dataset true ...`命令产生),避免大型数据集训练/推理时,tokenize过程占用gpu时间。该参数用于设置缓存训练数据集文件夹路径,默认为`[]`。例子参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/cached_dataset)。
|
||||
- 提示:cached_dataset只会在数据集中额外存储length字段(为避免存储压力),并过滤掉会报错的数据样本。在训练/推理时,支持`--max_length`参数进行超长数据过滤/裁剪以及`--packing`参数。数据实际预处理过程将在训练时同步进行,该过程和训练是重叠的,并不会影响训练速度。(例外情况:当设置`--truncation_strategy split`会存储'input_ids',你需要在数据导出和训练时设置相同的`max_length`和`truncation_strategy`,packing兼容不受影响)
|
||||
- cached_dataset在`ms-swift`和`Megatron-SWIFT`之间是通用的,且支持pt/sft/infer/rlhf,使用`--template_mode`设置训练类型;支持embedding/reranker/seq_cls任务,使用`--task_type`设置任务类型。
|
||||
- 支持对cache_dataset进行采样,语法为`<cached_dataset_path>#采样数`,支持采样数高于和少于样本数的情况,功能与实现参考`--dataset`的介绍。
|
||||
- cached_val_dataset: 缓存验证数据集的文件夹路径,默认为`[]`。
|
||||
- 🔥split_dataset_ratio: 不指定val_dataset时从训练集拆分验证集的比例,默认为0.,即不从训练集切分验证集。
|
||||
- data_seed: 数据集随机种子,默认为42。
|
||||
- 🔥dataset_num_proc: 数据集预处理的进程数,默认为1。
|
||||
- 提示:纯文本模型建议将该值开大加速预处理速度。而多模态模型不建议开太大,这可能导致更慢的预处理速度(多模态模型若出现cpu利用率100%,但是处理速度极慢的情况,建议额外设置`OMP_NUM_THREADS`环境变量)。
|
||||
- 🔥load_from_cache_file: 是否从缓存中加载数据集,默认为False。**建议在实际运行时设置为True,debug阶段设置为False**。你可以修改`MODELSCOPE_CACHE`环境变量控制缓存的路径。
|
||||
- dataset_shuffle: 是否对dataset进行随机操作。默认为True。
|
||||
- 注意:**CPT/SFT的随机包括两个部分**:数据集的随机,由`dataset_shuffle`控制;train_dataloader中的随机,由`train_dataloader_shuffle`控制。
|
||||
- val_dataset_shuffle: 是否对val_dataset进行随机操作。默认为False。
|
||||
- streaming: 流式读取并处理数据集,默认False。(流式数据集的随机并不彻底,可能导致loss波动剧烈。)
|
||||
- 注意:需要额外设置`--max_steps`,因为流式数据集无法获得其长度。你可以通过设置`--save_strategy epoch`并设置较大的max_steps来实现与`--num_train_epochs`等效的训练。或者,你也可以设置`max_epochs`确保训练到对应epochs时退出训练,并对权重进行验证和保存。
|
||||
- 注意:流式数据集可以跳过预处理等待,将预处理时间与训练时间重叠。流式数据集的预处理只在rank0上进行,并通过数据分发的方式同步到其他进程,**其通常效率不如非流式数据集采用的数据分片读取方式**。当训练的world_size较大时,预处理和数据分发将成为训练瓶颈。
|
||||
- interleave_prob: 默认值为 None。在组合多个数据集时,默认使用datasets库的 `concatenate_datasets` 函数;如果设置了该参数,则会使用 `interleave_datasets` 函数。该参数通常用于流式数据集的组合,并会作为参数传入 `interleave_datasets` 函数中。该参数不对`--val_dataset`生效。
|
||||
- stopping_strategy: 可选为"first_exhausted", "all_exhausted",默认为"first_exhausted"。传入`interleave_datasets`函数中。该参数不对`--val_dataset`生效。
|
||||
- shuffle_buffer_size: 该参数用于指定**流式数据集**的随机buffer大小,默认为1000。该参数只在`dataset_shuffle`设置为true时有效。
|
||||
- download_mode: 数据集下载模式,包含`reuse_dataset_if_exists`和`force_redownload`,默认为'reuse_dataset_if_exists'。
|
||||
- 通常在使用hub端数据集报错时设置为`--download_mode force_redownload`。
|
||||
- columns: 用于对数据集进行列映射,使数据集满足AutoPreprocessor可以处理的样式,AutoPreprocessor可以处理的数据集格式查看[自定义数据集文档](../Customization/Custom-dataset.md)。你可以传入json字符串,例如:`'{"text1": "query", "text2": "response"}'`,代表将数据集中的"text1"映射为"query","text2"映射为"response",而query-response格式可以被AutoPreprocessor处理。默认为None。
|
||||
- strict: 如果为True,则数据集只要某行有问题直接抛错,否则会丢弃出错数据样本。默认False。该参数通常用于排查错误。
|
||||
- 🔥remove_unused_columns: 是否删除数据集中不被使用的列,默认为True。
|
||||
- 若该参数设置为False,则将额外的数据集列传递至trainer的`compute_loss`函数内,**方便自定义损失函数使用额外的数据集列**。
|
||||
- GRPO该参数的默认值为False。
|
||||
- disable_auto_column_mapping: 默认情况下会对数据集的列名进行自动映射,该参数用于关闭此行为(columns 参数依旧生效),默认为False。自动映射规则如下:
|
||||
- 以下字段会自动转成对应的images, videos, audios字段。
|
||||
- images: image, images.
|
||||
- videos: video, videos.
|
||||
- audios: audio, audios.
|
||||
- 以下字段会自动转成对应的system、query、response字段。(solution字段会保留)
|
||||
- system: 'system', 'system_prompt'.
|
||||
- query: 'query', 'prompt', 'input', 'instruction', 'question', 'problem'.
|
||||
- response: 'response', 'answer', 'output', 'targets', 'target', 'answer_key', 'answers', 'solution', 'text', 'completion', 'content'.
|
||||
- 🔥model_name: **仅用于自我认知任务**,只对`swift/self-cognition`数据集生效,替换掉数据集中的`{{NAME}}`通配符。传入模型中文名和英文名,以空格分隔,例如:`--model_name 小黄 'Xiao Huang'`。默认为None。
|
||||
- 🔥model_author: 仅用于自我认知任务,只对`swift/self-cognition`数据集生效,替换掉数据集中的`{{AUTHOR}}`通配符。传入模型作者的中文名和英文名,以空格分隔,例如:`--model_author '魔搭' 'ModelScope'`。默认为None。
|
||||
- custom_dataset_info: 自定义数据集注册的json文件路径,参考[自定义数据集](../Customization/Custom-dataset.md)和[内置'dataset_info.json'文件](https://github.com/modelscope/ms-swift/blob/main/swift/dataset/data/dataset_info.json)。默认为`[]`。
|
||||
|
||||
### 模板参数
|
||||
- 🔥template: 对话模板类型。默认为None,自动选择对应model的template类型,对应关系参考[支持的模型列表](./Supported-models-and-datasets.md)。
|
||||
- 🔥system: 自定义system字段,可以传入字符串或者**txt文件路径**。默认为None,使用注册template时的默认system。
|
||||
- 注意:数据集中的system**优先级**最高,然后是`--system`,最后是注册template时设置的`default_system`。
|
||||
- 🔥max_length: 限制单数据集样本经过`tokenizer.encode`后的tokens最大长度,超过的数据样本会根据`truncation_strategy`参数进行处理(避免训练OOM)。默认为None,即设置为模型支持的tokens最大长度(max_model_len)。
|
||||
- 当PPO、GRPO、GKD和推理情况下,`max_length`代表`max_prompt_length`。
|
||||
- truncation_strategy: 如果单样本的tokens超过`max_length`如何处理,支持'delete'、'left'、'right'和'split',代表删除、左侧裁剪、右侧裁剪和切成多条数据样本,默认为'delete'。
|
||||
- 注意:`--truncation_strategy split`只支持预训练时使用,即`swift/megatron pt`场景下,该策略会将超长字段切成多条数据样本,从而避免tokens浪费。该策略只支持纯文本数据集,多模态数据集无法正确处理pixel_values的切分。
|
||||
- 注意:若多模态模型的训练时将'truncation_strategy'设置为`left`或`right`,**ms-swift会保留所有的image_token等多模态tokens**,这可能会导致训练时OOM。
|
||||
- 🔥max_pixels: 多模态模型输入图片的最大像素数(H\*W),将超过该限制的图像进行缩放(避免训练OOM)。默认为None,不限制最大像素数。
|
||||
- 注意:该参数适用于所有的多模态模型。而Qwen2.5-VL特有的模型参数`MAX_PIXELS`(你可以在文档最下面找到)只针对Qwen2.5-VL模型。
|
||||
- 🔥agent_template: Agent模板,确定如何将工具列表'tools'转换成'system'、如何在推理/部署时从模型回复中提取toolcall部分,以及确定'messages'中`{"role": "tool_call", "content": "xxx"}`, `{"role": "tool_response", "content": "xxx"}`的模板格式。可选为"react_en", "hermes", "glm4", "qwen_en", "toolbench"等,更多请查看[这里](https://github.com/modelscope/ms-swift/blob/main/swift/agent_template/mapping.py)。默认为None,根据模型类型进行自动选择。可以参考[Agent文档](./Agent-support.md)。
|
||||
- norm_bbox: 控制如何缩放边界框(即数据集中的"bbox",里面的数据为绝对坐标,参考[自定义数据集文档](https://swift.readthedocs.io/zh-cn/latest/Customization/Custom-dataset.html#grounding))。选项为'norm1000'和'none'。'norm1000'表示将bbox坐标缩放至千分位坐标,而'none'表示不进行缩放。默认值为None,将根据模型自动选择。
|
||||
- 当**图片在训练中发生缩放时**(例如设置了max_pixels参数),该参数也能很好进行解决。
|
||||
- use_chat_template: 使用chat模板还是generation模板(generation模板通常用于预训练时)。默认为`True`。
|
||||
- 注意:`swift pt`默认为False,使用generation模板。该参数可以很好的**兼容多模态模型**。
|
||||
- padding_side: 当训练`batch_size>=2`时的padding_side,可选值为'left'、'right',默认为'right'。(推理时的batch_size>=2时,只进行左padding)。
|
||||
- 注意:PPO和GKD默认设置为'left'。
|
||||
- 🔥padding_free: 将一个batch中的数据进行展平而避免数据padding,从而降低显存占用并加快训练(**同一batch的不同序列之间依旧是不可见的**)。默认为False。当前支持CPT/SFT/DPO/GRPO/KTO/GKD。
|
||||
- 注意:使用padding_free请结合`--attn_impl flash_attn`使用且"transformers>=4.44",具体查看[该PR](https://github.com/huggingface/transformers/pull/31629)。(同packing)
|
||||
- **相较于packing,padding_free不需要额外的预处理时间,但packing的训练速度更快且显存占用更稳定**。
|
||||
- 🔥loss_scale: 训练tokens的loss权重设置。默认为`'default'`。loss_scale包含3种基本策略:'default'、'last_round'、'all',以及其他策略:'ignore_empty_think'以及agent需要的:'react'、'hermes'、'qwen'、'agentflan'、'alpha_umi'等,可选值参考[loss_scale模块](https://github.com/modelscope/ms-swift/blob/main/swift/loss_scale/mapping.py)。ms-swift 支持了基本策略和其他策略的混用,例如:`'default+ignore_empty_think'`,`'last_round+ignore_empty_think'`。若没有指定基本策略,则默认为'default',例如:'hermes'与'default+hermes'等价。
|
||||
- 'default': 所有response(含history)以权重1计算交叉熵损失(**messages中的system/user/多模态tokens以及Agent训练中`tool_response`部分不计算损失**)。(**SFT默认为该值**)
|
||||
- 'last_round': 只计算最后一轮response的损失。最后一轮含义为最后一个"user"之后的所有内容。(**RLHF默认为该值**)
|
||||
- 'all': 计算所有tokens的损失。(**`swift pt`默认为该值**)
|
||||
- 'ignore_empty_think': 忽略空的`'<think>\n\n</think>\n\n'`损失计算。(满足正则匹配`'<think>\\s*</think>\\s*'`即可)。
|
||||
- 'react', 'hermes', 'qwen': 将`tool_call`部分的loss权重调整为2。
|
||||
- 注意:在"ms-swift>=4.3.1",支持了多个非基本策略串联使用(依次处理上一个策略的输出片段,权重相乘),例如:`'last_round+hermes+ignore_empty_think'`,其中'last_round'为基础策略,'hermes+ignore_empty_think'为多个非基本策略的串联使用,共用基础策略。
|
||||
- disable_ignore_empty_think: 是否禁用对混合思考模型的loss_scale自动追加`ignore_empty_think`策略。默认为`False`,即对混合思考模型(如Qwen3.5-4B)自动在loss_scale后追加`+ignore_empty_think`,使空的`'<think>\n\n</think>\n\n'`不参与损失计算。若用户已手动在loss_scale中指定了`ignore_empty_think`,则不会重复追加。该参数仅在训练时生效,对纯思考模型和非思考模型无效。设置为`True`可关闭此默认行为。
|
||||
- 注意:该参数在"ms-swift>=4.3.1"增加。在"ms-swift<4.3.1"需手动添加`--loss_scale ignore_empty_think`。
|
||||
- is_binary_loss_scale: 当loss_scale只可能为0/1时,该语义可被labels替代,将loss_scale为0的部分的labels设置为-100,从而兼容liger_kernel降低显存。默认为None,进行自动设置。
|
||||
- sequence_parallel_size: 序列并行大小,默认是1。当前支持CPT/SFT/DPO/GRPO。训练脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/sequence_parallel)。
|
||||
- template_backend: 选择template后端,可选为'swift'、'jinja',默认为'swift'。如果使用jinja,则使用transformers的`apply_chat_template`。
|
||||
- 注意:jinja的template后端只支持推理,不支持训练(无法确定损失计算的tokens范围)。
|
||||
- response_prefix: response的前缀字符,该参数只在推理时生效。默认为None,根据enable_thinking参数和模版类型确定。
|
||||
- enable_thinking: 该参数在推理时生效,代表是否开启thinking模式。默认为None,默认值由模板(模型)类型确定(混合思考/非思考模板为False,思考模板为True)。若enable_thinking为False,则增加非思考前缀,例如Qwen3-8B混合思考模型增加前缀`'<think>\n\n</think>\n\n'`,Qwen3-8B-Thinking则不增加前缀。若enable_thinking为True,则增加思考前缀,例如`'<think>\n'`。注意:该参数的优先级低于`response_prefix`参数。
|
||||
- 注意:"ms-swift<4.3.1",混合思考模板默认值为True,在"ms-swift>=4.3.1"修改为False。
|
||||
- preserve_thinking: 是否在推理和训练时,对历史思考内容进行保留。当设置为True时,则保留所有轮次的思考内容。若设置为False,则只保留最后一轮的思考内容(即最后一个user信息后的内容)。默认为None。
|
||||
- 默认行为:对于思考模型(思考/混合思考)或显式开启enable_thinking,我们会在推理和训练时,默认设置为False,只保留最后一轮的思考内容。若训练时的`loss_scale`基本策略不为'last_round',例如为'default',则默认为True,不对历史的思考内容进行删除。
|
||||
- add_non_thinking_prefix: 该参数只在训练时生效,代表是否对数据样本assistant部分**不以思考标记`'<think>'`开头**的数据样本增加非思考前缀(通常混合思考模型含非思考前缀)。该特性可以让swift内置的数据集可以训练混合思考模型。默认值为True。例如:例如Qwen3-8B混合思考模型的非思考前缀为`'<think>\n\n</think>\n\n'`,Qwen3-8B-Thinking/Instruct的非思考前缀为`''`。注意:训练时,loss_scale的基本策略为last_round,则只对最后一轮做此修改;否则,例如为'default'、'all',则对每一轮数据做此修改。若设置为False,则不对数据样本增加非思考前缀。
|
||||
|
||||
### 生成参数
|
||||
参考[generation_config](https://huggingface.co/docs/transformers/main_classes/text_generation#transformers.GenerationConfig)文档。
|
||||
|
||||
- 🔥max_new_tokens: 推理最大生成新tokens的数量。默认为None,无限制。
|
||||
- temperature: 温度参数,温度越高,输出的随机性越大。默认为None,读取'generation_config.json'。
|
||||
- 你可以设置`--temperature 0`或者`--top_k 1`以取消推理随机性。
|
||||
- top_k: top_k参数,保留概率最高的top_k数量 tokens用于生成,默认为None。读取'generation_config.json'。
|
||||
- top_p: top_p参数,保留概率最高的累计概率达到 top_p 的tokens用于生成,默认为None。读取generation_config.json。
|
||||
- repetition_penalty: 重复惩罚参数。1.0 表示不进行惩罚。默认为None,读取generation_config.json。
|
||||
- num_beams: beam search的并行保留数量,默认为1。
|
||||
- 🔥stream: 流式输出,默认为`None`,即使用交互式界面时为True,数据集批量推理时为False。
|
||||
- stop_words: 除了eos_token外额外的停止词,默认为`[]`。
|
||||
- 注意:eos_token会在输出respsone中被删除,额外停止词会在输出中保留。
|
||||
- logprobs: 是否输出logprobs,默认为False。
|
||||
- top_logprobs: 输出top_logprobs的数量,默认为None。
|
||||
- structured_outputs_regex: 结构化输出(引导解码)的正则表达式模式。设置后,模型生成将被约束为匹配指定的正则表达式模式。仅在`infer_backend`为`vllm`时生效。默认为`None`。
|
||||
|
||||
### 量化参数
|
||||
以下为加载模型时量化的参数,具体含义可以查看[量化](https://huggingface.co/docs/transformers/main/en/main_classes/quantization)文档。这里不包含`swift export`中涉及的`gptq`、`awq`量化参数。
|
||||
|
||||
- 🔥quant_method: 加载模型时采用的量化方法,可选项为'bnb'、'hqq'、'eetq'、'quanto'和'fp8',默认为None。
|
||||
- 若对awq/gptq量化模型进行qlora训练,则不需要设置额外`quant_method`等量化参数。
|
||||
- 🔥quant_bits: 量化bits数,默认为None。
|
||||
- hqq_axis: hqq量化axis,默认为None。
|
||||
- bnb_4bit_compute_dtype: bnb量化计算类型,可选为`float16`、`bfloat16`、`float32`。默认为None,设置为`torch_dtype`。
|
||||
- bnb_4bit_quant_type: bnb量化类型,支持`fp4`和`nf4`,默认为`nf4`。
|
||||
- bnb_4bit_use_double_quant: 是否使用双重量化,默认为`True`。
|
||||
- bnb_4bit_quant_storage: bnb量化存储类型,默认为None。
|
||||
|
||||
### RAY参数
|
||||
|
||||
- use_ray: boolean类型。是否使用ray,默认为`False`
|
||||
- ray_exp_name: ray实验名字,这个字段会用作cluster和worker名称前缀,可以不填
|
||||
- device_groups: 字符串(jsonstring)类型。在使用ray时,该字段必须配置,具体可以查看[ray文档](Ray.md)。
|
||||
|
||||
### yaml/json支持
|
||||
|
||||
这里以`swift sft`为例子,yaml/json的方式启动也支持`swift infer/rlhf/...`以及`megatron sft/rlhf`。请参考[这里的例子](https://github.com/modelscope/ms-swift/tree/main/examples/yaml)。
|
||||
- yaml/json文件会在训练/推理后,存储在`output_dir`中。
|
||||
|
||||
```shell
|
||||
swift sft xxx.yaml
|
||||
swift sft xxx.json
|
||||
```
|
||||
|
||||
xxx.yaml/xxx.json的内容为具体命令行配置:
|
||||
|
||||
```yaml
|
||||
model: "Qwen/Qwen2.5-7B-Instruct"
|
||||
dataset: "swift/self-cognition#500"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "Qwen/Qwen2.5-7B-Instruct",
|
||||
"dataset": "swift/self-cognition#500"
|
||||
}
|
||||
```
|
||||
|
||||
你也可以混合使用yaml和命令行方式。例如yaml为不经常修改的参数,命令行传入常修改参数。
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer examples/yaml/deepspeed/infer.yaml \
|
||||
--adapters output/vx-xxx/checkpoint-xxx
|
||||
```
|
||||
|
||||
如何在yaml/json中指定环境环境变量:
|
||||
```yaml
|
||||
ENV:
|
||||
MAX_PIXELS: '1003520'
|
||||
VIDEO_MAX_PIXELS: '50176'
|
||||
FPS_MAX_FRAMES: '12'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"ENV": {
|
||||
"MAX_PIXELS": "1003520",
|
||||
"VIDEO_MAX_PIXELS": "50176",
|
||||
"FPS_MAX_FRAMES": "12"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 原子参数
|
||||
|
||||
### Seq2SeqTrainer参数
|
||||
|
||||
该参数列表继承自transformers `Seq2SeqTrainingArguments`,ms-swift对其默认值进行了覆盖。未列出的请参考[HF官方文档](https://huggingface.co/docs/transformers/main/en/main_classes/trainer#transformers.Seq2SeqTrainingArguments)。
|
||||
|
||||
- 🔥output_dir: 模型预测结果和检查点将被写入的输出目录。默认为None,设置为`'output/<model_name>'`。
|
||||
- 🔥gradient_checkpointing: 是否使用gradient_checkpointing,默认为True。该参数可以显著降低显存占用,但降低训练速度。
|
||||
- 🔥vit_gradient_checkpointing: 多模态模型训练时,是否对vit部分开启gradient_checkpointing。默认为None,即当`--freeze_vit`为`false`时开启。例子参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/multimodal/vit_gradient_checkpointing.sh)。
|
||||
- 🔥deepspeed: 默认为None。可以设置为'zero0', 'zero1', 'zero2', 'zero3', 'zero2_offload', 'zero3_offload'来使用ms-swift内置的deepspeed配置文件。你也可以传入自定义deepspeed配置文件的路径。
|
||||
- zero_hpz_partition_size: 默认为None,这个参数是ZeRO++的特性,即node内模型分片,node间数据分片,如果遇到grad_norm NaN,请尝试使用`--torch_dtype float16`。
|
||||
- deepspeed_autotp_size: DeepSpeed张量并行大小,默认为1。使用DeepSpeed AutoTP时需将参数`--deepspeed`设置为'zero0'、'zero1'或'zero2'。(注意:该功能只支持全参数)
|
||||
- 🔥fsdp: FSDP2分布式训练配置。默认为None。可以设置为'fsdp2'来使用ms-swift内置的FSDP2配置文件。你也可以传入自定义FSDP配置文件的路径。FSDP2是PyTorch原生的分布式训练方案,与DeepSpeed二选一使用。
|
||||
- 🔥per_device_train_batch_size: 默认值1。
|
||||
- 🔥per_device_eval_batch_size: 默认值1。
|
||||
- 🔥gradient_accumulation_steps: 梯度累加。**默认为None,即设置gradient_accumulation_steps使得total_batch_size>=16**。total_batch_size等于`per_device_train_batch_size * gradient_accumulation_steps * world_size`。在GRPO训练中,默认为1。
|
||||
- 在CPT/SFT训练中,梯度累加的训练效果等价使用更大的batch_size,但在RLHF训练中,训练效果并不等价。
|
||||
- weight_decay: weight衰减系数,默认值0.1。
|
||||
- adam_beta1: Adam系列优化器中一阶矩估计(动量)的指数衰减率。默认为0.9。
|
||||
- adam_beta2: Adam系列优化器中二阶矩估计(方差)的指数衰减率。默认为0.95。
|
||||
- adam_epsilon: Adam系列优化器中用于数值稳定性的epsilon值。默认为1e-8。
|
||||
- 🔥learning_rate: 学习率,**全参数训练默认为1e-5,LoRA训练等tuners为1e-4**。
|
||||
- 提示:若要设置`min_lr`,您可以传入参数`--lr_scheduler_type cosine_with_min_lr --lr_scheduler_kwargs '{"min_lr": 1e-6}'`。
|
||||
- 🔥vit_lr: 当训练多模态大模型时,该参数指定vit的学习率,默认为None,等于learning_rate。通常与`--freeze_vit`、`--freeze_aligner`参数结合使用。
|
||||
- 提示:在日志中打印的"learning_rate"为`param_groups[0]`的学习率,其中param_groups的顺序依次是vit, aligner, llm(若含可训练参数)。
|
||||
- 🔥aligner_lr: 当训练多模态大模型时,该参数指定aligner的学习率,默认为None,等于learning_rate。
|
||||
- lr_scheduler_type: lr_scheduler类型,默认为'cosine'。常见选择:'linear', 'constant', 'cosine_with_min_lr'。
|
||||
- lr_scheduler_kwargs: lr_scheduler其他参数。默认为None。
|
||||
- gradient_checkpointing_kwargs: 传入`torch.utils.checkpoint`中的参数。例如设置为`--gradient_checkpointing_kwargs '{"use_reentrant": false}'`。默认为None。
|
||||
- 注意:当使用DDP而不使用deepspeed/fsdp,且gradient_checkpointing_kwargs为None,会默认设置其为`'{"use_reentrant": false}'`而避免出现报错。
|
||||
- full_determinism: 确保训练中获得可重现的结果,注意:这会对性能产生负面影响。默认为False。
|
||||
- 🔥report_to: 默认值为`tensorboard`。你也可以指定`--report_to tensorboard wandb swanlab`、`--report_to all`。
|
||||
- 如果你指定了`--report_to wandb`,你可以通过`WANDB_PROJECT`设置项目名称,`WANDB_API_KEY`指定账户对应的API KEY。
|
||||
- logging_first_step: 是否记录第一个step的日志,默认为True。
|
||||
- logging_steps: 日志打印间隔,默认为5。
|
||||
- router_aux_loss_coef: 用于moe模型训练时,设置 aux_loss 的权重,默认为`0.`。
|
||||
- enable_dft_loss: 是否在SFT训练中使用[DFT](https://arxiv.org/abs/2508.05629) (Dynamic Fine-Tuning) loss,默认为False。
|
||||
- enable_channel_loss: 启用channel loss,默认为`False`。你需要在数据集中准备"channel"字段,ms-swift会根据该字段分组统计loss(若未准备"channel"字段,则归为默认`None` channel)。数据集格式参考[channel loss](../Customization/Custom-dataset.md#channel-loss)。channel loss兼容packing/padding_free/loss_scale等技术。
|
||||
- safe_serialization: 是否存储为safetensors,默认为True。
|
||||
- max_shard_size: 单存储文件最大大小,默认'5GB'。
|
||||
- logging_dir: tensorboard日志保存路径。默认为None,即设置为`f'{self.output_dir}/runs'`。
|
||||
- predict_with_generate: 验证时使用生成式的方式,默认为False。
|
||||
- metric_for_best_model: 默认为None,即当`predict_with_generate`设置为False时,设置为'loss',否则设置为'rouge-l'(在PPO训练时,不进行默认值设置;GRPO训练设置为'reward')。
|
||||
- greater_is_better: 默认为None,即当`metric_for_best_model`含'loss'时,设置为False,否则设置为True。
|
||||
- max_epochs: 训练到`max_epochs`时强制退出训练,并对权重进行验证和保存。该参数在使用流式数据集时很有用。默认为None。
|
||||
|
||||
其他重要参数:
|
||||
- 🔥num_train_epochs: 训练的epoch数,默认为3。
|
||||
- 🔥save_strategy: 保存模型的策略,可选为'no'、'steps'、'epoch',默认为'steps'。
|
||||
- 🔥save_steps: 默认为500。
|
||||
- 🔥eval_strategy: 评估策略。默认为None,跟随`save_strategy`的策略。
|
||||
- 若不使用`val_dataset`和`eval_dataset`且`split_dataset_ratio`为0,则默认为'no'。
|
||||
- 🔥eval_steps: 默认为None,如果存在评估数据集,则跟随`save_steps`的策略。
|
||||
- eval_on_start: 是否在训练前执行一次评估步骤,以确保验证步骤能正常工作。默认为False。
|
||||
- 🔥save_total_limit: 最多保存的checkpoint数,会将过期的checkpoint进行删除。默认为None,保存所有的checkpoint。若设置为2,则保存best checkpoint和last checkpoint。
|
||||
- max_steps: 最大训练的steps数。在数据集为流式时需要被设置。默认为-1。
|
||||
- 🔥warmup_ratio: 默认为0.。
|
||||
- save_on_each_node: 在每一个节点都进行权重保存。默认为False。该参数在多机训练时需要被考虑。
|
||||
- 提示:在多机训练时,通常将`output_dir`设置为节点共享目录,因此无需额外设置该参数。
|
||||
- save_only_model: 是否只保存模型权重而不包含优化器状态,随机种子状态等内容,这在全参数训练时可以减少保存的时间消耗和空间占用。默认为False。
|
||||
- 🔥resume_from_checkpoint: 断点续训参数,指定checkpoint路径。默认为None。
|
||||
- 提示:**断点续训请保持其他参数不变,额外增加`--resume_from_checkpoint checkpoint_dir`**。权重等信息将在trainer中读取。
|
||||
- 注意: resume_from_checkpoint会读取模型权重,优化器状态,随机种子,并从上次训练的steps继续开始训练。你可以指定`--resume_only_model`只读取模型权重。
|
||||
- resume_only_model: 默认为False。如果在指定resume_from_checkpoint的基础上,将该参数设置为True,则仅resume模型权重,而忽略优化器状态和随机种子。
|
||||
- 注意:**resume_only_model默认将进行数据跳过**,此行为可通过 `ignore_data_skip` 参数控制。
|
||||
- ignore_data_skip: 当设置`resume_from_checkpoint`和`resume_only_model`时,该参数控制是否跳过已经训练的数据,并将epoch和迭代数等训练状态进行恢复。默认为False。若设置为True,则将不加载训练状态并不进行数据跳过,将从迭代数0开始训练。
|
||||
- 🔥ddp_find_unused_parameters: 默认为None。
|
||||
- 🔥dataloader_num_workers: 默认为None,若是windows平台,则设置为0,否则设置为1。
|
||||
- dataloader_pin_memory: 默认为True。
|
||||
- dataloader_persistent_workers: 默认为False。
|
||||
- dataloader_prefetch_factor: 默认为None。若 `dataloader_num_workers > 0`,则设置为2。每个工作进程预先加载的批次数量。2 表示所有工作进程总共会预取 2 * num_workers 个批次。
|
||||
- train_dataloader_shuffle: CPT/SFT训练的dataloader是否随机,默认为True。该参数对IterableDataset无效(即对流式数据集失效)。IterableDataset采用顺序的方式读取。
|
||||
- optim: 优化器,默认值为 `"adamw_torch"` (对于 torch>=2.8 为 `"adamw_torch_fused"`)。完整的优化器列表请参见 [training_args.py](https://github.com/huggingface/transformers/blob/main/src/transformers/training_args.py) 中的 `OptimizerNames`。
|
||||
- optim_args: 提供给优化器的可选参数,默认为None。
|
||||
- group_by_length: 是否在训练数据集中将长度大致相同的样本分组在一起(有随机因素),以最小化填充并确保各节点与进程的负载均衡以提高效率。默认为False。具体算法参考`transformers.trainer_pt_utils.get_length_grouped_indices`。
|
||||
- 🔥neftune_noise_alpha: neftune添加的噪声系数。默认为0,通常可以设置为5、10、15。
|
||||
- 🔥use_liger_kernel: 是否启用[Liger](https://github.com/linkedin/Liger-Kernel)内核加速训练并减少显存消耗。默认为False。示例shell参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/liger)。
|
||||
- 注意:liger_kernel不支持device_map,请使用DDP/DeepSpeed进行多卡训练。liger_kernel目前只支持`task_type='causal_lm'`。
|
||||
- average_tokens_across_devices: 是否在设备之间进行token数平均。如果设置为True,将使用all_reduce同步`num_tokens_in_batch`以进行精确的损失计算。默认为False。
|
||||
- max_grad_norm: 梯度裁剪。默认为1.。
|
||||
- 注意:日志中的grad_norm记录的是裁剪前的值。
|
||||
- push_to_hub: 推送checkpoint到hub。默认为False。
|
||||
- hub_model_id: 默认为None。
|
||||
- hub_private_repo: 默认为False。
|
||||
|
||||
### Tuner参数
|
||||
- 🔥freeze_llm: 该参数只对多模态模型生效,可用于全参数训练和LoRA训练,但会产生不同的效果。若是全参数训练,将freeze_llm设置为True会将LLM部分权重进行冻结;若是LoRA训练且`target_modules`设置为'all-linear',将freeze_llm设置为True将会取消在LLM部分添加LoRA模块。该参数默认为False。
|
||||
- 🔥freeze_vit: 该参数只对多模态模型生效,可用于全参数训练和LoRA训练,但会产生不同的效果。若是全参数训练,将freeze_vit设置为True会将vit部分权重进行冻结;若是LoRA训练且`target_modules`设置为'all-linear',将freeze_vit设置为True将会取消在vit部分添加LoRA模块。该参数默认为True。
|
||||
- 注意:**这里的vit不仅限于vision_tower, 也包括audio_tower**。若是Omni模型,若你只希望对vision_tower加LoRA,而不希望对audio_tower加LoRA,你可以修改[这里的代码](https://github.com/modelscope/ms-swift/blob/a5d4c0a2ce0658cef8332d6c0fa619a52afa26ff/swift/llm/model/model_arch.py#L544-L554)。
|
||||
- 🔥freeze_aligner: 该参数只对多模态模型生效,可用于全参数训练和LoRA训练,但会产生不同的效果。若是全参数训练,将freeze_aligner设置为True会将aligner(也称为projector)部分权重进行冻结;若是LoRA训练且`target_modules`设置为'all-linear',将freeze_aligner设置为True将会取消在aligner部分添加LoRA模块。该参数默认为True。
|
||||
- 🔥target_modules: 指定lora模块, 默认为`['all-linear']`。你也可以设置为module的后缀,例如:`--target_modules q_proj k_proj v_proj`。该参数不限于LoRA,可用于其他tuners。
|
||||
- 注意:在LLM和多模态LLM中,'all-linear'的行为有所不同。若是LLM则自动寻找除lm_head外的linear并附加tuner;**若是多模态LLM,则默认只在LLM上附加tuner,该行为可以被`freeze_llm`、`freeze_vit`、`freeze_aligner`控制**。
|
||||
- 🔥target_regex: 指定lora模块的regex表达式,默认为`None`。如果该值传入,则target_modules参数失效。例如你可以设置`--target_regex '^(language_model).*\.(q_proj|k_proj|v_proj|o_proj|gate_proj|up_proj|down_proj)$'`,将符合该正则的模块指定为LoRA模块。该参数不限于LoRA,可用于其他tuners。
|
||||
- target_parameters: 要替换为LoRA的参数名称列表。该参数的行为与 `target_modules` 类似,但传入的应是参数名称而不是模块名称。该特性需要安装"peft>=0.17.0"。例如,在 Hugging Face Transformers 中许多混合专家(MoE)层中,并未使用 `nn.Linear`,而是使用了 `nn.Parameter`。这时可以使用target_parameters参数实现。
|
||||
- 注意:该参数需要设置`lora_dropout`为 0。
|
||||
- init_weights: 初始化weights的方法,LoRA可以指定为'true'、'false'、'gaussian'、'pissa'、'pissa_niter_[number of iters]'、'olora'、'loftq'、'lora-ga',Bone可以指定为'true'、'false'、'bat'。默认值'true'。
|
||||
- 🔥modules_to_save: 在已附加tuner后,额外指定一部分原模型模块参与训练和存储。默认为`[]`。该参数不限于LoRA,可用于其他tuners。例如设置为`--modules_to_save embed_tokens lm_head`,在LoRA训练中解开embed_tokens和lm_head层进行训练,这两部分的权重信息最终会保存在`adapter_model.safetensors`中。
|
||||
|
||||
#### 全参
|
||||
- freeze_parameters: 需要被冻结参数的前缀,默认为`[]`。
|
||||
- freeze_parameters_regex: 需要被冻结参数的正则表达式,默认为None。
|
||||
- freeze_parameters_ratio: 从下往上冻结的参数比例,默认为0。可设置为1将所有参数冻结,结合`trainable_parameters`设置可训练参数。
|
||||
- trainable_parameters: 额外可训练参数的前缀,默认为`[]`。
|
||||
- trainable_parameters_regex: 匹配额外可训练参数的正则表达式,默认为None。
|
||||
- 备注:`trainable_parameters`、`trainable_parameters_regex`的优先级高于`freeze_parameters`、`freeze_parameters_regex`和`freeze_parameters_ratio`。例如:当指定全参数训练时,会将所有模块设置为可训练的状态,随后根据`freeze_parameters`、`freeze_parameters_regex`、`freeze_parameters_ratio`将部分参数冻结,最后根据`trainable_parameters`、`trainable_parameters_regex`重新打开部分参数参与训练。
|
||||
|
||||
#### LoRA
|
||||
- 🔥lora_rank: 默认为`8`。
|
||||
- 🔥lora_alpha: 默认为`32`。
|
||||
- lora_dropout: 默认为`0.05`。
|
||||
- lora_bias: 默认为`'none'`,可以选择的值: 'none'、'all'。如果你要将bias全都设置为可训练,你可以设置为`'all'`。
|
||||
- lora_dtype: 指定lora模块的dtype类型。支持'float16'、'bfloat16'、'float32'。默认为None,跟随peft行为。
|
||||
- 🔥use_dora: 默认为`False`,是否使用`DoRA`。
|
||||
- use_rslora: 默认为`False`,是否使用`RS-LoRA`。
|
||||
- 🔥lorap_lr_ratio: LoRA+参数,默认值`None`,建议值为`10~16`。使用lora时额外指定该参数可使用lora+。
|
||||
|
||||
##### LoRA-GA
|
||||
- lora_ga_batch_size: 默认值为 `2`。在 LoRA-GA 中估计梯度以进行初始化时使用的批处理大小。
|
||||
- lora_ga_iters: 默认值为 `2`。在 LoRA-GA 中估计梯度以进行初始化时的迭代次数。
|
||||
- lora_ga_max_length: 默认值为 `1024`。在 LoRA-GA 中估计梯度以进行初始化时的最大输入长度。
|
||||
- lora_ga_direction: 默认值为 `ArB2r`。在 LoRA-GA 中使用估计梯度进行初始化时的初始方向。允许的值有:`ArBr`、`A2rBr`、`ArB2r` 和 `random`。
|
||||
- lora_ga_scale: 默认值为 `stable`。LoRA-GA 的初始化缩放方式。允许的值有:`gd`、`unit`、`stable` 和 `weightS`。
|
||||
- lora_ga_stable_gamma: 默认值为 `16`。当初始化时选择 `stable` 缩放时的 gamma 值。
|
||||
|
||||
#### FourierFt
|
||||
|
||||
FourierFt使用`target_modules`、`target_regex`、`modules_to_save`三个参数,含义见上面文档中的描述。额外参数包括:
|
||||
|
||||
- fourier_n_frequency: 傅里叶变换的频率数量, `int`类型, 类似于LoRA中的`r`. 默认值`2000`.
|
||||
- fourier_scaling: W矩阵的缩放值, `float`类型, 类似LoRA中的`lora_alpha`. 默认值`300.0`.
|
||||
|
||||
#### BOFT
|
||||
|
||||
BOFT使用`target_modules`、`target_regex`、`modules_to_save`三个参数,含义见上面文档中的描述。额外参数包括:
|
||||
|
||||
- boft_block_size: BOFT块尺寸, 默认值4.
|
||||
- boft_block_num: BOFT块数量, 不能和`boft_block_size`同时使用.
|
||||
- boft_dropout: boft的dropout值, 默认0.0.
|
||||
|
||||
#### Vera
|
||||
|
||||
Vera使用`target_modules`、`target_regex`、`modules_to_save`三个参数,含义见上面文档中的描述。额外参数包括:
|
||||
|
||||
- vera_rank: Vera Attention的尺寸, 默认值256.
|
||||
- vera_projection_prng_key: 是否存储Vera映射矩阵, 默认为True.
|
||||
- vera_dropout: Vera的dropout值, 默认`0.0`.
|
||||
- vera_d_initial: Vera的d矩阵的初始值, 默认`0.1`.
|
||||
|
||||
#### GaLore
|
||||
|
||||
- 🔥use_galore: 默认值False, 是否使用GaLore.
|
||||
- galore_target_modules: 默认值None, 不传的情况下对attention和mlp应用GaLore.
|
||||
- galore_rank: 默认值128, GaLore的rank值.
|
||||
- galore_update_proj_gap: 默认值50, 分解矩阵的更新间隔.
|
||||
- galore_scale: 默认值1.0, 矩阵权重系数.
|
||||
- galore_proj_type: 默认值`std`, GaLore矩阵分解类型.
|
||||
- galore_optim_per_parameter: 默认值False, 是否给每个Galore目标Parameter设定一个单独的optimizer.
|
||||
- galore_with_embedding: 默认值False, 是否对embedding应用GaLore.
|
||||
- galore_quantization: 是否使用q-galore. 默认值`False`.
|
||||
- galore_proj_quant: 是否对SVD分解矩阵做量化, 默认`False`.
|
||||
- galore_proj_bits: SVD量化bit数.
|
||||
- galore_proj_group_size: SVD量化分组数.
|
||||
- galore_cos_threshold: 投影矩阵更新的cos相似度阈值. 默认值0.4.
|
||||
- galore_gamma_proj: 在投影矩阵逐渐相似后会拉长更新间隔, 本参数为每次拉长间隔的系数, 默认值2.
|
||||
- galore_queue_size: 计算投影矩阵相似度的队列长度, 默认值5.
|
||||
|
||||
#### LISA
|
||||
|
||||
注意: LISA仅支持全参数,即`--tuner_type full`。
|
||||
|
||||
- 🔥lisa_activated_layers: 默认值`0`,代表不使用LISA,改为非0代表需要激活的layers个数,建议设置为2或8。
|
||||
- lisa_step_interval: 默认值`20`,多少iter切换可反向传播的layers。
|
||||
|
||||
#### UNSLOTH
|
||||
|
||||
🔥unsloth无新增参数,对已有参数进行调节即可支持,例如:
|
||||
|
||||
```
|
||||
--tuner_backend unsloth
|
||||
--tuner_type full/lora
|
||||
--quant_bits 4
|
||||
```
|
||||
|
||||
#### LLAMAPRO
|
||||
|
||||
- 🔥llamapro_num_new_blocks: 默认值`4`, 插入的新layers总数.
|
||||
- llamapro_num_groups: 默认值`None`, 分为多少组插入new_blocks, 如果为`None`则等于`llamapro_num_new_blocks`, 即每个新的layer单独插入原模型.
|
||||
|
||||
#### AdaLoRA
|
||||
|
||||
以下参数`tuner_type`设置为`adalora`时生效. adalora的`target_modules`等参数继承于lora的对应参数,但`lora_dtype`参数不生效。
|
||||
|
||||
- adalora_target_r: 默认值`8`, adalora的平均rank.
|
||||
- adalora_init_r: 默认值`12`, adalora的初始rank.
|
||||
- adalora_tinit: 默认值`0`, adalora的初始warmup.
|
||||
- adalora_tfinal: 默认值`0`, adalora的final warmup.
|
||||
- adalora_deltaT: 默认值`1`, adalora的step间隔.
|
||||
- adalora_beta1: 默认值`0.85`, adalora的EMA参数.
|
||||
- adalora_beta2: 默认值`0.85`, adalora的EMA参数.
|
||||
- adalora_orth_reg_weight: 默认值`0.5`, adalora的正则化参数.
|
||||
|
||||
#### ReFT
|
||||
|
||||
以下参数`tuner_type`设置为`reft`时生效.
|
||||
|
||||
> 1. ReFT无法合并tuner
|
||||
> 2. ReFT和gradient_checkpointing不兼容
|
||||
> 3. 如果使用DeepSpeed遇到问题请暂时卸载DeepSpeed
|
||||
|
||||
- 🔥reft_layers: ReFT应用于哪些层上, 默认为`None`, 代表所有层, 可以输入层号的list, 例如reft_layers 1 2 3 4`
|
||||
- 🔥reft_rank: ReFT矩阵的rank, 默认为`4`.
|
||||
- reft_intervention_type: ReFT的类型, 支持'NoreftIntervention', 'LoreftIntervention', 'ConsreftIntervention', 'LobireftIntervention', 'DireftIntervention', 'NodireftIntervention', 默认为`LoreftIntervention`.
|
||||
- reft_args: ReFT Intervention中的其他支持参数, 以json-string格式输入.
|
||||
|
||||
### vLLM参数
|
||||
参数含义可以查看[vllm文档](https://docs.vllm.ai/en/latest/serving/engine_args.html)。
|
||||
|
||||
- 🔥vllm_gpu_memory_utilization: GPU内存比例,取值范围为0到1。默认值`0.9`。
|
||||
- 🔥vllm_tensor_parallel_size: tp并行数,默认为`1`。
|
||||
- vllm_pipeline_parallel_size: pp并行数,默认为`1`。
|
||||
- vllm_data_parallel_size: dp并行数,默认为`1`,在`swift deploy/rollout`命令中生效。
|
||||
- 若在`swift infer`中,使用`NPROC_PER_NODE`来设置dp并行数。参考这里的[例子](https://github.com/modelscope/ms-swift/blob/main/examples/infer/vllm/mllm_ddp.sh)。
|
||||
- vllm_enable_expert_parallel: 开启专家并行,默认为False。
|
||||
- vllm_max_num_seqs: 单次迭代中处理的最大序列数,默认为`256`。
|
||||
- 🔥vllm_max_model_len: 模型支持的最大长度。默认为`None`,即从config.json中读取。
|
||||
- vllm_disable_custom_all_reduce: 禁用自定义的 all-reduce 内核,回退到 NCCL。为了稳定性,默认为`True`。
|
||||
- vllm_enforce_eager: vllm使用pytorch eager模式还是建立cuda graph,默认为`False`。设置为True可以节约显存,但会影响效率。
|
||||
- vllm_mm_processor_cache_gb: 多模态处理器缓存大小(GiB),用于缓存已处理的多模态输入(如图像、视频)避免重复处理。默认为`4`。设置为`0`可禁用缓存但会降低性能(不推荐)。仅对多模态模型生效。
|
||||
- vllm_speculative_config: 推测解码配置,传入json字符串。默认为None。
|
||||
- vllm_disable_cascade_attn: 是否强制关闭V1引擎的cascade attention实现以防止潜在数值误差,默认为False,由vLLM内部逻辑决定是否使用。
|
||||
- 🔥vllm_limit_mm_per_prompt: 控制vllm使用多图,默认为`None`。例如传入`--vllm_limit_mm_per_prompt '{"image": 5, "video": 2}'`。
|
||||
- vllm_max_lora_rank: 默认为`16`。vllm对于lora支持的参数。
|
||||
- vllm_quantization: vllm可以在内部量化模型,参数支持的值详见[这里](https://docs.vllm.ai/en/latest/serving/engine_args.html)。
|
||||
- 🔥vllm_enable_prefix_caching: 开启vllm的自动前缀缓存,节约重复查询前缀的处理时间,加快推理效率。默认为`None`,跟随vLLM行为。
|
||||
- vllm_use_async_engine: vLLM backend下是否使用async engine。默认为None,会根据场景自动设置:encode任务(embedding、seq_cls、reranker、generative_reranker)默认为True,部署场景(swift deploy)默认为True,其他情况默认为False。注意:encode任务需使用async engine。
|
||||
- vllm_reasoning_parser: 推理解析器类型,用于思考模型的思维链内容解析。默认为`None`。仅用于 `swift deploy` 命令。可选的种类参考[vLLM文档](https://docs.vllm.ai/en/latest/features/reasoning_outputs.html#streaming-chat-completions)。
|
||||
- vllm_engine_kwargs: vllm的额外参数,格式为json字符串。默认为None。
|
||||
|
||||
### SGLang参数
|
||||
参数含义可以查看[sglang文档](https://docs.sglang.ai/backend/server_arguments.html)。
|
||||
|
||||
- 🔥sglang_tp_size: tp数。默认为1。
|
||||
- sglang_pp_size: pp数。默认为1。
|
||||
- sglang_dp_size: dp数。默认为1。
|
||||
- sglang_ep_size: ep数。默认为1。
|
||||
- sglang_enable_ep_moe: 是否启用ep moe。默认为False。该参数已在最新sglang中移除。
|
||||
- sglang_mem_fraction_static: 用于静态分配模型权重和KV缓存内存池的GPU内存比例。如果你遇到GPU内存不足错误,可以尝试降低该值。默认为None。
|
||||
- sglang_context_length: 模型的最大上下文长度。默认为 None,将使用模型的`config.json`中的值。
|
||||
- sglang_disable_cuda_graph: 禁用CUDA图。默认为False。
|
||||
- sglang_quantization: 量化方法。默认为None。
|
||||
- sglang_kv_cache_dtype: 用于k/v缓存存储的数据类型。'auto'表示将使用模型的数据类型。'fp8_e5m2'和'fp8_e4m3'适用于CUDA 11.8及以上版本。默认为'auto'。
|
||||
- sglang_enable_dp_attention: 为注意力机制启用数据并行,为前馈网络(FFN)启用张量并行。数据并行的规模(dp size)应等于张量并行的规模(tp size)。目前支持DeepSeek-V2/3以及Qwen2/3 MoE模型。默认为False。
|
||||
- sglang_disable_custom_all_reduce: 禁用自定义的 all-reduce 内核,回退到 NCCL。为了稳定性,默认为True。
|
||||
- sglang_speculative_algorithm: 推测算法,可选值:None、"EAGLE"、"EAGLE3"、"NEXTN"、"STANDALONE"、"NGRAM"。默认为None。
|
||||
- sglang_speculative_num_steps: 在推测解码中从草稿模型采样的步数。默认值为None。
|
||||
- sglang_speculative_eagle_topk: 在 EAGLE2 算法中每步从草稿模型采样的 token 数量。默认值为None。
|
||||
- sglang_speculative_num_draft_tokens: 在推测解码中从草稿模型采样的 token 数量。默认值为None。
|
||||
|
||||
### LMDeploy参数
|
||||
参数含义可以查看[lmdeploy文档](https://lmdeploy.readthedocs.io/en/latest/api/pipeline.html#turbomindengineconfig)。
|
||||
|
||||
- 🔥lmdeploy_tp: tensor并行度。默认为`1`。
|
||||
- lmdeploy_session_len: 最大会话长度。默认为`None`。
|
||||
- lmdeploy_cache_max_entry_count: k/v缓存占用的GPU内存百分比。默认为`0.8`。
|
||||
- lmdeploy_quant_policy: 默认为0。当需要将k/v量化为4或8位时,分别将其设置为4或8。
|
||||
- lmdeploy_vision_batch_size: 传入VisionConfig的max_batch_size参数。默认为`1`。
|
||||
|
||||
### 合并参数
|
||||
|
||||
- 🔥merge_lora: 是否合并lora,本参数支持lora、llamapro、longlora,默认为False。例子参数[这里](https://github.com/modelscope/ms-swift/blob/main/examples/export/merge_lora.sh)。
|
||||
- safe_serialization: 是否存储为safetensors,默认为True。
|
||||
- max_shard_size: 单存储文件最大大小,默认'5GB'。
|
||||
|
||||
|
||||
## 集成参数
|
||||
|
||||
### 训练参数
|
||||
训练参数除包含[基本参数](#基本参数)、[Seq2SeqTrainer参数](#Seq2SeqTrainer参数)、[tuner参数](#tuner参数)外,还包含下面的部分:
|
||||
|
||||
- add_version: 在`output_dir`上额外增加目录`'<版本号>-<时间戳>'`防止权重覆盖,默认为True。
|
||||
- check_model: 检查本地模型文件有损坏或修改并给出提示,默认为True。**如果是断网环境,请设置为False**。
|
||||
- 🔥create_checkpoint_symlink: 额外创建checkpoint软链接,方便书写自动化训练脚本。best_model和last_model的软链接路径分别为f'{output_dir}/best'和f'{output_dir}/last'。
|
||||
- 🔥packing: 使用`padding_free`的方式将不同长度的数据样本打包成**近似**统一长度的样本(packing能保证不对完整的序列进行切分),实现训练时各节点与进程的负载均衡(避免长文本拖慢短文本的训练速度),从而提高GPU利用率,保持显存占用稳定。当使用 `--attn_impl flash_attn` 时,可确保packed样本内的不同序列之间相互独立,互不可见。该参数默认为`False`,目前支持 CPT/SFT/DPO/KTO/GKD以及embedding/reranker/seq_cls任务的packing。注意:**packing会导致数据集样本数减少,请自行调节梯度累加数和学习率**。
|
||||
- 注意:Qwen3-Next的packing请使用Megatron-SWIFT。Qwen3.5的transformers生态padding_free/packing支持请使用"ms-swift>=4.3.1"(或使用Megatron-SWIFT),具体参考[Qwen3.5最佳实践](../BestPractices/Qwen3_5-Best-Practice.md)。
|
||||
- packing_length: packing的长度。默认为None,设置为max_length。
|
||||
- packing_num_proc: packing的进程数,默认为1。需要注意的是,不同的`packing_num_proc`,最终形成的packed数据集是不同的。(该参数在流式packing时不生效)。通常不需要修改该值,packing速度远快于tokenize速度。
|
||||
- packing_strategy: packing 算法,可选为'binpack'和'sequential',默认为'binpack'。'binpack'使用 best-fit-decreasing 装箱(会按长度重排样本);'sequential'使用保序的贪心装箱(next-fit:仅维护一个开放 pack,放不下即 flush),按输入顺序逐条装箱,使样本顺序与每个 pack 的边界跟随顺序采样器(建议配合 packing_num_proc=1 以保证全局顺序)。
|
||||
- lazy_tokenize: 是否使用lazy_tokenize。若该参数设置为False,则在训练之前对所有的数据集样本进行tokenize(多模态模型则包括从磁盘中读取图片)。该参数默认为None,在LLM训练中默认为False,而MLLM训练默认为True,节约内存。
|
||||
- 注意:若你要进行图像的数据增强,你需要将lazy_tokenize(或streaming)设置为True,并修改Template类中的encode方法。
|
||||
- use_logits_to_keep: 通过在`forward`中根据labels传入logits_to_keep,减少无效logits的计算与存储,从而减少显存占用并加快训练速度。默认为None,进行自动选择。
|
||||
- acc_strategy: 训练和验证时计算acc的策略。可选为`seq`和`token`级别的acc,默认为`token`。
|
||||
- max_new_tokens: 覆盖生成参数。predict_with_generate=True时的最大生成token数量,默认64。
|
||||
- temperature: 覆盖生成参数。predict_with_generate=True时的temperature,默认0。
|
||||
- optimizer: 使用的optimizers插件(优先级高于`--optim`),默认为None。可选optimizers参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/optimizers/mapping.py)。
|
||||
- loss_type: 自定义的loss_type名称。默认为None,使用模型自带损失函数。可选loss参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/loss/mapping.py)。
|
||||
- mrl_dims: Embedding训练的[Matryoshka表征学习(MRL)](https://arxiv.org/abs/2205.13147)维度配置,默认为None。格式为`Dict[int, float]`或Json字符串,key为截断的embedding维度,value为该维度对应的loss权重,例如`'{"32": 1.0, "64": 1.0, "128": 1.0}'`。开启后,trainer会对`last_hidden_state`分别截断到每个维度并做L2归一化,再调用`loss_type`对应的loss加权累加。仅在`task_type='embedding'`下生效。
|
||||
- 注意:可支持的最大embedding维度由模型`config.json`中的`hidden_size`决定,key大于`hidden_size`的KV对将被自动忽略。
|
||||
- eval_metric: 自定义eval metric名称。默认为None。可选eval_metric参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/metrics/mapping.py)。
|
||||
- 关于默认值:当`task_type`为'causal_lm', 且`predict_with_generate=True`的情况下默认设置为'nlg'。`task_type` 为'embedding',根据loss_type,默认值为'infonce' 或 'paired'。`task_type`为'reranker/generative_reranker',默认值为'reranker'。
|
||||
- callbacks: 自定义trainer callback,默认为`[]`。可选callbacks参考[这里](https://github.com/modelscope/ms-swift/blob/main/swift/callbacks/mapping.py)。例如:通过在`callbacks`中添加`deepspeed_elastic`(可选`graceful_exit`)可以来启用弹性训练。参考[Elastic示例](../BestPractices/Elastic.md)
|
||||
- early_stop_interval: 早停的间隔,会检验best_metric在early_stop_interval个周期内(基于`save_steps`, 建议`eval_steps`和`save_steps`设为同值)没有提升时终止训练。具体代码在[early_stop.py](https://github.com/modelscope/ms-swift/blob/main/swift/callbacks/early_stop.py)中。同时,如果有较为复杂的早停需求,直接覆盖callback.py中的已有实现即可。设置该参数时,自动加入`early_stop`的trainer callback。
|
||||
- eval_use_evalscope: 是否使用evalscope进行训练时评测,需要设置该参数来开启评测,具体使用参考[示例](../Instruction/Evaluation.md#训练中评测)。
|
||||
- eval_dataset: 评测数据集,可设置多个数据集,用空格分割。
|
||||
- eval_dataset_args: 评测数据集参数,json格式,可设置多个数据集的参数。
|
||||
- eval_limit: 评测数据集采样数。
|
||||
- eval_generation_config: 评测时模型推理配置,json格式,默认为`{'max_tokens': 512}`。
|
||||
- use_flash_ckpt: 是否启用[DLRover Flash Checkpoint](https://github.com/intelligent-machine-learning/dlrover)的flash checkpoint。默认为`false`,启用后,权重会先保存至共享内存,之后异步持久化;建议搭配`PYTORCH_CUDA_ALLOC_CONF="expandable_segments:True"` 一起使用,避免训练过程CUDA OOM。
|
||||
|
||||
#### SWANLAB
|
||||
|
||||
- swanlab_token: SwanLab的api-key。你也可以使用`SWANLAB_API_KEY`环境变量指定。
|
||||
- swanlab_project: swanlab的project,可以在页面中预先创建[https://swanlab.cn/space/~](https://swanlab.cn/space/~)或自动创建,默认为"ms-swift"。
|
||||
- swanlab_workspace: 默认为None,会使用api-key对应的username。
|
||||
- swanlab_exp_name: 实验名,可以为空,为空时默认传入--output_dir的值。
|
||||
- swanlab_notification_method: 在训练完成/发生错误时,swanlab的通知方式,具体参考[这里](https://docs.swanlab.cn/plugin/notification-dingtalk.html)。支持'dingtalk'、'lark'、'email'、'discord'、'wxwork'、'slack'。
|
||||
- swanlab_webhook_url: 默认为None。swanlab的`swanlab_notification_method`对应的 webhook url。
|
||||
- swanlab_secret: 默认为None。swanlab的`swanlab_notification_method`对应的 secret。
|
||||
- swanlab_mode: 可选cloud和local,云模式或者本地模式。
|
||||
|
||||
|
||||
### RLHF参数
|
||||
RLHF参数继承于[训练参数](#训练参数)。
|
||||
|
||||
- 🔥rlhf_type: 人类对齐算法类型,支持'dpo'、'orpo'、'simpo'、'kto'、'cpo'、'rm'、'ppo'、'grpo'和'gkd'。默认为'dpo'。
|
||||
- ref_model: 采用dpo、kto、ppo、grpo算法且使用全参数训练时需要传入。默认为None,设置为`--model`。
|
||||
- ref_adapters: 默认为`[]`。若你要使用SFT产生的LoRA权重进行DPO/KTO/GRPO,请在训练时设置`--adapters sft_ckpt --ref_adapters sft_ckpt`。若是此场景的断点续训,则设置`--resume_from_checkpoint rlhf_ckpt --ref_adapters sft_ckpt`。
|
||||
- ref_model_type: 同model_type。默认为None。
|
||||
- ref_model_revision: 同model_revision。默认为None。
|
||||
- 🔥beta: 控制与参考模型偏差程度的参数。beta值越高,表示与参考模型的偏差越小。默认为`None`,使用不同rlhf算法的默认值不同,其中`simpo`算法默认为`2.`,GRPO默认为`0.04`,GKD默认为0.5,其他算法默认为`0.1`。具体参考[文档](./RLHF.md)。
|
||||
- label_smoothing: 是否使用DPO smoothing,默认值为`0`。
|
||||
- max_completion_length: GRPO/PPO/GKD算法中的最大生成长度,默认为512。
|
||||
- 🔥rpo_alpha: 来自[RPO 论文](https://arxiv.org/abs/2404.19733)中的参数,用于控制损失函数中NLL项的权重(即SFT损失),`loss = dpo_loss + rpo_alpha * sft_loss`,论文中推荐设置为`1.`。默认为`None`,即默认不引入sft_loss。
|
||||
- ld_alpha: 来自[LD-DPO 论文](https://arxiv.org/abs/2409.06411),对超出公共前缀部分的logps加权 $\alpha$ 抑制长度偏好。
|
||||
- discopop_tau: 来自 [DiscoPOP 论文](https://arxiv.org/abs/2406.08414)的温度参数 $\tau$ ,用于缩放 log-ratio。默认值0.05。在 loss_type 为 discopop 时生效。
|
||||
- loss_type: 损失类型。默认为None,使用不同的rlhf算法,其默认值不同。
|
||||
- DPO: 可选项参考[文档](https://huggingface.co/docs/trl/main/en/dpo_trainer#loss-functions),支持传入多个值实现混合训练([MPO](https://arxiv.org/abs/2411.10442)), 传入多个值时需要设置参数 loss_weights。默认为`sigmoid`。
|
||||
- GRPO: 参考[GRPO参数](#grpo参数)。
|
||||
- loss_weights: 在 DPO 训练中设置多个 loss_type 时,用于指定各个损失项的权重。
|
||||
- cpo_alpha: CPO/SimPO loss 中 nll loss的系数, 默认为`1.`。
|
||||
- simpo_gamma: SimPO算法中的reward margin项,论文建议设置为0.5-1.5,默认为`1.`。
|
||||
- desirable_weight: KTO算法中用于抵消 desirable 和 undesirable 数量不均衡的影响,对 desirable 损失按该系数进行加权,默认为`1.`。
|
||||
- undesirable_weight: KTO算法中用于抵消 desirable 和 undesirable 数量不均衡的影响,对 undesirable 损失按该系数进行加权,默认为`1.`。
|
||||
- center_rewards_coefficient: 用于RM训练。用于激励奖励模型输出均值为零的奖励的系数,具体查看这篇[论文](https://huggingface.co/papers/2312.09244)。推荐值:0.01。
|
||||
- loss_scale: 覆盖模板参数。rlhf训练时,默认为'last_round'。
|
||||
- temperature: 默认为0.9,该参数将在PPO、GRPO、GKD中使用。
|
||||
- top_k: rollout采样的top-k参数,-1表示不进行top-k过滤。默认为-1。
|
||||
- top_p: rollout采样的top-p参数,1.0表示不进行top-p过滤。默认为1.0。
|
||||
|
||||
#### GKD参数
|
||||
- lmbda: 默认为0.5。该参数在GKD中使用。控制学生数据比例的 lambda 参数(即策略内学生生成输出所占的比例)。若lmbda为0,则不使用学生生成数据。
|
||||
- sft_alpha: 默认为0。控制GKD中加入sft_loss的权重。最后的loss为`gkd_loss + sft_alpha * sft_loss`。
|
||||
- gkd_logits_topk: 使用 Top-K logits 计算 KL 散度,默认为 None(即使用完整词表计算)。设置该参数可有效降低训练显存峰值;当配置 `teacher_model_server` 时,此参数为必填项。详见[蒸馏文档](./Distillation.md#31-gkd散度作为直接损失)。
|
||||
- truncation_strategy: 用于处理输入长度超过 max_length 的样本,支持 delete 和 left 两种策略,分别表示删除该样本和从左侧裁剪。默认值为 left。若使用 delete 策略,被删除的超长样本或编码失败的样本将在原数据集中通过重采样进行替换。
|
||||
- log_completions: 是否记录训练中的模型生成内容,搭配 `--report_to wandb/swanlab` 使用。默认为False。
|
||||
- 提示:若没有设置`--report_to wandb/swanlab`,则会在checkpoint中创建`completions.jsonl`来存储生成内容。
|
||||
- 仅记录 vLLM 采样结果。
|
||||
|
||||
#### Reward/Teacher模型参数
|
||||
reward模型参数将在PPO、GRPO中使用;teacher模型参数在GKD与GRPO中使用。
|
||||
|
||||
- reward_model: 默认为None。
|
||||
- reward_adapters: 默认为`[]`。
|
||||
- reward_model_type: 默认为None。
|
||||
- reward_model_revision: 默认为None。
|
||||
- teacher_model: 默认为None。
|
||||
- teacher_adapters: 默认为`[]`。
|
||||
- teacher_model_type: 默认为None。
|
||||
- teacher_model_revision: 默认为None。
|
||||
- teacher_model_server: 教师模型服务地址,通过 `swift deploy` 部署后用于获取 logprobs。支持单 teacher URL(如 `http://localhost:8000`)或多 teacher JSON(如 `'[{"url":"http://t1:8000","tags":["data/math.jsonl"]},{"url":"http://t2:8001","tags":["data/code.jsonl"]}]'`)。`tags` 与数据集或样本标识的对应关系见[蒸馏文档](./Distillation.md#multi-teacher多教师路由)。
|
||||
- teacher_tag_key: 多 teacher 路由时,样本用于匹配 teacher `tags` 的字段名,默认为 `"dataset"`。多数据集时按 `--dataset` 各项匹配;也可在数据中自定义列(如 `teacher_tag`)并设为 `--teacher_tag_key teacher_tag`。
|
||||
- teacher_deepspeed: 同 deepspeed 参数,控制 teacher model 的 deepspeed 配置,默认使用训练模型的 deepspeed 配置。
|
||||
- offload_teacher_model: 卸载教师模型以节约显存,在采样/计算logps时加载,仅在设置teacher_model时生效默认为False。
|
||||
|
||||
|
||||
#### PPO参数
|
||||
|
||||
以下参数含义可以参考[这里](https://huggingface.co/docs/trl/main/ppo_trainer)。
|
||||
- num_ppo_epochs: 默认为4。
|
||||
- whiten_rewards: 默认为False。
|
||||
- kl_coef: 默认为0.05。
|
||||
- cliprange: 默认为0.2。
|
||||
- vf_coef: 默认为0.1。
|
||||
- cliprange_value: 默认为0.2。
|
||||
- gamma: 默认为1.0。
|
||||
- lam: 默认为0.95。
|
||||
- num_mini_batches: 默认为1。
|
||||
- local_rollout_forward_batch_size: 默认为64。
|
||||
- num_sample_generations: 默认为10。
|
||||
- missing_eos_penalty: 默认为None。
|
||||
|
||||
|
||||
#### GRPO参数
|
||||
- beta: KL正则系数,默认为0.04,设置为0时不加载ref model。
|
||||
- per_device_train_batch_size: 每个设备训练批量大小,在GRPO中,指 completion 的批次大小。
|
||||
- per_device_eval_batch_size: 每个设备评估批量大小,在GRPO中,指 completion 的批次大小。
|
||||
- steps_per_generation: 每轮生成的优化步数,默认等于 gradient_accumulation_steps。与 generation_batch_size 只能同时设置一个。
|
||||
- generation_batch_size: 总的采样 completion 批量大小,需要是 num_processes * per_device_train_batch_size 的倍数,默认等于 per_device_train_batch_size * steps_per_generation * num_processes。
|
||||
- num_generations: 每个prompt采样的数量,论文中的G值,generation_batch_size 必须能被 num_generations 整除。默认为 8。
|
||||
- num_generations_eval: 评估阶段每个prompt采样的数量。允许在评估时使用较少的生成数量以节省计算资源。如果为 None,则使用 num_generations 的值。默认为 None。
|
||||
- ds3_gather_for_generation: 该参数适用于DeepSpeed ZeRO-3。如果启用,策略模型权重将被收集用于生成,从而提高生成速度。然而,禁用此选项允许训练超出单个GPU VRAM的模型,尽管生成速度会变慢。禁用此选项与vLLM生成不兼容。默认为True。
|
||||
- reward_funcs: GRPO算法奖励函数,可选项为`accuracy`、`format`、`cosine`、`repetition`和`soft_overlong`,见swift/rewards/orm.py。你也可以在plugin中自定义自己的奖励函数。默认为`[]`。
|
||||
- reward_weights: 每个奖励函数的权重。必须与奖励函数和奖励模型的总数量匹配。如果为 None,则所有奖励的权重都相等,为`1.0`。
|
||||
- 提示:如果GRPO训练中包含`--reward_model`,则其加在奖励函数的最后位置。
|
||||
- reward_model_plugin: 奖励模型逻辑,默认为orm逻辑, 详细见[自定义奖励模型](./GRPO/DeveloperGuide/reward_model.md#自定义奖励模型)。
|
||||
- dataset_shuffle: 是否对dataset进行随机操作,默认为True。
|
||||
- truncation_strategy: 用于处理输入长度超过 max_length 的样本,支持 delete 和 left 两种策略,分别表示删除该样本和从左侧裁剪。默认值为 left。若使用 delete 策略,被删除的超长样本或编码失败的样本将在原数据集中通过重采样进行替换。
|
||||
- loss_type: loss 归一化的类型,可选项为['grpo', 'bnpo', 'dr_grpo', 'dapo', 'cispo', 'sapo', 'real', 'fipo'], 默认为'grpo', 具体参考[文档](./GRPO/DeveloperGuide/loss_types.md)
|
||||
- fipo_decay_rate: FIPO Future-KL 折扣半衰参数,实际折扣为`2 ** (-1 / fipo_decay_rate)`,默认值为32.0。
|
||||
- fipo_clip_range: FIPO influence weight 裁剪范围,默认值为0.2;设置为None或0时不裁剪。
|
||||
- fipo_clip_high_only: 是否只将FIPO influence weight裁剪到`[1.0, 1.0 + fipo_clip_range]`,默认值为True。
|
||||
- fipo_safety_threshold: 当负advantage token的IS ratio超过该阈值时,将FIPO influence weight限制到`[0.8, 1.0]`,默认值为4.0。
|
||||
- log_completions: 是否记录训练中的模型生成内容,搭配 `--report_to wandb/swanlab` 使用。默认为False。
|
||||
- 提示:若没有设置`--report_to wandb/swanlab`,则会在checkpoint中创建`completions.jsonl`来存储生成内容。
|
||||
- use_vllm: 是否使用 vLLM 作为 GRPO 生成的 infer_backend,默认为False。
|
||||
- vllm_mode: vLLM 集成模式,可选项为 `server` 和 `colocate`。server 模式使用 `swift rollout` 拉起的 vLLM 服务器进行采样,colocate 模式在程序内部署 vLLM。使用server端时,
|
||||
- vllm_mode server 参数
|
||||
- vllm_server_host: vLLM server host地址,默认为None。
|
||||
- vllm_server_port: vLLM server 服务端口,默认为8000。
|
||||
- vllm_server_base_url: vLLM server的Base URL(比如 `http://local_host:8000`), 默认为None。设置后,忽略host和port设置。
|
||||
- vllm_server_group_port: vllm server 内部通信端口,除非端口被占用,一般无需设置,默认为51216。
|
||||
- vllm_server_timeout: 连接vLLM server的超时时间,默认为 240s。
|
||||
- vllm_server_pass_dataset: 透传额外的数据集信息到vLLM server,用于多轮训练。
|
||||
- async_generate: 异步rollout以提高训练速度,注意开启时采样会使用上一轮更新的模型进行采样,不支持多轮场景。默认`false`。
|
||||
- enable_flattened_weight_sync: 是否使用 flattened tensor 进行权重同步。启用后会将多个参数打包为单个连续 tensor 进行传输,可提升同步效率,在 Server Mode 下生效,默认为 True。
|
||||
- SWIFT_UPDATE_WEIGHTS_BUCKET_SIZE: 环境变量,用于控制flattened tensor 权重同步时的传输桶大小(bucket size),适用于 Server Mode 下的全参数训练,单位为 MB,默认值为 512 MB。
|
||||
- vllm_mode colocate 参数(更多参数支持参考[vLLM参数](#vLLM参数)。)
|
||||
- vllm_gpu_memory_utilization: vllm透传参数,默认为0.9。
|
||||
- vllm_max_model_len: vllm透传参数,默认为None。
|
||||
- vllm_enforce_eager: vllm透传参数,默认为False。
|
||||
- vllm_limit_mm_per_prompt: vllm透传参数,默认为None。
|
||||
- vllm_enable_prefix_caching: vllm透传参数,默认为True。
|
||||
- vllm_tensor_parallel_size: tp并行数,默认为`1`。
|
||||
- vllm_enable_lora: 支持vLLM Engine 加载 LoRA adapter,默认为False。用于加速LoRA训练的权重同步,具体参考[文档](./GRPO/GetStarted/GRPO.md#权重同步加速)。
|
||||
- sleep_level: 训练时释放 vLLM 显存,可选项为[0, 1, 2], 默认为0,不释放。
|
||||
- offload_optimizer: 是否在vLLM推理时offload optimizer参数,默认为False。
|
||||
- offload_model: 是否在vLLM推理时 offload 模型,默认为False。
|
||||
- completion_length_limit_scope: 在多轮对话中,`max_completion_length` 的限制范围。
|
||||
`total`限制所有对话轮次的总输出长度不超过`max_completion_length`, `per_round`限制每一轮的输出长度。
|
||||
- num_iterations: 每条数据的更新次数,[GRPO论文](https://arxiv.org/abs/2402.03300)中的 $\mu$ 值,默认为1。
|
||||
- epsilon: clip 系数,默认为0.2。
|
||||
- epsilon_high: upper clip 系数,默认为None,设置后与epsilon共同构成[epsilon, epsilon_high]裁剪范围。
|
||||
- tau_pos: [SAPO](https://arxiv.org/abs/2511.20347)算法中正优势的温度参数,控制软门控函数的锐度。较大值使门控更锐利(接近硬裁剪),较小值使门控更平滑。默认为1.0。
|
||||
- tau_neg: SAPO算法中负优势的温度参数,控制软门控函数的锐度。通常设置`tau_neg > tau_pos`以对负优势施加更强约束。默认为1.05。
|
||||
- dynamic_sample: 筛除group内奖励标准差为0的数据,额外采样新数据,默认为False。
|
||||
- max_resample_times: dynamic_sample设置下限制重采样次数,默认3次。
|
||||
- overlong_filter: 跳过超长截断的样本,不参与loss计算,默认为False。
|
||||
- delta: [INTELLECT-2 tech report](https://huggingface.co/papers/2505.07291)中双侧 GRPO 上界裁剪值。若设置,建议大于 1 + epsilon。默认为None。
|
||||
- importance_sampling_level: 控制重要性采样比计算,可选项为 `token` 和 `sequence`,`token` 模式下保留原始的每个 token 的对数概率比,`sequence` 模式下则会对序列中所有有效 token 的对数概率比进行平均。[GSPO论文](https://arxiv.org/abs/2507.18071)中使用sequence级别计算来稳定训练,默认为`token`。
|
||||
- advantage_estimator: 优势计算函数,默认为 `grpo`,即计算组内相对优势,可选项为 `grpo`、[`rloo`](./GRPO/AdvancedResearch/RLOO.md)、[`reinforce_plus_plus`](./GRPO/AdvancedResearch/REINFORCEPP.md)。
|
||||
- kl_in_reward: 控制 KL 散度正则项的处理位置;`false`表示作为损失函数的独立正则项,`true`表示将 KL 直接并入奖励(从奖励中扣除)。默认情况与advantage_estimator绑定,`grpo`下默认为`false`,`rloo` 和 `reinforce_plus_plus` 下默认为 `true`。
|
||||
- scale_rewards: 指定奖励的缩放策略。可选值包括 `group`(按组内标准差缩放)、`batch`(按整个批次的标准差缩放)、`none`(不进行缩放)、`gdpo`(对每个奖励函数分别进行组内归一化后加权聚合,参考 [GDPO 论文](https://arxiv.org/abs/2601.05242))。默认值与 `advantage_estimator` 绑定:`grpo` 对应 `group`,`rloo` 对应 `none`,`reinforce_plus_plus` 对应 `batch`。
|
||||
- 注意:`gdpo` 模式不支持 `kl_in_reward=True`,若同时设置会自动将 `kl_in_reward` 设为 `False`。
|
||||
- GDPO 适用于多奖励优化场景:当使用多个奖励函数时,GDPO 会对每个奖励函数分别在组内进行标准化(减均值、除标准差),然后使用 `reward_weights` 进行加权求和,最后再进行批次级别的标准化。这种方式可以更好地保留各个奖励的相对差异,避免不同奖励组合坍塌成相同的 advantage 值。
|
||||
- teacher_kl_coef: OPD-RL中teacher_kl的系数,即 `adv_t = base_adv + teacher_kl_coef * teacher_kl`。默认为 1.0。
|
||||
- sync_ref_model: 是否定期同步ref_model,默认为False。
|
||||
- ref_model_mixup_alpha: 控制在更新过程中model和先前ref_model之间的混合。更新公式为 $π_{ref} = α * π_θ + (1 - α) * π_{ref_{prev}}$。默认为0.6。
|
||||
- ref_model_sync_steps: 同步频率,默认为512。
|
||||
- move_model_batches: 在模型向vLLM等快速推理框架移动参数时,将layers分为多少个batch. 默认为None, 代表整个模型不进行拆分,否则拆分为move_model_batches+1(非layer参数)+1(多模态部分参数)个。
|
||||
- multi_turn_scheduler: 多轮GRPO参数, 传入对应的plugin名称, 同时在plugin/multi_turn.py中添加好对应的实现。
|
||||
- max_turns: 多轮GRPO的轮数上限。默认为None,不做限制。
|
||||
- gym_env: 全局指定 gym 环境名称,需先在 plugin 里注册。默认为None,可在数据集每行 `env_config.name` 中覆盖。具体参考[文档](./GRPO/DeveloperGuide/gym_env.md)。
|
||||
- use_gym_env: 是否将 env 给出的 `total_reward` 作为奖励,启用后无需配置 reward 函数。默认为None;未显式传入时,若设置了 `gym_env` 则自动设为True,否则server模式下从rollout继承,其它情况为False。
|
||||
- top_entropy_quantile: 仅对熵值处于前指定分位的 token 参与损失计算,默认为1.0,即不过滤低熵 token,具体参考[文档](./GRPO/AdvancedResearch/entropy_mask.md)
|
||||
- log_entropy: 记录训练中的熵值变化动态,默认为False,具体参考[文档](./GRPO/GetStarted/GRPO.md#logged-metrics)
|
||||
- rollout_importance_sampling_mode: 训推不一致校正模式,可选项为 `token_truncate`、`token_mask`、`sequence_truncate`、`sequence_mask`。默认为None,不启用校正。具体参考[文档](./GRPO/AdvancedResearch/training_inference_mismatch.md)
|
||||
- rollout_importance_sampling_threshold: 重要性采样权重的阈值,用于截断或屏蔽极端权重。默认为2.0。
|
||||
- log_rollout_offpolicy_metrics: 当 `rollout_importance_sampling_mode` 未设置时,是否记录训推不一致诊断指标(KL、PPL、χ²等)。当设置了 `rollout_importance_sampling_mode` 时,指标会自动记录。默认为False。
|
||||
- off_policy_sequence_mask_delta: Off-Policy Sequence Masking 阈值,来自 [DeepSeek-V3.2 论文](https://arxiv.org/abs/2512.02556)。当设置此值时,会计算每个序列的 `mean(old_policy_logps - policy_logps)`,若该值大于阈值且该序列的优势为负,则 mask 掉该序列不参与损失计算。具体参考[文档](./GRPO/AdvancedResearch/training_inference_mismatch.md#off-policy-sequence-masking)
|
||||
|
||||
##### 奖励函数参数
|
||||
内置的奖励函数参考[文档](./GRPO/DeveloperGuide/reward_function.md)
|
||||
cosine 奖励参数
|
||||
- cosine_min_len_value_wrong: cosine 奖励函数参数,生成错误答案时,最小长度对应的奖励值。默认值为-0.5。
|
||||
- cosine_max_len_value_wrong: 生成错误答案时,最大长度对应的奖励值。默认值为0.0。
|
||||
- cosine_min_len_value_correct: 生成正确答案时,最小长度对应的奖励值。默认值为1.0。
|
||||
- cosine_max_len_value_correct: 生成正确答案时,最大长度对应的奖励值。默认值为0.5。
|
||||
- cosine_max_len: 生成文本的最大长度限制。默认等于 max_completion_length。
|
||||
|
||||
repetition 奖励参数
|
||||
- repetition_n_grams: 用于检测重复的 n-gram 大小。默认值为3。
|
||||
- repetition_max_penalty: 最大惩罚值,用于控制惩罚的强度。默认值为-1.0。
|
||||
|
||||
soft overlong 奖励参数
|
||||
- soft_max_length: 论文中的L_max,模型的最大生成长度,默认等于max_completion_length。
|
||||
- soft_cache_length: 论文中的L_cache,控制长度惩罚区间,区间为[soft_max_length-soft_cache_length, soft_max_length]。
|
||||
|
||||
### 推理参数
|
||||
|
||||
推理参数除包含[基本参数](#基本参数)、[合并参数](#合并参数)、[vLLM参数](#vllm参数)、[LMDeploy参数](#LMDeploy参数)外,还包含下面的部分:
|
||||
|
||||
- 🔥infer_backend: 推理加速后端,支持'transformers'、'vllm'、'sglang'、'lmdeploy'四种推理引擎。默认为'transformers'。
|
||||
- 注意:这四种引擎使用的都是swift的template,使用`--template_backend`控制。
|
||||
- 🔥max_batch_size: 指定infer_backend为'transformers'时生效,用于批量推理,默认为1。若设置为-1,则不受限制。
|
||||
- 🔥result_path: 推理结果存储路径(jsonl),默认为None。如果对数据集进行推理/评测,则默认保存在checkpoint目录(含args.json文件)或者'./result'目录,最终存储路径会在命令行中打印(交互式推理或部署默认不存储结果)。
|
||||
- 注意:若已存在`result_path`文件,则会进行追加写入。
|
||||
- write_batch_size: 结果写入`result_path`的batch_size。默认为1000。若设置为-1,则不受限制。
|
||||
- metric: 对推理的结果进行评估,目前支持'acc'和'rouge'。默认为None,即不进行评估。
|
||||
- val_dataset_sample: 推理数据集采样数,默认为None。
|
||||
- reranker_use_activation: 在reranker推理时,是否在score之后使用sigmoid,默认为True。
|
||||
|
||||
|
||||
### 部署参数
|
||||
|
||||
部署参数继承于[推理参数](#推理参数)。
|
||||
|
||||
- host: 服务host,默认为'0.0.0.0'。
|
||||
- port: 端口号,默认为8000。
|
||||
- api_key: 访问需要使用的api_key,默认为None。
|
||||
- owned_by: 默认为`swift`。
|
||||
- 🔥served_model_name: 提供服务的模型名称,默认使用model的后缀。
|
||||
- verbose: 打印详细日志,默认为True。
|
||||
- 注意:在`swift app`或者`swift eval`时,默认为False。
|
||||
- log_interval: tokens/s统计值打印间隔,默认20秒。设置为-1则不打印。
|
||||
- max_logprobs: 最多返回客户端的logprobs数量,默认为20。
|
||||
|
||||
### Rollout参数
|
||||
Rollout参数继承于[部署参数](#部署参数)
|
||||
- multi_turn_scheduler: 多轮GRPO训练规划器,传入对应的plugin名称, 同时在plugin/multi_turn.py中添加好对应的实现。默认为None,具体参考[文档](./GRPO/DeveloperGuide/multi_turn.md)。
|
||||
- max_turns: 多轮GRPO训练下的最大轮数,默认为None,即不做约束。
|
||||
- gym_env: 全局指定 gym 环境名称,需先在 plugin 里注册。默认为None,可在数据集每行 `env_config.name` 中覆盖。具体参考[文档](./GRPO/DeveloperGuide/gym_env.md)。
|
||||
- use_gym_env: 是否启用 gym 环境模式(rollout 输出 `total_reward` 供 trainer 使用)。默认为None,未显式传入时若设置了 `gym_env` 则自动设为True。
|
||||
- vllm_enable_lora: 支持vLLM Engine 加载 LoRA adapter,默认为False。用于加速LoRA训练的权重同步,具体参考[文档](./GRPO/GetStarted/GRPO.md#权重同步加速)。
|
||||
- vllm_max_lora_rank: vLLM Engine LoRA参数,需大于等于训练的lora_rank,建议等于。默认为16。
|
||||
- vllm_enable_expert_parallel: 开启MoE模型的专家并行(EP),将experts分布到不同rank,在vllm_use_async_engine True时生效。默认为False。
|
||||
|
||||
### Web-UI参数
|
||||
- server_name: web-ui的host,默认为'0.0.0.0'。
|
||||
- server_port: web-ui的port,默认为7860。
|
||||
- share: 默认为False。
|
||||
- lang: web-ui的语言,可选为'zh', 'en'。默认为'zh'。
|
||||
|
||||
|
||||
### App参数
|
||||
|
||||
App参数继承于[部署参数](#部署参数), [Web-UI参数](#Web-UI参数)。
|
||||
- base_url: 模型部署的base_url,例如`http://localhost:8000/v1`。默认为`None`,使用本地部署。
|
||||
- studio_title: studio的标题。默认为None,设置为模型名。
|
||||
- is_multimodal: 是否启动多模态版本的app。默认为None,自动根据model判断,若无法判断,设置为False。
|
||||
- lang: 覆盖Web-UI参数,默认为'en'。
|
||||
|
||||
### 评测参数
|
||||
|
||||
评测参数继承于[部署参数](#部署参数)。
|
||||
|
||||
- 🔥eval_backend: 评测后端,默认为'Native',也可以指定为'OpenCompass'或'VLMEvalKit'。
|
||||
- 🔥eval_dataset: 评测数据集,请查看[评测文档](./Evaluation.md)。
|
||||
- eval_limit: 每个评测集的采样数,默认为None。
|
||||
- eval_output_dir: 评测存储结果的文件夹,默认为'eval_output'。
|
||||
- temperature: 覆盖生成参数,默认为0。
|
||||
- eval_num_proc: 评测时客户端最大并发数,默认为16。
|
||||
- eval_url: 评测url,例如`http://localhost:8000/v1`。例子可以查看[这里](https://github.com/modelscope/ms-swift/tree/main/examples/eval/eval_url)。默认为None,采用本地部署评估。
|
||||
- eval_generation_config: 评测时模型推理配置,需传入json字符串格式,例如:`'{"max_new_tokens": 512}'`;默认为None。
|
||||
- extra_eval_args: 额外评测参数,需传入json字符串格式,默认为空。仅对Native评测有效,更多参数说明请查看[这里](https://evalscope.readthedocs.io/zh-cn/latest/get_started/parameters.html)。
|
||||
- local_dataset: 部分评测集,如`CMB`无法直接运行,需要下载额外数据包才可以使用。设置本参数为`true`可以自动下载全量数据包,并在当前目录下创建`data`文件夹并开始评测。数据包仅会下载一次,后续会使用缓存。该参数默认为`false`。
|
||||
- 注意:默认评测会使用`~/.cache/opencompass`下的数据集,在指定本参数后会直接使用当前目录下的data文件夹。
|
||||
|
||||
### 导出参数
|
||||
|
||||
导出参数除包含[基本参数](#基本参数)和[合并参数](#合并参数)外,还包含下面的部分:
|
||||
|
||||
- 🔥output_dir: 导出结果存储路径。默认为None,会自动设置合适后缀的路径。
|
||||
- exist_ok: 如果output_dir存在,不抛出异常,进行覆盖。默认为False。
|
||||
- 🔥quant_method: 可选为'gptq'、'awq'、'bnb'和'fp8',默认为None。例子参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/export/quantize)。
|
||||
- quant_n_samples: gptq/awq的校验集采样数,默认为256。
|
||||
- quant_batch_size: 量化batch_size,默认为1。
|
||||
- group_size: 量化group大小,默认为128。
|
||||
- to_cached_dataset: 提前对数据集进行tokenize并导出,默认为False。例子参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/cached_dataset)。更多介绍请查看`cached_dataset`。
|
||||
- 提示:你可以通过`--split_dataset_ratio`或者`--val_dataset`指定验证集内容。
|
||||
- template_mode: 用于支持对`swift rlhf`训练的`cached_dataset`功能。该参数只在`--to_cached_dataset true`时生效。可选项包括: 'train'、'rlhf'和'kto'。其中`swift pt/sft`使用'train',`swift rlhf --rlhf_type kto`使用'kto',其他rlhf算法使用'rlhf'。注意:当前'gkd', 'ppo', 'grpo'算法不支持`cached_dataset`功能。默认为'train'。
|
||||
- to_ollama: 产生ollama所需的Modelfile文件。默认为False。
|
||||
- 🔥to_mcore: HF格式权重转成Megatron格式。默认为False。
|
||||
- to_hf: Megatron格式权重转成HF格式。默认为False。
|
||||
- mcore_model: mcore格式模型路径。默认为None。
|
||||
- mcore_adapter: mcore格式模型的adapter路径,默认为None。
|
||||
- thread_count: `--to_mcore true`时的模型切片数。默认为None,根据模型大小自动设置,使得最大分片小于10GB。
|
||||
- 🔥offload_bridge: Megatron导出的用于vLLM更新HF格式权重使用CPU主存存放,以降低 GPU 显存占用。默认为 False。
|
||||
- 🔥test_convert_precision: 测试HF和Megatron格式权重转换的精度误差。默认为False。
|
||||
- test_convert_dtype: 转换精度测试使用的dtype,默认为'float32'。
|
||||
- 🔥push_to_hub: 是否推送hub,默认为False。例子参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/export/push_to_hub.sh)。
|
||||
- hub_model_id: 推送的model_id,默认为None。
|
||||
- hub_private_repo: 是否是private repo,默认为False。
|
||||
- commit_message: 提交信息,默认为'update files'。
|
||||
|
||||
### 采样参数
|
||||
|
||||
- prm_model: 过程奖励模型的类型,可以是模型id(以'transformers'方式拉起),或者plugin中定义的prm key(自定义推理过程)。
|
||||
- orm_model: 结果奖励模型的类型,通常是通配符或测试用例等,一般定义在plugin中。
|
||||
- sampler_type: 采样类型,目前支持 sample, distill。
|
||||
- sampler_engine: 支持`transformers`, `lmdeploy`, `vllm`, `client`, `no`,默认为`transformers`,采样模型的推理引擎。
|
||||
- output_dir: 输出目录,默认为`sample_output`。
|
||||
- output_file: 输出文件名称,默认为`None`使用时间戳作为文件名。传入时不需要传入目录,仅支持jsonl格式。
|
||||
- override_exist_file: 如`output_file`存在,是否覆盖。
|
||||
- num_sampling_batch_size: 每次采样的batch_size。
|
||||
- num_sampling_batches: 共采样多少batch。
|
||||
- n_best_to_keep: 返回多少最佳sequences。
|
||||
- data_range: 本采样处理数据集的分片。传入格式为`2 3`,代表数据集分为3份处理(这意味着通常有三个`swift sample`在并行处理),本实例正在处理第3个分片。
|
||||
- temperature: 在这里默认为1.0。
|
||||
- prm_threshold: PRM阈值,低于该阈值的结果会被过滤掉,默认值为`0`。
|
||||
- easy_query_threshold: 单个query的所有采样中,ORM评估如果正确,大于该比例的query会被丢弃,防止过于简单的query出现在结果中,默认为`None`,代表不过滤。
|
||||
- engine_kwargs: 传入sampler_engine的额外参数,以json string传入,例如`{"cache_max_entry_count":0.7}`。
|
||||
- num_return_sequences: 采样返回的原始sequence数量。默认为64,本参数对`sample`采样有效。
|
||||
- cache_files: 为避免同时加载prm和generator造成显存OOM,可以分两步进行采样,第一步将prm和orm置为`None`,则所有结果都会输出到文件中,第二次运行采样将sampler_engine置为`no`并传入`--cache_files`为上次采样的输出文件,则会使用上次输出的结果进行prm和orm评估并输出最终结果。
|
||||
- 注意:使用cache_files时,`--dataset`仍然需要传入,这是因为cache_files的id是由原始数据计算的md5,需要把两部分信息结合使用。
|
||||
|
||||
## 特定模型参数
|
||||
除了以上参数外,有些模型还支持额外的具体模型参数。这些参数含义通常可以在对应模型官方repo或者其推理代码中找到相应含义。**ms-swift引入这些参数以确保训练的模型与官方推理代码效果对齐**。
|
||||
- 特定模型参数可以通过`--model_kwargs`或者环境变量进行设置,例如: `--model_kwargs '{"fps_max_frames": 12}'`或者`FPS_MAX_FRAMES=12`。
|
||||
- 注意:若你在训练时指定了特定模型参数,请在推理时也设置对应的参数,这可以提高训练效果。
|
||||
|
||||
### qwen2_vl, qvq, qwen2_5_vl, mimo_vl, keye_vl, keye_vl_1_5
|
||||
参数含义与`qwen_vl_utils<0.0.12`或者`qwen_omni_utils`库中含义一致,可以查看[这里](https://github.com/QwenLM/Qwen2.5-VL/blob/main/qwen-vl-utils/src/qwen_vl_utils/vision_process.py#L24)。ms-swift通过修改这些常数值来控制图片分辨率和视频帧率,避免训练时OOM。
|
||||
|
||||
- IMAGE_FACTOR: 默认为28。
|
||||
- MIN_PIXELS: 默认为`4 * 28 * 28`。图像的最小分辨率。建议设置为28*28的倍数。
|
||||
- 🔥MAX_PIXELS: 默认为`16384 * 28 * 28`。图像的最大分辨率。建议设置为28*28的倍数。
|
||||
- MAX_RATIO: 默认为200。
|
||||
- VIDEO_MIN_PIXELS: 默认为`128 * 28 * 28`。视频中一帧的最小分辨率。建议设置为28*28的倍数。
|
||||
- 🔥VIDEO_MAX_PIXELS: 默认为`768 * 28 * 28`。视频中一帧的最大分辨率。建议设置为28*28的倍数。
|
||||
- VIDEO_TOTAL_PIXELS: 默认为`24576 * 28 * 28`。
|
||||
- FRAME_FACTOR: 默认为2。
|
||||
- FPS: 默认为2.0。
|
||||
- FPS_MIN_FRAMES: 默认为4。一段视频的最小抽帧数。
|
||||
- 🔥FPS_MAX_FRAMES: 默认为768。一段视频的最大抽帧数。
|
||||
- 🔥QWENVL_BBOX_FORMAT: grounding格式使用'legacy'还是'new'。'legacy'格式为:`<|object_ref_start|>一只狗<|object_ref_end|><|box_start|>(432,991),(1111,2077)<|box_end|>`,'new'格式参考:[Qwen3-VL cookbook](https://github.com/QwenLM/Qwen3-VL/blob/main/cookbooks/2d_grounding.ipynb),并参考[grounding数据集格式文档](../Customization/Custom-dataset.md#grounding)。默认为'legacy'。
|
||||
- 注意:该环境变量适配Qwen2/2.5/3-VL和Qwen2.5/3-Omni系列模型。
|
||||
|
||||
### qwen2_audio, qwen3_asr
|
||||
- SAMPLING_RATE: 默认为16000。
|
||||
|
||||
### qwen3_vl, qwen3_5
|
||||
参数含义与`qwen_vl_utils>=0.0.14`库中的含义一致,可以查看[这里](https://github.com/QwenLM/Qwen2.5-VL/blob/main/qwen-vl-utils/src/qwen_vl_utils/vision_process.py#L24)。通过传入以下环境变量,可以修改该库的全局变量默认值。(也兼容使用`qwen2_5_vl`的环境变量,例如:`MAX_PIXELS`、`VIDEO_MAX_PIXELS`,会做自动转换。)
|
||||
|
||||
- SPATIAL_MERGE_SIZE: 默认为2。
|
||||
- IMAGE_MIN_TOKEN_NUM: 默认为`4`,代表一张图片最小图像tokens的个数。
|
||||
- 🔥IMAGE_MAX_TOKEN_NUM: 默认为`16384`,代表一张图片最大图像tokens的个数。(用于避免OOM)。
|
||||
- 提示:等价最大图像像素为`IMAGE_MAX_TOKEN_NUM * 32 *32`。
|
||||
- VIDEO_MIN_TOKEN_NUM: 默认为`128`,代表视频中一帧的最小视频tokens的个数。
|
||||
- 🔥VIDEO_MAX_TOKEN_NUM: 默认为`768`,代表视频中一帧的最大视频tokens的个数。(用于避免OOM)
|
||||
- MAX_RATIO: 默认为200。
|
||||
- FRAME_FACTOR: 默认为2。
|
||||
- FPS: 默认为2.0。
|
||||
- FPS_MIN_FRAMES: 默认为4。代表一段视频的最小抽帧数。
|
||||
- 🔥FPS_MAX_FRAMES: 默认为768,代表一段视频的最大抽帧数。(用于避免OOM)。
|
||||
|
||||
|
||||
### qwen2_5_omni, qwen3_omni
|
||||
qwen2_5_omni除了包含qwen2_5_vl和qwen2_audio的模型特定参数外,还包含以下参数:(注意:qwen3_omni包含的是**qwen3_vl**, qwen2_audio的模型特定参数)
|
||||
- USE_AUDIO_IN_VIDEO: 默认为False。是否使用video中的音频信息。
|
||||
- 🔥ENABLE_AUDIO_OUTPUT: 默认为None,即使用`config.json`中的值。若使用zero3进行训练,请设置为False。
|
||||
- 提示:ms-swift只对thinker部分进行微调,建议设置为False以降低显存占用(只创建thinker部分的模型结构)。
|
||||
|
||||
|
||||
### qwen3_vl_emb, qwen3_vl_reranker
|
||||
参数含义与`qwen3_vl`相同,见上面的描述。以下为对默认值的覆盖:
|
||||
|
||||
- IMAGE_MAX_TOKEN_NUM: qwen3_vl_emb默认为1800, qwen3_vl_reranker默认为1280。具体参考这里:[qwen3_vl_embedding](https://modelscope.cn/models/Qwen/Qwen3-VL-Embedding-2B/file/view/master/scripts%2Fqwen3_vl_embedding.py?status=1#L26), [qwen3_vl_reranker](https://modelscope.cn/models/Qwen/Qwen3-VL-Reranker-2B/file/view/master/scripts%2Fqwen3_vl_reranker.py?status=1#L16)。
|
||||
- FPS: 默认为1。
|
||||
- FPS_MAX_FRAMES: 默认为64。
|
||||
|
||||
|
||||
### internvl_chat
|
||||
参数含义可以查看[这里](https://modelscope.cn/models/OpenGVLab/InternVL2_5-2B)。
|
||||
- MAX_NUM: 默认为12。
|
||||
- INPUT_SIZE: 默认为448。
|
||||
- VIDEO_MAX_NUM: 默认为1。视频的MAX_NUM。
|
||||
- VIDEO_SEGMENTS: 默认为8。
|
||||
|
||||
|
||||
### minicpmv2_6, minicpmv4, minicpmo
|
||||
- MAX_SLICE_NUMS: 默认为9,参考[这里](https://modelscope.cn/models/OpenBMB/MiniCPM-V-2_6/file/view/master?fileName=config.json&status=1)。
|
||||
- VIDEO_MAX_SLICE_NUMS: 默认为1,视频的MAX_SLICE_NUMS,参考[这里](https://modelscope.cn/models/OpenBMB/MiniCPM-V-2_6)。
|
||||
- MAX_NUM_FRAMES: 默认为64,参考[这里](https://modelscope.cn/models/OpenBMB/MiniCPM-V-2_6)。
|
||||
|
||||
### minicpmo
|
||||
- INIT_TTS: 默认为False。是否创建和加载TTS模型。
|
||||
- INIT_AUDIO: 默认为True。是否创建和加载Audio模型。
|
||||
- USE_AUDIO_IN_VIDEO: 默认为False。是否使用video中的音频信息。
|
||||
|
||||
|
||||
### minicpmv4_6
|
||||
- DOWNSAMPLE_MODE: 默认为`'16x'`。视觉token下采样模式。`'16x'`合并token以提升效率;`'4x'`保留4倍更多的token以获取更精细的细节。
|
||||
- MAX_SLICE_NUMS: 默认为9。分割高分辨率图像时的最大切片数量。数值越高,大图像保留的细节越多。建议图像设为36。
|
||||
- VIDEO_MAX_SLICE_NUMS: 默认为1。视频的`MAX_SLICE_NUMS`。
|
||||
- MAX_NUM_FRAMES: 默认为128。从视频中采样的主帧最大数量。
|
||||
- STACK_FRAMES: 默认为1。每秒的总采样点数。1表示仅使用主帧(无堆叠)。N(N>1)表示每秒包含1个主帧 + N−1个子帧;子帧会被合成一张网格图像,并与主帧交错排列。建议短视频设为1,长视频设为3或5。
|
||||
|
||||
|
||||
### ovis1_6, ovis2
|
||||
- MAX_PARTITION: 默认为9,参考[这里](https://github.com/AIDC-AI/Ovis/blob/d248e34d755a95d24315c40e2489750a869c5dbc/ovis/model/modeling_ovis.py#L312)。
|
||||
|
||||
### ovis2_5
|
||||
以下参数含义可以在[这里](https://modelscope.cn/models/AIDC-AI/Ovis2.5-2B)的示例代码中找到。
|
||||
- MIX_PIXELS: int类型,默认为`448 * 448`。
|
||||
- MAX_PIXELS: int类型,默认为`1344 * 1792`。若出现OOM,可以调小该值。
|
||||
- VIDEO_MAX_PIXELS: int类型,默认为`896 * 896`。
|
||||
- NUM_FRAMES: 默认为8。用于视频抽帧。
|
||||
|
||||
### mplug_owl3, mplug_owl3_241101
|
||||
- MAX_NUM_FRAMES: 默认为16,参考[这里](https://modelscope.cn/models/iic/mPLUG-Owl3-7B-240728)。
|
||||
|
||||
### xcomposer2_4khd
|
||||
- HD_NUM: 默认为55,参考[这里](https://modelscope.cn/models/Shanghai_AI_Laboratory/internlm-xcomposer2-4khd-7b)。
|
||||
|
||||
### xcomposer2_5
|
||||
- HD_NUM: 图片数量为1时,默认值为24。大于1,默认为6。参考[这里](https://modelscope.cn/models/AI-ModelScope/internlm-xcomposer2d5-7b/file/view/master?fileName=modeling_internlm_xcomposer2.py&status=1#L254)。
|
||||
|
||||
### video_cogvlm2
|
||||
- NUM_FRAMES: 默认为24,参考[这里](https://github.com/zai-org/CogVLM2/blob/main/video_demo/inference.py#L22)。
|
||||
|
||||
### phi3_vision
|
||||
- NUM_CROPS: 默认为4,参考[这里](https://modelscope.cn/models/LLM-Research/Phi-3.5-vision-instruct)。
|
||||
|
||||
### llama3_1_omni
|
||||
- N_MELS: 默认为128,参考[这里](https://github.com/ictnlp/LLaMA-Omni/blob/544d0ff3de8817fdcbc5192941a11cf4a72cbf2b/omni_speech/infer/infer.py#L57)。
|
||||
|
||||
### video_llava
|
||||
- NUM_FRAMES: 默认为16。
|
||||
|
||||
|
||||
## 其他环境变量
|
||||
- USE_HF: 使用ModelScope/HuggingFace,默认为'0',使用ModelScope。
|
||||
- CUDA_VISIBLE_DEVICES: 控制使用哪些GPU卡。默认使用所有卡。
|
||||
- ASCEND_RT_VISIBLE_DEVICES: 控制使用哪些NPU卡(只对ASCEND卡生效)。默认使用所有卡。
|
||||
- MODELSCOPE_CACHE: 控制缓存路径。(多机训练时建议设置该值,以确保不同节点使用相同的数据集缓存)。
|
||||
- PYTORCH_CUDA_ALLOC_CONF: 推荐设置为`'expandable_segments:True'`,这将减少GPU内存碎片,具体请参考[torch文档](https://docs.pytorch.org/docs/stable/notes/cuda.html#cuda-memory-management)。
|
||||
- NPROC_PER_NODE: torchrun中`--nproc_per_node`的参数透传。默认为1。若设置了`NPROC_PER_NODE`或者`NNODES`环境变量,则使用torchrun启动训练或推理。
|
||||
- MASTER_PORT: torchrun中`--master_port`的参数透传。默认为29500。
|
||||
- MASTER_ADDR: torchrun中`--master_addr`的参数透传。
|
||||
- NNODES: torchrun中`--nnodes`的参数透传。
|
||||
- NODE_RANK: torchrun中`--node_rank`的参数透传。
|
||||
- LOG_LEVEL: 日志的level,默认为'INFO',你可以设置为'WARNING', 'ERROR'等。
|
||||
- SWIFT_DEBUG: 在`engine.infer(...)`时,若设置为'1',TransformersEngine将会打印input_ids和generate_ids的内容方便进行调试与对齐。
|
||||
- VLLM_USE_V1: 用于切换vLLM使用V0/V1版本。
|
||||
- SWIFT_TIMEOUT: 若多模态数据集中存在图像URL,该参数用于控制获取图片的timeout,默认为20s。
|
||||
- ROOT_IMAGE_DIR: 图像(多模态)资源的根目录。通过设置该参数,可以在数据集中使用相对于 `ROOT_IMAGE_DIR` 的相对路径。默认情况下,是相对于运行目录的相对路径。
|
||||
- SWIFT_SINGLE_DEVICE_MODE: 单设备模式,可选值为"0"(默认值)/"1",在此模式下,每个进程只能看到一个设备。
|
||||
- SWIFT_AUDIO_LOAD_BACKEND: 音频波形加载后端。`librosa`(默认)或 `soundfile_pyav`(soundfile 优先、失败时 pyav fallback)。GRPO/GKD训练 在 `--use_vllm true`时默认为 `soundfile_pyav`,保证训练侧 encode 与 vLLM rollout 解析同一音频 URL 时波形一致。
|
||||
@@ -0,0 +1,398 @@
|
||||
# 知识蒸馏(Knowledge Distillation)
|
||||
|
||||
知识蒸馏是一种将教师模型(teacher model)的能力迁移到学生模型(student model)的训练方法。其核心思想是:让学生在每个 token 位置上向教师的输出分布靠拢,从而获得比单纯模仿标注答案更丰富的监督信号——教师不仅告诉学生「哪个 token 是对的」,还告诉学生「其他 token 有多好 / 多差」。
|
||||
|
||||
本文档自顶向下地介绍:蒸馏为什么有效(第一节)、蒸馏方法的统一设计框架(第二节),最后落到 swift 中三种具体的蒸馏训练方法 GKD / OPD-RL / OPSD(第三节)。
|
||||
|
||||
---
|
||||
|
||||
## 一、为什么需要蒸馏:从稀疏信号到稠密信号
|
||||
|
||||
一个语言模型的能力,通常由一连串训练阶段堆叠而成:
|
||||
|
||||
- **预训练(Pre-training)**:习得语言、世界知识、基础推理等通用能力。
|
||||
- **中训练(Mid-training)**:注入领域知识,如代码、医学、公司内部文档等。
|
||||
- **后训练(Post-training)**:激发目标行为,如指令遵循、数学推理、对话风格等。
|
||||
|
||||
蒸馏主要发生在**后训练**阶段。要理解它的价值,需要从两个彼此独立的维度来看待后训练方法:
|
||||
|
||||
1. **采样方式(数据从哪来)**:训练序列是由学生自己生成(on-policy),还是来自外部固定数据(off-policy)。
|
||||
2. **反馈密度(每条序列能学到多少)**:是整条序列只有一个奖励(per-sequence,稀疏),还是每个 token 都有信号(per-token,稠密)。
|
||||
|
||||
|
||||
**SFT / 离线蒸馏**(off-policy + 稠密):在固定数据上对齐标注或教师分布。信号稠密,但训练时见到的都是教师/标注的状态,和学生推理时自己会进入的状态不一致。学生一旦在推理早期犯了教师不会犯的错,就会进入训练中从未见过的状态,误差不断累积,这被称为 **exposure bias(曝光偏差)**。
|
||||
|
||||
**RL**(on-policy + 稀疏):学生自己采样,按最终结果给奖励。分布与学生推理一致,但奖励通常是**序列级**标量,一般不指明具体哪个 token 出错。
|
||||
|
||||
**On-policy 蒸馏**(on-policy + 稠密):学生自己采样轨迹,再由教师对轨迹的**每一个 token** 打分。训练分布与学生推理分布一致,且反馈为 per-token 级别。
|
||||
|
||||
### SFT 是蒸馏的一个特例
|
||||
|
||||
理解蒸馏的一个自然切入点是 SFT 的损失函数。SFT 的 cross-entropy loss,等价于以标注 token 的 one-hot 分布 $\delta_{y^*}$ 为「教师」的 KL 散度:
|
||||
|
||||
$$-\log P_S(y^*_t) = \text{KL}(\delta_{y^*} \,\|\, P_S)$$
|
||||
|
||||
知识蒸馏只是把这个确定性的 one-hot「教师」换成了真实教师模型的软分布 $P_T$,在每个 token 上优化 $\text{KL}(P_T \,\|\, P_S)$,从而提供比 one-hot 丰富的监督信号。
|
||||
|
||||
| 方法 | 采样方式 | 反馈密度 | 教师分布 |
|
||||
|------|----------|----------|----------|
|
||||
| SFT | off-policy(固定数据) | 稠密(per-token) | one-hot $\delta_{y^*}$ |
|
||||
| 离线(off-policy)蒸馏 | off-policy(固定数据或教师生成) | 稠密(per-token) | 教师软分布 |
|
||||
| RL | on-policy(学生采样) | 稀疏(per-sequence) | 无 |
|
||||
| **On-policy 蒸馏** | **on-policy(学生采样)** | **稠密(per-token)** | **教师软分布** |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 二、蒸馏的两个核心选择
|
||||
|
||||
不同蒸馏方法的差异,几乎都可以归结为两个问题。理解了这两个维度,后面的方法都只是它们的不同组合。
|
||||
|
||||
### 2.1 教师信号怎么算
|
||||
|
||||
在每个 token 位置上,量化教师分布 $P_T$ 与学生分布 $P_S$ 的差异(我们称之为 **Teacher KL**),有两个子选择。
|
||||
|
||||
**(a) 散度方向**
|
||||
|
||||
| 散度 | 定义 | 优化时的行为(信息论含义) |
|
||||
|------|------|------|
|
||||
| Forward KL | $\text{KL}(P_T \,\|\, P_S)$ | Mode-covering:学生需对教师概率较高的区域都赋予足够概率 |
|
||||
| Reverse KL | $\text{KL}(P_S \,\|\, P_T)$ | Mode-seeking:学生主要拟合教师的众数(高概率)区域 |
|
||||
| 广义 JSD($\beta$) | $\beta\,\text{KL}(P_T\|M) + (1-\beta)\,\text{KL}(P_S\|M)$,其中 $M=\beta P_T+(1-\beta)P_S$ | 在两者之间插值 |
|
||||
|
||||
> 其中 $\beta=0$ 退化为 Forward KL,$\beta=1$ 退化为 Reverse KL。SFT 等价于 Forward KL(教师为 one-hot)。
|
||||
|
||||
在 swift 中:
|
||||
- GKD 默认 $\beta=0.5$(JSD),可通过 `--beta` 在 Forward / JSD / Reverse 之间选择;
|
||||
- OPD-RL 的实现固定使用 Reverse KL 的 k1 估计量 $\log\pi_{\text{teacher}}(y_t)-\log\pi_{\text{student}}(y_t)$ 作为 per-token advantage。
|
||||
|
||||
**(b) 计算粒度**
|
||||
|
||||
| 计算粒度 | 需要的教师信息 | 说明 |
|
||||
|----------|---------------|------|
|
||||
| 全词表 | 教师完整的 next-token 分布 | 可计算散度的精确值;显存开销大 |
|
||||
| Top-K | 教师概率最高的 K 个 token | 在 top-K 子集上重新归一化后的近似;适合外部 API(受 `max_logprobs` 限制) |
|
||||
| 采样 token | 教师在学生实际采样 token 上的单个 logp | Reverse KL 的单样本蒙特卡洛估计;通信开销最低 |
|
||||
|
||||
> **精度 vs 开销**:全词表需要物化完整 logits;采样 token 只需教师在已采样 token 上的 logp(可走远程 API)。[DeepSeek-V4](https://arxiv.org/abs/2606.19348)技术报告指出,仅用采样 token 的 log-ratio 作 advantage 时梯度估计方差较大,因此其全词表 OPD 采用完整 logit 蒸馏
|
||||
|
||||
### 2.2 信号怎么传给学生
|
||||
|
||||
| | **路径 A:GKD(直接损失)** | **路径 B:OPD-RL(RL Advantage)** |
|
||||
|---|---|---|
|
||||
| 训练范式 | `--rlhf_type gkd` | `--rlhf_type grpo` + 教师 |
|
||||
| 信号传递 | 把信号作为 loss | 把信号当 advantage,走 policy gradient |
|
||||
| 梯度流经 | 学生**全词表** logits(或 top-k) | 仅学生**采样 token** 的 $\nabla\log\pi(y_t)$ |
|
||||
| 教师信息需求 | 全词表分布(或 top-k logits) | 采样 token 上的单个 logp |
|
||||
| 散度选择 | Forward / Reverse / JSD(`--beta`) | Reverse KL(k1 log-ratio) |
|
||||
| 与任务奖励组合 | 通过 `sft_alpha` 混合 SFT loss | 可与 GRPO reward 叠加为 advantage |
|
||||
|
||||
两者**共享同一套教师基础设施**(见下文),区别只在如何使用 teacher KL 信号。
|
||||
|
||||
> **蒸馏的常见用法**
|
||||
> 1. **能力融合**:多个专家模型蒸馏到统一模型。
|
||||
> 2. **强到弱**:大模型向小模型传递能力。
|
||||
> 3. **防遗忘**:用旧 checkpoint 作教师,在多阶段训练后恢复先前能力。
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 三、swift 中的蒸馏方法
|
||||
|
||||
swift 提供三种蒸馏训练方法,它们共享同一套教师基础设施:
|
||||
|
||||
| 方法 | 信号传递路径 | 启用方式 | 一句话 |
|
||||
|------|--------------|----------|--------|
|
||||
| **GKD** | 直接损失(路径 A) | `--rlhf_type gkd` | 教师散度作为 loss 反向传播;支持全词表 / top-k 散度 |
|
||||
| **OPD-RL** | RL advantage(路径 B) | `--rlhf_type grpo` + 教师 | 教师 log-ratio 注入 GRPO advantage,可与任务奖励叠加 |
|
||||
| **OPSD** | A 或 B 均可 | 在上面基础上提供 `teacher_prompt` | 单模型自蒸馏:教师输入含特权信息(如参考解答) |
|
||||
|
||||
**教师的三种来源**(GKD 与 OPD-RL 通用):
|
||||
|
||||
- `--teacher_model`:在训练进程中加载一个独立的冻结教师模型。
|
||||
- `--teacher_model_server`:连接一个外部教师服务(`swift deploy`启动的vllm服务),不在训练卡上加载教师。GKD 使用 API 时需同时设置 `--gkd_logits_topk`。支持单 URL 与多 teacher JSON 配置。
|
||||
- **自蒸馏**:教师与学生同源。LoRA 训练且 `--teacher_model` 与 `--model` 相同时,自动用 `disable_adapter()` 以基座为固定教师,无需额外加载;不传 `--teacher_model` 且设置 `teacher_prompt`, 则以「学生当前权重」为动态教师。
|
||||
|
||||
| 参数 | 默认值 | 说明 |
|
||||
|------|--------|------|
|
||||
| `--teacher_model` | None | 教师模型路径;GKD 下不传则为动态自蒸馏 |
|
||||
| `--teacher_model_server` | None | 教师 API 地址(与 `teacher_model` 互斥);见下方格式说明 |
|
||||
| `--teacher_tag_key` | `"dataset"` | 多 teacher 路由时,样本用于匹配 teacher `tags` 的字段名 |
|
||||
| `--teacher_deepspeed` | None | 教师模型的 DeepSpeed 配置(如 `zero3`) |
|
||||
| `--offload_teacher_model` | False | 非前向阶段将教师卸载到 CPU(仅 `teacher_model` 生效) |
|
||||
|
||||
完整参数说明见[命令行参数](./Command-line-parameters.md#rewardteacher模型参数)。
|
||||
|
||||
#### 外部教师 API
|
||||
|
||||
通过 `swift deploy --model xxx --infer_backend vllm` 部署教师服务后,训练进程按 prompt 向 API 请求 logprobs,无需在训练卡上加载教师权重。
|
||||
|
||||
- **GKD**:需设置 `--gkd_logits_topk`(API 仅返回 top-k logprobs),用于计算 JSD 散度损失。
|
||||
- **OPD-RL**:取采样 token 的 logp(`prompt_logprobs=0`),注入 advantage;系数由全局 `--teacher_kl_coef` 控制(见 [3.2](#32-opd-rlkl-作为-rl-advantage))。
|
||||
|
||||
#### Multi-Teacher(多教师路由)
|
||||
|
||||
Multi-Teacher 允许同时连接多个外部教师 API,按 tag 将每条样本路由到一个教师,实现领域专家蒸馏。
|
||||
|
||||
**`teacher_model_server` 格式**
|
||||
|
||||
```bash
|
||||
# 单 teacher
|
||||
--teacher_model_server http://localhost:8000
|
||||
|
||||
# 多 teacher(`tags` 与 `--dataset` 或数据列取值对应,见下方「模式一」)
|
||||
--teacher_model_server '[{"url":"http://t1:8000","tags":["data/math.jsonl"]},{"url":"http://t2:8001","tags":["data/code.jsonl"]}]'
|
||||
```
|
||||
|
||||
每个 teacher 配置项:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `url` | 教师 API 地址 |
|
||||
| `tags` | 该教师负责的数据来源;单 teacher 可省略;多 teacher 时各条目不重叠,且与下方路由方式中的标识一致 |
|
||||
|
||||
**路由方式**
|
||||
|
||||
**模式一:按数据集路由(默认)**
|
||||
|
||||
传入多个 `--dataset` 时,样本按来源数据集匹配 teacher。默认 `--teacher_tag_key` 为 `"dataset"`,`tags` 与 `--dataset` 中对应项保持一致:
|
||||
|
||||
```bash
|
||||
# Hub ID
|
||||
--dataset AI-ModelScope/alpaca-gpt4-data-en AI-ModelScope/alpaca-cleaned \
|
||||
--teacher_model_server '[{"url":"http://t1:8000","tags":["AI-ModelScope/alpaca-gpt4-data-en"]},{"url":"http://t2:8001","tags":["AI-ModelScope/alpaca-cleaned"]}]'
|
||||
|
||||
# 本地路径
|
||||
--dataset data/math.jsonl data/code.jsonl \
|
||||
--teacher_model_server '[{"url":"http://t1:8000","tags":["data/math.jsonl"]},{"url":"http://t2:8001","tags":["data/code.jsonl"]}]'
|
||||
```
|
||||
|
||||
**模式二:按样本路由**
|
||||
|
||||
在数据文件中为每条样本增加列(如 `teacher_tag`),设置 `--teacher_tag_key teacher_tag`,`tags` 与该列取值对应。适用于只传一个 `--dataset` 但仍需多个 teacher 的场景。
|
||||
|
||||
**示例:GKD + 多 teacher**
|
||||
|
||||
```bash
|
||||
# 部署两个教师服务(GKD 需 max_logprobs >= gkd_logits_topk)
|
||||
CUDA_VISIBLE_DEVICES=1 swift deploy --model Qwen/Qwen3.5-4B --port 8000 --max_logprobs 64
|
||||
CUDA_VISIBLE_DEVICES=2 swift deploy --model Qwen/Qwen3.5-1.7B --port 8001 --max_logprobs 64
|
||||
|
||||
CUDA_VISIBLE_DEVICES=0 swift rlhf \
|
||||
--rlhf_type gkd \
|
||||
--model Qwen/Qwen3.5-0.6B \
|
||||
--teacher_model_server '[{"url":"http://localhost:8000","tags":["data/math.jsonl"]},{"url":"http://localhost:8001","tags":["data/code.jsonl"]}]' \
|
||||
--gkd_logits_topk 64 \
|
||||
--dataset data/math.jsonl data/code.jsonl \
|
||||
...
|
||||
```
|
||||
|
||||
**示例:OPD-RL + 多 teacher**
|
||||
|
||||
```bash
|
||||
CUDA_VISIBLE_DEVICES=1 swift deploy --model Qwen/Qwen3.5-4B --port 8000 --max_logprobs 1
|
||||
CUDA_VISIBLE_DEVICES=2 swift deploy --model Qwen/Qwen3.5-1.7B --port 8001 --max_logprobs 1
|
||||
|
||||
CUDA_VISIBLE_DEVICES=0 swift rlhf \
|
||||
--rlhf_type grpo \
|
||||
--model Qwen/Qwen3.5-0.6B \
|
||||
--teacher_model_server '[{"url":"http://localhost:8000","tags":["data/math.jsonl"]},{"url":"http://localhost:8001","tags":["data/code.jsonl"]}]' \
|
||||
--teacher_kl_coef 1.0 \
|
||||
--dataset data/math.jsonl data/code.jsonl \
|
||||
--use_vllm true --vllm_mode colocate \
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.1 GKD:散度作为直接损失
|
||||
|
||||
GKD([Generalized Knowledge Distillation](https://arxiv.org/pdf/2306.13649))直接把教师-学生间的散度作为损失函数反向传播。
|
||||
|
||||
**损失函数**
|
||||
|
||||
$$
|
||||
\mathcal{L}_{\text{GKD}}(x, y) = \sum_{t=1}^{|y|} D_{\text{JSD}(\beta)}\big(P_{\text{teacher}}(\cdot|x,y_{<t}),\, P_{\text{student}}(\cdot|x,y_{<t})\big)
|
||||
$$
|
||||
|
||||
其中散度 $D$ 由 `--beta` 选择(见 2.1):$\beta=0$ 为 Forward KL,$\beta=1$ 为 Reverse KL,$0<\beta<1$ 为广义 JSD(默认 $0.5$)。
|
||||
|
||||
**On-Policy vs Off-Policy:`lmbda`**
|
||||
|
||||
GKD 通过 `lmbda` 控制每个 batch 用学生在线采样的概率:
|
||||
|
||||
```python
|
||||
if random() <= lmbda:
|
||||
y = student.generate(x) # on-policy:学生自己采样
|
||||
else:
|
||||
y = y_ground_truth # off-policy:使用数据集标注
|
||||
loss = D(P_teacher(·|x, y), P_student(·|x, y))
|
||||
```
|
||||
|
||||
- `lmbda=0`:纯离线(传统 SFT 蒸馏)。
|
||||
- `lmbda=1`:纯在线(学生从自身错误中学习,即 on-policy 蒸馏)。
|
||||
- `0<lmbda<1`:混合。
|
||||
|
||||
> 若希望走teacher生成数据路径,需先用教师离线生成响应写入数据集,再设 `lmbda=0` 训练
|
||||
|
||||
**GKD参数**
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `--beta` | float | 0.5 | 散度插值:0=Forward KL,0.5=JSD,1=Reverse KL |
|
||||
| `--lmbda` | float | 0.5 | 在线采样概率:0=离线,1=纯在线 |
|
||||
| `--sft_alpha` | float | 0 | 混合 SFT loss 比例,最终 `loss = gkd_loss + sft_alpha * sft_loss`(仅对**非学生生成**的数据生效) |
|
||||
| `--gkd_logits_topk` | int | None | 仅用教师 top-K logits 计算 KL;使用 `teacher_model_server` 时为必填 |
|
||||
|
||||
**Top-K 蒸馏(省显存)**
|
||||
|
||||
默认用完整词表计算 KL,词表很大时容易 OOM,可使用`--gkd_logits_topk`参数。
|
||||
|
||||
**外部教师 API**
|
||||
|
||||
设置 `--teacher_model_server` 时需同时设置 `--gkd_logits_topk`(API 仅返回 top-k logprobs)。示例如下:
|
||||
|
||||
```bash
|
||||
# 步骤 1:部署教师模型(max_logprobs 需 >= gkd_logits_topk)
|
||||
CUDA_VISIBLE_DEVICES=0 swift deploy \
|
||||
--model Qwen/Qwen3.5-9B \
|
||||
--infer_backend vllm \
|
||||
--port 8000 \
|
||||
--max_logprobs 64
|
||||
|
||||
# 步骤 2:启动 GKD 训练
|
||||
CUDA_VISIBLE_DEVICES=1,2,3,4 \
|
||||
NPROC_PER_NODE=4 \
|
||||
swift rlhf \
|
||||
--rlhf_type gkd \
|
||||
--model Qwen/Qwen3.5-2B \
|
||||
--teacher_model_server http://localhost:8000 \
|
||||
--gkd_logits_topk 64 \
|
||||
--lmbda 1.0 \
|
||||
--beta 1.0 \
|
||||
--dataset xxx
|
||||
```
|
||||
|
||||
**在线采样加速**
|
||||
|
||||
`lmbda > 0` 时学生需在线生成序列,建议用 vLLM 加速采样(colocate / server 两种模式,与 GRPO 一致),参考 [GRPO 文档](./GRPO/GetStarted/GRPO.md#集群支持)。
|
||||
|
||||
**多轮 GKD**
|
||||
|
||||
GKD 多轮训练,与 GRPO 共享同一套 `MultiTurnScheduler` 基础设施。
|
||||
|
||||
完整接口和自定义方式请参考 [GRPO 多轮训练文档](./GRPO/DeveloperGuide/multi_turn.md)。
|
||||
|
||||
**参考脚本**
|
||||
|
||||
- 基础训练:[examples/train/rlhf/gkd/](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/gkd/)
|
||||
- 多轮训练:[examples/train/rlhf/gkd/multi_turn.sh](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/gkd/multi_turn.sh)
|
||||
- 多模态:[examples/train/multimodal/rlhf/gkd/](https://github.com/modelscope/ms-swift/tree/main/examples/train/multimodal/rlhf/gkd/)
|
||||
- Megatron:[examples/megatron/rlhf/gkd/](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/rlhf/gkd/)
|
||||
|
||||
|
||||
---
|
||||
|
||||
### 3.2 OPD-RL:KL 作为 RL Advantage
|
||||
|
||||
OPD(On-Policy Distillation)RL 把教师 KL 信号注入 GRPO 的 **per-token advantage**,通过 policy gradient 更新学生。
|
||||
|
||||
**原理**
|
||||
|
||||
标准 GRPO 的 advantage 来自组内归一化的任务奖励(per-sequence 标量)。OPD-RL 在 advantage 归一化**之后**逐 token 注入教师信号:
|
||||
|
||||
$$
|
||||
A_t = A_t^{\text{base}} + \alpha \cdot \big(\log \pi_{\text{teacher}}(y_t|x,y_{<t}) - \log \pi_{\text{student}}(y_t|x,y_{<t})\big)
|
||||
$$
|
||||
|
||||
- $A_t^{\text{base}}$:GRPO 归一化后的任务奖励 advantage(无奖励函数时为 0)。
|
||||
- $\alpha$:`--teacher_kl_coef`,教师信号强度。
|
||||
- $\log\pi_{\text{teacher}}(y_t) - \log\pi_{\text{student}}(y_t)$:教师 log-ratio,即 Reverse KL 梯度中 $\nabla_\theta\log\pi_\theta(y_t)$ 系数对应的 k1 估计量(见 `compute_teacher_logratio`)。
|
||||
|
||||
**纯蒸馏模式**:不设 `--reward_funcs` 时 base advantage 为 0,教师信号是唯一驱动力,此时 $A_t = \alpha\cdot(\log\pi_{\text{teacher}}(y_t)-\log\pi_{\text{student}}(y_t))$。
|
||||
|
||||
**监控指标**:日志中的 `teacher_kl` 是 k3 估计量 $e^{d}-d-1$($d=\log\pi_{\text{teacher}}-\log\pi_{\text{student}}$),衡量学生与教师的距离。
|
||||
|
||||
**启用方式**:在 `--rlhf_type grpo` 下设置 `--teacher_model` 或 `--teacher_model_server` 即自动启用 OPD-RL,无需额外开关。教师相关参数见上文共享参数表。
|
||||
|
||||
**OPD-RL 特有参数**
|
||||
|
||||
| 参数 | 默认值 | 说明 |
|
||||
|------|--------|------|
|
||||
| `--teacher_kl_coef` | 1.0 | 教师 log-ratio 注入 advantage 的系数 $\alpha$ |
|
||||
|
||||
**参考脚本**
|
||||
|
||||
- HF:[examples/train/grpo/opd_rl.sh](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/opd_rl.sh)
|
||||
- Megatron:[examples/megatron/grpo/opd_rl.sh](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/grpo/opd_rl.sh)
|
||||
- Ray:[examples/ray/grpo/run_opd.sh](https://github.com/modelscope/ms-swift/tree/main/examples/ray/grpo/run_opd.sh)
|
||||
|
||||
|
||||
---
|
||||
|
||||
### 3.3 OPSD:On-Policy Self-Distillation
|
||||
|
||||
OPSD([On-Policy Self-Distillation](https://arxiv.org/abs/2601.18734))是一种单模型自蒸馏方法:同一模型分别构造学生输入与教师输入,教师侧额外接收**特权信息**(如参考解答),再对齐两者在学生采样响应上的输出分布。
|
||||
|
||||
**核心机制**
|
||||
|
||||
- **学生**:仅看到问题,正常推理。
|
||||
- **教师**:看到问题 + 参考解答(通过 `teacher_prompt` 列提供特权信息)。
|
||||
- **训练目标**:用散度(JSD / KL)对齐学生与教师在同一份学生采样响应上的输出分布。
|
||||
|
||||
OPSD 既可走 GKD 路径,也可走 OPD-RL 路径:
|
||||
|
||||
- **GKD + OPSD**:`--rlhf_type gkd`,教师 KL 作为直接损失。
|
||||
- **OPD-RL + OPSD**:`--rlhf_type grpo`;动态模式不传 `--teacher_model`,固定模式设 `--teacher_model` 与 `--model` 相同。
|
||||
|
||||
**两种自蒸馏权重模式**
|
||||
|
||||
| 模式 | 参数配置 | 教师权重 | 说明 |
|
||||
|------|---------|---------|------|
|
||||
| **Dynamic(动态)** | 不传 `--teacher_model` | 学生当前权重 | 教师随训练同步更新 |
|
||||
| **Fixed(固定)** | `--teacher_model` 设为与 `--model` 相同 | 初始教师权重 | 教师权重固定 |
|
||||
|
||||
**数据格式**
|
||||
|
||||
OPSD 数据集需包含 `teacher_prompt` 列,可通过 `--external_plugins` 加载数据处理插件来构建。以数学推理数据集 `open-r1/OpenThoughts-114k-math` 为例:
|
||||
|
||||
```python
|
||||
from swift.dataset import DatasetMeta, RowPreprocessor, register_dataset
|
||||
|
||||
class OpenThoughtsOPSDPreprocessor(RowPreprocessor):
|
||||
def preprocess(self, row):
|
||||
if not row.get('correct', True):
|
||||
return None
|
||||
problem = row.get('problem', '')
|
||||
solution = row.get('solution', '')
|
||||
teacher_prompt = f'{problem}\n\nReference solution:\n{solution}\n\nNow articulate your own reasoning.'
|
||||
messages = [
|
||||
{'role': 'system', 'content': 'Please reason step by step, and put your final answer within \\boxed{}.'},
|
||||
{'role': 'user', 'content': problem},
|
||||
]
|
||||
return {'messages': messages, 'teacher_prompt': teacher_prompt}
|
||||
|
||||
register_dataset(DatasetMeta(
|
||||
ms_dataset_id='open-r1/OpenThoughts-114k-math',
|
||||
preprocess_func=OpenThoughtsOPSDPreprocessor(),
|
||||
tags=['math', 'opsd'],
|
||||
))
|
||||
```
|
||||
|
||||
**参考脚本**
|
||||
|
||||
- HF:[examples/train/rlhf/opsd/](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/opsd/)
|
||||
- Megatron:[examples/megatron/rlhf/gkd/opsd.sh](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/rlhf/gkd/opsd.sh)
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
|
||||
- Kevin Lu & Thinking Machines Lab. [On-Policy Distillation](https://thinkingmachines.ai/blog/on-policy-distillation/). 2025.
|
||||
- Agarwal et al. [On-Policy Distillation of Language Models (GKD)](https://arxiv.org/pdf/2306.13649). 2023.
|
||||
- Gu et al. [MiniLLM: Knowledge Distillation of Large Language Models](https://arxiv.org/abs/2306.08543). 2023.
|
||||
- DeepSeek-AI. [DeepSeek-V4](https://arxiv.org/html/2606.19348v1). 2026.
|
||||
- Qwen Team. [Qwen3 Technical Report](https://arxiv.org/abs/2505.09388). 2025.
|
||||
- Zhipu AI. [GLM-5: from Vibe Coding to Agentic Engineering](https://arxiv.org/html/2602.15763). 2026.
|
||||
- Kimi Team. [Kimi K2.5: Visual Agentic Intelligence](https://arxiv.org/abs/2602.02276). 2026.
|
||||
@@ -0,0 +1,284 @@
|
||||
# 评测
|
||||
|
||||
SWIFT支持了eval(评测)能力,用于对原始模型和训练后的模型给出标准化的评测指标。
|
||||
|
||||
## 能力介绍
|
||||
|
||||
SWIFT的eval能力使用了魔搭社区[评测框架EvalScope](https://github.com/modelscope/eval-scope),并进行了高级封装以支持各类模型的评测需求。
|
||||
|
||||
> 注意:EvalScope支持许多其他的复杂能力,例如[模型的性能评测](https://evalscope.readthedocs.io/zh-cn/latest/user_guides/stress_test/quick_start.html),请直接使用EvalScope框架。
|
||||
|
||||
目前我们支持了**标准评测集**的评测流程,以及**用户自定义**评测集的评测流程。其中**标准评测集**由三个评测后端提供支持:
|
||||
|
||||
下面展示所支持的数据集名称,若需了解数据集的详细信息,请参考[所有支持的数据集](https://evalscope.readthedocs.io/zh-cn/latest/get_started/supported_dataset/index.html)
|
||||
|
||||
1. Native(默认):
|
||||
|
||||
主要支持纯文本评测,同时**支持**评测结果可视化
|
||||
```text
|
||||
'arc', 'bbh', 'ceval', 'cmmlu', 'competition_math',
|
||||
'general_qa', 'gpqa', 'gsm8k', 'hellaswag', 'humaneval',
|
||||
'ifeval', 'iquiz', 'mmlu', 'mmlu_pro',
|
||||
'race', 'trivia_qa', 'truthful_qa'
|
||||
```
|
||||
|
||||
2. OpenCompass:
|
||||
|
||||
主要支持纯文本评测,暂**不支持**评测结果可视化
|
||||
```text
|
||||
'obqa', 'cmb', 'AX_b', 'siqa', 'nq', 'mbpp', 'winogrande', 'mmlu', 'BoolQ', 'cluewsc', 'ocnli', 'lambada',
|
||||
'CMRC', 'ceval', 'csl', 'cmnli', 'bbh', 'ReCoRD', 'math', 'humaneval', 'eprstmt', 'WSC', 'storycloze',
|
||||
'MultiRC', 'RTE', 'chid', 'gsm8k', 'AX_g', 'bustm', 'afqmc', 'piqa', 'lcsts', 'strategyqa', 'Xsum', 'agieval',
|
||||
'ocnli_fc', 'C3', 'tnews', 'race', 'triviaqa', 'CB', 'WiC', 'hellaswag', 'summedits', 'GaokaoBench',
|
||||
'ARC_e', 'COPA', 'ARC_c', 'DRCD'
|
||||
```
|
||||
|
||||
3. VLMEvalKit:
|
||||
|
||||
主要支持多模态评测,暂**不支持**评测结果可视化
|
||||
```text
|
||||
'COCO_VAL', 'MME', 'HallusionBench', 'POPE', 'MMBench_DEV_EN', 'MMBench_TEST_EN', 'MMBench_DEV_CN', 'MMBench_TEST_CN',
|
||||
'MMBench', 'MMBench_CN', 'MMBench_DEV_EN_V11', 'MMBench_TEST_EN_V11', 'MMBench_DEV_CN_V11',
|
||||
'MMBench_TEST_CN_V11', 'MMBench_V11', 'MMBench_CN_V11', 'SEEDBench_IMG', 'SEEDBench2',
|
||||
'SEEDBench2_Plus', 'ScienceQA_VAL', 'ScienceQA_TEST', 'MMT-Bench_ALL_MI', 'MMT-Bench_ALL',
|
||||
'MMT-Bench_VAL_MI', 'MMT-Bench_VAL', 'AesBench_VAL', 'AesBench_TEST', 'CCBench', 'AI2D_TEST', 'MMStar',
|
||||
'RealWorldQA', 'MLLMGuard_DS', 'BLINK', 'OCRVQA_TEST', 'OCRVQA_TESTCORE', 'TextVQA_VAL', 'DocVQA_VAL',
|
||||
'DocVQA_TEST', 'InfoVQA_VAL', 'InfoVQA_TEST', 'ChartQA_TEST', 'MathVision', 'MathVision_MINI',
|
||||
'MMMU_DEV_VAL', 'MMMU_TEST', 'OCRBench', 'MathVista_MINI', 'LLaVABench', 'MMVet', 'MTVQA_TEST',
|
||||
'MMLongBench_DOC', 'VCR_EN_EASY_500', 'VCR_EN_EASY_100', 'VCR_EN_EASY_ALL', 'VCR_EN_HARD_500',
|
||||
'VCR_EN_HARD_100', 'VCR_EN_HARD_ALL', 'VCR_ZH_EASY_500', 'VCR_ZH_EASY_100', 'VCR_ZH_EASY_ALL',
|
||||
'VCR_ZH_HARD_500', 'VCR_ZH_HARD_100', 'VCR_ZH_HARD_ALL', 'MMDU', 'MMBench-Video', 'Video-MME'
|
||||
```
|
||||
|
||||
## 环境准备
|
||||
|
||||
```shell
|
||||
pip install ms-swift[eval] -U
|
||||
```
|
||||
|
||||
或从源代码安装:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/modelscope/ms-swift.git
|
||||
cd ms-swift
|
||||
pip install -e '.[eval]'
|
||||
```
|
||||
|
||||
## 评测
|
||||
|
||||
支持纯文本评测、多模态评测、url评测、自定义数据集评测四种方式
|
||||
|
||||
**基本示例**
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift eval \
|
||||
--model Qwen/Qwen2.5-0.5B-Instruct \
|
||||
--eval_backend Native \
|
||||
--infer_backend transformers \
|
||||
--eval_limit 10 \
|
||||
--eval_dataset gsm8k
|
||||
```
|
||||
其中:
|
||||
- model: 可指定本地模型路径或者modelscope上的模型ID
|
||||
- eval_backend: 可选 Native, OpenCompass, VLMEvalKit,默认为 Native
|
||||
- infer_backend: 可选 transformers, vllm, sglang, lmdeploy,默认为 transformers
|
||||
- eval_limit: 每个评测集的采样数,默认为None,表示使用全部数据,可用于快速验证
|
||||
- eval_dataset: 评测数据集,可设置多个数据集,用空格分割
|
||||
|
||||
**复杂评测示例**
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift eval \
|
||||
--model Qwen/Qwen2.5-0.5B-Instruct \
|
||||
--eval_backend Native \
|
||||
--infer_backend transformers \
|
||||
--eval_limit 10 \
|
||||
--eval_dataset gsm8k \
|
||||
--eval_dataset_args '{"gsm8k": {"few_shot_num": 0, "filters": {"remove_until": "</think>"}}}' \
|
||||
--eval_generation_config '{"max_tokens": 512, "temperature": 0}' \
|
||||
--extra_eval_args '{"ignore_errors": true, "debug": true}'
|
||||
```
|
||||
|
||||
详细评测的参数列表可以参考[这里](Command-line-parameters.md#评测参数)。
|
||||
|
||||
## 训练中评测
|
||||
|
||||
SWIFT支持在训练过程中使用EvalScope对当前的模型进行评测,以便及时了解模型的训练效果。
|
||||
|
||||
**基本示例**
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift sft \
|
||||
--model "Qwen/Qwen2.5-0.5B-Instruct" \
|
||||
--tuner_type "lora" \
|
||||
--dataset "AI-ModelScope/alpaca-gpt4-data-zh#100" \
|
||||
--torch_dtype "bfloat16" \
|
||||
--num_train_epochs "1" \
|
||||
--per_device_train_batch_size "1" \
|
||||
--learning_rate "1e-4" \
|
||||
--lora_rank "8" \
|
||||
--lora_alpha "32" \
|
||||
--target_modules "all-linear" \
|
||||
--gradient_accumulation_steps "16" \
|
||||
--save_steps "50" \
|
||||
--save_total_limit "5" \
|
||||
--logging_steps "5" \
|
||||
--max_length "2048" \
|
||||
--eval_strategy "steps" \
|
||||
--eval_steps "5" \
|
||||
--per_device_eval_batch_size "5" \
|
||||
--eval_use_evalscope \
|
||||
--eval_dataset "gsm8k" \
|
||||
--eval_dataset_args '{"gsm8k": {"few_shot_num": 0}}' \
|
||||
--eval_limit "10"
|
||||
```
|
||||
|
||||
注意启动命令为`sft`,其中eval相关的参数有:
|
||||
- eval_strategy: 评估策略。默认为None,跟随`save_strategy`的策略
|
||||
- eval_steps: 默认为None,如果存在评估数据集,则跟随`save_steps`的策略
|
||||
- eval_use_evalscope: 是否使用evalscope进行评测,需要设置该参数来开启评测
|
||||
- eval_dataset: 评测数据集,可设置多个数据集,用空格分割
|
||||
- eval_dataset_args: 评测数据集参数,json格式,可设置多个数据集的参数
|
||||
- eval_limit: 评测数据集采样数
|
||||
- eval_generation_config: 评测时模型推理配置,json格式,默认为`{'max_tokens': 512}`
|
||||
|
||||
|
||||
更多评测的样例可以参考[examples](https://github.com/modelscope/ms-swift/tree/main/examples/eval)
|
||||
|
||||
## 自定义评测集
|
||||
|
||||
本框架支持选择题和问答题,两种预定义的数据集格式,使用流程如下:
|
||||
|
||||
*注意:使用自定义评测时,eval_backend参数必须为Native*
|
||||
|
||||
### 选择题格式(MCQ)
|
||||
适合用户是选择题的场景,评测指标为准确率(accuracy)。
|
||||
|
||||
**数据准备**
|
||||
|
||||
准备选择题格式的csv文件,该目录结构如下:
|
||||
|
||||
```text
|
||||
mcq/
|
||||
├── example_dev.csv # (可选)文件名组成为`{subset_name}_dev.csv`,用于fewshot评测
|
||||
└── example_val.csv # 文件名组成为`{subset_name}_val.csv`,用于实际评测的数据
|
||||
```
|
||||
|
||||
其中csv文件需要为下面的格式:
|
||||
|
||||
```text
|
||||
id,question,A,B,C,D,answer
|
||||
1,通常来说,组成动物蛋白质的氨基酸有____,4种,22种,20种,19种,C
|
||||
2,血液内存在的下列物质中,不属于代谢终产物的是____。,尿素,尿酸,丙酮酸,二氧化碳,C
|
||||
```
|
||||
其中:
|
||||
- `id`是序号(可选)
|
||||
- `question`是问题
|
||||
- `A`, `B`, `C`, `D`等是可选项,最大支持10个选项
|
||||
- `answer`是正确选项
|
||||
|
||||
**启动评测**
|
||||
|
||||
运行下面的命令:
|
||||
|
||||
```bash
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift eval \
|
||||
--model Qwen/Qwen2.5-0.5B-Instruct \
|
||||
--eval_backend Native \
|
||||
--infer_backend transformers \
|
||||
--eval_dataset general_mcq \
|
||||
--eval_dataset_args '{"general_mcq": {"local_path": "/path/to/mcq", "subset_list": ["example"]}}'
|
||||
```
|
||||
其中:
|
||||
- `eval_dataset` 需要设置为 `general_mcq`
|
||||
- `eval_dataset_args` 需要设置
|
||||
- `local_path` 自定义数据集文件夹路径
|
||||
- `subset_list` 评测数据集名称,上述 `*_dev.csv` 中的 `*`
|
||||
|
||||
**运行结果**
|
||||
|
||||
```text
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Model | Dataset | Metric | Subset | Num | Score | Cat.0 |
|
||||
+=====================+=============+=================+==========+=======+=========+=========+
|
||||
| Qwen2-0.5B-Instruct | general_mcq | AverageAccuracy | example | 12 | 0.5833 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
```
|
||||
|
||||
## 问答题格式(QA)
|
||||
适合用户是问答题的场景,评测指标是`ROUGE`和`BLEU`。
|
||||
|
||||
**数据准备**
|
||||
|
||||
准备一个问答题格式的jsonline文件,该目录包含了一个文件:
|
||||
|
||||
```text
|
||||
qa/
|
||||
└── example.jsonl
|
||||
```
|
||||
|
||||
该jsonline文件需要为下面的格式:
|
||||
|
||||
```json
|
||||
{"query": "中国的首都是哪里?", "response": "中国的首都是北京"}
|
||||
{"query": "世界上最高的山是哪座山?", "response": "是珠穆朗玛峰"}
|
||||
{"query": "为什么北极见不到企鹅?", "response": "因为企鹅大多生活在南极"}
|
||||
```
|
||||
|
||||
**启动评测**
|
||||
|
||||
运行下面的命令:
|
||||
|
||||
```bash
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift eval \
|
||||
--model Qwen/Qwen2.5-0.5B-Instruct \
|
||||
--eval_backend Native \
|
||||
--infer_backend transformers \
|
||||
--eval_dataset general_qa \
|
||||
--eval_dataset_args '{"general_qa": {"local_path": "/path/to/qa", "subset_list": ["example"]}}'
|
||||
```
|
||||
|
||||
其中:
|
||||
- `eval_dataset` 需要设置为 `general_qa`
|
||||
- `eval_dataset_args` 是一个json字符串,需要设置:
|
||||
- `local_path` 自定义数据集文件夹路径
|
||||
- `subset_list` 评测数据集名称,上述 `*.jsonl` 中的 `*`
|
||||
|
||||
**运行结果**
|
||||
|
||||
```text
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Model | Dataset | Metric | Subset | Num | Score | Cat.0 |
|
||||
+=====================+=============+=================+==========+=======+=========+=========+
|
||||
| Qwen2-0.5B-Instruct | general_qa | bleu-1 | default | 12 | 0.2324 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | bleu-2 | default | 12 | 0.1451 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | bleu-3 | default | 12 | 0.0625 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | bleu-4 | default | 12 | 0.0556 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-1-f | default | 12 | 0.3441 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-1-p | default | 12 | 0.2393 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-1-r | default | 12 | 0.8889 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-2-f | default | 12 | 0.2062 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-2-p | default | 12 | 0.1453 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-2-r | default | 12 | 0.6167 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-l-f | default | 12 | 0.333 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-l-p | default | 12 | 0.2324 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
| Qwen2-0.5B-Instruct | general_qa | rouge-l-r | default | 12 | 0.8889 | default |
|
||||
+---------------------+-------------+-----------------+----------+-------+---------+---------+
|
||||
```
|
||||
@@ -0,0 +1,59 @@
|
||||
# 导出与推送
|
||||
|
||||
|
||||
## Merge LoRA
|
||||
|
||||
- 查看[这里](https://github.com/modelscope/ms-swift/blob/main/examples/export/merge_lora.sh)。
|
||||
|
||||
## 量化
|
||||
|
||||
SWIFT支持AWQ、GPTQ、FP8、BNB模型的量化导出。其中使用AWQ、GPTQ需使用校准数据集,量化性能较好但量化耗时较长;而FP8、BNB无需校准数据集,量化耗时较短。
|
||||
|
||||
| 量化技术 | 多模态 | 推理加速 | 继续训练 |
|
||||
| -------- | ------ | -------- | -------- |
|
||||
| FP8 | ✅ | ✅ | ✅ |
|
||||
| GPTQ | ✅ | ✅ | ✅ |
|
||||
| AWQ | ✅ | ✅ | ✅ |
|
||||
| BNB | ❌ | ✅ | ✅ |
|
||||
|
||||
|
||||
除SWIFT安装外,需要安装以下额外依赖:
|
||||
```shell
|
||||
# 使用awq量化:
|
||||
# autoawq和cuda版本有对应关系,请按照`https://github.com/casper-hansen/AutoAWQ`选择版本
|
||||
# 如果出现torch依赖冲突,请额外增加指令`--no-deps`
|
||||
pip install autoawq -U
|
||||
|
||||
# 使用gptq量化:
|
||||
# auto_gptq和cuda版本有对应关系,请按照`https://github.com/PanQiWei/AutoGPTQ#quick-installation`选择版本
|
||||
pip install auto_gptq optimum -U
|
||||
|
||||
# 使用gptq v2量化:
|
||||
pip install gptqmodel optimum -U
|
||||
|
||||
# 使用bnb量化:
|
||||
pip install bitsandbytes -U
|
||||
```
|
||||
|
||||
我们提供了一系列脚本展现SWIFT的量化导出能力:
|
||||
- 支持[AWQ](https://github.com/modelscope/ms-swift/blob/main/examples/export/quantize/awq.sh)/[GPTQ](https://github.com/modelscope/ms-swift/blob/main/examples/export/quantize/gptq.sh)/[GPTQ v2](https://github.com/modelscope/ms-swift/blob/main/examples/export/quantize/gptq_v2.sh)/[BNB](https://github.com/modelscope/ms-swift/blob/main/examples/export/quantize/bnb.sh)量化导出。
|
||||
- 多模态量化: 支持使用GPTQ和AWQ对多模态模型进行量化,其中AWQ支持的多模态模型有限。参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/export/quantize/mllm)。
|
||||
- 更多系列模型的支持: 支持[Bert](https://github.com/modelscope/ms-swift/tree/main/examples/export/quantize/bert),[Reward Model](https://github.com/modelscope/ms-swift/tree/main/examples/export/quantize/reward_model)的量化导出。
|
||||
- 使用SWIFT量化导出的模型支持使用vllm/sglang/lmdeploy进行推理加速;也支持使用QLoRA继续进行SFT/RLHF。
|
||||
|
||||
|
||||
## 推送模型
|
||||
|
||||
SWIFT支持将训练/量化的模型重新推送到ModelScope/HuggingFace。默认推送到ModelScope,你可以指定`--use_hf true`推送到HuggingFace。
|
||||
```shell
|
||||
swift export \
|
||||
--model output/vx-xxx/checkpoint-xxx \
|
||||
--push_to_hub true \
|
||||
--hub_model_id '<model-id>' \
|
||||
--hub_token '<sdk-token>' \
|
||||
--use_hf false
|
||||
```
|
||||
|
||||
小贴士:
|
||||
- 你可以使用`--model <checkpoint-dir>`或者`--adapters <checkpoint-dir>`指定需要推送的checkpoint目录,这两种写法在推送模型场景没有差异。
|
||||
- 推送到ModelScope时,你需要确保你已经注册了魔搭账号,你的SDK token可以在[该页面](https://www.modelscope.cn/my/myaccesstoken)中获取。推送模型需确保sdk token的账号具有model_id对应组织的编辑权限。推送模型将自动创建对应model_id的模型仓库(如果该模型仓库不存在),你可以使用`--hub_private_repo true`来自动创建私有的模型仓库。
|
||||
@@ -0,0 +1,471 @@
|
||||
# 常见问题整理
|
||||
|
||||
下面是SWIFT使用过程中遇到的一些常见问题。
|
||||
|
||||
## 训练
|
||||
|
||||
SWIFT支持的训练方法包括预训练、指令监督微调、偏好学习、GRPO、Embedding、Reranker、序列分类任务等,详见[主页](https://github.com/modelscope/ms-swift/blob/main/README_CN.md)。
|
||||
|
||||
### Q1: SWIFT支持的模型有哪些?如何设置本地模型路径?
|
||||
支持的模型详见文档[支持的模型和数据集](https://swift.readthedocs.io/zh-cn/latest/Instruction/Supported-models-and-datasets.html)。如果模型已经下载到了本地,设置`--model <path_to_model>`即可。对于离线环境训练,同时设置`--model 本地路径`,`--check_model false`,如果提示git clone相关报错,需要clone repo,然后通过`local_repo_path`指定,详见[命令行参数](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)。从ModelScope下载的模型,可以配置环境变量`MODELSCOPE_CACHE=your_path`将原始的模型存到指定路径;如果用ModelScope SDK下载,通过`cache_dir="本地地址"`;也可以使用`modelscope download`命令行工具或`git`下载,详见modelscope文档[模型下载](https://modelscope.cn/docs/models/download)。如果需要从Hugging Face下载模型,设置环境变量`USE_HF=1`。
|
||||
SWIFT会自动匹配model_type,也可以查看文档[支持的模型和数据集](https://swift.readthedocs.io/zh-cn/latest/Instruction/Supported-models-and-datasets.html),手动指定。
|
||||
|
||||
### Q2: SWIFT支持的数据集有哪些?如何使用自定义数据集?
|
||||
支持的数据集详见文档[支持的模型和数据集](https://swift.readthedocs.io/zh-cn/latest/Instruction/Supported-models-and-datasets.html)。自定义数据集格式及使用方法见文档[自定义数据集](https://swift.readthedocs.io/zh-cn/latest/Customization/Custom-dataset.html),符合这些格式的数据集会自动使用swift内置的数据预处理器。如果与文档中的格式不一致,请自行转换格式,或者参考已支持的数据集接入。若自定义数据集中有额外的字段,这些字段默认不会被使用,可以通过[命令行参数remove_unused_columns](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html#id4)进行设置。
|
||||
需要将数据集下载到本地,然后通过路径指定,请查看[自定义数据集文档](https://swift.readthedocs.io/zh-cn/latest/Customization/Custom-dataset.html#dataset-info-json)。`git clone`下载到本地,然后通过dataset_info.json文件中的`dataset_path`字段指定就行。
|
||||
数据随机详见[命令行参数dataset_shuffle](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)。
|
||||
强制重新下载数据集,设置命令行参数`--download_mode`。对数据集进行错误检查,请设置命令行参数`strict`。需要数据集质检工具时,可以查看另一个库[data-juicer](https://github.com/modelscope/data-juicer)。
|
||||
由于datasets的底层pyarrow对于类型管控比较严格,图像grounding数据集的objects部分、agent数据集的tools部分等,因为这个原因要用str,要不pyarrow就会报错:你每行的类型不一致。
|
||||
训练中遇到报错`AttributeError:’TrainerState’ object has no attribute ’last_model_checkpoint’`,数据集太少了,数据数量不足一个step导致的报错,增加一些数据。另外,切分的验证集数据很少时也会有类似报错。
|
||||
下面是一个assistant字段为空导致的报错:
|
||||
```text
|
||||
File "/mnt/workspace/swift/swift/1lm/dataset/preprocessor/core. py", line 69, in _check_messages raise
|
||||
ValueError(f'assistant_message; {assistant_message}')
|
||||
ValueError: assistant_message: {'role' :'assistant', 'content': ''}
|
||||
```
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 NPROC_PER_NODE=1 MAX_PIXELS=1003520 swift sft --model Qwen/Qwen2.5-VL-7B-Instruct --tuner_type lora --dataset /mnt/workspace/data.json --deepspeed zero2 --max_length 16384
|
||||
```
|
||||
数据集assistant字段为空,如果是推理,把这个空字符串删掉,因为这个会导致训练时NaN,会做检查。
|
||||
|
||||
### Q3: 从缓存加载数据集相关问题
|
||||
设置命令行参数`--load_from_cache_file true`,可以加快数据集加载速度,尤其是在多模态数据集、数据量较大等场景。在debug或修改preprocessor时,设置为false,更多说明请在[命令行参数文档](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)中搜索该参数。
|
||||
|
||||
### Q4: 如何搭建SWIFT环境?有镜像可以使用吗?
|
||||
环境搭建详见[SWIFT安装文档](https://swift.readthedocs.io/zh-cn/latest/GetStarted/SWIFT-installation.html),一些常见依赖的推荐版本可以在[主页](https://github.com/modelscope/ms-swift/blob/main/README_CN.md)上找到。文档中提供了镜像,用`docker run`命令启动容器即可,如:`docker run --gpus all -p 8000:8000 -it -d --name ms modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.9.0-vllm0.13.0-modelscope1.33.0-swift3.12.5 /bin/bash`,启动容器后拉最新代码安装swift。
|
||||
|
||||
### Q5: 多模态模型训练数据格式、参数冻结、优化器设置相关问题
|
||||
多模态模型训练的[例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/multimodal)。支持纯文本、图文数据训练,也可以两种数据混合训练。图像、视频、音频相关的参数,如,最大像素、fps等请查看[特定模型参数](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html#id19)。
|
||||
Grounding任务中通用数据格式支持了一个物体对应多个bbox,参考文档[自定义数据集](https://swift.readthedocs.io/zh-cn/latest/Customization/Custom-dataset.html#grounding)。videos可以是图片列表,使用文件目录的方式。
|
||||
SWIFT按max_pixels对图像进行调整,会保存预处理前和后的图像,然后对bbox进行调整,不过推理没有这样的调整,需要提前手动处理图像。
|
||||
VLM模型训练减少显存使用,请配置`--freeze_vit true`,以及限制最大像素的参数`--max_pixels`。`--freeze_vit`,`--freeze_aligner`,`--freeze_llm`这几个参数详见[命令行参数文档](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html#tuner)。如果ViT没有训练,那有会有warning: none of the inputs have requires_grad=True是正常的,如果训练了,则不应该抛出。
|
||||
使用全参数微调visual encoder同时使用LoRA微调LLM,参考这里[例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/multimodal/lora_llm_full_vit)。
|
||||
|
||||
### Q6: template相关问题
|
||||
由于jinja chat template没有labels,所以不支持训练。
|
||||
多模态数据集如果需要在加载数据之后做动态数据增强,例如,给输入数据随机添加噪声等,请在template中修改encode方法。
|
||||
|
||||
### Q7: SWIFT训练如何debug?
|
||||
详见[预训练与微调文档](https://swift.readthedocs.io/zh-cn/latest/Instruction/Pre-training-and-Fine-tuning.html)。
|
||||
|
||||
### Q8: SWIFT如何使用python脚本训练?
|
||||
参考[notebook例子](https://github.com/modelscope/ms-swift/tree/main/examples/notebook)。
|
||||
|
||||
### Q9: SWIFT如何使用UI界面训练?
|
||||
使用`swift web-ui`命令,界面训练与命令行一致,界面上的参数请查看命令行参数文档。自定义数据集的使用与上面Q2一致。Megatron-SWIFT不支持UI界面训练。
|
||||
|
||||
### Q10: 单机多卡训练相关问题
|
||||
SWIFT多卡训练底层依赖torchrun。`deepspeed` 和 `device_map`不兼容,两个只能选1个。更多细节请查看代码库中的[例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/multi-gpu)。
|
||||
|
||||
### Q11: 多机多卡训练相关问题
|
||||
请查看[多机多卡例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/multi-node)。多机多卡训练时,只有主节点有日志。
|
||||
多机训练速度缓慢,如,使用DeepSpeed ZeRO3训练会出现严重的速度下降,请查看[issue](https://github.com/modelscope/ms-swift/issues/1825)。
|
||||
|
||||
### Q12: 大规模数据集相关问题
|
||||
数据集太大了,然后每次tokenize都需要很久,请使用`lazy_tokenize`或流式读取`streaming`,详见[命令行参数](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)。
|
||||
|
||||
### Q13: 断点续训相关问题
|
||||
先前训练脚本中的参数不变,加上`--resume_from_checkpoint output/xxx/vx-xxx/checkpoint-xxx`,详见[命令行参数](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)。如果数据集发生了改动,仅加载模型,请同时设置`--resume_only_model`。更复杂的场景,请在命令行参数文档中搜索resume。
|
||||
|
||||
### Q14: 数据集流式加载相关问题
|
||||
流式加载`--streaming true`,一边训练一边加载,需要设置max_steps,详见`streaming`参数说明,[命令行参数文档](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html#id4)。
|
||||
注意:streaming是不随机的,也不划分验证集,验证集通过命令行参数`val_dataset`指定。
|
||||
断点续训时,流式只能往前索引,不能随机索引,跳过已经训练的数据耗时特别长,不建议用流式。
|
||||
|
||||
### Q15: packing相关问题
|
||||
packing要和flash_attn一起使用,不然是有误差,attention_mask会出问题。
|
||||
Qwen3.5模型中的linear-attention不支持var_len,不建议开启packing。
|
||||
开启packing,多模态数据会有两次map,map完一次后还会进行第二次mapping,一次是数据集的,一次是template的。如果速度非常慢,可以设置`OMP_NUM_THREADS=14`加速,或者可以把packing去掉,就不会有第二次了。
|
||||
|
||||
### Q16: 数据集多进程处理
|
||||
数据集map过程比较慢时,设置参数`--dataset_num_proc`可以开多进程。多模态数据集map比较慢是正常的。
|
||||
|
||||
### Q17: 当前训练完默认保存多少个checkpoint?
|
||||
默认保存所有的checkpoint,详见[命令行参数 save_total_limit](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)。
|
||||
|
||||
### Q18: 训练过程的loss和acc
|
||||
自定义的损失函数在plugin中加就可以。如果需要不同数据集的loss曲线,请设置`--enable_channel_loss`。
|
||||
如果eval得到的acc和对应保存的ckpt去重新推理一遍计算得到的acc不是一致的,是因为训练时候的eval_acc和推理时候的acc计算方式不一样导致的。`acc_strategy`: 默认为`'token'`, 可选择的值包括: `'token'`,`'seq'`。
|
||||
训练过程中没有token_acc是因为有些模型`logits`和`labels`数量对不上,就不算的。
|
||||
可以在[这里](https://github.com/modelscope/ms-swift/blob/main/swift/loss/mapping.py)查看当前支持的loss或添加新的loss,
|
||||
检查`<image>`等特殊token是否参与损失计算,可以在命令行日志中找一下打印的labels。
|
||||
训练agent时,tool_call就是应该算loss,tool_response不算loss。
|
||||
|
||||
### Q19: 模型参数freeze相关问题
|
||||
训练的过程中,冻结某些层时导致某些参数未参与梯度回传,请配置参数`--ddp_find_unused_parameters true`。
|
||||
freeze_parameters和freeze_vit/freeze_aligner/freeze_llm:先freeze parameters再active parameters。`freeze vit/freeze aligner/freeze llm`这三个参数会对freeze parameters 和trainable parameters进行调整.因为有些模型的ViT中包含`aligner`,所以会将`aligner`单独加入trainable_parameters。
|
||||
freeze_parameters_ratio这个参数的机制是从embedding开始从下往上freeze。
|
||||
|
||||
### Q20: 序列并行相关问题
|
||||
序列并行支持pt, sft, dpo and grpo。参考这个例子[sequence_parallel](https://github.com/modelscope/ms-swift/tree/main/examples/train/sequence_parallel)。
|
||||
VLM模型的目前仅支持flash-attn,纯文本支持flash-attn和sdpa。
|
||||
sequence parallel可以和Liger kernel同时使用。
|
||||
sequence parallel和自定义loss冲突时,由于sequence parallel在自己的代码中定制了loss,可以自己改下[这里](https://github.com/modelscope/ms-swift/blob/main/swift/trainers/sequence_parallel/ulysses.py)。
|
||||
|
||||
### Q21: 扩充词表
|
||||
用SWIFT框架扩充词表需要设置命令行参数`new_special_tokens`,`--modules_to_save embed_tokens lm_head`,详见[例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/new_special_tokens)。
|
||||
|
||||
### Q22: tuners相关问题
|
||||
SWIFT中的LlamaPro对多模态做了适配。
|
||||
LongLoRA只有LLaMA系列模型能用。
|
||||
LoRA训练和`--trainable_parameters`参数不兼容,LoRA模块之外其他的可训练参数用modules_to_save。
|
||||
|
||||
### Q23: embedding/reranker训练
|
||||
[embedding训练例子](https://github.com/modelscope/ms-swift/blob/main/examples/train/embedding)。
|
||||
[reranker训练例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/reranker)。
|
||||
数据格式见[自定义数据集](https://swift.readthedocs.io/zh-cn/latest/Customization/Custom-dataset.html)。
|
||||
|
||||
### Q24: 分类任务训练
|
||||
SWIFT支持多标签分类,自定义数据集文档有数据格式,在命令行参数文档中搜索`problem_type`,其他和回归是一样的。
|
||||
注意:label字段和message字段同级。
|
||||
|
||||
### Q25: thinking模型训练
|
||||
查看这个[issue](https://github.com/modelscope/ms-swift/issues/4030)。
|
||||
|
||||
### Q26: 想问一下,SWIFT支持蒸馏吗?
|
||||
参考这个[例子](https://github.com/modelscope/ms-swift/blob/main/examples/sampler/distill/distill.sh)。
|
||||
|
||||
### Q27: gkd训练student model和teacher model的model_type需要一致吗,一个dense一个moe可以吗?
|
||||
可以的,只需要词表一样,不过带MoE就会比较慢。
|
||||
|
||||
### Q28: GRPO训练相关问题
|
||||
SWIFT现在支持多模态的GRPO训练。GRPO训练过程中loss接近0是正常情况,参考[issue](https://github.com/huggingface/open-r1/issues/239#issuecomment-2646297851)。
|
||||
设置sleep_mode,推理结束VllmEngine释放显存。下次调用时,再加载,而不是一直占用。
|
||||
GRPO训练时不想引入KL项,可以通过命令行参数beta设置。
|
||||
LoRA微调后继续做GRPO训练,请在命令行参数文档中搜索`--adapters`。
|
||||
由于算entropy会有额外的一点开销,所以默认没有记录熵曲线。如果需要,请设置`--log_entropy true`,
|
||||
colocate模式不支持use_async_engine。
|
||||
GRPO不支持channel_loss。
|
||||
Liger kernel和padding free没法在GRPO阶段一起开。如果一起开,需要改liger grpo loss的实现,在liger kernel库中,不方便改。
|
||||
如果训练集有不同的task,请查看[多任务训练](https://swift.readthedocs.io/zh-cn/latest/Instruction/GRPO/DeveloperGuide/multi_task.html)。
|
||||
|
||||
### Q29: reward函数(模型)相关问题
|
||||
reward_model和reward_funcs可以一起使用。
|
||||
自定义reward函数参考[examples/train/grpo/plugin](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin)。
|
||||
针对math问题,要从数据集里面传solution,不然不好算accuracy。
|
||||
如果在ORM的自定义奖励函数中需要传入数据集中的某个列,请将该列放到messages之外的其他列。
|
||||
在GRPO训练的过程中如果需要指定一个llm-judge模型来做打分,请参考奖励模型的文档。
|
||||
|
||||
### Q30: rollout相关问题
|
||||
Rollout应该是不兼容pipeline parallel。
|
||||
vLLM推理引擎默认trust_rwmote_code为true。
|
||||
|
||||
### Q31: 请教一个问题,grpo脚本中的save_steps指的是step还是global step?目前本地训练显示的global step是18, wandb上显示的step是628。
|
||||
`global_step`,本地tqdm显示的。
|
||||
|
||||
### Q32: 默认只用 num_iterations=1 的话,clip 就失去作用了吧?dapo 的 clip higher 也没用。我看 veRL 有个 micro batch 可以设置单轮小批次更新 policy model 来使得 clip 项生效,ms-swift 的 mini batch 看源码貌似只是做了梯度累加?
|
||||
是的,需要num_iterations>1。
|
||||
|
||||
### Q33: 请问gspo训练支持传入参数top_entropy_quantile吗?传入了--importance_sampling_level sequence后,还能实现对熵分布前x%的token的优化吗?
|
||||
支持,顺序是先正常计算loss(受importance_sampling_level影响),再根据top_entropy_quantile mask掉loss。
|
||||
|
||||
### Q34: GRPO文档中的faq
|
||||
更多GRPO相关的FAQ,请查看[GRPO文档](https://swift.readthedocs.io/zh-cn/latest/Instruction/GRPO/GetStarted/GRPO.html#faq)
|
||||
|
||||
### Q35: ppo等偏好训练相关问题
|
||||
PPO训练不支持梯度裁剪。
|
||||
目前PPO还只支持RM和policy是同一系列的模型(tokenizer/template)。
|
||||
不支持多轮的DPO。
|
||||
|
||||
### Q36: MoE模型训练相关问题
|
||||
MoE模型LoRA训练,如果aux-loss基本没变化,将all-router也加到target_modules。
|
||||
LoRA训练中,路由器模块是否参与训练看gate是否是nn.Linear实现,如果是nn.Parameter就不训练,详见命令行参数[target_parameters](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html#tuner)。
|
||||
|
||||
### Q37: Megatron-SWIFT训练相关问题
|
||||
Checkpoint保存,参考命令行参数[save_strategy](https://swift.readthedocs.io/zh-cn/latest/Megatron-SWIFT/Command-line-parameters.html)。
|
||||
Megatron多机训练时,因为pp并行只有在pp last rank有完整的信息, 日志在last rank打印,而不是从master node打印。
|
||||
Megatron-SWIFT支持了save_total_limit,支持了SwanLab监控训练,详见[Megatron-SWIFT命令行参数文档](https://swift.readthedocs.io/zh-cn/latest/Megatron-SWIFT/Command-line-parameters.html)
|
||||
ViT用的是transformers的模型结构,目前没有并行,训练遇到OOM时降低`decoder_first_pipeline_num_layers`。
|
||||
Megatron-SWIFT支持新的模型,目前没有教程,请查看新增模型的PR。
|
||||
sequence_parallel的并行数等于tp数。
|
||||
FP8训练支持block wise,参考[examples/megatron/fp8例子](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/fp8)。
|
||||
|
||||
### Q38: 请问Megatron-SWIFT如何配置断点续训?
|
||||
配置`--mcore_model`加载checkpoint,另外根据需要配置这几个参数,`--finetune`,`--no_load_optim`,`--no_load_rng`。如果是LoRA断点续训,配置`--mcore_adapter`,其他同全参数训练,详见[Megatron-SWIFT命令行参数文档](https://swift.readthedocs.io/zh-cn/latest/Megatron-SWIFT/Command-line-parameters.html)。
|
||||
|
||||
### Q39: mtp相关问题
|
||||
需要MTP训练,请设置命令行参数`mtp_num_layers`。
|
||||
如果base模型不附带MTP结构,可以从头初始化训练MTP。
|
||||
多模态的MTP目前还没支持。
|
||||
|
||||
### Q40: 有个关于Megatron GKD的问题请教,如果teacher是Qwen3-235B,student是Qwen3-30BA3B,之前SFT 235B都是pp8然后decoder fist和decoder last设为11。如果我在GKD的时候也设置decoder first last,会不会影响student的并行?
|
||||
现在两个模型的并行参数是共用的,不同并行的设置会在v4版本后支持。
|
||||
|
||||
### Q41: 量化模型训练相关问题
|
||||
QLoRA微调参考[例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/qlora)。
|
||||
量化模型不能全参数微调,GPTQ模型的int型参数无法参与求导,只能附着LoRA等额外结构参与更新。
|
||||
QLoRA训练后的模型merge参考[QLoRA例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/qlora)。
|
||||
Megatron-SWIFT不支持QLoRA训练。
|
||||
|
||||
### Q42: 一些特殊模型的训练
|
||||
SWIFT目前不支持MiniCPM-O使用音频模态输入的训练。
|
||||
微调DeepSeek-VL-2,transformers用4.42以前的版本,`peft==0.11.*`。
|
||||
Moonlight-16B-A3B-Instruct微调。因为模型文件中禁止了训练, 参考DeepSeek-VL-2的解决方案,issue中搜索。
|
||||
微调Ovis2这个模型有点特殊,需要padding到max_length。设置一下`--max_length`。
|
||||
Qwen2.5-Omni目前不支持talker训练,只有thinker。
|
||||
Qwen2-Audio的sft不支持packing。
|
||||
|
||||
### Q43: 请问在不支持flash attention的设备上attention implemation默认是什么呢?文档中默认是none
|
||||
默认使用sdpa。
|
||||
|
||||
### Q44: 请问默认模型训练都是left padding是吧?
|
||||
训练可以选择使用左padding还是右padding。默认是右padding, `batch infer`都是左padding。
|
||||
|
||||
### Q45: 请问下MoE的参数有哪些,参数表里关键字搜索不到?专家数量,专家路由这些参数怎么设置?
|
||||
直接用config.json中的参数。
|
||||
|
||||
### Q46: SWIFT能够支持设置最小的learning rate吗,感觉最后减到太小了
|
||||
可以设置,`--lr_scheduler_type cosine_with_min_lr --lr_scheduler_kwargs '{"min_lr": 1e-6}'`。
|
||||
|
||||
### Q47: 目前支持用yaml文件配置grpo和sft吗?
|
||||
都支持的,该配置是在main.py中直接处理成命令行。
|
||||
|
||||
### Q48: 请问现在是不支持use_liger_kernel和log_entropy一起用吗?
|
||||
不支持。
|
||||
|
||||
### Q49: 请问下,遇到这个报错,怎么处理?安装了apex也不行
|
||||
```text
|
||||
RuntimeError: ColumnParallelLinear was called with gradient_accumulation_fusion set to True but the custom CUDA extension fused_weight_gradient_mlp_cuda module is not found. To use gradient_accumulation_fusion you must install APEX with --cpp_ext and --cuda_ext. For example: pip install --global-option="--cpp_ext" --global-option="--cuda_ext ." Note that the extension requires CUDA>=11. Otherwise, you must turn off gradient accumulation fusion.
|
||||
```
|
||||
设置一下`--gradient_accumulation_fusion false`。
|
||||
|
||||
### Q50: 几个任务一起finetune vlm,不同任务视频采样规则不一致,ms-swift是否支持?在哪里配置?
|
||||
[命令行参数文档](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)看下`interleave_prob`。
|
||||
|
||||
### Q51: 想问一个问题,多模态packing预训练每次pytorch allocator cache flushes since last step后,显存使用好像就会增长一点,步数多了容易oom
|
||||
加个环境变量`PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True'`。
|
||||
|
||||
### Q52: use_logits_to_keep 现在多模态大模型上可以用吗?
|
||||
如果多模态token的展开在模型的forward内会报错。
|
||||
|
||||
### Q53: 请问一下为什么训练到会有好几次显存大幅度增加,已经50step或者100step
|
||||
设置环境变量`PYTORCH_CUDA_ALLOC_CONF`,具体查看PyTorch文档。
|
||||
|
||||
### Q54: 从qwen base模型微调成chat模型有没有实践文档,有什么要特别配置的吗?
|
||||
`swift sft`,没有其他需要特别配置的,参考[例子](https://github.com/modelscope/ms-swift/tree/main/examples/train/base_to_chat)。
|
||||
|
||||
### Q55: 模型训练后,回复重复了很多内容
|
||||
参考[预训练与微调](https://swift.readthedocs.io/zh-cn/latest/Instruction/Pre-training-and-Fine-tuning.html)。如果训练过程中出现重复的情况,请多训练几个epoch, 清洗数据, 全参数训练, 采用RLHF的方式缓解。
|
||||
|
||||
### Q56: 请问为什么 --torch_dtype float16 (卡不能使用bf16)会出现报错:lib/python3.12/site-packages/torch/amp/grad_scaler.py", line 260, in _unscale_grads_ raise ValueError("Attempting to unscale FP16 gradients.") ValueError: Attempting to unscale FP16 gradients.
|
||||
全参数,不能fp16训练的。
|
||||
|
||||
### Q57: 请问下,lora参数合并报错,目前peft是0.11.0,这个是因为peft版本需要升级吗
|
||||
```text
|
||||
File "/opt/conda/lib/python3.9/site-packages/peft/config.py", line 118, in from_peft_type
|
||||
return config_cls(**kwargs)
|
||||
TypeError: __init__() got an unexpected keyword argument 'corda_config'
|
||||
```
|
||||
训练和合并的peft版本不一致导致的。
|
||||
|
||||
### Q58: 请问这个问题如何解决?safetensors_rust.SafetensorError: Error while deserializing header: HeaderTooLarge
|
||||
磁盘空间不足了,模型没有保存完整。
|
||||
|
||||
### Q59: 这个错误为什么会出现在这,numpy.object找不到在哪?
|
||||
`numpy==1.26.3`,尝试一下。
|
||||
|
||||
### Q60: unsloth训练,报错:assert(type(target modules) in (list,tuple,))。配置的参数是--target modules all-linear
|
||||
别用`all-linear`,改为具体的模块列表,比如`--target_modules q k v`。
|
||||
|
||||
### Q61: 请问对于qwen2.5-omni来说--freeze_vit false意味这视觉编码器和音频编码器都打开了,有什么办法可以只打开音频编码器不打开视觉编码器吗?
|
||||
`--target_regex`写一下。
|
||||
|
||||
## 推理
|
||||
|
||||
SWIFT支持python脚本、命令行、ui界面推理,详见[推理和部署](https://swift.readthedocs.io/zh-cn/latest/Instruction/Inference-and-deployment.html)。
|
||||
|
||||
### Q1:SWIFT推理如何设置模型?
|
||||
如果是全参数训练的模型、LoRA训练后合并的模型或者从model hub下载的模型,设置命令行参数`--model <model_id_or_path>`;LoRA训练后未合并的模型,用`--adapters`设置,同时可通过`--model`指定基模路径。
|
||||
|
||||
### Q2: SWIFT如何使用数据集进行推理?推理结果保存在哪儿?
|
||||
`--val_dataset <your-val-dataset>`,指定数据集。对于训练后的模型也可以设置参数`--load_data_args true`。推理结果保存路径通过`--result_path your_path`设置,日志中会打印路径。详见文档[命令行参数](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)。
|
||||
如果需要保留推理数据集中额外的字段,请设置`--remove_unused_columns false`。
|
||||
|
||||
### Q3: SWIFT如何设置批量推理?
|
||||
如果infer_backend为`transformers`,设置命令行参数`--max_batch_size 16`,或[python脚本](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo.py)。这里max_batch_size指的是每张卡上的batch_size。
|
||||
|
||||
### Q4: SWIFT如何设置流式推理?
|
||||
`--stream true`,此时推理结果将逐条写入jsonl文件。需要注意的是,流式推理不支持ddp。
|
||||
|
||||
### Q5: vLLM和SGLang推理后端相关的问题
|
||||
对于LoRA训练的模型,请查看vLLM和SGLang文档,如果支持LoRA推理则不需要合并。此外,SGLang推理目前不支持多模态。
|
||||
|
||||
### Q6: 生成参数相关的问题
|
||||
temperature等参数默认从generation_config.json中读取。设置`--temperature 0`或者`--top_k 1`可以取消推理随机性。
|
||||
|
||||
### Q7: 如何将system_prompt置空?命令行不设置system参数,但是它会加上默认的system。
|
||||
设置`--system ''`。
|
||||
|
||||
### Q8: 推理时如何计算acc/rouge等指标?
|
||||
参考[推理参数metric](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html#id14)。
|
||||
|
||||
### Q9: 模型推理的时候如果需要在特定前缀下继续推理的话是设置哪个参数?
|
||||
参数`--response_prefix`。
|
||||
|
||||
### Q10: 数据answer里面已经包含了部分prompt,希望补全answer,应该怎么修改inference?
|
||||
```text
|
||||
{"messages": [{"role": "system", "content": "<system>"}, {"role": "user", "content": "<query1>"}, {"role": "assistant", "content": "answer1, "}]}
|
||||
```
|
||||
参考[examples/infer/demo_agent](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_agent.py)。
|
||||
|
||||
### Q11: 多模态模型推理时如何限制最大像素,以减少显存占用?
|
||||
设置命令行参数`--max_pixels xxx`、环境变量`MAX_PIXELS=xxx`、或特定模型参数`--model_kwargs '{"max_pixels": xxx}'`,其中环境变量仅对文档中对应的模型生效,详见文档[特定模型参数](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html#id18)。
|
||||
|
||||
### Q12: SWIFT推理如何输出概率值logprobs参数?
|
||||
命令行推理设置`--logprobs true`,python脚本推理设置`request_config = RequestConfig(..., logprobs=True, top_logprobs=2)`,参考[test_logprobs.py](https://github.com/modelscope/ms-swift/blob/main/tests/infer/test_logprobs.py)。
|
||||
|
||||
### Q13: SWIFT推理如何输出last_hidden_state?
|
||||
没有例子,可以参考GRPO trainer的`_get_last_hidden_state`方法。
|
||||
|
||||
### Q14: transformers,vllm,ollama等推理结果不一致问题
|
||||
SWIFT的template是对齐transformers的。检查推理参数是否对其。此外,VllmEngine和TransformersEngine是有差异的。
|
||||
|
||||
### Q15: embedding/reranker模型推理
|
||||
embedding模型推理参考这里的[例子](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_embedding.py)。reranker模型推理参考这里的[例子](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_reranker.py)。
|
||||
|
||||
### Q16: 请问在使用python脚本推理时,如何使用cpu?
|
||||
设置环境变量,`os.environ['CUDA_VISIBLE_DEVICES'] = '-1'`。
|
||||
|
||||
### Q17: 使用swift infer命令进行推理,支持多机推理吗?
|
||||
如果单节点放得下模型,外面封装k8s就行。如果单节点放不下那就不支持。
|
||||
|
||||
### Q18: swift sample的时候,好像不支持batch?好像是for循环一个个例子sample,有点慢
|
||||
有一个[脚本](https://github.com/modelscope/ms-swift/blob/main/examples/train/rft/rft.py),可以用多进程对数据集拆分采样。
|
||||
|
||||
### Q19: 特殊模型依赖版本相关问题
|
||||
Qwen2-Audio推理结果出现混乱,请使用transformers4.48。
|
||||
transformers4.55.2训练的LoRA不能使用小于4.52的版本加载了,详见[issue#5440](https://github.com/modelscope/ms-swift/issues/5440)。
|
||||
swift对不同版本的qwen-vl-utils做了兼容,使用qwen2.5-vl和qwen3-vl模型时不需要切换该依赖版本。
|
||||
|
||||
### Q20: 报错,safetensors_rust.SafetensorError: Error while deserializing header:MetadataIncompleteBuffer
|
||||
模型权重损坏了。
|
||||
|
||||
### Q21: vLLM 报错 `ValueError: the decoder prompt contains a(n) video item with length 16758, which exceeds the pre-allocated encoder cache size 16384. please reduce the input size or increase the encoder cache size by setting --limit-mm-per-prompt at startup.`
|
||||
这通常是多模态输入过长,超过了 vLLM 预分配的 encoder cache size 导致的。可以通过 `--limit-mm-per-prompt` 调整 encoder cache size。另一个可行的解决方法是增大 `max_num_batched_tokens`,在 Swift cli 中传入:
|
||||
```shell
|
||||
--vllm_engine_kwargs '{"max_num_batched_tokens": 20000}'
|
||||
```
|
||||
|
||||
## 导出
|
||||
|
||||
### Q1: autoawq相关的报错
|
||||
如果推理没有涉及AWQ量化模型,但出现了autoawq相关的报错,可以尝试卸载autoawq再进行推理。不支持AWQ量化的模型,尝试用GPTQ进行量化。
|
||||
|
||||
### Q2: SWIFT量化模型时,一张卡上放不下模型的情况
|
||||
尝试设置`--device_map cpu`。或者多卡加载模型,单卡量化。
|
||||
|
||||
### Q3: 想问一下用swift export对qwen2.5 72B模型进行gptq int4量化,max model length=32768用的是默认值,给的校准数据集有128个样本,但是量化的时候报错了,报错日志是:factorization could not be completed because the input is not positive-definite(the leading minor of order 18145 is not pisitive-definite)。是什么原因?
|
||||
海森矩阵不正定的问题,试试其他的数据集。
|
||||
|
||||
### Q4: swift export的时候传入自定义的template_type,是不是就可以永久改掉template_type了?如果swift export --template_type 自定义,是不是就可以把模型对应的template改掉
|
||||
不会被修改,swift中的template是定义在swift内部的,不是以jinja方式保存的。
|
||||
|
||||
### Q5: 模型训练完能直接转gguf格式吗?
|
||||
目前只支持导出ModelFile,详见文档[命令行参数](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html)。
|
||||
|
||||
## 部署
|
||||
|
||||
### Q1: SWIFT部署如何设置模型?
|
||||
同上面推理Q1。
|
||||
|
||||
### Q2: SWIFT如何进行多卡部署?
|
||||
详见[例子](https://github.com/modelscope/ms-swift/tree/main/examples/deploy)。如果是transformers engine,不支持DDP,不能多卡部署。此外,不支持异构部署,如不同型号的显卡、各显卡设置不同的存储占比等。
|
||||
|
||||
### Q3: 通过--system参数指定system prompt与数据集中每个数据前加system prompt以及template的system prompt是不是有一个就行?这些方式对模型来说,是不是一样的?
|
||||
system优先级:数据集中的>命令行的>template中默认的。
|
||||
|
||||
### Q4: 客户端多模态输入相关问题
|
||||
客户端传入图片、音频等,见[客户端例子](https://github.com/modelscope/ms-swift/tree/main/examples/deploy/client/mllm)。
|
||||
如果图片url非法,可以设置请求的超时时间,环境变量`SWIFT_TIMEOUT`,或者`InferClient`中可以传参数。
|
||||
|
||||
### Q5: 生成参数设置相关问题
|
||||
temperature等参数推理只能启动前设置,部署可以在启动时设置默认值,之后在客户端继续设置,覆盖默认值。
|
||||
|
||||
### Q6: SWIFT部署的模型怎么设置流式生成?
|
||||
客户端控制的,查看[examples/deploy/client](https://github.com/modelscope/ms-swift/tree/main/examples/deploy/client)。
|
||||
|
||||
### Q7: SWIFT部署如何输出token的概率?
|
||||
服务端设置`--logprobs true`,要客户端传参数,`request_config = RequestConfig(..., logprobs=True, top_logprobs=2)`。
|
||||
|
||||
### Q8: 部署模型时,thinking相关问题
|
||||
如果需要禁止思考,目前只能在swift deploy启动的时候禁止thinking。查看这个[issue](https://github.com/modelscope/ms-swift/issues/4030)。
|
||||
|
||||
### Q9: 部署时,设置什么参数可以实现一次输出多个结果?
|
||||
`RequestConfig`参数`n`。
|
||||
|
||||
### Q10: SWIFT部署,指定--infer_backend vllm,和直接使用vllm部署相关问题
|
||||
如果两者推理结果相差较多,可能是template没对齐。如果推理速度相差较多,可能是图像分辨率不一致。swift默认使用V1 engine,可以通过环境变量`VLLM_USE_V1=1`控制。
|
||||
|
||||
### Q11: 特殊模型和依赖版本相关问题
|
||||
如果遇到报错没有“model.language_model.embed_tokens.weight”,训练前后的transformers版本不一致。
|
||||
qwen2.5使用fp16推理如果遇到返回乱码,尝试bf16。
|
||||
|
||||
### Q12: 有个问题想问一下,qwen2-7b部署后使用客户端时,调用openai的api要使用client.completions.create,不能使用client.chat.completions.create,但是使用qwen2-7b-instruct-q5_k_m.gguf的时候可以使用client.chat.completions.create,这是为什么呀?
|
||||
base模型可以用client.chat.completions.create的,不过这个是兼容行为。
|
||||
|
||||
## 评测
|
||||
|
||||
### Q1: SWIFT支持的评测集有哪些?以及如何使用自定义评测集?
|
||||
标准评测集和用户自定义评测集的使用详见文档[评测](https://swift.readthedocs.io/zh-cn/latest/Instruction/Evaluation.html)。
|
||||
|
||||
### Q2: 官方支持的评测数据集手动下载后,swift eval能配置本地路径评测吗?
|
||||
离线评测请参考EvalScope文档[快速上手](https://evalscope.readthedocs.io/zh-cn/latest/get_started/basic_usage.html)
|
||||
|
||||
### Q3: eval微调后的模型,总是会在固定的百分比停掉,但是vllm服务看着一直是有在正常运行的。模型越大,断开的越早。
|
||||
`SWIFT_TIMEOUT`环境变量设置为-1。
|
||||
|
||||
### Q4: 评估的时候可不可以控制数据集条数?评估一个mmlu需要一个多小时,也太慢了。
|
||||
配置参数`--eval_limit`,这里的`--eval_limit`是控制了每个subset的条数,比如mmlu有50多个subset,每个limit10条,那就是500多条。
|
||||
|
||||
### Q5: 问一下评估swift eval里,模型最多生成1024token就结束了,这个如何修改?设置--max_new_tokens 5000,看起来没起作用
|
||||
查看命令行参数[eval_generation_config](https://swift.readthedocs.io/zh-cn/latest/Instruction/Command-line-parameters.html#id16)
|
||||
|
||||
### Q6: 请教一下,想使用OpenCompass的后端评测,如何从本地加载下载好的数据集?
|
||||
OpenCompass后端不支持设置`data_args`。
|
||||
|
||||
### Q7: swift eval 来评估模型,--eval_backend OpenCompass不支持自定义数据集吗?
|
||||
```text
|
||||
ValueError: eval_dataset: /mnt/workspace/data.jsonl is not supported.
|
||||
eval_backend: OpenCompass supported datasets: ['C3', 'summedits', 'WiC', 'csl', 'lambada', 'mbpp', 'hellaswag', 'ARC_e', 'math', 'nq', 'race', 'MultiRC', 'cmb', 'ceval', 'GaokaoBench', 'mmlu', 'winogrande', 'tnews', 'triviaqa', 'CB', 'cluewsc', 'humaneval', 'AX_g', 'DRCD', 'RTE', 'ocnli_fc', 'gsm8k', 'obqa', 'ReCoRD', 'Xsum', 'ocnli', 'WSC', 'siqa', 'agieval', 'piqa', 'cmnli', 'cmmlu', 'eprstmt', 'storycloze', 'AX_b', 'afqmc', 'strategyqa', 'bustm', 'BoolQ', 'COPA', 'ARC_c', 'PMMEval', 'chid', 'CMRC', 'lcsts']
|
||||
```
|
||||
OpenCompass不支持自定义数据集,用native可以自定义模式。
|
||||
|
||||
### Q8: evalscope原生是可以生成报告的,其他后端如opencompass是不支持生成报告可视化是吗?
|
||||
目前只支持native的可视化,其他后端还不支持。
|
||||
|
||||
### Q9: 请问一下评测ifeval报这个错是什么原因?
|
||||
```text
|
||||
[Errno 20] Not a directory: '/root/nltk_data/tokenizers/punkt_tab.zip/punkt_tab/english/collocations.tab'
|
||||
```
|
||||
解压这个文件,`unzip /path/to/nltk_data/tokenizers/punkt_tab.zip`。
|
||||
|
||||
### Q10: 请问评测时eval_backend='OpenCompass',怎么指定离线数据集路径?
|
||||
查看[数据准备教程](https://evalscope.readthedocs.io/zh-cn/latest/user_guides/backend/opencompass_backend.html#id3),下载数据集并解压。不用指定`dataset-args`,将数据集文件夹(即data文件夹)放置在当前工作路径下即可。
|
||||
|
||||
### Q11: 用evalscope报这个错是什么原因?
|
||||
```text
|
||||
unzip: cannot find or open /root/nltk_data/tokenizers/punkt_tab.zip, /root/nltk_data/tokenizers/punkt_tab.zip.zip or /root/nltk_data/tokenizers/punkt_tab.zip.ZIP
|
||||
```
|
||||
这是在下载nltk的依赖,手动下载[punkt_tab.zip](https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/open_data/nltk_data/punkt_tab.zip),解压到`~/nltk_data/tokenizers`下面。
|
||||
|
||||
### Q12: 为啥纯文本没问题,测多模态我们指定路径了,但他还是检测不到数据集,会去下载?
|
||||
VLMEvalKit流程跟native不一样,会自己下载数据放到`~/LMUData/`下面,详见[文档](https://evalscope.readthedocs.io/zh-cn/latest/user_guides/backend/vlmevalkit_backend.html#id2)。
|
||||
|
||||
### Q13: 请问一下swift eval做benchmark评测的时候,是否可以指定llm作为judge, 参数应该怎么传进去?
|
||||
支持,使用swift得从`extra_eval_args`去传递`judge-model-args`参数,包括`api_key,api_url,model_id`,整体是一个json字符串。
|
||||
|
||||
### Q14: 请问在执行eval的时候出现了多卡显存分配不均是什么原因?
|
||||
```shell
|
||||
NPROC_PER_NODE=8
|
||||
ASCEND_RT_VISIBLE_DEVICES=0,1,2,3,4,5,6,7\ MAX_PIXELS=802816\ swift eval\
|
||||
--model "$MODEL_PATH” \$EXTRA_ARGS \
|
||||
--eval_backend Native \ --infer_backend transformers\ --device_map auto \
|
||||
--eval_limit"$EVAL_LIMIT"\ --eval_dataset general_qa\
|
||||
--dataset_args "{\"general_qa\": {\"local_path\": \"${DATA_PATH}\", \"subset_list\": [\"${SUBSET_NAME}\"]}}" \ --host 127.0.0.1\> "$LOG_FILE" 2>&1
|
||||
```
|
||||
swift eval不支持DDP方式启动。
|
||||
|
||||
### Q15: 请问哪里可以看到swift评测的时候送入的query除了问题之外还有哪些额外的字段呢?
|
||||
最简单的方法是看输出的reviews文件中的input字段,是输入给模型的内容转换后的Markdown格式。如果用backend是opencompass的话没有这些,需要用native backend。
|
||||
|
||||
ms-swift的eval能力使用了魔搭社区评测框架EvalScope, 复杂能力请直接使用[EvalScope框架](https://evalscope.readthedocs.io/zh-cn/latest/get_started/introduction.html)。
|
||||
@@ -0,0 +1,63 @@
|
||||
# On-Policy RL Meets Off-Policy Experts: Harmonizing SFT and RL via Dynamic Weighting (CHORD)
|
||||
|
||||
本文档介绍论文 [On-Policy RL Meets Off-Policy Experts: Harmonizing SFT and RL via Dynamic Weighting](https://arxiv.org/abs/2508.11408) 中提出的 CHORD 算法。CHORD 的核心思想是在强化学习过程中,动态融合专家数据(SFT),通过 全局权重 μ + token 级别权重 φ 的双重控制机制,在模仿与探索之间实现平衡。
|
||||
|
||||
## 算法概述
|
||||
CHORD 算法通过在 GRPO loss 中引入 **SFT loss**,实现动态混合训练。总体目标函数为:
|
||||
|
||||
$$
|
||||
\mathcal{L}_{\text{CHORD}} = (1 - \mu) \cdot \mathcal{L}_{\text{GRPO}} + \mu \cdot \mathcal{L}_{\text{SFT}}
|
||||
$$
|
||||
|
||||
其中:
|
||||
- $\mathcal{L}_{\text{GRPO}}$:基于 on-policy 采样的强化学习损失(类似 PPO)。
|
||||
- $\mathcal{L}_{\text{SFT}}$:监督微调损失。
|
||||
- $\mu \in [0, 1]$:全局平衡系数,控制 SFT 信号在总梯度中的贡献。
|
||||
|
||||
### 参数配置(数据与批量大小)
|
||||
我们可以基于 GRPO 训练实现 CHORD 训练。
|
||||
|
||||
CHORD 需要在训练时指定额外的 SFT 数据集和批量大小:
|
||||
- `chord_sft_dataset`: 用于提供专家数据的 SFT 数据集。
|
||||
- `chord_sft_per_device_train_batch_size`: 每个设备的 SFT mini-batch 大小。
|
||||
|
||||
---
|
||||
|
||||
## 两种 CHORD 变体
|
||||
|
||||
论文提出了两种算法变体:**CHORD-µ** 和 **CHORD-ϕ**。
|
||||
|
||||
### CHORD-µ
|
||||
通过在训练过程中逐步 **衰减 μ**,实现从模仿专家到自主探索的过渡。
|
||||
|
||||
**参数:**
|
||||
- `chord_mu_peak`:μ 的峰值。
|
||||
- `chord_mu_valley` μ 的衰减终值。
|
||||
- `chord_mu_warmup_steps` μ 值上升至峰值的训练步数。
|
||||
- `chord_mu_decay_steps` μ 从峰值衰减到谷值的训练步数。
|
||||
|
||||
### CHORD-ϕ(Token 级加权)
|
||||
**CHORD-ϕ** 通过 **token-wise 权重函数 φ** 动态控制每个专家 token 的梯度贡献。
|
||||
|
||||
**φ 定义:**
|
||||
$$
|
||||
\phi(y_t^\star, \pi_\theta) = p_t \cdot (1 - p_t)
|
||||
$$
|
||||
|
||||
其中:
|
||||
- $p_t = \pi_\theta(y_t^\star \mid x, y_{<t}^\star)$:模型当前预测专家 token 的概率。
|
||||
- 当 $p_t ≈ 0.5$(模型不确定时),φ 取最大值 → 强化学习不确定的 token。
|
||||
- 当 $p_t ≈ 0$ 或 $p_t ≈ 1$,φ → 0 → 避免对过于确定或完全不会的 token 过度学习。
|
||||
|
||||
**开启 φ 加权的参数**:
|
||||
- `chord_enable_phi_function: bool = False`
|
||||
- 设置为 `True` 即启用 token-wise 权重 φ。
|
||||
|
||||
注:如果使用常数 μ 值 ,设置 chord_mu_peak 与 chord_mu_valley 相同
|
||||
|
||||
<details>
|
||||
<summary>mu值衰减与loss计算代码实现</summary>
|
||||
请参考`GRPOTrainer`的`_compute_chord_loss`方法:
|
||||
</details>
|
||||
|
||||
训练参考该[脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/internal/chord.sh)
|
||||
@@ -0,0 +1,61 @@
|
||||
# Clipped Importance Sampling Policy Optimization (CISPO)
|
||||
|
||||
|
||||
Clipped Importance Sampling Policy Optimization (CISPO) 是 [MiniMax-M1](https://arxiv.org/abs/2506.13585) 论文中提出的一种强化学习算法。相比GRPO(Group Relative Policy Optimization)算法,CISPO 对重要性采样权重(importance sampling weights)本身进行裁剪。
|
||||
|
||||
## 算法原理
|
||||
为便于理解,我们基于 GRPO 算法进行对比说明。
|
||||
|
||||
GRPO通过裁剪策略比率来限制策略更新幅度,其损失函数为:
|
||||
|
||||
$$
|
||||
\mathcal{L}_{\text{GRPO}}(\theta) = -\mathbb{E}\left[\min\left(r_t(\theta) \cdot \hat{A}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \cdot \hat{A}_t\right)\right]
|
||||
$$
|
||||
|
||||
其中 $r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{\text{old}}}(a_t|s_t)}$ 是重要性采样比。
|
||||
|
||||
在处理长推理链条时,这种裁剪方式可能导致以下问题:
|
||||
|
||||
**关键 Token 的梯度被抑制**:在复杂推理任务中,某些关键的低概率 token(如 *However, Recheck, Wait, Aha*)对于触发深度思考和推理纠错至关重要。这些 token 在旧策略 $\pi_{\theta_{\text{old}}}$ 中概率较低,当新策略试图提高其概率时,会导致较大的策略比率 $r_t(\theta)$,GRPO 的裁剪机制会将这些 token 丢弃。
|
||||
|
||||
|
||||
### CISPO 的解决方案
|
||||
|
||||
CISPO 的核心思想是:裁剪重要性采样权重,保留梯度更新。具体来说,CISPO 的损失函数为:
|
||||
|
||||
$$
|
||||
\mathcal{L}_{\text{CISPO}}(\theta) = -\mathbb{E}\left[\text{detach}\left(\min(r_t(\theta), \epsilon_{\text{high}})\right) \cdot \hat{A}_t \cdot \log \pi_\theta(a_t|s_t)\right]
|
||||
$$
|
||||
|
||||
其中 $r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{\text{old}}}(a_t|s_t)}$ 是重要性采样比。
|
||||
|
||||
**关键机制**:
|
||||
- 对重要性采样权重进行裁剪:$\min(r_t(\theta), \epsilon_{\text{high}})$
|
||||
- **detach 操作**:裁剪后的权重不参与梯度计算,作为常数系数
|
||||
- 梯度来自 $\log \pi_\theta(a_t|s_t)$ 项,保证所有 token 都有梯度贡献
|
||||
|
||||
|
||||
## 实现细节
|
||||
CISPO 的伪代码实现如下:
|
||||
|
||||
```python
|
||||
log_ratio = per_token_logps - old_per_token_logps
|
||||
importance_weights = torch.exp(log_ratio) # r_t(θ) = π_θ / π_θ_old
|
||||
|
||||
clamped_ratios = torch.clamp(importance_weights, max=epsilon_high).detach()
|
||||
|
||||
per_token_loss = -clamped_ratios * advantages.unsqueeze(1) * per_token_logps
|
||||
```
|
||||
|
||||
## 参数设置
|
||||
|
||||
我们可以基于 `GRPOTrainer`,通过设置以下参数实现 CISPO 训练:
|
||||
|
||||
```bash
|
||||
--loss_type cispo
|
||||
--epsilon_high 5.0
|
||||
```
|
||||
|
||||
> 相比其他算法, cispo 的 epsilon_high 一般取值较大,minimax论文中未给出具体的参数设置,这里的值参考论文[ScaleRL](https://arxiv.org/pdf/2510.13786)的实验设置
|
||||
|
||||
其他训练参数参考 [GRPO参数文档](../../Command-line-parameters.md#grpo参数)
|
||||
@@ -0,0 +1,102 @@
|
||||
# DAPO: An Open-Source LLM Reinforcement Learning System at Scale
|
||||
|
||||
|
||||
|
||||
[Decoupled Clip and Dynamic sAmpling Policy Optimization (DAPO)](https://arxiv.org/abs/2503.14476)在GRPO的基础上设置了几种trick,分别是
|
||||
- [Clip Higher](#clip-higher)
|
||||
- [Dynamic Sampling](#dynamic-sampling)
|
||||
- [Token level Loss](#token-level-loss)
|
||||
- [Overlong Filtering](#overlong-filtering)
|
||||
- [Soft Overlong Punishment](#soft-overlong-punishment)
|
||||
|
||||
## Clip Higher
|
||||
PPO和GRPO使用对称裁剪范围(如±0.2)限制策略更新幅度,虽然保证了稳定性,但也制约了模型的探索能力。特别是当某些token在旧策略中概率极低时,即使当前梯度显示其应被强化(A>0),最大增幅也被严格限制。
|
||||
|
||||
DAPO使用非对称裁剪范围, 提高上裁剪范围来鼓励模型进行探索:
|
||||
|
||||
- 上界(鼓励侧)放宽至0.28
|
||||
- 下界(抑制侧)保持0.2不变
|
||||
|
||||
GRPO中,默认使用`epsilon`设置用对称裁剪范围
|
||||
|
||||
使用参数
|
||||
|
||||
- `epsilon_high` 设置上裁剪范围,此时参数`epsilon` 为下裁剪范围
|
||||
|
||||
## Dynamic Sampling
|
||||
GRPO对每个问题采样多个回答计算组间优势,
|
||||
|
||||
$$
|
||||
\hat{A}_{i,t} = \frac{R_i - \text{mean}(\{R_j\}_{j=1}^G)}{\text{std}(\{R_j\}_{j=1}^G)}
|
||||
$$
|
||||
而当生成的所有输出{oi}获得相同奖励时,组间优势等于0,会出现梯度消失导致训练效率下降
|
||||
|
||||
DAPO引入动态采样策略解决这一问题:
|
||||
|
||||
- 采样阶段跳过组间奖励标准差为0的数据
|
||||
- 持续生成样本直到填满批次
|
||||
|
||||
|
||||
使用参数
|
||||
|
||||
- `dynamic_sample true` 来开启动态采样
|
||||
- `max_resample_times` 设置最多重采样次数
|
||||
|
||||
## Token level Loss
|
||||
GRPO 在归一化损失时采用句子级归一化,这会导致损失计算具有长度偏差。
|
||||
|
||||
DAPO 使用token级归一化,避免了回答长度在损失计算上的偏差。
|
||||
|
||||
使用参数
|
||||
|
||||
- loss_type bnpo/dapo 来使用token级归一化
|
||||
|
||||
> loss_type 计算公式可参考[文档](../DeveloperGuide/loss_types.md)
|
||||
|
||||
## Overlong Filtering
|
||||
DAPO 认为被强制截断的回复的奖励噪声较大,可能会导致模型难以区分质量问题和长度问题。为此,DAPO 筛除了训练中被截断的数据,使其不参与损失计算。
|
||||
|
||||
使用参数
|
||||
|
||||
- overlong_filter 开启对超长样本的过滤
|
||||
|
||||
|
||||
## Soft Overlong Punishment
|
||||
语言模型常面临生成长度控制难题:
|
||||
|
||||
- 过长输出可能被截断,导致正确内容被误判
|
||||
|
||||
- 无约束生成长度影响实用性和计算效率
|
||||
|
||||
DAPO 设计了三段式长度惩罚函数:
|
||||
|
||||
$$
|
||||
R_{\text{length}}(L) =
|
||||
\begin{cases}
|
||||
0, & L \leq L_{\text{max}} - L_{\text{cache}} \\[10pt]
|
||||
\dfrac{(L_{\text{max}} - L_{\text{cache}}) - L}{L_{\text{cache}}}, & L_{\text{max}} - L_{\text{cache}} < L \leq L_{\text{max}} \\[10pt]
|
||||
-1, & L > L_{\text{max}}
|
||||
\end{cases}
|
||||
$$
|
||||
|
||||
在长度位于 $(L_{\text{max}} - L_{\text{cache}} < L \leq L_{\text{max}})$ 区间时设置线性递增惩罚,在 $(L > L_{\text{max}})$ 时设置最大惩罚(-1)
|
||||
|
||||
|
||||
使用参数
|
||||
|
||||
- `reward_funcs soft_overlong` 启用该奖励函数
|
||||
- `soft_max_length` 设置L_max,默认等于为模型的最长输出长度(max_completion_length)。
|
||||
- `soft_cache_length`: 设置L_cache
|
||||
|
||||
## 参数设置
|
||||
综上所述,我们可以基于GRPOTrainer,设置以下参数实现 DAPO 训练。
|
||||
|
||||
| 参数 | 类型 | 值 |
|
||||
|----------------------|-----------|-------------|
|
||||
| `--loss_type` | `str` | `bnpo`/`dapo`|
|
||||
| `--epsilon_high` | `float` | `0.28` |
|
||||
| `--dynamic_sample` | `bool` | `true` |
|
||||
| `--max_resample_times` | `int` | `3` |
|
||||
| `--overlong_filter` | `bool` | `true` |
|
||||
| `--reward_funcs` | `str` | `soft_overlong`|
|
||||
| `--soft_cache_length` | `int` | `4096`|
|
||||
@@ -0,0 +1,54 @@
|
||||
# FIPO: Future-KL Influenced Policy Optimization
|
||||
|
||||
作者: [li2zhi](https://github.com/li2zhi)
|
||||
|
||||
[FIPO](https://arxiv.org/abs/2603.19835) 是一种面向长链推理的 value-free RL 方法。它保留 GRPO/DAPO 的整体训练框架,但改变 token 级策略更新的加权方式:不再让一个序列级 advantage 均匀作用到所有 token,而是用折扣累积的 Future-KL 信号判断“从当前 token 开始的后续轨迹”整体是在被增强还是被削弱。
|
||||
|
||||
## 核心思想
|
||||
|
||||
GRPO/DAPO 中,每个 response 的 token 通常共享同一个序列级 advantage:
|
||||
|
||||
$$
|
||||
\hat{A}_{i,t} = \hat{A}_{i}
|
||||
$$
|
||||
|
||||
这种做法稳定且简单,但 credit assignment 粒度较粗。FIPO 引入当前策略与旧策略在每个 token 上的 log-prob shift:
|
||||
|
||||
$$
|
||||
\Delta \log p_t = \log \pi_\theta(y_t \mid x, y_{<t}) -
|
||||
\log \pi_{\mathrm{old}}(y_t \mid x, y_{<t})
|
||||
$$
|
||||
|
||||
如果 $\Delta \log p_t > 0$,说明当前训练正在提高该 token 的概率;如果小于 0,则说明该 token 正在被压低。FIPO进一步从当前位置向后折扣累积该信号:
|
||||
|
||||
$$
|
||||
\mathrm{FutureKL}_t =
|
||||
\sum_{k=t}^{T}\gamma^{k-t} M_k \Delta \log p_k
|
||||
$$
|
||||
|
||||
其中 $M_k$ 是 completion mask,$\gamma = 2^{-1 / \text{decay\_rate}}$。`decay_rate` 越大,越远的 future token 对当前位置的影响越强;`decay_rate` 越小,Future-KL 越偏局部。然后将 Future-KL 映射为 influence weight:
|
||||
|
||||
$$
|
||||
f_t = \mathrm{clip}(\exp(\mathrm{FutureKL}_t), 1-\epsilon_f, 1+\epsilon_f)
|
||||
$$
|
||||
|
||||
最终把原本的 advantage 改成 future-aware advantage:
|
||||
|
||||
$$
|
||||
\tilde{A}_{i,t} = \hat{A}_{i} \cdot f_{i,t}
|
||||
$$
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|---------------------------|---------|--------|----------------------------------------------------------------------------------------------------------------|
|
||||
| `--loss_type` | `str` | `grpo` | 设置为`fipo` 启用 FIPO loss |
|
||||
| `--delta` | `float` | `None` | 启用后会同时用于 Future-KL 高 IS ratio token 过滤和主 loss 的 dual-clip 上限,应大于 `1 + epsilon_high`,对齐FIPO 32B训练脚本建议设置为 `10.0` |
|
||||
| `--fipo_decay_rate` | `float` | `32.0` | Future-KL 折扣半衰参数,实际折扣为`2 ** (-1 / fipo_decay_rate)` |
|
||||
| `--fipo_clip_range` | `float` | `0.2` | influence weight 裁剪范围;`0.2` 表示默认裁剪到 `[0.8, 1.2]` |
|
||||
| `--fipo_clip_high_only` | `bool` | `true` | 若为`true`,权重只裁剪到 `[1.0, 1.0 + fipo_clip_range]`,更偏向放大正 Future-KL |
|
||||
| `--fipo_safety_threshold` | `float` | `4.0` | 负 advantage 且 IS ratio 超过该阈值时,将 FIPO 权重限制到 `[0.8, 1.0]` 以避免过度惩罚 |
|
||||
|
||||
## 训练示例
|
||||
|
||||
[swift](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/internal/fipo.sh)
|
||||
@@ -0,0 +1,64 @@
|
||||
# Group Sequence Policy Optimization
|
||||
|
||||
[Group Sequence Policy Optimization](https://arxiv.org/abs/2507.18071)中指出GRPO在计算重要性采样权重时,是在token级别进行操作的。然而,这种做法由于每个token仅采样一次,无法实现有效的分布校正,反而会在模型训练过程中引入高方差噪声,极易导致模型的梯度估计不稳定,最终造成模型训练的崩塌。因此,论文认为,优化目标的单位应该与奖励的单位保持一致。由于奖励通常是在序列级别(即完整生成的回复)给出的,因此更合理的做法是将 off-policy 校正和优化也提升到序列级别,而非 token 级别。以下是三种计算策略对比:
|
||||
|
||||
1. GRPO
|
||||
对每个 token 独立计算重要性采样比,具体公式为
|
||||
|
||||
$$
|
||||
w^{\mathrm{GRPO}}_{i,t} = \frac{\pi_\theta (y_{i, t} \mid x, y_{i, <t})}{\pi_{\theta_{\mathrm{old}}} (y_{i, t} \mid x, y_{i, <t})}
|
||||
$$
|
||||
|
||||
2. GSPO (Sequence-Level)
|
||||
|
||||
在序列级别上计算重要性采样比,具体公式为
|
||||
|
||||
$$
|
||||
w^{\mathrm{GSPO}}_{i} = \left[ \frac{\pi_\theta (y_i \mid x)}{\pi_{\theta_{\mathrm{old}}} (y_i \mid x)} \right]^{\frac{1}{|y_i|}}
|
||||
= \exp\left( \frac{1}{|y_i|} \sum_{t=1}^{|y_i|} \log \frac{\pi_\theta (y_{i, t} \mid x, y_{i, <t})}{\pi_{\theta_{\mathrm{old}}} (y_{i, t} \mid x, y_{i, <t})} \right)
|
||||
$$
|
||||
|
||||
3. GSPO-token
|
||||
GSPO-token 结合了序列级与 token 级的重要性采样思想
|
||||
|
||||
$$
|
||||
w_{i, t}^{\mathrm{GSPO-token}} = \mathrm{sg}\left[w_i^{\mathrm{GSPO}}\right] \cdot \frac{\pi_{\theta}(y_{i, t} \mid x, y_{i, < t})}{\mathrm{sg}\left[\pi_{\theta}(y_{i, t} \mid x, y_{i, < t})\right]}
|
||||
$$
|
||||
|
||||
其中,$(\mathrm{sg}[\cdot])$ 表示梯度截断(detach())。
|
||||
|
||||
> 注意:根据梯度推导(即论文中的公式(11)和(18)),当各 token 的 advantage 相同时,GSPO-token 与 GSPO 等价。当前的 GRPO 实现中,所有 token 的 advantage 实际上都是基于句子级 reward 并在 group 内进行归一化,因此在这种设置下,GSPO-token 和 GSPO 在理论上是等价的。不过,GSPO-token 为未来更细粒度(token 级别)的 advantage 提供了支持。
|
||||
|
||||
伪代码实现
|
||||
```python
|
||||
log_ratio = per_token_logps - old_per_token_logps
|
||||
# GRPO
|
||||
log_importance_weights = log_ratio
|
||||
|
||||
# GSPO (Sequence-Level)
|
||||
seq_weight = (log_ratio * mask).sum(-1) / mask.sum(-1)
|
||||
log_importance_weights = seq_weight.unsqueeze(-1) # (B,1)
|
||||
|
||||
# GSPO-token
|
||||
seq_weight = (log_ratio * mask).sum(-1) / mask.sum(-1)
|
||||
log_importance_weights = seq_weight.detach().unsqueeze(-1) + (per_token_logps - per_token_logps.detach())
|
||||
|
||||
importance_weights = torch.exp(log_importance_weights)
|
||||
```
|
||||
|
||||
我们可以在 GRPO 训练的基础上,通过参数 `--importance_sampling_level` 选择不同的算法:
|
||||
|
||||
- `importance_sampling_level token` (默认,GRPO 实现)
|
||||
- `importance_sampling_level sequence` (GSPO)
|
||||
- `importance_sampling_level sequence_token` (GSPO-token)
|
||||
|
||||
论文其他超参
|
||||
```bash
|
||||
--epsilon 3e-4 # from paper section 5.1
|
||||
--epsilon_high 4e-4 # from paper section 5.1
|
||||
--gradient_accumulation_steps 8
|
||||
--steps_per_generation 32 # from paper section 5.1 (each batch of rollout data is partitioned into four minibatches for gradient updates)
|
||||
--beta 0 # zero kl regularization https://github.com/volcengine/verl/pull/2775#issuecomment-3131807306
|
||||
```
|
||||
|
||||
训练可以参考该[脚本](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/internal/gspo.sh)
|
||||
@@ -0,0 +1,77 @@
|
||||
# Rewards as Labels: Revisiting RLVR from a Classification Perspective
|
||||
|
||||
作者: [li2zhi](https://github.com/li2zhi)
|
||||
|
||||
[Rewards as Labels: Revisiting RLVR from a Classification Perspective](https://arxiv.org/abs/2602.05630) 针对GRPO提出把奖励视为标签,在group内分类而不是计算advantage,从而将策略优化问题转化为分类问题,以此解决GRPO Loss中存在的正样本**梯度错配**与负样本**梯度主导**问题。
|
||||
|
||||
## 背景与动机
|
||||
|
||||
GRPO目标函数
|
||||
|
||||
$$
|
||||
J_{\mathrm{GRPO}}(\theta)=\mathbb{E}_{q,o\sim\pi_{\mathrm{od}}(\cdot|q)}\left[\frac{1}{|o|}\sum_{t=1}^{|o|}\left(\min\left(\rho_tA_t,\mathrm{clip}(\rho_t,1-\epsilon,1+\epsilon)A_t\right)\right)\right]
|
||||
$$
|
||||
|
||||
其中$\rho_t=\frac{\pi_\theta(o_t|q)}{\pi_{\mathrm{old}}(o_t|q)}$为相对概率,$A_{t}$为优势函数,故梯度为:
|
||||
|
||||
$$
|
||||
\nabla_{\theta} J_{\mathrm{GRPO}} = \mathbb { E } \left[ \frac { 1 } { | o | } \sum _ { t = 1 } ^ { | o | } \mathbb { I } _ { \mathrm { clip } } \cdot A _ { t } e ^ { s _ { t } } \nabla _ { \theta } \log \pi _ { \theta } \left( o _ { t } | q \right) \right]
|
||||
$$
|
||||
|
||||
其中$s_t=\log\frac{\pi_\theta(o_t|q)}{\pi_{\mathrm{old}}(o_t|q)}$作为token的相对对数概率,$\mathbb { I } _ { \mathrm { clip } }$为指示函数
|
||||
|
||||
故 GRPO 对单 token 的梯度权重为:
|
||||
|
||||
$$
|
||||
|\mathcal{W}_{\mathrm{GRPO}}|=\left\{ \begin{array} {ll}\left|A\cdot e^s\right|, & \mathrm{if~}\mathbb{I}_{\mathrm{clip}}=1, \\ 0, & \text{otherwise.} \end{array}\right.
|
||||
$$
|
||||
|
||||

|
||||
|
||||
- 正样本的梯度错配(Gradient Misassignment):对正样本来说,随着相对概率$s$变小,梯度更新幅度反而越弱。这违背直觉,因为模型对“不太自信”的正确 token 本来就需要更大的更新幅度来强化,但更多的梯度权重却放到更“自信”的 token,没学好的 token 得不到足够的重视。
|
||||
|
||||
- 负样本的梯度主导(Gradient Domination):对负样本来说,随着相对概率$s$变小,梯度更新幅度呈指数级增加。这意味着,只要出现几个模型“盲目自信”的错误 token,它们产生的巨大梯度就会把同组内其他负样本的信号淹没。由于缺乏上限保护,模型在处理这些错误样本时可能会产生过大的参数更新,让训练过程变得不太可控。
|
||||
|
||||
为解决上述问题,Real提出将奖励直接视为标签然后进行组内的样本分类训练
|
||||
|
||||

|
||||
|
||||
分类的logits分值设计:
|
||||
|
||||
$$
|
||||
\bar{s}^k=\frac{1}{|o^k|}\sum_{t=1}^{|o^k|}\left(\log\frac{\pi_\theta(o_t^k\mid q)}{\pi_{\mathrm{old}}(o_t^k\mid q)}\right)
|
||||
$$
|
||||
|
||||
- $\bar{s}^k > 0$: 表示该样本在当前策略下生成的概率比旧策略整体更高,模型倾向于**增强**该样本。
|
||||
- $\bar{s}^k < 0$: 表示该样本在当前策略下生成的概率比旧策略整体更低,模型倾向于**抑制**该样本。
|
||||
|
||||
损失函数设计:
|
||||
|
||||
$$
|
||||
\mathcal{L}_{REAL}=\log\left(1+\sum_{\mathcal{O}_+}e^{-\bar{s}^i/\tau}\right)+\log\left(1+\sum_{\mathcal{O}_-}e^{\bar{s}^j/\tau}\right)
|
||||
$$
|
||||
|
||||
梯度特性:
|
||||
$$
|
||||
|\mathcal{W}_{\mathrm{REAL}}|=
|
||||
\begin{cases}
|
||||
\frac{1}{\tau}\frac{1}{1+C_{+}e^{\bar{s}^{k}/\tau}}, & r=1 \\
|
||||
\\
|
||||
\frac{1}{\tau}\frac{1}{1+C_{-}e^{-\bar{s}^{k}/\tau}}, & r=0 & & &
|
||||
\end{cases}
|
||||
$$
|
||||
|
||||
## 参数设置
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|-------------------|---------|-------|-------------------|
|
||||
| `--loss_type` | `str` | - | 设置为 `real` |
|
||||
| `--real_tau` | `float` | `0.5` | 温度参数,控制决策边界锐度 |
|
||||
|
||||
训练脚本参考
|
||||
|
||||
[swift](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/internal/real.sh)
|
||||
|
||||
## 注意事项
|
||||
|
||||
设置参数时,确保 per_device_train_batch_size 能够被 num_generations 整除,以此保证单个训练batch中能拿到完整的 group 进行分类。
|
||||
@@ -0,0 +1,87 @@
|
||||
# REINFORCE++: An Efficient RLHF Algorithm with Robustness to Both Prompt and Reward Models
|
||||
|
||||
[REINFORCE++ Baseline](https://arxiv.org/abs/2501.03262) 是 REINFORCE++ 算法的简化版本,适用于 outcome rewards(response-level 标量奖励)。它与 GRPO 类似,对每个prompt输入采样多条模型输出,并使用组内 baseline 来估计优势函数,主要区别在于标准化时使用的统计量不同。
|
||||
|
||||
|
||||
## 算法原理
|
||||
为便于理解,我们基于 GRPO(Group Relative Policy Optimization)算法进行对比说明。
|
||||
|
||||
GRPO 和 REINFORCE++ Baseline 都采用组内对比的方式来估计优势函数,主要区别在于:
|
||||
|
||||
### 区别1:标准化时使用的统计量不同
|
||||
|
||||
**GRPO (Group Relative Policy Optimization)**
|
||||
|
||||
对每个 prompt 生成 $G$ 个响应样本,使用**组内所有样本的均值和标准差**进行标准化:
|
||||
|
||||
$$
|
||||
\hat{A}_{i} = \frac{R_i - \text{mean}(\{R_j\}_{j=1}^G)}{\text{std}(\{R_j\}_{j=1}^G)}
|
||||
$$
|
||||
|
||||
当设置 `scale_rewards='batch'` 时,使用**原始奖励的批次 std**:
|
||||
|
||||
$$
|
||||
\hat{A}_{i} = \frac{R_i - \text{mean}(\{R_j\}_{j=1}^G)}{\text{std}(\{R_j\}_{j=1}^{N})}
|
||||
$$
|
||||
|
||||
其中 $N$ 是批次中所有样本数。
|
||||
|
||||
**REINFORCE++ Baseline**
|
||||
|
||||
对每个 prompt 生成 $G$ 个响应样本,先减去组内均值,再使用**减去组内均值后的奖励**的标准差进行标准化:
|
||||
|
||||
$$
|
||||
\begin{align}
|
||||
\tilde{A}_{i} &= R_i - \text{mean}(\{R_j\}_{j=1}^G) \\
|
||||
\hat{A}_{i} &= \frac{\tilde{A}_{i}}{\text{std}(\{\tilde{A}_k\}_{k=1}^{N})}
|
||||
\end{align}
|
||||
$$
|
||||
|
||||
其中 $N$ 是批次中所有样本数。
|
||||
|
||||
**关键区别**:
|
||||
- **GRPO**:标准化时使用**原始奖励 $R$** 的标准差
|
||||
- **REINFORCE++**:标准化时使用**减去组内均值后的奖励 $\tilde{A}$** 的标准差
|
||||
|
||||
### 区别2: KL 散度正则化
|
||||
|
||||
与 RLOO 类似,REINFORCE++ Baseline 将 KL 散度整合到奖励项中:
|
||||
|
||||
$$
|
||||
R'_i = R_i - \beta \cdot \text{KL}(\pi_\theta || \pi_{\text{ref}})
|
||||
$$
|
||||
|
||||
其中 $\beta$ 是 KL 散度的权重系数(对应参数 `beta`),$\pi_{\text{ref}}$ 是参考策略。
|
||||
|
||||
## 参数设置
|
||||
|
||||
我们可以基于 `GRPOTrainer`,通过设置以下参数实现 REINFORCE++ Baseline 训练:
|
||||
|
||||
```bash
|
||||
--advantage_estimator reinforce_plus_plus
|
||||
--scale_rewards batch
|
||||
--kl_in_reward true
|
||||
```
|
||||
|
||||
训练可以参考该[脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/internal/reinforce_plus_plus.sh)
|
||||
|
||||
### 重要参数说明
|
||||
|
||||
- **`--advantage_estimator`**:选择优势函数估计方法
|
||||
- `grpo`(默认):标准化时使用原始奖励的标准差
|
||||
- `reinforce_plus_plus`:标准化时使用减去组内均值后的奖励的标准差
|
||||
|
||||
- **`--kl_in_reward`**:控制 KL 散度正则化项的处理位置
|
||||
- `false`:KL 散度作为损失函数的独立正则化项(GRPO 默认)
|
||||
- `true`:KL 散度直接从奖励中扣除(REINFORCE++ 原始实现)
|
||||
|
||||
- **`--scale_rewards`**:控制标准化方式
|
||||
- `group`(默认):组内标准化
|
||||
- `batch`:全局批次标准化(REINFORCE++原始实现)
|
||||
- `none`:不进行标准化
|
||||
|
||||
- **`--num_generations`**:每个 prompt 生成的样本数量 $G$
|
||||
|
||||
- **`--beta`**:KL 散度正则化系数 $\beta$
|
||||
|
||||
其他参数参考 [GRPO参数](../../Command-line-parameters.md#grpo参数)
|
||||
@@ -0,0 +1,92 @@
|
||||
# REINFORCE Leave-One-Out (RLOO)
|
||||
|
||||
[REINFORCE Leave-One-Out (RLOO)](https://arxiv.org/abs/2402.14740) 基于经典的 REINFORCE 策略梯度方法,通过留一法(Leave-One-Out)构造无偏的优势函数基线。
|
||||
|
||||
## 算法原理
|
||||
|
||||
为便于理解,我们基于 GRPO(Group Relative Policy Optimization)算法进行对比说明。
|
||||
|
||||
GRPO 和 RLOO 都采用组内对比的方式来估计优势函数,避免了全局基线估计带来的高方差问题。两者的核心区别主要体现在以下两个方面:
|
||||
|
||||
### 区别1:优势函数基线的构造方法
|
||||
|
||||
**1. GRPO (Group Relative Policy Optimization)**
|
||||
|
||||
GRPO 对每个 prompt 生成 $G$ 个响应样本,使用**组内所有样本的均值和标准差**进行标准化:
|
||||
|
||||
$$
|
||||
\hat{A}_{i} = \frac{R_i - \text{mean}(\{R_j\}_{j=1}^G)}{\text{std}(\{R_j\}_{j=1}^G)}
|
||||
$$
|
||||
|
||||
其中:
|
||||
- $R_i$ 是第 $i$ 个样本的奖励值
|
||||
- $\text{mean}(\{R_j\}_{j=1}^G) = \frac{1}{G}\sum_{j=1}^G R_j$ 是组内均值
|
||||
- $\text{std}(\{R_j\}_{j=1}^G)$ 是组内标准差
|
||||
|
||||
**2. RLOO (REINFORCE Leave-One-Out)**
|
||||
|
||||
RLOO 对每个 prompt 生成 $K$ 个响应样本,使用 **留一法(Leave-One-Out)** 构造基线,即第 $i$ 个样本的基线为除自己外的其他 $K-1$ 个样本的均值:
|
||||
|
||||
$$
|
||||
\hat{A}_{i} = R_i - \frac{1}{K-1}\sum_{j \neq i} R_j
|
||||
$$
|
||||
|
||||
这个公式可以等价地改写为:
|
||||
|
||||
$$
|
||||
\hat{A}_{i} = \frac{K}{K-1} \left(R_i - \bar{R}\right)
|
||||
$$
|
||||
|
||||
其中 $\bar{R} = \frac{1}{K}\sum_{j=1}^K R_j$ 是组内所有样本的均值。
|
||||
|
||||
> **说明**:这里使用 $K$ 对齐论文符号,与 GRPO 中的 $G$ 含义一致,均对应配置参数 `num_generations`
|
||||
|
||||
**为什么使用留一法?**
|
||||
|
||||
留一法的关键优势在于**无偏性**。对于第 $i$ 个样本,其奖励 $R_i$ 和基线 $\frac{1}{K-1}\sum_{j \neq i} R_j$ 是独立的,因此优势估计是无偏的。相比之下,如果使用包含自身的均值作为基线,会引入偏差。
|
||||
|
||||
### 区别2:KL 散度正则化项的处理方式
|
||||
|
||||
为防止策略偏离参考策略过远,两种算法都引入了 KL 散度正则化,但处理方式不同:
|
||||
|
||||
**GRPO**:将 KL 散度作为独立的正则化项添加到[损失函数](../GetStarted/GRPO.md#算法原理)中:
|
||||
|
||||
$$
|
||||
\mathcal{L}(\theta) = -\mathbb{E}\left[\hat{A}_i \log \pi_\theta(a_i|s_i)\right] + \beta \cdot \text{KL}(\pi_\theta || \pi_{\text{ref}})
|
||||
$$
|
||||
|
||||
**RLOO**:将 KL 散度直接整合到奖励项中,构造修正后的奖励:
|
||||
|
||||
$$
|
||||
R'_i = R_i - \beta \cdot \text{KL}(\pi_\theta || \pi_{\text{ref}})
|
||||
$$
|
||||
|
||||
其中 $\beta$ 是 KL 散度的权重系数(对应参数 `beta`),$\pi_{\text{ref}}$ 是参考策略(通常是 SFT 模型或初始策略)。
|
||||
|
||||
## 参数设置
|
||||
|
||||
我们可以基于 `GRPOTrainer`,通过设置以下参数实现 RLOO 训练:
|
||||
```bash
|
||||
# 基本 RLOO 配置
|
||||
--advantage_estimator rloo # 使用 RLOO 的留一法优势函数计算
|
||||
--kl_in_reward true # 将 KL 散度项整合到奖励中(RLOO 默认方式)
|
||||
```
|
||||
|
||||
训练可以参考该[脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/internal/rloo.sh)
|
||||
|
||||
### 重要参数说明
|
||||
|
||||
- **`--advantage_estimator`**:选择优势函数估计方法
|
||||
- `grpo`(默认):使用组内均值和标准差进行标准化
|
||||
- `rloo`:使用留一法(Leave-One-Out)构造基线
|
||||
|
||||
- **`--kl_in_reward`**:控制 KL 散度正则化项的处理位置
|
||||
- `false`:KL 散度作为损失函数的独立正则化项(GRPO 方式)
|
||||
- `true`:KL 散度直接从奖励中扣除,构造修正后的奖励(RLOO 方式)
|
||||
|
||||
- **`--num_generations`**:每个 prompt 生成的样本数量 $K$
|
||||
|
||||
- **`--beta`**:KL 散度正则化系数 $\beta$
|
||||
- 控制策略更新的保守程度
|
||||
|
||||
其他参数与 [GRPO参数](../../Command-line-parameters.md#grpo参数)一致
|
||||
@@ -0,0 +1,91 @@
|
||||
# Soft Adaptive Policy Optimization (SAPO)
|
||||
|
||||
[Soft Adaptive Policy Optimization (SAPO)](https://arxiv.org/abs/2511.20347) 针对 GRPO 中硬裁剪(hard clipping)带来的问题,提出了一种基于温度控制的软门控(soft gate)机制,用于平滑地衰减离策略更新,同时保留有用的学习信号。
|
||||
|
||||
## 背景与动机
|
||||
|
||||
在强化学习训练 LLM 时,GRPO 通过计算 token 级别的重要性采样比(Importance Sampling Ratio)来处理 off-policy 训练:
|
||||
|
||||
$$
|
||||
r_t = \frac{\pi_\theta(y_t|x, y_{<t})}{\pi_{\theta_{\mathrm{old}}}(y_t|x, y_{<t})}
|
||||
$$
|
||||
|
||||
然而,token 级别的重要性采样比往往表现出高方差,这一现象在以下情况下可能更严重:
|
||||
- **长文本生成**
|
||||
- **MoE 模型的路由异质性**:采样时的 old-policy 模型与训练模型可能使用不同的专家路由,导致 logps 差异显著放大
|
||||
|
||||
为此,GRPO 通过硬裁剪来限制策略更新的幅度:
|
||||
|
||||
$$
|
||||
L^{\mathrm{GRPO}} = -\min\left( r_t \cdot A, \mathrm{clip}(r_t, 1-\epsilon, 1+\epsilon) \cdot A \right)
|
||||
$$
|
||||
|
||||
**硬裁剪的困境**:硬裁剪难以在稳定性和学习效率之间取得平衡——裁剪范围过严格会限制有效样本的数量,而过宽松则会引入离策略样本的噪声梯度,导致训练不稳定。
|
||||
|
||||
## SAPO 方法
|
||||
|
||||
SAPO 使用温度控制的 sigmoid 软门控函数替代硬裁剪,实现平滑的梯度衰减。
|
||||
|
||||
### 软门控函数
|
||||
|
||||
SAPO 的核心是使用 sigmoid 函数对重要性采样比进行软门控:
|
||||
|
||||
对于正向优势($A > 0$),使用正向门控:
|
||||
|
||||
$$
|
||||
g^{+}_t = \sigma\left( \tau_{\mathrm{pos}} \cdot (r_t - 1) \right) \cdot \frac{4}{\tau_{\mathrm{pos}}}
|
||||
$$
|
||||
|
||||
对于负向优势($A < 0$),使用负向门控:
|
||||
|
||||
$$
|
||||
g^{-}_t = \sigma\left( \tau_{\mathrm{neg}} \cdot (r_t - 1) \right) \cdot \frac{4}{\tau_{\mathrm{neg}}}
|
||||
$$
|
||||
|
||||
其中:
|
||||
- $\sigma(\cdot)$ 是 sigmoid 函数
|
||||
- $\tau_{\mathrm{pos}}$ 和 $\tau_{\mathrm{neg}}$ 是温度参数,控制门控函数的斜率
|
||||
- $r_t$ 是重要性采样比
|
||||
|
||||
### SAPO 损失函数
|
||||
|
||||
$$
|
||||
L^{\mathrm{SAPO}} = -g_t \cdot A
|
||||
$$
|
||||
|
||||
其中 $g_t = g^{+}_t$ 当 $A > 0$,$g_t = g^{-}_t$ 当 $A < 0$。
|
||||
|
||||
### 温度参数
|
||||
|
||||
温度参数 $\tau$ 控制软门控函数的衰减速率,数值越大,衰减越快。
|
||||
|
||||

|
||||
|
||||
论文指出正向优势会提升采样token的logit,并降低所有未采样token的logit;负向优势相反,提高许多未采样token的logit,可能会扩散到大量无关token上,带来一定的不稳定性。所以论文推荐设置温度 $\tau_\text{neg} > \tau_\text{pos}$,来使负向奖励的token梯度衰减更快,提升训练的稳定性和性能。
|
||||
|
||||
论文默认推荐 $\tau_{\mathrm{pos}} = 1.0$,$\tau_{\mathrm{neg}} = 1.05$。
|
||||
|
||||
## 参数设置
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `--loss_type` | `str` | - | 设置为 `sapo` |
|
||||
| `--tau_pos` | `float` | `1.0` | 正向优势的温度参数,控制门控斜率 |
|
||||
| `--tau_neg` | `float` | `1.05` | 负向优势的温度参数,控制门控斜率 |
|
||||
|
||||
```bash
|
||||
swift rlhf \
|
||||
--rlhf_type grpo \
|
||||
--loss_type sapo \
|
||||
--tau_pos 1.0 \
|
||||
--tau_neg 1.05 \
|
||||
# ... 其他参数
|
||||
```
|
||||
|
||||
训练脚本参考
|
||||
|
||||
- [swift](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/internal/sapo.sh)
|
||||
- [megatron swift](https://github.com/modelscope/ms-swift/blob/main/examples/megatron/grpo/sapo.sh)
|
||||
|
||||
> SAPO 的软门控机制仅在 off-policy 训练下生效。
|
||||
> SAPO 中的重要性采样粒度为 token 级别(即 importance_sampling_level 默认设置为 token),与 GSPO 冲突。
|
||||
@@ -0,0 +1,95 @@
|
||||
# DeepEyes: Incentivizing "Thinking with Images" via Reinforcement Learning
|
||||
|
||||
## 原理介绍
|
||||
|
||||
[DeepEyes论文](https://arxiv.org/abs/2505.14362) 提出了一种利用强化学习使模型具备“think with images”(以图辅助思考)能力的方法。该方法通过端到端的强化学习,模型能力自发涌现,无需额外的 SFT(监督微调)过程。模型内置图像定位能力,能够主动调用“图像放大工具”:在推理过程中,模型会自动选取图片中的具体区域进行放大和裁剪,将处理后的区域信息进行进一步推理,实现视觉与文本的链式推理。
|
||||
|
||||

|
||||
|
||||
## 最佳实践
|
||||
|
||||
**数据集下载与注册**
|
||||
|
||||
下载 DeepEyes 官方训练数据集到本地
|
||||
```bash
|
||||
# modelscope
|
||||
modelscope download --dataset Lixiang/ChenShawn-DeepEyes-Datasets-47k
|
||||
|
||||
# huggingface
|
||||
huggingface-cli download ChenShawn/DeepEyes-Datasets-47k --repo-type=dataset
|
||||
```
|
||||
|
||||
数据集内有三个parquet文件,`swift/dataset/data/dataset_info.json` 文件中分别进行注册,将数据集中的 `prompt` 列重命名为 `messages`
|
||||
|
||||
```json
|
||||
{
|
||||
"ms_dataset_id": "path/to/data_0.1.2_visual_toolbox_v2.parquet",
|
||||
"columns": {
|
||||
"prompt": "messages"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ms_dataset_id": "path/to/data/data_thinklite_reasoning_acc.parquet",
|
||||
"columns": {
|
||||
"prompt": "messages"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ms_dataset_id": "path/to/data/data_v0.8_visual_toolbox_v2.parquet",
|
||||
"columns": {
|
||||
"prompt": "messages"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在本地注册论文中所用到的奖励函数和工具调用逻辑,实现可以参考[DeepEyes实现示例](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin/deepeyes/deepeyes_plugin.py)
|
||||
|
||||
**部署验证模型**
|
||||
|
||||
Deepeyes 的奖励函数依赖生成式奖励模型对模型生成结果与标准答案进行对比评估,为了加速这一环节,推荐对模型进行部署。
|
||||
|
||||
假设使用 Qwen2.5-VL-72B-Instruct 模型进行评估,参考以下部署命令
|
||||
|
||||
```bash
|
||||
# 4*80G
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
swift deploy \
|
||||
--model Qwen/Qwen2.5-VL-72B-Instruct \
|
||||
--infer_backend vllm \
|
||||
--vllm_tensor_parallel_size 4 \
|
||||
```
|
||||
|
||||
在 plugin 文件中,使用OpenAI接口进行调用,参考[奖励模型文档](../DeveloperGuide/reward_model.md#外部部署)
|
||||
|
||||
|
||||
训练参考该[脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin/deepeyes/deepeyes.sh)
|
||||
|
||||
## 实现细节
|
||||
|
||||
[DeepEyes实现示例](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin/deepeyes/deepeyes_plugin.py)参考[官方实现](https://github.com/Visual-Agent/DeepEyes/blob/main/verl/utils/reward_score/vl_agent.py) 给出了 DeepEyes 训练插件的样例代码,涵盖了奖励函数与多轮交互调用的相关逻辑。
|
||||
|
||||
**数据集数据**如下
|
||||
|
||||
| 数据集文件名 | data_source | 对应评分函数 | 工具调用 |
|
||||
|------------------------------------------|-----------------------|----------------------------------|------------------|
|
||||
| data_v0.8_visual_toolbox_v2.parquet | chart | vl_agent.compute_score | True (image_zoom_in_tool) |
|
||||
| data_0.1.2_visual_toolbox_v2.parquet | vstar | vl_agent.compute_score | True (image_zoom_in_tool) |
|
||||
| data_thinklite_reasoning_acc.parquet | thinklite_eureka | vl_agent.compute_score_math | False |
|
||||
|
||||
|
||||
**注意**:多模态大模型在处理图像输入时,可能会对图像进行预处理(例如受 max_pixels 参数限制的裁剪或缩放等操作)。当调用图像放大工具 image_zoom_in_tool 时,模型会根据输入图像输出裁剪后的 bbox。因此,在调用图像放大工具时,需要确保输入的是经过预处理后的图像。示例代码展示了 Qwen2.5-VL 系列模型的实现方式:
|
||||
|
||||
```python
|
||||
from qwen_vl_utils import fetch_image
|
||||
# 这里的images尚未经过图像处理
|
||||
infer_request.images
|
||||
# 通过加载为PIL.Image格式,进行裁剪(使用环境变量MAX_PIXELS时的处理)
|
||||
img = fetch_image({'image': load_pil_image(infer_request.images[0])})
|
||||
```
|
||||
|
||||
**工具奖励**
|
||||
|
||||
论文中指出当最终答案正确,且轨迹至少使用一个工具时给予工具奖励。为了避免模型生成的工具调用是无效的,我们通过图像数量而不是`<tool_call>` 等token进行判断。
|
||||
```python
|
||||
tool_reward = 1.0 if num_image > 1 and acc_reward > 0.5 else 0.0
|
||||
```
|
||||
@@ -0,0 +1,30 @@
|
||||
# Beyond the 80/20 Rule: High-Entropy Minority Tokens Drive Effective Reinforcement Learning for LLM Reasoning
|
||||
|
||||
[论文](https://arxiv.org/abs/2506.01939)发现在以 RLVR等方法训练大型语言模型推理能力时,驱动学习进步的关键在于一小部分高熵“少数 token”,而并非大多数信息熵低的 token。
|
||||
|
||||
论文指出,在模型推理的 token 分布中,只有极少数信息熵较高的 token 起到了主导作用。这些 token 往往出现在推理和决策路径分歧最大的关键节点(如 "wait"、"since" 等),决定了模型能否习得复杂推理任务。而大多数熵低的 token 对模型推理能力的提升作用有限。论文提出只对高熵 token 计算策略梯度、舍弃低熵 token 的梯度。
|
||||
|
||||
|
||||
token 熵公式如下
|
||||
|
||||
$
|
||||
H_t := -∑_{j=1}^{V} p_{t,j} \log p_{t,j}, \qquad where (p_{t,1}, ···, p_{t,V}) = \mathbf{p}_t = π_θ(\cdot | \mathbf{q}, \mathbf{o}_{<t}) = \text{Softmax}(\frac{\mathbf{z}_t}{T})
|
||||
$
|
||||
|
||||
其中
|
||||
- $\pi_\theta$:参数为 $\theta$ 的模型
|
||||
- $\mathbf{q}$:输入查询(input query)。
|
||||
- $\mathbf{o}_{<t} = (o_1, o_2, \cdots, o_{t-1})$:时间步 $t$ 之前已生成的 token 序列。
|
||||
- $V$:词表大小(vocabulary size)。
|
||||
- $\mathbf{z}_t \in \mathbb{R}^V$:时间步 $t$ 的 pre-softmax 逻辑值(logits)。
|
||||
- $\mathbf{p}_t \in \mathbb{R}^V$:模型对词表的概率分布。
|
||||
- $T \in \mathbb{R}$:解码温度(decoding temperature),控制分布的平滑程度。
|
||||
|
||||
熵的计算对象:$H_t$ 是 token 生成分布 $\mathbf{p}_t$ 的熵,用于衡量训练策略 $\pi_\theta$ 在给定上下文 $(\mathbf{q}, \mathbf{o}_{<t})$ 下的不确定性。
|
||||
|
||||
> "Token entropy" $H_t$ 始终指向位置 $t$ 的生成分布 $\mathbf{p}_t$ 的不确定性,而非 token $o_t$ 本身的属性。即$H_t$ 是位置 $t$ 对应分布 $\mathbf{p}_t$ 的熵,与采样得到的 token $o_t$ 无关。
|
||||
|
||||
|
||||
在实践中,我们可以在 GRPO 训练中通过参数 `top_entropy_quantile` 控制训练范围。论文实验设置该参数为 0.2,即每次仅对处于熵分布前 20% 的 token 进行训练优化。
|
||||
|
||||
同时使用参数`log_entropy`,可以记录训练过程中的熵值变化,参考[文档](../GetStarted/GRPO.md#logged-metrics)
|
||||
@@ -0,0 +1,19 @@
|
||||
Advanced Research
|
||||
===============
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
entropy_mask.md
|
||||
CISPO.md
|
||||
DAPO.md
|
||||
deepeyes.md
|
||||
FIPO.md
|
||||
GSPO.md
|
||||
CHORD.md
|
||||
RLOO.md
|
||||
REINFORCEPP.md
|
||||
REAL.md
|
||||
router_replay.md
|
||||
SAPO.md
|
||||
training_inference_mismatch.md
|
||||
treepo.md
|
||||
@@ -0,0 +1,104 @@
|
||||
# Router Replay (R2/R3)
|
||||
|
||||
**TL;DR**: 在 MoE 模型的 RL 训练中,训练引擎与推理引擎之间的路由(expert routing)不一致会显著放大训推偏差,甚至导致训练崩溃。Router Replay 通过在训练前向传播中回放固定的路由掩码来消除这一不一致。根据回放来源的不同,分为 R2(Vanilla Routing Replay)和 R3(Rollout Routing Replay)两种策略。
|
||||
|
||||
## Background
|
||||
|
||||
### MoE RL 中的三个策略
|
||||
|
||||
在 MoE 模型的 GRPO 训练中,存在三个不同阶段的策略,它们共享相同的模型权重,但路由行为可能不同:
|
||||
|
||||
| 策略 | 符号 | 路由结果 | 说明 |
|
||||
|------|------|---------|------|
|
||||
| **训练策略** | $\pi_\theta$ | $e^{\pi}_t$ | 梯度更新中的模型 |
|
||||
| **Old Policy** | $\pi_{\theta_{\text{old}}}$ | $e^{\pi}_{\text{old},t}$ | 批次更新前的模型状态 |
|
||||
| **Rollout Policy** | $\mu_{\theta_{\text{old}}}$ | $e^{\mu}_{\text{old},t}$ | 推理引擎(如vLLM)中的采样策略,权重与 old policy 相同,但 kernel 实现、精度等差异导致路由不同 |
|
||||
|
||||
其中,$\pi_{\theta_{\text{old}}}$ 和 $\mu_{\theta_{\text{old}}}$ 的权重在采样时完全一致,但由于推理引擎与训练引擎的实现差异(如算子实现),即使输入相同,路由结果也可能不同。
|
||||
|
||||
### 训推不一致的分解
|
||||
|
||||
根据 [论文](https://arxiv.org/abs/2507.18071) 的分析,token 级重要性采样比可以分解为两个因子:
|
||||
|
||||
$$
|
||||
\frac{\pi_\theta(y_t|x, y_{<t})}{\mu_{\theta_{\text{old}}}(y_t|x, y_{<t})} = \underbrace{\frac{\pi_{\theta_{\text{old}}}(y_t|x, y_{<t})}{\mu_{\theta_{\text{old}}}(y_t|x, y_{<t})}}_{\text{training-inference discrepancy}} \times \underbrace{\frac{\pi_\theta(y_t|x, y_{<t})}{\pi_{\theta_{\text{old}}}(y_t|x, y_{<t})}}_{\text{policy staleness}}
|
||||
$$
|
||||
|
||||
对于 MoE 模型,专家路由与这两个因子深度耦合:
|
||||
|
||||
- **Training-inference discrepancy**:训练引擎和推理引擎的路由不一致($e^{\pi}_{\text{old},t} \neq e^{\mu}_{\text{old},t}$),放大输出差异
|
||||
- **Policy staleness**:随着 mini-batch 更新,路由也随之漂移($e^{\pi}_t \neq e^{\pi}_{\text{old},t}$),进一步偏离采样时的策略
|
||||
|
||||
## R2: Vanilla Routing Replay
|
||||
|
||||
R2 的核心思想是在梯度更新时,**回放 old policy 在训练引擎中确定的路由**($e^{\pi}_{\text{old},t}$)。
|
||||
|
||||
### 原理
|
||||
|
||||
在训练前向传播中,先用 old policy 的权重做一次前向,记录每一层 MoE Router 选择的 expert indices,然后在训练模型 $\pi_\theta$ 的前向传播中强制使用这些 indices:
|
||||
|
||||
$$
|
||||
g_{\text{replay},i} = \frac{I^{\pi}_{\text{old},i} \cdot \exp(s_{\text{train},i})}{\sum_j I^{\pi}_{\text{old},j} \cdot \exp(s_{\text{train},j})}
|
||||
$$
|
||||
|
||||
其中 $I^{\pi}_{\text{old}}$ 是 old policy 的路由掩码,$s_{\text{train}}$ 是训练模型计算的 router logits。softmax 仍作用于训练 logits,因此梯度可以正常回传到 router 权重。
|
||||
|
||||
### 特性
|
||||
|
||||
| 场景 | 行为 |
|
||||
|------|------|
|
||||
| **首个 mini-batch**(on-policy) | $\theta = \theta_{\text{old}}$,故 $e^{\pi}_t = e^{\pi}_{\text{old},t}$,**target policy 不变**(无 bias) |
|
||||
| **后续 mini-batch**(off-policy) | $\theta \neq \theta_{\text{old}}$,故 $e^{\pi}_t \neq e^{\pi}_{\text{old},t}$,**target policy 被改变**(有 bias),但 policy staleness 被控制 |
|
||||
|
||||
## R3: Rollout Routing Replay
|
||||
|
||||
R3 的核心思想是在训练前向传播中,**回放 rollout policy 在推理引擎中确定的路由**($e^{\mu}_{\text{old},t}$)。
|
||||
|
||||
### 原理
|
||||
|
||||
在推理引擎(如 vLLM)采样时,额外记录每个 token 在每一层 MoE Router 的 expert indices(路由掩码),然后将这些掩码传递到训练引擎,在 $\pi_\theta$ 的前向传播中强制使用:
|
||||
|
||||
$$
|
||||
g_{\text{replay},i} = \frac{I^{\mu}_{\text{old},i} \cdot \exp(s_{\text{train},i})}{\sum_j I^{\mu}_{\text{old},j} \cdot \exp(s_{\text{train},j})}
|
||||
$$
|
||||
|
||||
### 与其他方法的兼容性
|
||||
|
||||
- R3 与 **GSPO** 正交,组合使用可进一步提升
|
||||
- R3 与 **TIS** 组合不一定有增益(R3 已从根源消除不一致,TIS 的额外修正可能多余)
|
||||
- Router Replay 与 **Clipping** 在 off-policy 训练中缺一不可
|
||||
|
||||
## Router Mask Caching
|
||||
|
||||
R3 论文还提出路由掩码可以与 KV Cache 一起缓存:对于相同的前缀 token,MoE Router 的输出是确定性的,因此路由掩码可以随 prefix KVCache 一起存储和复用。这在多轮 Agent 场景(tool calling)中尤为重要,避免了重新 prefill 前缀来获取路由掩码,整体 rollout 延迟开销 < 3%。
|
||||
|
||||
## Swift 实现
|
||||
|
||||
### 参数设置
|
||||
|
||||
通过 `--router_replay_mode` 参数选择路由回放策略:
|
||||
|
||||
| 参数值 | 说明 |
|
||||
|--------|------|
|
||||
| `disabled`(默认) | 不启用路由回放 |
|
||||
| `R2` | Vanilla Routing Replay:在训练引擎中记录 old policy 路由并回放 |
|
||||
| `R3` | Rollout Routing Replay:从推理引擎导出路由掩码并在训练中回放 |
|
||||
|
||||
环境依赖:
|
||||
|
||||
- R3 需要 vLLM ≥ 0.14.0 以支持返回 `routed_experts` 信息。
|
||||
- 当前 Router Replay 仅在 Megatron backend 下可用,需要 megatron-core ≥ 0.16.0。
|
||||
|
||||
### 与训推校正的关系
|
||||
|
||||
Router Replay 与 [Training-Inference Mismatch](training_inference_mismatch.md) 中介绍的重要性采样校正(IS correction)是互补的:
|
||||
|
||||
- **IS correction**:在 loss 层面通过权重修正训推概率偏差
|
||||
- **Router Replay**:在模型结构层面通过固定路由消除偏差来源
|
||||
|
||||
## 参考资料
|
||||
|
||||
1. [Stabilizing MoE Reinforcement Learning by Aligning Training and Inference Routers](https://arxiv.org/abs/2510.11370)
|
||||
2. [Group Sequence Policy Optimization](https://arxiv.org/abs/2507.18071)
|
||||
3. [Stabilizing Reinforcement Learning with LLMs: Formulation and Practices](https://arxiv.org/abs/2512.01374)
|
||||
4. [Megatron Core Router Replay Design Document](https://docs.nvidia.com/megatron-core/developer-guide/nightly/api-guide/router_replay.html)
|
||||
@@ -0,0 +1,239 @@
|
||||
# Training-Inference-Mismatch
|
||||
|
||||
|
||||
**TL;DR**: GRPO 引入 vLLM 加速采样过程的同时,也引入了训练-推理不一致(Training-Inference Mismatch)的问题,从而可能影响训练稳定性。本文将解释这个问题的背景、原因以及相应的解决方案。
|
||||
|
||||
## Background
|
||||
|
||||
### GRPO 的基本假设
|
||||
|
||||
GRPO (Group Relative Policy Optimization) 的训练目标可以表示为:
|
||||
|
||||
$$
|
||||
\mathcal{L}_{\text{GRPO}} = - \mathbb{E}_{y \sim \pi_\theta} \left[ \min \left( r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \hat{A}_t \right) \right]
|
||||
$$
|
||||
|
||||
其中:
|
||||
- $r_t(\theta) = \frac{\pi_\theta(y_t|x, y_{<t})}{\pi_{\theta_{\text{old}}}(y_t|x, y_{<t})}$ 是重要性采样比
|
||||
- $\hat{A}_t$ 是优势函数(advantage),基于 reward 和 group baseline 计算
|
||||
- $\epsilon$ 是 clipping 参数
|
||||
|
||||
**核心假设**:样本 $y$ 是从策略 $\pi_\theta$ 中采样得到的。在实际训练中,这意味着:
|
||||
1. 采样模型(rollout model)与训练模型(policy model)应当是**同一个模型** $\pi_\theta$
|
||||
2. 两个模型的概率分布应当**完全一致**,即 $\pi_{\text{rollout}} = \pi_\theta$
|
||||
|
||||
### 引入 vLLM 后的假设偏离
|
||||
|
||||
GRPO 的训练速度很大程度上受到采样过程(rollout)的速度制约。为了加速,训练框架引入高效推理引擎(如 vLLM)来执行采样。**理想假设**是:通过权重同步,vLLM 与训练模型保持一致,即 $\pi_{\text{vLLM}} \equiv \pi_\theta$。
|
||||
|
||||
然而,在实践中,即使权重完全同步,由于算子实现等差异,两者的概率分布仍然存在偏差:
|
||||
|
||||
$$
|
||||
\pi_{\text{vLLM}}(y|x) \neq \pi_\theta(y|x)
|
||||
$$
|
||||
|
||||
此时,实际的训练目标变为:
|
||||
|
||||
$$
|
||||
\mathcal{L} = - \mathbb{E}_{y \sim \pi_{\text{vLLM}}} \left[ \min \left( r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \hat{A}_t \right) \right]
|
||||
$$
|
||||
|
||||
其中样本来自 $\pi_{\text{vLLM}}$,但梯度是基于 $\pi_\theta$ 计算的,这**破坏了算法的 on-policy 假设**,引入了训推不一致的问题。
|
||||
|
||||
## Solution
|
||||
|
||||
针对训推不一致问题,可以引入**重要性采样(Importance Sampling, IS)**的校正机制。
|
||||
|
||||
### 重要性采样校正
|
||||
|
||||
重要性采样的基本思想是:当样本来自分布 $q$ 而非目标分布 $p$ 时,可以通过引入权重来修正期望的计算:
|
||||
|
||||
$$
|
||||
\mathbb{E}_{x \sim p} [f(x)] = \mathbb{E}_{x \sim q} \left[ \frac{p(x)}{q(x)} \cdot f(x) \right]
|
||||
$$
|
||||
|
||||
应用到 GRPO 的场景,修正后的损失函数为:
|
||||
|
||||
$$
|
||||
\mathcal{L}_{\text{corrected}} = - \mathbb{E}_{y \sim \pi_{\text{vLLM}}} \left[ w(x, y) \cdot \min \left( r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \hat{A}_t \right) \right]
|
||||
$$
|
||||
|
||||
其中 $w(x, y)$ 是重要性采样权重,用于校正 vLLM 与训练模型之间的分布偏差
|
||||
|
||||
重要性采样权重可以在不同粒度上计算和应用:
|
||||
|
||||
1. **Token-Level**
|
||||
|
||||
每个 token 上计算重要性采样比:
|
||||
|
||||
$$
|
||||
w_{i,t}^{\text{token}} = \frac{\pi_\theta(y_{i,t}|x, y_{i,<t})}{\pi_{\text{vLLM}}(y_{i,t}|x, y_{i,<t})}
|
||||
$$
|
||||
|
||||
2. **Sequence-Level**
|
||||
|
||||
计算序列级别的重要性采样比,然后广播到每个 token:
|
||||
|
||||
$$
|
||||
w_i^{\text{seq}} = \left[ \frac{\pi_\theta(y_i|x)}{\pi_{\text{vLLM}}(y_i|x)} \right]^{\frac{1}{|y_i|}} = \exp\left( \frac{1}{|y_i|} \sum_{t=1}^{|y_i|} \log \frac{\pi_\theta(y_{i,t}|x, y_{i,<t})}{\pi_{\text{vLLM}}(y_{i,t}|x, y_{i,<t})} \right)
|
||||
$$
|
||||
|
||||
### 稳定性控制:Truncate vs. Mask
|
||||
|
||||
过大的重要性采样权重会导致梯度爆炸,破坏训练稳定性。因此需要对权重进行控制:
|
||||
|
||||
#### 1. Truncate(截断)
|
||||
|
||||
将重要性采样权重截断到 $[0, \tau]$ 区间:
|
||||
|
||||
$$
|
||||
w_{\text{truncate}} = \min(w, \tau)
|
||||
$$
|
||||
|
||||
该方法保留所有样本,但限制其影响范围。
|
||||
|
||||
#### 2. Mask(屏蔽)
|
||||
|
||||
舍弃权重超过阈值的 token/sequence 数据
|
||||
|
||||
$$
|
||||
w_{\text{mask}} = \begin{cases}
|
||||
w & \text{if } w \leq \tau \\
|
||||
0 & \text{otherwise}
|
||||
\end{cases}
|
||||
$$
|
||||
|
||||
|
||||
### 四种校正模式
|
||||
|
||||
结合粒度和控制策略,共设置四种校正模式(通过 `--rollout_importance_sampling_mode` 参数选择):
|
||||
|
||||
| 模式 | 说明 |
|
||||
|------|------|
|
||||
| `token_truncate` | Token 级截断 |
|
||||
| `token_mask` | Token 级屏蔽 |
|
||||
| `sequence_truncate` | Sequence 级截断 |
|
||||
| `sequence_mask` | Sequence 级屏蔽 |
|
||||
|
||||
其中阈值通过 `--rollout_importance_sampling_threshold` 参数设置。
|
||||
|
||||
## Metrics
|
||||
|
||||
为了监控训练中训推不一致的程度,我们在log中加入以下指标(前缀为 `rollout_correction/`):
|
||||
|
||||
### 1. KL 散度(KL Divergence)
|
||||
|
||||
KL 散度衡量 rollout 策略与训练策略之间的偏离程度。两个指标都估计 $\text{KL}(\pi_{\text{vLLM}} \| \pi_\theta)$
|
||||
|
||||
|
||||
**直接估计器 `kl`**:
|
||||
|
||||
$$
|
||||
\text{KL}(\pi_{\text{vLLM}} \| \pi_\theta) = \mathbb{E}_{\pi_{\text{vLLM}}}\left[ \log \frac{\pi_{\text{vLLM}}}{\pi_\theta} \right]
|
||||
$$
|
||||
|
||||
**K3 估计器 `k3_kl`**:
|
||||
|
||||
$$
|
||||
\text{KL}(\pi_{\text{vLLM}} \| \pi_\theta) \approx \mathbb{E}_{\pi_{\text{vLLM}}}\left[ \rho - \log \rho - 1 \right], \quad \rho = \frac{\pi_\theta}{\pi_{\text{vLLM}}}
|
||||
$$
|
||||
|
||||
K3 估计器在 KL 值较小时数值更稳定,且始终非负。
|
||||
|
||||
### 2. Perplexity (PPL)
|
||||
|
||||
困惑度衡量模型对序列的预测不确定性:
|
||||
|
||||
$$
|
||||
\text{PPL} = \exp\left( -\frac{1}{|y|} \sum_{t=1}^{|y|} \log p(y_t) \right)
|
||||
$$
|
||||
|
||||
相关指标:
|
||||
- `training_ppl` / `training_log_ppl`:训练策略的 PPL 及其对数
|
||||
- `rollout_ppl` / `rollout_log_ppl`:rollout 策略的 PPL 及其对数
|
||||
- `log_ppl_diff`:log PPL 差异,正值表示训练策略分配的概率更低
|
||||
- `log_ppl_abs_diff`:log PPL 绝对差异
|
||||
- `log_ppl_diff_max` / `log_ppl_diff_min`:log PPL 差异的最大/最小值
|
||||
- `ppl_ratio`:PPL 比率 $\frac{\text{PPL}_{\text{training}}}{\text{PPL}_{\text{rollout}}}$
|
||||
|
||||
### 3. χ² 散度(Chi-squared Divergence)
|
||||
|
||||
χ² 散度衡量重要性采样权重的方差:
|
||||
|
||||
$$
|
||||
\chi^2(\pi_\theta \| \pi_{\text{vLLM}}) = \mathbb{E}_{\pi_{\text{vLLM}}}\left[ \rho^2 \right] - 1, \quad \rho = \frac{\pi_\theta}{\pi_{\text{vLLM}}}
|
||||
$$
|
||||
|
||||
- `chi2_token`:Token 级别 χ² 散度,$\mathbb{E}[\rho_t^2] - 1$
|
||||
- `chi2_seq`:Sequence 级别 χ² 散度(基于几何平均),$\mathbb{E}[\rho_{\text{geo}}^2] - 1$,其中 $\rho_{\text{geo}} = \exp(\frac{1}{T}\sum_t \log \rho_t)$
|
||||
|
||||
χ² 散度越大,表示 IS 权重方差越大,训练越不稳定。`chi2_seq` 使用几何平均而非乘积,使其与 `chi2_token` 在量级上可比较。
|
||||
|
||||
### 4. Effective Sample Size (ESS)
|
||||
|
||||
有效样本大小衡量重要性采样后实际起作用的样本数量:
|
||||
|
||||
$$
|
||||
\text{ESS} = \frac{1}{\mathbb{E}\left[\left(\frac{w}{\mathbb{E}[w]}\right)^2\right]}
|
||||
$$
|
||||
|
||||
ESS 值越大(接近1),表示重要性采样权重分布越均匀,样本的有效利用率越高。当所有权重相等时(on-policy),ESS = 1;当权重差异很大时(严重 off-policy),ESS 会很小。
|
||||
|
||||
### 5. IS 权重统计
|
||||
|
||||
- `is_weight_mean`:平均重要性采样权重,理想值为 1.0
|
||||
- `clipped_frac`:被截断或屏蔽的样本比例
|
||||
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 仅记录诊断指标
|
||||
|
||||
如果只想监控训推不一致的程度,而不启用重要性采样校正,可以设置:
|
||||
|
||||
```
|
||||
--log_rollout_offpolicy_metrics true
|
||||
```
|
||||
|
||||
这将记录上述所有诊断指标(KL、PPL、χ² 等),但不会对损失函数进行任何修正。
|
||||
|
||||
### 启用重要性采样校正
|
||||
|
||||
在GRPO训练中,设置以下参数启用校正机制:
|
||||
|
||||
```
|
||||
--rollout_importance_sampling_mode (默认为None)
|
||||
--rollout_importance_sampling_threshold (默认为2)
|
||||
```
|
||||
|
||||
当设置了 `rollout_importance_sampling_mode` 时,诊断指标会自动记录,无需额外设置 `log_rollout_offpolicy_metrics`。
|
||||
|
||||
## Off-Policy Sequence Masking
|
||||
|
||||
除了重要性采样校正外,还可以使用 **Off-Policy Sequence Masking** 技术来处理训推不一致问题。该技术来自 [DeepSeek-V3.2 论文](https://arxiv.org/abs/2512.02556)。
|
||||
|
||||
### 原理
|
||||
|
||||
Off-Policy Sequence Masking 的核心思想是:当当前策略相对于旧策略(rollout 或 old policy)发生较大偏移时,直接丢弃(mask)该序列,不参与损失计算。这种方法特别针对**优势为负的序列**,因为这些序列在策略偏移较大时更容易导致训练不稳定。
|
||||
|
||||
具体来说,对于每个序列,计算:
|
||||
|
||||
$$
|
||||
\delta_i = \frac{1}{|y_i|} \sum_{t=1}^{|y_i|} \bigl( \log \pi_{\text{old}}(y_{i,t}|x, y_{i,<t}) - \log \pi_\theta(y_{i,t}|x, y_{i,<t}) \bigr)
|
||||
$$
|
||||
|
||||
当满足以下条件时,序列 $i$ 将被 mask 掉(平均只在 completion token 上计算,即 `completion_mask=1` 的位置):
|
||||
1. $\delta_i > \tau$
|
||||
2. **且** $\hat{A}_i < 0$
|
||||
|
||||
其中:
|
||||
- $\pi_{\text{old}}$ 优先使用 `rollout_per_token_logps`(rollout/行为策略的 logprobs),若不存在则使用 `old_per_token_logps`
|
||||
- $\tau$ 是用户设置的阈值(`--off_policy_sequence_mask_delta`,默认 None 表示关闭)
|
||||
|
||||
|
||||
参考资料
|
||||
|
||||
1. https://yingru.notion.site/When-Speed-Kills-Stability-Demystifying-RL-Collapse-from-the-Training-Inference-Mismatch-271211a558b7808d8b12d403fd15edda
|
||||
2. https://fengyao.notion.site/off-policy-rl
|
||||
3. https://github.com/volcengine/verl/blob/main/verl/trainer/ppo/rollout_corr_helper.py
|
||||
4. [DeepSeek-V3.2: Pushing the Frontier of Open Large Language Models](https://arxiv.org/abs/2512.02556)
|
||||
@@ -0,0 +1,35 @@
|
||||
# TreePO: Bridging the Gap of Policy Optimization and Efficacy and Inference Efficiency with Heuristic Tree-based Modeling
|
||||
|
||||
|
||||
作者: [li2zhi](https://github.com/li2zhi)
|
||||
|
||||
## 原理介绍
|
||||
[TreePO论文](https://arxiv.org/abs/2508.17445) 提出了一种树状结构建模方法。该方法将序列生成组织为分段式的树结构搜索,通过动态分支、回退与提前终止机制,显著提高KV缓存复用率,从而降低计算开销,同时保持甚至增强了探索的多样性。
|
||||
|
||||

|
||||
|
||||
## 实现细节
|
||||
[TreePO实现示例](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin/treepo/tree_rollout_plugin.py)参考[官方实现](https://github.com/multimodal-art-projection/TreePO/blob/main/recipe/treepo/vllm_rollout_tree.py) 给出了 TreePO 训练插件的样例代码,涵盖了多轮交互、终止判断,与分支回退等相关逻辑。
|
||||
|
||||
**注意**:在实际使用中,你需要根据自己的场景需求,重写step、check_finished等方法的逻辑,以确保其能够在自定义场景下按照预期执行。而关于自定义奖励的设计与使用,你可以参考[DeepEyes](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin/deepeyes/deepeyes_plugin.py)的实现。
|
||||
|
||||
训练参考该[脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin/treepo/tree_rollout.sh)
|
||||
|
||||
## 测试数据
|
||||
> model: Qwen/Qwen2.5-0.5B
|
||||
> dataset: AI-MO/NuminaMath-TIR
|
||||
> subset size: 1,000 samples
|
||||
> 1 GPU for training, 1 GPU for inference
|
||||
|
||||
| \ | batch_size | num_generation | max_tree_depth | global_step | total inference calls | saving ratio | train_speed(iter/s) | improvement rate |
|
||||
| ----------------------- | ---------- | -------------- | -------------- | ----------- | --------------------- | ------------ | ------------------- | ---------------- |
|
||||
| original implementation | 8 | 8 | 4 | 200 | 5965 | 0.00% | 0.292436 | 0.00% |
|
||||
| tree(max_divergence=3) | 8 | 8 | 4 | 200 | 3678 | 38.34% | 0.31819 | 8.81% |
|
||||
| | | | | | | | | |
|
||||
| original implementation | 8 | 8 | 5 | 105 | 4312 | 0.00% | 0.261324 | 0.00% |
|
||||
| tree(max_divergence=2) | 8 | 8 | 5 | 105 | 2513 | 52.69% | 0.336639 | 28.82% |
|
||||
| tree(max_divergence=3) | 8 | 8 | 5 | 105 | 2990 | 30.66% | 0.308791 | 18.16% |
|
||||
| | | | | | | | | |
|
||||
| original implementation | 8 | 8 | 6 | 105 | 5202 | 0.00% | 0.24832 | 0.00% |
|
||||
| tree(max_divergence=2) | 8 | 8 | 6 | 105 | 3348 | 35.64% | 0.27755 | 11.77% |
|
||||
| tree(max_divergence=3) | 8 | 8 | 6 | 105 | 3888 | 25.26% | 0.272339 | 9.67% |
|
||||
@@ -0,0 +1,360 @@
|
||||
# GYM环境训练
|
||||
|
||||
GYM 风格的环境训练把"模型 → 环境 → 奖励"这条链路封装成一个抽象接口,让 LLM 像 Agent 一样与环境进行多轮交互,每一步的奖励直接由环境给出,无需再单独写 reward 函数从轨迹里反推。本文先介绍接口,再用一个完整的自定义示例(FrozenLake)说明如何接入训练。
|
||||
|
||||
## Gym 接口
|
||||
|
||||
GYM 源自 [Gymnasium库](https://github.com/Farama-Foundation/Gymnasium)。在 ms-swift 中我们定义了如下接口:
|
||||
|
||||
```python
|
||||
class Env(ABC):
|
||||
|
||||
def __init__(self, env_config):
|
||||
"""env_config 来自数据集每行的 env_config 列,可承载初始化参数"""
|
||||
self.env_config = env_config
|
||||
|
||||
@abstractmethod
|
||||
async def reset(self, config: RolloutInferRequest) -> Tuple[str, Dict[str, Any], str]:
|
||||
"""
|
||||
Returns:
|
||||
- observation: 作为首轮 user 消息发送给模型
|
||||
- info: 调试/日志信息,记录到 completions.jsonl
|
||||
- system_message: 本条轨迹的 system prompt
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def step(self, action: Messages) -> Tuple[str, float, bool, Dict[str, Any]]:
|
||||
"""
|
||||
Args:
|
||||
action: 截止当前的完整对话消息,最后一条即模型最新回复
|
||||
Returns:
|
||||
- next_observation: 下一轮 user 消息
|
||||
- reward: 当前 step 奖励
|
||||
- done: 轨迹是否结束
|
||||
- info: 调试/日志信息
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def close(self):
|
||||
"""释放资源"""
|
||||
pass
|
||||
```
|
||||
|
||||
`reset` 接收到的 `RolloutInferRequest` 包含数据集行的 `messages`、`data_dict`(额外列,包括 `env_config`)等。完整示例参见 [入参示例](./multi_turn.md#多轮规划器-multiturnscheduler)。
|
||||
|
||||
> 如果需要在每轮 rollout 之间额外控制对话历史(例如动态压缩、注入额外提示),推荐直接继承 `MultiTurnScheduler` 并实现 `on_trajectory_start` / `on_turn_end` hook,或重写 `step` / `run` 方法,详见[多轮训练文档](./multi_turn.md#自定义多轮交互逻辑)。
|
||||
|
||||
## 启动训练
|
||||
|
||||
使用内置的 [gym_scheduler](https://github.com/modelscope/ms-swift/blob/main/swift/rollout/multi_turn.py) 把 env 串到多轮 rollout 中。
|
||||
|
||||
`GYMScheduler` 基于通用 hook 协议实现:
|
||||
- 继承 `MultiTurnScheduler`,无需自定义 `run` 方法
|
||||
- 实现 `on_trajectory_start`(调用 `env.reset`)和 `on_turn_end`(调用 `env.step`)
|
||||
- 同时适用于 server mode(`run()`)和 colocate mode(`run_multi_turn()`)
|
||||
|
||||
用户自定义的 env 通过 `--external_plugins your_plugin.py` 加载,plugin 里执行 `envs['my_env'] = MyEnv` 完成注册(下文 FrozenLake 示例完整演示)。
|
||||
|
||||
**Colocate 模式**:
|
||||
|
||||
```bash
|
||||
megatron rlhf \
|
||||
--rlhf_type grpo \
|
||||
--vllm_mode colocate \
|
||||
--external_plugins examples/megatron/grpo/multi_turn/frozen_lake_plugin.py \
|
||||
--multi_turn_scheduler gym_scheduler \
|
||||
--gym_env frozen_lake \
|
||||
--use_gym_env true \
|
||||
--max_turns 10 \
|
||||
...
|
||||
|
||||
# swift rlhf 同理
|
||||
```
|
||||
|
||||
|
||||
**Server 模式**
|
||||
|
||||
```bash
|
||||
swift rollout \
|
||||
--model xxx \
|
||||
--use_gym_env true \
|
||||
--external_plugins examples/megatron/grpo/multi_turn/frozen_lake_plugin.py \
|
||||
--multi_turn_scheduler gym_scheduler \
|
||||
--gym_env frozen_lake \
|
||||
--max_turns 10
|
||||
|
||||
# trainer 侧需要加 --vllm_server_pass_dataset true,把 env_config 等额外列透传给 rollout 端
|
||||
megatron rlhf --vllm_mode server --vllm_server_pass_dataset true ...
|
||||
# or swift rlhf --vllm_mode server --vllm_server_pass_dataset true ...
|
||||
```
|
||||
|
||||
|
||||
环境选择有两种方式:
|
||||
- 通过 `--gym_env env_name` 全局指定(同一脚本里所有 prompt 共用一个 env);
|
||||
- 在每行数据的 `env_config.name` 中指定(适用于多环境混合场景,每条数据可指向不同 env,会覆盖 `--gym_env`)。
|
||||
|
||||
## 示例:从零写一个 FrozenLake 环境
|
||||
|
||||
<img src="https://gymnasium.farama.org/_images/frozen_lake.gif" width="220" alt="FrozenLake 环境示意图(来源:Gymnasium 官方文档)" />
|
||||
|
||||
[FrozenLake](https://gymnasium.farama.org/environments/toy_text/frozen_lake/) 是 OpenAI Gym 中的经典任务:智能体从起点出发,需要穿过一片冰湖到达终点,途中要避开冰窟。原始环境如上图所示。下面以纯文本版本(把上图网格直接渲染成 ASCII 字符)为例。
|
||||
|
||||
以下完整代码参考完整代码:[frozen_lake_plugin](https://github.com/modelscope/ms-swift/blob/main/examples/megatron/grpo/multi_turn/frozen_lake_plugin.py)。
|
||||
|
||||
**1. 定义 Env**
|
||||
|
||||
每条数据派生一张随机 4x4 地图(随机洞 + 随机 S/G 位置,BFS 校验保证可解)。单元含义:`S` 起点 / `G` 终点 / `H` 冰窟(踩到=失败)/ `F` 安全冰面 / `P` 玩家当前位置。
|
||||
|
||||
```python
|
||||
class FrozenLakeEnv(Env):
|
||||
def __init__(self, env_config):
|
||||
super().__init__(env_config)
|
||||
self.size = int(env_config.get('size', 4))
|
||||
self.p = float(env_config.get('p', 0.8))
|
||||
seed = env_config.get('seed')
|
||||
self.seed = int(seed) if seed is not None else None
|
||||
|
||||
async def reset(self, config: RolloutInferRequest):
|
||||
self.grid = generate_random_map(size=self.size, p=self.p, seed=self.seed)
|
||||
...
|
||||
return observation, {'seed': self.seed}, SYSTEM_PROMPT
|
||||
|
||||
async def step(self, action: Messages):
|
||||
move = _parse_action(action[-1]['content']) # <action>up|down|left|right</action>
|
||||
# 推进一格、判断 G / H;外层 max_turns 由 scheduler 兜底
|
||||
if cell == 'G': return obs, 1.0, True, {'status': 'goal'}
|
||||
if cell == 'H': return obs, 0.0, True, {'status': 'hole'}
|
||||
...
|
||||
```
|
||||
|
||||
**2. GYMScheduler 的 hook 实现**
|
||||
|
||||
框架内置的 `GYMScheduler` 基于多轮 hook 完成了控制逻辑:
|
||||
|
||||
```python
|
||||
class GYMScheduler(MultiTurnScheduler):
|
||||
def on_trajectory_start(self, requests):
|
||||
# 为每个请求创建 env,调用 env.reset,注入初始 observation
|
||||
for req in requests:
|
||||
env = self._create_env(req.data_dict.get('env_config', {}))
|
||||
observation, info, system_message = env.reset(req)
|
||||
req.messages = [system_msg, user_msg(observation)]
|
||||
self._envs[req.uuid] = env
|
||||
|
||||
def on_turn_end(self, req, response_choice, current_turn):
|
||||
# 调用 env.step,累积 reward,返回 done + rollout_infos
|
||||
next_obs, reward, done, info = env.step(deepcopy(req.messages))
|
||||
self._total_rewards[req.uuid] += reward
|
||||
return {
|
||||
'done': done,
|
||||
'rollout_infos': {
|
||||
'total_reward': self._total_rewards[req.uuid],
|
||||
'step_rewards': [...],
|
||||
}
|
||||
}
|
||||
|
||||
def step(self, req, response_choice, current_turn):
|
||||
# 注入下一帧 observation 到 user message
|
||||
if self._pending_obs.get(req.uuid):
|
||||
req.messages.append({'role': 'user', 'content': next_obs})
|
||||
return {'infer_request': req}
|
||||
```
|
||||
|
||||
用户只需实现 Env 接口,无需关心多轮控制细节。
|
||||
|
||||
**3. 注册**
|
||||
|
||||
将 env 类挂到 swift 的 `envs` 注册表里。`--external_plugins` 在训练启动时会 import 该文件,注册随之生效:
|
||||
|
||||
```python
|
||||
# examples/megatron/grpo/multi_turn/frozen_lake_plugin.py
|
||||
from swift.rollout.gym_env import Env, envs
|
||||
|
||||
class FrozenLakeEnv(Env):
|
||||
...
|
||||
|
||||
envs['frozen_lake'] = FrozenLakeEnv
|
||||
```
|
||||
|
||||
**4. 准备数据集**
|
||||
|
||||
数据集在这里仅作占位符处理,数据构造由环境生成,和 `env_config.seed`来控制地图生成的随机性:
|
||||
|
||||
```json
|
||||
{"messages":[{"role":"user","content":"<placeholder>"}],"env_config":{"seed":0}}
|
||||
{"messages":[{"role":"user","content":"<placeholder>"}],"env_config":{"seed":1}}
|
||||
...
|
||||
{"messages":[{"role":"user","content":"<placeholder>"}],"env_config":{"seed":127}}
|
||||
```
|
||||
|
||||
**5. (可选)叠加自定义 reward**
|
||||
|
||||
设置 `--use_gym_env true` 后,env 给出的 `total_reward` 会自动作为一路奖励参与训练,无需再写 reward 函数。如果想在此之外再叠加自定义信号(如格式/长度等),通过 `--reward_funcs` 传入即可,gym 奖励会作为额外一列与 reward_funcs 拼在一起,由 `--reward_weights` 统一加权。例如同时启用一个格式校验 reward:
|
||||
|
||||
```bash
|
||||
megatron rlhf ... --use_gym_env true --reward_funcs format --reward_weights 0.2 1.0
|
||||
# reward_weights 末位对应 gym 的 total_reward
|
||||
```
|
||||
|
||||
**6. 训练**
|
||||
|
||||
运行脚本参考:[`examples/megatron/grpo/multi_turn/frozen_lake.sh`](https://github.com/modelscope/ms-swift/blob/main/examples/megatron/grpo/multi_turn/frozen_lake.sh)
|
||||
|
||||
|
||||
## OpenEnv 环境训练
|
||||
|
||||
[OpenEnv](https://github.com/huggingface/openenv) 是 HuggingFace 开源的 Agentic RL 环境框架,通过 WebSocket 与环境服务器交互。与上文 FrozenLake 的本地 `Env` 接口不同,OpenEnv 将环境逻辑放在独立的服务进程中,swift 通过 `OpenEnvScheduler` + `OpenEnvWrapper` 与之通信。
|
||||
|
||||
### 架构对比
|
||||
|
||||
| 特性 | 内置 Gym (`GYMScheduler`) | OpenEnv (`OpenEnvScheduler`) |
|
||||
|------|--------------------------|------------------------------|
|
||||
| 环境运行位置 | 训练进程内(Python 对象) | 独立服务器(WebSocket 通信) |
|
||||
| 环境接口 | 继承 `Env`,实现 `reset/step/close` | 服务器提供 HTTP/WebSocket API |
|
||||
| 注册方式 | `--external_plugins` + `envs` 注册表 | `--external_plugins` + `multi_turns` 注册表 |
|
||||
| 适用场景 | 轻量本地环境(FrozenLake 等) | 复杂服务端环境(TextArena、CARLA 等) |
|
||||
| 并发控制 | 无需 | 内置 Semaphore 限制并发连接 |
|
||||
|
||||
### OpenEnvScheduler
|
||||
|
||||
`OpenEnvScheduler` 继承 `GYMScheduler`,将本地 `Env` 替换为 `OpenEnvWrapper`(WebSocket 客户端)。核心设计:
|
||||
|
||||
- **`_create_env`**:创建 `OpenEnvWrapper`,连接 OpenEnv 服务器
|
||||
- **`on_trajectory_start`**:为每个请求创建 wrapper,调用 `reset()`,用 Semaphore 限制并发(默认 4)
|
||||
- **`on_turn_end`**:解析模型输出,调用 `wrapper.step()`,累积奖励
|
||||
- **`parse_action`**(可覆盖):将模型文本解析为 action dict,默认 `json.loads`
|
||||
- **`format_observation`**(可覆盖):将服务器返回的 observation 格式化为字符串,默认 `json.dumps`
|
||||
|
||||
用户通过继承 `OpenEnvScheduler` 并覆盖 `parse_action`、`format_observation`、`on_trajectory_start`、`on_turn_end` 来适配具体环境。
|
||||
|
||||
### 示例:Sudoku 环境
|
||||
|
||||
以 TextArena Sudoku 为例,模型需要通过 `[row col number]` 格式下棋,在 9x9 数独棋盘上填入数字。完整代码参考:[sudoku_scheduler.py](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/openenv/sudoku_scheduler.py)。
|
||||
|
||||
**1. 启动 OpenEnv 服务器**
|
||||
|
||||
安装 OpenEnv 和 Sudoku 环境包(textarena 和 nltk 会作为依赖自动安装):
|
||||
|
||||
```bash
|
||||
pip install openenv
|
||||
pip install git+https://huggingface.co/spaces/openenv/sudoku
|
||||
```
|
||||
|
||||
使用提供的启动脚本启动本地服务器(默认端口 8000)。`MAX_CONCURRENT_ENVS` 需 ≥ 训练时的 `num_generations`:
|
||||
|
||||
```bash
|
||||
TEXTARENA_ENV_ID=Sudoku-v0 MAX_CONCURRENT_ENVS=8 python examples/train/grpo/plugin/openenv/start_sudoku_server.py
|
||||
```
|
||||
|
||||
数据集中将 `base_url` 指向本地服务器地址:
|
||||
|
||||
```json
|
||||
{"messages":[{"role":"user","content":"Play"}],"env_config":{"name":"openenv","base_url":"http://127.0.0.1:8000"}}
|
||||
```
|
||||
|
||||
**2. 自定义 Scheduler**
|
||||
|
||||
继承 `OpenEnvScheduler`,实现 Sudoku 专用的动作解析、观察格式化和多组件奖励:
|
||||
|
||||
```python
|
||||
from swift.rollout.multi_turn import OpenEnvScheduler
|
||||
|
||||
class SudokuScheduler(OpenEnvScheduler):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._last_content_len = {} # 内容差分跟踪
|
||||
|
||||
async def on_trajectory_start(self, requests):
|
||||
# 创建环境、解析棋盘、生成 hints
|
||||
# hints 包括「保证正确的走法」和候选数字列表
|
||||
...
|
||||
|
||||
async def on_turn_end(self, infer_request, response_choice, current_turn):
|
||||
# 解析 [row col number],step 环境
|
||||
# 计算 5 路奖励:空格选择 / 合法移动 / 重复惩罚 / 进度 / 正确
|
||||
# 返回更新后的棋盘 + hints 作为下一轮观察
|
||||
...
|
||||
|
||||
def parse_action(self, text):
|
||||
import re
|
||||
match = re.search(r'\[\s*(\d+)\s+(\d+)\s+(\d+)\s*\]', text)
|
||||
if match:
|
||||
row, col, num = match.groups()
|
||||
return {"message": f"[{row} {col} {num}]"}
|
||||
return {"message": "[1 1 1]"}
|
||||
```
|
||||
|
||||
**多组件奖励系统**(参考 [TRL Sudoku 示例](https://github.com/huggingface/trl/blob/main/examples/notebooks/openenv_sudoku_grpo.ipynb)):
|
||||
|
||||
| 奖励组件 | 计算方式 | 作用 |
|
||||
|---------|---------|------|
|
||||
| `empty_cell_reward` | 目标是空格 +1 / 覆盖已有 -1 | 引导模型选择合法位置 |
|
||||
| `valid_move_reward` | 合法新走法 +1 / 警告 -0.5 / 无效 0 | 鼓励合法操作 |
|
||||
| `repetition_reward` | 重复走法指数惩罚(-2^n,上限 -10) | 避免重复 |
|
||||
| `progress_reward` | (已填充 - 初始) / (81 - 初始) | 衡量解题进度 |
|
||||
| `correct_reward` | 环境返回的二值奖励 | 完全解出 |
|
||||
|
||||
组合奖励 = 各组件均值之和,提供比单一二值奖励更密集的学习信号。
|
||||
|
||||
**3. Hints 系统**
|
||||
|
||||
每轮交互中,scheduler 解析当前棋盘状态,为模型提供提示:
|
||||
|
||||
- **GUARANTEED MOVES**:只有一个候选数字的空格(可直接填入)
|
||||
- **Other options**:2-3 个候选数字的空格
|
||||
- **MOVES ALREADY TRIED**:已尝试过的走法(避免重复)
|
||||
|
||||
**4. 准备数据集**
|
||||
|
||||
数据集仅作占位符,实际棋盘由环境服务器生成。`base_url` 指向 OpenEnv 托管地址:
|
||||
|
||||
```json
|
||||
{"messages":[{"role":"user","content":"Play"}],"env_config":{"name":"openenv","base_url":"http://127.0.0.1:8000"}}
|
||||
```
|
||||
|
||||
**5. 注册 Scheduler**
|
||||
|
||||
`sudoku_scheduler.py` 末尾已包含注册代码,通过 `--external_plugins` 加载即可:
|
||||
|
||||
```python
|
||||
# sudoku_scheduler.py 末尾
|
||||
from swift.rollout.multi_turn import multi_turns
|
||||
multi_turns['sudoku_scheduler'] = SudokuScheduler
|
||||
```
|
||||
|
||||
**6. 启动训练**
|
||||
|
||||
```bash
|
||||
swift rlhf \
|
||||
--rlhf_type grpo \
|
||||
--model Qwen/Qwen3.5-4B \
|
||||
--dataset examples/train/grpo/plugin/openenv/sudoku.jsonl \
|
||||
--external_plugins examples/train/grpo/plugin/openenv/sudoku_scheduler.py \
|
||||
--enable_thinking false \
|
||||
--max_completion_length 256 \
|
||||
--use_gym_env true \
|
||||
--multi_turn_scheduler sudoku_scheduler \
|
||||
--max_turns 20 \
|
||||
--use_vllm true \
|
||||
--vllm_mode colocate \
|
||||
...
|
||||
```
|
||||
|
||||
运行脚本参考:[`examples/train/grpo/plugin/openenv/run_grpo_sudoku.sh`](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/openenv/run_grpo_sudoku.sh)
|
||||
|
||||
### 注意事项
|
||||
|
||||
- **vLLM 模式**:以上示例使用 `--vllm_mode colocate`,vLLM 与训练共享 GPU。若使用 `--vllm_mode server`,需额外启动 `swift rollout` 作为 vLLM 服务器,且 `--multi_turn_scheduler` 和 `--max_turns` 参数应传给 `swift rlhf` 而非 `swift rollout`。
|
||||
- **并发会话数**:`start_sudoku_server.py` 的 `MAX_CONCURRENT_ENVS` 需 ≥ 训练时的 `num_generations`。默认的 `python -m textarena_env.server.app` 只支持 1 个并发会话。
|
||||
- **enable_thinking**:Sudoku 等环境不需要 CoT 推理,建议设置 `--enable_thinking false` 以减少 token 消耗。
|
||||
- **同步 I/O**:`OpenEnvWrapper` 的 `reset()`/`step()` 是同步 WebSocket 调用。`OpenEnvScheduler` 的子类应使用 `asyncio.to_thread()` 包装这些调用以避免阻塞事件循环。
|
||||
|
||||
|
||||
参考资料:
|
||||
|
||||
- https://gymnasium.farama.org/environments/toy_text/frozen_lake/
|
||||
- https://github.com/alibaba/ROLL/tree/main/roll/pipeline/agentic/env/frozen_lake
|
||||
- https://github.com/huggingface/openenv
|
||||
- https://github.com/huggingface/trl/blob/main/examples/notebooks/openenv_sudoku_grpo.ipynb
|
||||
@@ -0,0 +1,11 @@
|
||||
Developer Guide
|
||||
===============
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
loss_types.md
|
||||
multi_turn.md
|
||||
multi_task.md
|
||||
reward_function.md
|
||||
reward_model.md
|
||||
gym_env.md
|
||||
@@ -0,0 +1,131 @@
|
||||
# Loss Types
|
||||
|
||||
GRPO训练支持多种不同的loss类型,主要区别在于归一化的维度和梯度处理方式上有所不同。
|
||||
|
||||
## 损失函数
|
||||
|
||||
token 级别上,GRPO 训练使用以下损失函数
|
||||
|
||||
$$\mathcal{L}_{i,t} = -\min\left(\rho_{i,t} A_{i,t}, \text{clip}(\rho_{i,t}, 1-\epsilon, 1+\epsilon) A_{i,t}\right)$$
|
||||
|
||||
当设置`loss_type cispo`时,使用 cispo 损失
|
||||
|
||||
$$\mathcal{L}_{i,t}^{\text{CISPO}} = -\text{detach}\left(\min(\rho_{i,t}, \epsilon_{\text{high}})\right) \cdot A_{i,t} \cdot \log \pi_\theta(y_{i,t}|y_{i,<t})$$
|
||||
|
||||
当设置`loss_type sapo`时,使用软门控替代硬裁剪,详见 [SAPO](../AdvancedResearch/SAPO.md)
|
||||
|
||||
$$\mathcal{L}_{i,t}^{\text{SAPO}} = -g_{i,t} \cdot A_{i,t}$$
|
||||
|
||||
其中 $g_{i,t} = \sigma(\tau \cdot (\rho_{i,t} - 1))$ 是温度控制的软门控函数。
|
||||
|
||||
其中:
|
||||
- $\rho_{i,t} = \frac{\pi_\theta(y_{i,t}|y_{i,<t})}{\pi_{\theta_{\text{old}}}(y_{i,t}|y_{i,<t})}$ 是重要性采样权重
|
||||
- $A_{i,t}$ 是优势函数
|
||||
- $\epsilon$ 和 $\epsilon_{\text{high}}$ 是clipping参数
|
||||
- $\text{detach}(\cdot)$ 表示该项不参与梯度计算
|
||||
- $\sigma(\cdot)$ 是 sigmoid 函数,$\tau$ 是温度参数
|
||||
|
||||
## GRPO
|
||||
|
||||
`--loss_type grpo`
|
||||
|
||||
GRPO是标准的损失函数实现,对每个样本的token-level损失取平均,然后对所有样本取平均。
|
||||
|
||||
**公式:**
|
||||
|
||||
$$\mathcal{L}_{\text{GRPO}} = \frac{1}{N} \sum_{i=1}^{N} \frac{1}{T_i} \sum_{t=1}^{T_i} \mathcal{L}_{i,t}$$
|
||||
|
||||
其中:
|
||||
- $N$ 是批次中的样本数量
|
||||
- $T_i$ 是第$i$个样本的completion token数量
|
||||
|
||||
**归一化维度:** 样本维度(先对每个样本的所有token取平均,再对所有样本取平均)
|
||||
|
||||
## BNPO (Batch Normalized Policy Optimization)
|
||||
|
||||
`--loss_type bnpo`
|
||||
|
||||
BNPO将所有样本的所有token的损失直接求和,然后除以所有completion token的总数量。
|
||||
|
||||
**公式:**
|
||||
|
||||
$$\mathcal{L}_{\text{BNPO}} = \frac{\sum_{i=1}^{N} \sum_{t=1}^{T_i} \mathcal{L}_{i,t}}{\sum_{i=1}^{N} T_i}$$
|
||||
|
||||
其中:
|
||||
- $N$ 是批次中的样本数量
|
||||
- $T_i$ 是第$i$个样本的completion token数量
|
||||
|
||||
**归一化维度:** Token维度(对所有completion token取平均)
|
||||
|
||||
## DR-GRPO
|
||||
|
||||
`--loss_type dr_grpo`
|
||||
|
||||
DR-GRPO将所有样本的所有token的损失求和,然后除以批次大小乘以最大completion长度。
|
||||
|
||||
**公式:**
|
||||
|
||||
$$\mathcal{L}_{\text{DR-GRPO}} = \frac{\sum_{i=1}^{N} \sum_{t=1}^{T_i} \mathcal{L}_{i,t}}{N \times L_{\text{max}}}$$
|
||||
|
||||
其中:
|
||||
- $N$ 是批次中的样本数量
|
||||
- $T_i$ 是第$i$个样本的completion token数量
|
||||
- $L_{\text{max}}$ 是最大completion长度
|
||||
|
||||
**归一化维度:** 固定维度(批次大小 × 最大completion长度)
|
||||
|
||||
## CISPO
|
||||
|
||||
`--loss_type cispo`
|
||||
|
||||
CISPO损失按所有进程的completion token总数进行归一化。
|
||||
|
||||
**公式:**
|
||||
|
||||
$$\mathcal{L}_{\text{CISPO}} = \frac{\sum_{i=1}^{N} \sum_{t=1}^{T_i} \mathcal{L}_{i,t}^{\text{CISPO}}}{\sum_{\text{all processes}} \sum_{i=1}^{N_p} T_{p,i}}$$
|
||||
|
||||
其中:
|
||||
- $N$ 是当前进程批次中的样本数量
|
||||
- $T_i$ 是第$i$个样本的completion token数量
|
||||
- $N_p$ 是第$p$个进程的样本数量
|
||||
|
||||
**归一化维度:** 全局token维度(跨所有进程的completion token总数)
|
||||
|
||||
## DAPO
|
||||
|
||||
`--loss_type dapo`
|
||||
|
||||
DAPO与BNPO类似,使用token-level归一化,但基于全局数据(多进程)进行归一化。
|
||||
|
||||
**公式:**
|
||||
|
||||
$$\mathcal{L}_{\text{DAPO}} = \frac{\sum_{i=1}^{N} \sum_{t=1}^{T_i} \mathcal{L}_{i,t}}{\sum_{\text{all processes}} \sum_{i=1}^{N_p} T_{p,i}}$$
|
||||
|
||||
其中:
|
||||
- $N$ 是当前进程批次中的样本数量
|
||||
- $T_i$ 是第$i$个样本的completion token数量
|
||||
- $N_p$ 是第$p$个进程的样本数量
|
||||
|
||||
**归一化维度:** 全局token维度(跨所有进程的completion token总数)
|
||||
|
||||
## FIPO
|
||||
|
||||
`--loss_type fipo`
|
||||
|
||||
FIPO 在 DAPO/GRPO 的 clipped policy loss 上引入 Future-KL influence weight。每个 token 的序列级 advantage 会乘以从当前位置到后续 token 的折扣累积 KL 位移得到的权重:
|
||||
|
||||
$$f_{i,t} = \text{clip}\left(\exp\left(\sum_{k=t}^{T_i} \gamma^{k-t} M_{i,k} \Delta \log p_{i,k}\right), 1-\epsilon_f, 1+\epsilon_f\right)$$
|
||||
|
||||
$$\mathcal{L}_{i,t}^{\text{FIPO}} = f_{i,t} \cdot \mathcal{L}_{i,t}$$
|
||||
|
||||
FIPO 的 influence weight 默认不参与梯度计算,并使用与 DAPO 相同的全局 token 归一化。
|
||||
|
||||
**归一化维度:** 全局 token 维度(所有进程的 completion token 总数)
|
||||
|
||||
## SAPO
|
||||
|
||||
`--loss_type sapo`
|
||||
|
||||
SAPO使用温度控制的软门控替代硬裁剪,实现平滑的梯度衰减。归一化方式与GRPO相同。
|
||||
|
||||
详细说明请参考 [SAPO](../AdvancedResearch/SAPO.md)
|
||||
@@ -0,0 +1,51 @@
|
||||
# 多任务训练
|
||||
我们可以在数据集中添加一个用于标识任务类型的列,并在奖励函数/奖励模型插件中根据任务类型进行判断,从而实现多任务训练。假设数据集中包含数学和编程任务,比如:
|
||||
|
||||
```
|
||||
{"query": "Solve the equation x + 2 = 5", "solution": "3", "task": "math"},
|
||||
{"query": "Write a function to calculate the Fibonacci sequence", "solution": "xxx", "task": "code"},
|
||||
{"query": "What is the integral of x^2?", "solution": "xxx", "task": "math"},
|
||||
{"query": "Implement a sorting algorithm in Python", "solution": "xxx", "task": "code"},
|
||||
```
|
||||
|
||||
我们可以设置不同的奖励函数来分别处理数学数据和代码数据,注意数据集中的列会传入奖励函数,所以我们可以通过 `task` 列
|
||||
|
||||
下面是针对不同任务的奖励函数的示例:
|
||||
|
||||
```python
|
||||
from swift.rewards import ORM, orms
|
||||
import random
|
||||
|
||||
# Math-specific reward function
|
||||
class MathRandomReward(ORM):
|
||||
def __call__(self, completions, task, **kwargs):
|
||||
rewards = []
|
||||
for completion, t in zip(completions, task):
|
||||
if t == "math":
|
||||
import random
|
||||
# imple math accuracy logic
|
||||
reward = random.random()
|
||||
rewards.append(reward)
|
||||
else:
|
||||
# Return None for non-math tasks
|
||||
rewards.append(None)
|
||||
return rewards
|
||||
|
||||
# Coding-specific reward function
|
||||
class CodeRandomReward(ORM):
|
||||
def __call__(self, completions, task, **kwargs):
|
||||
rewards = []
|
||||
for prompt, completion, t in zip(prompts, completions, task):
|
||||
if t == "code":
|
||||
# imple coding accuracy logic
|
||||
reward = random.random()
|
||||
rewards.append(reward)
|
||||
else:
|
||||
# Return None for non-coding tasks
|
||||
rewards.append(None)
|
||||
return rewards
|
||||
|
||||
orms['math_reward'] = MathRandomReward
|
||||
orms['code_reward'] = CodeRandomReward
|
||||
```
|
||||
对于非当前任务的数据, 通过返回 None 来处理,从而使得奖励相关仅计算任务内的数据。
|
||||
@@ -0,0 +1,312 @@
|
||||
# 多轮训练
|
||||
|
||||
在强化学习训练场景中,模型采样可能需要与环境进行多轮交互(如工具调用)。这种交互式训练要求模型能够根据环境反馈信息进行连续推理。本文档将详细介绍如何在 GRPO 训练中自定义多轮训练流程。
|
||||
|
||||
> GKD 同样支持多轮训练,与 GRPO 共享同一套 `MultiTurnScheduler` 基础设施。
|
||||
|
||||
以下是多轮训练示例图,模型可能涉及多轮 rollout,包括环境交互、工具调用等步骤:
|
||||
|
||||

|
||||
|
||||
## 多轮规划器 MultiTurnScheduler
|
||||
|
||||
`MultiTurnScheduler` 是一个抽象基类,提供了默认的多轮对话管理逻辑,其工作流程如下图所示:
|
||||
|
||||
<img src="https://raw.githubusercontent.com/modelscope/ms-swift/main/docs/resources/multiturn_pipeline.png " width="300" />
|
||||
|
||||
多轮规划器主要承担两大核心功能:
|
||||
- **终止条件判断**:通过 `check_finished` 方法判断当前轮次推理是否应该结束
|
||||
- **推理请求构造**:通过 `step` 方法构建下一轮推理的请求对象
|
||||
|
||||
抽象基类 `MultiTurnScheduler` 的核心方法如下:
|
||||
|
||||
```python
|
||||
class MultiTurnScheduler(ABC):
|
||||
|
||||
def __init__(self, max_turns: Optional[int] = None, *args, **kwargs):
|
||||
self.max_turns = max_turns
|
||||
|
||||
def on_trajectory_start(self, requests: List['RolloutInferRequest']) -> None:
|
||||
"""在首轮推理前调用,用于初始化轨迹级别状态。
|
||||
|
||||
可在此方法中直接修改 requests(如注入环境初始 observation)。
|
||||
默认实现为空(no-op)。
|
||||
"""
|
||||
pass
|
||||
|
||||
def on_turn_end(self, infer_request: 'RolloutInferRequest',
|
||||
response_choice: 'ChatCompletionResponseChoice',
|
||||
current_turn: int) -> Dict[str, Any]:
|
||||
"""在 assistant 消息追加后、check_finished 前调用。
|
||||
|
||||
用于推进环境状态(如 env.step)并返回每轮元数据。
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: 可选包含以下键:
|
||||
- 'done' (bool): 若存在,将覆盖 check_finished 的结果
|
||||
- 'rollout_infos' (dict): 合并到轨迹累积的额外信息中
|
||||
默认返回空字典(no-op)。
|
||||
"""
|
||||
return {}
|
||||
|
||||
def step(self, infer_request: 'RolloutInferRequest', response_choice: 'ChatCompletionResponseChoice',
|
||||
current_turn: int) -> Dict:
|
||||
"""
|
||||
处理对话轮次之间的转换。
|
||||
|
||||
Args:
|
||||
infer_request: 当前推理请求
|
||||
response_choice: 当前轮次的响应
|
||||
current_turn: 当前轮次数
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: 包含推理结果的字典,结构如下:
|
||||
- infer_request (必需): 下一轮的推理请求对象
|
||||
- response_token_ids (可选): 每个 rollout 轮次的响应 token IDs
|
||||
- response_loss_mask (可选): 每个 rollout 轮次响应的损失掩码
|
||||
- rollout_logprobs (可选): 每个 rollout 轮次的响应对应的 logps
|
||||
- rollout_infos (可选): 额外信息数据
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def check_finished(self, infer_request: 'RolloutInferRequest', response_choice: 'ChatCompletionResponseChoice',
|
||||
current_turn: int) -> bool:
|
||||
"""
|
||||
检查多轮 rollout 是否应该结束的默认终止逻辑。
|
||||
|
||||
默认终止条件:
|
||||
1. 当响应达到长度限制时 (finish_reason == 'length')
|
||||
2. 当对话达到最大轮数时 (如果设置了 max_turns)
|
||||
|
||||
Args:
|
||||
infer_request: 推理请求对象
|
||||
response_choice: 包含生成结果的响应选择,包括 finish_reason
|
||||
current_turn: 当前对话轮数
|
||||
|
||||
Returns:
|
||||
bool: True 表示终止对话,False 表示继续
|
||||
"""
|
||||
if response_choice.finish_reason == 'length':
|
||||
return True
|
||||
if self.max_turns and current_turn >= self.max_turns:
|
||||
return True
|
||||
return False
|
||||
```
|
||||
|
||||
`step` 和 `check_finished` 方法接收的参数说明:
|
||||
- **infer_request**: 当前的推理请求
|
||||
- **response_choice**: 当前轮次的推理结果
|
||||
- **current_turn**: 当前推理轮次(从 1 开始)
|
||||
|
||||
<details><summary>入参示例(点击展开)</summary>
|
||||
|
||||
```python
|
||||
infer_request
|
||||
"""
|
||||
RolloutInferRequest(
|
||||
messages=[
|
||||
{'role': 'system', 'content': 'A conversation between User and Assistant. The user asks a question, and the Assistant solves it. The assistant first thinks about the reasoning process in the mind and then provides the user with the answer. The reasoning process and answer are enclosed within <think> </think> and <answer> </answer> tags, respectively, i.e., <think> reasoning process here </think><answer> answer here </answer>\n'}, {'role': 'user', 'content': 'What is the value of $\\sqrt{36 \\times \\sqrt{16}}$?'},
|
||||
{'role': 'assistant', 'content': 'To find the value of \\(\\sqrt{36 \\times \\sqrt{16}}\\), we will break down the problem step-by-step.\n\nFirst, we need to evaluate the inner square root:\n\\[\n\\sqrt{16}\n\\]\nWe know that:\n\\[\n4^2 = 16 \\implies \\sqrt{16} = 4\n\\]\n\nNext, we substitute this result back into the original expression:\n\\[\n\\sqrt{36 \\times \\sqrt{16}} = \\sqrt{36 \\times 4}\n\\]\n\nNow, we need to evaluate the product inside the square root:\n\\[\n36 \\times 4 = 144\n\\]\n\nSo, the expression simplifies to:\n\\[\n\\sqrt{144}\n\\]\n\nFinally, we determine the square root of 144:\n\\[\n\\sqrt{144} = 12\n\\]\n\nThus, the value of \\(\\sqrt{36 \\times \\sqrt{16}}\\) is:\n\\[\n\\boxed{12}\n\\]'}
|
||||
],
|
||||
images=[],
|
||||
audios=[],
|
||||
videos=[],
|
||||
tools=None,
|
||||
objects={},
|
||||
data_dict={
|
||||
'problem': 'What is the value of $\\sqrt{36 \\times \\sqrt{16}}$?',
|
||||
'solution': "To solve the problem, we need to evaluate the expression \\(\\sqrt{36 \\times \\sqrt{16}}\\).\n\nWe can break down the steps as follows:\n\n1. Evaluate the inner square root: \\(\\sqrt{16}\\).\n2. Multiply the result by 36.\n3. Take the square root of the product obtained in step 2.\n\nLet's compute this step by step using Python code for accuracy.\n```python\nimport math\n\n# Step 1: Evaluate the inner square root\ninner_sqrt = math.sqrt(16)\n\n# Step 2: Multiply the result by 36\nproduct = 36 * inner_sqrt\n\n# Step 3: Take the square root of the product\nfinal_result = math.sqrt(product)\nprint(final_result)\n```\n```output\n12.0\n```\nThe value of \\(\\sqrt{36 \\times \\sqrt{16}}\\) is /\\(\\boxed{12}\\)."
|
||||
}
|
||||
)
|
||||
"""
|
||||
response_choice
|
||||
"""
|
||||
ChatCompletionResponseChoice(
|
||||
index=0,
|
||||
message=ChatMessage(
|
||||
role='assistant',
|
||||
content='To find the value of \\(\\sqrt{36 \\times \\sqrt{16}}\\), we will break down the problem step-by-step.\n\nFirst, we need to evaluate the inner square root:\n\\[\n\\sqrt{16}\n\\]\nWe know that:\n\\[\n4^2 = 16 \\implies \\sqrt{16} = 4\n\\]\n\nNext, we substitute this result back into the original expression:\n\\[\n\\sqrt{36 \\times \\sqrt{16}} = \\sqrt{36 \\times 4}\n\\]\n\nNow, we need to evaluate the product inside the square root:\n\\[\n36 \\times 4 = 144\n\\]\n\nSo, the expression simplifies to:\n\\[\n\\sqrt{144}\n\\]\n\nFinally, we determine the square root of 144:\n\\[\n\\sqrt{144} = 12\n\\]\n\nThus, the value of \\(\\sqrt{36 \\times \\sqrt{16}}\\) is:\n\\[\n\\boxed{12}\n\\]', tool_calls=None),
|
||||
finish_reason='stop',
|
||||
logprobs=None,
|
||||
messages=None)
|
||||
"""
|
||||
# response_choice.messages will be copied at the end of multi-turn inference.
|
||||
```
|
||||
</details>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
默认的 `check_finished` 逻辑会在以下两种情况下停止推理:
|
||||
- 模型回复被截断,即超出了 `max_completion_length`
|
||||
- 模型推理轮数超出了限制的最大轮数
|
||||
|
||||
完整的默认多轮 rollout 逻辑请参考该类的 `run` 方法,我们也可以通过重载`run` 方法来实现自定义多轮逻辑。
|
||||
|
||||
## 设置多轮训练参数
|
||||
|
||||
在 swift rollout 命令中,设置 multi_turn_scheduler 参数指定规划器
|
||||
|
||||
```bash
|
||||
swift rollout \
|
||||
--model Qwen/Qwen3-1.7B \
|
||||
--vllm_use_async_engine true \
|
||||
--multi_turn_scheduler thinking_tips_scheduler \
|
||||
--vllm_max_model_len 32768 \
|
||||
--vllm_gpu_memory_utilization 0.8 \
|
||||
--max_turns 3
|
||||
```
|
||||
|
||||
|
||||
> 通过参数 `external_plugins`,我们可以将本地的多轮规划器注册到 ms-swift 中,具体实现请参考[代码](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/plugin.py)。
|
||||
|
||||
多轮训练脚本请参考[脚本](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/external/vllm_multi_turn.sh)。
|
||||
|
||||
|
||||
对于多轮 rollout,我们使用 AsyncEngine 来实现高效的批量数据异步多轮采样。AsyncEngine 在多轮推理时能够减少推理过程中的计算气泡:
|
||||
|
||||
<img src="https://raw.githubusercontent.com/modelscope/ms-swift/main/docs/resources/asyncengine.png" width="400" />
|
||||
|
||||
在 `rollout` 命令中使用参数 `vllm_use_async_engine` 来指定 engine 的种类(默认使用 async engine):
|
||||
|
||||
> 注意: async engine 仅在 server mode 下可用。
|
||||
|
||||
### GYM 环境训练
|
||||
|
||||
如果你的多轮任务可以建模为标准的 gym environment(`reset` / `step` / 环境直接给奖励),推荐直接复用框架内置的 `gym_scheduler`,并通过实现一个 `Env` 子类来描述任务。
|
||||
|
||||
`GYMScheduler` 基于通用 hook 协议实现,无需重载 `run` 方法:
|
||||
- **`on_trajectory_start`**: 调用 `env.reset` 并注入初始 observation 到首轮 user 消息
|
||||
- **`on_turn_end`**: 调用 `env.step` 推进环境,返回 `{'done': bool, 'rollout_infos': dict}`
|
||||
|
||||
这种设计使得 `GYMScheduler` 同时适用于 server mode(`run()`)和 colocate mode(`run_multi_turn()`),用户只需实现 Env 接口即可。
|
||||
|
||||
完整接口、自定义 env 的步骤参考 [GYM 环境训练文档](./gym_env.md)。
|
||||
|
||||
## 高级设置
|
||||
|
||||
### 自定义多轮交互逻辑
|
||||
|
||||
在以上默认逻辑中,我们用一条轨迹来计算多轮 rollout 的损失,这里需要假设多轮交互的过程中,模型的历史信息没有受到改变。
|
||||
|
||||
而在一些多轮场景中,我们可能需要在多轮 rollout 过程中动态地修改模型的历史信息(比如压缩历史信息),此时,我们需要将每轮的 rollout 单独作为一条轨迹进行训练。
|
||||
|
||||
#### 方式一:使用 hook
|
||||
|
||||
```python
|
||||
class CustomScheduler(MultiTurnScheduler):
|
||||
def on_trajectory_start(self, requests):
|
||||
# 首轮推理前初始化(如环境 reset、注入初始状态)
|
||||
for req in requests:
|
||||
req.messages = [system_msg, user_msg(initial_observation)]
|
||||
|
||||
def on_turn_end(self, req, response_choice, current_turn):
|
||||
# 每轮推理后推进状态,返回 done 和 rollout_infos
|
||||
next_obs, reward, done = self.advance_env(req.messages)
|
||||
return {
|
||||
'done': done,
|
||||
'rollout_infos': {'reward': reward, ...}
|
||||
}
|
||||
```
|
||||
|
||||
这种方式同时适用于 server mode 和 colocate mode,无需重载 `run` 方法。
|
||||
|
||||
#### 方式二:重载 run 方法(完全自定义)
|
||||
|
||||
比较常见的一种场景是对于思考类模型,在实际推理过程中,模型通常只会保留最后一轮的思考内容,而忽略历史模型回复中的思考内容。
|
||||
|
||||
对于这类场景,我们需要重写多轮规划器中的交互逻辑,即重载 `run` 方法,从而单独返回每一轮的 Rollout 的结果。
|
||||
|
||||
框架内置的 `ThinkingModelTipsScheduler` 类展示了如何通过重写 `run()` 方法来实现完全自定义的多轮推理逻辑。请参考[内置多轮调度器实现](https://github.com/modelscope/ms-swift/blob/main/swift/rollout/multi_turn.py)
|
||||
|
||||
**注意**: 这种情况下,相同轨迹的数据会拆分为多条数据,在奖励相关的处理中,需要对相同轨迹的数据分配同样的reward。
|
||||
|
||||
可以在kwargs中获取 trajectory_inputs 获取完整轨迹的数据,具体实现参考[MultiTurnThinkingTips类](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/plugin.py)
|
||||
|
||||
### 多模态数据修改
|
||||
在多模态多轮交互场景下,可能需要在对话过程中动态增删或修改多模态数据,并确保这些变更同步至 trainer。
|
||||
|
||||
实现方式:借助 rollout_infos,通过指定键值覆盖原始数据集的多模态内容。
|
||||
|
||||
现已支持覆盖的键:images、audios、videos。
|
||||
|
||||
具体请参考[DeepEyes Schduler](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/deepeyes/deepeyes_plugin.py#L403-L404)
|
||||
|
||||
### 返回 response token ids
|
||||
在默认的多轮交互流程中,规划器先把模型生成的文本字符串返回给 trainer,trainer 再将其重新 encode 为 token id,用于后续训练。为了避免这一步重复编码的开销,你可以让规划器直接返回 response_token_ids,省去 trainer 侧的再次 encode。
|
||||
|
||||
具体做法如下:
|
||||
|
||||
- 在 response_choice 对象中读取 token_ids 属性,即可获得本次 rollout 生成的 token 序列。
|
||||
- 在 step/run 方法的返回值里加入 response_token_ids,trainer 便能直接使用这些 token id 参与训练,无需重新编码。
|
||||
|
||||
具体实现可以参考[ThinkingModelTipsScheduler](https://github.com/modelscope/ms-swift/blob/main/swift/rollout/multi_turn.py)类
|
||||
|
||||
### 损失掩码
|
||||
|
||||
在工具调用或环境交互返回结果时,若需将返回内容作为模型响应的一部分,建议对这些插入内容进行掩码处理,以确保模型在训练过程中不会对这些外部生成的内容计算损失。
|
||||
|
||||
我们可以通过两种方式设置损失掩码
|
||||
|
||||
**第一种:设置 loss_scale**
|
||||
|
||||
ms-swift 提供 loss_scale 参数来对模型回复部分的内容进行损失缩放设置。比如设置`--loss_scale last_round`,可以将非最后一轮的模型回复的损失置零。我们也可以实现自定义 loss_scale,具体请参考[定制化 loss_scale 文档](../../../Customization/Architecture.md#loss-scale)。
|
||||
|
||||
> 注:在GRPO中,loss_scale 只提供掩码功能,不提供缩放功能。
|
||||
|
||||
**第二种:设置loss_mask**
|
||||
|
||||
在`step`或者`run`方法中设置 response_loss_mask, 可以在规划器中自定义损失掩码。前提需要返回response token ids,返回的 response_loss_mask 需要与 response token ids等长。当返回 response_loss_mask 时,loss_scale 参数失效。
|
||||
|
||||
response_loss_mask 返回可以参考[ToolCallScheduler类](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/plugin.py)
|
||||
|
||||
### 奖励函数相关
|
||||
|
||||
在奖励函数中获取多轮 Rollout 中的信息
|
||||
|
||||
在 `on_turn_end` 方法或 `step/run` 方法中,返回 `rollout_infos` 对象,在奖励函数的 kwargs 中获取 `rollout_infos`:
|
||||
|
||||
```python
|
||||
class Scheduler():
|
||||
def on_turn_end(self, infer_request, response_choice, current_turn):
|
||||
...
|
||||
return {'done': done, 'rollout_infos': extra_dict}
|
||||
|
||||
# 或者在 step 方法中
|
||||
def step(self, infer_request, response_choice, current_turn):
|
||||
...
|
||||
return {'infer_request': infer_request, 'rollout_infos': extra_dict}
|
||||
|
||||
class RewardFunction():
|
||||
def __call__(self, completions, **kwargs):
|
||||
infos = kwargs.get('rollout_infos', {})
|
||||
...
|
||||
```
|
||||
|
||||
### 在 Scheduler 中获取额外的数据集信息
|
||||
|
||||
在训练侧设置参数`--vllm_server_pass_dataset`,可将数据集中的其他列传入多轮规划器。在`infer_request.data_dict`中获取。
|
||||
|
||||
### 训推一致性兼容
|
||||
swift 支持从 vLLM 侧返回 rollout 的 logps 用于纠正训推不一致问题,具体请参考该[文档](../AdvancedResearch/training_inference_mismatch.md)
|
||||
|
||||
在多轮训练中,如果启用了 `rollout_importance_sampling_mode`,框架会自动收集每轮 rollout 的 log probabilities,用于校正训推不一致带来的 off-policy 问题。
|
||||
|
||||
**默认行为**:
|
||||
- 使用默认的 `run` 方法时,框架会自动从 `response_choice.logprobs` 中提取 log probabilities
|
||||
- 这些 logprobs 会与 `response_token_ids` 和 `response_loss_mask` 一起传递给 trainer
|
||||
|
||||
**自定义 Scheduler 的注意事项**:
|
||||
|
||||
如果你在 `step` 方法中修改了 response(如截断、添加内容),需要同步返回对应的 `rollout_logprobs`
|
||||
|
||||
**关键规则**:
|
||||
- `rollout_logprobs` 的长度应该等于 `response_loss_mask` 中值为 1 的数量
|
||||
- 对于 `loss_mask=0` 的 token(如用户添加的提示、工具返回结果),不需要提供 logprobs
|
||||
- 如果 `step` 方法没有返回 `rollout_logprobs`,框架会自动从 `response_choice.logprobs` 中提取
|
||||
|
||||
**重写 `run` 方法的场景**:
|
||||
|
||||
如果你完全重写了 `run` 方法,需要手动收集和传递 `rollout_logprobs`
|
||||
|
||||
具体的实现请参考[内置实现](https://github.com/modelscope/ms-swift/blob/main/swift/rollout/multi_turn.py)
|
||||
@@ -0,0 +1,146 @@
|
||||
# 奖励函数
|
||||
## 自定义奖励函数
|
||||
奖励函数接受模型生成的文本 completions 其他数据集中的列以及训练器状态作为参数(kwargs)进行打分, 其中[训练器状态](https://huggingface.co/docs/transformers/main/main_classes/callback#transformers.TrainerState)包含训练的步数等信息。
|
||||
|
||||
注意:模型输入相关的列(比如query,response)会被处理为 messages 键,原数据集中的 assistant response 会被舍弃,请使用额外的列进行保留。
|
||||
相关处理的列名参考[文档](../../../Customization/Custom-dataset.md#query-response格式)
|
||||
|
||||
以下是一个示例,展示了如何实现一个简单的长度奖励函数。该函数会在模型生成的文本长度超过 1024 时,给予 1.0 的奖励信号;否则,奖励信号为 0.0。
|
||||
|
||||
```python
|
||||
from swift.rewards import ORM, orms
|
||||
class DummyLengthRewardFunction(ORM)
|
||||
def __call__(completions, **kwargs):
|
||||
return [1.0 if len(completion) > 1024 else 0.0 for completion in completions]
|
||||
|
||||
orms['dummy']= DummyLengthRewardFunction
|
||||
```
|
||||
|
||||
**获取数据集中的其他列**
|
||||
|
||||
比如奖励函数需要获取数据集`solution`列、当前训练步数和总步数作为辅助计算,以下是两种获取方式
|
||||
|
||||
第一种:在__call__入参中显式定义列名
|
||||
```python
|
||||
def __call__(completions, solution, trainer_state, **kwargs):
|
||||
print(solution)
|
||||
global_step = trainer_state.global_step
|
||||
max_steps = trainer_state.max_steps
|
||||
...
|
||||
```
|
||||
|
||||
第二种:在kwargs中获取
|
||||
```python
|
||||
def __call__(completions, **kwargs):
|
||||
solution = kwargs.get('solution')
|
||||
trainer_state = kwargs.get('trainer_state')
|
||||
global_step = trainer_state.global_step
|
||||
max_steps = trainer_state.max_steps
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
**使用自定义奖励函数**
|
||||
|
||||
可以在`swift/examples/train/grpo/plugin/plugin.py`中加入该奖励函数,使用参数`--external_plugins examples/train/grpo/plugin/plugin.py`进行注册,并通过 reward_funcs 参数进行指定
|
||||
|
||||
执行脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo/plugin/run_external_reward_func.sh)
|
||||
|
||||
## 异步奖励函数
|
||||
|
||||
对于涉及 I/O 操作的奖励函数(如 API 调用、数据库查询等),可以使用异步(async)奖励函数来提高性能。异步奖励函数使用 `asyncio.gather` 并行执行,可以显著加速奖励计算。
|
||||
|
||||
```python
|
||||
from swift.rewards import AsyncORM, orms
|
||||
import asyncio
|
||||
|
||||
class AsyncAPIReward(AsyncORM):
|
||||
async def __call__(self, completions, **kwargs):
|
||||
import aiohttp
|
||||
|
||||
async def score_single(session, text):
|
||||
async with session.post(
|
||||
'https://api.example.com/score',
|
||||
json={'text': text}
|
||||
) as resp:
|
||||
result = await resp.json()
|
||||
return result['score']
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
# 使用 asyncio.gather 并行发送所有请求
|
||||
tasks = [score_single(session, c) for c in completions]
|
||||
rewards = await asyncio.gather(*tasks)
|
||||
return list(rewards)
|
||||
|
||||
orms['async_api'] = AsyncAPIReward
|
||||
```
|
||||
|
||||
swift 支持同时使用同步和异步奖励函数。训练器会自动检测奖励函数的类型:
|
||||
- 同步奖励函数按顺序执行
|
||||
- 异步奖励函数使用 `asyncio.gather` 并行执行
|
||||
|
||||
[plugin](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/plugin.py)文件中提供了一个调用`swift deploy`服务的生成式奖励模型的例子(async_genrm)
|
||||
|
||||
## 内置奖励函数
|
||||
swift内置了五种基于规则的奖励函数(代码见swift/rewards/orm.py)
|
||||
|
||||
| 奖励函数 | 论文 |
|
||||
|----------------|----------------------------------------------------------------------------|
|
||||
| accuracy | [DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via RL](https://arxiv.org/abs/2501.12948) |
|
||||
| format | 同上 |
|
||||
| cosine | [Demystifying Long Chain-of-Thought Reasoning in LLMs](https://arxiv.org/abs/2502.03373) |
|
||||
| repetition | 同上 |
|
||||
| soft_overlong | [Decoupled Clip and Dynamic sAmpling Policy Optimization (DAPO)](https://arxiv.org/abs/2503.14476) |
|
||||
|
||||
### 1. **accuracy**
|
||||
|
||||
该函数将模型的生成结果与数据集中的 solution 列进行比较,计算准确率分数。如果生成结果与标准答案一致,则得分为 1.0;否则为 0.0。
|
||||
|
||||
注意:该奖励函数使用`math_verify`库解析生成结果和solution中的答案,可能只适用于特定的数学数据集。
|
||||
|
||||
### 2. **format**
|
||||
|
||||
论文中使用以下system prompt要求模型按照固定格式进行返回
|
||||
```
|
||||
A conversation between User and Assistant. The user asks a question, and the Assistant solves it. The assistant first thinks about the reasoning process in the mind and then provides the user with the answer. The reasoning process and answer are enclosed within <think> </think> and <answer> </answer> tags, respectively, i.e., <think> reasoning process here </think><answer> answer here </answer>
|
||||
```
|
||||
|
||||
该函数检查模型是否按照 `<think>think content</think><answer>answer content</answer>` 的格式进行生成。如果生成文本符合格式要求,则得分为 1.0;否则为 0.0。
|
||||
|
||||
### 3. **cosine**
|
||||
|
||||
论文发现,仅使用 accuracy 奖励函数进行训练会导致模型的生成长度趋于超长,从而影响训练效果。cosine 奖励函数通过控制模型的生成长度来优化训练过程:
|
||||
|
||||
- 对于生成正确答案的文本,奖励值随长度增加而递减,鼓励模型生成简洁的回答。
|
||||
- 对于生成错误答案的文本,奖励值随长度增加而递增,鼓励模型进行更深入的思考。
|
||||
|
||||
使用余弦函数平滑地调整奖励值,确保奖励变化在合理范围内。余弦函数的参数包括生成文本的长度、最大长度限制以及奖励的最小值和最大值。
|
||||
|
||||
参数
|
||||
- cosine_min_len_value_wrong(默认值:-0.5):生成错误答案时,最小长度对应的奖励值。
|
||||
- cosine_max_len_value_wrong(默认值:0.0):生成错误答案时,最大长度对应的奖励值。
|
||||
- cosine_min_len_value_correct(默认值:1.0):生成正确答案时,最小长度对应的奖励值。
|
||||
- cosine_max_len_value_correct(默认值:0.5):生成正确答案时,最大长度对应的奖励值。
|
||||
- cosine_max_len(默认值等于模型生成的最大程度):生成文本的最大长度限制。
|
||||
|
||||
|
||||
### 4. **repetition**
|
||||
|
||||
惩罚模型生成文本中的重复内容,通过检测生成文本中的重复 n-gram 模式来评估重复程度,并给予相应的惩罚。
|
||||
|
||||
函数将生成文本分割为单词,并提取指定大小的 n-gram(默认为 3-gram)。通过统计不同 n-gram 的数量与总 n-gram 数量的比例,计算重复比例。如果生成文本中重复的 n-gram 比例较高,则给予较大的负奖励(惩罚)。惩罚值通过重复比例和最大惩罚值(默认为 -1.0)计算得出。
|
||||
|
||||
参数
|
||||
- repetition_n_grams(默认值:3):用于检测重复的 n-gram 大小。
|
||||
- repetition_max_penalty(默认值:-1.0):最大惩罚值,用于控制惩罚的强度。
|
||||
|
||||
### 5. **soft overlong punishment**
|
||||
定义长度惩罚区间。在这个区间内,给予[-1,0]的线性惩罚。
|
||||
|
||||
参数
|
||||
- soft_max_length: 论文中的L_max,模型的最大生成长度,默认等于max_completion_length
|
||||
- soft_cache_length: 论文中的L_cache,控制长度惩罚区间,区间为[soft_max_length-soft_cache_length, soft_max_length]
|
||||
|
||||
## 注意事项
|
||||
|
||||
如果需要在奖励函数中加载模型,默认会使用训练的 deepspeed 插件(transformers逻辑),zero3下可能会导致模型无法正常推理,参考该[issue](https://github.com/modelscope/ms-swift/issues/4580)来跳过 deepspeed 初始化环境
|
||||
@@ -0,0 +1,145 @@
|
||||
# 奖励模型
|
||||
|
||||
默认情况下,奖励模型是指具有分类头数值输出的模型,通常称为输出奖励模型(ORM)。这些模型会对其他模型的输出进行评分,从而生成一个标量值,表示模型响应的质量。
|
||||
|
||||
我们可以通过使用参数 `reward_models` 来加载具有分类头的奖励模型,或者加载经过[奖励建模](../../RLHF.md#rm)训练的奖励模型,进而使用模型的logits作为奖励。
|
||||
|
||||
## 自定义奖励模型
|
||||
对于生成式奖励模型,有两种常见的调用方式:一种是在 Trainer 内部直接使用 reward_model_plugin 定义奖励模型的逻辑,可以使用TransformersEngine对奖励模型进行推理,另一种是通过外部部署的模型服务进行调用。
|
||||
|
||||
- 使用 reward_model_plugin 调用奖励模型时,模型会被内嵌在 Trainer 内部,无需额外占用计算资源。该方式优点是方便集成,但生成速度相对较慢,更适合参数量较小的奖励模型场景。
|
||||
|
||||
- 外部部署奖励模型时,可以通过诸如 swift deploy 或 vllm serve 等命令将模型服务部署于独立设备,大幅提升推理速度,适合参数量较大的模型。但这样需要预留额外的硬件资源。
|
||||
|
||||
### 内部插件
|
||||
|
||||
我们可以在 reward_model_plugin 中灵活地自定义奖励模型的处理逻辑。这使得实现诸如生成式奖励模型等技术成为可能,包括:
|
||||
|
||||
- 自定义模型的系统提示:定义特定的指令和上下文以指导评估过程。
|
||||
- 处理模型交互历史:管理对话上下文,以提供有意义且具有上下文感知的评估。
|
||||
- 定义自定义评估标准:设置独特的标准和度量,用于评估模型的响应,超越默认的准确性和相关性衡量标准。
|
||||
|
||||
通过reward_model_plugin,开发者可以针对其应用的特定需求定制奖励评估过程。这种灵活性允许更细致和有效的基于奖励的训练策略。
|
||||
|
||||
奖励模型通过plugin的`__call__`方法进行调用,该方法接受 `inputs` 作为参数,包含了模型输入输出的 messages 和数据集中的其他列
|
||||
|
||||
```python
|
||||
def __call__(self, inputs):
|
||||
print(inputs)
|
||||
"""
|
||||
[
|
||||
{
|
||||
'messages': [
|
||||
{'role': 'system', 'content': 'system prompt'},
|
||||
{'role': 'query', 'content': 'query'},
|
||||
{'role': 'user', 'content': 'completions1'},
|
||||
],
|
||||
'solution': "abc",
|
||||
},
|
||||
{
|
||||
'messages': [
|
||||
{'role': 'system', 'content': 'system prompt'},
|
||||
{'role': 'query', 'content': 'query'},
|
||||
{'role': 'user', 'content': 'completions2'},
|
||||
],
|
||||
'solution': "abc",
|
||||
}
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
在插件中使用 TransformersEngine 进行奖励模型的推理, 我们只需构造 messages ,并通过 infer 接口调用:
|
||||
```python
|
||||
class RMPlugin(DefaultRMPlugin):
|
||||
|
||||
def __init__(self, model, template):
|
||||
|
||||
super().__init__(model, template)
|
||||
# initilize TransformersEngine to infer
|
||||
self.engine = TransformersEngine(self.model, template=self.template, max_batch_size=0)
|
||||
|
||||
def __call__(self, inputs):
|
||||
system_prompt = ...
|
||||
query = ...
|
||||
messages = [{'role': 'system', 'content': system_prompt}, {'role': 'query', 'content': query}]
|
||||
result = self.engine.infer([messages], self.request_config, use_tqdm=False)
|
||||
rewards = ...
|
||||
return rewards
|
||||
```
|
||||
|
||||
我们在 [rm_plugin.py](https://github.com/modelscope/ms-swift/blob/main/swift/rewards/rm_plugin.py) 中提供了一个简单的生成式奖励模型示例(GenRMPlugin)。
|
||||
|
||||
在 [plugin.py](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/plugin/plugin.py) 中自定义奖励模型插件,并使用 `external_plugins` 参数进行注册。
|
||||
|
||||
|
||||
注意:
|
||||
1. 在 GRPOTrainer 中,reward_model 会依次append到 reward_funcs 中。因此,reward_weights 的顺序对应 [reward_funcs, reward_model]。
|
||||
2. reward_model_plugin 默认为 default,即使用 ORM 处理逻辑。
|
||||
3. 对于参数量较大的模型,TransformersEngine 生成速度较慢,请使用[外部部署](#外部部署)方法
|
||||
|
||||
对于 BERT 这类无法通过 reward_model 加载的模型,我们可以内置在 reward_function 中进行加载,参考[issue](https://github.com/modelscope/ms-swift/issues/4580)
|
||||
|
||||
### 外部部署
|
||||
|
||||
**示例 2:使用 swift deploy 部署奖励模型并进行远程调用**
|
||||
|
||||
这类方法则不需要使用 reward_model_plugin , 而是直接在奖励函数中进行调用即可
|
||||
|
||||
首先用如下命令启动模型服务:
|
||||
|
||||
```bash
|
||||
# 注意部署的设备不要与训练设备重叠
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
swift deploy \
|
||||
--model Qwen/Qwen2.5-72B-Instruct \
|
||||
--vllm_tensor_parallel_size 4
|
||||
|
||||
# [INFO:swift] model_list: ['Qwen2.5-72B-Instruct']
|
||||
# INFO: Started server process [xxxxxx]
|
||||
# INFO: Waiting for application startup.
|
||||
# INFO: Application startup complete.
|
||||
# INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
在奖励函数中通过 OpenAI 库初始化客户端,指定模型服务的地址和端口,示例代码如下:
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
|
||||
class RMReward(ORM):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
try:
|
||||
self.client = OpenAI(
|
||||
api_key='EMPTY',
|
||||
base_url='http://127.0.0.1:8000/v1', # 若在本地部署则为 127.0.0.1
|
||||
)
|
||||
self.verify_model_name = self.client.models.list().data[0].id
|
||||
except Exception as e:
|
||||
raise RuntimeError('Failed to connect to the model service. Please deploy the model '
|
||||
"using 'swift deploy' or 'vllm serve'.") from e
|
||||
|
||||
|
||||
def __call__(self, completions, messages, **kwargs) -> List[float]:
|
||||
rewards = []
|
||||
for completion, message in zip(completions, messages):
|
||||
rm_prompt = ... # 构建 reward model 的prompt
|
||||
chat_response = self.client.chat.completions.create(
|
||||
model=self.verify_model_name,
|
||||
messages=[
|
||||
{
|
||||
'role': 'system',
|
||||
'content': 'You are a helpful assistant.'
|
||||
},
|
||||
{
|
||||
'role': 'user',
|
||||
'content': rm_prompt
|
||||
},
|
||||
],
|
||||
)
|
||||
response = chat_response.choices[0].message.content.strip()
|
||||
reward = ... # 根据奖励模型生成结果提取奖励值
|
||||
rewards.append(reward)
|
||||
return rewards
|
||||
|
||||
```
|
||||
@@ -0,0 +1,398 @@
|
||||
# GRPO
|
||||
|
||||
[GRPO(Group Relative Policy Optimization)](https://arxiv.org/abs/2402.03300) 算法利用组内相对优势计算来替代 PPO 算法中独立的价值模型,并直接在损失函数中加入 KL 散度惩罚来提高训练稳定性。
|
||||
|
||||
## 算法原理
|
||||
|
||||
GRPO 目标函数
|
||||
|
||||
$
|
||||
{\scriptstyle
|
||||
\begin{aligned}
|
||||
\mathcal{J}_{G R P O}(\theta) & =\mathbb{E}_{\left[q \sim P(Q),\left\{o_i\right\}_{i=1}^G \sim \pi_{\theta_{o l d}}(O \mid q)\right]} \\
|
||||
& \frac{1}{G} \sum_{i=1}^G \frac{1}{\left|o_i\right|} \sum_{t=1}^{\left|o_i\right|}\left\{\min \left[\frac{\pi_\theta\left(o_{i, t} \mid q, o_{i,<t}\right)}{\pi_{\theta_{o l d}}\left(o_{i, t} \mid q, o_{i,<t}\right)} \hat{A}_{i, t}, \operatorname{clip}\left(\frac{\pi_\theta\left(o_{i, t} \mid q, o_{i,<t}\right)}{\pi_{\theta_{o l d}}\left(o_{i, t} \mid q, o_{i,<t}\right)}, 1-\varepsilon, 1+\varepsilon\right) \hat{A}_{i, t}\right]-\beta \mathbb{D}_{K L}\left[\pi_\theta| | \pi_{r e f}\right]\right\}
|
||||
\end{aligned}
|
||||
}
|
||||
$
|
||||
|
||||
其中优势函数定义为
|
||||
|
||||
$
|
||||
\hat{A}_{i,t} = \frac{R_i - \text{mean}(\{R_j\}_{j=1}^G)}{\text{std}(\{R_j\}_{j=1}^G)}
|
||||
$
|
||||
|
||||
|
||||
<details> <summary>GRPO算法伪代码</summary>
|
||||
|
||||
```python
|
||||
# ========== 1. Rollout Generation Phase ==========
|
||||
prompt = "Question: Which is bigger? 9.11 or 9.9?"
|
||||
|
||||
# Generate multiple completions through parallel sampling
|
||||
completions = rollout_function(
|
||||
model=current_policy_model,
|
||||
prompt=prompt,
|
||||
num_generations=8, # Hyperparameter: number of samples per prompt
|
||||
temperature=1.0 # Hyperparameter: sampling diversity
|
||||
)
|
||||
"""
|
||||
completions = [
|
||||
(completion 1) "The larger number is 9.9...",
|
||||
(completion 2) "9.11 is bigger than...",
|
||||
...
|
||||
(completion 8) "After calculation, 9.9..."
|
||||
]
|
||||
"""
|
||||
|
||||
# ========== 2. Reward Calculation Phase ==========
|
||||
# Evaluate generated completions using reward model
|
||||
rewards = reward_function(
|
||||
completions=completions,
|
||||
ground_truth="9.9" # Expected correct answer
|
||||
)
|
||||
"""
|
||||
rewards = [
|
||||
(reward 1) 1.0, # Correct answer
|
||||
(reward 2) 0.0, # Incorrect
|
||||
...
|
||||
(reward 8) 1.0 # Correct
|
||||
]
|
||||
"""
|
||||
|
||||
# Normalize rewards to advantages
|
||||
rewards_mean = mean(rewards) # μ = 0.5
|
||||
rewards_std = std(rewards) # σ = 0.25
|
||||
advantages = (rewards - rewards_mean) / (rewards_std + 1e-8) # Standardization
|
||||
"""
|
||||
advantages = [
|
||||
(advantage 1) 2.0, # (1.0 - 0.5)/0.25
|
||||
(advantage 2) -2.0,
|
||||
...
|
||||
(advantage 8) 2.0
|
||||
]
|
||||
"""
|
||||
|
||||
# ========== 3. Policy Optimization Phase ==========
|
||||
# Get token-level log probabilities from different models
|
||||
current_logps = get_per_token_logps(current_policy_model, prompt, completions) # π_θ
|
||||
old_logps = get_per_token_logps(old_policy_model, prompt, completions) # π_θ_old
|
||||
ref_logps = get_per_token_logps(reference_model, prompt, completions) # π_ref
|
||||
|
||||
# PPO Clipped Objective
|
||||
is_ratio = exp(current_logps - old_logps) # Importance sampling ratio: e^(π_θ - π_θ_old)
|
||||
clipped_ratio = clip(is_ratio, 1-ε, 1+ε) # ε=0.2 typically
|
||||
|
||||
# Policy gradient term (dual form)
|
||||
policy_loss = -mean(
|
||||
minimum(is_ratio * advantages, # Unclipped objective
|
||||
clipped_ratio * advantages) # Clipped objective
|
||||
)
|
||||
|
||||
# KL Divergence Penalty (K3 estimator)
|
||||
# KL(π_θ||π_ref) ≈ e^(logπ_ref - logπ_θ) - (logπ_ref - logπ_θ) - 1
|
||||
kl_penalty = beta * mean(
|
||||
exp(ref_logps - current_logps) -
|
||||
(ref_logps - current_logps) - 1
|
||||
)
|
||||
|
||||
# Total Loss = Policy Loss + KL Penalty
|
||||
total_loss = policy_loss + kl_penalty
|
||||
|
||||
# ========== 4. Update Rule ==========
|
||||
# Apply gradient descent to minimize total_loss
|
||||
optimizer.zero_grad()
|
||||
total_loss.backward()
|
||||
optimizer.step()
|
||||
```
|
||||
</details>
|
||||
|
||||
训练脚本示例参考[examples](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo)
|
||||
|
||||
GRPO参数参考[文档](../../../Instruction/Command-line-parameters.md#grpo参数)
|
||||
|
||||
## 集群支持
|
||||
|
||||

|
||||
|
||||
GRPO 训练框架支持集成高性能推理引擎(如 vLLM)来加速采样过程,提供以下两种部署模式:
|
||||
|
||||
### 1. Colocate(Internal) Mode
|
||||
|
||||
训练与推理共享GPU资源,在 Trainer 内部启动推理服务,
|
||||
|
||||
启动参数
|
||||
```bash
|
||||
--use_vllm true \
|
||||
--vllm_mode colocate
|
||||
```
|
||||
|
||||
#### Colocate 模式下的显存优化方案
|
||||
在 Colocate 模式下运行时,容易出现显存不足(OOM)的情况。以下是几种有效的显存优化方法和参数配置:
|
||||
|
||||
1. 降低`vllm_gpu_memory_utilization` 参数
|
||||
|
||||
|
||||
2. 在训练阶段,释放 vLLM 占用的显存:
|
||||
|
||||
```bash
|
||||
--sleep_level 1
|
||||
```
|
||||
|
||||
3. 在vLLM 推理阶段,释放模型和优化器占用的显存:
|
||||
|
||||
```bash
|
||||
--offload_optimizer true \
|
||||
--offload_model true \
|
||||
```
|
||||
|
||||
4. 在vLLM中使用 Tensor Parallel 技术:
|
||||
|
||||
```bash
|
||||
--vllm_tensor_parallel_size [tp_size]
|
||||
```
|
||||
|
||||
5. 分批 Gather 模型权重(zero3下同步 vLLM 权重时):
|
||||
|
||||
```bash
|
||||
--move_model_batches [批次数量]
|
||||
```
|
||||
|
||||
6. 将 Megatron 导出的用于 vLLM 更新的 HF 格式权重存放在 CPU 主存中,以降低 GPU 显存占用:
|
||||
|
||||
```bash
|
||||
--offload_bridge true
|
||||
```
|
||||
|
||||
### 2. Async(External) Mode
|
||||
|
||||
训练与推理资源分离,启动单独的推理服务器
|
||||
|
||||
使用`swift rollout`命令部署vLLM 服务器, 现仅支持vLLM backend
|
||||
```bash
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift rollout \
|
||||
--model Qwen/Qwen2.5-VL-7B-Instruct \
|
||||
--vllm_tensor_parallel_size 2 \
|
||||
--vllm_data_parallel_size 1
|
||||
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
swift rollout \
|
||||
--model Qwen/Qwen2.5-VL-7B-Instruct \
|
||||
--vllm_tensor_parallel_size 2 \
|
||||
--vllm_data_parallel_size 1
|
||||
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
swift rollout \
|
||||
--model Qwen/Qwen2.5-VL-7B-Instruct \
|
||||
--vllm_tensor_parallel_size 2 \
|
||||
--vllm_data_parallel_size 2
|
||||
```
|
||||
|
||||
更多 rollout 参数参考[vLLM参数](../../../Instruction/Command-line-parameters.md#vllm参数)和[rollout 参数](../../../Instruction/Command-line-parameters.md#rollout参数)
|
||||
|
||||
注意:在使用 vllm_use_async_engine 时,仅开启 DP 可能会导致错误,相关问题参考: [vllm issue](https://github.com/vllm-project/vllm/issues/18567)。如果出现错误,请尝试同时启用 TP 和 DP,或升级vLLM
|
||||
|
||||
|
||||
训练使用以下参数配置外部 vLLM 服务器
|
||||
```bash
|
||||
--use_vllm true \
|
||||
--vllm_mode server \
|
||||
--vllm_server_host <服务器IP> \
|
||||
--vllm_server_port <服务端口> \
|
||||
--vllm_server_timeout <超时时间> \
|
||||
```
|
||||
|
||||
### 权重同步加速
|
||||
设置以下参数可以通过仅同步 LoRA adapter 权重而非全量模型权重,优化 LoRA 训练的权重同步速度。
|
||||
|
||||
> 注意:这种同步方式会略微影响 vLLM 推理速度。
|
||||
|
||||
```bash
|
||||
# rollout(server mode)
|
||||
swift rollout \
|
||||
--vllm_enable_lora true \
|
||||
--vllm_max_lora_rank xxx # 与训练脚本lora_rank一致
|
||||
...
|
||||
|
||||
# grpo(colocate mode)
|
||||
swift rlhf \
|
||||
--rlhf_type grpo \
|
||||
--vllm_mode colocate \
|
||||
--vllm_enable_lora true \
|
||||
...
|
||||
|
||||
# megatron grpo(colocate mode)
|
||||
swift megatron rlhf \
|
||||
--rlhf_type grpo \
|
||||
--vllm_mode colocate \
|
||||
--vllm_enable_lora true \
|
||||
...
|
||||
```
|
||||
|
||||
**多模态模型 ViT 层 LoRA 同步:** 如果训练时开启了 ViT 层的 LoRA(`freeze_vit false`),
|
||||
在仅同步LoRA的模式下,需要相应在 vLLM 侧开启 tower/connector LoRA 支持。
|
||||
|
||||
通过 `vllm_engine_kwargs` 传入:
|
||||
|
||||
```bash
|
||||
--vllm_engine_kwargs '{"enable_tower_connector_lora": true}'
|
||||
```
|
||||
|
||||
该功能为 vLLM 实验性特性,目前支持 Qwen2.5-VL、Qwen3-VL 等模型。
|
||||
具体支持情况请参阅 [vLLM 文档](https://docs.vllm.ai/en/latest/features/lora/)
|
||||
和 [vLLM issue](https://github.com/vllm-project/vllm/issues/31479)。
|
||||
|
||||
## logged metrics
|
||||
- completions/mean_length:生成的 completion 的平均长度。
|
||||
- completions/min_length:生成的 completion 的最小长度。
|
||||
- completions/max_length:生成的 completion 的最大长度。
|
||||
- completions/clipped_ratio:被长度截断的 completion 占比。
|
||||
- reward/{reward_func_name}/mean:某个特定 reward function 的平均奖励值。
|
||||
- reward/{reward_func_name}/std:某个特定 reward function 的奖励标准差。
|
||||
> 注意, 上述两个指标是在所有 completions 范围内统计得到的。
|
||||
- reward:加权 reward_weights 后的整体平均奖励。
|
||||
- reward_std:加权 reward_weights 后,每个 batch 内整体奖励的标准差。
|
||||
> 注意:上述两个指标是先在每个组内分别计算均值/std,然后再对各组的结果取平均。
|
||||
- frac_reward_zero_std:在生成 batch 中,reward 标准差为零的样本比例,意味着该 prompt 上的答案几乎无多样性(所有回答奖励一致)。
|
||||
- kl:生成的 completion 上,模型与参考模型之间的平均 KL 散度。仅当 beta 非零时记录。
|
||||
- clip_ratio/region_mean:不同句子中被 CLIP 的的 token 平均比例
|
||||
- clip_ratio/low_mean:不同句子中被 下CLIP 的的 token 平均比例
|
||||
- clip_ratio/low_min:不同句子中被 下CLIP 的的 token 最小比例
|
||||
- clip_ratio/high_mean:不同句子中被 上CLIP 的的 token 平均比例
|
||||
- clip_ratio/high_max:不同句子中被 上CLIP 的的 token 最大比例
|
||||
> 注意:如果开启`overlong_filter`, kl 和 clip_ratio 指标会过滤超长的样本
|
||||
|
||||
如果设置了`log_entropy`参数,则会额外记录entropy相关指标,包括
|
||||
- entropy/mean: 不同句子中的 entropy 均值
|
||||
- entropy/max: 不同句子中的 entropy 最大值
|
||||
- entropy/min: 不同句子中的 entropy 最小值
|
||||
> 注意这里的 句子 entropy 指 completion 中的 token entropy 均值
|
||||
|
||||
|
||||
如果设置了`top_entropy_quantile`参数<1.0, 则会记录entropy threshold的值
|
||||
- entropy/threshold: 分位点处的 entropy 值,小于该值的 token 将不会被计算 loss
|
||||
|
||||
训推一致性指标,前缀为rollout_correction,需设置`log_rollout_offpolicy_metrics=true`或`rollout_importance_sampling_mode`:
|
||||
- `kl` / `k3_kl`:训练策略与 rollout 策略之间的 KL 散度(直接估计器 / K3 估计器)
|
||||
- `training_ppl` / `rollout_ppl`:训练策略和 rollout 策略的困惑度
|
||||
- `log_ppl_diff`:log PPL 差异,反映分布偏移程度
|
||||
- `ppl_ratio`:PPL 比率
|
||||
- `chi2_token` / `chi2_seq`:Token/Sequence 级别的 χ² 散度
|
||||
|
||||
IS 校正指标(需设置`rollout_importance_sampling_mode`):
|
||||
- `is_weight_mean`:平均重要性采样权重
|
||||
- `ess`:有效样本大小(Effective Sample Size)
|
||||
- `clipped_frac`:被截断或屏蔽的样本比例
|
||||
|
||||
> 训推一致性指标详细说明请参考文档 [Training-Inference-Mismatch](../AdvancedResearch/training_inference_mismatch.md)
|
||||
|
||||
如果设置了`log_completions`, 将保存训练动态在output对应文件夹中,包括
|
||||
- step:记录时的训练步数
|
||||
- prompt:模型输入
|
||||
- completion:模型采样回答
|
||||
- {reward_func_name}:特定奖励
|
||||
- entropy:entropy token 均值,在设置`log_entropy`时记录
|
||||
|
||||
设置 `report_to wandb/swanlab` 将训练动态Table推送到对应的平台
|
||||
|
||||
如果需要在Table中额外记录其他列,请在 `GRPOTrainer._generate_and_score_completions` 方法中,设置 metrics_to_gather 字典。
|
||||
|
||||
默认自动检测
|
||||
- `image`:视觉数据集图像输入。(暂时只支持wandb)
|
||||
- `solution`:数据集中的 solution 列。
|
||||
|
||||
## FAQ
|
||||
**1. 训练过程中 loss 等于0 / 接近0 / 小于0**
|
||||
|
||||
正常情况, 参考[issue](https://github.com/huggingface/open-r1/issues/239#issuecomment-2646297851)
|
||||
|
||||
**2. num_generations / 批量大小相关**
|
||||
|
||||
在 GRPO 中,batch_size 以 completion(模型生成结果) 为单位。例如,设置 per_device_train_batch_size=8 表示每张 GPU 在训练过程中会同时处理 8 个 completion 的 loss 计算。
|
||||
|
||||
训练阶段,在一次完整的梯度累计 batch 中,总的批量大小等于:
|
||||
|
||||
```
|
||||
effective_batch_size = num_processes * per_device_train_batch_size * gradient_accumulation_steps
|
||||
```
|
||||
|
||||
采样阶段,总的批量大小 (completion-level) 数量等于:
|
||||
|
||||
1. 设置 generation_batch_size 下,等于 generation_batch_size
|
||||
2. 设置 steps_per_generation 下,等于 per_device_train_batch_size * steps_per_generation * num_processes
|
||||
3. 默认情况下,steps_per_generation = gradient_accumulation_steps,generation_batch_size = per_device_train_batch_size * steps_per_generation * num_processes = per_device_train_batch_size * gradient_accumulation_steps * num_processes = effective_batch_size
|
||||
|
||||
在评估阶段,completion 的数量等于:
|
||||
```
|
||||
num_processes * per_device_eval_batch_size
|
||||
```
|
||||
|
||||
参数 `num_generations` 必须能够被以上采样阶段和评估的总批量大小整除,以保证生成任务可以均匀分配到各个设备上。
|
||||
|
||||
**示例**
|
||||
|
||||
- num_processes = 8
|
||||
- per_device_train_batch_size = 4
|
||||
- gradient_accumulation_steps = 8
|
||||
- generation_batch_size = 512
|
||||
- num_generations = 64
|
||||
|
||||
1. 采样需要的总数据(prompt)量等于 512 / 64 = 8
|
||||
2. 每次采样 512 条模型回复
|
||||
3. 每次更新模型权重批量大小为 8 *4 * 8 = 256
|
||||
|
||||
|
||||
**3. 为什么 KL 出现了NaN**
|
||||
|
||||
开启 overlong_filter 后,某一卡上的所有 completion 都被截断
|
||||
|
||||
**4. 训练的steps怎么计算?**
|
||||
|
||||
参考[issue](https://github.com/modelscope/ms-swift/issues/3912)
|
||||
|
||||
**5. clip_ratio为什么总是0?**
|
||||
|
||||
Clip机制的核心目的是限制策略更新的幅度,防止因单次更新过大而导致策略性能崩溃(即策略更新后表现急剧下降)。
|
||||
Clip操作的具体公式如下:
|
||||
|
||||
$
|
||||
L_{\text{CLIP}}(\theta) = \mathbb{E}_{t} \left[ \min\left(r_{t}(\theta) \hat{A}_{t}, \text{clip}(r_{t}(\theta), 1 - \epsilon, 1 + \epsilon) \hat{A}_{t} \right) \right]
|
||||
$
|
||||
|
||||
其中:$r_{t}(\theta) = \frac{\pi_{\theta}(a_{t} \mid s_{t})}{\pi_{\text{old}}(a_{t} \mid s_{t})}$ 是重要性采样比,衡量新旧策略的差异。$\hat{A}_{t}$ 是优势函数(advantage function),表示动作的相对收益。$\epsilon$ 用于限制 $r_{t}(\theta)$ 的偏离范围。
|
||||
|
||||
在 on-policy 训练过程中,由于每次更新都使用最新策略生成的数据,新旧策略相同,即 $\pi_{\theta} = \pi_{\text{old}}$
|
||||
|
||||
因此重要性采样比恒为 1,此时,clip 操作不会生效。
|
||||
|
||||
在设置以下参数情况下,算法为off-policy (near-on-policy)
|
||||
1. num_iterations > 1, 或者
|
||||
2. gradient_accumulation_steps % steps_per_generation != 0
|
||||
|
||||
参考[issue](https://github.com/huggingface/open-r1/issues/239#issuecomment-2646297851)
|
||||
|
||||
**6. 如何设置训练的 `mini-batch size`**
|
||||
|
||||
在 GRPO 训练中,我们可以通过以下两种方式配置 mini-batch 更新:
|
||||
- 设置 `generation_batch_size` 为训练 global batch size (effective_batch_size) 的整数倍
|
||||
- 或设置 `steps_per_generation` 为 `gradient_accumulation_steps` 的整数倍
|
||||
|
||||
典型配置示例:
|
||||
- 当配置:
|
||||
steps_per_generation = 16, gradient_accumulation_steps = 8, mini_batch_size = steps_per_generation / gradient_accumulation_steps = 2. 则 1 次 rollout 结果将拆分成 2 批 mini-batch 进行更新。
|
||||
|
||||
**7. swift deploy 与 swift rollout 的区别**
|
||||
|
||||
- swift deploy 主要用于模型的部署和推理,支持 PT、vLLM、SGLang 等多种引擎,兼容流式推理与 OpenAI API 的调用格式。
|
||||
|
||||
- swift rollout 则专注于 GRPO 推理加速,目前仅支持 vLLM 引擎,并内置了权重自动同步的功能。
|
||||
|
||||
|
||||
**8. 如何取消 KL 项损失**
|
||||
|
||||
将参数设置为 `--beta 0`,即可关闭 KL 损失的计算,并且不会加载参考模型(ref model)。
|
||||
|
||||
|
||||
## RL微信群
|
||||
|
||||
<img src="https://raw.githubusercontent.com/modelscope/ms-swift/main/docs/resources/wechat/grpo.png" width="250">
|
||||
@@ -0,0 +1,6 @@
|
||||
Get Started
|
||||
===============
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
GRPO.md
|
||||
@@ -0,0 +1,19 @@
|
||||
GRPO
|
||||
===============
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Get Started
|
||||
|
||||
GetStarted/index.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Developer Guide
|
||||
|
||||
DeveloperGuide/index.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Advanced Research
|
||||
|
||||
AdvancedResearch/index.rst
|
||||
@@ -0,0 +1,352 @@
|
||||
# 推理和部署
|
||||
|
||||
以下为swift支持的推理引擎以及接入部分的相应能力,三种推理加速引擎为SWIFT的推理、部署、评测模块提供推理加速:
|
||||
|
||||
| 推理加速引擎 | OpenAI API | 多模态 | 量化模型 | 多LoRA | QLoRA | Batch推理 | 并行技术 |
|
||||
| ------------ | -------------- | ---------- | ------ | -------- | ------ | ----- | ----- |
|
||||
| transformers | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/deploy/client/llm/chat/openai_client.py) | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/app/mllm.sh) | ✅ | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_lora.py) | ✅ | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/infer/transformers/batch_ddp.sh) |DDP/device_map |
|
||||
| [vllm](https://github.com/vllm-project/vllm) | ✅ | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/infer/vllm/mllm_tp.sh) | ✅ | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/deploy/lora/server.sh) | ❌ | ✅ | TP/PP/DP |
|
||||
| [sglang](https://github.com/sgl-project/sglang) | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ | TP/PP/DP/EP |
|
||||
| [lmdeploy](https://github.com/InternLM/lmdeploy) | ✅ | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/infer/lmdeploy/mllm_tp.sh) | ✅ | ❌ | ❌ | ✅ | TP/DP |
|
||||
|
||||
|
||||
## 推理
|
||||
ms-swift使用了分层式的设计思想,用户可以使用命令行界面、Web-UI界面和直接使用Python的方式进行推理。
|
||||
|
||||
如果要查看LoRA微调后模型的推理,可以参考[预训练与微调文档](./Pre-training-and-Fine-tuning.md#推理微调后模型)。
|
||||
|
||||
### 使用CLI
|
||||
|
||||
**全参数模型:**
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 swift infer \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--stream true \
|
||||
--infer_backend transformers \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
**LoRA模型:**
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 swift infer \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--adapters swift/test_lora \
|
||||
--stream true \
|
||||
--infer_backend transformers \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
|
||||
**命令行推理指令**
|
||||
|
||||
以上为交互式命令行界面推理,脚本运行后仅需在terminal中输入query即可。你也可以输入以下特殊指令:
|
||||
- `multi-line`: 切换到多行模式,在输入中支持换行输入,以`#`代表输入结束。
|
||||
- `single-line`: 切换到单行模式,以换行代表输入结束。
|
||||
- `reset-system`: 重置system并清空历史记录。
|
||||
- `clear`: 清除历史记录。
|
||||
- `quit` or `exit`: 退出对话。
|
||||
|
||||
**多模态模型**
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
MAX_PIXELS=1003520 \
|
||||
VIDEO_MAX_PIXELS=50176 \
|
||||
FPS_MAX_FRAMES=12 \
|
||||
swift infer \
|
||||
--model Qwen/Qwen2.5-VL-3B-Instruct \
|
||||
--stream true \
|
||||
--infer_backend transformers \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
如果要进行多模态模型的推理,可以在query中添加`<image>/<video>/<audio>`等标签(代表图像表征在`inputs_embeds`中的位置),例如输入`<image><image>这两张图有啥区别`,`<video>描述这段视频`。然后根据提示输入相应的图像/视频/音频即可。
|
||||
|
||||
以下为一个推理的示例:
|
||||
```
|
||||
<<< <image><image>这两张图有什么区别
|
||||
Input an image path or URL <<< http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png
|
||||
Input an image path or URL <<< http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/animal.png
|
||||
这两张图片的区别在于它们所展示的动物和场景。
|
||||
|
||||
1. **第一张图片**:
|
||||
- 展示了一只小猫。
|
||||
- 小猫有大大的眼睛,表情显得有些困惑或好奇。
|
||||
- 背景是模糊的,可能是室内环境。
|
||||
|
||||
2. **第二张图片**:
|
||||
- 展示了一群羊。
|
||||
- 羊们站在草地上,背景是绿色的山丘和蓝天白云。
|
||||
- 羊的表情看起来很平静,似乎在享受大自然的环境。
|
||||
|
||||
总结来说,第一张图片是一只小猫,而第二张图片是一群羊。
|
||||
--------------------------------------------------
|
||||
<<< clear
|
||||
<<< <video>描述这段视频
|
||||
Input a video path or URL <<< https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4
|
||||
The video shows a baby wearing sunglasses sitting on a bed and reading a book. The baby is holding the book with both hands and appears to be focused on the pages. The baby's feet are visible in the frame, and they are moving slightly as they read. The background of the video shows a room with a bed and some furniture.
|
||||
```
|
||||
|
||||
|
||||
**数据集推理:**
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 swift infer \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--stream true \
|
||||
--infer_backend transformers \
|
||||
--val_dataset AI-ModelScope/alpaca-gpt4-data-zh \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
以上提供了全参数和LoRA流式推理的例子,以下介绍更多SWIFT中的推理技术:
|
||||
- 界面推理:你可以将`swift infer`改成`swift app`。
|
||||
- batch推理:`infer_backend=transformers`可以指定`--max_batch_size`对大模型和多模态大模型进行batch推理,具体参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/transformers/batch_ddp.sh)。在进行batch推理时,你不能设置`--stream true`。
|
||||
- DDP/device_map推理:`infer_backend=transformers`支持使用DDP/device_map技术进行并行推理,具体参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/transformers/mllm_device_map.sh)。
|
||||
- 推理加速:swift支持使用vllm/sglang/lmdeploy对推理、部署和评测模块进行推理加速,只需要额外指定`--infer_backend vllm/sglang/lmdeploy`即可。可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/vllm/mllm_ddp.sh)。
|
||||
- 多模态模型:我们提供了[transformers](https://github.com/modelscope/ms-swift/blob/main/examples/infer/transformers/mllm_device_map.sh)/[vllm](https://github.com/modelscope/ms-swift/blob/main/examples/infer/vllm/mllm_tp.sh)/[lmdeploy](https://github.com/modelscope/ms-swift/blob/main/examples/infer/lmdeploy/mllm_tp.sh)对多模态模型进行多GPU推理的shell脚本。
|
||||
- 量化模型:直接选择GPTQ、AWQ、BNB量化的模型,例如:`--model Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4`即可。
|
||||
- 更多模型类型:我们提供了[bert](https://github.com/modelscope/ms-swift/blob/main/examples/infer/transformers/bert.sh)、[reward_model](https://github.com/modelscope/ms-swift/blob/main/examples/infer/transformers/reward_model.sh)、[prm](https://github.com/modelscope/ms-swift/blob/main/examples/infer/transformers/prm.sh)的推理脚本。
|
||||
|
||||
|
||||
**小帖士:**
|
||||
- SWIFT会将推理结果保存起来,你可以通过`--result_path`指定保存路径。
|
||||
- 如果要输出logprobs,只需要在推理时,指定`--logprobs true`即可。SWIFT会保存。注意,设置`--stream true`将不会存储。
|
||||
- infer_backend为'transformers'支持所有swift已支持模型的推理,而infer_backend为vllm/sglang/lmdeploy只支持部分模型,具体请参考[vllm](https://docs.vllm.ai/en/latest/models/supported_models.html)、[sglang](https://docs.sglang.ai/supported_models/generative_models.html)、[lmdeploy](https://lmdeploy.readthedocs.io/en/latest/supported_models/supported_models.html)文档。
|
||||
- 使用`--infer_backend vllm`出现OOM,可以通过降低`--vllm_max_model_len`,`--vllm_max_num_seqs`,选择合适的`--vllm_gpu_memory_utilization`,设置`--vllm_enforce_eager true`。或者使用tensor并行`--vllm_tensor_parallel_size`来解决。
|
||||
- 使用`--infer_backend vllm`推理多模态模型,需要传入多张图片。可以设置`--vllm_limit_mm_per_prompt`解决,例如:`--vllm_limit_mm_per_prompt '{"image": 10, "video": 5}'`。
|
||||
- 推理qwen2-vl/qwen2.5-vl出现OOM,可以通过设置`MAX_PIXELS`、`VIDEO_MAX_PIXELS`、`FPS_MAX_FRAMES`解决,可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/app/mllm.sh)。
|
||||
- swift内置对话模板与使用transformers运行的对话模板对齐,测试参考[这里](https://github.com/modelscope/ms-swift/blob/main/tests/test_align/test_template/test_vision.py)。如果出现未对齐情况,欢迎提issue/PR修正。
|
||||
|
||||
|
||||
### 使用Web-UI
|
||||
如果你要使用界面的方式进行推理,可以查看[Web-UI文档](../GetStarted/Web-UI.md)。
|
||||
|
||||
### 使用Python
|
||||
|
||||
文本模型:
|
||||
```python
|
||||
import os
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||
|
||||
from swift.infer_engine import TransformersEngine, RequestConfig, InferRequest
|
||||
model = 'Qwen/Qwen2.5-0.5B-Instruct'
|
||||
|
||||
# 加载推理引擎
|
||||
engine = TransformersEngine(model, max_batch_size=2)
|
||||
request_config = RequestConfig(max_tokens=512, temperature=0)
|
||||
|
||||
# 这里使用了2个infer_request来展示batch推理
|
||||
infer_requests = [
|
||||
InferRequest(messages=[{'role': 'user', 'content': 'who are you?'}]),
|
||||
InferRequest(messages=[{'role': 'user', 'content': '浙江的省会在哪?'},
|
||||
{'role': 'assistant', 'content': '浙江省的省会是杭州。'},
|
||||
{'role': 'user', 'content': '这里有什么好玩的地方'},]),
|
||||
]
|
||||
resp_list = engine.infer(infer_requests, request_config)
|
||||
query0 = infer_requests[0].messages[0]['content']
|
||||
print(f'response0: {resp_list[0].choices[0].message.content}')
|
||||
print(f'response1: {resp_list[1].choices[0].message.content}')
|
||||
```
|
||||
|
||||
多模态模型:
|
||||
```python
|
||||
import os
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||
os.environ['MAX_PIXELS'] = '1003520'
|
||||
os.environ['VIDEO_MAX_PIXELS'] = '50176'
|
||||
os.environ['FPS_MAX_FRAMES'] = '12'
|
||||
|
||||
from swift.infer_engine import TransformersEngine, RequestConfig, InferRequest
|
||||
model = 'Qwen/Qwen2.5-VL-3B-Instruct'
|
||||
|
||||
# 加载推理引擎
|
||||
engine = TransformersEngine(model, max_batch_size=2)
|
||||
request_config = RequestConfig(max_tokens=512, temperature=0)
|
||||
|
||||
# 这里使用了3个infer_request来展示batch推理
|
||||
infer_requests = [
|
||||
InferRequest(messages=[{'role': 'user', 'content': 'who are you?'}]),
|
||||
InferRequest(messages=[{'role': 'user', 'content': '<image><image>两张图的区别是什么?'}],
|
||||
images=['http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png',
|
||||
'http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/animal.png']),
|
||||
InferRequest(messages=[{'role': 'user', 'content': '<video>describe the video'}],
|
||||
videos=['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4']),
|
||||
]
|
||||
resp_list = engine.infer(infer_requests, request_config)
|
||||
query0 = infer_requests[0].messages[0]['content']
|
||||
print(f'response0: {resp_list[0].choices[0].message.content}')
|
||||
print(f'response1: {resp_list[1].choices[0].message.content}')
|
||||
print(f'response2: {resp_list[2].choices[0].message.content}')
|
||||
```
|
||||
|
||||
我们也提供了更多使用python推理的demo:
|
||||
- 使用流式推理以及`VllmEngine`、`SglangEngine`、`LmdeployEngine`进行推理加速,可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo.py)。
|
||||
- 多模态推理:除了上述多模态输入格式外,swift兼容OpenAI的多模态输入格式,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_mllm.py)。
|
||||
- grounding任务:对多模态模型进行Grounding任务画框,可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_grounding.py)。
|
||||
- 多LoRA推理:参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_lora.py)。
|
||||
- agent推理:参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_agent.py)。
|
||||
- 异步接口:使用`engine.infer_async`进行python方式推理,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo.py)。
|
||||
|
||||
|
||||
## 部署
|
||||
|
||||
如果要查看LoRA微调后模型的部署,可以参考[预训练与微调文档](./Pre-training-and-Fine-tuning.md#部署微调后模型)。
|
||||
|
||||
这里主要介绍对多模态模型的部署和调用,文本大模型我们给出简单的部署和调用的简单案例:
|
||||
|
||||
服务端部署:
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 swift deploy \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--infer_backend vllm \
|
||||
--max_new_tokens 2048 \
|
||||
--served_model_name Qwen2.5-7B-Instruct
|
||||
```
|
||||
|
||||
客户端调用测试:
|
||||
```shell
|
||||
curl http://localhost:8000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "Qwen2.5-7B-Instruct",
|
||||
"messages": [{"role": "user", "content": "晚上睡不着觉怎么办?"}],
|
||||
"max_tokens": 256,
|
||||
"temperature": 0
|
||||
}'
|
||||
```
|
||||
|
||||
|
||||
### 服务端
|
||||
|
||||
```shell
|
||||
# test env: pip install transformers==4.51.3 vllm==0.8.5.post1
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
MAX_PIXELS=1003520 \
|
||||
VIDEO_MAX_PIXELS=50176 \
|
||||
FPS_MAX_FRAMES=12 \
|
||||
swift deploy \
|
||||
--model Qwen/Qwen2.5-VL-3B-Instruct \
|
||||
--infer_backend vllm \
|
||||
--vllm_gpu_memory_utilization 0.9 \
|
||||
--vllm_max_model_len 8192 \
|
||||
--max_new_tokens 2048 \
|
||||
--vllm_limit_mm_per_prompt '{"image": 5, "video": 2}' \
|
||||
--served_model_name Qwen2.5-VL-3B-Instruct
|
||||
```
|
||||
|
||||
|
||||
### 客户端
|
||||
|
||||
这里介绍3种调用客户端的方式,分别是curl、openai库和swift客户端。
|
||||
|
||||
|
||||
方案一: curl
|
||||
```shell
|
||||
curl http://localhost:8000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "Qwen2.5-VL-3B-Instruct",
|
||||
"messages": [{"role": "user", "content": [
|
||||
{"type": "image", "image": "http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png"},
|
||||
{"type": "image", "image": "http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/animal.png"},
|
||||
{"type": "text", "text": "两张图的区别是什么?"}
|
||||
]}],
|
||||
"max_tokens": 256,
|
||||
"temperature": 0
|
||||
}'
|
||||
```
|
||||
|
||||
方案2: openai库
|
||||
```python
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
api_key='EMPTY',
|
||||
base_url=f'http://127.0.0.1:8000/v1',
|
||||
)
|
||||
model = client.models.list().data[0].id
|
||||
print(f'model: {model}')
|
||||
|
||||
messages = [{'role': 'user', 'content': [
|
||||
{'type': 'video', 'video': 'https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4'},
|
||||
{'type': 'text', 'text': 'describe the video'}
|
||||
]}]
|
||||
|
||||
resp = client.chat.completions.create(model=model, messages=messages, max_tokens=512, temperature=0)
|
||||
query = messages[0]['content']
|
||||
response = resp.choices[0].message.content
|
||||
print(f'query: {query}')
|
||||
print(f'response: {response}')
|
||||
|
||||
# base64
|
||||
import base64
|
||||
import requests
|
||||
resp = requests.get('https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4')
|
||||
base64_encoded = base64.b64encode(resp.content).decode('utf-8')
|
||||
messages = [{'role': 'user', 'content': [
|
||||
{'type': 'video', 'video': f'data:video/mp4;base64,{base64_encoded}'},
|
||||
{'type': 'text', 'text': 'describe the video'}
|
||||
]}]
|
||||
|
||||
gen = client.chat.completions.create(model=model, messages=messages, stream=True, temperature=0)
|
||||
print(f'query: {query}\nresponse: ', end='')
|
||||
for chunk in gen:
|
||||
if chunk is None:
|
||||
continue
|
||||
print(chunk.choices[0].delta.content, end='', flush=True)
|
||||
print()
|
||||
```
|
||||
|
||||
方案三:swift客户端
|
||||
|
||||
```python
|
||||
from swift import InferRequest, InferClient, RequestConfig, InferStats
|
||||
|
||||
engine = InferClient(host='127.0.0.1', port=8000)
|
||||
print(f'models: {engine.models}')
|
||||
metric = InferStats()
|
||||
request_config = RequestConfig(max_tokens=512, temperature=0)
|
||||
|
||||
# 这里使用了3个infer_request来展示batch推理
|
||||
# 支持传入本地路径、base64和url
|
||||
infer_requests = [
|
||||
InferRequest(messages=[{'role': 'user', 'content': 'who are you?'}]),
|
||||
InferRequest(messages=[{'role': 'user', 'content': '<image><image>两张图的区别是什么?'}],
|
||||
images=['http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/cat.png',
|
||||
'http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/animal.png']),
|
||||
InferRequest(messages=[{'role': 'user', 'content': '<video>describe the video'}],
|
||||
videos=['https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4']),
|
||||
]
|
||||
|
||||
resp_list = engine.infer(infer_requests, request_config, metrics=[metric])
|
||||
print(f'response0: {resp_list[0].choices[0].message.content}')
|
||||
print(f'response1: {resp_list[1].choices[0].message.content}')
|
||||
print(f'response2: {resp_list[2].choices[0].message.content}')
|
||||
print(metric.compute())
|
||||
metric.reset()
|
||||
|
||||
# base64
|
||||
import base64
|
||||
import requests
|
||||
resp = requests.get('https://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/baby.mp4')
|
||||
base64_encoded = base64.b64encode(resp.content).decode('utf-8')
|
||||
messages = [{'role': 'user', 'content': [
|
||||
{'type': 'video', 'video': f'data:video/mp4;base64,{base64_encoded}'},
|
||||
{'type': 'text', 'text': 'describe the video'}
|
||||
]}]
|
||||
infer_request = InferRequest(messages=messages)
|
||||
request_config = RequestConfig(max_tokens=512, temperature=0, stream=True)
|
||||
gen_list = engine.infer([infer_request], request_config, metrics=[metric])
|
||||
print(f'response0: ', end='')
|
||||
for chunk in gen_list[0]:
|
||||
if chunk is None:
|
||||
continue
|
||||
print(chunk.choices[0].delta.content, end='', flush=True)
|
||||
print()
|
||||
print(metric.compute())
|
||||
```
|
||||
我们也提供了更多部署的demo:
|
||||
- 多LoRA部署与调用: 参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/deploy/lora)。
|
||||
- Base模型的部署与调用: 参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/deploy/client/llm/base)。
|
||||
- 更多模型类型: 我们提供了[bert](https://github.com/modelscope/ms-swift/tree/main/examples/deploy/bert)、[reward_model](https://github.com/modelscope/ms-swift/tree/main/examples/deploy/reward_model)的部署脚本。
|
||||
@@ -0,0 +1,319 @@
|
||||
# 预训练与微调
|
||||
|
||||
训练能力:
|
||||
|
||||
| 方法 | 全参数 | LoRA | QLoRA | Deepspeed | 多机 | 多模态 |
|
||||
| ------ | ------ |---------------------------------------------------------------------------------------------| ----- | ------ | ------ |----------------------------------------------------------------------------------------------|
|
||||
| [预训练](https://github.com/modelscope/ms-swift/blob/main/examples/train/pretrain) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [指令监督微调](https://github.com/modelscope/ms-swift/blob/main/examples/train/lora_sft.sh) | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/train/full/train.sh) | ✅ | [✅](https://github.com/modelscope/ms-swift/tree/main/examples/train/qlora) | [✅](https://github.com/modelscope/ms-swift/tree/main/examples/train/multi-gpu/deepspeed) | [✅](https://github.com/modelscope/ms-swift/tree/main/examples/train/multi-node) | [✅](https://github.com/modelscope/ms-swift/tree/main/examples/train/multimodal) |
|
||||
| [GRPO](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [GKD](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/gkd) | ✅ | ✅ | ✅ | ✅ | ✅ | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/train/multimodal/rlhf/gkd) |
|
||||
| [PPO](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/ppo) | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||
| [DPO](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/dpo) | ✅ | ✅ | ✅ | ✅ | ✅ | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/train/multimodal/rlhf/dpo) |
|
||||
| [KTO](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/kto.sh) | ✅ | ✅ | ✅ | ✅ | ✅ | [✅](https://github.com/modelscope/ms-swift/blob/main/examples/train/multimodal/rlhf/kto.sh) |
|
||||
| [奖励模型](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/rm.sh) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [CPO](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/cpo.sh) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [SimPO](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/simpo.sh) | ✅ | ✅ | ✅ | ✅| ✅ | ✅ |
|
||||
| [ORPO](https://github.com/modelscope/ms-swift/blob/main/examples/train/rlhf/orpo.sh) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [Embedding](https://github.com/modelscope/ms-swift/blob/main/examples/train/embedding) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [Reranker](https://github.com/modelscope/ms-swift/tree/main/examples/train/reranker) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [序列分类](https://github.com/modelscope/ms-swift/blob/main/examples/train/seq_cls) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
|
||||
## 环境准备
|
||||
推荐的第三方库版本参考[SWIFT安装文档](../GetStarted/SWIFT-installation.md)
|
||||
```bash
|
||||
pip install ms-swift -U
|
||||
|
||||
# 若使用deepspeed zero2/zero3
|
||||
pip install deepspeed -U
|
||||
```
|
||||
|
||||
## 预训练
|
||||
预训练使用`swift pt`命令,这将自动使用生成式而非对话式的template,即将`use_chat_template`设置为False(其他所有的命令,例如`swift sft/rlhf/infer`,都默认将`use_chat_template`设置为True)。此外,`swift pt`与`swift sft`相比,具有不同的数据集格式,可以参考[自定义数据集文档](../Customization/Custom-dataset.md)。
|
||||
|
||||
使用CLI进行预训练的脚本可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/pretrain/train.sh)。更多训练技术的介绍可以参考微调章节。
|
||||
|
||||
小贴士:
|
||||
- `swift pt`与`swift sft --use_chat_template false --loss_scale all`等价。
|
||||
|
||||
## 微调
|
||||
|
||||
ms-swift使用了分层式的设计思想,用户可以使用命令行界面、Web-UI界面和直接使用Python的方式进行微调。
|
||||
|
||||
### 使用CLI
|
||||
|
||||
我们提供了10分钟在单卡3090上对Qwen2.5-7B-Instruct进行自我认知微调的最佳实践,具体参考[这里](../GetStarted/Quick-start.md),这可以帮助您快速了解SWIFT。
|
||||
|
||||
此外,我们给出了一系列脚本帮助您了解SWIFT的训练能力:
|
||||
|
||||
- 轻量化训练:SWIFT支持的轻量微调示例可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/tuners)。(注意:这些方式预训练也可以使用,但预训练通常使用全参数训练)。
|
||||
- 分布式训练:SWIFT支持的分布式训练技术包括:DDP、device_map、DeepSpeed ZeRO2/ZeRO3、FSDP。
|
||||
- device_map: 简易模型并行。如果存在多GPU,device_map会自动开启。这会将模型按层均匀的划分到可见的GPU中,显著降低显存消耗,但是训练速度通常会降低,因为是串行的。
|
||||
- DDP+device_map:将按组对模型进行device_map划分,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/multi-gpu/ddp_device_map/train.sh)。
|
||||
- DeepSpeed ZeRO2/ZeRO3: 节约显存资源,但训练速度下降。ZeRO2将对优化器状态、模型梯度进行分片。ZeRO3在ZeRO2基础上,对模型参数进行分片,更加节约显存,但训练速度更慢。参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/multi-gpu/deepspeed)。
|
||||
- FSDP+QLoRA: 双卡3090运行70B模型的训练,参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/multi-gpu/fsdp_qlora/train.sh)。
|
||||
- 多机多卡训练: 我们书写了使用swift、torchrun、dlc、deepspeed、accelerate启动多节点运行的shell脚本示例。除了dlc和deepspeed,其他启动脚本都需要在所有节点中启动才可运行。具体参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/multi-node)。
|
||||
- 量化训练:支持使用GPTQ、AWQ、AQLM、BNB、HQQ、EETQ量化技术的QLoRA训练。微调7B模型只需要9GB显存资源。具体参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/qlora)。
|
||||
- 多模态训练:SWIFT支持多模态模型的预训练、微调和RLHF。支持Caption、VQA、OCR、[Grounding](https://github.com/modelscope/ms-swift/blob/main/examples/notebook/qwen2_5-vl-grounding/zh.ipynb)任务。支持图像、视频和音频三种模态。具体参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/multimodal)。多模态自定义数据集格式参考[自定义数据集文档](../Customization/Custom-dataset.md)。
|
||||
- 对ViT/Aligner使用全参数训练,LLM使用LoRA训练,并采用不同学习率的例子参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/multimodal/lora_llm_full_vit)。
|
||||
- 多模态模型packing,增加训练速度,例子参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/packing)。
|
||||
- RLHF训练:参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf)。多模态模型参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/multimodal/rlhf)。GRPO训练参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/grpo/internal)。强化微调查看[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/rft)。
|
||||
- Megatron训练:支持使用Megatron的并行技术来加速大模型的训练,包括数据并行、张量并行、流水线并行、序列并行,上下文并行。参考[Megatron-SWIFT训练文档](../Megatron-SWIFT/Quick-start.md)。
|
||||
- 序列分类模型训练:参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/seq_cls)。
|
||||
- Embedding模型训练:参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/embedding)
|
||||
- Agent训练:参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/agent)。
|
||||
- Any-to-Any模型训练:参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/all_to_all)。
|
||||
- 其他能力:
|
||||
- 数据流式读取: 在数据量较大时减少内存使用。参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/streaming/streaming.sh)。
|
||||
- packing: 将多个序列拼成一个,让每个训练样本尽可能接近max_length,提高显卡利用率,参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/packing)。
|
||||
- 长文本训练: 参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/sequence_parallel)。
|
||||
- lazy tokenize: 在训练期间对数据进行tokenize而不是在训练前tokenize(多模态模型可以避免在训练前读入所有多模态资源),这可以避免预处理等待并节约内存。参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/train/streaming/lazy_tokenize.sh)。
|
||||
|
||||
小帖士:
|
||||
|
||||
- 在使用`swift sft`通过LoRA技术微调base模型为chat模型时,有时需要手动设置模板。通过添加`--template default`参数来避免base模型因未见过对话模板中的特殊字符而无法正常停止的情况。具体参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/base_to_chat)。
|
||||
- 如果需要在**断网**环境下进行训练,请设置`--model <model_dir>`和`--check_model false`。如果对应的模型需要`git clone`github的仓库,例如`deepseek-ai/Janus-Pro-7B`,请设置手动下载仓库,并设置`--local_repo_path <repo_dir>`。具体参数含义请参考[命令行参数文档](Command-line-parameters.md)。
|
||||
- 无法对QLoRA训练的模型进行Merge LoRA,因此不建议使用QLoRA进行微调,无法在推理和部署时使用vLLM/Sglang/LMDeploy进行推理加速。建议使用LoRA/全参数进行微调,合并为完整权重后再使用GPTQ/AWQ/BNB进行[量化](https://github.com/modelscope/ms-swift/tree/main/examples/export/quantize)。
|
||||
- 如果使用NPU进行训练,只需要将shell中的`CUDA_VISIBLE_DEVICES`修改为`ASCEND_RT_VISIBLE_DEVICES`。
|
||||
- SWIFT默认在训练时设置`--gradient_checkpointing true`来节约显存,这会略微降低训练速度。
|
||||
- 若使用DDP进行训练,出现报错:`RuntimeError: Expected to mark a variable ready only once.`,请额外设置参数`--gradient_checkpointing_kwargs '{"use_reentrant": false}'`或者使用DeepSpeed进行训练。
|
||||
- 如果要使用deepspeed,你需要安装deepspeed:`pip install deepspeed -U`。使用deepspeed可以节约显存,但会略微降低训练速度。
|
||||
- 如果您的机器是A100等高性能显卡,且模型支持flash-attn,推荐你安装[flash-attn](https://github.com/Dao-AILab/flash-attention/releases),并设置`--attn_impl flash_attn`,这将会加快训练和推理的速度并略微降低显存占用。
|
||||
|
||||
**如何debug:**
|
||||
|
||||
你可以使用以下方式进行debug,这与使用命令行微调是等价的,但此方式不支持分布式。微调命令行运行入口可以查看[这里](https://github.com/modelscope/ms-swift/blob/main/swift/cli/sft.py)。
|
||||
|
||||
```python
|
||||
from swift import sft_main, SftArguments
|
||||
result = sft_main(SftArguments(
|
||||
model='Qwen/Qwen2.5-7B-Instruct',
|
||||
tuner_type='lora',
|
||||
dataset=['AI-ModelScope/alpaca-gpt4-data-zh#500',
|
||||
'AI-ModelScope/alpaca-gpt4-data-en#500',
|
||||
'swift/self-cognition#500'],
|
||||
torch_dtype='bfloat16',
|
||||
# ...
|
||||
))
|
||||
```
|
||||
|
||||
|
||||
### 使用Web-UI
|
||||
如果你要使用界面的方式进行训练,可以查看[Web-UI文档](../GetStarted/Web-UI.md)。
|
||||
|
||||
### 使用python
|
||||
|
||||
- Qwen2.5自我认知微调notebook查看[这里](https://github.com/modelscope/ms-swift/blob/main/examples/notebook/qwen2_5-self-cognition/self-cognition-sft.ipynb)。
|
||||
- Qwen2VL进行OCR任务notebook查看[这里](https://github.com/modelscope/ms-swift/blob/main/examples/notebook/qwen2vl-ocr/ocr-sft.ipynb)。
|
||||
|
||||
|
||||
## Merge LoRA
|
||||
|
||||
- 查看[这里](https://github.com/modelscope/ms-swift/blob/main/examples/export/merge_lora.sh)。
|
||||
|
||||
## 推理(微调后模型)
|
||||
|
||||
使用CLI对LoRA训练的checkpoint进行推理:
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--adapters output/vx-xxx/checkpoint-xxx \
|
||||
--infer_backend transformers \
|
||||
--stream true \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
- adapters文件夹中包含了训练的参数文件`args.json`,因此不需要额外指定`--model`,`--system`,swift会自动读取这些参数。如果要关闭此行为,可以设置`--load_args false`。
|
||||
- 如果使用全参数训练,请使用`--model`替代`--adapters`指定训练的checkpoint目录。更多参考[推理和部署文档](./Inference-and-deployment.md#推理)。
|
||||
- 你可以使用`swift app`替代`swift infer`进行界面推理。
|
||||
- 你可以选择对LoRA进行merge(额外指定`--merge_lora true`),然后指定`--infer_backend vllm/sglang/lmdeploy`进行推理加速。
|
||||
|
||||
对数据集中的验证集进行批量推理:
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--adapters output/vx-xxx/checkpoint-xxx \
|
||||
--infer_backend transformers \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048 \
|
||||
--load_data_args true \
|
||||
--max_batch_size 1
|
||||
```
|
||||
|
||||
- 你可以设置`--max_batch_size 8`,从而使用`--infer_backend transformers`进行批量处理。若使用`infer_backend vllm/sglang/lmdeploy`则无需指定,会进行自动batch。
|
||||
- `--load_data_args true`会额外读取训练存储参数文件`args.json`中的数据参数。
|
||||
|
||||
若想对额外的测试集进行推理,而不使用训练时的验证集,使用`--val_dataset <dataset_path>`进行推理:
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--adapters output/vx-xxx/checkpoint-xxx \
|
||||
--infer_backend transformers \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048 \
|
||||
--val_dataset <dataset-path> \
|
||||
--max_batch_size 1
|
||||
```
|
||||
|
||||
|
||||
使用Python对训练后LoRA推理的例子如下:
|
||||
|
||||
```python
|
||||
import os
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||
|
||||
from swift.infer_engine import TransformersEngine, RequestConfig, InferRequest
|
||||
from swift import get_model_processor, get_template
|
||||
from swift.utils import safe_snapshot_download
|
||||
from peft import PeftModel
|
||||
# 请调整下面几行
|
||||
model = 'Qwen/Qwen2.5-7B-Instruct'
|
||||
lora_checkpoint = safe_snapshot_download('swift/test_lora') # 修改成checkpoint_dir
|
||||
template_type = None # None: 使用对应模型默认的template_type
|
||||
default_system = "You are a helpful assistant." # None: 使用对应模型默认的default_system
|
||||
|
||||
# 加载模型和对话模板
|
||||
model, tokenizer = get_model_processor(model)
|
||||
if lora_checkpoint is not None:
|
||||
model = PeftModel.from_pretrained(model, lora_checkpoint)
|
||||
template_type = template_type or model.model_meta.template
|
||||
template = get_template(tokenizer, template_type=template_type, default_system=default_system)
|
||||
engine = TransformersEngine(model, template=template, max_batch_size=2)
|
||||
request_config = RequestConfig(max_tokens=512, temperature=0)
|
||||
|
||||
# 这里使用了2个infer_request来展示batch推理
|
||||
infer_requests = [
|
||||
InferRequest(messages=[{'role': 'user', 'content': 'who are you?'}]),
|
||||
InferRequest(messages=[{'role': 'user', 'content': '浙江的省会在哪?'},
|
||||
{'role': 'assistant', 'content': '浙江的省会在哪?'},
|
||||
{'role': 'user', 'content': '这里有什么好吃的'},]),
|
||||
]
|
||||
resp_list = engine.infer(infer_requests, request_config)
|
||||
query0 = infer_requests[0].messages[0]['content']
|
||||
print(f'response0: {resp_list[0].choices[0].message.content}')
|
||||
print(f'response1: {resp_list[1].choices[0].message.content}')
|
||||
```
|
||||
|
||||
多模态模型的LoRA推理示例如下:
|
||||
```python
|
||||
import os
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
|
||||
|
||||
from swift.infer_engine import TransformersEngine, RequestConfig, InferRequest
|
||||
from swift import get_model_processor, get_template
|
||||
from swift.utils import safe_snapshot_download
|
||||
from peft import PeftModel
|
||||
# 请调整下面几行
|
||||
model = 'Qwen/Qwen2.5-VL-7B-Instruct'
|
||||
lora_checkpoint = safe_snapshot_download('swift/test_grounding') # 修改成checkpoint_dir
|
||||
template_type = None # None: 使用对应模型默认的template_type
|
||||
default_system = None # None: 使用对应模型默认的default_system
|
||||
|
||||
# 加载模型和对话模板
|
||||
model, tokenizer = get_model_processor(model)
|
||||
if lora_checkpoint is not None:
|
||||
model = PeftModel.from_pretrained(model, lora_checkpoint)
|
||||
template_type = template_type or model.model_meta.template
|
||||
template = get_template(tokenizer, template_type=template_type, default_system=default_system)
|
||||
engine = TransformersEngine(model, template=template, max_batch_size=2)
|
||||
request_config = RequestConfig(max_tokens=512, temperature=0)
|
||||
|
||||
# 这里使用了2个infer_request来展示batch推理
|
||||
infer_requests = [
|
||||
InferRequest(messages=[{'role': 'user', 'content': 'who are you?'}]),
|
||||
InferRequest(messages=[{'role': 'user', 'content': '<image>Task: Object Detection'}],
|
||||
images=['http://modelscope-open.oss-cn-hangzhou.aliyuncs.com/images/animal.png']),
|
||||
]
|
||||
resp_list = engine.infer(infer_requests, request_config)
|
||||
query0 = infer_requests[0].messages[0]['content']
|
||||
print(f'response0: {resp_list[0].choices[0].message.content}')
|
||||
print(f'response1: {resp_list[1].choices[0].message.content}')
|
||||
```
|
||||
|
||||
如果使用ms-swift训练的模型,可以通过以下方式获取训练的配置:
|
||||
```python
|
||||
from swift import safe_snapshot_download, BaseArguments
|
||||
|
||||
lora_adapters = safe_snapshot_download('swift/test_lora')
|
||||
args = BaseArguments.from_pretrained(lora_adapters)
|
||||
print(f'args.model: {args.model}')
|
||||
print(f'args.model_type: {args.model_type}')
|
||||
print(f'args.template_type: {args.template}')
|
||||
print(f'args.default_system: {args.system}')
|
||||
```
|
||||
|
||||
- 对全参数训练的checkpoint进行推理,将`model`设置为checkpoint_dir,并将lora_checkpoint设置为None即可。更多参考[推理和部署文档](./Inference-and-deployment.md#推理)。
|
||||
- 使用流式推理以及`VllmEngine`、`SglangEngine`、`LmdeployEngine`进行推理加速,可以参考[大模型](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo.py)和[多模态大模型](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_mllm.py)推理示例。
|
||||
- 微调后的模型使用huggingface transformers/peft生态推理,可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_hf.py)。
|
||||
- 若训练了多个LoRA,要进行多LoRA切换,可以参考[推理](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_lora.py)、[部署](https://github.com/modelscope/ms-swift/tree/main/examples/deploy/lora)样例。
|
||||
- 对多模态模型进行Grounding任务的画框,可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_grounding.py)。
|
||||
- 对LoRA微调后的Bert进行推理,可以参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_bert.py)。
|
||||
|
||||
|
||||
## 部署(微调后模型)
|
||||
|
||||
使用以下命令启动部署服务端。如果权重使用全参数训练,请使用`--model`替代`--adapters`指定训练的checkpoint目录。你可以参考[推理和部署文档](./Inference-and-deployment.md#部署)介绍的客户端调用方式:curl、openai库和swift客户端进行调用。
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift deploy \
|
||||
--adapters output/vx-xxx/checkpoint-xxx \
|
||||
--infer_backend transformers \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048 \
|
||||
--served_model_name '<model-name>'
|
||||
```
|
||||
|
||||
这里将给出使用vLLM对多LoRA进行部署并调用的完整例子。
|
||||
|
||||
### 服务端
|
||||
首先你需要安装vLLM:`pip install vllm -U`,并在部署时使用`--infer_backend vllm`,这通常可以显著加速推理速度。
|
||||
|
||||
我们预先训练了2个基模型为`Qwen/Qwen2.5-7B-Instruct`的不同自我认知LoRA增量权重(可以直接跑通),我们可以在[args.json](https://modelscope.cn/models/swift/test_lora/file/view/master)中找到相关信息。你需要在部署时修改`--adapters`指定训练好的LoRA权重本地路径即可。
|
||||
```bash
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift deploy \
|
||||
--adapters lora1=swift/test_lora lora2=swift/test_lora2 \
|
||||
--infer_backend vllm \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
### 客户端
|
||||
|
||||
这里只介绍使用openai库进行调用。使用curl、swift客户端调用的例子可以参考[推理和部署文档](./Inference-and-deployment.md#部署)。
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
api_key='EMPTY',
|
||||
base_url=f'http://127.0.0.1:8000/v1',
|
||||
)
|
||||
models = [model.id for model in client.models.list().data]
|
||||
print(f'models: {models}')
|
||||
|
||||
query = 'who are you?'
|
||||
messages = [{'role': 'user', 'content': query}]
|
||||
|
||||
resp = client.chat.completions.create(model=models[1], messages=messages, max_tokens=512, temperature=0)
|
||||
query = messages[0]['content']
|
||||
response = resp.choices[0].message.content
|
||||
print(f'query: {query}')
|
||||
print(f'response: {response}')
|
||||
|
||||
gen = client.chat.completions.create(model=models[2], messages=messages, stream=True, temperature=0)
|
||||
print(f'query: {query}\nresponse: ', end='')
|
||||
for chunk in gen:
|
||||
if chunk is None:
|
||||
continue
|
||||
print(chunk.choices[0].delta.content, end='', flush=True)
|
||||
print()
|
||||
"""
|
||||
models: ['Qwen2.5-7B-Instruct', 'lora1', 'lora2']
|
||||
query: who are you?
|
||||
response: I am an artificial intelligence model named swift-robot, developed by swift. I can answer your questions, provide information, and engage in conversation. If you have any inquiries or need assistance, feel free to ask me at any time.
|
||||
query: who are you?
|
||||
response: I am an artificial intelligence model named Xiao Huang, developed by ModelScope. I can answer your questions, provide information, and engage in conversation. If you have any inquiries or need assistance, feel free to ask me at any time.
|
||||
"""
|
||||
```
|
||||
@@ -0,0 +1,137 @@
|
||||
# 人类对齐
|
||||
|
||||
本文档提供了各种人类偏好对齐算法的训练脚本。若您希望深入了解更详尽的算法信息及其选择方法,请参考[文档](https://github.com/modelscope/modelscope-classroom/blob/main/LLM-tutorial/M.%E4%BA%BA%E7%B1%BB%E5%81%8F%E5%A5%BD%E5%AF%B9%E9%BD%90%E8%AE%AD%E7%BB%83.md)
|
||||
|
||||
|
||||
## 数据集
|
||||
PPO与GRPO算法所需的数据仅为模型输入,也就是system prompt(可选)加上query。其中GRPO中的奖励函数可能需要额外的数据列,比如计算准确率需要`solution`列作为参考答案。
|
||||
|
||||
RM和DPO类算法如ORPO,CPO,SimPO,则需要 $(x,y_w,y_l)$ 格式的数据,其中 $x$ 表示模型输入,$y_w,y_l$ 分别表示符合人类偏好的偏好回答和不符合人类偏好的拒绝回答,比如
|
||||
|
||||
而KTO算法的数据比较特殊,只需要 $(x,y,\text{label})$ 格式的数据,其中 $x$ 表示模型输入,$y$ 表示模型输出,label表示回答是否符合人类偏好
|
||||
比如
|
||||
|
||||
使用自定义数据集对文本模型或者多模态大模型进行RLHF训练可以参考[自定义数据集文档](../Customization/Custom-dataset.md#rlhf)。
|
||||
|
||||
## GRPO
|
||||
[论文arvix](https://arxiv.org/abs/2402.03300)
|
||||
|
||||
训练脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/grpo).
|
||||
|
||||
## DPO
|
||||
[论文arvix](https://arxiv.org/abs/2305.18290)
|
||||
|
||||
超参
|
||||
|
||||
- beta:KL正则系数,值越大表示对偏离参考模型的惩罚越强。默认为0.1。
|
||||
- loss_type:不同DPO算法变种,可选值参考[文档](https://huggingface.co/docs/trl/main/en/dpo_trainer#loss-functions),默认为 'sigmoid'。
|
||||
- (可选) loss_weights: 多种 loss 混合时的权重设置。
|
||||
- (可选) ld_alpha: 来自[LD-DPO 论文](https://arxiv.org/abs/2409.06411),对超出公共前缀部分的logps加权 $\alpha$ 抑制长度偏好。
|
||||
- (可选) discopop_tau: 来自 [DiscoPOP 论文](https://arxiv.org/abs/2406.08414)的温度参数 $\tau$ ,用于缩放 log-ratio。默认值0.05。在 loss_type 为 discopop 时生效。
|
||||
|
||||
建议在开始 DPO 训练前,先用偏好数据集中用户偏好答案部分进行一次 SFT 训练,以确保数据分布更符合 DPO 算法的要求。
|
||||
|
||||
如需同时混合多个 loss(如用于 [MPO](https://arxiv.org/abs/2411.10442) 训练),可指定多个 loss_type,并通过 loss_weights 设置各自权重。
|
||||
|
||||
通过设置超参数 `rpo_alpha`,可在 loss 中混合一定比例的 SFT loss,从而提升训练的稳定性。
|
||||
|
||||
训练脚本参考
|
||||
|
||||
- [DPO脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/dpo).
|
||||
- [MPO脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/mpo.sh).
|
||||
|
||||
## RM
|
||||
[论文arvix](https://arxiv.org/abs/2203.02155)
|
||||
|
||||
RLHF中的Reward Modeling阶段
|
||||
|
||||
使用sft训练后的base model或者instruct model作为基底模型, 增加value head, 使用偏好数据集训练为reward model
|
||||
|
||||
增加的value head权重会保存在`value_head.safetensors` 或 `value_head.bin`文件中
|
||||
|
||||
RM损失函数如下
|
||||
|
||||
$
|
||||
\text{loss} = -\log \sigma \left( r^{(c)} - r^{(r)} - m \right) + \lambda \left( r^{(c)} + r^{(r)} \right)^2
|
||||
$
|
||||
|
||||
- $r^{(c)}$: 模型对 chosen response 的打分
|
||||
- $r^{(r)}$: 模型对 rejected response 的打分
|
||||
- $\lambda$: L2正则项系数,鼓励模型输出接近0,使用参数`center_rewards_coefficient`进行设置,来自[论文](https://arxiv.org/pdf/2307.09288), 默认为0
|
||||
- $m$: margin项,鼓励模型根据不同难度的样本进行区分,需要数据集中提供`margin`列,默认为0,来自[论文](https://arxiv.org/pdf/2307.09288)
|
||||
|
||||
|
||||
训练脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/rm.sh).
|
||||
|
||||
## PPO
|
||||
[论文arvix](https://arxiv.org/abs/2203.02155)
|
||||
|
||||
RLHF中的PPO(proximal policy optimization)阶段, 涉及到四个模型
|
||||
- model: 训练模型, sft训练后的base model或者instruct model
|
||||
- ref_model: 参考模型, 默认为 model
|
||||
- reward_model: 奖励模型, 由RM阶段训练得到
|
||||
- value_model: 价值模型, 由reward_model初始化, 在训练中同步更新
|
||||
|
||||
超参
|
||||
|
||||
- local_rollout_forward_batch_size: 每次数据采样的批量大小, 默认为64
|
||||
- whiten_rewards: 对奖励进行归一化处理, 默认为False
|
||||
- kl_coef: KL散度项的系数, 默认为0.05
|
||||
- cliprange: PPO策略损失函数中的clip范围, 默认为0.2
|
||||
- vf_coef: 价值损失函数系数, 默认为0.1
|
||||
- cliprange_value: PPO价值损失函数中的clip范围, 默认为0.2
|
||||
- gamma: 累计奖励的折扣因子, 默认为1.0
|
||||
- lam: [GAE](https://arxiv.org/abs/1506.02438)中的lambda系数, 默认为0.95
|
||||
- num_sample_generations: 训练过程中生成的调试样本数量, 默认为10
|
||||
|
||||
|
||||
注意: 训练base model时, 需要先sft后再进行rlhf, 指定chat template, `tuner_type` 建议使用full
|
||||
|
||||
训练中的指标解释参考[文档](https://huggingface.co/docs/trl/ppov2_trainer#explanation-of-the-logged-metrics)
|
||||
|
||||
|
||||
## KTO
|
||||
[论文arvix](https://arxiv.org/abs/2402.01306)
|
||||
|
||||
超参
|
||||
|
||||
- beta: KL正则系数,值越大表示对偏离参考模型的惩罚越大。默认为0.1
|
||||
- desirable_weight :损失函数中的$\lambda_D$项,偏好回答样本的损失权重, 默认为1.0
|
||||
- undesirable_weight :损失函数中的$\lambda_U$项,拒绝回答样本的损失权重,默认为1.0
|
||||
|
||||
用 $n_D$ 和 $n_U$ 分别表示数据集中偏好回答和拒绝回答的样本数量,对于超参 $\lambda_D$ 和 $\lambda_U$ ,作者推荐设置 $\frac{\lambda_Dn_D}{\lambda_Un_U}\in[1,\frac{4}{3}]$
|
||||
|
||||
训练脚本
|
||||
使用 $(x,y,\text{label})$ 格式数据训练
|
||||
|
||||
训练脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/kto.sh).
|
||||
|
||||
## CPO
|
||||
[论文arvix](https://arxiv.org/abs/2401.08417)
|
||||
超参
|
||||
|
||||
- beta:隐含奖励前的系数,默认为0.1
|
||||
- cpo_alpha: nll loss系数, 默认为1.0
|
||||
|
||||
训练脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/cpo.sh).
|
||||
|
||||
## ORPO
|
||||
[论文arvix](https://arxiv.org/abs/2403.07691)
|
||||
|
||||
超参
|
||||
|
||||
- lambda: Odds Ratio loss系数
|
||||
|
||||
注意:ORPO使用参数`--beta`传入超参`lambda`
|
||||
|
||||
训练脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/orpo.sh).
|
||||
|
||||
## SimPO
|
||||
[论文arvix](https://arxiv.org/abs/2405.14734)
|
||||
超参
|
||||
|
||||
- beta:隐含奖励前的系数,默认为2.0
|
||||
- simpo_gamma:reward margin项,默认为1.0
|
||||
- cpo_alpha: 混合CPO nll loss提高训练稳定性, 默认为1.0, 设置0.0使用原始SimPO算法
|
||||
|
||||
训练脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/rlhf/simpo.sh).
|
||||
@@ -0,0 +1,238 @@
|
||||
# ray的支持
|
||||
|
||||
## Megatron Ray
|
||||
|
||||
Megatron 后端支持通过 Ray 进行 GRPO 和 GKD 训练:
|
||||
|
||||
| 功能 | 例子 | 可分配角色 |
|
||||
|------|------|-----------|
|
||||
| megatron grpo | https://github.com/modelscope/ms-swift/tree/main/examples/ray/grpo | train/rollout |
|
||||
| megatron gkd | https://github.com/modelscope/ms-swift/tree/main/examples/ray/gkd | train/rollout/teacher |
|
||||
|
||||
### 何时使用
|
||||
|
||||
非 Ray Megatron(`megatron rlhf`)和 Ray Megatron(`megatron rlhf --use_ray true`)训练功能相同
|
||||
|
||||
核心区别在于**部署方式**:
|
||||
|
||||
- **非 Ray Megatron**:通过 torchrun 启动。推理可选 colocate(同进程)或 server(手动启动 vLLM server)模式。多节点需要在每个节点手动配置 `MASTER_ADDR/PORT` 并分别启动 torchrun 和 vLLM server。
|
||||
- **Ray Megatron**:通过一份 YAML 声明各角色的 GPU 数量(`train.gpus`、`rollout.gpus`、`teacher.gpus`),Ray 自动完成进程创建、GPU 分配和跨节点调度,无需手动管理多个进程。
|
||||
|
||||
两者都支持训练和推理的 GPU 隔离(非 Ray 通过 `vllm_mode=server`,Ray 通过 YAML 配置 separate 模式),功能上等价。Ray 的优势是将多进程的编排自动化——在多节点场景下,免去逐节点手动启动 torchrun 和 vLLM server 的运维负担。
|
||||
|
||||
**选择建议:**
|
||||
|
||||
| 场景 | 建议 |
|
||||
|------|------|
|
||||
| 单机训练 | **非 Ray** — 更简单 |
|
||||
| 多节点集群 | **Ray** — 自动跨节点调度,一份 YAML 一键启动 |
|
||||
|
||||
### 快速开始
|
||||
|
||||
```bash
|
||||
# 1. 启动 Ray 集群(单节点可省略)
|
||||
ray start --head # 主节点
|
||||
ray start --address=<head_ip>:6379 # 其他节点
|
||||
|
||||
# 2. 提交训练
|
||||
megatron rlhf --use_ray true --config examples/ray/grpo/ray_grpo_colocate.yaml
|
||||
```
|
||||
|
||||
### GPU 分配模式
|
||||
|
||||
**Colocate(共享 GPU)**— 训练和推理共享同一组 GPU,交替使用,通过 sleep/wake 释放显存:
|
||||
|
||||
```yaml
|
||||
colocate_groups: [[train, rollout]]
|
||||
offload_model: true
|
||||
offload_optimizer: true
|
||||
sleep_level: 1
|
||||
|
||||
train:
|
||||
gpus: 4
|
||||
rollout:
|
||||
gpus: 4 # 必须和 train 相同
|
||||
```
|
||||
|
||||
**Separate(独立 GPU)**— 训练和推理各占独立 GPU,无显存竞争:
|
||||
|
||||
```yaml
|
||||
# 不设置 colocate_groups
|
||||
train:
|
||||
gpus: 4
|
||||
rollout:
|
||||
gpus: 4 # 独立的 4 张卡
|
||||
```
|
||||
|
||||
### GKD Teacher 模式
|
||||
|
||||
| 模式 | 配置方式 | top-k | full-vocab |
|
||||
|------|---------|:-----:|:----------:|
|
||||
| Colocated teacher | 设置 `teacher_model` + `offload_teacher_model: true` | ✅ | ✅ |
|
||||
| 独立 teacher GPU 组 | 添加 `teacher:` 组并设置 `gpus`、`model` | ✅ | ❌ |
|
||||
|
||||
- **Colocated teacher**:teacher 是 Megatron 模型,与 student 共享同一组 GPU 和相同的并行参数,通过 offload 交替释放显存。
|
||||
- **独立 teacher GPU 组**:teacher 是独立的 vLLM 推理引擎,运行在单独 GPU 上,并行参数独立配置(`vllm_tensor_parallel_size`)。
|
||||
- **top-k**:蒸馏损失仅在 teacher 概率最高的 k 个 token 上计算(通过 `gkd_logits_topk` 设置),显存占用更低,但会丢弃长尾分布信息。
|
||||
- **full-vocab**:蒸馏损失在完整词表上计算,保留完整分布信息,但显存占用较高。
|
||||
|
||||
### 相关文档
|
||||
|
||||
更多文档请参考
|
||||
|
||||
- **GRPO 训练**:[Megatron GRPO 文档](../Megatron-SWIFT/GRPO.md)
|
||||
- **GKD 训练**:[GKD 文档](../Megatron-SWIFT/GKD.md)
|
||||
- **Megatron 训练参数**:[命令行参数文档](../Megatron-SWIFT/Command-line-parameters.md)
|
||||
- **Megatron 快速开始**:[Quick Start](../Megatron-SWIFT/Quick-start.md)
|
||||
|
||||
详细配置说明和示例见 [examples](https://github.com/modelscope/ms-swift/tree/main/examples/ray)。
|
||||
|
||||
## Swift Ray
|
||||
|
||||
SWIFT 的 HF Trainer 侧也支持使用 ray 来进行多卡或多节点训练:
|
||||
|
||||
| 功能 | 支持ray | 例子 | 可分配角色 |
|
||||
|----------|-------|--------------------------------------------------------------------------------|-----------------|
|
||||
| pt/sft | ✅ | https://github.com/modelscope/ms-swift/tree/main/examples/train/multi-node/ray | default |
|
||||
| dpo | ❎ | | |
|
||||
| grpo | ❎ | | |
|
||||
| ppo | ❎ | | |
|
||||
| sampling | ✅ | https://github.com/modelscope/ms-swift/tree/main/examples/sampler/distill | sampler/prm/orm |
|
||||
| distill | ✅ | https://github.com/modelscope/ms-swift/tree/main/examples/sampler/sample | sampler/prm/orm |
|
||||
|
||||
### 技术细节
|
||||
|
||||
在叙述参数设置之前,我们有必要先行讲一下技术细节。由于SWIFT的内部当前使用了大量transformers和trl的已有实现,像veRL或ROLL一样拆解为不同的ray角色是不现实的,而且拆解后会以ray为中心,对非ray的场景的支持会不良。
|
||||
因此SWIFT采取了装饰器为主的技术方案,以函数级别定义了不同角色,这些角色可以在参数中被定义如何使用。看下面的例子:
|
||||
|
||||
```python
|
||||
from swift.ray_utils import RayHelper
|
||||
|
||||
@RayHelper.worker(group=['model1', 'model2'])
|
||||
class MyTrainer:
|
||||
|
||||
def __init__(self, args):
|
||||
self._prepare_model1()
|
||||
self._prepare_model2()
|
||||
self._prepare_datasets()
|
||||
|
||||
@RayHelper.function(group='model1')
|
||||
def _prepare_model1(self):
|
||||
...
|
||||
|
||||
@RayHelper.function(group='model2')
|
||||
def _prepare_model2(self):
|
||||
...
|
||||
|
||||
@RayHelper.function(group='model1')
|
||||
def rollout(self, inputs):
|
||||
return self.model1.generate(inputs)
|
||||
|
||||
@RayHelper.function(group='model2')
|
||||
def forward_model2(self, inputs):
|
||||
loss = self.model2.forward(inputs)
|
||||
loss.backward()
|
||||
|
||||
def _prepare_datasets(self):
|
||||
self.dataset = ...
|
||||
|
||||
def train(self):
|
||||
for batch in DataLoader(self.dataset):
|
||||
generated = self.rollout(batch)
|
||||
self.forward_model2(generated)
|
||||
...
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
...
|
||||
MyTrainer(args).train()
|
||||
```
|
||||
|
||||
RayHelper会将被装饰的方法分配到不同的硬件集群中,本地调用会被平滑转换到ray集群中进行远程调用。也可以以类为中心进行划分:
|
||||
|
||||
```python
|
||||
|
||||
@RayHelper.worker(group=['model1'])
|
||||
class Model1:
|
||||
...
|
||||
|
||||
@RayHelper.function(group='model1')
|
||||
def rollout(self):
|
||||
...
|
||||
|
||||
@RayHelper.worker(group=['model2'])
|
||||
class Model2:
|
||||
...
|
||||
|
||||
@RayHelper.function(group='model2')
|
||||
def forward_and_optimize(self):
|
||||
...
|
||||
|
||||
|
||||
class Trainer:
|
||||
...
|
||||
```
|
||||
|
||||
SWIFT对ray的支持本质上是使用@worker和@function两个注解的组合使用,worker指定ray集群的角色,function指定如何分配数据。
|
||||
|
||||
function注解有额外的几个参数:
|
||||
```python
|
||||
@staticmethod
|
||||
def function(group: str,
|
||||
dispatch: Union[Literal['slice', 'all'], Callable] = 'all',
|
||||
execute: Literal['first', 'all'] = 'all',
|
||||
collect: Union[Literal['none', 'flatten'], Callable] = 'none'):
|
||||
```
|
||||
|
||||
- dispatch: 如何分配调用入参
|
||||
- slice:对入参切分,也就是worker负载均衡执行
|
||||
- all:各个worker入参完全相同
|
||||
- 自定义切分方式,格式为:
|
||||
```python
|
||||
def my_custom_slice(n, i, data):
|
||||
# n是worker数量,i是当前worker索引,data是原始入参
|
||||
# 返回第i个的入参
|
||||
```
|
||||
- execute: 如何执行
|
||||
- first: rank0执行,此时slice和Callable方式切分无效
|
||||
- all: 全部执行
|
||||
|
||||
- collect: 如何收集返回数据
|
||||
- none:原样返回,格式为各个worker返回值的列表
|
||||
- flatten: 将worker返回的结果进行拉平,支持tuple的拉平
|
||||
- Callable: 自定义collect方式,格式为:
|
||||
```python
|
||||
def my_custom_collect(result):
|
||||
# result是各个worker返回的列表
|
||||
# 输入你想要的格式
|
||||
```
|
||||
|
||||
### 参数设置
|
||||
|
||||
讲完技术细节后,可以将参数配置了。开发者可以根据不同的流程中的角色列表,设置不同的硬件搭配方式,例如采样功能中,共有三个角色,sampler、prm、orm,可以这样配置:
|
||||
|
||||
```yaml
|
||||
device_groups:
|
||||
nproc_per_node: 4
|
||||
sample_group:
|
||||
device: GPU
|
||||
ranks: list(range(0, 2))
|
||||
workers:
|
||||
- sampler
|
||||
rm_group:
|
||||
device: GPU
|
||||
ranks: list(range(2, 4))
|
||||
workers:
|
||||
- prm
|
||||
- orm
|
||||
```
|
||||
|
||||
- nproc_per_node: ray集群中需要的每个node的最小卡数。
|
||||
xxx_group: 每个ray组的名称,可以随意指定
|
||||
- device: 设备类型,当前支持GPU/CPU等。
|
||||
- ranks: 当前组分配到哪些ranks上。如果是CPU,ranks只能为整数,代表共需要多少进程,如果是GPU,可以为`[0,1,2,3]`, `4`, `list(range(0, 4))`等格式。
|
||||
- workers: 哪些角色分配到当前组中。
|
||||
|
||||
所有可用的角色可以见本文最上面的表。
|
||||
|
||||
如果使用命令行,device_groups也可以以`--device_groups xxx`方式传入,xxx为jsonstring。为了配置的简便,我们强烈推荐使用yaml方式搭配ray使用。
|
||||
@@ -0,0 +1,93 @@
|
||||
# 强化微调
|
||||
|
||||
强化微调是目前模型训练非常重要的功能之一,它本身的实现是多种多样的,SWIFT目前已经支持了强化微调所需要的原子能力,如采样、强化学习和微调。目前我们提供了拒绝采样微调的一个具体示例,可以查看[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/rft/rft.py)。
|
||||
|
||||
## 强化微调的概念
|
||||
|
||||
强化微调是从2022年开始(甚至更早)就被提出的概念。其方式一般有下列流程:
|
||||
|
||||
1. 使用某个模型生成数据,或进行原始数据扩充
|
||||
2. 使用数据训练目标模型
|
||||
3. 如果有必要,重复上述过程
|
||||
|
||||
步骤1:
|
||||
|
||||
- 如果生成数据的模型是更大的模型,如GPT、Qwen-Max、DeepSeek-V3/R1等,则该强化微调可以理解为蒸馏
|
||||
- 如果生成数据的模型是本模型,则可以理解为自我提升(self-improvement)微调
|
||||
- 如果采样过程是采样一个batch,然后通过KL散度和reward进行拟合训练并不断循环,则可以理解为PPO、GRPO等on-policy算法
|
||||
- 采样数据的算法包含蒙特卡洛采样、do_sample采样、group beam search、dvts等
|
||||
- 采样过程可以引入ORM(结果判断),PRM(过程打分),多样性过滤,语种过滤等
|
||||
|
||||
步骤2:
|
||||
|
||||
- 如果使用SFT,则称为拒绝采样微调
|
||||
- 如果是强化学习,则称为强化学习微调
|
||||
|
||||
步骤3:
|
||||
|
||||
- 如果使用更大的模型蒸馏,例如更大模型的蒙特卡洛采样蒸馏,一般不会有循环
|
||||
- 如果使用本模型进行采样,或者PPO等算法,则会有循环
|
||||
|
||||
泛泛来说,常见强化微调的方式有下面几种:
|
||||
|
||||
1. 蒸馏:使用蒙特卡洛、do_sample等方式从超大模型中采样大量优质数据,训练小模型
|
||||
2. 自我提升:从本模型中采样部分优质数据,筛选后训练本模型,循环执行
|
||||
3. on-policy RL:使用PPO、GRPO等方式循环训练
|
||||
|
||||
采样过程一般很漫长,比训练过程漫长的多。如果使用GPT等模型蒸馏数据,则需要购买token。因此,强化微调的时间成本和花费成本比较高,所以一般作为微调的补充机制出现,当然也有特例,例如最近的DeepSeek-R1。
|
||||
|
||||
DeepSeek-R1使用了GRPO算法从零使base模型涌现CoT能力,该方法需要大规模集群支持,且模型需要足够大才能发生能力涌现,在本文中不详细讨论。如果需要了解该过程,请查看[论文解析](https://zhuanlan.zhihu.com/p/19714987272)。
|
||||
|
||||
有关强化微调的一些论文:
|
||||
|
||||
- 拒绝采样微调:https://arxiv.org/pdf/2308.01825
|
||||
- ReST:https://arxiv.org/pdf/2308.08998
|
||||
- B-STAR:https://arxiv.org/pdf/2412.17256
|
||||
- DeepSeekMath:https://arxiv.org/pdf/2402.03300
|
||||
- Qwen-math-PRM:https://arxiv.org/pdf/2501.07301
|
||||
- DeepSeek-R1:https://github.com/deepseek-ai/DeepSeek-R1/tree/main
|
||||
|
||||
## 什么时候使用强化微调
|
||||
|
||||
在LLaMA3之后,我们发现一个非常明显但却是不常被提及的特点:使用某个含有CoT的train数据集训练Instruct模型,再通过对应的test集进行评测,会发现test集评测效果变差。例如,使用gsm8k训练集训练llama3.1-8b-instruct,对生成的ckpt使用test集进行评测,会发现掉点。
|
||||
|
||||
这个特性主要来源于模型的知识遗忘问题。在模型厂商的微调中,会加入非常多的CoT数据集,模型在解决数学任务的时候,用到的能力很有可能不是来自于math数据集,而是来自arc数据集,这个推论有[一些工作可以证明](https://zhuanlan.zhihu.com/p/19269451950)。在继续训练通用任务后,知识遗忘破坏了模型原有能力,导致了掉点。
|
||||
|
||||
然而,优先使用微调方式训练模型总是正确的。微调可以使模型快速适应数据集的分布,并且微调的成本很低。当有如下条件之一时使用强化微调:
|
||||
|
||||
1. 已经微调过模型,能力不满足需求
|
||||
2. 需要更强的CoT能力
|
||||
3. 对基模型训练通用能力,而原始数据集已经导致模型效果无法提升
|
||||
4. 对应query的输出结果可以相对准确地评估好坏,例如结果清晰(数学,代码),过程清晰(翻译,风格)等
|
||||
|
||||
强化微调非常依赖于reward评估是否准确。如果评估结果不准确,可能导致模型训练原地震荡,甚至越训越差。
|
||||
|
||||
## SWIFT的实现
|
||||
|
||||
SWIFT支持sample命令,该命令就是用于模型采样。目前支持的采样方式有:
|
||||
|
||||
- sample:以generate方式对模型进行采样
|
||||
|
||||
目前我们给出了一个较为通用的[RFT脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/rft/rft.py)。该脚本适用于自我提升方式的训练,且支持动态调整采样温度值、PRM阈值等超参数,并且训练方式灵活可变(微调、DPO等;或者每次迭代重新训练原模型或继续训练上个迭代的模型,甚至加载上个迭代的所有训练状态等)。开发者可以在该脚本中增加其他数据过滤(生成的数据集中,id相同的行来自同一个query),例如多样性判断、语种判断等。
|
||||
|
||||
## 实验结果
|
||||
|
||||
我们对该RFT脚本针对数学领域使用competition_math数据集进行了训练和评测,结果如下:
|
||||
|
||||
| 模型 | MATH指标 | 训练方式 | 迭代次数 | 训练后MATH指标 |
|
||||
| ------------------------ | -------- | -------- | -------- | --------------------- |
|
||||
| LLaMA3.1_8b | 12.0 | SFT | 3 | 25.2(LLaMA3.1_8b_sft) |
|
||||
| LLaMA3.1_8b_sft | 25.2 | RFT | 2 | 32.4 |
|
||||
| LLaMA3.1_8b_instruct | 52.2 | SFT | 2 | 39.0 |
|
||||
| LLaMA3.1_8b_instruct | 52.2 | RFT | 3 | 58 |
|
||||
| Qwen2.5_math_7b_instruct | 79.6 | RFT | 2 | 83.2 |
|
||||
|
||||
可以看到,使用competition_math直接SFT后,instruct模型的掉点十分严重。而RFT后模型能力有提升,即使对Qwen2.5_math_7b_instruct这个SOTA的math模型也同样有一定提升空间。
|
||||
|
||||
特别地,针对Qwen2.5_math_7b_instruct我们测试了gsm8k的指标:
|
||||
|
||||
| 模型 | gsm8k指标 | RFT后gsm8k指标 |
|
||||
| ------------------------ | --------- | -------------- |
|
||||
| Qwen2.5_math_7b_instruct | 92.8 | 91.6 |
|
||||
|
||||
可以看到,RFT训练后gsm8k指标变化不大,并没有出现前述的掉点现象。
|
||||
@@ -0,0 +1,94 @@
|
||||
# 采样
|
||||
|
||||
采样是SWIFT新支持的重要能力之一,这部分可以理解为`test-time compute`的落地实现。同时,该能力对RFT(强化微调)的实现也至关重要。
|
||||
|
||||
## 能力介绍
|
||||
|
||||
SWIFT的sample能力可以使用下面的例子进行:
|
||||
```shell
|
||||
swift sample --model LLM-Research/Meta-Llama-3.1-8B-Instruct --sampler_engine transformers --num_return_sequences 5 --dataset AI-ModelScope/alpaca-gpt4-data-zh#5
|
||||
```
|
||||
在当前文件夹的`sample_output`目录下,会生成以时间戳为文件名的jsonl文件,该文件应该包含25行,每一行都是一个完整`messages`格式的数据。
|
||||
|
||||
采样的参数列表请参考[这里](Command-line-parameters.md)。
|
||||
|
||||
## 环境准备
|
||||
|
||||
```shell
|
||||
pip install ms-swift[llm] -U
|
||||
```
|
||||
|
||||
或从源代码安装:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/modelscope/ms-swift.git
|
||||
cd ms-swift
|
||||
pip install -e '.[llm]'
|
||||
```
|
||||
|
||||
## 使用PRM和ORM进行结果过滤
|
||||
|
||||
采样重要的能力就是对过程和结果进行监督,这可以通过设置额外参数来支持。
|
||||
|
||||
```shell
|
||||
swift sample --model LLM-Research/Meta-Llama-3.1-8B-Instruct --sampler_engine lmdeploy --num_return_sequences 5 --n_best_to_keep 2 --dataset tastelikefeet/competition_math#5 --prm_model AI-ModelScope/GRM-llama3.2-3B-rewardmodel-ft --orm_model math
|
||||
```
|
||||
|
||||
在当前文件夹的`sample_output`目录下,会生成以时间戳为文件名的jsonl文件,该文件**至多包含**10行,每一行都是一个完整`messages`格式的数据。
|
||||
> 之所以至多包含10行,是因为虽然设置了共处理5个数据,每个数据保留2个(n_best_to_keep),但是orm可能会校验失败,失败数据不会保留到文件中。
|
||||
> 另外,增加了--prm_model或--orm_model后文件格式有所不同,包含了rejected_response key,内容来自于prm评分最低的行。
|
||||
|
||||
## 自定义PRM或ORM
|
||||
|
||||
PRM和ORM的自定义可以在plugin中按照现有代码增加一个新的实现。例如:
|
||||
```python
|
||||
class CustomPRM:
|
||||
|
||||
# 构造需要是无参的
|
||||
def __init__(self):
|
||||
# init here
|
||||
pass
|
||||
|
||||
def __call__(self, infer_requests: List[InferRequest], ground_truths: List[str], **kwargs) -> List[Union[float, List[float]]]:
|
||||
...
|
||||
|
||||
|
||||
prms = {'custom': CustomPRM}
|
||||
```
|
||||
|
||||
之后在命令行中使用`--prm_model custom`即可。
|
||||
|
||||
## 显存控制
|
||||
|
||||
如果被采样模型和PRM共同加载进显存,则可能出现OOM的问题。因此采样可以分为两段进行:
|
||||
|
||||
- 第一段指定`--model`和``--sampler_engine`,同时不指定`--orm_model`和`--prm_model`,仅进行采样,并存储为文件
|
||||
- 第二段指定`--sampler_engine no`,指定`--orm_model`和`--prm_model`,并同时指定`--cache_files`,仅进行RM数据过滤,不重新采样
|
||||
|
||||
通过两段方式可以每次仅加载一个模型,防止OOM。
|
||||
|
||||
## 实际例子
|
||||
|
||||
请参考[强化微调脚本](https://github.com/modelscope/ms-swift/tree/main/examples/train/rft/rft.py)。该脚本给出了使用采样进行强化微调的实际例子。
|
||||
|
||||
> 注意:该脚本的实际效果和模型、数据、RM的质量强相关,因此仅作为样例出现,用户请自行修改该脚本并训练自己的RM和generator模型。
|
||||
|
||||
## 大模型蒸馏采样
|
||||
|
||||
SWIFT的sample支持使用OpenAI API的方式,用大模型蒸馏数据,如下示例:
|
||||
```shell
|
||||
OPENAI_API_KEY="your_api_key" \
|
||||
swift sample \
|
||||
--sampler_type distill \
|
||||
--sampler_engine client \
|
||||
--model deepseek-r1 \
|
||||
--stream true \
|
||||
--dataset tastelikefeet/competition_math#5 \
|
||||
--num_return_sequences 1 \
|
||||
--temperature 0.6 \
|
||||
--top_p 0.95 \
|
||||
--engine_kwargs '{"base_url":"https://dashscope.aliyuncs.com/compatible-mode/v1"}'
|
||||
```
|
||||
在以上示例中,base_url和model分别是api地址和模型名称,stream表示发起请求的stream参数。
|
||||
|
||||
注意,对于Deepseek-R1系列模型,输出会被格式化为:`<think>{reasoning_content}</think>\n\n<answer>{content}</answer>`。
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,133 @@
|
||||
# 使用Tuners
|
||||
|
||||
tuner是指附加在模型上的额外结构部分,用于减少训练参数量或者提高训练精度。目前SWIFT支持的tuners有:
|
||||
|
||||
- LoRA: [LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS](https://arxiv.org/abs/2106.09685)
|
||||
- LoRA+: [LoRA+: Efficient Low Rank Adaptation of Large Models](https://arxiv.org/pdf/2402.12354.pdf)
|
||||
- LLaMA PRO: [LLAMA PRO: Progressive LLaMA with Block Expansion](https://arxiv.org/pdf/2401.02415.pdf)
|
||||
- GaLore/Q-GaLore: [GaLore: Memory-Efficient LLM Training by Gradient Low-Rank Projection](https://arxiv.org/abs/2403.03507)
|
||||
- Liger Kernel: [Liger Kernel: Efficient Triton Kernels for LLM Training](https://arxiv.org/abs/2410.10989)
|
||||
- LISA: [LISA: Layerwise Importance Sampling for Memory-Efficient Large Language Model Fine-Tuning](https://arxiv.org/abs/2403.17919)
|
||||
- UnSloth: https://github.com/unslothai/unsloth
|
||||
- SCEdit: [SCEdit: Efficient and Controllable Image Diffusion Generation via Skip Connection Editing](https://arxiv.org/abs/2312.11392) < [arXiv](https://arxiv.org/abs/2312.11392) | [Project Page](https://scedit.github.io/) >
|
||||
- NEFTune: [Noisy Embeddings Improve Instruction Finetuning](https://arxiv.org/abs/2310.05914)
|
||||
- LongLoRA: [Efficient Fine-tuning of Long-Context Large Language Models](https://arxiv.org/abs/2309.12307)
|
||||
- Adapter: [Parameter-Efficient Transfer Learning for NLP](http://arxiv.org/abs/1902.00751)
|
||||
- Vision Prompt Tuning: [Visual Prompt Tuning](https://arxiv.org/abs/2203.12119)
|
||||
- Side: [Side-Tuning: A Baseline for Network Adaptation via Additive Side Networks](https://arxiv.org/abs/1912.13503)
|
||||
- Res-Tuning: [Res-Tuning: A Flexible and Efficient Tuning Paradigm via Unbinding Tuner from Backbone](https://arxiv.org/abs/2310.19859) < [arXiv](https://arxiv.org/abs/2310.19859) | [Project Page](https://res-tuning.github.io/) >
|
||||
- [PEFT](https://github.com/huggingface/peft)提供的tuners, 如AdaLoRA、DoRA、Fourierft等
|
||||
|
||||
## 接口列表
|
||||
|
||||
### Swift类静态接口
|
||||
|
||||
- `Swift.prepare_model(model, config, **kwargs)`
|
||||
- 接口作用:加载某个tuner到模型上,如果是PeftConfig的子类,则使用Peft库的对应接口加载tuner。在使用SwiftConfig的情况下,本接口可以传入SwiftModel实例并重复调用,此时和config传入字典的效果相同。
|
||||
- 本接口支持并行加载不同类型的多个tuners共同使用
|
||||
- 参数:
|
||||
- `model`: `torch.nn.Module`或`SwiftModel`的实例,被加载的模型
|
||||
- `config`: `SwiftConfig`、`PeftConfig`的实例,或者一个自定义tuner名称对config的字典
|
||||
- 返回值:`SwiftModel`或`PeftModel`的实例
|
||||
- `Swift.merge_and_unload(model)`
|
||||
- 接口作用:将LoRA weights合并回原模型,并将LoRA部分完全卸载
|
||||
- 参数:
|
||||
- model: `SwiftModel`或`PeftModel`的实例,已加载LoRA的模型实例
|
||||
- 返回值:None
|
||||
|
||||
- `Swift.merge(model)`
|
||||
|
||||
- 接口作用:将LoRA weights合并回原模型,不卸载LoRA部分
|
||||
|
||||
- 参数:
|
||||
- model: `SwiftModel`或`PeftModel`的实例,已加载LoRA的模型实例
|
||||
|
||||
- 返回值:None
|
||||
|
||||
- `Swift.unmerge(model)`
|
||||
|
||||
- 接口作用:将LoRA weights从原模型weights中拆分回LoRA结构
|
||||
|
||||
- 参数:
|
||||
- model: `SwiftModel`或`PeftModel`的实例,已加载LoRA的模型实例
|
||||
|
||||
- 返回值:None
|
||||
|
||||
- `Swift.save_to_peft_format(ckpt_dir, output_dir)`
|
||||
|
||||
- 接口作用:将存储的LoRA checkpoint转换为Peft兼容的格式。主要改变有:
|
||||
|
||||
- `default`会从对应的`default`文件夹中拆分到output_dir根目录中
|
||||
- weights中的`{tuner_name}.`字段会被移除,如`model.layer.0.self.in_proj.lora_A.default.weight`会变为`model.layer.0.self.in_proj.lora_A.weight`
|
||||
- weights中的key会增加`basemodel.model`前缀
|
||||
|
||||
- 注意:只有LoRA可以被转换,其他类型tuner由于Peft本身不支持,因此会报转换错误。此外,由于LoRAConfig中存在额外参数,如`dtype`,因此在这些参数有设定的情况下,不支持转换为Peft格式,此时可以手动删除adapter_config.json中的对应字段
|
||||
|
||||
- 参数:
|
||||
|
||||
- ckpt_dir:原weights目录
|
||||
- output_dir:目标weights目录
|
||||
|
||||
- 返回值:None
|
||||
|
||||
- `Swift.from_pretrained(model, model_id, adapter_name, revision, **kwargs)`
|
||||
- 接口作用:从存储的weights目录中加载起tuner到模型上,如果adapter_name不传,则会将model_id目录下所有的tuners都加载起来。同`prepare_model`相同,本接口可以重复调用
|
||||
- 参数:
|
||||
- model:`torch.nn.Module`或`SwiftModel`的实例,被加载的模型
|
||||
- model_id:`str`类型,待加载的tuner checkpoint, 可以是魔搭hub的id,或者训练产出的本地目录
|
||||
- adapter_name:`str`或`List[str]`或`Dict[str, str]`类型或`None`,待加载tuner目录中的tuner名称,如果为`None`则加载所有名称的tuners,如果是`str`或`List[str]`则只加载某些具体的tuner,如果是`Dict`,则将`key`指代的tuner加载起来后换成`value`的名字
|
||||
- revision: 如果model_id是魔搭的id,则revision可以指定对应版本号
|
||||
|
||||
### SwiftModel接口
|
||||
|
||||
下面列出用户可能调用的接口列表,其他内部接口或不推荐使用的接口可以通过`make docs`命令查看API Doc文档。
|
||||
|
||||
- `SwiftModel.create_optimizer_param_groups(self, **defaults)`
|
||||
- 接口作用:根据加载的tuners创建parameter groups,目前仅对`LoRA+`算法有作用
|
||||
- 参数:
|
||||
- defaults:`optimizer_groups`的默认参数,如`lr`和`weight_decay`
|
||||
- 返回值:
|
||||
- 创建的`optimizer_groups`
|
||||
|
||||
- `SwiftModel.add_weighted_adapter(self, ...)`
|
||||
- 接口作用:将已有的LoRA tuners合并为一个
|
||||
- 参数:
|
||||
- 本接口是PeftModel.add_weighted_adapter的透传,参数可以参考:[add_weighted_adapter文档](https://huggingface.co/docs/peft/main/en/package_reference/lora#peft.LoraModel.add_weighted_adapter)
|
||||
|
||||
- `SwiftModel.save_pretrained(self, save_directory, safe_serialization, adapter_name)`
|
||||
- 接口作用:存储tuner weights
|
||||
- 参数:
|
||||
- save_directory:存储目录
|
||||
- safe_serialization: 是否使用safe_tensors,默认为False
|
||||
- adapter_name:存储的adapter tuner,如果不传则默认存储所有的tuners
|
||||
- `SwiftModel.set_active_adapters(self, adapter_names, offload=None)`
|
||||
- 接口作用:设置当前激活的adapters,不在列表中的adapters会被失活
|
||||
- 在`推理`时支持环境变量`USE_UNIQUE_THREAD=0/1`,默认值`1`,如果为`0`则set_active_adapters只对当前线程生效,此时默认使用本线程激活的tuners,不同线程tuners互不干扰
|
||||
- 参数:
|
||||
- adapter_names:激活的tuners
|
||||
- offload:失活的adapters如何处理,默认为`None`代表留在显存中,同时支持`cpu`和`meta`,代表offload到cpu和meta设备中以减轻显存消耗,在`USE_UNIQUE_THREAD=0`时offload不要传值以免影响其他线程
|
||||
- 返回值:None
|
||||
- `SwiftModel.activate_adapter(self, adapter_name)`
|
||||
- 接口作用:激活一个tuner
|
||||
- 在`推理`时支持环境变量`USE_UNIQUE_THREAD=0/1`,默认值`1`,如果为`0`则activate_adapter只对当前线程生效,此时默认使用本线程激活的tuners,不同线程tuners互不干扰
|
||||
- 参数:
|
||||
- adapter_name:待激活的tuner名字
|
||||
- 返回值:None
|
||||
- `SwiftModel.deactivate_adapter(self, adapter_name, offload)`
|
||||
- 接口作用:失活一个tuner
|
||||
- 在`推理`时环境变量`USE_UNIQUE_THREAD=0`时不要调用本接口
|
||||
- 参数:
|
||||
- adapter_name:待失活的tuner名字
|
||||
- offload:失活的adapters如何处理,默认为`None`代表留在显存中,同时支持`cpu`和`meta`,代表offload到cpu和meta设备中以减轻显存消耗
|
||||
- 返回值:None
|
||||
|
||||
- `SwiftModel.get_trainable_parameters(self)`
|
||||
|
||||
- 接口作用:返回训练参数信息
|
||||
|
||||
- 参数:无
|
||||
|
||||
- 返回值:训练参数信息,格式如下:
|
||||
```text
|
||||
trainable params: 100M || all params: 1000M || trainable%: 10.00% || cuda memory: 10GiB.
|
||||
```
|
||||
@@ -0,0 +1,191 @@
|
||||
# Ascend NPU
|
||||
|
||||
关于Megatron-SWIFT在Ascend NPU上的环境准备,请参考[NPU最佳实践](../BestPractices/NPU-support.md)。
|
||||
|
||||
## NPU 性能数据采集
|
||||
|
||||
NPU性能采集通过`torch_npu.profiler.profile`接口进行采集,创建torch_npu.profiler.profile对象,通过start和stop接口控制采集性能数据,采集过程需要修改ms-swift源码,修改swift/megatron/trainers/base.py文件中的train函数,采集示例如下:
|
||||
```python
|
||||
import torch_npu
|
||||
...
|
||||
|
||||
experimental_config = torch_npu.profiler._ExperimentalConfig(
|
||||
profiler_level=torch_npu.profiler.ProfilerLevel.Level1,
|
||||
aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization,
|
||||
)
|
||||
|
||||
prof = torch_npu.profiler.profile(
|
||||
activities=[
|
||||
torch_npu.profiler.ProfilerActivity.CPU,
|
||||
torch_npu.profiler.ProfilerActivity.NPU
|
||||
],
|
||||
schedule=torch_npu.profiler.schedule(wait=0, warmup=0, active=1, repeat=1, skip_first=6),
|
||||
on_trace_ready=torch_npu.profiler.tensorboard_trace_handler("./result"),
|
||||
profile_memory=False, # 关闭采集内存信息
|
||||
with_stack=False, # 关闭采集堆栈信息
|
||||
experimental_config=experimental_config)
|
||||
prof.start()
|
||||
# ms-swift 逻辑
|
||||
while state.iteration < args.train_iters:
|
||||
...
|
||||
metric, grad_norm, update_successful = train_step(train_data_iterator)
|
||||
# 性能数据采集
|
||||
prof.step()
|
||||
...
|
||||
prof.stop()
|
||||
```
|
||||
|
||||
## NPU 精度数据采集
|
||||
### 安装msprobe
|
||||
```shell
|
||||
pip install mindstudio-probe
|
||||
```
|
||||
|
||||
### 代码修改
|
||||
为了支持 msprobe 工具进行精度调试,我们需要修改 `swift/megatron/model/mm_gpt_model.py` 文件中的 `_patch_word_embeddings` 函数。主要改动是调整函数参数和内部实现逻辑,使其能够正确地对嵌入层进行patch
|
||||
|
||||
下面是具体的修改内容:
|
||||
|
||||
修改前:
|
||||
```python
|
||||
def _patch_word_embeddings(self, kwargs):
|
||||
origin_forward = VocabParallelEmbedding.forward
|
||||
|
||||
def forward(_self, input_):
|
||||
args = get_args()
|
||||
reduce_scatter_embeddings = _self.reduce_scatter_embeddings
|
||||
_self.reduce_scatter_embeddings = False
|
||||
input_ = torch.masked_fill(input_, input_ < 0, 0)
|
||||
res = origin_forward(_self, input_)
|
||||
_self.reduce_scatter_embeddings = reduce_scatter_embeddings
|
||||
packed_seq_params = kwargs.get('packed_seq_params')
|
||||
# ...其他逻辑...
|
||||
return res
|
||||
VocabParallelEmbedding.forward = forward
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
VocabParallelEmbedding.forward = origin_forward
|
||||
|
||||
def forward(
|
||||
self,
|
||||
input_ids: torch.Tensor,
|
||||
position_ids: torch.Tensor,
|
||||
attention_mask: torch.Tensor = None,
|
||||
decoder_input: torch.Tensor = None,
|
||||
labels: torch.Tensor = None,
|
||||
inference_params: InferenceParams = None,
|
||||
packed_seq_params: PackedSeqParams = None,
|
||||
**kwargs,
|
||||
) -> torch.Tensor:
|
||||
if decoder_input is not None:
|
||||
pass
|
||||
elif self.pre_process:
|
||||
kwargs.update({'input_ids': input_ids, 'packed_seq_params': packed_seq_params})
|
||||
with self._patch_word_embeddings(kwargs):
|
||||
decoder_input = self.language_model.embedding(input_ids=input_ids, position_ids=position_ids)
|
||||
|
||||
# ...其他逻辑...
|
||||
```
|
||||
|
||||
修改后:
|
||||
```python
|
||||
def _patch_word_embeddings(self, kwargs, emb): # 修改1
|
||||
origin_forward = emb.word_embeddings.forward # 修改2
|
||||
|
||||
def forward(input_): # 修改3
|
||||
args = get_args()
|
||||
_self = emb.word_embeddings # 修改4
|
||||
reduce_scatter_embeddings = _self.reduce_scatter_embeddings
|
||||
_self.reduce_scatter_embeddings = False
|
||||
input_ = torch.masked_fill(input_, input_ < 0, 0)
|
||||
res = origin_forward(input_) # 修改5
|
||||
_self.reduce_scatter_embeddings = reduce_scatter_embeddings
|
||||
packed_seq_params = kwargs.get('packed_seq_params')
|
||||
# ...其他逻辑...
|
||||
return res
|
||||
|
||||
emb.word_embeddings.forward = forward # 修改6
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
emb.word_embeddings.forward = origin_forward # 修改7
|
||||
|
||||
def forward(
|
||||
self,
|
||||
input_ids: torch.Tensor,
|
||||
position_ids: torch.Tensor,
|
||||
attention_mask: torch.Tensor = None,
|
||||
decoder_input: torch.Tensor = None,
|
||||
labels: torch.Tensor = None,
|
||||
inference_params: InferenceParams = None,
|
||||
packed_seq_params: PackedSeqParams = None,
|
||||
**kwargs,
|
||||
) -> torch.Tensor:
|
||||
if decoder_input is not None:
|
||||
pass
|
||||
elif self.pre_process:
|
||||
kwargs.update({'input_ids': input_ids, 'packed_seq_params': packed_seq_params})
|
||||
with self._patch_word_embeddings(kwargs, self.language_model.embedding): # 修改8
|
||||
decoder_input = self.language_model.embedding(input_ids=input_ids, position_ids=position_ids)
|
||||
|
||||
# ...其他逻辑...
|
||||
```
|
||||
|
||||
主要变化包括:
|
||||
1. `_patch_word_embeddings` 方法增加了 `emb` 参数,用于接收 embedding 模块实例
|
||||
2. 直接获取 `emb.word_embeddings.forward` 而不是 `VocabParallelEmbedding.forward`
|
||||
3. 内部 `forward` 函数签名从 `(_self, input_)` 改为 `(input_)`
|
||||
4. 在函数内部通过 `emb.word_embeddings` 获取 `_self`
|
||||
5. 调用原始 forward 时直接传入 `input_`
|
||||
6. 使用 `emb.word_embeddings.forward` 进行替换和恢复操作(修改6、7)
|
||||
7. 在调用 `_patch_word_embeddings` 时传入 `self.language_model.embedding` 实例
|
||||
|
||||
对文件swift/megatron/trainers/base.py中的train_step函数进行修改
|
||||
修改前:
|
||||
```python
|
||||
def train_step(self, forward_step_func, data_iterator, model, optimizer, opt_param_scheduler, config, *args,
|
||||
**kwargs):
|
||||
new_data_iterator = self._replace_data_iterator(data_iterator, model)
|
||||
return self._origin_train_step(forward_step_func, new_data_iterator, model, optimizer, opt_param_scheduler,
|
||||
config, *args, **kwargs)
|
||||
|
||||
```
|
||||
修改后:
|
||||
```python
|
||||
def train_step(self, forward_step_func, data_iterator, model, optimizer, opt_param_scheduler, config, *args,
|
||||
**kwargs):
|
||||
new_data_iterator = self._replace_data_iterator(data_iterator, model)
|
||||
from msprobe.pytorch import PrecisionDebugger
|
||||
debugger = PrecisionDebugger(dump_path='./dump_path', level='mix', model=model)
|
||||
debugger.start()
|
||||
try:
|
||||
origin_train_step_out = self._origin_train_step(
|
||||
forward_step_func, new_data_iterator, model, optimizer, opt_param_scheduler,config, *args, **kwargs)
|
||||
finally:
|
||||
debugger.stop()
|
||||
debugger.step()
|
||||
return origin_train_step_out
|
||||
```
|
||||
|
||||
|
||||
### 使能
|
||||
|
||||
另外,由于msprobe不支持融合计算,需要在启动脚本添加`--bias_dropout_fusion false`、`--bias_swiglu_fusion false`、`--cross_entropy_loss_fusion false`
|
||||
|
||||
#### 示例
|
||||
```shell
|
||||
PYTORCH_NPU_ALLOC_CONF='expandable_segments:True' \
|
||||
NPROC_PER_NODE=2 \
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
megatron sft \
|
||||
--mcore_model Qwen2.5-7B-Instruct-mcore \
|
||||
--dataset 'AI-ModelScope/alpaca-gpt4-data-zh#500' \
|
||||
'AI-ModelScope/alpaca-gpt4-data-en#500' \
|
||||
'swift/self-cognition#500' \
|
||||
--tensor_model_parallel_size 2 \
|
||||
...
|
||||
--bias_dropout_fusion false \
|
||||
--bias_swiglu_fusion false \
|
||||
--cross_entropy_loss_fusion false
|
||||
```
|
||||
@@ -0,0 +1,439 @@
|
||||
# 命令行参数
|
||||
|
||||
## Megatron参数
|
||||
|
||||
**训练参数**:
|
||||
- 🔥micro_batch_size: 每个DP组的批次大小,默认为1。
|
||||
- 🔥global_batch_size: 总批次大小,等价于`micro_batch_size*数据并行大小*梯度累加步数`。默认为16。
|
||||
- 其中,`数据并行大小 (DP) = 总GPU数 / (TP × PP × CP)`。
|
||||
- 🔥recompute_granularity: 重新计算激活的粒度,可选项为'full', 'selective' and 'none'。其中full代表重新计算整个transformer layer,selective代表只计算transformer layer中的核心注意力部分。通常'selective'是推荐的。默认为'selective'。
|
||||
- 当你设置为'selective'时,你可以通过指定`--recompute_modules`来选择对哪些部分进行重新计算。
|
||||
- 🔥recompute_method: 该参数需将recompute_granularity设置为'full'才生效,可选项为'uniform', 'block'。默认为None。
|
||||
- 🔥recompute_num_layers: 该参数需将recompute_granularity设置为'full'才生效,默认为None。若`recompute_method`设置为uniform,该参数含义为每个均匀划分的重新计算单元的transformer layers数量。例如你可以指定为`--recompute_granularity full --recompute_method uniform --recompute_num_layers 4`。recompute_num_layers越大,显存占用越小,计算成本越大。注意:当前进程中的模型层数需能被`recompute_num_layers`整除。默认为None。
|
||||
- 🔥recompute_modules: 选项包括"core_attn", "moe_act", "layernorm", "mla_up_proj", "mlp", "moe",默认值为`["core_attn"]`。该参数在`--recompute_granularity selective`时生效。例如在MoE训练时,你可以通过指定`--recompute_granularity selective --recompute_modules core_attn moe`降低显存。其中"core_attn"、"mlp" 和 "moe" 使用常规检查点,"moe_act"、"layernorm" 和 "mla_up_proj" 使用输出丢弃检查点。
|
||||
- "core_attn": 重新计算 Transformer 层中的核心注意力部分。
|
||||
- "mlp": 重新计算密集的 MLP 层。
|
||||
- "moe": 重新计算 MoE 层。
|
||||
- "moe_act": 重新计算 MoE 中的 MLP 激活函数部分。
|
||||
- "layernorm": 重新计算 input_layernorm 和 pre_mlp_layernorm。
|
||||
- "mla_up_proj": 重新计算 MLA 上投影和 RoPE 应用部分。
|
||||
- 🔥train_iters: 训练的总迭代次数,默认为None。
|
||||
- 提示:你可以通过设置`--num_train_epochs`来设置训练的epochs数。在使用非流式数据集时,会自动根据数据集数量计算`train_iters`(兼容packing)。
|
||||
- 🔥num_train_epochs: 指定训练的epochs数。当使用非流式数据集时,该参数会为你自动计算train_iters而不需要手动传入`train_iters`。当使用流式数据集时,该参数会在训练到`num_train_epochs`时强制退出训练,并对权重进行验证和保存。默认为None。
|
||||
- masked_softmax_fusion: 默认为True。用于开启query_key_value的scaling, masking, and softmax融合。
|
||||
- bias_dropout_fusion: 默认为True。用于开启bias和dropout的融合。
|
||||
- bias_activation_fusion: 如果为True,则在可能的情况下融合偏置加法和激活函数。默认为True。
|
||||
- apply_rope_fusion: 默认为False。用于开启rope融合。该参数为megatron-core参数透传。注意:并不是所有情况都支持rope融合,例如:MLA、mrope等不支持。
|
||||
- gradient_accumulation_fusion: 默认为True。用于开启梯度累加融合。
|
||||
- 🔥cross_entropy_loss_fusion: 启动交叉熵损失计算融合。默认为True。
|
||||
- cross_entropy_fusion_impl: 交叉熵损失融合的实现。可选为'native'和'te'。默认为'native'。
|
||||
- **"ms-swift>=4.3.1"默认值从"te"修改为"native"**,原因查看[这个PR](https://github.com/NVIDIA/Megatron-LM/pull/5115),这可能会导致更多的显存占用。
|
||||
- calculate_per_token_loss: 根据全局批次中的非填充token数量来对交叉熵损失进行缩放。默认为None,`task_type`为'causal_lm'且为预训练/微调时,默认为True,否则默认为False。
|
||||
- 🔥attention_backend: 使用的注意力后端 (flash、fused、unfused、local、auto、flash_2、flash_3、flash_4)。默认为 flash。
|
||||
- 如果安装'flash_attention_4/3',`--attention_backend flash`则优先使用fa4/fa3。如果要显式设置,你可以设置`--attention_backend flash_2/3/4`。训练脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/train/flash_attention_3)。多模态模型的vit部分要使用flash_attention_4/3,请设置`--attn_impl flash_attention_4/3`。
|
||||
- 有些模型可能不支持flash,你需要手动设置`--attention_backend unfused/fused --padding_free false`,例如:Llama4、GPT-OSS。
|
||||
- optimizer: 优化器类型,可选为'adam'、'sgd'、'muon'和'dist_muon'。默认为adam。
|
||||
- 注意:此'adam'为'adamw',参考[这里](https://github.com/NVIDIA/TransformerEngine/blob/d8f1e68f7c414f3e7985a8b41de4443b2f819af3/transformer_engine/pytorch/optimizers/fused_adam.py#L69-L70)。
|
||||
- 其中'muon'和'dist_muon'需要"megatron-core>=0.16"。
|
||||
- 🔥optimizer_cpu_offload: 将优化器状态卸载到 CPU,例如设置:`--use_precision_aware_optimizer true --optimizer_cpu_offload true --optimizer_offload_fraction 0.7`。默认为False。
|
||||
- 该参数可以显著降低显存占用(但增加内存占用)。若global_batch_size较大,则对训练速度的影响不大。
|
||||
- 🔥optimizer_offload_fraction: 卸载到 CPU 的优化器状态所占比例。默认为1.。
|
||||
- optimizer_cuda_graph: 是否为优化器步骤启用 CUDA 计算图,传递给 Megatron 的 `--optimizer-cuda-graph`。默认为False。该参数需要"megatron-core>=0.17"。
|
||||
- use_precision_aware_optimizer: 使用 TransformerEngine 中的精度感知优化器,该优化器允许将主参数和优化器状态设置为较低精度,例如 fp16 和 fp8。
|
||||
- main_grads_dtype: 启用 use_precision_aware_optimizer 时主梯度的 dtype。可选为'fp32', 'bf16'。默认为'fp32'。
|
||||
- main_params_dtype: 启用 use_precision_aware_optimizer 时主参数的 dtype。可选为'fp32', 'fp16'。默认为'fp32'。
|
||||
- exp_avg_dtype: 启用 use_precision_aware_optimizer 时,adam 优化器中 exp_avg(即一阶矩)的 dtype。该 dtype 用于在训练过程中将优化器状态存储在内存中,但不会影响内核计算时的精度。可选为'fp32', 'fp16', 'bf16', 'fp8'。默认为'fp32'。
|
||||
- exp_avg_sq_dtype: 启用 use_precision_aware_optimizer 时,adam 优化器中 exp_avg_sq(即二阶矩)的 dtype。该 dtype 用于在训练过程中将优化器状态存储在内存中,但不会影响内核计算的精度。可选为'fp32', 'fp16', 'bf16', 'fp8'。默认为'fp32'。
|
||||
- manual_gc: 禁用默认垃圾回收器,手动触发垃圾回收。默认为False。
|
||||
- manual_gc_steps: 手动触发垃圾回收的间隔(steps)。默认为0。
|
||||
- manual_gc_eval: 当使用手动垃圾回收时(`--manual_gc true`),在每次评估运行的开始和结束时禁用垃圾回收。默认为True。
|
||||
|
||||
**数据参数**:
|
||||
- seed: python、numpy、pytorch和cuda的随机种子,默认为42。
|
||||
- dataset_shuffle: 是否对dataset进行随机操作。默认为True。
|
||||
- 注意:**Megatron-SWIFT的随机包括两个部分**:数据集的随机,由`dataset_shuffle`控制;train_dataloader中的随机,由`train_dataloader_shuffle`控制。
|
||||
- train_dataloader_shuffle: 是否对train_dataloader使用随机,默认为True。val_dataset不进行随机操作。
|
||||
- 🔥dataloader_num_workers: dataloader的workers数量,默认为4。
|
||||
- 注意:若设置`--streaming true`,则设置为1。
|
||||
- dataloader_pin_memory: 默认为True。
|
||||
- dataloader_persistent_workers: 默认为False。
|
||||
- 注意:该参数在"ms-swift<4.3"默认为True,为避免内存泄漏问题修改为False。
|
||||
- dataloader_prefetch_factor: 默认为2。
|
||||
- data_sharding: 当`--train_dataloader_shuffle true`时对 train_dataloader 生效,默认为False。该参数控制数据集随机的范围。若设置为True,则先对数据集进行分片,然后对每个分片进行随机处理(略节约内存);若设置为False,则先对数据集进行随机,再进行分片(更好的随机效果)。
|
||||
- 🔥group_by_length: 是否在训练数据集中将长度大致相同的样本分组在一起(有随机因素),以最小化填充并确保各节点与进程的负载均衡以提高效率。默认为False。具体算法参考`transformers.trainer_pt_utils.get_length_grouped_indices`。
|
||||
- te_rng_tracker: 使用 Transformer Engine 版本的随机数生成器。默认为False。
|
||||
- data_parallel_random_init: 在数据并行的各个 rank 之间启用不同的随机初始化。默认为False。
|
||||
- padding_free: 将一个batch中的数据进行展平而避免数据padding,从而降低显存占用并加快训练。默认为True。
|
||||
- 若要自定义attention_mask,你可以设置`--padding_free false`。
|
||||
- 注意:**Megatron-SWIFT训练特性优先支持padding_free格式**,若非特殊情况,请勿修改该值。
|
||||
- mlp_padding_free: 默认为False。用于padding_free设置为false时,对mlp进行padding_free优化。这可以在自定义attention_mask的同时,提升训练速度和减少显存占用。(暂时与CP不兼容)
|
||||
|
||||
|
||||
**学习率参数**:
|
||||
- lr_warmup_init: 学习率warmup的初始值。学习率调度器从这个值开始进行预热。默认为0。
|
||||
- 🔥lr: 初始学习率,最终会根据学习率预热策略和衰减策略决定每个迭代的学习率。默认为None,**全参数训练默认为1e-5,LoRA训练默认为1e-4**。
|
||||
- lr_decay_style: 学习率衰减策略,默认为'cosine'。通常设置为'constant', 'linear', 'cosine', 'inverse-square-root', 'WSD'。
|
||||
- 🔥lr_decay_iters: 学习率衰减的迭代次数。默认为None,则设置为`--train_iters`。
|
||||
- lr_warmup_iters: 线性学习率预热的迭代次数,默认为0。
|
||||
- 🔥lr_warmup_fraction: 线性学习率预热阶段所占比例,默认为None。
|
||||
- 🔥min_lr: 学习率的最小值,将低于该阈值的学习率裁剪为该值,默认为0。
|
||||
- 注意:`vit_lr`的`min_lr`为`vit_lr * min_lr / lr`,`aligner_lr`的`min_lr同理。
|
||||
- lr_wsd_decay_style: WSD 退火阶段的衰减方式。默认为'exponential'。
|
||||
- lr_wsd_decay_iters: 学习率衰减的迭代次数。默认为 None。
|
||||
|
||||
**正则化参数**:
|
||||
- 🔥weight_decay: 默认为0.1。
|
||||
- weight_decay_incr_style: 权重衰减的递增函数。可选为'constant', 'linear', 'cosine'。默认为'constant'。
|
||||
- start_weight_decay: L2 正则化的初始权重衰减系数。
|
||||
- end_weight_decay: 训练结束时 L2 正则化的权重衰减系数。
|
||||
- 🔥clip_grad: l2梯度裁剪,默认为1.0。
|
||||
- 日志中打印的grad_norm为未裁剪前的值。
|
||||
- adam_beta1: 默认0.9。
|
||||
- adam_beta2: 默认0.95。
|
||||
- adam_eps: 默认1e-8。
|
||||
- sgd_momentum: 设置`--optimizer sgd`时生效,默认为0.9。
|
||||
|
||||
|
||||
**muon参数**:
|
||||
- muon_momentum: Muon 优化器的动量因子。默认为0.9。
|
||||
- muon_split_qkv: 是否为 Muon 优化器拆分 QKV 参数,默认为True。
|
||||
- muon_use_nesterov: 是否在内部 SGD 中使用 Nesterov 风格的动量,默认为False。
|
||||
- muon_scale_mode: Muon 优化器的缩放模式。可选为'spectral', 'unit_rms_norm', 'shape_scaling'。默认为'spectral'。
|
||||
- muon_fp32_matmul_prec: Newton-Schulz 迭代的 FP32 矩阵乘法精度,可选为'low', 'medium', 'high'。默认为'medium'。
|
||||
- muon_coefficient_type: Muon 优化器 Newton-Schulz 迭代的系数类型,传递给 Megatron 的 `--muon-coefficient-type`。可选值取决于所安装的 emerging_optimizers 版本(如'quintic', 'polar_express', 'simple', 'cans', 'aol', 'deepseekv4', 'cubic5', 'custom')。默认为'quintic'。
|
||||
- muon_num_ns_steps: Muon 优化器的 Newton-Schulz 步数。默认为5。
|
||||
- muon_tp_mode: 张量模型并行权重的 NS 计算方式。可选为'blockwise', 'duplicated', 'distributed'。默认为'blockwise'。
|
||||
- muon_extra_scale_factor: Muon 更新的额外缩放因子,默认为1。
|
||||
- muon_scalar_optimizer: 使用 Muon 时非线性参数(embeddings、biases、norms)的优化器,可选为'adam'或'lion'。默认为'adam'。
|
||||
|
||||
|
||||
**checkpoint参数**:
|
||||
- 🔥output_dir: checkpoint的输出目录,默认None。在训练中,若未设置该参数,则默认为`f'megatron_output/{model_suffix}'`,例如`'megatron_output/Qwen2.5-7B-Instruct'`。
|
||||
- 注意:**若在多机训练时,请确保每个节点的保存路径指向相同位置**,否则你需要在训练后手动集中这些权重。
|
||||
- 🔥save_steps: checkpoint保存的间隔(steps),默认为500。
|
||||
- 注意:训练结束时一定会保存权重。
|
||||
- 🔥no_save_optim: 不保存optimizer,默认为False。在全参数训练时,可以显著降低存储时间。
|
||||
- 🔥no_save_rng: 不保存rng,默认为False。
|
||||
- 🔥mcore_model: 加载的checkpoint目录(mcore存储格式),默认None。对于断点续训的介绍,请查看`--finetune false`参数的介绍。
|
||||
- megatron-swift推荐直接加载和存储safetensors权重,参考[mcore-bridge文档](./Mcore-Bridge.md)。
|
||||
- `--model`与`--mcore_model`的区别:`--model/--adapters/--ref_model/--ref_adapters`后加safetensors权重目录,`--mcore_model/--mcore_adapter/--mcore_ref_model/--mcore_ref_adapter`后加mcore权重目录。
|
||||
- `--model/--adapters`不支持加载断点续训状态,因此若设置`--no_save_optim false`,将额外存储mcore权重格式用于断点续训,你需要使用`--mcore_model/--mcore_adapter`来加载断点续训的状态。例如:`--model <hf-model> --mcore_adapter <mcore-adapter-ckpt>`。(使用`--model/--adapters`断点续训也可以,只是不读取优化器状态。)
|
||||
- 🔥no_load_optim: 不载入optimizer,默认为False。
|
||||
- 注意:断点续训时,设置`--no_load_optim false`读取优化器状态通常比`--no_load_optim true`不读取优化器状态消耗更大的显存资源。
|
||||
- 🔥no_load_rng: 不载入rng,默认为False。
|
||||
- 🔥finetune: 将模型加载并微调。**不加载检查点的优化器和随机种子状态,并将迭代数设置为0**。默认为True。
|
||||
- 注意:**断点续训**你需要设置`--mcore_model`(lora训练需要额外设置`--mcore_adapter`),若设置`--finetune true`,将不加载优化器状态和随机种子状态并将迭代数设置为0,不会进行数据集跳过;若设置`--finetune false`,将读取迭代数并跳过之前训练的数据集数量,优化器状态和随机种子状态的读取通过`--no_load_optim`和`--no_load_rng`控制。
|
||||
- 流式数据集`--streaming`,暂不支持跳过数据集。
|
||||
- perform_initialization: 对权重进行初始化,默认为False。
|
||||
- use_cpu_initialization: 在cpu上初始化权重,默认为False。在进行HF和MCore权重转换时会被使用。通常不需要修改该值。
|
||||
- 🔥async_save: 使用异步检查点保存。目前仅适用于`torch_dist`分布式检查点格式。默认为False。
|
||||
- 🔥save_total_limit: 最多保存的checkpoint数,会将过期的checkpoint进行删除。默认为None,保存所有的checkpoint。该参数需设置为`>=2`的数,若设置为2,则保存best checkpoint和last checkpoint。该参数暂不兼容`async_save`。
|
||||
- metric_for_best_model: 默认为None,GRPO默认为'reward',其他情况默认为'loss'。
|
||||
- greater_is_better: 默认为None,即当`metric_for_best_model`含'loss'时,设置为False,否则设置为True。
|
||||
- use_persistent_ckpt_worker: 为异步保存启动持久化检查点工作进程。默认为False。
|
||||
- dist_ckpt_save_pre_mcore_014: 使用 Megatron-Core 0.14 之前的格式存储。默认为False。
|
||||
- dist_ckpt_optim_fully_reshardable: 使优化器分布式检查点完全可重分片(TP/PP/EP/DP),而不是仅支持普通的DP重分片。默认为False。
|
||||
- distrib_optim_fully_reshardable_mem_efficient: 在分布式优化器检查点保存和加载过程中,通过使用Gloo(而非NCCL),并仅使用单个rank进行保存,以尽可能减少内存使用。仅在遇到主机或设备内存问题时启用,仅在设置了`--dist-ckpt-optim-fully-reshardable`标志时生效。默认为False。
|
||||
|
||||
|
||||
**分布式参数**:
|
||||
并行技术的选择请参考[训练技巧文档](Quick-start.md#训练技巧)。
|
||||
|
||||
- ddp_backend: 分布式后端,可选为'nccl', 'gloo'。默认为nccl。
|
||||
- ddp_timeout: 默认为18000000,单位为秒。
|
||||
- 🔥use_distributed_optimizer: 使用分布式优化器(即zero1)。默认为True。
|
||||
- 🔥tensor_model_parallel_size: tp数,默认为1。
|
||||
- 🔥pipeline_model_parallel_size: pp数,默认为1。
|
||||
- 🔥decoder_first_pipeline_num_layers: decoder第一个流水线阶段所包含的Transformer层数。默认为 None,表示将Transformer层数平均分配到所有流水线阶段。
|
||||
- 该参数通常用于**Transformer层数无法被PP整除**,或者多模态模型第0个pp阶段显存占用过高的情况。
|
||||
- 🔥decoder_last_pipeline_num_layers: decoder最后一个流水线阶段所包含的Transformer层数。默认为 None,表示将Transformer层数平均分配到所有流水线阶段。
|
||||
- overlap_p2p_comm: 在 1F1B 中将流水线并行通信与前向和反向块重叠,默认为True。
|
||||
- batch_p2p_comm: 使用 batch_isend_irecv 代替单独的 isend/irecv 调用。默认为None,设置为`not args.overlap_p2p_comm`。
|
||||
- 如果出现PP并行卡住的情况,可以设置为False。
|
||||
- align_param_gather: 设置为True,所有 PP 阶段将同时启动参数全收集(all-gather)操作。否则,每个 PP 阶段将根据需要独立启动。默认为True。
|
||||
- 🔥sequence_parallel: 启动序列并行优化,该参数需要设置`tensor_model_parallel_size`才生效。默认为False。
|
||||
- 🔥context_parallel_size: cp数,默认为1。
|
||||
- cp_partition_mode: THD 序列行如何在上下文并行(context-parallel)ranks 之间进行划分。可选为"zigzag", "contiguous",默认为"zigzag"。
|
||||
- sequence_packing_scheduler: 用于序列打包和动态上下文并行的调度器。可选为"dp_balanced","default_dynamic_cp"。dp_balanced:用于序列打包的 DP 均衡调度器。default_dynamic_cp:用于打包序列均衡的动态 CP 调度器。默认为None。
|
||||
- tp_comm_overlap: 启用张量并行通信与GEMM(通用矩阵乘法)内核的重叠(降低通信耗时)。默认为False。
|
||||
- 🔥overlap_grad_reduce: 启用DDP中grad reduce操作的重叠(降低DP通信耗时)。默认为False。
|
||||
- 🔥overlap_param_gather: 启用分布式优化器中参数all-gather的重叠(降低DP通信耗时)。默认为False。
|
||||
- virtual_pipeline_model_parallel_size: 每个流水线并行 rank 的虚拟流水线阶段数量。默认为None。vpp并行,用于减少pp并行的计算空泡,提高GPU利用率,但会略微提高通信量。
|
||||
- microbatch_group_size_per_vp_stage: 每个虚拟流水线阶段处理的连续微批次数量。默认为None,等于pipeline_model_parallel_size。
|
||||
- 🔥pipeline_model_parallel_layout: 一个描述自定义流水线(pp/vpp)模型并行布局的字符串。例如:`"Et*4|(tttt|)*14tmL"`。其中 E、L、t、m 分别表示嵌入层(embedding)、损失层(loss)、Transformer 解码器层和 MTP 层。阶段之间用 "|" 分隔。重复的阶段或层可以通过乘法表示。逗号仅用于提升可读性(无实际语法作用)。默认值为 None,表示不使用此参数设置布局。
|
||||
- 该参数通常在异构GPU集群上使用。
|
||||
- 🔥expert_model_parallel_size: 专家并行数,默认为1。
|
||||
- 🔥expert_tensor_parallel_size: 专家TP并行度。默认值为1。
|
||||
|
||||
|
||||
**日志参数**:
|
||||
- report_to: 启用的日志后端。默认为`['tensorboard']`。可选项为'tensorboard', 'wandb'和'swanlab'。'wandb'和'swanlab'登陆可以使用`WANDB_API_KEY`、`SWANLAB_API_KEY`环境变量。
|
||||
- 🔥logging_steps: 日志记录的间隔(steps),默认为5。
|
||||
- tensorboard_dir: tensorboard日志写入的目录。默认None,即存储在`f'{output_dir}/runs'`目录下。
|
||||
- tensorboard_queue_size: 用于暂存事件和摘要的 TensorBoard 队列大小;当队列中待处理的事件和摘要数量达到该大小时,下一次调用 "add" 相关方法会触发将数据刷新写入磁盘。默认为50。
|
||||
- wandb_project: wandb项目名称,默认为'megatron-swift'。
|
||||
- wandb_exp_name: wandb 实验名称。默认为`--output_dir`的值。
|
||||
- swanlab_project: swanlab项目名称,默认为'megatron-swift'。
|
||||
- swanlab_exp_name: swanlab 实验名称。默认为`--output_dir`的值。
|
||||
|
||||
**评估参数**:
|
||||
- 🔥eval_iters: 评估的迭代次数,默认为`-1`,根据验证数据集的数量设置合适的值。**若验证集数量少于global_batch_size,则不进行评估**。若使用流式数据集,该值需要手动设置。
|
||||
- 🔥eval_steps: 评估的间隔(steps),即每训练多少steps进行评估。默认为None,即设置为`save_steps`。
|
||||
|
||||
**fp8参数**:
|
||||
- fp8_format: 用于前向和反向传播中FP8张量的FP8格式方案。可选为'e4m3','hybrid'。默认为None。
|
||||
- fp8_recipe: 用于前向和反向传播中 FP8 张量的 FP8 算法方案。可选为'tensorwise', 'delayed', 'mxfp8', 'blockwise'。默认为'delayed'。其中blockwise fp8需要 cuda129 以上版本。
|
||||
- fp8_param_gather: 保持计算参数为 fp8(不使用任何其他中间数据类型),并在 fp8 格式下执行参数的 all-gather 操作。默认为False。
|
||||
- 提示:若想导出FP8权重格式,设置为True;否则设置为False。
|
||||
- fp8_amax_history_len: 每个张量记录 amax 历史的步数。默认为1024。
|
||||
- fp8_amax_compute_algo: 用于根据历史记录计算 amax 的算法。可选为'most_recent', 'max'。默认为'max'。
|
||||
|
||||
**fp4参数**:
|
||||
- fp4_format: 用于前向和反向传播中FP4张量的FP4格式方案,可选为'e2m1'。默认为None。
|
||||
- fp4_recipe: 若设置此参数,则通过 Transformer Engine 启用 FP4 精度。目前仅支持 'nvfp4',该选项使用适用于 Blackwell+ 架构的 NVFP4BlockScaling 方案。默认为'nvfp4'。
|
||||
- fp4_param_gather: 若设置此参数,则将参数保持为 FP4 精度以节省内存。注意并非所有参数都会被转换为 FP4,例如偏置项将保持不变。默认为False。
|
||||
|
||||
|
||||
**混合精度参数**:
|
||||
- fp16: fp16模式。默认为None,会根据模型的torch_dtype进行设置,即torch_dtype为float16或者float32则fp16设置为True。torch_dtype默认读取config.json。
|
||||
- bf16: bf16模式。默认为None,会根据模型的torch_dtype进行设置,即torch_dtype为bfloat16则bf16设置为True。
|
||||
- apply_query_key_layer_scaling: 将`Q * K^T` 缩放为 `1 / 层数`(例如:第layer_num层则除以layer_num)。这对fp16训练很有帮助。默认为None,即若使用`--fp16`,则设置为True。
|
||||
- 🔥attention_softmax_in_fp32: 在attention_mask和softmax中使用fp32进行计算。默认为True。
|
||||
- accumulate_allreduce_grads_in_fp32: 在 fp32 精度下进行梯度累积和全规约操作。如果开启`--bf16`且`main_params_dtype`为'fp32',则设置为True。否则默认设置为False。
|
||||
|
||||
**MoE参数**:
|
||||
- moe_router_load_balancing_type: 确定路由器的负载均衡策略。可选项为"aux_loss"、"seq_aux_loss"、"global_aux_loss"、"sinkhorn"、"none"。支持单/多种策略的列表(如 `--moe_router_load_balancing_type seq_aux_loss global_aux_loss`)。默认值为 None。从config.json中读取。
|
||||
- 🔥moe_router_dtype: 用于路由计算和专家输出加权平均的数据类型。可选为'none', 'fp32'、'fp64',这增强了数值稳定性,尤其是在专家数量较多时。与`moe_permute_fusion`一起使用时,性能影响可以忽略不计。默认为'fp32'。'none'代表不改变数据类型。
|
||||
- moe_token_dispatcher_type: 要使用的token分发器类型。可选选项包括 'allgather'、'alltoall'、'flex'和'alltoall_seq'。默认值为'alltoall'。
|
||||
- moe_enable_deepep: 启用 DeepEP 以实现 MoE 模型中的高效 token 调度和合并。仅在通过设置 `--moe_token_dispatcher_type flex` 使用弹性 token 调度器时有效。
|
||||
- 🔥moe_grouped_gemm: 当每个rank包含多个专家时,通过在多个流中启动多个本地 GEMM 内核,利用 TransformerEngine中的GroupedLinear提高利用率和性能。默认为True。
|
||||
- 🔥moe_permute_fusion: 在令牌分发过程中融合令牌重排操作。默认为False。
|
||||
- 🔥moe_aux_loss_coeff: 默认为0,不使用aux_loss。支持单/多个浮点数的列表 ,对应`moe_router_load_balancing_type` 中各策略。通常情况下,该值设置的越大,训练效果越差,但MoE负载越均衡,请根据实验效果,选择合适的值。
|
||||
- moe_z_loss_coeff: z-loss 的缩放系数。默认为None。
|
||||
- 🔥moe_shared_expert_overlap: 启用共享专家计算与调度器通信之间的重叠。如果不启用此选项,共享专家将在路由专家之后执行。仅在设置了`moe_shared_expert_intermediate_size`时有效。默认为False。
|
||||
- 🔥moe_expert_capacity_factor: 每个专家的容量因子,None表示不会丢弃任何token。默认为None。通过设置 `--moe_expert_capacity_factor`,超出专家容量的 token 会基于其被选中的概率被丢弃。可以**令训练负载均匀,提升训练速度**(例如设置为1或2)。
|
||||
- moe_pad_expert_input_to_capacity: 对每个专家(expert)的输入进行填充,使其长度与专家容量(expert capacity length)对齐,默认为False。该操作仅在设置了 `--moe_expert_capacity_factor` 参数后才生效。
|
||||
- moe_token_drop_policy: 可选为'probs', 'position'。默认为'probs'。
|
||||
|
||||
**DSA参数**
|
||||
- dsa_indexer_loss_coeff: DSA 索引器 KL 散度损失的系数。设置为 0 可禁用索引器损失。默认为`0.`。
|
||||
- dsa_indexer_use_sparse_loss: 是否使用稀疏 DSA 索引器损失。如果为 True,索引器损失将使用 top-k 索引进行计算。默认为False。
|
||||
- apply_dsa_kernel_fusion: 是否启用融合 DSA 稀疏注意力内核(FlashMLA + cuDNN DSA)。设为 False 将回退到未融合的 PyTorch 实现。需要安装 flash_mla 和 nvidia-cudnn-frontend >= 1.24.0。默认为False。(需使用Megatron-LM dev分支)
|
||||
|
||||
**Deepseek-V4**
|
||||
- csa_dense_mode: 是否对压缩稀疏注意力使用密集模式。若为 `True`,CSA 索引器将被禁用。默认为False。
|
||||
- use_fused_mhc: 对 mHC 操作使用 cuTile 融合内核。若为 True,将尝试用融合的 cuda.tile(cuTile)自动微分函数替换参考
|
||||
mHC 模块以在支持的 GPU 上获得更好的性能。需要安装 cuTile;若 cuTile 不可用,该标志将被静默重置为 False 并发出警告。默认为False。
|
||||
- mhc_recompute_layer_num: 每个 MHC 重计算块的层数。设置后,每 `mhc_recompute_layer_num` 层构成一个重计算块。若为 None,Transformer 块中的所有层共享单个重计算块。默认为None。
|
||||
|
||||
**MTP参数**
|
||||
- mtp_num_layers: 多token预测(MTP)层的数量。MTP将每个位置的预测范围扩展到多个未来token。此MTP实现使用D个顺序模块依次预测D个额外的token。默认为None。
|
||||
- 注意:mtp_num_layers的值,将不自动从config.json获取,需手动设置。你可以参考config.json中的`num_nextn_predict_layers`, `mtp_num_hidden_layers`字段填写该值。使用mcore-bridge时,将优先从safetensors文件中加载MTP权重,若无法找到,则进行随机初始化。
|
||||
- mtp_loss_scaling_factor: 多token预测(MTP)损失的缩放因子。我们计算所有深度上MTP损失的平均值,然后乘以该缩放因子得到总体MTP损失,它将作为一个额外的训练目标。默认为0.1。
|
||||
- mtp_decoder_input_detach: 用来控制 MTP 分支里的 decoder_input 是否停止梯度。默认为False。开启后,MTP loss 不会直接通过 decoder_input 回传到 embedding/vit,但仍会通过 hidden_states 路径更新主干。
|
||||
- mtp_shared_weights: MTP层之间共享权重,采用GLM-5使用的mtp方案。默认为False。例如你可以设置`--mtp_num_layers 3 --mtp_shared_weights true`。
|
||||
|
||||
**Tuner参数**:
|
||||
- tuner_type: 可选为'lora', 'full'和'lora_llm'。默认为'full'。
|
||||
- 其中'lora_llm'代表对llm部分进行lora,vit/aligner部分使用'full'。你可以使用`vit_lr/aligner_lr`设置各自的学习率。
|
||||
- 🔥freeze_llm: 该参数只对多模态模型生效,可用于全参数训练和LoRA训练,但会产生不同的效果。若是全参数训练,将freeze_llm设置为True会将LLM部分权重进行冻结;若是LoRA训练且`target_modules`设置为'all-linear',将freeze_llm设置为True将会取消在LLM部分添加LoRA模块。该参数默认为False。
|
||||
- 🔥freeze_vit: 该参数只对多模态模型生效,可用于全参数训练和LoRA训练,但会产生不同的效果。若是全参数训练,将freeze_vit设置为True会将vit部分权重进行冻结;若是LoRA训练且`target_modules`设置为'all-linear',将freeze_vit设置为True将会取消在vit部分添加LoRA模块。该参数默认为True。
|
||||
- 注意:**这里的vit不仅限于vision_tower, 也包括audio_tower**。若是Omni模型,若你只希望对vision_tower加LoRA,而不希望对audio_tower加LoRA,你可以修改[这里的代码](https://github.com/modelscope/ms-swift/blob/a5d4c0a2ce0658cef8332d6c0fa619a52afa26ff/swift/llm/model/model_arch.py#L544-L554)。
|
||||
- 🔥freeze_aligner: 该参数只对多模态模型生效,可用于全参数训练和LoRA训练,但会产生不同的效果。若是全参数训练,将freeze_aligner设置为True会将aligner(也称为projector)部分权重进行冻结;若是LoRA训练且`target_modules`设置为'all-linear',将freeze_aligner设置为True将会取消在aligner部分添加LoRA模块。该参数默认为True。
|
||||
|
||||
全参数训练:
|
||||
- freeze_parameters: 需要被冻结参数的前缀,默认为`[]`。
|
||||
- freeze_parameters_regex: 需要被冻结参数的正则表达式,默认为None。
|
||||
- freeze_parameters_ratio: 从下往上冻结的参数比例,默认为0。可设置为1将所有参数冻结,结合`trainable_parameters`设置可训练参数。除了设置为0/1,该参数不兼容pp并行。
|
||||
- trainable_parameters: 额外可训练参数的前缀,默认为`[]`。
|
||||
- trainable_parameters_regex: 匹配额外可训练参数的正则表达式,默认为None。
|
||||
|
||||
lora训练:
|
||||
- mcore_adapter: 加载adapter的权重路径,用于lora断点续训,默认为None。lora断点续训方式与全参数一致,请关注`--finetune`参数的含义。
|
||||
- 🔥target_modules: 指定lora模块的后缀,例如:你可以设置为`--target_modules linear_qkv linear_proj`。默认为`['all-linear']`,代表将所有的linear设置为target_modules。
|
||||
- 注意:在LLM和多模态LLM中,'all-linear'的行为有所不同。若是LLM则自动寻找除lm_head外的linear并附加tuner;**若是多模态LLM,则默认只在LLM上附加tuner,该行为可以被`freeze_llm`、`freeze_vit`、`freeze_aligner`控制**。
|
||||
- 注意:若需要将所有的router设置为target_modules, 你可以额外设置`--target_modules all-router ...`,例如:`--target_modules all-router all-linear`。
|
||||
- transformers和Megatron的Linear层后缀名称不同,在Megatron中,`linear_proj`代表`o_proj`,`linear_qkv`代表`q_proj, k_proj, v_proj`的拼接,`linear_fc1`代表`gate_proj`, `up_proj`的拼接,`linear_fc2`代表`down_proj`。
|
||||
- 🔥target_regex: 指定lora模块的regex表达式,默认为`None`。如果该值传入,则target_modules参数失效。
|
||||
- 🔥modules_to_save: 在已附加tuner后,额外指定一部分原模型模块参与训练和存储。默认为`[]`。例如设置为`--modules_to_save word_embeddings output_layer`,在LoRA训练中解开`word_embeddings`和`output_layer`层进行训练,这两部分的权重信息最终会进行保存。
|
||||
- 🔥lora_rank: 默认为`8`。
|
||||
- 🔥lora_alpha: 默认为`32`。
|
||||
- lora_dropout: 默认为`0.05`。
|
||||
- lora_bias: 默认为`'none'`,可以选择的值: 'none'、'all'。如果你要将bias全都设置为可训练,你可以设置为`'all'`。
|
||||
- use_rslora: 默认为`False`,是否使用`RS-LoRA`。
|
||||
|
||||
**Mcore-Bridge参数**
|
||||
- model: safetensors权重的model_id或者model_path。默认为None。
|
||||
- model_type: 模型类型。介绍参考[ms-swift命令行参数文档](../Instruction/Command-line-parameters.md)。
|
||||
- 🔥save_safetensors: 默认为True,是否直接保存成safetensors权重。若设置了`--no_save_optim false`则额外mcore格式权重和优化器权重(也保存在output_dir中)。断点续训时使用`--mcore_model/--mcore_adapter/--no_load_optim/--no_load_rng`参数加载mcore格式权重。
|
||||
- adapters: safetensors格式的LoRA增量权重的adapter_id或者adapter_path。默认为`[]`。
|
||||
- ref_model: ref_model safetensors权重的model_id或者model_path。采用grpo、dpo、kto算法且使用全参数训练时需要传入。默认为None,设置为`--model`。
|
||||
- ref_adapters: ref_adapters safetensors权重的adapter_id或者adapter_path的列表(目前只支持长度为1),默认为`[]`。
|
||||
- use_hf: 控制模型下载、数据集下载、模型推送使用[ModelScope](https://modelscope.cn/)还是[HuggingFace](https://huggingface.co/)。默认为False,使用ModelScope。
|
||||
- 提示:如果你想在国外访问ModelScope,可以尝试使用[ModelScope国际版](https://modelscope.ai/home),设置环境变量`MODELSCOPE_DOMAIN='www.modelscope.ai'`即可。
|
||||
- hub_token: hub token. modelscope的hub token可以查看[这里](https://modelscope.cn/my/myaccesstoken)。默认为None。
|
||||
- merge_lora: 是否存储合并后的权重。默认为None,若`save_safetensors`设置为True,该参数默认值为`True`,否则为False。即默认情况下,存储为safetensors格式时会合并LoRA;存储为torch_dist格式时,不会合并LoRA。
|
||||
- max_shard_size: safetensors格式存储文件最大大小,默认'5GB'。
|
||||
- 🔥offload_bridge: Megatron导出的用于vLLM更新HF格式权重使用CPU主存存放,以降低 GPU 显存占用。默认为 False。(在GRPO/GKD算法中生效)
|
||||
|
||||
**多模态参数**:
|
||||
- vit_gradient_checkpointing: 多模态模型训练时,是否对vit部分开启gradient_checkpointing。默认为None,即当`--freeze_vit`为`false`时开启。(**Megatron-SWIFT的vit实现使用transformers实现**)
|
||||
- vit_gradient_checkpointing_kwargs: 传入`torch.utils.checkpoint`中的参数。例如设置为`--vit_gradient_checkpointing_kwargs '{"use_reentrant": false}'`。默认为None。该参数只对`vit_gradient_checkpointing`生效。
|
||||
- vit_attn_impl: 多模态模型训练时,设置vit部分的attn_impl实现。默认为'flash_attn'。
|
||||
- vit_lr: 当训练多模态大模型时,该参数指定vit的学习率,默认为None,等于learning_rate。通常与`--freeze_vit`、`--freeze_aligner`参数结合使用。
|
||||
- 提示:在日志中打印的"learning rate"为llm的学习率。
|
||||
- aligner_lr: 当训练多模态大模型时,该参数指定aligner的学习率,默认为None,等于learning_rate。
|
||||
|
||||
|
||||
**其他参数**:
|
||||
- megatron_extra_kwargs: 透传入Megatron的其他参数(透传入[mcore-bridge](https://github.com/modelscope/mcore-bridge/blob/78cb9be33ebad69a0d940a2bc4e198f866084b70/src/mcore_bridge/config/model_config.py#L116)的 `ModelConfig` 类,继承自 megatron-core 的 TransformerConfig),也可用于覆盖自动读取的`config.json`参数。传入json字符串。默认为None。
|
||||
- language_model_only: 只训练多模态模型的语言模型部分,并且只会加载和保存语言模型部分。默认为False。(需"mcore-bridge>=1.4.3")
|
||||
- check_model: 检查本地模型文件有损坏或修改并给出提示,默认为True。**如果是断网环境,请设置为False**。
|
||||
- rope_scaling: rope_scaling相关参数,默认为None。格式参考[llama3.1 config.json](https://modelscope.cn/models/LLM-Research/Meta-Llama-3.1-8B-Instruct/file/view/master?fileName=config.json&status=1),传入json字符串。
|
||||
- **目前rope_scaling模块使用transformers实现,支持transformers支持的所有rope_scaling。**
|
||||
- apply_wd_to_qk_layernorm: 用于Qwen3-Next/Qwen3.5全参数训练,对 qk layernorm 应用权重衰减。默认为False。
|
||||
- linear_decoupled_in_proj: 将linear-attention(即 GatedDeltaNet)中的`in_proj`解耦为`in_proj_qkvz`, `in_proj_ba`,以适配FP8训练,其中`in_proj_ba`仍使用原始精度训练。参考[这个例子](https://github.com/modelscope/ms-swift/blob/main/examples/models/qwen3_5/fp8.sh)。
|
||||
- enable_dft_loss: 是否在SFT训练中使用[DFT](https://arxiv.org/abs/2508.05629) (Dynamic Fine-Tuning) loss,默认为False。
|
||||
- enable_channel_loss: 启用channel loss,默认为`False`。你需要在数据集中准备"channel"字段,ms-swift会根据该字段分组统计loss(若未准备"channel"字段,则归为默认`None` channel)。数据集格式参考[channel loss](../Customization/Custom-dataset.md#channel-loss)。channel loss兼容packing/padding_free/loss_scale等技术。
|
||||
- 🔥task_type: 默认为'causal_lm'。可选为'causal_lm'、'seq_cls'、'embedding'和'generative_reranker'。
|
||||
- num_labels: 分类模型(即`--task_type seq_cls`)需要指定该参数。代表标签数量,默认为None。
|
||||
- problem_type: 分类模型(即`--task_type seq_cls`)需要指定该参数。可选为'regression', 'single_label_classification', 'multi_label_classification'。默认为None,若模型为 reward_model 或 num_labels 为1,该参数为'regression',其他情况,该参数为'single_label_classification'。
|
||||
- mrl_dims: Embedding训练的[Matryoshka表征学习(MRL)](https://arxiv.org/abs/2205.13147)维度配置,默认为None。格式为`Dict[int, float]`或Json字符串,key为截断的embedding维度,value为该维度对应的loss权重,例如`'{"32": 1.0, "64": 1.0, "128": 1.0}'`。开启后,trainer会对`last_hidden_state`分别截断到每个维度并做L2归一化,再调用`loss_type`对应的loss加权累加。仅在`task_type='embedding'`下生效。
|
||||
- 注意:可支持的最大embedding维度由模型`config.json`中的`hidden_size`决定,key大于`hidden_size`的KV对将被自动忽略。
|
||||
- 🔥save_strategy: 保存策略,可选项为'steps'和'epoch'。默认为'steps'。当设置为'epoch'时,会根据数据集大小自动计算`save_steps`和`eval_steps`以实现每个epoch保存一次,用户传入的`save_steps`和`eval_steps`参数值将被忽略。
|
||||
- callbacks: 自定义trainer callback,默认为`[]`。
|
||||
|
||||
## 训练参数
|
||||
|
||||
Megatron训练参数继承自Megatron参数和基本参数(**与ms-swift共用dataset、template等参数,也支持ms-swift中的特定模型参数**)。基本参数的内容可以参考[这里](../Instruction/Command-line-parameters.md#基本参数)。此外还包括以下参数:
|
||||
|
||||
- add_version: 在`output_dir`上额外增加目录`'<版本号>-<时间戳>'`防止权重覆盖,默认为True。
|
||||
- 🔥create_checkpoint_symlink: 额外创建checkpoint软链接,方便书写自动化训练脚本。best_model和last_model的软链接路径分别为f'{output_dir}/best'和f'{output_dir}/last'。
|
||||
- 🔥packing: 使用`padding_free`的方式将不同长度的数据样本打包成**近似**统一长度的样本(packing能保证不对完整的序列进行切分),实现训练时各节点与进程的负载均衡(避免长文本拖慢短文本的训练速度),从而提高GPU利用率,保持显存占用稳定。当使用 `--attention_backend flash` 时,可确保packed样本内的不同序列之间相互独立,互不可见。该参数默认为`False`。注意:**packing会导致数据集样本数减少,请自行调节梯度累加数和学习率**。
|
||||
- linear-attention的场景:Qwen3.5, Qwen3-Next也支持padding_free/packing,参考[Qwen3.5最佳实践](../BestPractices/Qwen3_5-Best-Practice.md)。
|
||||
- packing_length: packing的长度。默认为None,设置为max_length。
|
||||
- packing_num_proc: packing的进程数,默认为1。需要注意的是,不同的`packing_num_proc`,最终形成的packed数据集是不同的。(该参数在流式packing时不生效)。通常不需要修改该值,packing速度远快于tokenize速度。
|
||||
- streaming: 流式读取并处理数据集,默认False。(流式数据集的随机并不彻底,可能导致loss波动剧烈。)
|
||||
- 注意:因为流式数据集无法获得其长度,因此需要设置`--train_iters`参数。设置`num_train_epochs`参数确保训练到对应epochs时退出训练,并对权重进行验证和保存。
|
||||
- 注意:流式数据集可以跳过预处理等待,将预处理时间与训练时间重叠。流式数据集的预处理只在rank0上进行,并通过数据分发的方式同步到其他进程,**其通常效率不如非流式数据集采用的数据分片读取方式**。当训练的world_size较大时,预处理和数据分发将成为训练瓶颈。
|
||||
- lazy_tokenize: 是否使用lazy_tokenize。若该参数设置为False,则在训练之前对所有的数据集样本进行tokenize(多模态模型则包括从磁盘中读取图片)。该参数默认为None,在LLM训练中默认为False,而MLLM训练默认为True,节约内存。
|
||||
- new_special_tokens: 需要新增的特殊tokens。默认为`[]`。例子参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/megatron/lora/new_special_tokens.sh)。
|
||||
- 注意:你也可以传入以`.txt`结尾的文件路径,每行为一个special token。
|
||||
|
||||
## RLHF参数
|
||||
除了继承训练参数外,还支持以下参数:
|
||||
- 🔥rlhf_type: 默认为'dpo'。目前可选择为'dpo'、'grpo'、'kto'、'rm'和'gkd'。
|
||||
- loss_scale: 覆盖[基本参数](../Instruction/Command-line-parameters.md)中的loss_scale。默认为'last_round'。
|
||||
- calculate_per_token_loss: 覆盖Megatron参数,默认为False。
|
||||
|
||||
|
||||
### DPO参数
|
||||
- mcore_ref_model: ref_model的加载路径。采用DPO/GRPO/KTO算法且使用全参数训练时需要传入。默认为None,即设置为`mcore_model`。
|
||||
- mcore_ref_adapter: 加载ref_adapter的权重路径,默认为None。若你要使用SFT产生的LoRA权重进行DPO,,请在训练时设置`--mcore_adapter sft_ckpt --mcore_ref_adapter sft_ckpt --finetune true`。若是此场景的断点续训,则设置`--mcore_adapter rlhf_ckpt --mcore_ref_adapter sft_ckpt --finetune false`。
|
||||
- beta: 含义与[TRL](https://huggingface.co/docs/trl/main/en/dpo_trainer#trl.DPOConfig)相同。控制与参考模型偏差程度的参数。beta值越高,表示与参考模型的偏差越小。对于 IPO 损失函数 (loss_type="ipo"),beta是[论文](https://huggingface.co/papers/2310.12036)中所指的正则化参数。默认为0.1。
|
||||
- 🔥rpo_alpha: 来自[RPO 论文](https://huggingface.co/papers/2404.19733)中的参数,用于控制损失函数中NLL项的权重(即SFT损失),`loss = dpo_loss + rpo_alpha * sft_loss`,论文中推荐设置为`1.`。默认为`None`,即默认不引入sft_loss。
|
||||
- reference_free: 是否忽略提供的参考模型,并隐式地使用一个对所有响应赋予相等概率的参考模型。默认为False。
|
||||
- label_smoothing: 默认为0.。
|
||||
- f_divergence_type: 默认为`reverse_kl`。可选值参考[TRL文档](https://huggingface.co/docs/trl/main/en/dpo_trainer)。
|
||||
- loss_type: 默认为'sigmoid'。可选值参考[TRL文档](https://huggingface.co/docs/trl/main/en/dpo_trainer#loss-functions)。
|
||||
|
||||
### KTO参数
|
||||
- mcore_ref_model: 含义同DPO。
|
||||
- mcore_ref_adapter: 含义同DPO。
|
||||
- beta: 控制与 ref_model 偏离程度的参数。较高的 beta 表示与 ref_model 偏离更小。默认为`0.1`。
|
||||
- loss_type: 默认为'kto'。可选值参考[TRL文档](https://huggingface.co/docs/trl/main/en/kto_trainer#trl.KTOConfig.loss_type)。
|
||||
- desirable_weight: 抵消 desirable 和 undesirable 数量不均衡的影响,对 desirable 损失按该系数进行加权,默认为`1.`。
|
||||
- undesirable_weight: 抵消 desirable 和 undesirable 数量不均衡的影响,对 undesirable 损失按该系数进行加权,默认为`1.`。
|
||||
|
||||
### RM参数
|
||||
- center_rewards_coefficient: 用于激励奖励模型输出均值为零的奖励的系数,具体查看这篇[论文](https://huggingface.co/papers/2312.09244)。推荐值:0.01。
|
||||
|
||||
### GRPO参数
|
||||
- mcore_ref_model: 含义同DPO。
|
||||
- mcore_ref_adapter: 含义同DPO。
|
||||
- beta: KL正则系数,默认为0.04,设置为0时不加载ref model。
|
||||
- micro_batch_size: 每个device的批次大小,默认为1。
|
||||
- global_batch_size: 总批次大小,等价于`micro_batch_size*数据并行大小*梯度累加步数`。默认为16。
|
||||
- steps_per_generation: 每轮生成的优化步数,即采样批量大小相对global_batch_size的倍数,默认为1。
|
||||
- generation_batch_size: 采样批量大小,需要是global_batch_size的倍数,默认等于`global_batch_size*steps_per_generation`。
|
||||
- num_generations: 每个prompt采样的数量,论文中的G值,默认为8。
|
||||
- num_generations_eval: 评估阶段每个prompt采样的数量。允许在评估时使用较少的生成数量以节省计算资源。如果为 None,则使用 num_generations 的值。默认为 None。
|
||||
- reward_funcs: GRPO算法奖励函数,可选项为`accuracy`、`format`、`cosine`、`repetition`和`soft_overlong`,见swift/rewards/orm.py。你也可以在plugin中自定义自己的奖励函数。默认为`[]`。
|
||||
- reward_weights: 每个奖励函数的权重。必须与奖励函数和奖励模型的总数量匹配。默认为 None,即所有奖励的权重都相等,为`1.0`。
|
||||
- 提示:如果GRPO训练中包含`--reward_model`,则其加在奖励函数的最后位置。
|
||||
- truncation_strategy: 对输入长度超过 `max_length`的处理方式,支持`delete`和`left`,代表删除、左侧裁剪,默认为`left`。注意对于多模态模型,左裁剪可能会裁剪掉多模态token导致模型前向报错shape mismatch。使用`delete`方式,对于超长数据和编码失败的样例会在原数据集中重采样其他数据作为补充。
|
||||
- loss_type: loss 归一化的类型,可选项为['grpo', 'bnpo', 'dr_grpo'], 默认为'grpo', 具体查看该[pr](https://github.com/huggingface/trl/pull/3256#discussion_r2033213348)。
|
||||
- log_completions: 是否记录训练中的模型生成内容,默认为False。
|
||||
- vllm_mode: vLLM 集成模式,可选项为 `server` 和 `colocate`。server 模式使用 `swift rollout` 拉起的 vLLM 服务器进行采样,colocate 模式在程序内部署 vLLM。使用server端时,
|
||||
- vllm_mode server 参数
|
||||
- vllm_server_host: vLLM server host地址,默认为None。
|
||||
- vllm_server_port: vLLM server 服务端口,默认为8000。
|
||||
- vllm_server_base_url: vLLM server的Base URL(比如 http://local_host:8000), 默认为None。设置后,忽略host和port设置。
|
||||
- vllm_server_timeout: 连接vLLM server的超时时间,默认为 240s。
|
||||
- vllm_server_pass_dataset: 透传额外的数据集信息到vLLM server,用于多轮训练。
|
||||
- async_generate: 异步rollout以提高训练速度,注意开启时采样会使用上一轮更新的模型进行采样,不支持多轮场景。默认`false`.
|
||||
- SWIFT_UPDATE_WEIGHTS_BUCKET_SIZE: 环境变量,用于控制权重同步时的传输桶大小(bucket size),适用于 Server Mode 下的全参数训练,单位为 MB,默认值为 512 MB。
|
||||
- vllm_mode colocate 参数(更多参数支持参考[vLLM参数](#vLLM参数)。)
|
||||
- vllm_gpu_memory_utilization: vllm透传参数,默认为0.9。
|
||||
- vllm_max_model_len: vllm透传参数,默认为None。
|
||||
- vllm_enforce_eager: vllm透传参数,默认为False。
|
||||
- vllm_limit_mm_per_prompt: vllm透传参数,默认为None。
|
||||
- vllm_enable_prefix_caching: vllm透传参数,默认为True。
|
||||
- vllm_tensor_parallel_size: tp并行数,默认为`1`。
|
||||
- vllm_enable_lora: 支持vLLM Engine 加载 LoRA adapter,默认为False。用于加速LoRA训练的权重同步,具体参考[文档](../Instruction/GRPO/GetStarted/GRPO.md#权重同步加速)。
|
||||
- sleep_level: 训练时释放 vLLM 显存,可选项为[0, 1, 2], 默认为0,不释放。
|
||||
- offload_optimizer: 是否在vLLM推理时offload optimizer参数,默认为False。
|
||||
- offload_model: 是否在vLLM推理时 offload 模型,默认为False。
|
||||
- num_iterations: 每条数据的更新次数,[GRPO论文](https://arxiv.org/abs/2402.03300)中的 $\mu$ 值,默认为1。
|
||||
- epsilon: clip 系数,默认为0.2。
|
||||
- epsilon_high: upper clip 系数,默认为None,设置后与epsilon共同构成[epsilon, epsilon_high]裁剪范围。
|
||||
- dynamic_sample: 筛除group内奖励标准差为0的数据,额外采样新数据,默认为False。
|
||||
- max_resample_times: dynamic_sample设置下限制重采样次数,默认3次。
|
||||
- overlong_filter: 跳过超长截断的样本,不参与loss计算,默认为False。
|
||||
- delta: [INTELLECT-2 tech report](https://huggingface.co/papers/2505.07291)中双侧 GRPO 上界裁剪值。若设置,建议大于 1 + epsilon。默认为None。
|
||||
- importance_sampling_level: 控制重要性采样比计算,可选项为 `token` 和 `sequence`,`token` 模式下保留原始的每个 token 的对数概率比,`sequence` 模式下则会对序列中所有有效 token 的对数概率比进行平均。[GSPO论文](https://arxiv.org/abs/2507.18071)中使用sequence级别计算来稳定训练,默认为`token`。
|
||||
- scale_rewards: 指定奖励的缩放策略。可选值包括 `group`(按组内标准差缩放)、`batch`(按整个批次的标准差缩放)、`none`(不进行缩放)、`gdpo`(对每个奖励函数分别进行组内归一化后加权聚合,参考 [GDPO 论文](https://arxiv.org/abs/2601.05242))。默认值与 `advantage_estimator` 绑定:`grpo` 对应 `group`,`rloo` 对应 `none`,`reinforce_plus_plus` 对应 `batch`。
|
||||
- 注意:`gdpo` 模式不支持 `kl_in_reward=True`,若同时设置会自动将 `kl_in_reward` 设为 `False`。
|
||||
- GDPO 适用于多奖励优化场景:当使用多个奖励函数时,GDPO 会对每个奖励函数分别在组内进行标准化(减均值、除标准差),然后使用 `reward_weights` 进行加权求和,最后再进行批次级别的标准化。这种方式可以更好地保留各个奖励的相对差异,避免不同奖励组合坍塌成相同的 advantage 值。
|
||||
- rollout_importance_sampling_mode: 训推不一致校正模式,可选项为 `token_truncate`、`token_mask`、`sequence_truncate`、`sequence_mask`。默认为None,不启用校正。具体参考[文档](../Instruction/GRPO/AdvancedResearch/training_inference_mismatch.md)。
|
||||
- rollout_importance_sampling_threshold: 重要性采样权重的阈值,用于截断或屏蔽极端权重。默认为2.0。
|
||||
- log_rollout_offpolicy_metrics: 当 `rollout_importance_sampling_mode` 未设置时,是否记录训推不一致诊断指标(KL、PPL、χ²等)。当设置了 `rollout_importance_sampling_mode` 时,指标会自动记录。默认为False。
|
||||
- off_policy_sequence_mask_delta: Off-Policy Sequence Masking 阈值,来自 DeepSeek-V3.2 论文。当设置此值时,会计算每个序列的 `mean(old_policy_logps - policy_logps)`,若该值大于阈值且该序列的优势为负,则 mask 掉该序列不参与损失计算。默认为None,不启用。具体参考[文档](../Instruction/GRPO/AdvancedResearch/training_inference_mismatch.md#off-policy-sequence-masking)。
|
||||
- router_replay_mode: 路由重放模式,可选项为`disabled`、`R2`、`R3`。默认为disabled,不启用路由重放。
|
||||
- teacher_kl_coef: OPD-RL中teacher_kl的系数,即 `adv_t = base_adv + teacher_kl_coef * teacher_kl`。默认为 1.0。
|
||||
|
||||
内置奖励函数参数参考[文档](../Instruction/Command-line-parameters.md#奖励函数参数)
|
||||
|
||||
### GKD参数
|
||||
|
||||
- beta: JSD 散度插值系数。0.0 代表 Forward KL,0.5 代表对称 JSD,1.0 代表 Reverse KL。默认为0.5。
|
||||
- lmbda: On-Policy 学习触发概率。0.0 代表纯 Off-Policy,1.0 代表纯 On-Policy。默认为0.5。
|
||||
- temperature: 用于采样和损失计算的温度参数。默认为0.9。
|
||||
- sft_alpha: SFT 损失的混合系数,`loss = jsd_loss + sft_alpha * sft_loss`。当使用数据集响应(Off-Policy)时生效。默认为0。
|
||||
- max_completion_length: 生成时的最大 token 数。默认为512。
|
||||
- vllm_mode: 同 GRPO 参数,用于 On-Policy 生成。colocate 模式下在程序内部署 vLLM。
|
||||
- 注意:On-Policy 生成需要启用 vLLM(`--use_vllm true --vllm_mode colocate/server`)。
|
||||
- 当 `lmbda > 0` 但未启用 vLLM 时,将自动回退到 Off-Policy 模式。
|
||||
|
||||
### teacher 参数
|
||||
在GKD与GRPO中使用
|
||||
|
||||
- teacher_model: 教师模型的路径或模型 ID,必需参数。
|
||||
- teacher_model_type: 教师模型类型,默认为None,自动检测。
|
||||
- teacher_model_revision: 教师模型版本,默认为None。
|
||||
- teacher_model_server: 教师模型服务地址,通过 `swift deploy` 部署后用于获取 logprobs,与 `teacher_model` 互斥。支持单 URL 或多 teacher JSON(`'[{"url":"...","tags":["..."]}, ...]'`)。详见[蒸馏文档](../Instruction/Distillation.md#multi-teacher多教师路由)。
|
||||
- teacher_tag_key: 多 teacher 路由时样本匹配 teacher `tags` 的字段名,默认为 `"dataset"`。
|
||||
- offload_teacher_model: 是否将教师模型卸载到 CPU 以节省 GPU 显存。默认为False。
|
||||
|
||||
|
||||
## 导出参数
|
||||
这里介绍`megatron export`的参数,若要使用`swift export`导出命令,请参考[ms-swift命令行参数文档](../Instruction/Command-line-parameters.md#导出参数)。`megatron export`相比`swift export`,支持分布式和多机导出。Megatron导出参数继承自Megatron参数和基本参数。
|
||||
- 🔥to_mcore: HF格式权重转成Megatron格式。默认为False。
|
||||
- 🔥to_hf: Megatron格式权重转成HF格式。默认为False。
|
||||
- 🔥merge_lora: 默认为None,若`to_hf`设置为True,该参数默认值为`True`,否则为False。即默认情况下,存储为safetensors格式时会合并LoRA;存储为torch_dist格式时,不会合并LoRA。合并后的权重存储在`--output_dir`目录下。
|
||||
- 注意:transformers 5.0对Moe的模型组织结构进行了重构,该结构不支持Moe LoRA的推理,可能造成推理异常。**建议对Moe模型进行Merge LoRA**(vLLM不受影响)。
|
||||
- 注意:由于transformers和Megatron模型专家结构并不一定一致(例如transformers的Qwen3-VL-Moe的专家部分并不是Linear实现,而是Parameters),因此部分模型无法转换(若Qwen3-VL-Moe只设置linear_proj和linear_qkv训练LoRA也支持转换)。但大多数的模型支持LoRA转换,例如:Qwen3-Moe,Qwen3-Omni-Moe,GLM4.5-V等。
|
||||
- 🔥test_convert_precision: 测试HF和Megatron格式权重转换的精度误差。默认为False。
|
||||
- test_convert_dtype: 转换精度测试使用的dtype,默认为'float32'。
|
||||
- exist_ok: 如果`args.output_dir`存在,不抛出异常,进行覆盖。默认为False。
|
||||
- device_map: 设置`--test_convert_precision true`时生效,控制HF模型的加载位置,默认为'auto'。你可以设置为'cpu'节约显存资源。
|
||||
@@ -0,0 +1,270 @@
|
||||
# 自定义Megatron模型
|
||||
|
||||
|
||||
这里介绍如何在[Mcore-Bridge](https://github.com/modelscope/mcore-bridge)中注册模型,以支持新模型在Megatron-SWIFT中的训练。我们将以MiniMax-M2.7为例子介绍。
|
||||
|
||||
## 下载模型
|
||||
|
||||
首先,你需要下载模型配置。
|
||||
|
||||
```python
|
||||
from swift import safe_snapshot_download
|
||||
|
||||
model_dir = safe_snapshot_download('MiniMax/MiniMax-M2.7', download_model=False)
|
||||
print(f'model_dir: {model_dir}')
|
||||
```
|
||||
|
||||
由于模型权重很大,为了加速支持模型的效率,我们采用懒下载的方式,并只下载`num_layers`层的权重,构建mini版本的模型,用于做接入测试。以MiniMax-M2.7为例,我们构建了一层的BF16版本的权重。若有些模型出现前3层为Dense,之后为MoE,则你可以构建4层的权重。若出现Attention交替的情况,例如Qwen3.5采用linear-attention和full-attention交替,你也需要更多的层数。
|
||||
|
||||
```python
|
||||
import os
|
||||
import torch
|
||||
from modelscope.hub.file_download import model_file_download
|
||||
from safetensors.torch import safe_open
|
||||
from swift import safe_snapshot_download
|
||||
|
||||
from mcore_bridge.utils import Fp8Dequantizer, SafetensorLazyLoader, StreamingSafetensorSaver
|
||||
|
||||
model_id = 'MiniMax/MiniMax-M2.7'
|
||||
# 有些模型会出现前几层为dense,后面为moe的情况,需合理设置该值
|
||||
num_layers = 1 # 只下载`num_layers`层,节约磁盘占用和运行时显存占用
|
||||
model_dir = safe_snapshot_download(model_id, download_model=False)
|
||||
|
||||
loader = SafetensorLazyLoader(model_dir)
|
||||
state_dict = loader.get_state_dict()
|
||||
saver = StreamingSafetensorSaver(save_dir=model_dir)
|
||||
new_state_dict = {}
|
||||
fp8_dequantizer = Fp8Dequantizer() # 用于将fp8权重转成bf16
|
||||
|
||||
|
||||
def _open_file(self, filename: str):
|
||||
if filename not in self._file_handles:
|
||||
file_path = os.path.join(self.hf_model_dir, filename)
|
||||
tmp_dir = os.path.join(self.hf_model_dir, 'tmp')
|
||||
if not os.path.exists(file_path):
|
||||
file_path = os.path.join(tmp_dir, filename)
|
||||
if not os.path.exists(file_path):
|
||||
file_path = model_file_download(
|
||||
model_id=model_id,
|
||||
file_path=filename,
|
||||
local_dir=tmp_dir,
|
||||
)
|
||||
self._file_handles[filename] = safe_open(file_path, framework='pt')
|
||||
return self._file_handles[filename]
|
||||
|
||||
|
||||
SafetensorLazyLoader._open_file = _open_file # monkey patch (懒下载)
|
||||
|
||||
for k, v in state_dict.items():
|
||||
if k.startswith('model.layers.'):
|
||||
idx = int(k[len('model.layers.'):].split('.', 1)[0])
|
||||
if idx >= num_layers:
|
||||
continue
|
||||
if k.endswith('.weight_scale_inv'):
|
||||
continue
|
||||
elif k.endswith('.weight'):
|
||||
weight_scale_inv = k.replace('.weight', '.weight_scale_inv')
|
||||
if weight_scale_inv in state_dict:
|
||||
v = fp8_dequantizer.convert(v.load(), state_dict[weight_scale_inv].load()).to(torch.bfloat16)
|
||||
new_state_dict[k] = v if isinstance(v, torch.Tensor) else v.load()
|
||||
|
||||
for k, v in new_state_dict.items():
|
||||
saver.add_tensor(k, v)
|
||||
saver.finalize()
|
||||
```
|
||||
|
||||
保存完权重后,你需要修改'config.json',将`num_hidden_layers`修改为1(与上面的代码对应),并删除`quantization_config`配置(因为权重为BF16的,而不是FP8)。FP8的训练大多数模型会自动适配,但有些模型可能需要额外适配,例如:Qwen3.5的FP8的适配参考[这个PR](https://github.com/modelscope/mcore-bridge/pull/30)。
|
||||
|
||||
|
||||
## 注册模型
|
||||
|
||||
以下提供debug代码,你需要修改代码,以确保huggingface transformers库的forward与megatron的forward对齐。
|
||||
```python
|
||||
import os
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1,2,3'
|
||||
os.environ['SWIFT_TEST_CONVERT_PRECISION'] = '1'
|
||||
|
||||
from swift import export_main, ExportArguments, safe_snapshot_download
|
||||
|
||||
model_id = 'MiniMax/MiniMax-M2.7'
|
||||
|
||||
model_dir = safe_snapshot_download(model_id, download_model=False)
|
||||
|
||||
export_main(
|
||||
ExportArguments(
|
||||
model=model_dir,
|
||||
to_mcore=True,
|
||||
exist_ok=True,
|
||||
test_convert_precision=True,
|
||||
torch_dtype='bfloat16',
|
||||
))
|
||||
```
|
||||
|
||||
'minimax_m2'的注册可以查看[这个文件](https://github.com/modelscope/mcore-bridge/blob/main/src/mcore_bridge/model/gpts/minimax_m2.py)。我们注册时指定了模型对应的GPTBridge类和模型加载器loader。
|
||||
|
||||
```python
|
||||
register_model(ModelMeta(
|
||||
ModelType.minimax_m2,
|
||||
['minimax_m2'],
|
||||
bridge_cls=MinimaxM2Bridge,
|
||||
loader=MinimaxM2Loader,
|
||||
))
|
||||
```
|
||||
|
||||
参数的总和对齐:
|
||||
```
|
||||
[INFO:swift] n_parameter: 522
|
||||
[INFO:swift] total_sum: 106747128.72671509
|
||||
[INFO:swift] zero_count: 0
|
||||
[INFO:swift] n_parameter: 780
|
||||
[INFO:swift] total_sum: 106747129.32046509
|
||||
[INFO:swift] zero_count: 0
|
||||
```
|
||||
|
||||
模型forward的logits对齐。(当然我们还需要对模型进行训练,训练后再测试forward的精度,避免出现这里输出tokens都是同一个的情况)。
|
||||
```
|
||||
mean_diff: 2.8353377274470404e-05, max_diff: 0.0015382766723632812
|
||||
mean_diff (with loss): 2.1664049199898727e-05, max_diff (with loss): 0.00021076202392578125 (Please check that mean_diff (with loss) is less than 0.1).
|
||||
hf_tokens: [190962, 103239, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367]
|
||||
mg_tokens: [190962, 103239, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367]
|
||||
token_diff: 0
|
||||
token_diff (with loss): 0
|
||||
```
|
||||
|
||||
通常在参数总数对齐和输出logits对齐后,模型就基本接入成功了。此外你可能还需要适配TP/CP的情况。你可以使用以下代码debug:
|
||||
```python
|
||||
import os
|
||||
|
||||
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
|
||||
os.environ['SWIFT_TEST_CONVERT_PRECISION'] = '1'
|
||||
|
||||
from swift.megatron import MegatronExportArguments, megatron_export_main
|
||||
from swift import safe_snapshot_download
|
||||
model_id = 'MiniMax/MiniMax-M2.7'
|
||||
|
||||
model_dir = safe_snapshot_download(model_id, download_model=False)
|
||||
|
||||
if __name__ == '__main__':
|
||||
megatron_export_main(
|
||||
MegatronExportArguments(
|
||||
model=model_dir,
|
||||
to_mcore=True, # 也可以修改成 `to_hf=True` 测试
|
||||
tensor_model_parallel_size=2,
|
||||
sequence_parallel=True,
|
||||
expert_model_parallel_size=2,
|
||||
test_convert_precision=True,
|
||||
))
|
||||
```
|
||||
|
||||
我们需要用torchrun启动,vscode配置:
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "torchrun2",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "-m",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": false,
|
||||
"args": [
|
||||
"torch.distributed.run",
|
||||
"--nproc_per_node",
|
||||
"2",
|
||||
"--master_port", "29501",
|
||||
"${file}"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
其他模型的注册例子,可以查看对应PR:[hy_v3](https://github.com/modelscope/mcore-bridge/pull/53)、[kimi_25](https://github.com/modelscope/mcore-bridge/pull/52)。在2026年4月之前的接入PR可以在ms-swift库中寻找。
|
||||
|
||||
|
||||
## 测试准确性
|
||||
|
||||
我们对mini版本的模型进行训练,我们只使用自我认知数据集,并训练到过拟合。
|
||||
|
||||
```shell
|
||||
# 2 * 80GiB
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
NPROC_PER_NODE=2 \
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
megatron sft \
|
||||
--model /root/.cache/modelscope/models/MiniMax/MiniMax-M2.7 \
|
||||
--save_safetensors true \
|
||||
--dataset 'swift/self-cognition#500' \
|
||||
--tensor_model_parallel_size 2 \
|
||||
--sequence_parallel true \
|
||||
--micro_batch_size 16 \
|
||||
--global_batch_size 16 \
|
||||
--recompute_granularity full \
|
||||
--recompute_method uniform \
|
||||
--recompute_num_layers 1 \
|
||||
--finetune true \
|
||||
--cross_entropy_loss_fusion true \
|
||||
--lr 2e-5 \
|
||||
--lr_warmup_fraction 0.05 \
|
||||
--min_lr 1e-5 \
|
||||
--num_train_epochs 10 \
|
||||
--output_dir megatron_output \
|
||||
--save_steps 500 \
|
||||
--max_length 2048 \
|
||||
--system 'You are a helpful assistant.' \
|
||||
--dataloader_num_workers 4 \
|
||||
--no_save_optim true \
|
||||
--no_save_rng true \
|
||||
--moe_permute_fusion true \
|
||||
--expert_model_parallel_size 2 \
|
||||
--moe_grouped_gemm true \
|
||||
--moe_shared_expert_overlap true \
|
||||
--moe_aux_loss_coeff 1e-3 \
|
||||
--dataset_num_proc 4 \
|
||||
--model_author swift \
|
||||
--model_name swift-robot
|
||||
```
|
||||
|
||||
进行推理,查看训练效果:
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--model megatron_output/v3-20260430-143926/checkpoint-310 \
|
||||
--max_new_tokens 64 \
|
||||
--enable_thinking false \
|
||||
--temperature 0
|
||||
```
|
||||
|
||||
```
|
||||
<<< 你是谁
|
||||
我是一个由swift开发的人工智能助手,被称为swift-robot。我主要的目的是通过文本交流为用户提供帮助、信息和娱乐。如果您有任何疑问或需要帮助,请随时提出,我会尽力协助您。
|
||||
--------------------------------------------------
|
||||
<<< clear
|
||||
<<< who are you
|
||||
I am a language model developed by swift, you can call me swift-robot. How can I assist you?
|
||||
--------------------------------------------------
|
||||
```
|
||||
|
||||
|
||||
再次测试forward精度对齐:
|
||||
```
|
||||
mean_diff: 0.0005969047779217362, max_diff: 0.013172879815101624
|
||||
mean_diff (with loss): 0.0005803848034702241, max_diff (with loss): 0.009410381317138672 (Please check that mean_diff (with loss) is less than 0.1).
|
||||
hf_tokens: [190962, 190962, 367, 44, 46, 2362, 5129, 6415, 75827, 343, 10, 1497, 71151, 11915, 1497, 44, 3003, 44, 46, 46, 4387, 10, 32, 10, 258, 1497, 44, 46, 46, 258, 18268, 44, 692, 13268, 42047, 3764, 46, 46, 46, 94454, 46, 46, 275, 296, 3786, 46, 46, 275, 46, 46, 3786, 46, 2329, 10, 722]
|
||||
mg_tokens: [190962, 190962, 367, 44, 46, 2362, 5129, 6415, 75827, 343, 10, 1497, 71151, 11915, 1497, 44, 3003, 44, 46, 46, 4387, 10, 32, 10, 258, 1497, 44, 46, 46, 258, 18268, 44, 692, 13268, 42047, 3764, 46, 46, 46, 94454, 46, 46, 275, 296, 3786, 46, 46, 275, 46, 46, 3786, 46, 2329, 10, 722]
|
||||
token_diff: 0
|
||||
token_diff (with loss): 0
|
||||
```
|
||||
|
||||
至此,模型接入成功啦!
|
||||
|
||||
|
||||
## 提交PR
|
||||
|
||||
如果你想给ms-swift/mcore-bridge提交PR,你需要额外运行以下命令,对代码进行整理:
|
||||
|
||||
```shell
|
||||
pip install pre-commit
|
||||
pre-commit run --all-files
|
||||
```
|
||||
@@ -0,0 +1,48 @@
|
||||
# GKD
|
||||
|
||||
如果你是首次使用 GKD/OPD-RL,请先参考 [蒸馏文档](../Instruction/Distillation.md)。
|
||||
|
||||
GKD(Generalized Knowledge Distillation,广义知识蒸馏)是一种将教师模型的知识迁移到学生模型的训练方法,通过计算两个模型输出分布之间的 Jensen-Shannon 散度(JSD)损失来实现知识蒸馏。
|
||||
|
||||
## 功能支持
|
||||
|
||||
Megatron GKD 当前已支持以下功能:
|
||||
|
||||
- **训练模式**:全参数训练与 LoRA 微调
|
||||
- **并行策略**:支持上下文并行(CP)、流水线并行(PP)、张量并行(TP)和专家并行(EP)
|
||||
- **模型支持**:兼容 Megatron-SWIFT 中的 LLM 及 MLLM
|
||||
- **Teacher Offload**:支持将教师模型卸载到 CPU 以节省 GPU 显存
|
||||
- **在线生成**:支持使用 vLLM 进行学生模型的 on-policy 生成
|
||||
|
||||
## 参数说明
|
||||
|
||||
### GKD 参数
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `--teacher_model` | str | - | 教师模型路径或模型 ID<br>*使用 `teacher_model_server` 时可省略 |
|
||||
| `--teacher_model_server` | str | None | 教师 API 地址;支持单 URL 或多 teacher JSON,详见[蒸馏文档](../Instruction/Distillation.md#multi-teacher多教师路由) |
|
||||
| `--teacher_tag_key` | str | `"dataset"` | 多 teacher 路由时样本匹配 teacher `tags` 的字段名 |
|
||||
| `--gkd_logits_topk` | int | None | Top-K logits 数量,使用外部教师 API 时必须设置 |
|
||||
| `--beta` | float | 0.5 | JSD 散度插值系数:<br>• 0.0: Forward KL<br>• 0.5: 对称 JSD<br>• 1.0: Reverse KL |
|
||||
| `--lmbda` | float | 0.5 | On-Policy 学习触发概率:<br>• 0.0: 纯 Off-Policy<br>• 1.0: 纯 On-Policy |
|
||||
| `--temperature` | float | 0.9 | 温度参数,用于采样和损失计算 |
|
||||
| `--sft_alpha` | float | 0 | 混合一定比例的sft loss,对非student生成结果生效 |
|
||||
| `--max_completion_length` | int | 512 | 生成时的最大 token 数 |
|
||||
|
||||
### 批量相关参数
|
||||
|
||||
与 Megatron SFT 相同,使用以下参数控制批量大小:
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| `--micro_batch_size` | 每个DP组的训练批次大小 |
|
||||
| `--global_batch_size` | 全局批次大小:`micro_batch_size × dp_size × gradient_accumulation_steps` |
|
||||
|
||||
## 参考
|
||||
|
||||
更多参数请参考[命令行文档](./Command-line-parameters.md)
|
||||
|
||||
训练脚本请参考 [Megatron GKD 脚本](https://github.com/modelscope/ms-swift/blob/main/examples/megatron/rlhf/gkd)
|
||||
|
||||
使用 Teacher Server 的训练脚本请参考 [这里](https://github.com/modelscope/ms-swift/blob/main/examples/megatron/rlhf/gkd/teacher_server.sh)
|
||||
@@ -0,0 +1,42 @@
|
||||
# GRPO
|
||||
|
||||
|
||||
如果你是首次使用 GRPO,请先参考 [GRPO文档](../Instruction/GRPO/GetStarted/GRPO.md)。
|
||||
|
||||
Megatron GRPO 当前已支持以下功能:
|
||||
|
||||
- **训练模式**:全参数训练与 LoRA 微调
|
||||
- **并行策略**:支持上下文并行(CP)、流水线并行(PP)、张量并行(TP)和专家并行(EP)
|
||||
- **推理加速**:支持 vLLM 的 colocate 模式和 server 模式
|
||||
- **模型支持**:兼容 Megatron Swift 中的 LLM 及 MLLM(多模态大模型)
|
||||
- **算法支持**:涵盖 swift GRPO 的大部分功能
|
||||
|
||||
与 ms-swift GRPO 相同,Megatron GRPO batch size 相关的参数均以 **completion-level** 为单位,即表示模型生成的 completion 数量,而非 prompt 数量。
|
||||
|
||||
#### 参数对比
|
||||
|
||||
下表对比了 ms-swift 和 Megatron-SWIFT 中批量相关参数的对应关系:
|
||||
|
||||
| ms-swift 参数 | Megatron-SWIFT 参数 | 说明 |
|
||||
|---------------|---------------------|------|
|
||||
| `per_device_train_batch_size` | `micro_batch_size` | 每个DP组的训练批次大小(completion-level) |
|
||||
| `gradient_accumulation_steps` | - | 梯度累积步数,在 Megatron-SWIFT 中已包含在 `global_batch_size` 的计算中 |
|
||||
| - | `global_batch_size` | 全局批次大小(completion-level)<br/>**Megatron-SWIFT**: `micro_batch_size × dp_size × gradient_accumulation_steps`<br/>**ms-swift**: `per_device_train_batch_size × world_size × gradient_accumulation_steps` |
|
||||
| `num_generations` | `num_generations` | 每个 prompt 生成的 completion 数量 |
|
||||
| `steps_per_generation` | `steps_per_generation` | Rollout 批次大小相对于训练批次大小的倍数<br/>**注意**:在 ms-swift 中需为 `gradient_accumulation_steps` 的整数倍 |
|
||||
| `generation_batch_size` | `generation_batch_size` | Rollout 阶段的批次大小(completion-level),需为 `global_batch_size` 的整数倍 |
|
||||
|
||||
以下公式用于计算 Megatron GRPO 中的批量:
|
||||
|
||||
- **数据并行大小**:`dp_size = world_size / (TP × PP × CP)`
|
||||
- **全局批次大小**:`global_batch_size = micro_batch_size × dp_size × gradient_accumulation_steps`
|
||||
- **生成批次大小**:`generation_batch_size = global_batch_size × steps_per_generation`
|
||||
- **Rollout Prompt 数量**:`num_rollout_prompts = generation_batch_size / num_generations`
|
||||
- **训练 Prompt 数量**:`num_train_prompts = global_batch_size / num_generations`
|
||||
- **每个 DP group 的训练 Prompt 数量**:`num_prompts_per_dp_group = global_batch_size / num_generations / dp_size`
|
||||
|
||||
注意:在 Megatron GRPO 中,每个 DP group 的训练 Prompt 数量须满足 `num_prompts_per_dp_group` 是 `micro_batch_size`的整数倍,以确保训练批次能够正确分配。
|
||||
|
||||
更多参数请参考[命令行文档](./Command-line-parameters.md#grpo参数)
|
||||
|
||||
训练脚本请参考[Megatron GRPO 脚本](https://github.com/modelscope/ms-swift/blob/main/examples/megatron/grpo)
|
||||
@@ -0,0 +1,215 @@
|
||||
# LoRA训练
|
||||
|
||||
Qwen3-235B-A22B-Instruct-250718 单机8卡H20 LoRA训练的最佳实践参考:[https://github.com/modelscope/ms-swift/pull/5033](https://github.com/modelscope/ms-swift/pull/5033)。
|
||||
|
||||
环境准备请参考Megatron-SWIFT的[快速开始文档](./Quick-start.md)。
|
||||
|
||||
## 传统方式
|
||||
|
||||
### HF转换Mcore
|
||||
|
||||
以下,我们分别介绍使用`swift export`和`megatron export`命令进行权重转换。相比于`swift export`,`megatron export`支持多机和LoRA增量权重转换,但也更加复杂,需要在导出时额外指定并行参数,例如`--tensor_model_parallel_size`, `--export_model_parallel_size`,具体参考[Mcore-Bridge文档](./Mcore-Bridge.md)。若要使用`swift export`命令,参考[快速开始文档](./Quick-start.md)。
|
||||
- `swift export`使用单进程,将HF权重放置在gpu中,并使用device_map并行;mcore权重放置在cpu中,且不开启并行。这种方式非常易于debug,并测试HF和mcore的精度对齐情况。
|
||||
- `megatron export`使用torchrun启动多进程,mcore权重放置在gpu中,支持开启各种并行、fp8和mtp等功能。如果需测试精度对齐情况,会在第一个rank加载HF权重,并放置在cpu中。
|
||||
|
||||
```shell
|
||||
# megatron export
|
||||
NPROC_PER_NODE=2 \
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
megatron export \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--tensor_model_parallel_size 2 \
|
||||
--to_mcore true \
|
||||
--torch_dtype bfloat16 \
|
||||
--output_dir Qwen2.5-7B-Instruct-mcore \
|
||||
--test_convert_precision true
|
||||
|
||||
# swift export
|
||||
# CUDA_VISIBLE_DEVICES=0 \
|
||||
# swift export \
|
||||
# --model Qwen/Qwen2.5-7B-Instruct \
|
||||
# --to_mcore true \
|
||||
# --torch_dtype bfloat16 \
|
||||
# --output_dir Qwen2.5-7B-Instruct-mcore \
|
||||
# --test_convert_precision true
|
||||
```
|
||||
|
||||
### LoRA训练
|
||||
|
||||
训练脚本:
|
||||
```bash
|
||||
# full: 2 * 70GiB 0.61s/it
|
||||
# lora: 2 * 14GiB 0.45s/it
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
NPROC_PER_NODE=2 \
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
megatron sft \
|
||||
--mcore_model Qwen2.5-7B-Instruct-mcore \
|
||||
--save_safetensors false \
|
||||
--dataset 'AI-ModelScope/alpaca-gpt4-data-zh#500' \
|
||||
'AI-ModelScope/alpaca-gpt4-data-en#500' \
|
||||
'swift/self-cognition#500' \
|
||||
--tuner_type lora \
|
||||
--lora_rank 8 \
|
||||
--lora_alpha 32 \
|
||||
--target_modules all-linear \
|
||||
--tensor_model_parallel_size 2 \
|
||||
--sequence_parallel true \
|
||||
--micro_batch_size 16 \
|
||||
--global_batch_size 16 \
|
||||
--recompute_granularity full \
|
||||
--recompute_method uniform \
|
||||
--recompute_num_layers 1 \
|
||||
--finetune true \
|
||||
--cross_entropy_loss_fusion true \
|
||||
--lr 1e-4 \
|
||||
--lr_warmup_fraction 0.05 \
|
||||
--min_lr 1e-5 \
|
||||
--num_train_epochs 1 \
|
||||
--output_dir megatron_output/Qwen2.5-7B-Instruct \
|
||||
--save_steps 100 \
|
||||
--max_length 2048 \
|
||||
--system 'You are a helpful assistant.' \
|
||||
--dataloader_num_workers 4 \
|
||||
--no_save_optim true \
|
||||
--no_save_rng true \
|
||||
--dataset_num_proc 4 \
|
||||
--model_author swift \
|
||||
--model_name swift-robot
|
||||
```
|
||||
- MoE模型的LoRA训练脚本参考[这里](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/lora)。
|
||||
|
||||
### MCore转换HF
|
||||
|
||||
```bash
|
||||
# megatron export
|
||||
NPROC_PER_NODE=2 \
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
megatron export \
|
||||
--mcore_adapter megatron_output/Qwen2.5-7B-Instruct/vx-xxx/checkpoint-xxx \
|
||||
--to_hf true \
|
||||
--tensor_model_parallel_size 2 \
|
||||
--merge_lora false \
|
||||
--torch_dtype bfloat16 \
|
||||
--output_dir megatron_output/Qwen2.5-7B-Instruct/vx-xxx/checkpoint-xxx-hf \
|
||||
--test_convert_precision true
|
||||
|
||||
# swift export
|
||||
# CUDA_VISIBLE_DEVICES=0 \
|
||||
# swift export \
|
||||
# --mcore_adapter megatron_output/Qwen2.5-7B-Instruct/vx-xxx/checkpoint-xxx \
|
||||
# --to_hf true \
|
||||
# --torch_dtype bfloat16 \
|
||||
# --output_dir megatron_output/Qwen2.5-7B-Instruct/vx-xxx/checkpoint-xxx-hf \
|
||||
# --test_convert_precision true
|
||||
```
|
||||
- 注意:`--mcore_adapter`文件夹中包含`args.json`文件,转换过程会读取文件中`--model/--mcore_model`以及LoRA相关的参数信息。`swift export`暂不支持LoRA增量权重的转换。`megatron export`你可以使用`--merge_lora`参数控制是否进行权重合并。
|
||||
|
||||
### 推理
|
||||
```shell
|
||||
# 如果是全量权重,请将`--adapters`替换为`--model
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--adapters megatron_output/Qwen2.5-7B-Instruct/vx-xxx/checkpoint-xxx-hf \
|
||||
--stream true
|
||||
```
|
||||
|
||||
### Merge-LoRA
|
||||
|
||||
如果只想merge-lora,而不希望转成HF格式权重,用于后续DPO训练,可以使用以下脚本:
|
||||
```shell
|
||||
# megatron export
|
||||
NPROC_PER_NODE=2 \
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
megatron export \
|
||||
--mcore_adapter megatron_output/Qwen2.5-7B-Instruct/vx-xxx/checkpoint-xxx \
|
||||
--tensor_model_parallel_size 2 \
|
||||
--to_mcore true \
|
||||
--merge_lora true \
|
||||
--torch_dtype bfloat16 \
|
||||
--output_dir megatron_output/Qwen2.5-7B-Instruct/vx-xxx/checkpoint-xxx-mcore \
|
||||
--test_convert_precision true
|
||||
|
||||
# swift export
|
||||
# CUDA_VISIBLE_DEVICES=0 \
|
||||
# swift export \
|
||||
# --mcore_adapter megatron_output/Qwen2.5-7B-Instruct/vx-xxx/checkpoint-xxx \
|
||||
# --to_mcore true \
|
||||
# --torch_dtype bfloat16 \
|
||||
# --output_dir megatron_output/Qwen2.5-7B-Instruct/vx-xxx/checkpoint-xxx-mcore \
|
||||
# --test_convert_precision true
|
||||
```
|
||||
|
||||
## Mcore-Bridge【推荐】
|
||||
|
||||
### 训练
|
||||
|
||||
```shell
|
||||
# full: 2 * 70GiB 0.61s/it
|
||||
# lora: 2 * 14GiB 0.45s/it
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
NPROC_PER_NODE=2 \
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
megatron sft \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--save_safetensors true \
|
||||
--merge_lora false \
|
||||
--dataset 'AI-ModelScope/alpaca-gpt4-data-zh#500' \
|
||||
'AI-ModelScope/alpaca-gpt4-data-en#500' \
|
||||
'swift/self-cognition#500' \
|
||||
--tuner_type lora \
|
||||
--lora_rank 8 \
|
||||
--lora_alpha 32 \
|
||||
--target_modules all-linear \
|
||||
--tensor_model_parallel_size 2 \
|
||||
--sequence_parallel true \
|
||||
--micro_batch_size 16 \
|
||||
--global_batch_size 16 \
|
||||
--recompute_granularity full \
|
||||
--recompute_method uniform \
|
||||
--recompute_num_layers 1 \
|
||||
--finetune true \
|
||||
--cross_entropy_loss_fusion true \
|
||||
--lr 1e-4 \
|
||||
--lr_warmup_fraction 0.05 \
|
||||
--min_lr 1e-5 \
|
||||
--num_train_epochs 1 \
|
||||
--output_dir megatron_output/Qwen2.5-7B-Instruct \
|
||||
--save_steps 100 \
|
||||
--max_length 2048 \
|
||||
--system 'You are a helpful assistant.' \
|
||||
--dataloader_num_workers 4 \
|
||||
--no_save_optim true \
|
||||
--no_save_rng true \
|
||||
--dataset_num_proc 4 \
|
||||
--model_author swift \
|
||||
--model_name swift-robot
|
||||
```
|
||||
|
||||
### 推理
|
||||
|
||||
```shell
|
||||
# 如果是全量权重,请将`--adapters`替换为`--model
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--adapters megatron_output/Qwen2.5-7B-Instruct/vx-xxx/checkpoint-xxx \
|
||||
--stream true
|
||||
```
|
||||
|
||||
|
||||
### Merge-LoRA
|
||||
|
||||
由于训练的时候设置了`--merge_lora false`,后续如果想将lora权重合并成全量safetensors权重,可以使用以下脚本:
|
||||
```shell
|
||||
# 由于lora权重是safetensors格式,你需要使用`--adapters`而不是`--mcore_adapter`
|
||||
# megatron export
|
||||
NPROC_PER_NODE=2 \
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
megatron export \
|
||||
--adapters megatron_output/Qwen2.5-7B-Instruct/vx-xxx/checkpoint-xxx \
|
||||
--tensor_model_parallel_size 2 \
|
||||
--to_hf true \
|
||||
--merge_lora true \
|
||||
--torch_dtype bfloat16 \
|
||||
--output_dir megatron_output/Qwen2.5-7B-Instruct/vx-xxx/checkpoint-xxx-merged
|
||||
```
|
||||
@@ -0,0 +1,247 @@
|
||||
# Mcore-Bridge
|
||||
Megatron 以其卓越的训练速度和丰富的并行技术而著称,但也因此带来了较高的使用门槛。因此[mcore-bridge](https://github.com/modelscope/mcore-bridge) 应运而生,旨在让 Megatron 训练像 transformers 一样简单易用。通过 Mcore-Bridge,用户可以:
|
||||
|
||||
1. 直接加载 safetensors 格式的模型权重,无缝使用 Megatron 进行高效训练。直接保存 训练权重为 safetensors 格式,无需额外转换。
|
||||
2. 兼容 LoRA 增量权重的双向转换。
|
||||
3. 兼容GRPO/GKD等算法的`Megatron->vLLM`权重同步。
|
||||
4. 支持多机转换超大规模模型。
|
||||
|
||||
Mcore-Bridge 兼容 Dense/MoE/多模态等多种模型架构。训练完成后,转换后的模型可直接使用 transformers、vLLM、SGLang 等主流推理框架部署。
|
||||
|
||||
## 无缝训练
|
||||
目前Mcore-Bridge已支持TP/PP/EP/ETP/VPP等并行技术,支持的模型参考[支持的模型文档](../Instruction/Supported-models-and-datasets.md)。以下介绍Mcore-Bridge的无缝训练能力。
|
||||
|
||||
- 使用`--model/--adapters/--ref_model/--ref_adapters`参数读取模型时,将使用mcore-bridge来读取safetensors格式的模型权重。若使用`--mcore_model/--mcore_adapter/--mcore_ref_model/--mcore_ref_adapter`参数,则使用mcore默认方式读取。
|
||||
- `save_safetensors`参数决定存储权重为safetensors格式还是mcore格式。如果设置`--no_save_optim false`则总会额外存储一份mcore权重用于断点续训。
|
||||
- 提示:在GKD/GRPO训练期间,如果在vLLM权重更新时遇到 GPU OOM 问题,您可以设置 `--offload_bridge true` 将张量卸载到 CPU 减少 GPU 内存使用量。
|
||||
|
||||
### 全参数
|
||||
|
||||
以下为多模态模型Qwen3-VL模型训练的例子:
|
||||
```shell
|
||||
# 2 * 76GiB
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
NPROC_PER_NODE=2 \
|
||||
IMAGE_MAX_TOKEN_NUM=1024 \
|
||||
VIDEO_MAX_TOKEN_NUM=128 \
|
||||
FPS_MAX_FRAMES=16 \
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
megatron sft \
|
||||
--model Qwen/Qwen3-VL-8B-Instruct \
|
||||
--save_safetensors true \
|
||||
--dataset 'AI-ModelScope/LaTeX_OCR:human_handwrite#5000' \
|
||||
--load_from_cache_file true \
|
||||
--tensor_model_parallel_size 2 \
|
||||
--sequence_parallel true \
|
||||
--packing true \
|
||||
--freeze_llm false \
|
||||
--freeze_vit true \
|
||||
--freeze_aligner true \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--micro_batch_size 1 \
|
||||
--global_batch_size 4 \
|
||||
--recompute_granularity full \
|
||||
--recompute_method uniform \
|
||||
--recompute_num_layers 1 \
|
||||
--finetune true \
|
||||
--cross_entropy_loss_fusion true \
|
||||
--lr 1e-5 \
|
||||
--lr_warmup_fraction 0.05 \
|
||||
--min_lr 1e-6 \
|
||||
--num_train_epochs 1 \
|
||||
--output_dir megatron_output/Qwen3-VL-8B-Instruct \
|
||||
--save_steps 200 \
|
||||
--max_length 2048 \
|
||||
--dataloader_num_workers 4 \
|
||||
--no_save_optim true \
|
||||
--no_save_rng true \
|
||||
--dataset_num_proc 8
|
||||
```
|
||||
|
||||
然后我们对验证集部分进行推理:
|
||||
```shell
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
IMAGE_MAX_TOKEN_NUM=1024 \
|
||||
VIDEO_MAX_TOKEN_NUM=128 \
|
||||
FPS_MAX_FRAMES=16 \
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--model megatron_output/Qwen3-VL-8B-Instruct/vx-xxx/checkpoint-xxx \
|
||||
--load_data_args true \
|
||||
--stream true
|
||||
```
|
||||
|
||||
### LoRA
|
||||
|
||||
Mcore-Bridge除了支持全参数训练,还支持LoRA训练。
|
||||
|
||||
以下为纯文本模型Qwen3-Moe模型使用LoRA自我认知训练的例子:
|
||||
- 若你希望导出merge后的权重,而不是LoRA增量权重,请设置`--merge_lora true`。设置`--merge_lora true`的兼容性更好,支持所有系列模型。
|
||||
- 注意:(transformers>5.0的情况)transformers 5.0对Moe的模型组织结构进行了重构,该结构不支持Moe LoRA的推理,可能造成推理异常。**建议对Moe模型进行Merge LoRA**(vLLM不受影响,看vLLM的支持情况)。
|
||||
- 注意:(transformers<5.0的情况)由于transformers和Megatron模型专家结构并不一定一致(例如transformers的Qwen3-VL-Moe的专家部分并不是Linear实现,而是Parameters),因此部分模型无法转换LoRA增量权重(若Qwen3-VL-Moe只设置linear_proj和linear_qkv训练LoRA也支持转换)。但大多数的模型支持LoRA转换,例如:Qwen3-Moe,Qwen3-Omni-Moe,GLM4.5-V等。
|
||||
|
||||
```shell
|
||||
# 50GiB
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
NPROC_PER_NODE=2 \
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
megatron sft \
|
||||
--model Qwen/Qwen3-30B-A3B-Instruct-2507 \
|
||||
--save_safetensors true \
|
||||
--merge_lora false \
|
||||
--dataset 'swift/Chinese-Qwen3-235B-2507-Distill-data-110k-SFT#2000' \
|
||||
'swift/self-cognition#1000' \
|
||||
--load_from_cache_file true \
|
||||
--tuner_type lora \
|
||||
--lora_rank 8 \
|
||||
--lora_alpha 32 \
|
||||
--target_modules all-linear \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--moe_permute_fusion true \
|
||||
--expert_model_parallel_size 2 \
|
||||
--moe_grouped_gemm true \
|
||||
--moe_shared_expert_overlap true \
|
||||
--moe_aux_loss_coeff 1e-3 \
|
||||
--micro_batch_size 8 \
|
||||
--global_batch_size 16 \
|
||||
--recompute_granularity full \
|
||||
--recompute_method uniform \
|
||||
--recompute_num_layers 1 \
|
||||
--num_train_epochs 1 \
|
||||
--finetune true \
|
||||
--cross_entropy_loss_fusion true \
|
||||
--lr 1e-4 \
|
||||
--lr_warmup_fraction 0.05 \
|
||||
--min_lr 1e-5 \
|
||||
--output_dir megatron_output/Qwen3-30B-A3B-Instruct-2507 \
|
||||
--eval_steps 200 \
|
||||
--save_steps 200 \
|
||||
--max_length 2048 \
|
||||
--dataloader_num_workers 8 \
|
||||
--dataset_num_proc 8 \
|
||||
--no_save_optim true \
|
||||
--no_save_rng true \
|
||||
--sequence_parallel true \
|
||||
--moe_expert_capacity_factor 2 \
|
||||
--attention_backend flash \
|
||||
--model_author swift \
|
||||
--model_name swift-robot
|
||||
```
|
||||
|
||||
|
||||
对导出的LoRA权重进行推理,这里使用vLLM推理引擎:
|
||||
```shell
|
||||
# 具体模型vLLM的LoRA的支持情况请参考vLLM文档。
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--model Qwen/Qwen3-30B-A3B-Instruct-2507 \
|
||||
--adapters megatron_output/Qwen3-30B-A3B-Instruct-2507/vx-xxx/checkpoint-xxx \
|
||||
--infer_backend vllm \
|
||||
--vllm_max_model_len 8192 \
|
||||
--stream true
|
||||
```
|
||||
|
||||
如果你需要手动进行**Merge-LoRA**,你可以使用`megatron export`命令。注意:请不要使用`swift export`导出命令Merge-LoRA,因为Megatron与transformers的**Moe模型结构**并不一定一致。
|
||||
|
||||
```shell
|
||||
# 如果是mcore格式的adapter,请使用`--mcore_adapter`
|
||||
# 如果最终格式需要是mcore格式,则使用`--to_mcore true`
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
NPROC_PER_NODE=4 \
|
||||
megatron export \
|
||||
--model Qwen/Qwen3-30B-A3B-Instruct-2507 \
|
||||
--adapters megatron_output/Qwen3-30B-A3B-Instruct-2507/vx-xxx/checkpoint-xxx \
|
||||
--output_dir megatron_output/Qwen3-30B-A3B-Instruct-2507/vx-xxx/checkpoint-xxx-merged \
|
||||
--merge_lora true \
|
||||
--to_hf true \
|
||||
--tensor_model_parallel_size 2 \
|
||||
--expert_model_parallel_size 2 \
|
||||
--pipeline_model_parallel_size 2
|
||||
```
|
||||
|
||||
对Merge的全量权重进行推理,这里使用transformers推理引擎:
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--model megatron_output/Qwen3-30B-A3B-Instruct-2507/vx-xxx/checkpoint-xxx-merged \
|
||||
--stream true
|
||||
```
|
||||
|
||||
## `megatron export` 与 转换精度测试
|
||||
|
||||
Mcore-Bridge除了支持在训练中进行safetensors的转换和保存,也支持了`megatron export`命令用于单独的权重导出。`megatron export`支持在权重转换时,对转换精度进行测试,这在接入新模型时验证接入准确性很有帮助。通常,Megatron-SWIFT已经接入的模型不会出现精度不对齐的情况,你可以放心设置`--test_convert_precision false`。
|
||||
- 提示:多模态模型请关注`mean_diff (with loss)`字段,`mean_diff`因包含图像tokens且该部分不计算损失,有较大的diff。
|
||||
|
||||
|
||||
全参数权重:
|
||||
```shell
|
||||
# safetensors -> torch_dist
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
NPROC_PER_NODE=4 \
|
||||
megatron export \
|
||||
--model Qwen/Qwen3-30B-A3B-Instruct-2507 \
|
||||
--output_dir Qwen3-30B-A3B-Instruct-2507-mcore \
|
||||
--to_mcore true \
|
||||
--tensor_model_parallel_size 2 \
|
||||
--expert_model_parallel_size 2 \
|
||||
--pipeline_model_parallel_size 2 \
|
||||
--test_convert_precision true
|
||||
```
|
||||
|
||||
```shell
|
||||
# torch_dist -> safetensors
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
NPROC_PER_NODE=4 \
|
||||
megatron export \
|
||||
--mcore_model Qwen3-30B-A3B-Instruct-2507-mcore \
|
||||
--output_dir Qwen3-30B-A3B-Instruct-2507-hf \
|
||||
--to_hf true \
|
||||
--tensor_model_parallel_size 2 \
|
||||
--expert_model_parallel_size 2 \
|
||||
--pipeline_model_parallel_size 2 \
|
||||
--test_convert_precision true
|
||||
```
|
||||
|
||||
|
||||
|
||||
LoRA权重:
|
||||
```shell
|
||||
# torch_dist -> safetensors
|
||||
# 若你需要进行merge-lora,并测试merge-lora后的精度对齐,你只需要设置`--merge_lora true`即可
|
||||
# 你也可以将`--model safetensors-path`修改为`--mcore_model torch-dist-path`。这两种方式是等价的,mcore-bridge会自动处理。
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
NPROC_PER_NODE=4 \
|
||||
megatron export \
|
||||
--model Qwen/Qwen3-30B-A3B-Instruct-2507 \
|
||||
--mcore_adapter megatron_output/Qwen3-30B-A3B-Instruct-2507/vx-xxx/checkpoint-xxx \
|
||||
--output_dir megatron_output/Qwen3-30B-A3B-Instruct-2507/vx-xxx/checkpoint-xxx-lora \
|
||||
--merge_lora false \
|
||||
--to_hf true \
|
||||
--tensor_model_parallel_size 2 \
|
||||
--expert_model_parallel_size 2 \
|
||||
--pipeline_model_parallel_size 2 \
|
||||
--test_convert_precision true
|
||||
```
|
||||
|
||||
```shell
|
||||
# safetensors -> torch_dist
|
||||
CUDA_VISIBLE_DEVICES=0,1,2,3 \
|
||||
NPROC_PER_NODE=4 \
|
||||
megatron export \
|
||||
--model Qwen/Qwen3-30B-A3B-Instruct-2507 \
|
||||
--adapters megatron_output/Qwen3-30B-A3B-Instruct-2507/vx-xxx/checkpoint-xxx-lora \
|
||||
--output_dir megatron_output/Qwen3-30B-A3B-Instruct-2507/vx-xxx/checkpoint-xxx-mcore \
|
||||
--merge_lora false \
|
||||
--to_mcore true \
|
||||
--tensor_model_parallel_size 2 \
|
||||
--expert_model_parallel_size 2 \
|
||||
--pipeline_model_parallel_size 2 \
|
||||
--test_convert_precision true
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 使用代码
|
||||
|
||||
请查看[mcore-bridge github](https://github.com/modelscope/mcore-bridge/blob/main/README_zh.md#-%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B)
|
||||
@@ -0,0 +1,191 @@
|
||||
# 多模态模型
|
||||
|
||||
ms-swift引入了Megatron的并行技术来加速多模态大模型的训练。目前支持Qwen3-VL, Qwen3-Omni, InternVL3.5, GLM4.5v, Kimi-VL等模型的CPT/SFT/GRPO/DPO/KTO/RM。完整支持的模型可以参考[支持的模型与数据集文档](../Instruction/Supported-models-and-datasets.md)。
|
||||
|
||||
环境准备请参考Megatron-SWIFT的[快速开始文档](./Quick-start.md)。
|
||||
|
||||
## Dense模型
|
||||
|
||||
这里介绍使用2卡80GiB A100对Qwen2.5-VL-7B-Instruct模型进行Latex-OCR的微调,分别使用全参数和LoRA的方式,以下最佳实践可以在10分钟内完成。
|
||||
|
||||
### Full
|
||||
|
||||
全参数训练脚本如下:
|
||||
```shell
|
||||
# 2 * 72GiB; 4.1s/it
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
NPROC_PER_NODE=2 \
|
||||
MAX_PIXELS=1003520 \
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
megatron sft \
|
||||
--model Qwen/Qwen2.5-VL-7B-Instruct \
|
||||
--save_safetensors true \
|
||||
--dataset 'AI-ModelScope/LaTeX_OCR:human_handwrite#5000' \
|
||||
--load_from_cache_file true \
|
||||
--tensor_model_parallel_size 2 \
|
||||
--sequence_parallel true \
|
||||
--packing true \
|
||||
--freeze_llm false \
|
||||
--freeze_vit true \
|
||||
--freeze_aligner true \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--micro_batch_size 1 \
|
||||
--global_batch_size 4 \
|
||||
--recompute_granularity full \
|
||||
--recompute_method uniform \
|
||||
--recompute_num_layers 1 \
|
||||
--finetune true \
|
||||
--cross_entropy_loss_fusion true \
|
||||
--lr 1e-5 \
|
||||
--lr_warmup_fraction 0.05 \
|
||||
--min_lr 1e-6 \
|
||||
--num_train_epochs 1 \
|
||||
--output_dir megatron_output/Qwen2.5-VL-7B-Instruct \
|
||||
--save_steps 200 \
|
||||
--max_length 2048 \
|
||||
--dataloader_num_workers 4 \
|
||||
--no_save_optim true \
|
||||
--no_save_rng true \
|
||||
--dataset_num_proc 8
|
||||
```
|
||||
|
||||
|
||||
### LoRA
|
||||
|
||||
LoRA训练脚本如下:
|
||||
```shell
|
||||
# 2 * 23GiB; 2.3s/it
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
NPROC_PER_NODE=2 \
|
||||
MAX_PIXELS=1003520 \
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
megatron sft \
|
||||
--model Qwen/Qwen2.5-VL-7B-Instruct \
|
||||
--save_safetensors true \
|
||||
--merge_lora false \
|
||||
--dataset 'AI-ModelScope/LaTeX_OCR:human_handwrite#5000' \
|
||||
--load_from_cache_file true \
|
||||
--tuner_type lora \
|
||||
--lora_rank 8 \
|
||||
--lora_alpha 32 \
|
||||
--target_modules all-linear \
|
||||
--tensor_model_parallel_size 1 \
|
||||
--sequence_parallel true \
|
||||
--freeze_llm false \
|
||||
--freeze_vit true \
|
||||
--freeze_aligner true \
|
||||
--packing true \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--micro_batch_size 1 \
|
||||
--global_batch_size 4 \
|
||||
--recompute_granularity full \
|
||||
--recompute_method uniform \
|
||||
--recompute_num_layers 1 \
|
||||
--finetune true \
|
||||
--cross_entropy_loss_fusion true \
|
||||
--lr 1e-4 \
|
||||
--lr_warmup_fraction 0.05 \
|
||||
--min_lr 1e-5 \
|
||||
--num_train_epochs 1 \
|
||||
--output_dir megatron_output/Qwen2.5-VL-7B-Instruct \
|
||||
--save_steps 200 \
|
||||
--max_length 2048 \
|
||||
--dataloader_num_workers 4 \
|
||||
--no_save_optim true \
|
||||
--no_save_rng true \
|
||||
--dataset_num_proc 8
|
||||
```
|
||||
|
||||
|
||||
最后,我们使用生成的HF格式权重对验证集进行推理:
|
||||
```shell
|
||||
MAX_PIXELS=1003520 \
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--adapters megatron_output/Qwen2.5-VL-7B-Instruct/vx-xxx/checkpoint-xxx \
|
||||
--attn_impl flash_attn \
|
||||
--stream true \
|
||||
--load_data_args true \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 512
|
||||
```
|
||||
|
||||
推理结果如下:
|
||||
```
|
||||
[QUERY] Using LaTeX to perform OCR on the image.
|
||||
[LABELS] \forall x \in X , ( \alpha f ) ( x ) = \alpha f ( x )
|
||||
[RESPONSE] \forall x \in X , ( \alpha f ) ( x ) = \alpha f ( x )
|
||||
--------------------------------------------------
|
||||
[QUERY] Using LaTeX to perform OCR on the image.
|
||||
[LABELS] \pi \int _ { c } ^ { d } \{ g ( y ) \} ^ { 2 } d y
|
||||
[RESPONSE] \pi \int _ { c } ^ { d } \{ g ( y ) \} ^ { 2 } d y
|
||||
--------------------------------------------------
|
||||
[QUERY] Using LaTeX to perform OCR on the image.
|
||||
[LABELS] [ \frac 2 3 x ^ { \frac 3 2 } ] _ { 0 } ^ { 1 }
|
||||
[RESPONSE] [ \frac 2 3 x ^ { \frac 3 2 } ] _ { 0 } ^ { 1 }
|
||||
```
|
||||
|
||||
|
||||
## Moe模型
|
||||
|
||||
|
||||
训练脚本:
|
||||
```bash
|
||||
# 2 * 43GiB, 8s/it
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
NPROC_PER_NODE=2 \
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
megatron sft \
|
||||
--model OpenGVLab/InternVL3_5-30B-A3B \
|
||||
--save_safetensors true \
|
||||
--merge_lora false \
|
||||
--dataset 'AI-ModelScope/LaTeX_OCR:human_handwrite#5000' \
|
||||
--load_from_cache_file true \
|
||||
--tuner_type lora \
|
||||
--lora_rank 8 \
|
||||
--lora_alpha 32 \
|
||||
--target_modules all-linear \
|
||||
--sequence_parallel true \
|
||||
--freeze_llm false \
|
||||
--freeze_vit true \
|
||||
--freeze_aligner true \
|
||||
--packing true \
|
||||
--split_dataset_ratio 0.01 \
|
||||
--expert_model_parallel_size 2 \
|
||||
--moe_permute_fusion true \
|
||||
--moe_grouped_gemm true \
|
||||
--moe_shared_expert_overlap true \
|
||||
--moe_aux_loss_coeff 1e-3 \
|
||||
--micro_batch_size 1 \
|
||||
--global_batch_size 4 \
|
||||
--recompute_granularity full \
|
||||
--recompute_method uniform \
|
||||
--recompute_num_layers 1 \
|
||||
--finetune true \
|
||||
--cross_entropy_loss_fusion true \
|
||||
--lr 1e-4 \
|
||||
--lr_warmup_fraction 0.05 \
|
||||
--min_lr 1e-5 \
|
||||
--num_train_epochs 1 \
|
||||
--output_dir megatron_output/InternVL3_5-30B-A3B \
|
||||
--eval_steps 200 \
|
||||
--save_steps 200 \
|
||||
--max_length 2048 \
|
||||
--dataloader_num_workers 8 \
|
||||
--dataset_num_proc 8 \
|
||||
--no_save_optim true \
|
||||
--no_save_rng true \
|
||||
--attention_backend flash
|
||||
```
|
||||
|
||||
训练结束后,我们使用生成的HF格式权重对验证集进行推理:
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--adapters megatron_output/InternVL3_5-30B-A3B/vx-xxx/checkpoint-xxx \
|
||||
--attn_impl flash_attn \
|
||||
--stream true \
|
||||
--load_data_args true \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 512
|
||||
```
|
||||
@@ -0,0 +1,267 @@
|
||||
|
||||
# 快速开始
|
||||
|
||||
ms-swift引入了Megatron的并行技术来加速大模型的训练,包括数据并行、张量并行、流水线并行、序列并行,上下文并行,专家并行。支持Qwen3、Qwen3.5、Deepseek-R1、GLM4.5、GPT-OSS等模型的CPT/SFT/GRPO/DPO/KTO/RM。完整支持的模型可以参考[支持的模型与数据集文档](../Instruction/Supported-models-and-datasets.md)。
|
||||
|
||||
|
||||
| 方法 | 全参数 | LoRA | MoE | 多模态 | FP8 |
|
||||
| ------ | ------ | ---- | ----- | ----- | ----- |
|
||||
| 预训练 | ✅ | ✅| ✅ | ✅ | ✅ |
|
||||
| [指令监督微调](https://github.com/modelscope/ms-swift/tree/main/examples/megatron) | ✅ | ✅| ✅ | ✅ | ✅ |
|
||||
| [GRPO](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/grpo) | ✅ | ✅| ✅ | ✅ | ✅ |
|
||||
| [GKD](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/rlhf/gkd) | ✅ | ✅| ✅ | ✅ | ✅ |
|
||||
| [DPO](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/rlhf/dpo) | ✅ | ✅| ✅ | ✅ | ✅ |
|
||||
| [KTO](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/rlhf/kto) | ✅ | ✅| ✅ | ✅ | ✅ |
|
||||
| [RM](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/rlhf/rm) | ✅ | ✅| ✅ | ✅ | ✅ |
|
||||
| [Embedding](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/embedding) | ✅ | ✅| ✅ | ✅ | ✅ |
|
||||
| [Reranker](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/reranker) | ✅ | ✅| ✅ | ✅ | ✅ |
|
||||
| [序列分类](https://github.com/modelscope/ms-swift/tree/main/examples/megatron/seq_cls) | ✅ | ✅| ✅ | ✅ | ✅ |
|
||||
|
||||
|
||||
## 环境准备
|
||||
使用Megatron-SWIFT,除了安装swift依赖外,还需要安装以下内容:
|
||||
|
||||
```shell
|
||||
# transformer_engine
|
||||
# 若出现安装错误,可以参考该issue解决: https://github.com/modelscope/ms-swift/issues/3793
|
||||
pip install --no-build-isolation transformer-engine[pytorch] --no-cache-dir
|
||||
# cuda13
|
||||
pip install pybind11
|
||||
pip install git+https://github.com/NVIDIA/TransformerEngine.git@stable --no-build-isolation
|
||||
|
||||
# apex
|
||||
# 提示:Megatron-SWIFT可以在不含apex的环境下运行,额外设置`--gradient_accumulation_fusion false`即可。
|
||||
git clone https://github.com/NVIDIA/apex
|
||||
cd apex
|
||||
pip install -v --disable-pip-version-check --no-cache-dir --no-build-isolation --config-settings "--build-option=--cpp_ext" --config-settings "--build-option=--cuda_ext" ./
|
||||
|
||||
# mcore-bridge
|
||||
pip install mcore-bridge -U
|
||||
# 安装main分支
|
||||
# pip install git+https://github.com/modelscope/mcore-bridge.git
|
||||
|
||||
# 若使用多机训练,请额外设置`MODELSCOPE_CACHE`环境变量为共享存储路径
|
||||
# 这将确保数据集缓存共享,而加速预处理速度。
|
||||
# 注意:这步很关键,不然多机训练可能因随机性问题导致数据不一致而训练卡住。
|
||||
export MODELSCOPE_CACHE='/xxx/shared'
|
||||
|
||||
# flash_attn
|
||||
# 选择合适的版本进行安装:https://github.com/Dao-AILab/flash-attention/releases/tag/v2.8.3
|
||||
# 注意:请勿安装高于transformer_engine限制的最高版本:https://github.com/NVIDIA/TransformerEngine/blob/release_v2.10/transformer_engine/pytorch/attention/dot_product_attention/utils.py#L118
|
||||
MAX_JOBS=8 pip install "flash-attn==2.8.3" --no-build-isolation
|
||||
```
|
||||
|
||||
或者你也可以使用镜像:(历史镜像查看[这里](../GetStarted/SWIFT-installation.md#镜像))
|
||||
```
|
||||
# cu128
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.10.0-vllm0.17.1-modelscope1.34.0-swift4.0.3
|
||||
modelscope-registry.cn-beijing.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.10.0-vllm0.17.1-modelscope1.34.0-swift4.0.3
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.8.1-py311-torch2.10.0-vllm0.17.1-modelscope1.34.0-swift4.0.3
|
||||
|
||||
# cu129 (fp8 training)
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.9.1-py312-torch2.10.0-vllm0.19.1-modelscope1.35.4-swift4.1.3
|
||||
modelscope-registry.cn-beijing.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.9.1-py312-torch2.10.0-vllm0.19.1-modelscope1.35.4-swift4.1.3
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda12.9.1-py312-torch2.10.0-vllm0.19.1-modelscope1.35.4-swift4.1.3
|
||||
|
||||
# cu130 (fp8 training)
|
||||
modelscope-registry.cn-hangzhou.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda13.0.3-py312-torch2.11.0-vllm0.23.0-modelscope1.37.1-swift4.3.2
|
||||
modelscope-registry.cn-beijing.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda13.0.3-py312-torch2.11.0-vllm0.23.0-modelscope1.37.1-swift4.3.2
|
||||
modelscope-registry.us-west-1.cr.aliyuncs.com/modelscope-repo/modelscope:ubuntu22.04-cuda13.0.3-py312-torch2.11.0-vllm0.23.0-modelscope1.37.1-swift4.3.2
|
||||
```
|
||||
|
||||
推荐运行环境:
|
||||
| | 范围 | 推荐 | 备注 |
|
||||
|--------------|--------------|-------------|--------------------|
|
||||
| python | >=3.10 | 3.12 | |
|
||||
| cuda | | cuda12.8/13.0 | |
|
||||
| torch | >=2.0 | 2.8.0/2.11.0 | |
|
||||
| transformer-engine | >=2.3 | 2.16.0 | |
|
||||
| apex | | 0.1 | |
|
||||
| megatron-core | >=0.16,<0.20 | 0.17.1 | |
|
||||
| mcore-bridge | >=1.3.0 | 1.5.0 | |
|
||||
| flash-attn | | 2.8.3/4.0.0b15 | |
|
||||
| transformers | >=4.33 | 4.57.6/5.12.1 | |
|
||||
| modelscope | >=1.23 | | |
|
||||
| peft | >=0.11,<0.20 | | LoRA |
|
||||
| trl | >=0.15,<1.0 | | RLHF |
|
||||
|
||||
|
||||
## 快速入门案例
|
||||
|
||||
这里介绍使用2卡80GiB A100对Qwen2.5-7B-Instruct模型进行自我认知微调的快速入门案例,以下最佳实践可以在10分钟内完成。
|
||||
|
||||
### 传统方式
|
||||
|
||||
首先,我们需要将HF格式的权重转为Megatron格式:
|
||||
- 多卡权重转换:将`CUDA_VISIBLE_DEVICES=0`删除即可使用多卡权重转换。
|
||||
- 转换精度测试:`--test_convert_precision true`将测试转换精度。在MoE大型模型的转换时,该参数所需时间较长,且需要更多的内存消耗,可酌情去除。
|
||||
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift export \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--to_mcore true \
|
||||
--torch_dtype bfloat16 \
|
||||
--output_dir Qwen2.5-7B-Instruct-mcore \
|
||||
--test_convert_precision true
|
||||
```
|
||||
|
||||
然后,使用以下脚本进行训练,训练所需显存资源为2*80GiB:
|
||||
- 若使用多机训练,建议共享磁盘,并将`--output_dir`指定为相同的路径。
|
||||
```shell
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
NPROC_PER_NODE=2 \
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
megatron sft \
|
||||
--mcore_model Qwen2.5-7B-Instruct-mcore \
|
||||
--save_safetensors false \
|
||||
--dataset 'AI-ModelScope/alpaca-gpt4-data-zh#500' \
|
||||
'AI-ModelScope/alpaca-gpt4-data-en#500' \
|
||||
'swift/self-cognition#500' \
|
||||
--tensor_model_parallel_size 2 \
|
||||
--sequence_parallel true \
|
||||
--micro_batch_size 16 \
|
||||
--global_batch_size 16 \
|
||||
--recompute_granularity full \
|
||||
--recompute_method uniform \
|
||||
--recompute_num_layers 1 \
|
||||
--finetune true \
|
||||
--cross_entropy_loss_fusion true \
|
||||
--lr 1e-5 \
|
||||
--lr_warmup_fraction 0.05 \
|
||||
--min_lr 1e-6 \
|
||||
--num_train_epochs 1 \
|
||||
--output_dir megatron_output/Qwen2.5-7B-Instruct \
|
||||
--save_steps 100 \
|
||||
--max_length 2048 \
|
||||
--system 'You are a helpful assistant.' \
|
||||
--dataloader_num_workers 4 \
|
||||
--no_save_optim true \
|
||||
--no_save_rng true \
|
||||
--dataset_num_proc 4 \
|
||||
--model_author swift \
|
||||
--model_name swift-robot
|
||||
```
|
||||
|
||||
最后,将Megatron格式权重转为HF格式:
|
||||
- 注意:`--mcore_model`请指向`iter_xxx`的上级目录。默认会使用`latest_checkpointed_iteration.txt`中对应的checkpoint。
|
||||
- 若出现OOM,将`CUDA_VISIBLE_DEVICES=0`删除。若出现内存不足,请将`--test_convert_precision true`删除。
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift export \
|
||||
--mcore_model megatron_output/Qwen2.5-7B-Instruct/vx-xxx/checkpoint-xxx \
|
||||
--to_hf true \
|
||||
--torch_dtype bfloat16 \
|
||||
--output_dir megatron_output/Qwen2.5-7B-Instruct/vx-xxx/checkpoint-xxx-hf \
|
||||
--test_convert_precision true
|
||||
```
|
||||
|
||||
我们对生成的HF格式权重进行推理:
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--model megatron_output/Qwen2.5-7B-Instruct/vx-xxx/checkpoint-xxx-hf \
|
||||
--stream true \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
推理结果如下:
|
||||
```
|
||||
<<< who are you?
|
||||
I am a language model developed by swift, you can call me swift-robot. How can I assist you?
|
||||
```
|
||||
|
||||
Megatron Ray 训练,参考[Ray 文档](../Instruction/Ray.md)
|
||||
|
||||
### Mcore-Bridge【推荐】
|
||||
|
||||
Mcore-Bridge去除模型转换的繁琐过程。具体参考[Mcore-Bridge文档](./Mcore-Bridge.md)。
|
||||
|
||||
训练脚本:
|
||||
```bash
|
||||
PYTORCH_CUDA_ALLOC_CONF='expandable_segments:True' \
|
||||
NPROC_PER_NODE=2 \
|
||||
CUDA_VISIBLE_DEVICES=0,1 \
|
||||
megatron sft \
|
||||
--model Qwen/Qwen2.5-7B-Instruct \
|
||||
--save_safetensors true \
|
||||
--dataset 'AI-ModelScope/alpaca-gpt4-data-zh#500' \
|
||||
'AI-ModelScope/alpaca-gpt4-data-en#500' \
|
||||
'swift/self-cognition#500' \
|
||||
--tensor_model_parallel_size 2 \
|
||||
--sequence_parallel true \
|
||||
--micro_batch_size 16 \
|
||||
--global_batch_size 16 \
|
||||
--recompute_granularity full \
|
||||
--recompute_method uniform \
|
||||
--recompute_num_layers 1 \
|
||||
--finetune true \
|
||||
--cross_entropy_loss_fusion true \
|
||||
--lr 1e-5 \
|
||||
--lr_warmup_fraction 0.05 \
|
||||
--min_lr 1e-6 \
|
||||
--num_train_epochs 1 \
|
||||
--output_dir megatron_output/Qwen2.5-7B-Instruct \
|
||||
--save_steps 100 \
|
||||
--max_length 2048 \
|
||||
--system 'You are a helpful assistant.' \
|
||||
--dataloader_num_workers 4 \
|
||||
--no_save_optim true \
|
||||
--no_save_rng true \
|
||||
--dataset_num_proc 4 \
|
||||
--model_author swift \
|
||||
--model_name swift-robot
|
||||
```
|
||||
|
||||
我们对生成的safetensors格式权重进行推理:
|
||||
```shell
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
swift infer \
|
||||
--model megatron_output/Qwen2.5-7B-Instruct/vx-xxx/checkpoint-xxx \
|
||||
--stream true \
|
||||
--temperature 0 \
|
||||
--max_new_tokens 2048
|
||||
```
|
||||
|
||||
- 若要进行预训练,你可以使用`megatron pt`替代`megatron sft`,这将会使用生成式的template进行训练。
|
||||
- Megatron-SWIFT使用与ms-swift相同的dataset和template处理模块,因此同样支持packing、loss_scale、agent训练等技术。自定义数据集格式参考[自定义数据集文档](../Customization/Custom-dataset.md)。
|
||||
- **更多案例**:包括packing、多机、32K上下文、DPO、MoE模型、预训练,可以查看[这里](https://github.com/modelscope/ms-swift/tree/main/examples/megatron)。
|
||||
|
||||
|
||||
## 训练技巧
|
||||
- 增加训练吞吐量方法:使用packing(不要开启流式)、增加DP、减少重计算、增加计算通信overlap。MoE还可以通过丢弃tokens加速。
|
||||
- 并行技术选择:
|
||||
- Megatron-SWIFT的并行技术采用zero1(默认开启use_distributed_optimizer)+各种并行技术的组合。
|
||||
- DP的速度最快,但显存占用较多,使用其他并行技术以降低显存占用。
|
||||
- TP/EP通信量较大,尽量不跨节点(NVLink域内),跨节点建议使用PP/DP;专家层建议使用EP而不是ETP,ETP更节约显存,但速度较慢。
|
||||
- MoE 并行折叠:MoE 相关的并行组与 Dense 组分离。Attention使用 tp-cp-dp-pp 组,MoE 使用 etp-ep-dp-pp 组。
|
||||
- 权重转换并行数的选择:Megatron-SWIFT在mcore端使用torch_dist存储格式,训练时可以调整并行数,不需要在权重转化时指定并行数。
|
||||
- 关于日志打印:Megatron-SWIFT的日志在last rank进行打印,因为在PP并行中,只有last pp_rank拥有完整的信息。
|
||||
|
||||
## Benchmark
|
||||
|
||||
使用`megatron sft`和`swift sft`在单机八卡A800环境下进行Dense模型全参数8K上下文训练的速度对比如下:([shell](https://github.com/modelscope/ms-swift/blob/main/examples/megatron/benchmark/deepspeed.sh))
|
||||
|
||||
**Dense** Qwen2.5-14B:
|
||||
|
||||
| | Megatron-LM | Deepspeed-ZeRO2 | Deepspeed-ZeRO3 |
|
||||
| -------- | ----------- | ---------- | ---------- |
|
||||
| 训练速度 | 9.04s/it | 10.32s/it | 10.56s/it |
|
||||
| 显存占用 | 8\*64GB | 8\*80GB | 8\*58GB |
|
||||
|
||||
使用`megatron sft`和`swift sft`在双机16卡A800环境下进行MoE模型全参数8K上下文训练的速度对比如下:
|
||||
|
||||
**MoE** Qwen3-30B-A3B:
|
||||
|
||||
- 注意:其中,DeepSpeed测试结果在"transformers<5.0"环境下进行。在"transformers>5.0",可以通过`--experts_impl grouped_mm`加速训练。
|
||||
|
||||
| | Megatron-LM | DeepSpeed-ZeRO2 | DeepSpeed-ZeRO3 |
|
||||
| -------- | ----------- | --------------- | --------------- |
|
||||
| 训练速度 | 9.6s/it | - | 91.2s/it |
|
||||
| 显存使用 | 16 * 60GiB | OOM | 16 * 80GiB |
|
||||
|
||||
|
||||
## Megatron-SWIFT微信群
|
||||
|
||||
<img src="https://raw.githubusercontent.com/modelscope/ms-swift/main/docs/resources/wechat/megatron.png" width="250">
|
||||
@@ -0,0 +1,10 @@
|
||||
.. currentmodule:: {{ module }}
|
||||
|
||||
|
||||
{{ name | underline}}
|
||||
|
||||
.. autoclass:: {{ name }}
|
||||
:inherited-members:
|
||||
:members:
|
||||
|
||||
.. autogenerated from source/_templates/autosummary/class.rst
|
||||
@@ -0,0 +1,12 @@
|
||||
.. currentmodule:: {{ module }}
|
||||
|
||||
|
||||
{{ name | underline}}
|
||||
|
||||
.. autoclass:: {{ name }}
|
||||
:members:
|
||||
:special-members: __init__, __call__
|
||||
|
||||
..
|
||||
autogenerated from source/_templates/classtemplate.rst
|
||||
note it does not have :inherited-members:
|
||||
@@ -0,0 +1,14 @@
|
||||
.. currentmodule:: {{ module }}
|
||||
|
||||
|
||||
{{ name | underline}}
|
||||
|
||||
.. autoclass:: {{ name }}
|
||||
:members:
|
||||
:exclude-members: MAXBIT, MAXDIM
|
||||
:undoc-members:
|
||||
|
||||
|
||||
..
|
||||
autogenerated from source/_templates/sobolengine.rst
|
||||
note it has specific options
|
||||
@@ -0,0 +1,123 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
|
||||
# import sphinx_book_theme
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../../'))
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'swift'
|
||||
copyright = '2022-2025, Alibaba ModelScope'
|
||||
author = 'ModelScope Authors'
|
||||
version_file = '../../swift/version.py'
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
language = 'zh_CN'
|
||||
|
||||
|
||||
def get_version():
|
||||
with open(version_file, 'r', encoding='utf-8') as f:
|
||||
exec(compile(f.read(), version_file, 'exec'))
|
||||
return locals()['__version__']
|
||||
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
version = get_version()
|
||||
release = version
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.napoleon',
|
||||
'sphinx.ext.autosummary',
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinx_markdown_tables',
|
||||
'sphinx_copybutton',
|
||||
'myst_parser',
|
||||
]
|
||||
|
||||
# build the templated autosummary files
|
||||
autosummary_generate = True
|
||||
numpydoc_show_class_members = False
|
||||
|
||||
# Enable overriding of function signatures in the first line of the docstring.
|
||||
autodoc_docstring_signature = True
|
||||
|
||||
# Disable docstring inheritance
|
||||
autodoc_inherit_docstrings = False
|
||||
|
||||
# Show type hints in the description
|
||||
autodoc_typehints = 'description'
|
||||
|
||||
# Add parameter types if the parameter is documented in the docstring
|
||||
autodoc_typehints_description_target = 'documented_params'
|
||||
|
||||
autodoc_default_options = {
|
||||
'member-order': 'bysource',
|
||||
}
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
source_suffix = ['.rst', '.md']
|
||||
|
||||
# The master toctree document.
|
||||
root_doc = 'index'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ['build', 'source/.ipynb_checkpoints', 'source/api/generated', 'Thumbs.db', '.DS_Store']
|
||||
# A list of glob-style patterns [1] that are used to find source files.
|
||||
# They are matched against the source file names relative to the source directory,
|
||||
# using slashes as directory separators on all platforms.
|
||||
# The default is **, meaning that all files are recursively included from the source directory.
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
# html_theme = 'sphinx_book_theme'
|
||||
# html_theme_path = [sphinx_book_theme.get_html_theme_path()]
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
# html_css_files = ['css/readthedocs.css']
|
||||
|
||||
# -- Options for HTMLHelp output ---------------------------------------------
|
||||
# Output file base name for HTML help builder.
|
||||
|
||||
# -- Extension configuration -------------------------------------------------
|
||||
# Ignore >>> when copying code
|
||||
copybutton_prompt_text = r'>>> |\.\.\. '
|
||||
copybutton_prompt_is_regexp = True
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {'https://docs.python.org/': None}
|
||||
|
||||
myst_enable_extensions = [
|
||||
'amsmath',
|
||||
'dollarmath',
|
||||
'colon_fence',
|
||||
]
|
||||
@@ -0,0 +1,84 @@
|
||||
.. swift documentation file,
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Swift DOCUMENTATION
|
||||
========================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Get Started
|
||||
|
||||
GetStarted/SWIFT-installation.md
|
||||
GetStarted/Quick-start.md
|
||||
GetStarted/Web-UI.md
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Instruction
|
||||
|
||||
Instruction/Command-line-parameters.md
|
||||
Instruction/Pre-training-and-Fine-tuning.md
|
||||
Instruction/GRPO/index.rst
|
||||
Instruction/Distillation.md
|
||||
Instruction/RLHF.md
|
||||
Instruction/Inference-and-deployment.md
|
||||
Instruction/Sample.md
|
||||
Instruction/Evaluation.md
|
||||
Instruction/Export-and-push.md
|
||||
Instruction/Ray.md
|
||||
Instruction/Reinforced-Fine-tuning.md
|
||||
Instruction/Agent-support.md
|
||||
Instruction/Supported-models-and-datasets.md
|
||||
Instruction/Use-tuners.md
|
||||
Instruction/Frequently-asked-questions.md
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Megatron-SWIFT
|
||||
|
||||
Megatron-SWIFT/Quick-start.md
|
||||
Megatron-SWIFT/Command-line-parameters.md
|
||||
Megatron-SWIFT/LoRA-Training.md
|
||||
Megatron-SWIFT/Multimodal-Model.md
|
||||
Megatron-SWIFT/Mcore-Bridge.md
|
||||
Megatron-SWIFT/GRPO.md
|
||||
Megatron-SWIFT/GKD.md
|
||||
Megatron-SWIFT/Ascend.md
|
||||
Megatron-SWIFT/Custom-Model.md
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Customization
|
||||
|
||||
Customization/Architecture.md
|
||||
Customization/Custom-model.md
|
||||
Customization/Custom-dataset.md
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Best Practices
|
||||
|
||||
BestPractices/GRPO.md
|
||||
BestPractices/GRPO-Multi-Modal-Training.md
|
||||
BestPractices/GRPO-Code-Training.md
|
||||
BestPractices/Qwen3-Best-Practice.md
|
||||
BestPractices/Qwen3-VL-Best-Practice.md
|
||||
BestPractices/Qwen3_5-Best-Practice.md
|
||||
BestPractices/deepseek-v4.md
|
||||
BestPractices/MLLM-Registration.md
|
||||
BestPractices/Embedding.md
|
||||
BestPractices/Reranker.md
|
||||
BestPractices/Rapidly-Training-VL-model.md
|
||||
BestPractices/NPU-support.md
|
||||
BestPractices/Metax-support.md
|
||||
BestPractices/AMD-support.md
|
||||
BestPractices/More-Best-Practices.md
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
Reference in New Issue
Block a user