GPT-LLM-Trainer:如何使用自己的数据轻松快速地微调和训练LLM

技术

点击上方蓝字关注我们

一、前言

想要轻松快速地使用您自己的数据微调和培训大型语言模型(LLM) ?我们知道训练大型语言模型具有挑战性并需要耗费大量计算资源,包括收集和优化数据集、确定合适的模型及编写训练代码等。今天我们将介绍一种实验性新方法,实现特定任务高性能模型的训练。

我们的目标是最大程度地简化模型微调训练过程,使您能够在最短时间内从构思转化为高性能的完全训练的模型。以微调开源模型LLaMa 2为例,整个过程 只需提供任务描述,系统便会为您生成数据集、解析成正确的数据格式并微调 LLaMA 2 模型 。轻松实现快速的微调和推理过程!

picture.image二、GPT-LLM-Trainer 介绍

GPT-LLM-Trainer 是一种全新、经济实惠且最简单的方法来训练大型语言模型。这个项目旨在探索一种新的训练高性能任务专用模型的流程,摆脱所有复杂的步骤,使你更容易从一个想法转变为完全训练好的模型。你只需输入任务描述,系统就会从头开始生成数据集,将其转换为你想要的任何格式,并为你进行模型微调。你可以在Google Colab上轻松的训练大型语言模型。GPT-LLM-Trainer 模型训练器利用 GPT-4 模型来简化整个过程,包括以下三个关键阶段:

  • 数据生成阶段 :利用 GPT-4 模型根据提供的输入用例生成多样化的提示和响应数据。
  • 系统消息生成 :通过为模型的交互设计最佳系统提示。
  • 微调模型过程 :生成数据集后,系统会自动将其拆分为训练集和验证集,为您微调模型,并为推理做好准备。

GPT大型语言模型训练器的主要优势在于它可以摆脱复杂的步骤,让用户更容易地从一个想法转变为完全训练好的模型。你只需输入任务描述,系统就会从头开始生成数据集,将其转换为你想要的任何格式,并为你进行模型微调。在这种情况下,你将使用LLaMa 2进行微调。

picture.image三、如何使用自己的数据微调LLM

这里主要介绍如何在Google Colab上训练 LLaMA 2 大型语言模型的实现步骤。首先,我们需要收集和整理数据集,将其格式化并选择合适的模型。接下来,我们需要编写训练代码,并将所有这些整合在一起进行训练。这个过程可能会遇到很多困难和挑战,但是通过使用GPT大型语言模型训练器,我们可以大大简化这个过程。

3.1、描述你的模型

picture.image

我们通过尝试一种新的方法,可以轻松地根据你输入的任务描述来构建特定任务的模型。首先,尽可能的使用GPU,可以在Google Colab中设置可用的 GPU,然后创建模型,只需要在提示中描述您想要构建的模型。具有描述性且清晰。你可以通过更改

Hyperparameters

单元格中的

model\_name

来更改要微调的模型。

3.2、数据生成步骤

picture.image

在这里写下您的 prompt

提示。描述性越强、越清晰越好!

然后,选择生成数据时使用的 temperature (0 到 1 之间)。较低的值非常适合精确的任务,例如编写代码,而较大的值更适合创造性的任务,例如编写故事。

最后,选择您想要生成的示例数量。生成的数据越多,a) 花费的时间就越长,b) 数据生成的成本就越高。但一般来说,更多的例子会产生更高质量的模型。100 通常是最低启动值。


        
prompt = "一个能够接收中文中类似猜灯谜的问题,并用经过深思熟虑、逐步推理的方式以中文回答的模型。"  
temperature = .4  
number_of_examples = 100
    

先安装OpenAI依赖。


        
!pip install openai
    

一旦你确定了提示,就可以执行下面的代码生成数据集。这可能需要比预期更长的时间。


        
import os  
import openai  
import random  
  
openai.api_key = "YOUR KEY HERE"  
  
