1k+ 小数据集 SFT + KTO 微调与评测完整攻略

1k+ 小数据集 SFT + KTO 微调与评测完整攻略

基于 Qwen3.5-9B + RTX 4090D (24GB) 实战经验总结

一、项目概述

1.1 目标

使用 1,000+ 条 高质量训练数据,通过 SFT + KTO 两阶段微调,让模型学会从需求描述中生成结构化测试用例(JSON 格式)。

1.2 最终成果(测试集评测)

指标 结果
格式正确率 100%
语义相似度(DeepSeek) 91.1/100
采纳率(≥80分) 86.0%
召回率 86.14%
准确率 85.68%
F1 0.859
隐藏需求覆盖 73.7%

✅ 结论:模型在未训练需求上表现出色,具备直接部署价值。

二、环境准备

2.1 硬件配置

组件 规格
GPU NVIDIA RTX 4090D (24GB) × 1
CPU 16 vCPU Intel Xeon Platinum 8352S
内存 62GB
系统盘 30GB(⚠️ 仅放系统,模型和数据放数据盘)
数据盘 50GB SSD

2.2 软件版本

组件 版本
CUDA 12.8
PyTorch 2.10.0
Transformers 5.3.0
ms-swift 4.4.2
vLLM 0.17.1(如兼容性问题可改用 Transformers 直接推理)

2.3 环境安装

复制代码
# 安装 ms-swift
pip install ms-swift

# 评测依赖(本地/云实例)
pip install openai sentence-transformers scikit-learn tqdm

2.4 ⚠️ 磁盘空间管理(关键)

复制代码
# 模型文件约 18-20GB,务必设置缓存到数据盘
export MODELSCOPE_CACHE=/your_data_disk/.cache/modelscope
export HF_HOME=/your_data_disk/.cache/huggingface
mkdir -p $MODELSCOPE_CACHE $HF_HOME

否则 30GB 系统盘会被模型下载撑爆,导致容器崩溃。

三、数据准备(阶段0)

3.1 数据格式

复制代码
{
  "messages": [
    {"role": "system", "content": "你的任务是帮我生成功能测试用例"},
    {"role": "user", "content": "模块:XXX,功能点:XXX..."},
    {"role": "assistant", "content": "[{\"testpoint\": \"...\", \"priority\": \"..\", \"steps\": \"...\", \"expectation\": \"...\"}]"}
  ]
}

3.2 数据拆分(⚠️ 关键)

复制代码
# 9:1 拆分
训练集: ~900 条 → 用于 SFT + KTO 训练
测试集: ~100 条 → 永久封印,仅用于最终评估

测试集绝不参与任何训练或生成过程,否则泛化评估失效。

四、阶段一:SFT(监督微调)

4.1 核心理念

让模型学会遵循指令、输出 JSON 格式。此阶段模型可能生成质量一般的测试用例,没关系------这正是阶段二(KTO)要解决的。

4.2 参数配置(1k 小数据集专属)

参数 说明
num_train_epochs 1 ~ 1.5 小数据集跑多轮必过拟合
learning_rate 1e-5 比常规略低
weight_decay 0.1 强正则化防过拟合
per_device_train_batch_size 1 9B模型+24GB显存,显存紧张
gradient_accumulation_steps 8 等效 batch_size = 8
max_length 2048 足够覆盖测试用例长度
max_new_tokens 1536 避免截断
bf16 true 节省显存

4.3 训练命令(ms-swift)

复制代码
CUDA_VISIBLE_DEVICES=0 \
swift sft \
    --model /path/to/Qwen3.5-9B \
    --tuner_type lora \
    --dataset /path/to/sft_train.json \
    --torch_dtype bfloat16 \
    --num_train_epochs 1 \
    --per_device_train_batch_size 1 \
    --learning_rate 1e-5 \
    --weight_decay 0.1 \
    --warmup_ratio 0.1 \
    --lora_rank 8 \
    --lora_alpha 32 \
    --target_modules all-linear \
    --gradient_accumulation_steps 8 \
    --save_steps 100 \
    --save_total_limit 2 \
    --logging_steps 10 \
    --max_length 2048 \
    --output_dir ./output_sft_v1 \
    --dataloader_num_workers 4

4.4 检查点选择

  • 训练完成后取 最后一个 checkpoint(如 checkpoint-134)。

  • 使用 swift export 合并 LoRA 权重为完整模型:

复制代码
swift export \
    --model /path/to/Qwen3.5-9B \
    --adapters ./output_sft_v1/checkpoint-134 \
    --output_dir ./model_merged \
    --merge_lora true
复制代码
 

