AGI 之 【Hugging Face】 的【文本摘要】的 [摘要基准 ] / [ GPT ] / [T5] / [ BART ] / [ 度量文本生成的质量] 的简单整理
目录
AGI 之 【Hugging Face】 的【文本摘要】的 [摘要基准 ] / [ GPT ] / [T5] / [ BART ] / [ 度量文本生成的质量] 的简单整理
一、简单介绍
AGI,即通用人工智能(Artificial General Intelligence),是一种具备人类智能水平的人工智能系统。它不仅能够执行特定的任务,而且能够理解、学习和应用知识于广泛的问题解决中,具有较高的自主性和适应性。AGI的能力包括但不限于自我学习、自我改进、自我调整,并能在没有人为干预的情况下解决各种复杂问题。
- AGI能做的事情非常广泛:
跨领域任务执行:AGI能够处理多领域的任务,不受限于特定应用场景。
自主学习与适应:AGI能够从经验中学习,并适应新环境和新情境。
创造性思考:AGI能够进行创新思维,提出新的解决方案。
社会交互:AGI能够与人类进行复杂的社会交互,理解情感和社会信号。
- 关于AGI的未来发展前景,它被认为是人工智能研究的最终目标之一,具有巨大的变革潜力:
技术创新:随着机器学习、神经网络等技术的进步,AGI的实现可能会越来越接近。
跨学科整合:实现AGI需要整合计算机科学、神经科学、心理学等多个学科的知识。
伦理和社会考量:AGI的发展需要考虑隐私、安全和就业等伦理和社会问题。
增强学习和自适应能力:未来的AGI系统可能利用先进的算法,从环境中学习并优化行为。
多模态交互:AGI将具备多种感知和交互方式,与人类和其他系统交互。
Hugging Face作为当前全球最受欢迎的开源机器学习社区和平台之一,在AGI时代扮演着重要角色。它提供了丰富的预训练模型和数据集资源,推动了机器学习领域的发展。Hugging Face的特点在于易用性和开放性,通过其Transformers库,为用户提供了方便的模型处理文本的方式。随着AI技术的发展,Hugging Face社区将继续发挥重要作用,推动AI技术的发展和应用,尤其是在多模态AI技术发展方面,Hugging Face社区将扩展其模型和数据集的多样性,包括图像、音频和视频等多模态数据。
- 在AGI时代,Hugging Face可能会通过以下方式发挥作用:
模型共享:作为模型共享的平台,Hugging Face将继续促进先进的AGI模型的共享和协作。
开源生态:Hugging Face的开源生态将有助于加速AGI技术的发展和创新。
工具和服务:提供丰富的工具和服务,支持开发者和研究者在AGI领域的研究和应用。
伦理和社会责任:Hugging Face注重AI伦理,将推动负责任的AGI模型开发和应用,确保技术进步同时符合伦理标准。
AGI作为未来人工智能的高级形态,具有广泛的应用前景,而Hugging Face作为开源社区,将在推动AGI的发展和应用中扮演关键角色。
(注意:以下代码运行,可能需要科学上网)
二、文本摘要
你可能曾经需要总结一份文件,包括研究文章、财务收益报告、一系列电子邮件。如果你仔细思考,这需要一系列的能力,包括理解长篇内容、推理内容、然后产生一段流畅的、包括原始文档主要主题的文本。此外,准确地总结新闻文章与总结法律合同非常不同,因此需要复杂的领域泛化能力。出于这些原因,总结文本(专业术语为文本摘要)对于神经语言模型,包括Transformer模型来说是一项困难的任务。尽管面临这些挑战,文本摘要因为能够显著加速领域专家的工作流程,企业可以通过文本摘要压缩内部知识、总结合同、自动生成社交媒体发布内容等。因此文本摘要NLP任务很有价值。
为了帮助你理解相关的挑战,本节将探讨如何利用Transformer预训练模型来进行文本摘要。摘要是一种经典的序列到序列(seq2seq)任务,需要输入文本和目标文本。
文本摘要是一种自然语言处理任务,其目标是从一个长文本中提取出简洁、重要的信息,生成一个简短的版本。文本摘要可以分为两种主要类型:抽取式摘要和生成式摘要。
- 抽取式摘要
抽取式摘要通过选择原始文本中的重要句子或段落,直接提取这些内容作为摘要。这种方法不改变原始文本中的词语和句子结构。
实现原理:
- 特征提取:首先,需要提取文本的各种特征,例如词频、句子位置、关键词、命名实体等。
- 重要性评分:基于提取的特征,计算每个句子的得分,以确定其重要性。
- 句子选择:根据重要性得分,选择最重要的句子来构建摘要。
难点:
- 重要性衡量:如何准确衡量句子的相对重要性。
- 冗余消除:避免选择内容重复的句子。
实现方式:
- 基于规则的方法:使用预定义的规则和统计方法来选择句子。
- 机器学习方法:使用有监督的学习算法,根据训练数据学习如何选择重要句子。
- 生成式摘要
生成式摘要通过理解原始文本并生成新的句子来概括其内容。这种方法可以创建更为自然和连贯的摘要,但也更加复杂。
实现原理:
- 编码器-解码器架构:使用序列到序列(Seq2Seq)模型,其中编码器将输入文本编码成上下文向量,解码器根据上下文向量生成摘要。
- 注意力机制:在解码过程中,模型可以关注输入文本的不同部分,从而生成更相关的内容。
- 预训练模型:使用预训练的语言模型(如BERT、GPT等)来提高生成摘要的质量。
难点:
- 内容连贯性:生成的摘要需要保持逻辑连贯,避免内容断裂。
- 信息完整性:确保生成的摘要包含原始文本中的关键信息。
- 模型复杂度:生成式摘要模型通常比抽取式摘要模型更复杂,需要更多的计算资源和训练数据。
实现方式:
- 经典的 Seq2Seq 模型:如基于 LSTM 的编码器-解码器模型。
- 预训练的 Transformer 模型:如 BERTSUM、T5、BART 等。
- Hugging Face 中的文本摘要
Hugging Face 提供了多种预训练模型和工具,可以方便地实现文本摘要任务。以下是一些常用的文本摘要模型和使用方法:
- 使用预训练模型进行摘要
以下是使用 Hugging Face 提供的 BART 模型进行文本摘要的示例代码:
from transformers import BartForConditionalGeneration, BartTokenizer # 加载预训练的BART模型和对应的tokenizer model_name = "facebook/bart-large-cnn" model = BartForConditionalGeneration.from_pretrained(model_name) tokenizer = BartTokenizer.from_pretrained(model_name) # 输入文本 input_text = """Your text to summarize goes here.""" # 对输入文本进行tokenize,并添加必要的模型输入 inputs = tokenizer([input_text], max_length=1024, return_tensors='pt') # 使用模型生成摘要 summary_ids = model.generate(inputs['input_ids'], num_beams=4, max_length=150, early_stopping=True) # 将生成的token序列转换回文本 summary = tokenizer.decode(summary_ids[0], skip_special_tokens=True) print(summary)
- 支持的摘要模型
Hugging Face 提供了多种用于文本摘要的预训练模型,包括但不限于:
- BART (facebook/bart-large-cnn)
- T5 (t5-small, t5-base, t5-large, t5-3b, t5-11b)
- PEGASUS (google/pegasus-xsum, google/pegasus-cnn_dailymail)
- 训练自己的摘要模型
如果需要更好地适应特定领域的文本摘要任务,可以使用自己的数据集对预训练模型进行微调。以下是一个简单的微调示例:
from transformers import Trainer, TrainingArguments, BartForConditionalGeneration, BartTokenizer from datasets import load_dataset # 加载数据集 dataset = load_dataset("cnn_dailymail", "3.0.0") # 加载预训练的BART模型和tokenizer model_name = "facebook/bart-large-cnn" model = BartForConditionalGeneration.from_pretrained(model_name) tokenizer = BartTokenizer.from_pretrained(model_name) # 数据预处理 def preprocess_function(examples): inputs = [doc for doc in examples['article']] model_inputs = tokenizer(inputs, max_length=1024, truncation=True) # 设定摘要作为目标 with tokenizer.as_target_tokenizer(): labels = tokenizer(examples['highlights'], max_length=150, truncation=True) model_inputs['labels'] = labels['input_ids'] return model_inputs tokenized_dataset = dataset.map(preprocess_function, batched=True) # 定义训练参数 training_args = TrainingArguments( output_dir="./results", evaluation_strategy="epoch", learning_rate=2e-5, per_device_train_batch_size=4, per_device_eval_batch_size=4, num_train_epochs=3, weight_decay=0.01, ) # 使用Trainer进行训练 trainer = Trainer( model=model, args=training_args, train_dataset=tokenized_dataset["train"], eval_dataset=tokenized_dataset["validation"], ) trainer.train()
文本摘要是一个复杂且具有挑战性的自然语言处理任务。通过使用 Hugging Face 提供的预训练模型和工具,可以大大简化文本摘要的实现过程。用户可以根据具体需求选择合适的模型,进行微调,以获得最佳的摘要效果。
在本节中,我们将建立自己的编码器-解码器模型,将多人对话压缩成简明的摘要。但在此之前,我们先来看看摘要领域中一个经典数据集:CNN/DailyMail语料库。
三、CNN/DailyMail数据集
CNN/DailyMail数据集包含大约30万对新闻文章及其对应的摘要,这些摘要由CNN和DailyMail附加到其文章的要点组成。这个数据集的一个要点是摘要是抽象出来的而不是提取出来的,这意味着摘要是使用新的句子组成的,而不是简单的对现有句子的摘录。该数据集可在Hub上获得(https://oreil.ly/jcRmb)。我们将使用3.0.0版本,这是一个为摘要设置的非匿名版本。我们可以像之前中看到的那样,使用version参数指定版本。我们深入了解一下它:
# 从 Hugging Face 的 transformers 库导入 pipeline 和 set_seed 函数
from transformers import pipeline, set_seed
# 从 Hugging Face 的 datasets 库导入 load_dataset 函数
from datasets import load_dataset
# 加载 CNN/DailyMail 数据集的版本 3.0.0
# 该数据集常用于文本摘要任务,包含新闻文章及其对应的摘要
dataset = load_dataset("cnn_dailymail", version="3.0.0")
# 打印数据集的列名,以了解数据集的结构和内容
print(f"Features: {dataset['train'].column_names}")
运行结果:
数据集有三列:article列包含新闻文章;highlights列包含摘要;id列是每篇文章的唯一标识。我们来看其中一篇文章的摘录:
# 从训练集中获取一个样本
sample = dataset["train"][1]
# 打印文章的一个片段(前500个字符),并显示文章的总长度
print(f"""
Article (excerpt of 500 characters, total length: {len(sample["article"])}):
""")
print(sample["article"][:500])
# 打印摘要及其长度
print(f'\nSummary (length: {len(sample["highlights"])}):')
print(sample["highlights"])
运行结果:
Article (excerpt of 500 characters, total length: 4051): Editor's note: In our Behind the Scenes series, CNN correspondents share their experiences in covering news and analyze the stories behind the events. Here, Soledad O'Brien takes users inside a jail where many of the inmates are mentally ill. An inmate housed on the "forgotten floor," where many mentally ill inmates are housed in Miami before trial. MIAMI, Florida (CNN) -- The ninth floor of the Miami-Dade pretrial detention facility is dubbed the "forgotten floor." Here, inmates with the most s Summary (length: 281): Mentally ill inmates in Miami are housed on the "forgotten floor" Judge Steven Leifman says most are there as a result of "avoidable felonies" While CNN tours facility, patient shouts: "I am the son of the president" Leifman says the system is unjust and he's fighting for change .
我们可以看到,文章比目标摘要长很多,就本例而言,差距是17倍。长文章对大多数Transformer模型都构成了挑战,因为上下文大小通常限制在1000个词元左右,只相当于几段文字。对于摘要来说,处理这个问题的标准但粗糙的方法是简单地截断超出模型上下文大小的文本。显然,在文本的末尾可能存在用于摘要的重要信息,但目前我们只能接受模型架构的这种局限性。
四、文本摘要pipeline
我们来看一下几种最流行的Transformer模型在前面例子中的表现如何。虽然我们将要探索的模型架构有不同的最大输入尺寸,但是我们将输入文本限制在2000个字符以使所有模型具有相同的输入,从而使输出更具可比性:
# 提取文章的前2000个字符作为示例文本
sample_text = dataset["train"][1]["article"][:2000]
# 创建一个字典来收集每个模型生成的摘要
summaries = {}
摘要中的一项惯例是通过换行符将摘要句子分开。我们可以在每个句号后添加一个换行符词元,但这个简单的启发式方法无法应对像“U.S.”或“U.N.”这样的字符串。NLTK(Natural Language Toolkit)包含了一个更复杂、可以区分句子末尾和缩写中出现的标点符号的算法:
# 导入 nltk 库,用于自然语言处理
import nltk
# 从 nltk 库中导入 sent_tokenize 函数,用于将文本分割成句子
from nltk.tokenize import sent_tokenize
# 下载 nltk 数据包 "punkt",该数据包包含句子分割模型
nltk.download("punkt")
# 定义一个字符串,其中包含两个句子
string = "The U.S. are a country. The U.N. is an organization."
# 使用 sent_tokenize 函数将字符串分割成句子
sentences = sent_tokenize(string)
# 打印分割后的句子
print(sentences)
(注意:如果没有安装 nltk , pip install nltk 安装即可)
运行结果:
['The U.S. are a country.', 'The U.N. is an organization.']
在接下来的介绍中,我们将加载多个大模型。如果你的内存不足,你可以将这些大模型替换为较小的checkpoint(例如“gpt”,“t5-small”),或者跳过本节,直接前往后面的 5 部分。
1、摘要基准
一种常见的新闻文章摘要基准是简单地取文章的前三句话。使用NLTK的句子词元分析器,我们可以轻松实现这样的基准:
# 定义一个函数,接受一个文本并返回其前三个句子的摘要
def three_sentence_summary(text):
# 使用 sent_tokenize 函数将文本分割成句子,并取前三个句子
return "\n".join(sent_tokenize(text)[:3])
# 调用 three_sentence_summary 函数,生成 sample_text 的摘要,并将其存储在 summaries 字典中,键为 "baseline"
summaries["baseline"] = three_sentence_summary(sample_text)
2、GPT-2
A. Radford et al., “Language Models Are Unsupervised Multitask Learners”(https://openai.com/blog/better-language-models),OpenAI(2019).
在之前的介绍中,我们已经看到了GPT-2如何根据一些提示生成文本。该模型令人惊讶的特点之一是,我们也可以使用它来生成摘要,只需在输入文本末尾附加“TL;DR”。“TL;DR”(“太长了;没看”的简写)通常用于表示一个长帖子的简短版本(例如Reddit等平台)。我们将通过使用Hugging Face Transformers库中的pipeline()函数重新创建原始论文的过程来开始我们的摘要实验
。我们将创建一个文本生成pipeline并加载GPT-2模型的大型版本(gpt2-xl):
# 从 transformers 库中导入 pipeline 和 set_seed 函数
from transformers import pipeline, set_seed
# 设置随机种子为 42,以确保结果的可重复性
set_seed(42)
# 使用 GPT-2 XL 模型创建一个文本生成管道
pipe = pipeline("text-generation", model="gpt2-xl")
# 定义一个查询字符串,将示例文本 sample_text 与 "TL;DR:\n" 连接起来
gpt2_query = sample_text + "\nTL;DR:\n"
# 使用文本生成管道生成摘要,最大长度为 512 个标记
pipe_out = pipe(gpt2_query, max_length=512, clean_up_tokenization_spaces=True)
# 从生成的文本中去掉原始查询字符串部分,并使用 sent_tokenize 函数将其分割成句子
# 将生成的摘要存储在 summaries 字典中,键为 "gpt2"
summaries["gpt2"] = "\n".join(
sent_tokenize(pipe_out[0]["generated_text"][len(gpt2_query) :])
)
注意:上面的老死机,可以换成模型 t5-small
# 导入必要的库和函数
from transformers import pipeline, set_seed
import nltk
from nltk.tokenize import sent_tokenize
# 定义一个空的字典 summaries,用于存储各个模型生成的摘要
summaries = {}
# 设置随机种子为 42,以确保结果的可重复性
set_seed(42)
# 假设 sample_text 是之前定义过的变量或字符串
sample_text = "Your sample text goes here."
# 使用 GPT-2 模型创建一个文本生成管道
pipe = pipeline("text-generation", model="gpt2")
# 定义一个查询字符串,将示例文本 sample_text 作为输入
gpt_query = sample_text
# 使用文本生成管道生成文本,最大长度为 512 个标记
pipe_out = pipe(gpt_query, max_length=512, clean_up_tokenization_spaces=True)
# 从生成的文本中去掉原始查询字符串部分,并使用 sent_tokenize 函数将其分割成句子
# 将生成的文本存储在 summaries 字典中,键为 "gpt2"
summaries["gpt2"] = "\n".join(
sent_tokenize(pipe_out[0]["generated_text"])
)
# 打印生成的文本
print(summaries["gpt2"])
运行结果:
这里我们通过对输入的查询进行切片,将生成的文本摘要存储在一个Python字典中,以便后面进行比较。
3、T5
接下来我们尝试T5 Transformer模型。正如我们在第3章中所看到的那样,这个模型的开发者们对NLP中的迁移学习进行了全面的研究,发现可以通过将所有任务都构建成文本到文本任务来创建一个通用的Transformer模型架构。T5 checkpoint是混合无监督数据(用于重建掩码单词)和多个任务(包括摘要)的监督数据进行训练的。因此,这些checkpoint可以直接使用与预训练中相同的提示来执行摘要,而无须微调。在这个框架中,用于让模型对文档进行摘要的输入格式为"summarize: <ARTICLE>",而用于翻译的格式则为"translate English to German: <TEXT>"。如图所示,这使得T5非常灵活,能够使用单个模型解决许多任务
![](https://i-blog.csdnimg.cn/direct/0db86f440ffe4c55bc83eadd1a6de56e.png)
除了翻译和摘要之外,还展示了CoLA(语言可接受性)和STSB(语义相似度)任务
我们可以直接使用pipeline()函数加载T5模型来进行摘要生成,该函数还会将输入格式化为文本对文本格式,因此我们无须在输入前添加"summarize":
# 导入必要的库和函数
from transformers import pipeline
# 使用 T5-large 模型创建一个摘要生成管道
pipe = pipeline("summarization", model="t5-large")
# 在样本文本上生成摘要
pipe_out = pipe(sample_text)
# 将生成的摘要分割成句子,并存储在 summaries 字典中,键为 "t5"
summaries["t5"] = "\n".join(sent_tokenize(pipe_out[0]["summary_text"]))
运行结果:
如果觉得上面数据较大可以使用下面 t5-small 模型
# 导入必要的库和函数
from transformers import pipeline, set_seed
import nltk
from nltk.tokenize import sent_tokenize
# 定义一个空的字典 summaries,用于存储各个模型生成的摘要
summaries = {}
# 设置随机种子为 42,以确保结果的可重复性
set_seed(42)
# 假设 sample_text 是之前定义过的变量或字符串
sample_text = "Your sample text goes here."
# 使用 T5-small 模型创建一个文本生成管道
pipe = pipeline("summarization", model="t5-small")
# 定义一个查询字符串,将示例文本 sample_text 截取至2000字符长度
gpt2_query = sample_text[:2000]
# 使用文本生成管道生成摘要,最大长度为 512 个标记
pipe_out = pipe(gpt2_query, max_length=512, min_length=30, clean_up_tokenization_spaces=True)
# 从生成的文本中去掉原始查询字符串部分,并使用 sent_tokenize 函数将其分割成句子
# 将生成的摘要存储在 summaries 字典中,键为 "t5-small"
summaries["t5-small"] = "\n".join(
sent_tokenize(pipe_out[0]["summary_text"])
)
# 打印生成的摘要
print(summaries["t5-small"])
运行结果:
your sample text goes here. if you have a sample text, send it to a samaritans page.
4、BART
M. Lewis et al., “BART: Denoising Sequence-to-Sequence Pre-training for Natural Language Generation, Translation, and Comprehension”(https://arxiv.org/abs/1910.13461), (2019).
B A RT也采用了编码器-解码器架构,并被训练用于重构损坏的输入。它是一个结合了BERT和GPT-2的预训练方案 。我们将使用facebook/bart-large-ccn checkpoint,该checkpoint专门使用了CNN/DailyMail数据集进行微调:
# 导入必要的库和函数
from transformers import pipeline
# 使用 BART-large 模型创建一个摘要生成管道
pipe = pipeline("summarization", model="facebook/bart-large-cnn")
# 在样本文本上生成摘要
pipe_out = pipe(sample_text)
# 将生成的摘要分割成句子,并存储在 summaries 字典中,键为 "bart"
summaries["bart"] = "\n".join(sent_tokenize(pipe_out[0]["summary_text"]))
运行结果:
5、PEGASUS
J. Zhang et al., “PEGASUS: Pre-Training with Extracted Gap-Sentences for Abstractive Summarization”(https://arxiv.org/abs/1912.08777),(2019).
PEGASUS与BART一样,也是一个编码器-解码器Transformer模型 。如下图所示,它的预训练目标是预测多句文本中的掩码句子。作者认为,预训练目标越接近下游任务,它就越有效。为了找到一个更接近摘要而不是一般语言建模的预训练目标,他们自动识别了一个非常大的语料库中包含其周围段落大部分内容的句子(使用摘要度量指标作为内容重叠的启发式),并对PEGASUS模型进行预训练以重构这些句子,从而获得了一个用于文本摘要的最先进模型。
![](https://i-blog.csdnimg.cn/direct/4721f627c4f2463e9e33bf91ce0291ac.png)
这个模型有专门的换行符词元,所以我们不需要使用sent tokenize()函数:
# 导入必要的库和函数
from transformers import pipeline
# 使用 Pegasus 模型创建一个摘要生成管道
pipe = pipeline("summarization", model="google/pegasus-cnn_dailymail")
# 在样本文本上生成摘要
pipe_out = pipe(sample_text)
# 将生成的摘要文本处理成更易读的格式,并存储在 summaries 字典中,键为 "pegasus"
summaries["pegasus"] = pipe_out[0]["summary_text"].replace(" .", ".\n")
运行结果:
报错,暂时不处理
TypeError: Couldn't build proto file into descriptor pool: duplicate file name sentencepiece_model.proto
五、比较不同的摘要
现在我们已经用四个不同的模型生成了摘要,我们来比较一下它们的结果。请记住,其中一个模型根本没有在数据集上进行训练(GPT-2),一个模型在执行其他任务的同时进行了微调(T5),而另外两个模型则专门针对这个任务进行了微调(BART和PEGASUS)。我们来看看这些模型生成的摘要:
# 打印“GROUND TRUTH”标识符
print("GROUND TRUTH")
# 打印训练数据集中的第一个样本的摘要
print(dataset["train"][1]["highlights"])
print("")
# 遍历 summaries 字典中的所有模型名称和对应的摘要
for model_name in summaries:
# 打印模型名称并将其转换为大写格式
print(model_name.upper())
# 打印对应模型生成的摘要
print(summaries[model_name])
print("")
运行结果:
GROUND TRUTH Mentally ill inmates in Miami are housed on the "forgotten floor" Judge Steven Leifman says most are there as a result of "avoidable felonies" While CNN tours facility, patient shouts: "I am the son of the president" Leifman says the system is unjust and he's fighting for change . GPT2 Your sample text goes here. For more information, call 888-466-2311. Hear from our sponsors at a local sports food market. Visit their website at www.seattle-seattle-vegetables.com, or write at tmsnrt.com/b/1QYV7J. Tell our sponsors the restaurant you were with. Read or Share this story: http://tnne.ws/1R0LvKX T5 a sample text goes here . a link back to this page takes you to the author's web site . the sample text you provide here is from a free . BART Use the weekly Newsquiz to test your knowledge of stories you saw on CNN.com. Your sample text goes to the bottom of the page. Today's newsquiz includes the question, "What do you think the weather will be like in the next few days?"
我们在查看模型输出时首先注意到的一件事是,由GPT-2生成的摘要与其他摘要非常不同。它不是给出文本的摘要,而是把人物摘要出来。由于GPT-2没有明确训练生成真实的摘要,它经常会“幻想”或虚构事实。例如,在撰写本文时,Nesta并不是世界上最快的人,而是排名第九。将其他三个模型的摘要与基准摘要进行比较,我们发现它们有着显著的重叠,其中PEGASUS的输出与基准摘要最相似。
现在我们已经检查了几个模型,我们要决定在生产环境中使用哪一个。所有四个模型似乎都提供了合理的质量结果,我们可以生成更多的例子来帮助我们做出决定。但是,这并不是确定最佳模型的系统性方法!理想情况下,我们将定义一个指标,在一些基准数据集上为所有模型进行度量,并选择性能最佳的那一个。但是,如何为文本生成定义一个指标呢?我们所看到的标准指标,如准确率、查准率和召回率,都不易用于此任务。对于每个由人类撰写的“黄金标准”摘要,可能有数十个可接受的摘要(通过同义词、释义或稍微不同的表达方式)。
接下来我们将探讨一些常见的、用来度量生成文本的质量的指标。
六、度量生成文本的质量
好的度量指标非常重要,因为我们不但在训练模型时需要靠它们来度量模型的性能,而且在模型投入使用后也需要它们。如果我们的指标不好,那么在模型投入使用后,我们可能会对模型退化失去警觉,如果这些指标与业务目标不一致,那么我们可能无法创造任何价值。
在文本生成任务上度量性能并不像情感分析或命名实体识别等标准分类任务那么容易。以翻译为例,对于像英语中的“I love dogs!”这样的句子,翻译成西班牙语可能有多种有效的可能性,比如“¡Me encantan los perros!”或“¡Me gustan los perros!”。仅仅通过检查输出翻译与参考翻译是否精确匹配是不理想的,即使是人类也不一定能够做到这点,因为我们每个人的文本写作风格略有不同(甚至自己与自己都不同,取决于具体日期甚至年份!)。幸运的是,有一些替代方案。
两个最常用的用于评估生成文本的度量指标是BLEU和ROUGE。我们看看它们的定义。
1、BLEU
K. Papineni et al., “BLEU:A Method for Automatic Evaluation of Machine Translation,”Proceedingsof the 40th Annual Meeting of the Association for Computational Linguistics(July 2002): 311-318,http://dx.doi.org/10.3115/1073083.1073135.
BLEU的思想很简单 :与其看生成文本中有多少个词元与参考文本的词元完全对齐,不如看单词或n-gram。BLEU是基于查准率的度量指标,这意味着当我们比较两个文本时,我们计算生成文本中出现在参考文本中的单词数,并将其除以生成文本的长度。
然而,这种普通的查准率存在一个问题。假设生成文本只是一遍又一遍地重复同一个单词,并且这个单词也出现在参考文本中。如果它重复了与参考文本长度相同的次数,那么我们就得到了完美的查准率!因此,BLEU论文的作者引入了一个轻微的修改:只计算一个单词在参考文本中出现的次数。这里举一个例子,假设我们的参考文本为“the cat is on the mat”,生成文本为“the the the the the the”。
从这个简单的例子中,我们可以按如下方式计算查准率:
我们可以看到,简单的校正已经产生了一个更合理的值。现在我们通过不仅考虑单个单词,而且考虑n-gram来扩展这个概念。假设我们有一个生成的句子snt,我们想要将其与参考句子snt'进行比较。我们提取所有可能的n-gram,进行统计以获取查准率Pn:
为了避免重复生成同一个单词,分子中的计数被截断(clip)。这意味着n-gram的出现计数被限制在它在参考句子中出现的次数。还要注意,这个公式中对于一个句子的定义并不非常严格,所以如果你的生成文本包含多个句子,则它们会被视为一个句子。
通常在测试集中我们有多个样本需要评估,因此我们需要稍微扩展方程式,对语料库C中所有样本进行求和:
我们快实现我们的目标了。由于我们不关注召回率,因此相比于更长的句子,所有短而准确的生成序列都有优势。因此,查准率评分更有利于短的生成序列。为了补偿这一点,BLEU的作者引入了一个额外的项,即短句惩罚(brevity penalty,在下面公式中简写为B R):
通过取最小值,我们确保这个惩罚项永远不会超过1,并且指数项在生成文本长度l小于参考文本长度l时,指数项将呈指数级下降。你可能会问,为什么我们不使用像F1分数这样的东西来考虑召回率呢?答案是,通常在翻译数据集中,会有多个参考句子而不是只有一个。因此,如果我们同时度量召回率,就会激励翻译使用所有参考句子中的所有单词。因此,更好的选择是寻求译文中的高查准率,并确保译文和参考具有相似的长度。
genref
最终,我们可以将所有内容整合在一起,得出BLEU分数的公式:
最后一个术语是修改后的n-gram N的查准率的几何平均值。在实践中,通常为BLEU-4分数。然而,你可能已经看到这个指标有许多局限性,例如,它没有考虑到同义词,并且推导过程中的许多步骤似乎是不通用且相当脆弱的启发式方法。你可以在Rachel Tatman的博客文章“Evaluating Text Output in NLP: BLEU at Your Own Risk”(在NLP中评估文本输出:自己承担BLEU的风险)(https://oreil.ly/nMXRh)中找到对BLEU的缺陷的精彩阐述。
总的来说,文本生成领域仍在寻找更好的度量指标,克服BLEU等指标的局限性是当前研究的一个活跃领域。BLEU指标的另一个缺点是它期望文本已经进行了词元化。如果没有使用完全相同的文本词元化方法,则可能会导致不同的结果。SacreBLEU指标通过内置词元化步骤来解决这个问题。因此,它是基准测试的首选指标。
理论部分我们已经学习完了,接下来需要通过代码计算生成文本的分数。我们是否需要写Python代码实现所有这些逻辑?不用担心,Hugging Face Datasets库已经提供了现成的指标!加载这些指标的方式与加载数据集的方式相同:
# 从 datasets 库中导入 load_metric 函数
from datasets import load_metric
# 使用 load_metric 函数加载 "sacrebleu" 评价指标,并传递 trust_remote_code=True 参数
bleu_metric = load_metric("sacrebleu", trust_remote_code=True)
(注意:可能需要安装 pip install sacrebleu)
bleu metric对象是Metric类的一个实例,它的工作方式类似于聚合器:你可以使用add()添加单个实例,也可以通过add batch()添加整个批量。在添加完需要评估的所有样本之后,调用compute()即可计算指标。然后会返回一个包含多个值的字典,包括每个n-gram的查准率、长度惩罚以及最终的BLEU分数。现在我们将bleu metric对象应用于之前的示例:
# 导入 pandas 和 numpy 库
import pandas as pd
import numpy as np
# 使用 BLEU 评价指标的 add 方法添加预测和参考文本
bleu_metric.add(
prediction="the the the the the the", # 生成的预测文本
reference=["the cat is on the mat"] # 参考文本
)
# 计算 BLEU 分数,使用 "floor" 平滑方法和平滑值 0
results = bleu_metric.compute(smooth_method="floor", smooth_value=0)
# 对结果中的精确度 (precisions) 进行四舍五入保留两位小数
results["precisions"] = [np.round(p, 2) for p in results["precisions"]]
# 将结果转换为 pandas DataFrame,并按行显示,将 "Value" 作为列名
pd.DataFrame.from_dict(results, orient="index", columns=["Value"])
运行结果:
Value | |
score | 0 |
counts | [2, 0, 0, 0] |
totals | [6, 5, 4, 3] |
precisions | [33.33, 0.0, 0.0, 0.0] |
bp | 1 |
sys_len | 6 |
ref_len | 6 |
BLEU分数也适用于存在多个参考译文的情况。这就是为什么reference以列表形式传的原因。为了使n-gram中的零计数对度量结果更平滑,BLEU集成了多个修改查准率计算的方法。一种方法是向分子添加一个常数。这样,缺失的n-gram不会导致分数自动降为零。只不过在本例中为了解释这些值,我们将smooth value设置为0关闭了该方法。
我们可以看到1-gram的查准率确实是2/6,而2/3/4-gram的查准率都是0[关于个别指标如计数和bp的更多信息,请参见SacreBLEU仓库(https://oreil.ly/kiZPl)]。这意味着几何平均值为零,同样BLEU分数也为零。我们再看看前面提到的另一个几乎正确的例子:
# 使用 BLEU 评价指标的 add 方法添加预测和参考文本
bleu_metric.add(
prediction="the cat is on mat", # 生成的预测文本
reference=["the cat is on the mat"] # 参考文本
)
# 计算 BLEU 分数,使用 "floor" 平滑方法和平滑值 0
results = bleu_metric.compute(smooth_method="floor", smooth_value=0)
# 对结果中的精确度 (precisions) 进行四舍五入保留两位小数
results["precisions"] = [np.round(p, 2) for p in results["precisions"]]
# 将结果转换为 pandas DataFrame,并按行显示,将 "Value" 作为列名
pd.DataFrame.from_dict(results, orient="index", columns=["Value"])
运行结果:
Value | |
score | 57.893007 |
counts | [5, 3, 2, 1] |
totals | [5, 4, 3, 2] |
precisions | [100.0, 75.0, 66.67, 50.0] |
bp | 0.818731 |
sys_len | 5 |
ref_len | 6 |
们注意到查准率分数要好得多。预测中的所有1-gram都匹配,然而我们从查准率分数中也发现一些问题。对于4-gram,只有两个候选项["the","cat","is","on"]和["cat","is","on","mat"],其中后者不匹配,因此查准率为0.5。
BLEU分数广泛用于评估文本,尤其是在机器翻译中,因为精确的翻译通常比包含所有可能和合适的单词的翻译更受青睐。
BLEU分数也同样应用于其他领域,例如文本摘要,只不过情况有些不同。在文本摘要中,我们希望生成文本能够包含所有重要信息,因此我们倾向于高召回率。这点导致通常会使用ROUGE分数。
2、ROUGE
C-Y. Lin,“ROUGE: A Package for Automatic Evaluation of Summaries,”Text SummarizationBranches Out(July 2004),https://aclanthology.org/W04-1013.pdf.
ROUGE分数是专门针对文本摘要等高召回率比查准率更重要的应用场景开发的 。这种方法与BLEU分数非常相似,我们观察不同的n-gram,并比较它们在生成文本和参考文本中的出现次数。不同之处在于,对于ROUGE,我们检查参考文本中有多少个n-gram也出现在生成文本中。对于BLEU,我们观察生成文本中有多少个n-gram出现在参考文本中,因此我们可以使用查准率公式,只需进行轻微修改,即在分母中计算生成文本中参考n-gram的(未截断)出现次数:
以上是ROUGE最初的版本。随后,研究人员发现完全删除查准率会产生强烈的负面影响。回到没有截断计数的BLEU公式,我们可以同时度量查准率和召回率,然后将两者的ROUGE分数组合在调和平均数中,得到F1分数。这个分数就是ROUGE现在常用的版本。
ROUGE还有一个版本是度量最长公共子串(LCS),称为ROUGE-L。LCS可以计算任意两个字符串之间的最长公共部分。例如,abab和abc的LCS是ab,它的长度为2。如果我们想比较两个样本之间的这个值,我们需要对其进行规范化,否则较长的文本将处于优势地位。为了实现这一点,ROUGE的发明者提出了一个类似F分数的方案,其中LCS与参考文本和生成文本的长度进行规范化,然后两个规范化分数被混合在一起:
,其中
这样可以正确地对LCS分数进行规范化,并可以在样本之间进行比较。Hugging Face Datasets库包含了两种ROUGE的变体:一种计算每个句子的分数并对摘要进行平均(ROUGE-L),另一种直接计算整个摘要的分数(ROUGE-Lsum)。
我们可以按照以下方式加载度量指标:
# 使用 load_metric 函数加载 "rouge" 评价指标,并传递 trust_remote_code=True 参数
rouge_metric = load_metric("rouge", trust_remote_code=True)
(注意:可能需要安装 rouge 包, pip install rouge_score)
前面我们已经用GPT-2和其他模型生成了一组摘要,现在我们有了可以系统比较这些摘要的度量方法。让我们对模型生成的所有摘要应用ROUGE分数:
# 从数据集中获取参考摘要
reference = dataset["train"][1]["highlights"]
# 用于存储每个模型的 ROUGE 分数的列表
records = []
# 要计算的 ROUGE 分数的名称列表
rouge_names = ["rouge1", "rouge2", "rougeL", "rougeLsum"]
# 对每个模型生成的摘要进行迭代
for model_name in summaries:
# 将模型生成的摘要和参考摘要添加到 ROUGE 评价指标中
rouge_metric.add(prediction=summaries[model_name], reference=reference)
# 计算 ROUGE 分数
score = rouge_metric.compute()
# 从计算结果中提取指定的 ROUGE 分数,并存储在字典中
rouge_dict = dict((rn, score[rn].mid.fmeasure) for rn in rouge_names)
# 将每个模型的 ROUGE 分数字典添加到 records 列表中
records.append(rouge_dict)
# 将 records 列表转换为 pandas DataFrame,并以模型名称作为索引
pd.DataFrame.from_records(records, index=summaries.keys())
运行结果:
rouge1 | rouge2 | rougeL | rougeLsum | |
gpt2 | 0.0577 | 0 | 0.038462 | 0.057692 |
t5 | 0.1282 | 0 | 0.102564 | 0.128205 |
bart | 0.2128 | 0.02174 | 0.148936 | 0.170213 |
Hugging Face Datasets库的ROUGE度量指标还会计算置信区间(默认为第5个和第95个百分位数)。平均值存储在mid属性中,区间可以通过low和high检索。
这些结果显然不太可靠,因为我们只看了一个样本,但是我们可以比较这个例子的摘要质量。结果表格证实了我们的观察结果,在我们的模型中,GPT-2表现最差,这点并不令人惊讶,因为它是这些模型中唯一没有专门针对文本摘要进行训练的模型。然而,令人惊讶的是,仅使用前三个句子的简单的基准模型(baseline)与其他具有数十亿参数的Transformer模型相比表现也不错!PEGASUS和BART是整体最好的模型(ROUGE分数越高越好),但在ROUGE-1和LCS分数方面,T5略好一些。依据这些结果,T5和PEGASUS为最佳模型,但是再次强调,我们需要谨慎看待这些结果,因为我们仅在一个例子上评估了这些模型。基于PEGASUS论文中的结果,我们预计PEGASUS在CNN/DailyMail数据集上表现可能会优于T5。
我们看看是否能够用PEGASUS复现那些结果。