def generate\_example(prompt, prev_examples, temperature=.5):  
    messages=[  
        {  
            "role": "system",  
            "content": f"您正在生成将用于训练机器学习模型的数据。\n\n您将获得我们想要训练的模型的高级描述,并由此生成数据样本,每个样本都有一个提示/ 响应对。\n\n您将按照以下格式执行此操作:\n````\nprompt\n------------\n$prompt\_goes\_here\n---------- -\n\nresponse\n-----------\n$response\_goes\_here\n------------\n```\n\n只能有一对提示/响应 每轮都会生成。\n\n对于每一轮,使示例比上一轮稍微复杂一点,同时确保多样性。\n\n确保您的样本是独特且多样化的,但高质量和复杂性足以训练一个良好的样本 执行模型。\n\n这是我们要训练的模型类型:\n`{prompt}`"  
        }  
    ]  
  
    if len(prev_examples) > 0:  
        if len(prev_examples) > 10:  
            prev_examples = random.sample(prev_examples, 10)  
        for example in prev_examples:  
            messages.append({  
                "role": "assistant",  
                "content": example  
            })  
  
    response = openai.ChatCompletion.create(  
        model="gpt-4",  
        messages=messages,  
        temperature=temperature,  
        max_tokens=1354,  
    )  
  
    return response.choices[0].message['content']  
  
# Generate examples  
prev_examples = []  
for i in range(number_of_examples):  
    print(f'Generating example {i}')  
    example = generate_example(prompt, prev_examples, temperature)  
    prev_examples.append(example)  
  
print(prev_examples)
    

生成完数据集,我们还需要生成系统消息。


        
def generate\_system\_message(prompt):  
  
    response = openai.ChatCompletion.create(  
        model="gpt-4",  
        messages=[  
          {  
            "role": "system",  
            "content": "您将获得我们正在训练的模型的高级描述,并据此生成一个简单的系统提示以供该模型使用。 请记住,您不是生成用于数据生成的系统消息 - 您正在生成用于推理的系统消息。 一个好的格式是“给定 $INPUT\_DATA,您将 $WHAT\_THE\_MODEL\_SHOULD\_DO。”。\n\n使其尽可能简洁。 在响应中只包含系统提示符。\n\n例如,切勿编写:`\"$SYSTEM\_PROMPT\_HERE\"`。\n\n应该类似于:`$SYSTEM\_PROMPT\_HERE`。"  
          },  
          {  
              "role": "user",  
              "content": prompt.strip(),  
          }  
        ],  
        temperature=temperature,  
        max_tokens=500,  
    )  
  
    return response.choices[0].message['content']  
  
system_message = generate_system_message(prompt)  
  
print(f'系统消息: `{system\_message}`。 如果您想要更好的结果,请随意重新运行此单元格。')
    

接下来,我们将示例放入数据框中,并将它们转换为最终的数据集对。


        
import pandas as pd  
  
# 初始化列表以存储提示和响应  
prompts = []  
responses = []  
  
# 从示例中解析出提示和响应  
for example in prev_examples:  
  try:  
    split_example = example.split('-----------')  
    prompts.append(split_example[1].strip())  
    responses.append(split_example[3].strip())  
  except:  
    pass  
  
# 创建数据框  
df = pd.DataFrame({  
    'prompt': prompts,  
    'response': responses  
})  
  
# 删除重复项  
df = df.drop_duplicates()  
  
print('有 ' + str(len(df)) + ' 成功生成的示例。 以下是前几个:')  
  
df.head()
    

将数据分为训练集和测试集。


        
# 将数据分为训练集和测试集,其中 90% 在训练集中  
train_df = df.sample(frac=0.9, random_state=42)  
test_df = df.drop(train_df.index)  
  
# 将数据帧保存到 .jsonl 文件  
train_df.to_json('train.jsonl', orient='records', lines=True)  
test_df.to_json('test.jsonl', orient='records', lines=True)
    

3.3、安装必要的库

picture.image


        
!pip install -q accelerate==0.21.0 peft==0.4.0 bitsandbytes==0.40.2 transformers==4.31.0 trl==0.4.7
    

        
import os  
import torch  
from datasets import load_dataset  
from transformers import (  
    AutoModelForCausalLM,  
    AutoTokenizer,  
    BitsAndBytesConfig,  
    HfArgumentParser,  
    TrainingArguments,  
    pipeline,  
    logging,  
)  
from peft import LoraConfig, PeftModel  
from trl import SFTTrainer
    

