使用Llama3模型进行关系抽取

Generated with DALL-E.

前提

关系抽取(RE)是从非结构化文本中提取关系以识别各种命名实体之间的联系的任务。它与命名实体识别(NER)一起进行,是自然语言处理流程中的一个重要步骤。随着大型语言模型(LLMs)的兴起,传统涉及标记实体跨度和分类它们之间(如果有的话)关系的监督方法被基于LLM的方法增强或完全取代[1]。

Llama3是生成性AI领域最新的主要发布[2]。该基础模型有两种尺寸,8B和70B,预计将很快发布一个400B模型。这些模型可在HuggingFace平台上获取;详情见[3]。70B变体为Meta的新聊天网站Meta.ai提供动力,并且表现出与ChatGPT相当的性能。8B模型在其类别中是性能最高的之一。Llama3的架构与Llama2相似,性能提升主要是由于数据升级。该模型配备了一个升级的分词器和扩展的上下文窗口。它被标记为开源,尽管只发布了一小部分数据。总的来说,这是一个极好的模型,我迫不及待地想尝试它。

Llama3–70B可以产生惊人的结果,但由于其大小,它在本地系统上使用是不切实际的、成本高昂且难以使用的。因此,为了利用其能力,我们让Llama3–70B教更小的Llama3–8B从非结构化文本中进行关系抽取的任务。

具体来说,在Llama3–70B的帮助下,我们构建了一个旨在进行关系抽取的监督微调数据集。然后我们使用这个数据集来微调Llama3–8B,以增强其关系抽取能力。

要在与本博客相关联的Google Colab笔记本中重现代码,你将需要:

•HuggingFace的凭证(可选,用于保存微调后的模型)和Llama3的访问权限,可以通过按照模型卡之一中的说明获得;

•一个免费的GroqCloud账户(你可以使用Google账户登录)以及相应的API密钥。

工作区设置

对于这个项目,我使用了配备A100 GPU和高RAM设置的Google Colab Pro。

我们首先安装所有所需的库:

!pip install -q groq !pip install -U accelerate bitsandbytes datasets evaluate !pip install -U peft transformers trl

我很高兴地注意到,整个设置从一开始就能够顺利工作,没有遇到任何依赖性问题,也没有必要从源代码安装transformers,尽管这个模型是全新的。

我们还需要给Google Colab访问驱动器和文件的权限,并设置工作目录:

# For Google Colab settings from google.colab import userdata, drive # This will prompt for authorization drive.mount(‘/content/drive’) # Set the working directory %cd ‘/content/drive/MyDrive/postedBlogs/llama3RE’

对于希望将模型上传到HuggingFace Hub的用户,我们需要上传Hub的凭证。在我的案例中,这些凭证存储在Google Colab的密钥中,可以通过左侧的密钥按钮访问。这一步是可选的。

# For Hugging Face Hub setting from huggingface_hub import login # Upload the HuggingFace token (should have WRITE access) from Colab secrets HF = userdata.get(‘HF’) # This is needed to upload the model to HuggingFace login(token=HF,add_to_git_credential=True)

我也添加了一些路径变量以简化文件访问:

# Create a path variable for the data folder data_path = ‘/content/drive/MyDrive/postedBlogs/llama3RE/datas/’ # Full fine-tuning dataset sft_dataset_file = f’{data_path}sft_train_data.json’ # Data collected from the the mini-test mini_data_path = f’{data_path}mini_data.json’ # Test data containing all three outputs all_tests_data = f’{data_path}all_tests.json’ # The adjusted training dataset train_data_path = f’{data_path}sft_train_data.json’ # Create a path variable for the SFT model to be saved locally sft_model_path = ‘/content/drive/MyDrive/llama3RE/Llama3_RE/’

现在我们的工作区已经设置好了,我们可以进入第一步,即为关系抽取任务构建一个合成数据集。

使用Llama3–70B创建关系抽取的合成数据集

有几种关系抽取数据集可用,其中最著名的是CoNLL04数据集。此外,还有像web_nlg这样优秀的数据集,可以在HuggingFace上找到,以及由AllenAI开发的SciREX。然而,这些数据集大多带有限制性许可证。

受web_nlg数据集格式的启发,我们将构建我们自己的数据集。如果我们计划微调在该数据集上训练的模型,这种方法将特别有用。首先,我们需要为我们的关系抽取任务收集一批简短的句子。我们可以通过多种方式编制这个语料库。

