微调很重要
微调是指通过在特定任务的数据上进行进一步训练,预训练模型,使其能够完成特定任务。这个过程利用了模型已经学到的通用知识,并将其适应新任务的具体需求。
2. 调优背后的理论微调在机器学习中至关重要,尤其是在处理像LLaMA3这样的复杂模型时。它允许你将预训练的模型适应特定任务或领域,利用已有的知识,同时调整模型,使其在新的特定数据上表现出色。这一过程涉及几个核心概念,其中迁移学习是微调的基础。
2.1 微调的主要概念 2.1.1 迁移学习(即从一个任务中学习并应用到另一个相关任务中)迁移学习是一种在机器学习中非常强大的方法,它涉及将一个任务上预先训练好的模型调整以执行不同的但相关的任务。迁移学习的主要优势在于训练效率和性能提升,特别是在新任务的数据量较少的情况下。接下来我们深入探讨迁移学习的工作原理及其为何如此有效。
- 预训练模型:这些模型是在大规模数据集上为通用任务进行训练的。例如,BERT或GPT-3等模型通过大量文本数据的训练,能够理解语言模式和关系。此类模型捕捉了广泛的知识和特征,这些特征对各种任务都有用。
- 特征提取过程:在迁移学习过程中,预训练模型充当特征提取器。它已经学会了识别和表示数据中的复杂模式,这些模式可以被重用于不同的任务。模型的内部层已经学习到了能捕捉输入数据各种特征的表示,这些表示可以应用于新任务。
- 适应新任务的方式:要将预训练模型适应到新任务,通常会通过添加新层或调整现有层来修改模型。例如,在微调过程中,你可能会替换模型的输出层,以匹配新任务所需的具体类别或输出数量。模型其余的层已经学到了有用的特征,这些层将保持不变或以较小的学习率进行微调。
- 微调:微调是指在新的特定任务数据集上继续训练预训练模型的过程。微调通常以比初始训练阶段较小的学习率进行。这是因为你希望稍微调整模型以适应新任务,而不显著改变已学到的特征。微调帮助模型将其学到的知识专门化到新任务的细节,同时保留初始训练期间获得的一般特征。
- 迁移学习的好处如下:
- 减少训练时间:从头开始训练模型在计算上可能很昂贵且耗时。迁移学习通过利用预训练模型现有的知识大大缩短了训练时间。
- 提高性能:预训练模型在新任务上的表现通常更好,因为它们从大量和多样化的数据集中学习了有用的特征。这在新任务的小数据集上特别有用。
- 资源效率:迁移学习节省了计算资源需求和数据需求,使其成为许多机器学习问题的有效解决方案。
- 全模型微调:调整模型的所有层
- 逐层微调:对模型的特定层进行微调
- 参数高效微调 (PEFT):通过调整少量参数来适应模型,如低秩适应(LoRA)之类的技巧
学习率决定了模型权重调整的幅度,也就是模型在估计误差后进行调整的程度。较高的学习率可以加速训练过程,但可能带来不稳定性,而较低的学习率则能保证训练的稳定性,但会使收敛速度变慢。
2.2.2: 批量大小批量大小指的是每次迭代中使用的训练样本的数量。较大的批量大小可以提供更准确的梯度估计,但同时需要更多的内存。
2.2.3 纪元一个周期是指完整地走过一遍整个训练数据集。更多的周期可能会让模型训练得更好,但如果处理不当也可能让模型过拟合。
2.2.4 规范化例如 dropout 和权重衰减这样的技术用于通过减少模型对训练数据的记忆能力来防止模型过拟合。
2.3 高级调优技术 2.3.1 LoRA (低秩适应技术)LoRA 在模型架构中引入了低秩张量,允许仅细调少量参数,同时保持其余参数不变。
2.3.2 提示微调提示调整涉及修改给模型的输入提示,以在特定任务上取得更好的效果。
第三步 准备调整 3.1 搭建您的环境配置 3.1.1硬件需求- GPU与CPU: 更倾向于使用GPU,因为它们的并行处理能力可以显著加快训练速度。
- 云端解决方案: 像AWS和Google Cloud这样的平台提供了可扩展的资源,用于训练大型模型。
- 库(如PyTorch、Hugging Face Transformers等): 在实现微调时非常关键。
- 安装步骤: 确保所有必要的软件包都已正确安装和配置好。
选择一个适合您任务的预训练模型。例如,BERT通常用来做分类任务,而GPT模型则更适合文本生成任务。
3.2.2 配置设置- 最大序列长度: 定义了输入序列的最大长度。
- 精度: 指的是模型参数的数值精度,比如4位精度。
- 数据来源: 使用来自Hugging Face Datasets等仓库的数据集或自定义数据集。
- 数据格式: 确保数据符合任务所需的格式(例如,分类任务所需的输入-输出对)。
- 合成数据生成: 生成额外的数据以提升模型的鲁棒性。
- 平衡: 确保数据集的平衡性,避免模型出现偏见。
# 安装所需的库
# 安装 "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git"
!pip install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git"
# 安装指定版本的库,不安装依赖
!pip install --no-deps "xformers<0.0.27" "trl<0.9.0" peft accelerate bitsandbytes
# 安装gradio
!pip install gradio
- Unsloth: 该包用于模型微调和管理训练流程。
- Xformers: 提供 Flash Attention(闪存注意)及其他可优化训练的注意力机制的实现。
- TRL (Transformer 强化学习): 适用于高级训练技术。
- PEFT (参数高效微调): 用于像 LoRA 这样的方法,这些方法调整模型参数的一个子集。
- Accelerate: 一个加速库,用于促进分布式和混合精度训练。
- Bitsandbytes: 提供高效的量化和优化算法实现。
「让我们从unsloth导入FastLanguageModel并导入torch吧」
from unsloth import FastLanguageModel
import torch
说明:
**FastLanguageModel**
来自Unsloth: 这是用于加载和使用及操作语言模型的类。**torch**
: PyTorch库,用于张量运算和模型训练。
# 设置序列的最大长度为2048
max_sequence_length = 2048
# 数据类型设置为 None
dtype = None
# 加载4位模式设置为真
加载4位模式 = 真
解释:
**max_sequence_length**
:模型可以处理的输入序列的最大长度。正确设置此参数可以有效管理内存和性能。**dtype**
:模型参数的数据类型。如果设置为None
,则使用默认精度,也可以设置为torch.float16
进行半精度训练。**load_in_4bit**
:一个布尔值,用于决定是否以4位精度加载模型,以节省内存和计算资源。
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="unsloth/Meta-Llama-3.1-8B",
max_seq_length=max_sequence_length,
dtype=dtype,
load_in_4bit=load_in_4bit,
)
从预训练模型加载模型和分词器,设置模型名称为“unsloth/Meta-Llama-3.1-8B”,最大序列长度,数据类型和是否加载4位精度。
没有相关内容需要解释。
**model_name="unsloth/Meta-Llama-3.1-8B"**
:指定要使用的预训练模型。这里选择的是带有8亿参数的LLaMA(Meta AI的大规模语言模型)3.1版本。**max_seq_length**
:确保模型可以处理指定长度的序列。**dtype**
:设置模型权重的数据类型(比如,float16
用于混合精度)。**load_in_4bit**
:使用4位量化加载模型,以提高效率。
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="启用梯度检查点",
random_state=3047,
use_rslora=False,
loftq_config=None,
)
**FastLanguageModel.get_peft_model**
: 应用参数高效微调(PEFT)技术的方法。**r=16**
: LoRA 的秩参数。它控制用于适应的低秩矩阵的秩。**target_modules**
: LoRA 应用于这些模型模块的列表。这些模块处理模型的不同注意力和投影方面。**lora_alpha=16**
: LoRA 的缩放因子。它调整应用于模型的更新幅度。**lora_dropout=0**
: LoRA 的 dropout 率。设置为 0,表示没有 dropout。**bias="none"**
: 指定是否在微调中包含偏差项。设置为"不添加额外偏差"
。**use_gradient_checkpointing="unsloth"**
: 启用梯度检查点以在训练过程中节省内存。**random_state=3047**
: 随机数生成的种子,以确保可重复性。**use_rslora=False**
: 是否使用 RSLORA,LoRA 的高级版本。此处设置为False
。**loftq_config=None**
: 其他微调参数的配置(此处未使用)。
- 准备数据集这一步
from datasets模块导入load_dataset
dataset = load_dataset("MattCoddity/dockerNLcommands", split='train')
# 打印各列及其长度
print("各列及其长度:")
for column in dataset.column_names:
print(f"列: {column}, 长度: {len(dataset[column])}")
解释
**load_dataset("MattCoddity/dockerNLcommands", split='train')**
: 加载由 MattCoddity 提供的 Docker 命令数据集。参数split='train'
指定了我们加载的是数据集的训练部分。**dataset.column_names**
: 列出数据集的列名。**len(dataset[column])**
: 输出每一列的长度,这有助于我们了解数据集的结构。
def formatting_prompt_func(example):
# 格式化提示函数
return {
"input": example["input"],
"instruction": example["instruction"],
"output": example["output"]
}
dataset = dataset.map(formatting_prompt_func)
print(f"数据集信息: {dataset.features}")
# 使用批量处理
formatted_dataset = dataset.map(formatting_prompt_func, batched=True)
-
**formatting_prompt_func**
:用于格式化数据集中每个样本的函数。它提取相关的字段(input
,instruction
,output
)以满足模型的需求。 -
**dataset.map(formatting_prompt_func)**
:将格式化函数应用于数据集中的每个样本。 **dataset.features**
:显示格式化后数据集的特征。
docker_prompt = """下面是一个任务的指令,附带提供更多信息的输入,写一个回复来同意该指令。
### 指令:
{}
### 信息:
{}
### 回复:
{}"""
**docker_prompt**
:模型输入模板的范本,包含用于指令、输入和预期输出的占位符。
EOS_Token = tokenizer.eos_token # 结束标记,表示一个序列的结束
def formatting_prompt_func(examples):
instructions = examples["instruction"]
inputs = examples["input"]
outputs = examples["output"]
texts = []
for instruction, input, output in zip(instructions, inputs, outputs): # docker_prompt 格式化指令,instruction, input, output
text = docker_prompt.format(instruction, input, output) + EOS_Token
texts.append(text)
return {"texts": texts}
**EOS_Token**
: 表示序列结束的标记。**formatting_prompt_func(examples)**
: 格式化示例以匹配提示模板。该函数处理每个示例的指令、输入和输出,并生成相应的格式化文本。
从trl模块导入SFTTrainer
从transformers模块导入TrainingArguments
从unsloth模块导入is_bf16_supported
# 初始化训练参数
training_args = TrainingArguments(
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
warmup_steps=5,
max_steps=50,
learning_rate=2e-4,
fp16=not is_bf16_supported(), # fp16设置为is_bf16_supported()的相反值
logging_steps=1,
optim="adamw_8bit",
lr_scheduler_type="linear",
seed=3407,
output_dir="outputs",
)
**训练参数配置**
: 配置训练参数。**per_device_train_batch_size=2**
: 每个设备的批量大小。有助于内存管理。**gradient_accumulation_steps=4**
: 在更新模型权重前积累多步梯度。有助于使用更大的有效批量大小进行训练。**warmup_steps=5**
: 学习率从0平滑增加至设定值的步数。**max_steps=50**
: 总的训练步数。**learning_rate=2e-4**
: 优化器的设定学习率。**fp16=not is_bf16_supported()**
: 若不支持bfloat16,则启用混合精度训练。**logging_steps=1**
: 记录训练进度的频率。**optim="adamw_8bit"**
: 使用基于8位精度的AdamW。**lr_scheduler_type="linear"**
: 学习率调度器类型。**seed=3407**
: 用于可重复性的随机种子。**output_dir="outputs"**
: 保存模型检查点及日志的文件夹。
# 初始化trainer,使用SFTTrainer类,传入模型、训练参数、训练数据集和分词器
trainer = SFTTrainer(model=model, args=training_args, train_dataset=formatted_dataset, tokenizer=tokenizer)
解释:
**SFTTrainer**
:这是用于微调模型的训练类,它负责处理训练循环、梯度更新和日志记录。**model**
:待微调的模型。**args=training_args**
:先前设置的训练参数。**train_dataset=formatted_dataset**
:用于训练的数据集(格式化过的)。**tokenizer**
:文本编码用的分词器。
训练器开始训练。
解释:
**trainer.train()**
:使用配置好的训练器和参数开始训练。
# 保存模型和分词器到指定路径
model.save_pretrained("path_to_save_model")
tokenizer.save_pretrained("path_to_save_model")
解释:
**model.save_pretrained("path_to_save_model")**
:将模型保存到指定路径。**tokenizer.save_pretrained("path_to_save_model")**
:将词元化器也保存到同一路径。
从transformers导入AutoModelForCausalLM和AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("path_to_save_model")
tokenizer = AutoTokenizer.from_pretrained("path_to_save_model")
解释:
**AutoModelForCausalLM.from_pretrained("path_to_save_model")**
: 从保存位置加载微调后的模型。**AutoTokenizer.from_pretrained("path_to_save_model")**
: 从保存位置加载分词工具。
def 生成Docker命令(input_text):
inputs = tokenizer(input_text, return_tensors="pt")
outputs = model.generate(**inputs)
return tokenizer.decode(outputs[0], skip_special_tokens=True)
print(生成Docker命令("编写一个用于启动容器的Docker命令"))
(无内容)
-
**evaluate_model(input_text)**
: 评估模型对输入文本的响应的函数(evaluate_model(input_text))。 -
**tokenizer(input_text, return_tensors="pt")**
: 将输入文本分词并转换为PyTorch张量格式。 -
**model.generate(**inputs)**
: 生成模型的响应(model.generate(inputs))。 **tokenizer.decode(outputs[0], skip_special_tokens=True)**
: 将生成的标记解码为可读的文本(tokenizer.decode(outputs[0], skip_special_tokens=True))。
结尾
在这份全面的指南中,我们深入探讨了使用逐步方法微调语言模型的复杂流程。我们从设置必要的环境开始,包括安装所需软件包和导入必要库。然后,我们加载并配置一个预训练的模型,并利用参数高效的微调技术(如LoRA)。我们还介绍了如何利用这些技术。
接下来,我们探讨了准备数据集并进行格式化,然后设计了提示来指导模型的响应。详细说明了如何设置训练参数配置、构建训练器并启动训练。最后,我们讨论了如何保存经过微调的模型并评估其在新文本上的表现。
通过拆解每个代码块并解释其功能,本指南旨在帮助你全面理解微调过程。无论你是初学者还是经验丰富的实践者,本指南都提供了宝贵的见解和实用的技巧,帮助你更有效地微调语言模型。
微调可以让模型针对特定任务进行定制和优化,这能使得模型更加精准和高效。随着AI和NLP应用变得越来越重要,掌握微调技术对于开发顶尖解决方案非常重要。