链接一、环境配置 (恒源云以下配置 0.65¥/小时)
https://www.yuque.com/zetao-eazwc/wybf4d/nagnqlwasunz16q8
链接一、环境配置 (恒源云以下配置 0.65¥/小时)
- GPU:4070-12G * 1
- 内存:64G
- CPU:32 核
- 数据盘:50T
- PyTorch 2.4.1 版本
- CUDA 12.8.93 版本
- python 3.11 版本

# 创建新环境
conda create -n swift python=3.11 -y
# 激活环境
conda activate swift# MS-SWIFT
pip install uv
uv pip install -U ms-swift
# Transformers 和相关库
uv pip install -U "transformers>=5.3.0" "qwen_vl_utils>=0.0.14" peft liger-kernel
# Flash-Linear-Attention(请安装 main 分支)
uv pip install -U git+https://github.com/fla-org/flash-linear-attention
# Causal-Conv1d
pip install -U git+https://github.com/Dao-AILab/causal-conv1d --no-build-isolation
# Flash-Attention
uv pip install "flash-attn==2.8.3" --no-build-isolation
# DeepSpeed(多卡训练)
uv pip install deepspeed
# vLLM(推理/部署/强化学习)
uv pip install -U "vllm>=0.17.0"
# 强化学习训练需要覆盖 vLLM 默认版本
uv pip install -U "transformers==5.2.0"# 方法:查看 swift 命令行工具
swift sft --help
# 如果能显示帮助信息,说明安装成功二、数据集准备
1、数据格式
本次微调主要是以文本数据为主,微调数据集格式如下
{
"instruction": "判断输入的内容是否为疑问句,如果是疑问句则输出 true,否则输出 false",
"input": "神经网络是价值的。",
"output": "false",
"category": "non_question",
"language": "zh"
}2、 数据集分布
- 每种语言 6000 条数据,训练集 5400 条、验证集 600 条
- 35% 直接疑问句
- 20% 间接疑问句
- 10% 边界案例
- 35% 非疑问句
3、数据集制作
注意:这里的数据集里 true、false 不能按照 json 格式输出来定义,需要严格按照以下格式输出
{"instruction": "判断输入的内容是否为疑问句,如果是疑问句则输出 true,否则输出 false", "input": "神经网络是价值的。", "output": "false", "category": "non_question", "language": "zh"}
{"instruction": "判断输入的内容是否为疑问句,如果是疑问句则输出 true,否则输出 false", "input": "DNA导致了推动了发展。", "output": "false", "category": "non_question", "language": "zh"}
{"instruction": "判断输入的内容是否为疑问句,如果是疑问句则输出 true,否则输出 false", "input": "影响的量子纠缠很复杂。", "output": "false", "category": "non_question", "language": "zh"}
{"instruction": "判断输入的内容是否为疑问句,如果是疑问句则输出 true,否则输出 false", "input": "基因编辑与其他技术不同。", "output": "false", "category": "non_question", "language": "zh"}分别制作了中文、英文、日语三种语种数据集

三、微调前准备
- 基座模型:https://www.modelscope.cn/models/Qwen/Qwen3.5-4B
- https://www.modelscope.cn/models/Qwen/Qwen3-4B-Instruct-2507
四、微调开始
4.1、微调指令
CUDA_VISIBLE_DEVICES=0 swift sft --model /hy-tmp/baseModel/Qwen3.5-4B --dataset /hy-tmp/datasets/zh/train.jsonl --val_dataset /hy-tmp/datasets/zh/val.jsonl --tuner_type lora --lora_rank 8 --lora_alpha 32 --torch_dtype bfloat16 --num_train_epochs 2 --per_device_train_batch_size 1 --gradient_accumulation_steps 8 --output_dir /hy-tmp/qwen3.5-4b-question4.2、 指令参数释义
**<font style="color:rgb(6, 10, 38);">CUDA_VISIBLE_DEVICES=0</font>**
**<font style="color:rgb(6, 10, 38);">--model /hy-tmp/baseModel/Qwen3.5-4B</font>**
**<font style="color:rgb(6, 10, 38);">--dataset /hy-tmp/datasets/zh/train.jsonl</font>**
**<font style="color:rgb(6, 10, 38);">--val_dataset /hy-tmp/datasets/zh/validation.jsonl</font>**
**<font style="color:rgb(6, 10, 38);">--output_dir /hy-tmp/qwen3.5-4b-question</font>**
**<font style="color:rgb(6, 10, 38);">--tuner_type lora</font>**
<font style="color:rgb(6, 10, 38);">lora</font><font style="color:rgb(6, 10, 38);">qlora</font><font style="color:rgb(6, 10, 38);">lora</font><font style="color:rgb(6, 10, 38);">lora</font><font style="color:rgb(6, 10, 38);">full</font><font style="color:rgb(6, 10, 38);">freeze</font><font style="color:rgb(6, 10, 38);">dora</font>****<font style="color:rgb(6, 10, 38);">adapter</font><font style="color:rgb(6, 10, 38);">ia3</font>
**<font style="color:rgb(6, 10, 38);">--lora_rank 8</font>**
<font style="color:rgb(6, 10, 38);">8</font>
**<font style="color:rgb(6, 10, 38);">--lora_alpha 32</font>**
<font style="color:rgb(6, 10, 38);">rank</font>
**<font style="color:rgb(6, 10, 38);">--torch_dtype bfloat16</font>**
**<font style="color:rgb(6, 10, 38);">--num_train_epochs 2</font>**
**<font style="color:rgb(6, 10, 38);">--per_device_train_batch_size 1</font>**
**<font style="color:rgb(6, 10, 38);">--gradient_accumulation_steps 8</font>**
4.3、日志输出详解