3.4、定义超参数


        
model_name = "NousResearch/llama-2-7b-chat-hf" # 如果您有权访问官方 LLaMA 2 模型“meta-llama/Llama-2-7b-chat-hf”,请使用此选项,但请记住,您需要传递 Hugging Face 键参数  
dataset_name = "/content/train.jsonl"  
new_model = "llama-2-7b-custom"  
lora_r = 64  
lora_alpha = 16  
lora_dropout = 0.1  
use_4bit = True  
bnb_4bit_compute_dtype = "float16"  
bnb_4bit_quant_type = "nf4"  
use_nested_quant = False  
output_dir = "./results"  
num_train_epochs = 1  
fp16 = False  
bf16 = False  
per_device_train_batch_size = 4  
per_device_eval_batch_size = 4  
gradient_accumulation_steps = 1  
gradient_checkpointing = True  
max_grad_norm = 0.3  
learning_rate = 2e-4  
weight_decay = 0.001  
optim = "paged\_adamw\_32bit"  
lr_scheduler_type = "constant"  
max_steps = -1  
warmup_ratio = 0.03  
group_by_length = True  
save_steps = 25  
logging_steps = 5  
max_seq_length = None  
packing = False  
device_map = {"": 0}
    

3.5、加载数据集并训练


        
# 加载数据集  
train_dataset = load_dataset('json', data_files='/content/train.jsonl', split="train")  
valid_dataset = load_dataset('json', data_files='/content/test.jsonl', split="train")  
  
# 预处理数据集  
train_dataset_mapped = train_dataset.map(lambda examples: {'text': [f'[INST] <<SYS>>\n{system\_message.strip()}\n<</SYS>>\n\n' + prompt + ' [/INST] ' + response for prompt, response in zip(examples['prompt'], examples['response'])]}, batched=True)  
valid_dataset_mapped = valid_dataset.map(lambda examples: {'text': [f'[INST] <<SYS>>\n{system\_message.strip()}\n<</SYS>>\n\n' + prompt + ' [/INST] ' + response for prompt, response in zip(examples['prompt'], examples['response'])]}, batched=True)  
  
compute_dtype = getattr(torch, bnb_4bit_compute_dtype)  
bnb_config = BitsAndBytesConfig(  
    load_in_4bit=use_4bit,  
    bnb_4bit_quant_type=bnb_4bit_quant_type,  
    bnb_4bit_compute_dtype=compute_dtype,  
    bnb_4bit_use_double_quant=use_nested_quant,  
)  
model = AutoModelForCausalLM.from_pretrained(  
    model_name,  
    quantization_config=bnb_config,  
    device_map=device_map  
)  
model.config.use_cache = False  
model.config.pretraining_tp = 1  
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)  
tokenizer.pad_token = tokenizer.eos_token  
tokenizer.padding_side = "right"  
peft_config = LoraConfig(  
    lora_alpha=lora_alpha,  
    lora_dropout=lora_dropout,  
    r=lora_r,  
    bias="none",  
    task_type="CAUSAL\_LM",  
)  
# 设置训练参数  
training_arguments = TrainingArguments(  
    output_dir=output_dir,  
    num_train_epochs=num_train_epochs,  
    per_device_train_batch_size=per_device_train_batch_size,  
    gradient_accumulation_steps=gradient_accumulation_steps,  
    optim=optim,  
    save_steps=save_steps,  
    logging_steps=logging_steps,  
    learning_rate=learning_rate,  
    weight_decay=weight_decay,  
    fp16=fp16,  
    bf16=bf16,  
    max_grad_norm=max_grad_norm,  
    max_steps=max_steps,  
    warmup_ratio=warmup_ratio,  
    group_by_length=group_by_length,  
    lr_scheduler_type=lr_scheduler_type,  
    report_to="all",  
    evaluation_strategy="steps",  
    eval_steps=5  # 每 20 步评估一次  
)  
# 设置监督微调参数  
trainer = SFTTrainer(  
    model=model,  
    train_dataset=train_dataset_mapped,  
    eval_dataset=valid_dataset_mapped,  # 在此处传递验证数据集  
    peft_config=peft_config,  
    dataset_text_field="text",  
    max_seq_length=max_seq_length,  
    tokenizer=tokenizer,  
    args=training_arguments,  
    packing=packing,  
)  
trainer.train()  
trainer.model.save_pretrained(new_model)  
  