收集句子集

我们将使用databricks-dolly-15k,这是一个由Databricks员工在2023年生成的开源数据集。该数据集旨在进行监督微调,并包含四个特征:指令、上下文、响应和类别。在分析了八个类别之后,我决定保留信息提取类别中上下文的第一句。数据解析步骤如下:

from datasets import load_dataset # Load the dataset dataset = load_dataset(“databricks/databricks-dolly-15k”) # Choose the desired category from the dataset ie_category = [e for e in dataset[“train”] if e[“category”]==“information_extraction”] # Retain only the context from each instance ie_context = [e[“context”] for e in ie_category] # Split the text into sentences (at the period) and keep the first sentence reduced_context = [text.split(‘.’)[0] + ‘.’ for text in ie_context] # Retain sequences of specified lengths only (use character length) sampler = [e for e in reduced_context if 30 < len(e) < 170]

选择过程产生了一个包含1,041个句子的数据集。鉴于这是一个小型项目,我没有手动挑选句子,因此,一些样本可能并不完全适合我们的工作。在一个专为生产而设计的项目中,我会仔细挑选最合适的句子。然而,对于本项目的目的来说,这个数据集已经足够了。

格式化数据

我们首先需要创建一个系统消息,该消息将定义输入提示并指导模型如何生成答案:

system_message = “”“You are an experienced annontator. Extract all entities and the relations between them from the following text. Write the answer as a triple entity1|relationship|entitity2. Do not add anything else. Example Text: Alice is from France. Answer: Alice|is from|France. “””

由于这是一个试验阶段,我将对模型的要求保持在最低限度。我确实测试了几个其他的提示,包括一些请求以CoNLL格式输出,其中实体被分类的提示,模型的表现相当好。然而,为了简单起见,我们目前将坚持基础。

我们还需要将数据转换为对话格式:

messages = [[ {“role”: “system”,“content”: f"{system_message}"}, {“role”: “user”, “content”: e}] for e in sampler]

Groq客户端和API

Llama3几天前刚刚发布,API选项的可用性仍然有限。虽然Llama3-70B提供了聊天界面,但这个项目需要一个API,可以用几行代码处理我的1000个句子。我发现了一个很棒的YouTube视频,它解释了如何免费使用GroqCloud API。更多细节请参考视频。
只是提醒一下:你需要登录并从GroqCloud网站获取一个免费的API密钥。我的API密钥已经保存在Google Colab的密钥中。我们首先通过初始化Groq客户端开始:

import os from groq import Groq gclient = Groq( api_key=userdata.get(“GROQ”), )

接下来,我们需要定义几个辅助函数,这些函数将使我们能够有效地与Meta.ai聊天界面进行交互(这些是从YouTube视频中改编的):