|---------------|----------|----------|---------------|
| 指标 | 开始 | 结束 | 结论 |
| Loss | 1.297 | 0.946 | ✅ 稳步下降,没有过拟合 |
| Token Acc | 71.0% | 77.3% | ✅ 持续提升 |
| 显存占用 | 22.58 GB | 23.07 GB | ✅ 稳定,未超24GB上限 |
| 训练步数 | 134步 | 134步 | ✅ 跑满1个Epoch |

复制代码
 

五、阶段二:KTO 数据准备(动态打标)

5.1 核心理念

用 SFT 模型对训练集重新生成回答,对比生成结果与标准答案的相似度,自动打上 label: true/false 标签------这就是 KTO 所需的"好/坏"样本。

5.2 ⚠️ 关键原则

必须用 SFT 模型自己生成,不能用更强的 API 模型替换!

5.3 生成参数(高温制造差异)

参数 说明
temperature 0.9 强制模型产生变异,暴露弱点
max_new_tokens 1536 完整生成
top_p 0.9 适度多样化

5.4 评分与打标

  • 使用deepseek推理模型对原回答和SFT模型回答进行打分设置分数阈值,打标签false

    首先筛选json格式,若非json格式,打标签flase
    若为json格式,进行语义相似度打分,设置阈值筛选flase评分标准:

    • 90-100 分:测试用例覆盖的核心功能点几乎完全一致,测试场景设计合理且相似
    • 80-89 分:核心功能点覆盖完整,测试场景设计基本一致
    • 70-79 分:主要功能点有覆盖,但测试场景设计有差异
    • 60-69 分:部分功能点有覆盖,测试场景设计差异较大
    • 50-59 分:仅有少量功能点重叠
    • 0-49 分:测试用例差异很大,几乎没有重叠

5.5 数据输出格式

复制代码
{
  "messages": [...],
  "label": true   // 或 false
}

六、阶段三:KTO

6.1 核心理念

用二元标签(好/坏)让模型更倾向于生成 true 样本的风格,同时厌恶 false 样本中的错误模式。

6.2 参数配置(1k 小数据集专属)

参数 说明
num_train_epochs 2 KTO 收敛快
learning_rate 5e-5 比 SFT 低一个数量级
undesirable_weight 1.2 ⚠️ 小数据集需加大奖惩力度!
per_device_train_batch_size 2 根据显存调整

6.3 训练命令(ms-swift)

复制代码
CUDA_VISIBLE_DEVICES=0 \
swift rlhf \
    --rlhf_type kto \
    --model /root/autodl-tmp/model_merged \
    --template qwen \
    --tuner_type lora \
    --dataset '/root/autodl-tmp/data/kto-train.json' \
    --load_from_cache_file true \
    --split_dataset_ratio 0.07 \
    --num_train_epochs 2 \
    --per_device_train_batch_size 1 \
    --per_device_eval_batch_size 1 \
    --learning_rate 5e-5 \
    --lora_rank 8 \
    --lora_alpha 32 \
    --target_modules all-linear \
    --gradient_accumulation_steps 16 \
    --eval_steps 100 \
    --save_steps 100 \
    --save_total_limit 2 \
    --logging_steps 5 \
    --output_dir output \
    --warmup_ratio 0.05 \
    --dataloader_num_workers 4 \
    --dataset_num_proc 4 \
    --beta 0.1 \
    --undesirable_weight 1.2 

6.4 合并最终模型

|---------------|------------------------|
| 指标 | 数值 |
| 总步数 | 184 steps(2 epochs) |
| 训练时长 | 1小时37分45秒 |
| 训练集大小 | 1469 条 |
| 验证集大小 | 110 条 |
| 平均训练 Loss | 0.242 (良好) |
| 最佳验证 Loss | 0.291 (在 step 100) |
| 可训练参数量 | 21.64M(占全量 0.39%) |

使用 checkpoint-100****进行模型导出和推理

复制代码
swift export \
    --model /root/autodl-tmp/model_merged \
    --adapters /root/autodl-tmp/output/v5-20260813-202529/checkpoint-100 \
    --output_dir /root/autodl-tmp/model_kto_final \
    --merge_lora true

七、模型推理

7.1 方案选择

方案 速度 稳定性 推荐场景
Transformers 直接推理 慢(~2-3小时/1000条) ✅ 高 小批量、稳定性优先
vLLM API 服务 快(~10-20分钟) ⚠️ 需环境兼容 大批量、生产环境

7.2 Transformers 推理命令

使用之前准备好的未经训练的数据集进行推理,便于后续评测