# 单元 4:测试模型  
logging.set_verbosity(logging.CRITICAL)  
prompt = f"[INST] <<SYS>>\n{system\_message}\n<</SYS>>\n\n编写一个反转字符串的函数。 [/INST]" # 将此处的命令替换为与您的任务相关的命令  
pipe = pipeline(task="text-generation", model=model, tokenizer=tokenizer, max_length=200)  
result = pipe(prompt)  
print(result[0]['generated\_text'])
    

3.6、运行推理

picture.image


        
from transformers import pipeline  
  
prompt = f"[INST] <<SYS>>\n{system\_message}\n<</SYS>>\n\n编写一个反转字符串的函数。 [/INST]" # 将此处的命令替换为与您的任务相关的命令  
num_new_tokens = 100  # 更改为您想要生成的新令牌的数量  
  
# 计算提示中的标记数量  
num_prompt_tokens = len(tokenizer(prompt)['input\_ids'])  
  
# 计算一代的最大长度  
max_length = num_prompt_tokens + num_new_tokens  
  
gen = pipeline('text-generation', model=model, tokenizer=tokenizer, max_length=max_length)  
result = gen(prompt)  
print(result[0]['generated\_text'].replace(prompt, ''))
    

3.7、合并模型并存储在 Google Drive 中

picture.image


        
# 合并并保存微调后的模型  
from google.colab import drive  
drive.mount('/content/drive')  
  
model_path = "/content/drive/MyDrive/llama-2-7b-custom"  # 更改为您的首选路径  
  
# 在 FP16 中重新加载模型并将其与 LoRA 权重合并  
base_model = AutoModelForCausalLM.from_pretrained(  
    model_name,  
    low_cpu_mem_usage=True,  
    return_dict=True,  
    torch_dtype=torch.float16,  
    device_map=device_map,  
)  
model = PeftModel.from_pretrained(base_model, new_model)  
model = model.merge_and_unload()  
  
# 重新加载分词器以保存它  
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)  
tokenizer.pad_token = tokenizer.eos_token  
tokenizer.padding_side = "right"  
  
# 保存合并后的模型  
model.save_pretrained(model_path)  
tokenizer.save_pretrained(model_path)
    

3.8、从 Drive 加载微调模型并运行推理

picture.image


        
from google.colab import drive  
from transformers import AutoModelForCausalLM, AutoTokenizer  
  
drive.mount('/content/drive')  
  
model_path = "/content/drive/MyDrive/llama-2-7b-custom"  # 更改为保存模型的路径  
  
model = AutoModelForCausalLM.from_pretrained(model_path)  
tokenizer = AutoTokenizer.from_pretrained(model_path)
    

3.9、测试微调训练后的模型

picture.image


        
from transformers import pipeline  
  
prompt = "请问,哪个字一年四季都不会凋零?"  # 更改为您想要的提示  
gen = pipeline('text-generation', model=model, tokenizer=tokenizer)  
result = gen(prompt)  
print(result[0]['generated\_text'])
    

picture.image四、总结

本文主要介绍了如何使用GPT大型语言模型训练器来训练你自己的大型语言模型;如何利用GPT-4的强大功能来简化训练过程,并确保你的模型能够实现最佳性能;最后介绍了如何在谷歌Colab上训练大型语言模型的实用技巧和步骤。GPT-LLM-Trainer 是一款经济实惠且易于使用的工具,用于使用您自己的数据训练大型语言模型。它简化了收集、提炼、格式化、选择和训练数据集的复杂过程,并根据您的任务描述为您微调模型。使用此工具,您可以生成各种提示、优化系统提示、拆分数据集、定义超参数以及在 Google Colab 或本地 Jupyter Notebook 上高效运行推理。

picture.image五、References

  • GPT-LLM-Trainer Github Repo:

https://github.com/mshumer/gpt-llm-trainer

  • Jupyter Notebook 的完整代码:

https://github.com/Crossme0809/frenzyTechAI/blob/main/fine-tuned-llm-trainer/How\_to\_Fine\_Tune\_and\_Train\_LLMs\_With\_FAST\_GPT\_LLM\_Trainer.ipynb

picture.image

如果你对这篇文章感兴趣,而且你想要了解更多关于AI 领域的实战技巧,可以

关注「技术狂潮AI」公众号

。在这里,你可以看到最新最热的AIGC 领域的干货文章和案例实战教程。

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论