- loss
损失值,衡量模型预测结果与真实答案之间差距的数值,数值越低越好
- grad_morm
梯度范围,衡量模型参数需要修正幅度的大小,亦可以理解为学习强度;数值越大则表示模型发现错误很大,正在剧烈调整参数;数值越小越接近 0 时表示模型自身觉得对了,无需修改了
- learning_rate
学习率,控制模型每次更新参数时的“步长“,是控制模型改错力度的指标,一般情况下任务难(例如复杂文章的生成)所需的学习率就要小点,慢慢学习,否则就会出现乱学的情况;任务简单(例如只要让模型做个 true、false 判断)学习率可以大些,因为输出的答案相对简单,不需要复杂学习
- token_acc
令牌准确率,表示模型预测正确的 Token 占总 Token 的比例,1 代表 100%正确
- epoch
训练轮数,表示整根数据集被模型完整扫描/读了一遍叫 1 个 Epoch
- global_step/max_steps
全局步数/总步数, 表示训练进度的计数器,例如 310/1350,这个表示总共有 1350 步,现在到了 310 轮
**- elapsed_time / remaining_time **
已用时间 / 剩余时间,表示训练已经花费的时间和预计还需的时间
**- memory(GiB) **
显存占用
**- train_speed(s/it) **
训练速度,表示
4.4、显存消耗情况(大概)
平均下来显存消耗量在 9.28G 左右

五、模型部署验证
CUDA_VISIBLE_DEVICES=0 swift infer --adapters /hy-tmp/qwen3.5-4b-question/vx-xxx/checkpoint-xxx \ --stream true \ --max_new_tokens 512import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
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-lora/vx-xxx/checkpoint-xxx'
model, processor = get_model_processor('qwen3.5-4b-question')
model = PeftModel.from_pretrained(model, adapter_dir)
template = get_template(processor)
engine = TransformersEngine(model, template=template)
infer_request = InferRequest(messages=[{
"role": "user",
"content": '你好,你是谁?',
}])
request_config = RequestConfig(max_tokens=256, temperature=0.7)
resp_list = engine.infer([infer_request], request_config=request_config)
response = resp_list[0].choices[0].message.content
print(response)CUDA_VISIBLE_DEVICES=0 \swift deploy \ --model output/qwen3.5-4b-lora \ --adapters output/qwen3.5-4b-lora/vx-xxx/checkpoint-xxx \ --port 8000okiu0-
七、模型结果合并导出
1、导出成“完整模型目录”
前最终 checkpoint 假设是:
/hy-tmp/qwen3.5-4b-question/v1-20260330-191038/checkpoint-1350直接执行:
CUDA_VISIBLE_DEVICES=0 \
swift export \
--adapters /hy-tmp/qwen3.5-4b-question/v1-20260330-191038/checkpoint-1350 \
--merge_lora true \
--output_dir /data/models/Qwen3.5-4B-Question-QTJ通过以上方式把 LoRA adapter 合并成一个完整权重目录
2、导出完成后目录验证
示例结构:
/data/models/Qwen3.5-4B-Question-QTJ
├── config.json
├── configuration.json # 某些模型会有
├── generation_config.json
├── tokenizer.json
├── tokenizer_config.json
├── special_tokens_map.json # 可能有
├── merges.txt # BPE tokenizer 常见
├── vocab.json # BPE tokenizer 常见
├── model-00001-of-0000N.safetensors
├── model-00002-of-0000N.safetensors
├── ...
└── model.safetensors.index.json3、验收命令
ls -lh /data/models/Qwen3.5-4B-Question-QTJ再重点检查这几个文件是否存在:
ls /data/models/Qwen3.5-4B-Question-QTJ/config.json
ls /data/models/Qwen3.5-4B-Question-QTJ/tokenizer_config.json
ls /data/models/Qwen3.5-4B-Question-QTJ/generation_config.json
ls /data/models/Qwen3.5-4B-Question-QTJ/model.safetensors.index.json如果你看到的还是:
adapter_model.safetensors
adapter_config.json那说明你拿到的还是 LoRA 补丁,不是完整模型。
4、用合并完的目录启动 vLLM
导出成功后采用以下命令进行启动验证
python3 -m vllm.entrypoints.openai.api_server \
--tensor-parallel-size 1 \
--dtype bfloat16 \
--max-num-seqs 5 \
--enable-chunked-prefill \
--max-num-batched-tokens 18192 \
--gpu-memory-utilization 0.9 \
--max-model-len 32768 \
--host 0.0.0.0 \
--port 3800 \
--served-model-name qwen3.5-4b-question \
--model /data/models/Qwen3.5-4B-Question-QTJ \
--disable-fastapi-docs5、启动后检查
5.1 模型列表
curl http://127.0.0.1:3800/v1/models5.2 聊天测试
curl http://127.0.0.1:3800/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3.5-4b-question",
"messages": [
{"role": "user", "content": "解释一下量子纠缠。"}
],
"temperature": 0.7,
"top_p": 0.9,
"max_tokens": 256
}'