复制代码
查看官方推荐,设置参数 官方推荐使用以下采样参数组合进行生成: 
  通用任务的思考模式:temperature=1.0, top_p=0.95, top_k=20, min_p=0.0, presence_penalty=1.5, repetition_penalty=1.0 
  精确编码任务(如 WebDev)的思考模式:temperature=0.6, top_p=0.95, top_k=20, min_p=0.0, presence_penalty=0.0, repetition_penalty=1.0 
  通用任务的指令(或非思考)模式:temperature=0.7, top_p=0.8, top_k=20, min_p=0.0, presence_penalty=1.5, repetition_penalty=1.0 
  推理任务的指令(或非思考)模式:temperature=1.0, top_p=0.95, top_k=20, min_p=0.0, presence_penalty=1.5, repetition_penalty=1.0 
请注意,不同推理框架对采样参数的支持情况可能有所不同。任务定位:属于“精确编码任务”
让模型从提问的需求中提取功能点并生成结构化的JSON数组,这本质上是信息提取 + 结构化输出
因此,参数设置如下:
temperature=0.1
top_p=0.95
top_k=20
min_p=0.0
presence_penalty=1.5
repetition_penalty=1.0

7.3 vLLM 推理(如果兼容)

复制代码
CUDA_VISIBLE_DEVICES=0 python -m vllm.entrypoints.openai.api_server \
    --model ./model_kto_final \
    --served-model-name testcase-generator \
    --dtype bfloat16 \
    --port 6006

八、评测体系

8.1 评测框架

使用 eval_results.jsonl(每条包含 expectedactual 数组),通过 DeepSeek API 进行语义评分。

8.2 核心指标

数据规模与格式

指标 计算方式
格式正确率 有效解析数 / 总样本数
平均测试点数 总测试点数 / 总样本数
生成/标准比例 生成总数 / 标准总数

语义质量(样本级)

指标 计算方式 阈值
平均相似度 所有样本 DeepSeek 评分的均值 ---
采纳率 得分 ≥ 80 的样本数 / 总样本数 80分

召回率与准确率(测试点级)

指标 计算方式 含义
召回率 匹配成功数 / 标准测试点总数 标准测试点被覆盖的比例
准确率 匹配成功数 / 生成测试点总数 生成测试点中有效的比例
F1 2 × P × R / (P + R) 综合平衡指标

隐藏需求检测

通过关键词匹配检测模型是否识别隐含约束:

复制代码
关键词: 权限、安全、兼容、性能、异常、边界、并发、超时、加密、解密、日志、缓存、隔离、容错

8.3 综合评级标准

复制代码
# 针对测试用例生成任务(低温度,高精度)
if precision > 0.9 and recall > 0.65 and adoption_rate > 0.8:
    ⭐⭐⭐ 优秀:可直接部署
elif precision > 0.8 and recall > 0.5 and adoption_rate > 0.7:
    ⭐⭐ 良好:基本可用,可优化
elif precision > 0.7 and recall > 0.35:
    ⭐ 一般:需要改进
else:
    ❌ 需改进:重新评估策略
以上评级标准基于本项目业务场景(测试用例生成)设定,供参考。

九、常见问题与避坑指南

9.1 vLLM 启动失败

现象:ValueError: Model architectures [...] are not supported

原因:vLLM 版本与 Transformers 版本不兼容。

解决:改用 Transformers 直接推理(稳定,虽然慢但兼容性好)。

9.2 磁盘空间不足

现象:系统盘 100%,容器异常。

解决:

  • MODELSCOPE_CACHEHF_HOME 指向数据盘。

  • 定期清理 ~/.cache

9.3 显存不足(OOM)

解决:

  • 降低 per_device_train_batch_size(从 4 降到 2 或 1)

  • 降低 max_length(从 2048 降到 1024)

  • 启用 bf16fp16

9.4 所有测试点都被标为 false

原因:生成内容与标准答案格式差异巨大(如自然语言 vs JSON)。

解决:

  1. 在 Prompt 中强制要求 JSON 格式输出

  2. 关闭思考模式

  3. 增加上下文长度

十、总结

阶段 核心动作 关键产出
阶段0(数据准备) 9:1 拆分,格式化为 messages sft_train.json + dev.json
阶段1(SFT) 1 轮 LoRA 微调 SFT 模型(checkpoint-134)
阶段2(动态打标) 高温生成 + 评分打标 KTO 数据(含 label)
阶段3(KTO) undesirable_weight=1.2+ 2 轮训练 KTO 最终模型
阶段4(推理) Transformers/vLLM 生成 generated.jsonl
阶段5(评测) DeepSeek 语义评分 + 贪心匹配 评测报告(召回率/准确率/F1)

核心心法:小数据集 = 防过拟合 + 强制挖负样本 + 加大奖惩。跑通全流程后,86% 的采纳率证明这套方案完全可行!🚀