AI 模型与平台

唯一需要的指南:使用 QLoRA 微调 Llama 3 或其他开源模型

mm
将 Unite.AI 添加到您在 Google 上的首选来源

微调大型语言模型(LLM)如 Llama 3,涉及使用特定任务的数据集来适应预训练模型。这一过程利用了模型的预先存在的知识,使其比从头开始训练更高效、更具成本效益。在本指南中,我们将逐步介绍使用 QLoRA(量化 LoRA)微调 Llama 3 的步骤,QLoRA 是一种参数高效的方法,能够最小化内存使用和计算成本。

微调概述

微调涉及几个关键步骤:

  1. 选择预训练模型:选择一个与您期望的架构相符的基础模型。
  2. 收集相关数据集:收集和预处理一个特定于任务的数据集。
  3. 微调:使用数据集来适应模型,以提高其在特定任务上的性能。
  4. 评估:使用定性和定量指标来评估微调模型的性能。

概念和技术

微调大型语言模型

微调大型语言模型

完整微调

完整微调 更新模型的所有参数,使其特定于新任务。这种方法需要大量计算资源,通常对于非常大的模型来说是不切实际的。

参数高效微调(PEFT)

PEFT 只更新模型的参数子集,减少内存要求和计算成本。这种技术防止灾难性遗忘,并保持模型的通用知识。

低秩适应(LoRA)和量化 LoRA(QLoRA)

LoRA 只微调几个低秩矩阵,而 QLoRA 将这些矩阵量化为更低的精度(例如 4 位而不是 8 位),进一步减少内存占用。

微调方法

  1. 完整微调:这涉及在任务特定数据集上训练模型的所有参数。虽然这种方法可以很有效,但它也计算成本高昂,需要大量内存。
  2. 参数高效微调(PEFT):PEFT 只更新模型的参数子集,使其更具内存效率。像低秩适应(LoRA)和量化 LoRA(QLoRA)这样的技术属于这一类别。

什么是 LoRA?

比较微调方法:QLoRA 通过 4 位精度量化和分页优化器增强 LoRA

比较微调方法:QLoRA 通过 4 位精度量化和分页优化器增强 LoRA

LoRA 是一种改进的微调方法,其中,相比微调预训练模型的所有权重,两个小矩阵被微调以近似较大的矩阵。这些矩阵构成了 LoRA 适配器。然后将该微调适配器加载到预训练模型中并用于推理。

LoRA 的关键优势:

  • 内存效率:LoRA 通过只微调小矩阵而不是整个模型来减少内存占用。
  • 可重用性:原始模型保持不变,多个 LoRA 适配器可以与其一起使用,实现处理多个任务的同时减少内存要求。

什么是量化 LoRA(QLoRA)?

QLoRA 将 LoRA 进一步量化为更低的精度(例如 4 位而不是 8 位),从而进一步减少内存占用和存储要求,同时保持与全精度模型相似的性能水平。

QLoRA 的关键优势:

  • 更大的内存效率:通过量化权重,QLoRA 大大减少了模型的内存和存储要求。
  • 保持性能:尽管精度降低,QLoRA 的性能仍然接近全精度模型的水平。

任务特定适应

在微调过程中,模型的参数根据新数据集进行调整,有助于模型更好地理解和生成与特定任务相关的内容。该过程保留了预训练期间获得的通用语言知识,同时将模型适应于目标领域的细微差别。

实践中的微调

完整微调与 PEFT

  • 完整微调:涉及训练整个模型,这可能计算成本高昂,需要大量内存。
  • PEFT(LoRA 和 QLoRA):只微调参数的子集,减少内存要求,防止灾难性遗忘,使其成为一种更高效的替代方案。

实现步骤

  1. 设置环境:安装必要的库并设置计算环境。
  2. 加载和预处理数据集:加载数据集并将其预处理为模型可以使用的格式。
  3. 加载预训练模型:加载带有量化配置的基础模型(如果使用 QLoRA)。
  4. 标记化:标记化数据集以准备训练。
  5. 训练:使用准备好的数据集微调模型。
  6. 评估:使用定性和定量指标评估模型的性能。

一步一步的 LLM 微调指南

设置环境

我们将使用 Jupyter 笔记本进行此教程。像 Kaggle 这样的平台提供免费的 GPU 使用,或者 Google Colab 是运行这些实验的理想选择。

1. 安装所需库

首先,确保安装了必要的库:

!pip install -qqq -U bitsandbytes transformers peft accelerate datasets scipy einops evaluate trl rouge_score

2. 导入库并设置环境


<p>import os
import torch
from datasets import load_dataset
from transformers import (
AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig, TrainingArguments,
pipeline, HfArgumentParser
)
from trl import ORPOConfig, ORPOTrainer, setup_chat_format, SFTTrainer
from tqdm import tqdm
import gc
import pandas as pd
import numpy as np
from huggingface_hub import interpreter_login</p>

