Unsloth大模型微调简明教程
在本文中,我们将介绍使用 Unsloth 库训练和微调语言模型的过程。我们将分解所提供代码的每个部分,解释其功能和用途。此外,我们还将提供进一步增强和优化项目的技巧。
1、安装和升级所需的库
在开始项目之前,必须安装和更新必要的库。
!pip install unsloth
!pip uninstall unsloth -y && pip install --upgrade --no-cache-dir --no-deps git+https://github.com/unslothai/unsloth.git
说明:
- 安装 Unsloth 库:第一个命令安装 unsloth 库。
- 升级 Unsloth 库:第二个命令卸载现有的 unsloth 安装并直接从 GitHub 存储库重新安装最新版本,确保你拥有最新的更新和功能。
技巧:
- 版本控制:定期检查和管理库版本以避免兼容性问题。
- 依赖管理:虽然
--no-deps
选项可以通过跳过依赖项来加快安装速度,但请确保在必要时手动管理所有必需的依赖项。
2、导入必要的模块
我们导入将在整个项目中使用的模块。
from unsloth import FastLanguageModel
import torch
from datasets import load_dataset
import os
import json
import re
import random
from sklearn.model_selection import train_test_split
说明:
FastLanguageModel
:来自 Unsloth 库的类,用于有效处理语言模型。torch
:用于深度学习操作的 PyTorch 库。datasets
:用于加载和管理数据集的库。os
、json
、re
、random
:用于系统操作、JSON 处理、正则表达式和随机操作的标准 Python 库。train_test_split
:来自 scikit-learn 的函数,用于将数据集拆分为训练集和验证集。
技巧:
- 模块化代码:在开头导入所有必要的模块可以提高可读性并简化调试。
3、设置配置参数
我们定义训练过程中将使用的配置设置。
# Configuration
max_seq_length = 2048
load_in_4bit = True # Efficient memory usage
dtype = None
# Directory to store checkpoints
checkpoint_dir = "/content/drive/MyDrive/Defense/outputs_Meta-Llama-3.1-8B-bnb-4bit"
说明:
max_seq_length
:模型可以处理的最大序列长度。load_in_4bit
:启用 4 位加载以高效使用内存。dtype
:数据类型配置(当前设置为 None)。checkpoint_dir
:将保存模型检查点的目录路径。
技巧:
- 内存管理:使用 4 位加载可显著减少内存消耗,允许在有限的硬件上训练更大的模型。
- 动态配置:考虑从外部文件(例如 JSON 或 YAML)加载配置参数,以获得更大的灵活性。
4、检查点管理函数
我们定义函数来管理训练期间的模型检查点。
def is_model_processed(model_name):
checkpoint_path = os.path.join(checkpoint_dir, model_name.split("/")[-1], "checkpoint-500", "trainer_state.json")
print(f"Checking checkpoint path: {checkpoint_path}")
return os.path.exists(checkpoint_path)
def mark_model_as_processed(model_name):
checkpoint_file = os.path.join(checkpoint_dir, f"{model_name.replace('/', '_')}.done")
print(f"Marking model as processed: {checkpoint_file}")
with open(checkpoint_file, 'w') as f:
f.write("")
说明:
is_model_processed
:通过验证 trainer_state.json 文件是否存在来检查特定模型的检查点是否存在。mark_model_as_processed
:通过创建 .done 文件将模型标记为已处理,表明模型已成功训练并保存。
提示:
- 状态跟踪:使用检查点文件有助于在发生中断时无缝恢复训练。
- 文件命名:替换模型名称中的特殊字符以防止文件路径出现问题。
5、数据预处理、验证和增强
我们对数据集进行预处理,以确保其干净、有效且经过增强,从而提高模型性能。
def preprocess_dataset(input_path, output_path, train_path, val_path, augmentation_factor=3):
print("Preprocessing, validating, and augmenting dataset...")
valid_entries = 0
def clean_text(text):
"""Normalize and clean text."""
text = re.sub(r"[^a-zA-Z0-9ğüşıöçĞÜŞİÖÇ.,!?\\-]", " ", text) # Remove unwanted characters
text = re.sub(r"\s+", " ", text).strip() # Remove extra spaces
return text.lower() # Normalize to lowercase
def augment_text(text):
"""Create variations of text for augmentation."""
synonyms = {
"highlight": ["emphasize", "focus on", "spotlight"],
"identify": ["detect", "recognize", "pinpoint"],
"discuss": ["elaborate on", "examine", "analyze"],
"important": ["crucial", "key", "essential"]
}
for word, replacements in synonyms.items():
if word in text:
text = text.replace(word, random.choice(replacements))
return text
augmented_data = []
with open(input_path, 'r', encoding='utf-8') as infile:
for line in infile:
try:
data = json.loads(line)
if 'instruction' in data and 'input' in data and 'output' in data:
cleaned_data = {
"instruction": clean_text(data.get("instruction", "")),
"input": clean_text(data.get("input", "")),
"output": clean_text(data.get("output", ""))
}
augmented_data.append(cleaned_data)
valid_entries += 1
for _ in range(augmentation_factor):
augmented_entry = {
"instruction": augment_text(cleaned_data['instruction']),
"input": augment_text(cleaned_data['input']),
"output": augment_text(cleaned_data['output'])
}
augmented_data.append(augmented_entry)
except json.JSONDecodeError:
print("Skipping invalid JSON line.")
print(f"Dataset preprocessing complete. Valid entries: {valid_entries}")
# Split into train and validation
train_data, val_data = train_test_split(augmented_data, test_size=0.2, random_state=42)
# Save datasets
with open(output_path, 'w', encoding='utf-8') as outfile:
for entry in augmented_data:
outfile.write(json.dumps(entry, ensure_ascii=False) + '\n')
with open(train_path, 'w', encoding='utf-8') as trainfile:
for entry in train_data:
trainfile.write(json.dumps(entry, ensure_ascii=False) + '\n')
with open(val_path, 'w', encoding='utf-8') as valfile:
for entry in val_data:
valfile.write(json.dumps(entry, ensure_ascii=False) + '\n')
print(f"Enhanced dataset saved to {output_path}. Train and validation sets saved to {train_path} and {val_path}.")
说明:
clean_text
:通过删除不需要的字符、多余的空格并将文本转换为小写来清理和规范化文本。augment_text
:通过用同义词替换特定单词来创建变体,从而增强数据集。preprocess_dataset
:读取输入数据集,清理和增强数据,将其拆分为训练和验证集,并保存处理后的数据。
提示:
- 数据清理:调整正则表达式以适应不同的语言或特定的数据集要求。
- 数据增强:结合更复杂的增强技术,例如释义或反向翻译,以增加数据多样性。
- 错误处理:增强错误日志记录以捕获有关有问题的数据条目的更多详细信息。
6、定义数据集路径和预处理
我们指定数据集的路径并执行预处理函数。
# Paths to dataset
dataset_input_path = "/content/drive/MyDrive/output.jsonl"
dataset_cleaned_path = "/content/drive/MyDrive/cleaned_dataset.jsonl"
train_dataset_path = "/content/drive/MyDrive/train_dataset.jsonl"
val_dataset_path = "/content/drive/MyDrive/val_dataset.jsonl"
# Preprocess the dataset
preprocess_dataset(dataset_input_path, dataset_cleaned_path, train_dataset_path, val_dataset_path, augmentation_factor=3)
说明:
dataset_input_path
:原始数据集路径。dataset_cleaned_path
:保存清理和增强数据集的路径。train_dataset_path
&val_dataset_path
:保存训练和验证子集的路径。preprocess_dataset
:使用指定路径和增强因子调用预处理函数。
提示:
- 数据管理:利用 Google Drive 等云存储解决方案高效处理大型数据集。
- 文件命名:使用描述性文件名来简化数据集跟踪和管理。
7、列出要进行微调的模型
我们定义了将进行微调的模型列表。
# List of models to try for fine-tuning
fourbit_models = [
"unsloth/Meta-Llama-3.1-8B-bnb-4bit",
"unsloth/Meta-Llama-3.1-8B-Instruct-bnb-4bit",
"unsloth/Meta-Llama-3.1-70B-bnb-4bit",
"unsloth/Meta-Llama-3.1-405B-bnb-4bit",
"unsloth/Mistral-Nemo-Base-2407-bnb-4bit",
"unsloth/Mistral-Nemo-Instruct-2407-bnb-4bit",
"unsloth/mistral-7b-v0.3-bnb-4bit",
"unsloth/mistral-7b-instruct-v0.3-bnb-4bit",
"unsloth/Phi-3.5-mini-instruct",
"unsloth/Phi-3-medium-4k-instruct",
"unsloth/gemma-2-9b-bnb-4bit",
"unsloth/gemma-2-27b-bnb-4bit",
]
说明:
此列表包含来自 Unsloth 的各种 4 位模型,这些模型将进行微调。这些模型的大小和配置各不相同,针对不同的任务进行了优化。
提示:
- 模型选择:根据任务的具体要求选择模型,在性能和计算资源之间取得平衡。
- 多样性:尝试不同的架构,以确定哪种架构最适合你的使用案例。
8、顺序加载和测试模型
我们遍历列表中的每个模型,加载它,并检查现有检查点以在可用的情况下恢复训练。
# Load and test models sequentially
for model_name in fourbit_models:
print(f"Processing model: {model_name}")
model_dir = os.path.join(checkpoint_dir)
print(f"Model directory: {model_dir}")
checkpoint_path = os.path.join(model_dir, "checkpoint-500", "trainer_state.json")
print(f"Checkpoint path: {checkpoint_path}")
if os.path.exists(checkpoint_path):
print(f"Resuming from checkpoint: {checkpoint_path}")
print(f"Files in checkpoint directory: {os.listdir(os.path.dirname(checkpoint_path))}")
model, tokenizer = FastLanguageModel.from_pretrained(
os.path.dirname(checkpoint_path),
max_seq_length=max_seq_length,
dtype=dtype,
load_in_4bit=load_in_4bit,
)
else:
print(f"Starting training for model: {model_name}")
model, tokenizer = FastLanguageModel.from_pretrained(
model_name=model_name,
max_seq_length=max_seq_length,
dtype=dtype,
load_in_4bit=load_in_4bit,
)
print(f"Loaded model: {model_name}")
说明:
- 模型处理循环:遍历 fourbit_models 列表中的每个模型。
- 检查点检查:验证当前模型是否存在检查点以恢复训练;否则,从头开始训练。
- 模型和标记器加载:使用 FastLanguageModel.from_pretrained 加载模型和标记器,可以从检查点加载,也可以直接从模型名称加载。
提示:
- 并行处理:考虑并行处理多个模型以节省时间,前提是有足够的计算资源。
- 检查点策略:实施强大的检查点策略,以最大限度地减少数据丢失并促进无缝训练恢复。
9、配置 LoRA 进行微调
我们将低秩自适应 (LoRA) 应用于模型以实现高效微调。
# LoRA Configuration for fine-tuning
model = FastLanguageModel.get_peft_model(
model,
r=16,
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",
random_state=42,
)
print(f"LoRA configuration done for model: {model_name}")
说明:
PEFT 模型:将 LoRA 应用于模型的选定模块,以实现高效微调。参数如下:
r
:LoRA 等级值,较低的值可节省内存。target_modules
:指定要适应哪些层。lora_alpha
:学习率的缩放因子。lora_dropout
:正则化的 Dropout 率。bias
:偏差配置设置。use_gradient_checkpointing
:启用梯度检查点以提高内存效率。random_state
:通过设置随机种子确保可重复性。
提示:
- LoRA 超参数:尝试不同的 r 和 lora_alpha 值,以找到性能和资源使用之间的最佳平衡。
- 层选择:谨慎选择要适应的层,因为这会显著影响性能和训练时间。
10、定义格式化提示
我们创建一个模板,以模型可以理解的方式格式化数据集示例。
# Define the formatting prompt
alpaca_prompt = """Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.
### Instruction:
{}
### Input:
{}
### Response:
{}"""
EOS_TOKEN = tokenizer.eos_token # Ensures proper sequence termination
def formatting_prompts_func(examples):
print("Formatting dataset prompts...")
instructions = examples.get("instruction", "")
inputs = examples.get("input", "")
outputs = examples.get("output", "")
texts = []
for instruction, input, output in zip(instructions, inputs, outputs):
text = alpaca_prompt.format(instruction, input, output) + EOS_TOKEN
texts.append(text)
return {"text": texts}
说明:
alpaca_prompt
:一个模板,它使用指令、输入和预期响应构造每个数据集示例。formatting_prompts_func
:将alpaca_prompt
应用于数据集中的每个示例,附加一个序列结束 (EOS) 标记以确保正确终止。
提示:
- 提示工程:尝试不同的提示结构,以确定哪种格式可产生最佳模型性能。
- 语言一致性:确保提示语言与数据集语言匹配,以保持一致性并提高模型理解。
11、加载和格式化训练和验证数据集
我们加载训练和验证数据集并应用格式化函数。
# Load the train and validation datasets
print(f"Loading train dataset from: {train_dataset_path}")
train_dataset = load_dataset("json", data_files=train_dataset_path, split="train")
print(f"Loading validation dataset from: {val_dataset_path}")
val_dataset = load_dataset("json", data_files=val_dataset_path, split="train")
train_dataset = train_dataset.map(formatting_prompts_func, batched=True)
val_dataset = val_dataset.map(formatting_prompts_func, batched=True)
print("Datasets loaded and formatted.")
说明:
load_dataset
:从 JSON 文件加载训练和验证数据集。map
:将formatting_prompts_func
分批应用于每个数据集,根据定义的提示格式化数据。
提示:
- 批处理:使用
batched=True
可加快大型数据集的处理速度。 - 数据验证:格式化后,进行快速检查以确保提示结构正确。
12、配置 SFTTrainer 进行训练
我们使用 SFTTrainer 设置训练配置来管理训练过程。
from trl import SFTTrainer
from transformers import TrainingArguments
from unsloth import is_bfloat16_supported
resume_checkpoint_dir = os.path.join(model_dir, "checkpoint-500")
print(f"Before Resuming from checkpoint: {resume_checkpoint_dir}")
if os.path.exists(os.path.join(resume_checkpoint_dir, "trainer_state.json")):
print(f"Resuming from checkpoint: {resume_checkpoint_dir}")
print(f"Checkpoint Files: {os.listdir(resume_checkpoint_dir)}")
resume_from_checkpoint = resume_checkpoint_dir
else:
print("No valid checkpoint found, starting from scratch.")
resume_from_checkpoint = None
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=train_dataset,
eval_dataset=val_dataset,
dataset_text_field="text",
max_seq_length=max_seq_length,
dataset_num_proc=2,
packing=False,
args=TrainingArguments(
per_device_train_batch_size=4,
gradient_accumulation_steps=8,
warmup_steps=50,
max_steps=1000,
save_steps=500,
save_total_limit=2,
learning_rate=3e-4,
fp16=not is_bfloat16_supported(),
bf16=is_bfloat16_supported(),
logging_steps=10,
optim="adamw_8bit",
weight_decay=0.01,
lr_scheduler_type="linear",
seed=42,
output_dir=model_dir,
report_to="none",
resume_from_checkpoint=resume_from_checkpoint, # Pass checkpoint path
),
)
说明:
SFTTrainer
:来自 trl 库的训练器类,专为监督微调而设计。TrainingArguments
:配置各种训练参数,如批量大小、学习率、优化器等。per_device_train_batch_size
:每个设备(GPU/CPU)的批量大小。gradient_accumulation_steps
:更新前累积梯度的步骤数。warmup_steps
:学习率调度程序的预热步骤数。max_steps
:总训练步骤数。save_steps
:保存检查点的频率。learning_rate
:优化器的学习率。fp16
和bf16
:混合精度训练选项,可加快计算速度并减少内存使用量。optim
:优化器类型(adamw_8bit 用于提高内存效率)。weight_decay
:正则化的权重衰减。lr_scheduler_type
:学习率调度程序的类型。seed
:用于重现性的随机种子。output_dir
:保存训练输出的目录。resume_from_checkpoint
:如果可用,从检查点恢复训练的路径。
提示:
- 超参数调整:调整学习率、批量大小和梯度累积步骤等超参数会显著影响模型性能。
- 混合精度训练:利用 FP16 或 BF16 可以加速训练并减少内存使用量,而不会牺牲模型性能。
- 检查点管理:定期保存检查点可以在发生中断时恢复训练并促进实验。
13、开始和完成模型训练
我们启动训练过程并监控其完成情况。
print("Starting training...")
trainer_stats = trainer.train(resume_from_checkpoint=resume_from_checkpoint)
print("Training completed.")
说明:
trainer.train
:开始训练过程。如果有检查点可用,则从该点恢复训练。trainer_stats
:包含与训练过程相关的统计数据和日志。
提示:
- 监控:使用 TensorBoard 或 Weights & Biases 等工具实时监控训练指标。
- 提前停止:当验证集上的性能停止改善时,通过停止训练来实现提前停止以防止过度拟合。
14、保存微调模型
训练后,我们保存微调模型和标记器以供将来使用。
# Save the fine-tuned model
print(f"Saving model to: {model_dir}")
model.save_pretrained(model_dir)
tokenizer.save_pretrained(model_dir)
print(f"Model {model_name} fine-tuned and saved to {model_dir}")
mark_model_as_processed(model_name)
print("----------------------------------------")
说明
save_pretrained
:将微调后的模型和 tokenizer 保存到指定目录。mark_model_as_processed
:创建一个 .done 文件,表示模型已成功处理并保存。
提示:
- 模型版本控制:实现版本控制系统,以跟踪不同的微调模型版本。
- 存储优化:使用压缩技术节省存储空间,尤其是对于大型模型。
15、增强项目的提示
要进一步改进你的项目,请考虑以下提示:
a) 扩展数据增强技术
通过结合更多样化的增强方法来增强数据集,例如改变句子结构或使用高级同义词替换。
b) 超参数优化
使用网格搜索或随机搜索等技术来找到超参数的最佳组合,这可以显著提高模型性能。
c) 模型并行化
对于训练大型模型,实现模型并行化技术以减少训练时间。这可以包括分布式训练或模型分片。
d) 性能监控和分析
集成 TensorBoard 或 Weights & Biases 等工具来监控和分析训练期间的模型性能。这有助于了解训练动态并尽早发现问题。
e) 尝试更多模型
尝试不同的模型架构和大小,以确定哪种最适合您的特定任务。随时了解最新模型以利用新进展。
f) 自动化微调过程
创建脚本或管道以自动化训练和微调过程。自动化可减少重复任务并最大限度地降低出错风险。
16、结束语
在本文中,我们彻底探讨了如何使用 Unsloth 库训练和微调语言模型。我们介绍了数据预处理、模型加载、LoRA 配置和训练过程。此外,我们还提供了各种技巧来增强和优化您的深度学习项目。我们希望本指南能成为你自己项目的宝贵资源。
深度学习项目可能很复杂,但通过采取循序渐进的方法并利用正确的工具,你可以取得成功的结果。像 Unsloth 这样的库简化了模型训练和微调的过程,使其更容易使用。我们祝愿你的项目取得成功!
原文链接:Deep Learning Project: Training and Fine-Tuning a Language Model with Unsloth
汇智网翻译整理,转载请标明出处