4.8 数据预处理
在微调模型之前,我们不能直接使用原始数据集,需要将数据集中的提示转换成模型能够理解的格式。
为了使数据集适配微调流程,这里编写辅助函数来格式化输入数据集。具体来说,就是将对话摘要(即提示-响应对)转换成大型语言模型(LLM)能够识别的明确指令。
def create_prompt_formats(sample):"""格式化样本的各种字段('instruction', 'output')然后使用两个换行符将它们连接起来:param sample: 样本字典"""INTRO_BLURB = "以下是描述任务的指令。写一个适当完成请求的响应。"INSTRUCTION_KEY = "### 指令:总结以下对话。"RESPONSE_KEY = "### 输出:"END_KEY = "### 结束"blurb = f"\n{INTRO_BLURB}"instruction = f"{INSTRUCTION_KEY}"input_context = f"{sample['dialogue']}" if sample["dialogue"] else Noneresponse = f"{RESPONSE_KEY}\n{sample['summary']}"end = f"{END_KEY}"parts = [part for part in [blurb, instruction, input_context, response, end] if part]formatted_prompt = "\n\n".join(parts)sample["text"] = formatted_promptreturn sample
上述函数负责将输入数据转换成提示格式。
接下来,我们会用模型的分词器对这些提示进行处理,将其转换成标记化的形式。我们的目标是生成长度统一的输入序列,这样做有助于微调语言模型,因为它能提升处理效率并降低计算成本。同时,我们还得确保这些序列的长度不超过模型允许的最大标记数。
from functools import partial# 来源 https://github.com/databrickslabs/dolly/blob/master/training/trainer.py
def get_max_length(model):conf = model.configmax_length = Nonefor length_setting in ["n_positions", "max_position_embeddings", "seq_length"]:max_length = getattr(model.config, length_setting, None)if max_length:print(f"找到最大长度:{max_length}")breakif not max_length:max_length = 1024print(f"使用默认最大长度:{max_length}")return max_lengthdef preprocess_batch(batch, tokenizer, max_length):"""分批标记化"""return tokenizer(batch["text"],max_length=max_length,truncation=True,)# 来源 https://github.com/databrickslabs/dolly/blob/master/training/trainer.py
def preprocess_dataset(tokenizer: AutoTokenizer, max_length: int, seed, dataset):"""格式化并标记化,使其准备好进行训练:param tokenizer (AutoTokenizer): 模型分词器:param max_length (int): 分词器发出的最大标记数"""# 为每个样本添加提示print("预处理数据集...")dataset = dataset.map(create_prompt_formats)#, batched=True)# 对每个批次的数据集应用预处理并移除 'instruction', 'context', 'response', 'category' 字段_preprocessing_function = partial(preprocess_batch, max_length=max_length, tokenizer=tokenizer)dataset = dataset.map(_preprocessing_function,batched=True,remove_columns=['id', 'topic', 'dialogue', 'summary'],)# 过滤掉输入_ids超过最大长度的样本dataset = dataset.filter(lambda sample: len(sample["input_ids"]) < max_length)# 随机打乱数据集dataset = dataset.shuffle(seed=seed)return dataset
通过这些函数的处理,我们的数据集已经准备好进行微调了。
## 数据集预处理
max_length = get_max_length(original_model)
print(max_length)train_dataset = preprocess_dataset(tokenizer, max_length,seed, dataset['train'])
eval_dataset = preprocess_dataset(tokenizer, max_length,seed, dataset['validation'])
4.9 准备模型进行QLoRA训练
使用PEFT库中的prepare_model_for_kbit_training
方法来准备模型。
通过这个方法,我们对原始模型original_model
进行初始化,设置好必要的配置,以便进行QLoRA训练。
4.10 设置PEFT进行微调
接下来,我们配置LoRA参数,以便对基础模型进行微调。
from peft import LoraConfig, get_peft_modelconfig = LoraConfig(r=32, # 定义适配器的秩lora_alpha=32, # 学习权重的缩放因子target_modules=['q_proj', 'k_proj', 'v_proj', 'dense'],bias="none",lora_dropout=0.05, # 防止过拟合task_type="CAUSAL_LM", # 任务类型
)# 启用梯度检查点减少内存消耗
original_model.gradient_checkpointing_enable()# 获取配置好的PEFT模型
peft_model = get_peft_model(original_model, config)
这里的r
(秩)参数控制适配器的复杂度,影响模型的表达能力和计算成本。lora_alpha
参数用于调整学习权重,影响LoRA激活的强度。配置完成后,我们可以通过辅助函数查看模型的可训练参数数量。
print(print_number_of_trainable_model_parameters(peft_model))
4.11 训练PEFT适配器
来设置训练参数,并初始化训练器。
import transformers
from transformers import TrainingArguments, Trainer, DataCollatorForLanguageModeling# 设置训练参数
output_dir = f'./peft-dialogue-summary-training-{str(int(time.time()))}'
peft_training_args = TrainingArguments(output_dir=output_dir,warmup_steps=1,per_device_train_batch_size=1,gradient_accumulation_steps=4,max_steps=1000,learning_rate=2e-4,optim="paged_adamw_8bit",logging_steps=25,logging_dir="./logs",save_strategy="steps",save_steps=25,evaluation_strategy="steps",eval_steps=25,do_eval=True,gradient_checkpointing=True,report_to="none",overwrite_output_dir=True,group_by_length=True,
)# 禁用缓存,准备训练器
peft_model.config.use_cache = False
peft_trainer = Trainer(model=peft_model,args=peft_training_args,train_dataset=train_dataset,eval_dataset=eval_dataset,data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False),
)
我们计划进行1000步训练,这个数字对于我们的特定数据集应该是合适的。不过,可能需要根据实际情况调整这个数值。现在,可以开始训练了,训练时间会根据超参数的不同而有所差异。
peft_trainer.train()
训练完成后,准备一个用于推理的模型。给原始的Phi-2模型添加一个适配器,并设置为不可训练状态,因为我们只打算用它来做推理。
import torch
from transformers import AutoModelForCausalLM, AutoTokenizerbase_model_id = "microsoft/phi-2"
base_model = AutoModelForCausalLM.from_pretrained(base_model_id)
eval_tokenizer = AutoTokenizer.from_pretrained(base_model_id, add_bos_token=True)
eval_tokenizer.pad_token = eval_tokenizer.eos_tokenfrom peft import PeftModelft_model = PeftModel.from_pretrained(base_model,"/kaggle/working/peft-dialogue-summary-training-1705417060/checkpoint-1000",torch_dtype=torch.float16,is_trainable=False
)
微调是一个需要反复试验的过程。我们可能需要根据验证集和测试集的表现来调整模型结构、超参数或训练数据,以提升模型性能。
4.12 定性评估模型(人工评估)
使用PEFT模型对同一个输入进行推理,以评估其性能。
from transformers import set_seed
set_seed(seed)# 选择测试集中的一个对话样本
index = 5
dialogue = dataset['test'][index]['dialogue']
summary = dataset['test'][index]['summary']# 构建推理提示
prompt = f"Instruct: Summarize the following conversation.\n{dialogue}\nOutput:\n"# 使用PEFT模型生成摘要
peft_model_res = gen(ft_model, prompt, 100)
peft_model_output = peft_model_res[0].split('Output:\n')[1]
prefix, _, _ = peft_model_output.partition('###')# 打印结果
dash_line = '-' * 100
print(dash_line)
print(f'输入提示:\n{prompt}')
print(dash_line)
print(f'人工摘要:\n{summary}\n')
print(dash_line)
print(f'PEFT模型输出:\n{prefix}')
这段代码将展示PEFT模型相对于人类基线摘要的表现。通过比较模型输出和人类生成的摘要,我们可以定性地评估PEFT模型的性能。
PEFT 模型输出
4.13 定量评估模型(使用ROUGE指标)
ROUGE(Recall-Oriented Understudy for Gisting Evaluation)是一种评估自动摘要和机器翻译软件的工具,它通过比较机器生成的摘要与人工生成的参考摘要来衡量效果。虽然不完美,但ROUGE指标能有效反映微调后摘要质量的整体提升。
我们通过ROUGE指标来定量评估模型生成的摘要。以下是评估过程:
from transformers import AutoModelForCausalLM
import pandas as pd
import evaluate
import numpy as np# 加载模型和数据
original_model = AutoModelForCausalLM.from_pretrained(base_model_id, device_map='auto')
dialogues = dataset['test'][0:10]['dialogue']
human_baseline_summaries = dataset['test'][0:10]['summary']# 初始化摘要列表
original_model_summaries = []
peft_model_summaries = []# 生成摘要并评估
for idx, dialogue in enumerate(dialogues):prompt = f"Instruct: Summarize the following conversation.\n{dialogue}\nOutput:\n"original_model_res = gen(original_model, prompt, 100)original_model_text_output = original_model_res[0].split('Output:\n')[1]peft_model_res = gen(ft_model, prompt, 100)peft_model_output = peft_model_res[0].split('Output:\n')[1]peft_model_text_output, _, _ = peft_model_output.partition('###')original_model_summaries.append(original_model_text_output)peft_model_summaries.append(peft_model_text_output)# 将结果存入DataFrame
df = pd.DataFrame(list(zip(human_baseline_summaries, original_model_summaries, peft_model_summaries)),columns=['human_baseline_summaries', 'original_model_summaries', 'peft_model_summaries'])# 计算ROUGE指标
rouge = evaluate.load('rouge')
original_model_results = rouge.compute(predictions=original_model_summaries, references=human_baseline_summaries)
peft_model_results = rouge.compute(predictions=peft_model_summaries, references=human_baseline_summaries)# 打印结果
print('原始模型ROUGE指标:')
print(original_model_results)
print('PEFT模型ROUGE指标:')
print(peft_model_results)# 计算提升百分比
improvement = (np.array(list(peft_model_results.values())) - np.array(list(original_model_results.values())))
for key, value in zip(peft_model_results.keys(), improvement):print(f'{key}提升: {value*100:.2f}%')
通过ROUGE指标,我们可以看到PEFT模型相较于原始模型在摘要质量上的显著提升。
5 结语
微调大型语言模型(LLM)已成为寻求优化运营流程的企业必不可少的步骤。虽然LLM的初始训练赋予了广泛的语言理解能力,但微调过程将这些模型细化为能够处理特定主题并提供更准确结果的专用工具。为不同任务、行业或数据集定制LLM扩展了这些模型的能力,确保了它们在不断变化的数字环境中的相关性和价值。展望未来,LLM的持续探索和创新,加上精细的微调方法,有望推进更智能、更高效、更具情境意识的人工智能系统的发展。
如何学习AI大模型?
作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费
】
一、全套AGI大模型学习路线
AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!
二、640套AI大模型报告合集
这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。
三、AI大模型经典PDF籍
随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。
四、AI大模型商业化落地方案
作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量。