<p># 禁用 Weights and Biases 日志记录
os.environ['WANDB_DISABLED'] = "true"
interpreter_login()</p>

3. 加载数据集

我们将使用 DialogSum 数据集进行此教程:

预处理数据集以满足模型的要求,包括应用适当的模板和确保数据格式适合微调​ (Hugging Face)​​ (DataCamp)​。


<p>dataset_name = "neil-code/dialogsum-test"
dataset = load_dataset(dataset_name)</p>

检查数据集结构:


print(dataset['test'][0])

4. 创建 BitsAndBytes 配置

要以 4 位格式加载模型:


<p>compute_dtype = getattr(torch, "float16")
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type='nf4',
bnb_4bit_compute_dtype=compute_dtype,
bnb_4bit_use_double_quant=False,
)</p>

5. 加载预训练模型

使用 Microsoft (MSFT ) 的 Phi-2 模型进行此教程:


<p>model_name = 'microsoft/phi-2'
device_map = {"": 0}
original_model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map=device_map,
quantization_config=bnb_config,
trust_remote_code=True,
use_auth_token=True
)</p>

6. 标记化

配置标记器:


<p>tokenizer = AutoTokenizer.from_pretrained(
model_name,
trust_remote_code=True,
padding_side="left",
add_eos_token=True,
add_bos_token=True,
use_fast=False
)
tokenizer.pad_token = tokenizer.eos_token</p>

微调 Llama 3 或其他模型

当微调像 Llama 3 这样的模型或其他最先进的开源 LLM 时,需要考虑特定的调整和要求,以确保最佳性能。以下是针对不同模型(包括 Llama 3、GPT-3 和 Mistral)的详细步骤和见解。

5.1 使用 Llama 3

模型选择:

  • 确保从 Hugging Face 模型中心获取正确的模型标识符。例如,Llama 3 模型可能在 Hugging Face 上标识为 meta-llama/Meta-Llama-3-8B
  • 确保请求访问并登录您的 Hugging Face 帐户(如果需要)以使用像 Llama 3 这样的模型​ (Hugging Face)​​

标记化:

  • 为 Llama 3 使用适当的标记器,确保它与模型兼容并支持所需的功能,如填充和特殊令牌。

内存和计算:

  • 微调像 Llama 3 这样的大型模型需要大量计算资源。确保您的环境(如强大的 GPU 设置)可以处理内存和处理要求。确保环境可以处理内存要求,可以通过使用 QLoRA 等技术来减少内存占用​ (Hugging Face 论坛)

示例:

model_name = 'meta-llama/Meta-Llama-3-8B'
device_map = {"": 0}
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True,
)
original_model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map=device_map,
quantization_config=bnb_config,
trust_remote_code=True,
use_auth_token=True
)

标记化:

根据具体用例和模型要求,确保正确的标记器配置,不要有冗余的设置。例如,use_fast=True 推荐用于更好的性能​ (Hugging Face)​​ (GitHub)

tokenizer = AutoTokenizer.from_pretrained(
model_name,
trust_remote_code=True,
padding_side="left",
add_eos_token=True,
add_bos_token=True,
use_fast=False
)
tokenizer.pad_token = tokenizer.eos_token

5.2 使用其他流行模型(例如 GPT-3、Mistral)

模型选择:

  • 对于像 GPT-3 和 Mistral 这样的模型,确保从 Hugging Face 模型中心或其他来源使用正确的模型名称和标识符。

标记化:

  • 与 Llama 3 类似,确保标记器正确设置并与模型兼容。

内存和计算:

  • 每个模型可能有不同的内存要求。根据需要调整环境设置。

示例(GPT-3):

model_name = 'openai/gpt-3'
device_map = {"": 0}
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True,
)
original_model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map=device_map,
quantization_config=bnb_config,
trust_remote_code=True,
use_auth_token=True
)

示例(Mistral):

model_name = 'mistral-7B'
device_map = {"": 0}
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True,
)
original_model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map=device_map,
quantization_config=bnb_config,
trust_remote_code=True,
use_auth_token=True
)

标记化考虑: 每个模型可能有独特的标记化要求。确保标记器与模型匹配并正确配置。

Llama 3 标记器示例:

tokenizer = AutoTokenizer.from_pretrained(
model_name,
trust_remote_code=True,
padding_side="left",
add_eos_token=True,
add_bos_token=True,
use_fast=False
)
tokenizer.pad_token = tokenizer.eos_token

GPT-3 和 Mistral 标记器示例:

tokenizer = AutoTokenizer.from_pretrained(
model_name,
use_fast=True
)

7. 使用零样本推理测试模型

使用示例输入评估基础模型:

from transformers import set_seed

<p>set_seed(42)
index = 10
prompt = dataset['test'][index]['dialogue']
formatted_prompt = f"Instruct: Summarize the following conversation.\n{prompt}\nOutput:\n"</p>

<p># 生成输出
def gen(model, prompt, max_length):
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_length=max_length)
return tokenizer.batch_decode(outputs, skip_special_tokens=True)</p>