import time from tqdm import tqdm def process_data(prompt): “”“Send one request and retrieve model’s generation.”“” chat_completion = gclient.chat.completions.create( messages=prompt, # input prompt to send to the model model=“llama3-70b-8192”, # according to GroqCloud labeling temperature=0.5, # controls diversity max_tokens=128, # max number tokens to generate top_p=1, # proportion of likelihood weighted options to consider stop=None, # string that signals to stop generating stream=False, # if set partial messages are sent ) return chat_completion.choices[0].message.content def send_messages(messages): “”“Process messages in batches with a pause between batches.”“” batch_size = 10 answers = [] for i in tqdm(range(0, len(messages), batch_size)): # batches of size 10 batch = messages[i:i+10] # get the next batch of messages for message in batch: output = process_data(message) answers.append(output) if i + 10 < len(messages): # check if there are batches left time.sleep(10) # wait for 10 seconds return answers

第一个函数 process_data() 是 Groq 客户端聊天完成功能的包装器。第二个函数 send_messages(),以小批量处理数据。如果你点击 Groq playground 页面上的 “Settings” 链接,你会发现一个指向 “Limits”(限制)的链接,它详细说明了我们可以使用免费 API 的条件,包括对请求数量和生成的令牌数量的限制。为了避免超出这些限制,我在每批10条消息后添加了10秒的延迟,尽管在我的案例中这并不是绝对必要的。你可能想要尝试调整这些设置。

现在剩下的就是生成我们的关系提取数据,并将其与初始数据集整合:

# Data generation with Llama3-70B answers = send_messages(messages) # Combine input data with the generated dataset combined_dataset = [{‘text’: user, ‘gold_re’: output} for user, output in zip(sampler, answers)]

评估Llama3-8B用于关系提取

在对模型进行微调之前,重要的是要在几个样本上评估其性能,以确定是否确实需要微调。

构建测试数据集

我们将从我们刚刚构建的数据集中选择20个样本并将它们留作测试用。数据集的其余部分将用于微调。

import random random.seed(17) # Select 20 random entries mini_data = random.sample(combined_dataset, 20) # Build conversational format parsed_mini_data = [[{‘role’: ‘system’, ‘content’: system_message}, {‘role’: ‘user’, ‘content’: e[‘text’]}] for e in mini_data] # Create the training set train_data = [item for item in combined_dataset if item not in mini_data]

我们将使用GroqCloud API和上面定义的实用程序,指定model=llama3-8b-8192,而函数的其余部分保持不变。在这种情况下,我们可以直接处理我们的小型数据集,而不必担心超出API限制。

以下是提供一个示例输出,它提供了原始文本,Llama3-70B生成的文本标记为gold_re,以及Llama3-8B生成的文本标记为test_re。

{‘text’: ‘Long before any knowledge of electricity existed, people were aware of shocks from electric fish.’, ‘gold_re’: ‘people|were aware of|shocks\nshocks|from|electric fish\nelectric fish|had|electricity’, ‘test_re’: ‘electric fish|were aware of|shocks’}

对于完整的测试数据集,请参考Google Colab笔记本。

仅从这个例子中,就变得清楚,Llama3-8B在关系提取能力上可能需要一些改进。让我们努力提高这一点。

Llama3-8B的监督式微调

我们将利用一整套技术来协助我们,包括QLoRA和Flash Attention。我不会在这里深入探讨选择超参数的具体细节,但如果你有兴趣进一步探索,请查看这些优秀的参考资料[4]和[5]。

A100 GPU支持Flash Attention和bfloat16,并且它拥有大约40GB的内存,这对我们的微调需求来说是足够的。

准备SFT数据集

我们首先将数据集解析为对话格式,包括系统消息、输入文本和我们从Llama3-70B生成中派生出的期望答案。然后我们将其保存为HuggingFace数据集:

def create_conversation(sample): return { “messages”: [ {“role”: “system”,“content”: system_message}, {“role”: “user”, “content”: sample[“text”]}, {“role”: “assistant”, “content”: sample[“gold_re”]} ] } from datasets import load_dataset, Dataset train_dataset = Dataset.from_list(train_data) # Transform to conversational format train_dataset = train_dataset.map(create_conversation, remove_columns=train_dataset.features, batched=False)

Choose the Model

model_id = “meta-llama/Meta-Llama-3-8B”

Load the Tokenizer

from transformers import AutoTokenizer # Tokenizer tokenizer = AutoTokenizer.from_pretrained(model_id, use_fast=True, trust_remote_code=True) tokenizer.pad_token = tokenizer.eos_token tokenizer.pad_token_id = tokenizer.eos_token_id tokenizer.padding_side = ‘left’ # Set a maximum length tokenizer.model_max_length = 512

Choose Quantization Parameters

from transformers import BitsAndBytesConfig bnb_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_use_double_quant=True, bnb_4bit_quant_type=“nf4”, bnb_4bit_compute_dtype=torch.bfloat16 )

Load the Model

from transformers import AutoModelForCausalLM from peft import prepare_model_for_kbit_training from trl import setup_chat_format device_map = {“”: torch.cuda.current_device()} if torch.cuda.is_available() else None model = AutoModelForCausalLM.from_pretrained( model_id, device_map=device_map, attn_implementation=“flash_attention_2”, quantization_config=bnb_config ) model, tokenizer = setup_chat_format(model, tokenizer) model = prepare_model_for_kbit_training(model)

LoRA Configuration

from peft import LoraConfig # According to Sebastian Raschka findings peft_config = LoraConfig( lora_alpha=128, #32 lora_dropout=0.05, r=256, #16 bias=“none”, target_modules=[“q_proj”, “o_proj”, “gate_proj”, “up_proj”, “down_proj”, “k_proj”, “v_proj”], task_type=“CAUSAL_LM”, )

