新手必看:Unsloth框架快速上手指南,从安装到微调一气呵成
Unsloth框架实战指南:从LoRA微调到全量微调与继续预训练
一、LoRA微调实战
1.1 环境准备与模型加载
首先安装必要的依赖并加载预训练模型:
!pip install unsloth !pip install swanlab from unsloth import FastLanguageModel import torch # 模型配置 max_seq_length = 2048 dtype = None # 自动检测 load_in_4bit = True # 4位量化节省显存 # 加载DeepSeek-R1-Distill-Qwen-1.5B模型 model, tokenizer = FastLanguageModel.from_pretrained( model_name = "deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B", max_seq_length = max_seq_length, dtype = dtype, load_in_4bit = load_in_4bit, )1.2 创建LoRA适配器
为模型添加LoRA适配层,显著减少可训练参数:
model = FastLanguageModel.get_peft_model( model, r = 16, # LoRA秩 target_modules = [ "q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj", ], lora_alpha = 16, lora_dropout = 0, bias = "none", use_gradient_checkpointing = "unsloth", # 节省30%显存 random_state = 3407, use_rslora = False, loftq_config = None, )1.3 数据准备与格式化
准备训练数据集并格式化:
from datasets import load_from_disk # 加载清洗后的数据集 train_dataset = load_from_disk("cleaned_dataset_v4.0.0") def formatting_prompts_func(examples): """格式化训练数据""" texts = [] for instruction, question, response in zip( examples["instruction"], examples["question"], examples["response"] ): text = f"""以下是一个任务说明,配有提供更多背景信息的输入。 请写出一个恰当的回答来完成该任务。 在回答之前,请仔细思考问题,并按步骤进行推理,确保回答逻辑清晰且准确。 ### Instruction: {instruction} ### Question: {question} ### Response: {response}""" texts.append(text) return {"text": texts} # 应用格式化 formatted_dataset = train_dataset.map(formatting_prompts_func, batched=True)1.4 配置训练参数
设置训练参数和SwanLab回调:
from trl import SFTTrainer, SFTConfig from swanlab.integration.transformers import SwanLabCallback # 配置SwanLab回调 swanlab_callback = SwanLabCallback( project="trl_integration", experiment_name="DeepSeek-R1-Distill-Qwen-1.5B-SFT", description="LoRA微调实验", config={"framework": "🤗TRL"}, ) # 配置训练器 trainer = SFTTrainer( model = model, tokenizer = tokenizer, train_dataset = formatted_dataset, args = SFTConfig( dataset_text_field = "text", per_device_train_batch_size = 2, gradient_accumulation_steps = 4, warmup_steps = 5, max_steps = 30, # 小批量测试 learning_rate = 2e-4, logging_steps = 1, optim = "adamw_8bit", weight_decay = 0.01, lr_scheduler_type = "linear", seed = 3407, report_to = "none", ), callbacks=[swanlab_callback], )1.5 执行微调训练
开始LoRA微调:
trainer_stats = trainer.train() print(f"训练完成: {trainer_stats}")1.6 模型推理测试
微调后进行推理测试:
# 启用推理模式 FastLanguageModel.for_inference(model) # 测试函数 def test_model(question, temperature=0.6, top_p=0.95): prompt = f"""以下是一个任务说明,配有提供更多背景信息的输入。 请写出一个恰当的回答来完成该任务。 在回答之前,请仔细思考问题,并按步骤进行推理,确保回答逻辑清晰且准确。 ### Instruction: 您是一位具有高级电气系统分析、机械动力学和运动控制规划知识的工程专家。 请回答以下电气机械运动领域的技术问题。 ### Question: {question} ### Response: """ inputs = tokenizer([prompt], return_tensors="pt").to("cuda") outputs = model.generate( input_ids=inputs.input_ids, attention_mask=inputs.attention_mask, max_new_tokens=max_seq_length, temperature=temperature, top_p=top_p, use_cache=False, ) response = tokenizer.batch_decode(outputs) return response[0].split("### Response:")[1] # 测试示例 test_response = test_model("输送机械动力电机选择,首推哪类?") print(test_response)1.7 模型保存
保存微调后的模型:
# 保存为FP16精度 model.save_pretrained_merged( save_directory="DeepSeekR1-1.5B-finetuned-fp16", tokenizer=tokenizer, save_method="merged_16bit" ) # 保存为4位量化(节省存储) model.save_pretrained_merged( save_directory="DeepSeekR1-1.5B-finetuned-4bit", tokenizer=tokenizer, save_method="merged_4bit" ) # 导出为GGUF格式(适用于Ollama) model.save_pretrained_gguf("DeepSeekR1-1.5B-Q8_0", tokenizer)二、全量微调实战
2.1 全量微调配置
全量微调更新所有参数,需要更多显存:
# 加载模型(关闭量化) model, tokenizer = FastLanguageModel.from_pretrained( model_name="deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B", load_in_4bit=False, load_in_8bit=False, device_map="auto", max_seq_length=2048, dtype=None, full_finetuning=True # 开启全量微调 )2.2 训练参数配置
from transformers import TrainingArguments training_args = TrainingArguments( per_device_train_batch_size=2, gradient_accumulation_steps=4, warmup_steps=10, num_train_epochs=1, learning_rate=2e-5, fp16=not torch.cuda.is_bf16_supported(), bf16=torch.cuda.is_bf16_supported(), logging_steps=1, output_dir="outputs", optim="adamw_8bit", save_strategy="steps", save_steps=20, )2.3 执行全量微调
from trl import SFTTrainer trainer = SFTTrainer( model=model, tokenizer=tokenizer, train_dataset=formatted_dataset, dataset_text_field="text", max_seq_length=2048, args=training_args, callbacks=[swanlab_callback], ) trainer.train()三、继续预训练实战
3.1 继续预训练配置
# 设置环境变量 import os os.environ["UNSLOTH_RETURN_LOGITS"] = "1" # 加载模型并添加LoRA适配器 model, tokenizer = FastLanguageModel.from_pretrained( model_name="deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B", max_seq_length=2048, dtype=None, load_in_4bit=True, ) # 创建LoRA适配器(包含embedding层) model = FastLanguageModel.get_peft_model( model, r=16, target_modules=[ "q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj", "embed_tokens", "lm_head", # 添加embedding层 ], lora_alpha=32, lora_dropout=0, bias="none", use_gradient_checkpointing="unsloth", random_state=2507, use_rslora=True, )3.2 准备领域数据
# 定义EOS标记 EOS_TOKEN = tokenizer.eos_token # 创建继续预训练数据格式 cpt_prompt = """### question:{} ### answer:{} """ domain_data = [ {'q': '在机械臂的x、y轴运动场景中,应选择哪种电机?机械臂的x、y轴运动需要高精度位置控制和快速响应能力。', 'a': '答案'}, {'q': '输送线的动力电机选型应优先考虑什么类型?', 'a': '答案'}, ] # 格式化数据 dataset = [] for item in domain_data: dataset.append(cpt_prompt.format(item['q'], item['a']) + EOS_TOKEN)3.3 执行继续预训练
from unsloth import UnslothTrainer, UnslothTrainingArguments trainer = UnslothTrainer( model=model, tokenizer=tokenizer, train_dataset=dataset, dataset_text_field="text", max_seq_length=max_seq_length, args=UnslothTrainingArguments( per_device_train_batch_size=2, gradient_accumulation_steps=4, warmup_ratio=0.1, num_train_epochs=70, learning_rate=5e-5, embedding_learning_rate=1e-5, # embedding层使用更小的学习率 logging_steps=1, optim="adamw_8bit", weight_decay=0.01, lr_scheduler_type="linear", seed=2507, output_dir="outputs", report_to="none", ), callbacks=[swanlab_callback], ) trainer.train()3.4 指令微调
继续预训练后进行指令微调:
trainer = UnslothTrainer( model=model, tokenizer=tokenizer, train_dataset=formatted_dataset, # 使用指令微调数据 dataset_text_field="text", max_seq_length=max_seq_length, args=UnslothTrainingArguments( per_device_train_batch_size=2, gradient_accumulation_steps=4, warmup_ratio=0.1, num_train_epochs=5, learning_rate=5e-5, embedding_learning_rate=1e-5, logging_steps=1, optim="adamw_8bit", weight_decay=0.00, lr_scheduler_type="linear", seed=3407, output_dir="outputs", report_to="none", ), callbacks=[swanlab_callback], ) trainer.train()四、关键参数说明
4.1 训练参数
| 参数 | 说明 | 推荐值 |
|---|---|---|
max_seq_length | 控制输出长度,避免冗余 | 推文生成:128,报告摘要:512 |
temperature | 控制生成随机性 | 医疗诊断:0.2,创意写作:0.9 |
top_p | 动态选择概率累积词汇 | 代码生成:0.7,广告文案:0.95 |
top_k | 限制候选词数量 | 事实核查:3,研究综述:10 |
learning_rate | 学习率大小 | LoRA:2e-4,全量微调:2e-5 |
4.2 推理参数调整
# 不同场景的推理参数配置 inference_configs = { "严谨回答": {"temperature": 0.2, "top_p": 0.8}, "创意生成": {"temperature": 0.9, "top_p": 0.95}, "代码生成": {"temperature": 0.3, "top_p": 0.7}, "对话系统": {"temperature": 0.7, "top_p": 0.9}, }五、实验经验总结
5.1 炼丹火候把控
- 初始阶段:使用较低学习率(5e-5)进行"文火暖鼎"
- 中期阶段:待loss下降后适当增大batch size进行"武火急炼"
- 后期阶段:经过多个epoch(建议7个)后转为FP16量化"退阴符固形"
- 最终阶段:获得高精度模型"九转灵砂"
5.2 常见问题解决
问题1:训练时loss不下降
- 原因:模型容量不足、学习率不当、batch size问题、数据集质量
- 解决方案:
- 尝试2e-4到2e-5之间的学习率
- 调整batch size(1-4之间)
- 检查数据集质量和数据打乱
问题2:显存不足
- 解决方案:
- 使用4位量化(
load_in_4bit=True) - 启用梯度检查点(
use_gradient_checkpointing="unsloth") - 减小batch size和序列长度
- 使用4位量化(
问题3:过拟合
- 解决方案:
- 增加dropout率
- 使用权重衰减(
weight_decay=0.01) - 早停策略
5.3 平衡内存与效率
- 保持小Batch Size:单GPU场景推荐1-2,配合梯度累积
- 分阶段调试:先用小模型、短序列跑通流程
- 启用优化器分页:全量微调时显著降低显存峰值
5.4 实用技巧
- 代码训练提升推理:混合代码和数学数据进行训练
- CPT+LoRA+GRPO组合:继续预训练+LoRA微调+强化学习优化
- 渐进式训练:先领域适应,再指令微调
- 多阶段学习率:embedding层使用更小的学习率
六、最佳实践建议
6.1 数据准备
- 确保数据质量,清洗噪声数据
- 数据格式统一,符合模型输入要求
- 适当的数据增强,提高泛化能力
6.2 训练策略
- 从小规模实验开始,逐步扩大
- 监控训练过程,及时调整参数
- 使用SwanLab等工具记录实验
6.3 模型评估
- 定期在验证集上测试
- 人工评估生成质量
- 对比不同参数配置的效果
6.4 部署优化
- 选择合适的量化方案
- 优化推理速度
- 考虑模型蒸馏进一步压缩
通过本指南,您可以系统掌握使用Unsloth框架进行大语言模型微调的完整流程,从LoRA微调到全量微调,再到继续预训练,每个步骤都有详细的代码示例和最佳实践建议。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