<p>res = gen(original_model, formatted_prompt, 100)
output = res[0].split('Output:\n')[1]</p>

<p>print(f'INPUT PROMPT:\n{formatted_prompt}')
print(f'MODEL GENERATION - ZERO SHOT:\n{output}')

8. 预处理数据集

将对话-摘要对转换为提示:


<p>def create_prompt_formats(sample):
blurb = "Below is an instruction that describes a task. Write a response that appropriately completes the request."
instruction = "### Instruct: Summarize the below conversation."
input_context = sample['dialogue']
response = f"### Output:\n{sample['summary']}"
end = "### End"</p>

<p>parts = [blurb, instruction, input_context, response, end]
formatted_prompt = "\n\n".join(parts)
sample["text"] = formatted_prompt
return sample</p>

<p>dataset = dataset.map(create_prompt_formats)</p>

标记化格式化的数据集:


<p>def preprocess_batch(batch, tokenizer, max_length):
return tokenizer(batch["text"], max_length=max_length, truncation=True)</p>

<p>max_length = 1024
train_dataset = dataset["train"].map(lambda batch: preprocess_batch(batch, tokenizer, max_length), batched=True)
eval_dataset = dataset["validation"].map(lambda batch: preprocess_batch(batch, tokenizer, max_length), batched=True)</p>

9. 为 QLoRA 准备模型

准备模型以进行参数高效的微调:


<p>original_model = prepare_model_for_kbit_training(original_model)</p>

超参数及其影响

超参数在优化模型性能方面起着至关重要的作用。以下是一些需要考虑的关键超参数:

  1. 学习率:控制模型更新其参数的速度。高学习率可能导致更快的收敛,但可能会超过最优解。低学习率可以确保稳定的收敛,但可能需要更多的轮数。
  2. 批次大小:在模型更新其参数之前处理的样本数量。较大的批次大小可以提高稳定性,但需要更多的内存。较小的批次大小可能会导致训练过程中的噪音增加。
  3. 梯度累积步骤:此参数通过在多个步骤中累积梯度来模拟较大的批次大小,然后再执行参数更新。
  4. 轮数数量:模型处理整个数据集的次数。更多的轮数可以提高性能,但如果不正确管理,可能会导致过拟合。
  5. 权重衰减:一种正则化技术,用于防止过拟合,方法是惩罚较大的权重。
  6. 学习率调度器:在训练过程中调整学习率,以提高性能和收敛性。

根据模型和任务的具体要求,自定义训练配置,调整超参数,如学习率、批次大小和梯度累积步骤。例如,Llama 3 模型可能需要与较小模型不同的学习率​ (Weights & Biases)​​ (GitHub)

示例训练配置

orpo_args = ORPOConfig(
learning_rate=8e-6,
lr_scheduler_type="linear",max_length=1024,max_prompt_length=512,
beta=0.1,per_device_train_batch_size=2,per_device_eval_batch_size=2,
gradient_accumulation_steps=4,optim="paged_adamw_8bit",num_train_epochs=1,
evaluation_strategy="steps",eval_steps=0.2,logging_steps=1,warmup_steps=10,
report_to="wandb",output_dir="./results/",)

10. 训练模型

设置训练器并开始训练:

trainer = ORPOTrainer(
model=original_model,
args=orpo_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
tokenizer=tokenizer,)
trainer.train()
trainer.save_model("fine-tuned-llama-3")

评估微调模型

训练后,使用定性和定量方法评估模型的性能。

1. 人工评估

将生成的摘要与人工编写的摘要进行比较,以评估质量。

2. 定量评估

使用 ROUGE 等指标评估性能:


from rouge_score import rouge_scorer

<p>scorer = rouge_scorer.RougeScorer(['rouge1', 'rouge2', 'rougeL'], use_stemmer=True)
scores = scorer.score(reference_summary, generated_summary)
print(scores)</p>

常见挑战和解决方案

1. 内存限制

使用 QLoRA 有助于通过量化模型权重为 4 位来缓解内存问题。确保您有足够的 GPU 内存来处理批次大小和模型大小。

2. 过拟合

监控验证指标以防止过拟合。使用早期停止和权重衰减等技术。

3. 训练速度慢

通过调整批次大小、学习率和使用梯度累积来优化训练速度。

4. 数据质量

确保您的数据集是干净和预处理良好的。数据质量差可能会显著影响模型性能。

结论

使用 QLoRA 微调 LLM 是一种高效的方法,用于将大型预训练模型适应特定任务,同时降低计算成本。通过遵循本指南,您可以微调 PHI、Llama 3 或其他开源模型,以在特定任务上实现高性能。

我已经沉浸在了令人着迷的机器学习和深度学习世界中五年了。我的热情和专业知识让我为超过50个不同的软件工程项目做出了贡献,特别关注AI/ML。我的持续的好奇心也让我对自然语言处理产生了兴趣,这是一个我渴望进一步探索的领域。