当针对所有线性层进行优化时,可以获得最佳结果。如果内存限制是一个问题,选择更标准的值,如alpha=32和rank=16可能是有益的,因为这些设置可以显著减少参数数量。

训练参数

from transformers import TrainingArguments # Adapted from Phil Schmid blogpost args = TrainingArguments( output_dir=sft_model_path, # directory to save the model and repository id num_train_epochs=2, # number of training epochs per_device_train_batch_size=4, # batch size per device during training gradient_accumulation_steps=2, # number of steps before performing a backward/update pass gradient_checkpointing=True, # use gradient checkpointing to save memory, use in distributed training optim=“adamw_8bit”, # choose paged_adamw_8bit if not enough memory logging_steps=10, # log every 10 steps save_strategy=“epoch”, # save checkpoint every epoch learning_rate=2e-4, # learning rate, based on QLoRA paper bf16=True, # use bfloat16 precision tf32=True, # use tf32 precision max_grad_norm=0.3, # max gradient norm based on QLoRA paper warmup_ratio=0.03, # warmup ratio based on QLoRA paper lr_scheduler_type=“constant”, # use constant learning rate scheduler push_to_hub=True, # push model to Hugging Face hub hub_model_id=“llama3-8b-sft-qlora-re”, report_to=“tensorboard”, # report metrics to tensorboard )

如果您选择将模型保存在本地,您可以省略最后三个参数。您还可能需要调整per_device_batch_size(每个设备的批次大小)和gradient_accumulation_steps(梯度累积步数),以防止出现内存不足(OOM)错误。

初始化训练器并训练模型

