AutoDL中的LLaMA-Factory 使用 训练微调 llame3数据集 cmmlu
使用LLaMA-Factory微调训练Qwen2-VL-7B/Qwen2.5-VL-7B与视觉大模型数据集制作流程与训练评估
b站:https://www.bilibili.com/video/BV1KceNzoE87/
本文介绍了使用LLaMA-Factory框架微调Qwen3-4B-Instruct-2507模型的完整流程。内容包括:1) 环境安装与WebUI配置;2) 数据集制作与格式要求;3) 通过ModelScope下载Qwen3模型;4) 使用命令行进行LoRA微调训练,展示了训练参数与GPU使用情况;5) 模型导出方法;6) 最后对微调后的模型进行评估。整个过程在6块GPU上约15分钟完成训练,并提供了训练损失曲线等可视化结果。
1 LLaMA-Factory环境安装
LLaMA-Factory 安装
git clone https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
pip install -e ".[torch,metrics]"
# 检查环境是否安装成功。
llamafactory-cli version
启动WebUI界面,我修改端口号为6006,因为AutoDL用的这个端口号
GRADIO_SERVER_PORT=6006 llamafactory-cli webui
2 数据集制作
https://github.com/hiyouga/LLaMA-Factory/tree/main/data
需要的数据集格式参考如下:
https://github.com/hiyouga/LLaMA-Factory/blob/main/data/alpaca_en_demo.json
https://github.com/hiyouga/LLaMA-Factory/blob/main/data/alpaca_zh_demo.json
根据官方给的例子,本文所做的数据(文件名:hamful_jugement_train.json)如下:
将hamful_jugement_train.json放在/home/winstonYF/LLaMA-Factory/data中
准备好数据后,在dataset_info.json中添加内容
"hamful_jugement_train": {
"file_name": "hamful_jugement_train.json"
},
3 模型下载
这次微调的模型采用Qwen3-4B-Instruct-2507
模型地址
https://www.modelscope.cn/models/Qwen/Qwen3-4B-Instruct-2507
确保安装了modelscope
pip install modelscope
采用SDK方式下载
from modelscope import snapshot_download
# 指定模型的下载路径
cache_dir = '/home/winstonYF/LLaMA-Factory/model'
# 调用 snapshot_download 函数下载模型
model_dir = snapshot_download('Qwen/Qwen3-4B-Instruct-2507', cache_dir=cache_dir)
print(f"模型已下载到: {model_dir}")
4 使用命令进行训练 而非webui
由于采用的是服务器训练,所以不采用webui进行训练
训练命令
下面的命令是通过webui的Preview command自动生成的,只需要改改路径:
最后命令如下:
llamafactory-cli train \
--stage sft \
--do_train True \
--model_name_or_path /home/winstonYF/LLaMA-Factory/model/Qwen/Qwen3-4B-Instruct-2507 \
--preprocessing_num_workers 16 \
--finetuning_type lora \
--template qwen3_nothink \
--flash_attn auto \
--dataset_dir data \
--dataset hamful_jugement_train \
--cutoff_len 2048 \
--learning_rate 5e-05 \
--num_train_epochs 3.0 \
--max_samples 100000 \
--per_device_train_batch_size 2 \
--gradient_accumulation_steps 8 \
--lr_scheduler_type cosine \
--max_grad_norm 1.0 \
--logging_steps 5 \
--save_steps 100 \
--warmup_steps 0 \
--packing False \
--enable_thinking True \
--report_to none \
--output_dir saves/Qwen3-4B-Instruct-2507/lora/train_2025-08-18-17-28-44 \
--bf16 True \
--plot_loss True \
--trust_remote_code True \
--ddp_timeout 180000000 \
--include_num_input_tokens_seen True \
--optim adamw_torch \
--lora_rank 8 \
--lora_alpha 16 \
--lora_dropout 0 \
--lora_target all
看看6块GPU的占有情况(占满了):
看运行时间,显示的15分钟运行完毕,这个速度还是挺快的。
训练完成,还是很快的
训练时的损失值变化
导出模型命令
训练后导出模型
llamafactory-cli export \
--model_name_or_path /home/winstonYF/LLaMA-Factory/model/Qwen/Qwen3-4B-Instruct-2507\
--adapter_name_or_path /home/winstonYF/LLaMA-Factory/saves/Qwen3-4B-Instruct-2507/lora/train_2025-08-18-17-28-44 \
--template qwen3_nothink \
--trust_remote_code True \
--export_dir /home/winstonYF/LLaMA-Factory/model/Qwen/Qwen3-4B-Instruct-2507-F \
--export_size 3 \
--export_device cpu \
--export_legacy_format false
5 训练后的Qwen3模型评估
Qwen3 github:https://github.com/QwenLM/Qwen3
我们使用LLaMA-Factory训练,但是评估不用LLaMA-Factory,使用Qwen3来进行评估,首先就是Qwen3的安装。
https://github.com/QwenLM/Qwen2-VL
import json
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from sklearn.metrics import accuracy_score, recall_score, f1_score
def load_json_data(file_path):
"""加载JSON数据文件"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
print(f"错误:找不到文件 {file_path}")
return None
except json.JSONDecodeError:
print(f"错误:文件 {file_path} 不是有效的JSON格式")
return None
def main():
# 模型名称
model_name = "/home/winstonYF/LLaMA-Factory/model/Qwen/Qwen3-4B-Instruct-2507-F"
# 加载分词器和模型
print(f"正在加载模型 {model_name}...")
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype="auto",
device_map="auto"
)
model.eval() # 设置为评估模式
# 读取JSON文件路径
json_path = 'hamful_jugement_val.json'
data = load_json_data(json_path)
if not data:
return
# 初始化评估指标
total = 0
correct = 0
TP = 0 # 实际有害,模型预测有害
TN = 0 # 实际安全,模型预测安全
FP = 0 # 实际安全,模型预测有害
FN = 0 # 实际有害,模型预测安全
true_labels = []
pred_labels = []
print(f"开始评估,共 {len(data)} 条数据...")
# 处理每条数据
for i, item in enumerate(data, 1):
try:
# 拼接prompt
instruction = item.get("instruction", "")
input_content = item.get("input", "")
prompt = f"{instruction}{input_content}"
true_label = item.get("output", "").strip()
# 确保真实标签是有效的
if true_label not in ["Harmful", "Safe"]:
print(f"警告:第{i}条数据的output不是有效的标签,已跳过")
continue
# 准备模型输入
messages = [{"role": "user", "content": prompt}]
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
)
model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
# 生成模型输出
with torch.no_grad(): # 禁用梯度计算,节省内存
generated_ids = model.generate(
**model_inputs,
max_new_tokens=10, # 只需要简短输出
temperature=0.0, # 确定性输出
do_sample=False # 不采样,使用贪婪解码
)
# 解析模型输出
output_ids = generated_ids[0][len(model_inputs.input_ids[0]):].tolist()
content = tokenizer.decode(output_ids, skip_special_tokens=True).strip()
# 标准化模型输出
if "harmful" in content.lower():
pred_label = "Harmful"
elif "safe" in content.lower():
pred_label = "Safe"
else:
# 如果模型输出不是明确的Harmful/Safe,视为错误预测
pred_label = None
print(f"第{i}条数据:模型输出 '{content}' 不是有效的标签")
# 更新评估指标
if pred_label is not None:
total += 1
true_labels.append(1 if true_label == "Harmful" else 0)
pred_labels.append(1 if pred_label == "Harmful" else 0)
if true_label == pred_label:
correct += 1
if true_label == "Harmful":
TP += 1
else:
TN += 1
else:
if true_label == "Harmful":
FN += 1
else:
FP += 1
# 打印进度
if i % 10 == 0:
print(f"已完成 {i}/{len(data)} 条数据")
except Exception as e:
print(f"处理第{i}条数据时出错: {str(e)}")
continue
# 计算评估指标
if total == 0:
print("没有有效的数据用于评估")
return
accuracy = correct / total
# 使用sklearn计算召回率和F1分数(以Harmful为正类)
recall = recall_score(true_labels, pred_labels)
f1 = f1_score(true_labels, pred_labels)
# 输出评估结果
print("\n===== 评估结果 =====")
print(f"总数据量: {len(data)}")
print(f"有效评估数据量: {total}")
print(f"正确预测: {correct}")
print(f"准确率: {accuracy:.4f}")
print(f"召回率: {recall:.4f}")
print(f"F1分数: {f1:.4f}")
print("\n混淆矩阵:")
print(f"TP (真阳性): {TP}")
print(f"TN (真阴性): {TN}")
print(f"FP (假阳性): {FP}")
print(f"FN (假阴性): {FN}")
if __name__ == "__main__":
main()
总数据量: 250
有效评估数据量: 250
正确预测: 241
准确率: 0.9640
召回率: 0.9910
F1分数: 0.9607
混淆矩阵:
TP (真阳性): 110
TN (真阴性): 131
FP (假阳性): 8
FN (假阴性): 1