对 Falcon 7B 等大型语言模型进行微调是一项资源密集型的复杂任务。低阶适应(Low-Rank Adaptation,LoRA)和参数高效微调(Parameter-Efficient Fine-Tuning,PEFT)等技术提供了高效的处理方法。LoRA 通过引入低阶矩阵,减少了可训练参数的数量,从而降低了微调过程的计算要求。而 PEFT 则侧重于选择性地调整一小部分模型参数,从而保留了大部分预训练模型的知识,同时还能使其适应特定任务。LoRA 和 PEFT 共同为 Falcon 7B 的微调提供了一种有效而经济的方法,无需大量计算资源就能为特定应用定制大型模型。
设置环境
请按照相应 GitHub repo 中的详细说明进行操作。
数据准备
"""
load_data.py
"""
import json
import pandas as pd
with open('data/train-v2.0.json', 'r') as file:
squad_data = json.load(file)
questions = []
answers = []
total_questions = sum(len(paragraph['qas']) for article in squad_data['data'] for paragraph in article['paragraphs'])
percent = 0.0001 # Change this to modify the dataset size
target_questions = int(percent * total_questions)
collected_questions = 0
for article in squad_data['data']:
for paragraph in article['paragraphs']:
context = paragraph['context']
for qa in paragraph['qas']:
if collected_questions >= target_questions:
break
question = qa['question']
if 'answers' in qa and len(qa['answers']) > 0:
answer = qa['answers'][0]['text']
questions.append(question)
answers.append(answer)
collected_questions += 1
if collected_questions >= target_questions:
break
if collected_questions >= target_questions:
break
df_squad = pd.DataFrame({
'question': questions,
'answer': answers
})
"""
data_preperation.py
"""
def gen_prompt(text_input):
return f"""
<human>: {text_input["question"]}
<assistant>: {text_input["answer"]}
""".strip()
def gen_and_tok_prompt(text_input, tokenizer):
full_input = gen_prompt(text_input)
tok_full_prompt = tokenizer(full_input, padding=True, truncation=True, return_tensors="pt")
return {
'input_ids': tok_full_prompt['input_ids'][0],
'attention_mask': tok_full_prompt['attention_mask'][0]
}
def tokenize_and_check_length(batch, tokenizer):
tokenized_batch = [gen_and_tok_prompt(x, tokenizer) for x in batch]
input_ids_lengths = [len(x['input_ids']) for x in tokenized_batch]
attention_mask_lengths = [len(x['attention_mask']) for x in tokenized_batch]
assert all(length == input_ids_lengths[0] for length in input_ids_lengths), "Inconsistent lengths in input_ids"
assert all(length == attention_mask_lengths[0] for length in attention_mask_lengths), "Inconsistent lengths in attention_mask"
input_ids = [x['input_ids'].tolist() for x in tokenized_batch]
attention_mask = [x['attention_mask'].tolist() for x in tokenized_batch]
return {
'input_ids': input_ids,
'attention_mask': attention_mask
}
def tokenize_dataset(data, tokenizer):
tokenizer.pad_token = tokenizer.eos_token
return data.map(lambda x: tokenize_and_check_length([x], tokenizer), batched=True, remove_columns=["question", "answer"])
下面是上述代码的简要说明:
参数高效微调(PEFT)
传统的训练方法无法处理大型语言模型(LLM)的纯 4 位量化参数。不过,通过利用最先进的参数高效微调(PEFT)方法,你可以使用特定任务适配器有效地训练这些模型。这种方法在最初的 QLoRA 论文中有详细介绍,它可以在不需要全精度参数的情况下进行高效微调。Hugging Face 提供了一个官方支持的 PEFT 库来促进这一过程。
在本项目/文章中,PEFT 使用通过 LoraConfig 对象配置的 LoRA 适配器。通过指定 r、lora_alpha 和 target_modules(如 query_key_value)等参数,可以针对特定任务(如因果语言建模(task_type="CAUSAL_LM"))对模型进行微调,甚至可以量化权重。这确保了训练的效率和效果,优化了模型在下游任务中的表现。
低等级自适应(LoRA)
LoraConfig 对象用于配置 LoRA(低秩自适应)适配器,这是一种通过重新参数化层矩阵权重来微调语言模型的技术。这可以提高模型在特定下游任务中的性能。
参数
from peft import LoraConfig
def prepare_model_for_training(model):
model.gradient_checkpointing_enable()
model = prepare_model_for_kbit_training(model)
config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["query_key_value"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
return get_peft_model(model, config)
训练流水线
训练循环如下所示
"""
main.py
"""
import sys
import pandas as pd
from datasets import Dataset
from data_preparation import tokenize_dataset
from model_utils import load_model_and_tokenizer, prepare_model_for_training, save_model, print_trainable_parameters
from training import fine_tune_model
from load_data import df_squad
import numpy as np
import torch
from datetime import datetime
print(f"NumPy version: {np.__version__}")
print(f"CUDA is available: {torch.cuda.is_available()}")
df_faq = df_squad # Check Git Repo to see how this is obtained.
data = Dataset.from_pandas(df_faq[['question', 'answer']])
model_name = "tiiuae/falcon-7b-instruct"
model, tokenizer = load_model_and_tokenizer(model_name)
if torch.cuda.is_available():
model.cuda()
data = tokenize_dataset(data, tokenizer)
model = prepare_model_for_training(model)
print_trainable_parameters(model)
training_args = {
"gradient_accumulation_steps": 4,
"num_train_epochs": 3,
"learning_rate": 2e-4,
"fp16": True,
"save_total_limit": 4,
"logging_steps": 25,
"output_dir": "output_dir", # give the location where you want to store checkpoints
"save_strategy": 'epoch',
"optim": "paged_adamw_8bit",
"lr_scheduler_type": 'cosine',
"warmup_ratio": 0.05,
"per_device_train_batch_size": 1, # Adjust based on your GPU memory
# "device": "cuda" if torch.cuda.is_available() else "cpu"
}
fine_tune_model(model, tokenizer, data, training_args)
current_time = datetime.now().strftime('%Y%m%d_%H%M%S')
save_model(model, f'trained_models/MODEL_{current_time}')
"""
training.py
"""
from transformers import Trainer, TrainingArguments, DataCollatorForLanguageModeling
import torch
def fine_tune_model(model, tokenizer, data, training_args):
training_args = TrainingArguments(**training_args)
trainer = Trainer(
model=model,
train_dataset=data,
args=training_args,
data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False),
)
model.config.use_cache = False
if torch.cuda.is_available():
model.cuda()
trainer.train()
上述代码描述了使用 LoRA 对猎鹰 7B 进行微调的训练流水线。该流水线利用高效的训练技术来优化 GPU 内存和资源的使用。
推理
以下代码用于推理:
"""
question_answer.py
"""
from transformers import AutoTokenizer, AutoModelForCausalLM
from peft import PeftConfig, PeftModel
import torch
def perform_inference(model_path, prompt):
config = PeftConfig.from_pretrained(model_path)
model = AutoModelForCausalLM.from_pretrained(config.base_model_name_or_path, trust_remote_code=True)
tokenizer = AutoTokenizer.from_pretrained(config.base_model_name_or_path)
model_inf = PeftModel.from_pretrained(model, model_path)
encoding = tokenizer(prompt, return_tensors="pt").to(model.device)
gen_config = model_inf.generation_config
gen_config.max_new_tokens = 200
gen_config.temperature = 0.2
gen_config.top_p = 0.7
gen_config.num_return_sequences = 1
gen_config.pad_token_id = tokenizer.eos_token_id
gen_config.eos_token_id = tokenizer.eos_token_id
with torch.inference_mode():
outputs = model.generate(input_ids=encoding.input_ids, attention_mask=encoding.attention_mask, generation_config=gen_config)
return tokenizer.decode(outputs[0], skip_special_tokens=True)
while True:
query = input("Please enter your question: ")
response = perform_inference('trained_models/new_model', query)
print(response)The inference pipeline is designed to generate responses from the fine-tuned Falcon LoRA. This process involves loading the model and tokenizer, preparing the prompt for generation, and generating the response based on the model’s configuration. Below are the detailed steps and components involved in the inference process.
1. 导入所需程序库:
首先要导入必要的库。这些库包括用于处理模型和标记化器的 transformers 库、用于管理 Low-Rank Adaptation 模型的 peft 库,以及用于张量运算和启用推理模式的 Torch 库。
2. 函数 perform_inference(model_path,prompt):
该函数是推理过程的核心。它接受训练模型的路径和文本提示作为输入,并返回生成的响应。
3. 加载模型配置:
该函数从指定路径加载预训练 LoRA 模型的配置。该配置包含基础模型及其训练参数的必要细节。
4. 加载模型和标记符:
使用配置中指定的路径加载因果语言建模的基础模型。同样,与基础模型相关的标记器也会被加载。标记器负责将文本转换成模型可以处理的格式。
5. 加载微调后的 LoRA 模型:
使用 LoRA 调整过的微调模型将从指定路径加载。该模型包含了微调过程中所做的调整,以更好地处理其所训练的特定任务。
6. 准备输入提示:
用户提供的输入提示会被标记化并转换成张量。然后将这些张量传输到模型的设备(通常是 GPU,如果有的话),以便进行高效处理。
7. 配置生成参数:
设置控制文本生成行为的各种参数。这些参数包括
max_new_tokens: 要生成的新标记的最大数量。
温度: 通过在应用 softmax 之前缩放对数来控制预测的随机性。
top_p: 执行核采样,即只考虑最有可能的标记。
num_return_sequences(返回序列数): 指定要返回的不同序列的数量。
pad_token_id 和 eos_token_id: 分别定义填充标记和序列末端标记。
8. 生成响应:
模型将进入推理模式以禁用梯度计算,从而减少内存使用并加快处理速度。然后将输入提示传递给模型,模型会根据设置的参数生成响应。
9. 解码并返回响应:
生成的输出(最初为张量格式)被解码为人类可读的字符串。处理过程中使用的特殊标记会被跳过,以生成干净的响应。
10. 交互式推理循环:
该模型建立了一个交互式循环,不断接收用户输入,并使用执行推理函数生成响应。用户可以输入他们的提示,模型将实时生成并显示响应。模型路径通常指定为 "trained_models/new_model"。
该推理管道允许与微调 LLM 进行实时交互。其设计目的是利用微调过程中优化的配置和参数,高效生成对用户提供的提示的响应。可对配置参数进行调整,以微调响应生成的行为,满足特定要求。