from trl import SFTTrainer trainer = SFTTrainer( model=model, args=args, train_dataset=sft_dataset, peft_config=peft_config, max_seq_length=512, tokenizer=tokenizer, packing=False, # True if the dataset is large dataset_kwargs={ “add_special_tokens”: False, # the template adds the special tokens “append_concat_token”: False, # no need to add additional separator token } ) trainer.train() trainer.save_model()

训练,包括模型保存,大约需要10分钟。

让我们清除内存以准备进行推理测试。如果您使用的是内存较少的GPU并遇到CUDA内存不足(OOM)错误,您可能需要重启运行时。

import torch import gc del model del tokenizer gc.collect() torch.cuda.empty_cache()

使用SFT模型进行推理

在这最后一步中,我们将加载半精度的基本模型以及Peft适配器。对于这次测试,我选择不将模型与适配器合并。

from peft import AutoPeftModelForCausalLM from transformers import AutoTokenizer, pipeline import torch # HF model peft_model_id = “solanaO/llama3-8b-sft-qlora-re” # Load Model with PEFT adapter model = AutoPeftModelForCausalLM.from_pretrained( peft_model_id, device_map=“auto”, torch_dtype=torch.float16, offload_buffers=True )

Next, we load the tokenizer:

okenizer = AutoTokenizer.from_pretrained(peft_model_id) tokenizer.pad_token = tokenizer.eos_token tokenizer.pad_token_id = tokenizer.eos_token_id

And we build the text generation pipeline:

pipe = pipeline(“text-generation”, model=model, tokenizer=tokenizer)

我们加载之前留出的20个样本组成的测试数据集,并以对话风格格式化数据。但这一次,我们省略了助手的消息,并将其格式化为Hugging Face数据集:

def create_input_prompt(sample): return { “messages”: [ {“role”: “system”,“content”: system_message}, {“role”: “user”, “content”: sample[“text”]}, ] } from datasets import Dataset test_dataset = Dataset.from_list(mini_data) # Transform to conversational format test_dataset = test_dataset.map(create_input_prompt, remove_columns=test_dataset.features, batched=False)

单个样本测试

让我们使用SFT Llama3-8B生成关系提取输出,并将其与前两个输出在单个实例上进行比较:

#Generate the input prompt prompt = pipe.tokenizer.apply_chat_template(test_dataset[2][“messages”][:2], tokenize=False, add_generation_prompt=True) # Generate the output outputs = pipe(prompt, max_new_tokens=128, do_sample=False, temperature=0.1, top_k=50, top_p=0.1, ) # Display the results print(f"Question: {test_dataset[2][‘messages’][1][‘content’]}\n") print(f"Gold-RE: {test_sampler[2][‘gold_re’]}\n") print(f"LLama3-8B-RE: {test_sampler[2][‘test_re’]}\n") print(f"SFT-Llama3-8B-RE: {outputs[0][‘generated_text’][len(prompt):].strip()}")

We obtain the following:

Question: Long before any knowledge of electricity existed, people were aware of shocks from electric fish. Gold-RE: people|were aware of|shocks shocks|from|electric fish electric fish|had|electricity LLama3-8B-RE: electric fish|were aware of|shocks SFT-Llama3-8B-RE: people|were aware of|shocks shocks|from|electric fish

在这个例子中,我们观察到通过微调,Llama3-8B的关系提取能力有了显著的提升。尽管微调数据集既不是非常干净也不特别大,但结果令人印象深刻。

对于20个样本数据集上的完整结果,请参考Google Colab笔记本。请注意,推理测试需要的时间更长,因为我们以半精度加载模型。

结论

总之,通过使用Llama3-70B和一个可用的数据集,我们成功地创建了一个合成数据集,然后使用该数据集对Llama3-8B进行了特定任务的微调。这个过程不仅让我们熟悉了Llama3,而且还允许我们应用了Hugging Face的直接技术。我们观察到,与Llama2的工作经验非常相似,显著的改进是提高了输出质量和更有效的分词器。

对于那些有兴趣进一步推动边界的人,考虑用更复杂的任务挑战模型,例如对实体和关系进行分类,并使用这些分类来构建知识图谱。

References

1.Somin Wadhwa, Silvio Amir, Byron C. Wallace, Revisiting Relation Extraction in the era of Large Language Models, arXiv.2305.05003 (2023).

2.Meta, Introducing Meta Llama 3: The most capable openly available LLM to date, April 18, 2024 (link).

3.Philipp Schmid, Omar Sanseviero, Pedro Cuenca, Youndes Belkada, Leandro von Werra, Welcome Llama 3 — Met’s new open LLM, April 18, 2024.

4.Sebastian Raschka, Practical Tips for Finetuning LLMs Using LoRA (Low-Rank Adaptation), Ahead of AI, Nov 19, 2023.

5.Philipp Schmid, How to Fine-Tune LLMs in 2024 with Hugging Face, Jan 22, 2024.

Dataset

databricks-dolly-15K on Hugging Face platform (CC BY-SA 3.0)

Full Code and Processed Data

Github Repo

如何学习大模型 AI ?

由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。

但是具体到个人,只能说是:

“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。

这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

在这里插入图片描述

第一阶段(10天):初阶应用

该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。

  • 大模型 AI 能干什么?
  • 大模型是怎样获得「智能」的?
  • 用好 AI 的核心心法
  • 大模型应用业务架构
  • 大模型应用技术架构
  • 代码示例:向 GPT-3.5 灌入新知识
  • 提示工程的意义和核心思想
  • Prompt 典型构成
  • 指令调优方法论
  • 思维链和思维树
  • Prompt 攻击和防范

第二阶段(30天):高阶应用

该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。

  • 为什么要做 RAG
  • 搭建一个简单的 ChatPDF
  • 检索的基础概念
  • 什么是向量表示(Embeddings)
  • 向量数据库与向量检索
  • 基于向量检索的 RAG
  • 搭建 RAG 系统的扩展知识
  • 混合检索与 RAG-Fusion 简介
  • 向量模型本地部署

第三阶段(30天):模型训练

恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。

到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?

  • 为什么要做 RAG
  • 什么是模型
  • 什么是模型训练
  • 求解器 & 损失函数简介
  • 小实验2:手写一个简单的神经网络并训练它
  • 什么是训练/预训练/微调/轻量化微调
  • Transformer结构简介
  • 轻量化微调
  • 实验数据集的构建

第四阶段(20天):商业闭环

对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。

  • 硬件选型
  • 带你了解全球大模型
  • 使用国产大模型服务
  • 搭建 OpenAI 代理
  • 热身:基于阿里云 PAI 部署 Stable Diffusion
  • 在本地计算机运行大模型
  • 大模型的私有化部署
  • 基于 vLLM 部署大模型
  • 案例:如何优雅地在阿里云私有部署开源大模型
  • 部署一套开源 LLM 项目
  • 内容安全
  • 互联网信息服务算法备案

学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。

如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值