作者:Joe Davison
编译:ronghuaiyang
原文链接:
使用最新的NLP技术来进行零样本学习的一些进展和工作。
自然语言处理现在是一个非常令人兴奋的领域。近年来,社区已经开始找到一些非常有效的方法,从互联网上大量的未标记数据中学习。从无监督模型进行迁移学习的成功使得我们在下游监督学习任务上超越了几乎所有现有的基准。随着我们继续开发新的模型架构和无监督的学习目标,“state of the art”持续的成为许多任务中的目标,在这些任务中有大量的标签数据可用。
随着模型的持续增长,一个主要的优势是我们看到下游任务对大量标注数据的依赖在缓慢减少。Open AI团队发布了一份预印本,描述了他们迄今为止最大的模型GPT-3,包含1750亿个参数。这篇论文的题目是"Language Models are Few-Shot Learners",它表明了超大的语言模型可以在下游任务中以比小型模型少得多的特定任务的数据进行竞争。
随着参数数量的增加,GPT-3少样本学习的性能也在增加
然而,这种尺寸的模型在实际应用中仍然是不实际的。例如,GPT-3的最大版本必须跨几十个GPU,才能放到内存中。在许多真实世界的情况中,标注数据要么缺少要么完全不可用。比GPT-3小得多的模型,如BERT,仍被证明在其权重中编码了大量信息。如果我们在这方面够聪明的话,我们似乎能够找出一些技术来将这些模型应用到下游任务中,从而利用这些潜在的信息,而不需要那么多特定于任务的标注数据。
当然,在这个领域已经做了一些研究。在这篇文章中,我将展示一些技术,这些技术来自已发表的研究和我们自己在Hugging Face的实验,用于使用最先进的NLP模型进行序列分类,而不需要大型标注训练集。
什么是零样本学习?
传统上,zero-shot learning (ZSL)通常指的是一种相当具体的任务类型:在一组标签上学习一个分类器,然后分类器在另一组从未见过的标签上进行评估。最近,特别是在NLP中,它被更广泛地用于表示让一个模型去做一些它没有被明确训练去做的事情。一个著名的例子是在GPT-2论文中,作者用语言模型评估了像机器翻译这样的下游任务,而没有直接对这些任务进行调优。
定义并不是那么重要,但是理解这个术语被用于不同的方式是有用的,因此在比较不同的方法时,我们应该注意理解实验环境。例如,为了让模型能够预测没有训练数据的类,传统的zero-shot学习需要为没见过的类别(例如一组视觉属性或类名)提供某种类型的描述符。不同的zero-shot方法可能对允许的类描述符采用不同的规则,理解这一点可以在讨论这些技术时提供相关的上下文。
隐嵌入方法
计算机视觉中zero shot learning的一种常见方法是使用现有的特征提取器将图像和任何可能的类名嵌入到它们相应的潜在表示中。然后,他们可以使用一些训练集,只使用可用标签的子集来学习线性投影,以对齐图像和标签嵌入。在测试时,这个框架允许人们将任何标签(可见或不可见)和图像嵌入到相同的潜空间中,并度量它们之间的距离。
在文本领域中,我们的优势是可以简单地使用单个模型将数据和类名嵌入到相同的空间中,从而消除了对数据需求很大的对齐步骤。这并不是一项新技术 —— 一段时间以来,研究人员和从业者以类似的方式对词进行池化。但是最近我们看到句子嵌入模型的质量有了显著的提高。因此,我们决定使用Sentence-BERT进行一些实验,作为获取序列和标签嵌入的一种方法,这是一种最新的技术,可以对池化的BERT序列表示进行调优,以增加语义丰富度。
假设我们有一个序列嵌入模型Φ~sent~,以及一组可能的类别C的名字,我们对给定的序列x进行分类:
这里有一个例子代码显示可以使用Sentence-BERT作为我们的文本嵌入模型Φ~sent~:
# load the sentence-bert model from the HuggingFace model hub
!pip install transformers
from transformers import AutoTokenizer, AutoModel
from torch.nn import functional as F
tokenizer = AutoTokenizer.from_pretrained('deepset/sentence_bert')
model = AutoModel.from_pretrained('deepset/sentence_bert')
sentence = 'Who are you voting for in 2020?'
labels = ['business', 'art & culture', 'politics']
# run inputs through model and mean-pool over the sequence
# dimension to get sequence-level representations
inputs = tokenizer.batch_encode_plus([sentence] + labels,
return_tensors='pt',
pad_to_max_length=True)
input_ids = inputs['input_ids']
attention_mask = inputs['attention_mask']
output = model(input_ids, attention_mask=attention_mask)[0]
sentence_rep = output[:1].mean(dim=1)
label_reps = output[1:].mean(dim=1)
# now find the labels with the highest cosine similarities to
# the sentence
similarities = F.cosine_similarity(sentence_rep, label_reps)
closest = similarities.argsort(descending=True)
for ind in closest:
print(f'label: {labels[ind]} \t similarity: {similarities[ind]}')
label: politics similarity: 0.21561521291732788
label: business similarity: 0.004524140153080225
label: art & culture similarity: -0.027396833524107933
注意:这段代码使用deepset/sentence_bert,这是S-BERT模型的最小版本。我们的实验使用的是更大的模型,目前只能在sentence-transformers可用,我们希望很快在Hugging Face model hub中看到这些模型。
这种方法的一个问题是,Sentence-BERT被设计用来学习有效的句子级别的嵌入,而不是像我们的类名那样的单个或多个单词表示。因此,假设我们的标签嵌入在语义上不如流行的词嵌入方法(即word2vec)显著是合理的。这在下面的t-SNE可视化图中可以看到,数据似乎按类别(颜色)很好地聚在一起,但标签的排列很差。但是,如果使用单词向量作为标签表示,则需要带标注的数据来学习S-BERT序列表示和word2vec标签表示之间的对齐。
在我们自己的一些内部实验中,我们用以下步骤解决了这个问题:
- 取word2vec模型词汇表中最常见的单词V
- 使用word2vec获取每个单词的嵌入Φ~word~(V)
- 使用S-BERT获得每个单词的嵌入Φ~sent~(V)
- 使用L2正则化学习一个从Φ~sent~(V)映射到Φ~word~(V)的最小二乘线性投影矩阵Z
由于我们只学习了单个单词嵌入的这种投影,我们不能期望它学习到S-BERT序列表示和word2vec嵌入的标签之间的有效映射。相反,我们在分类中使用Z作为对序列和标签的S-BERT嵌入的额外转换:
这个过程可以看作是一种降维。如下面的t-SNE可视化图所示,这种投影使得标签嵌入与其对应的数据聚类更好地对齐,同时保持S-BERT与合并词向量相比的优越性能。重要的是,除了按单词频率排序的word2vec字典外,这个过程不需要任何其他数据。
在Yahoo Answers主题分类任务中,我们发现有此投影步骤的F1分别为46.9和31.2。在上下文方面,Yahoo Answers有10个类,[监督模型](https://paperswithcode.com/sota/text- classificing-on yahoo-answers)在70年代中期达到了这个准确性。
当有一些标注数据的时候
这种技术非常灵活,可以很容易地适应以下情况:当可用的标记数据数量有限(few-shot learning),或者我们只对感兴趣的类的子集有标注的数据(传统的zero-shot learning)。
要做到这一点,我们只需从任何可用标签的嵌入数据中学习一个附加的最小二乘投影矩阵。然而,重要的是,我们这样做的时候不要过拟合我们有限的数据。我们的嵌入本身表现得很好,所以我们需要在它们之间找到一个投影,从我们拥有的训练数据中学习,同时仍然利用这些表示的语义丰富性。
为此,我们增加了L2正则化的一种变体,它将权值往单位矩阵上靠,而不是降低其范数。如果我们定义X~Tr~, Y~Tr~为我们的训练数据和标签,Φ(X) =Φ~sent~(X)Z是我们的嵌入函数,我们的正则化的目标是:
这等价于对以单位矩阵为中心的权重和由λ控制的方差采用高斯先验的贝叶斯线性回归。通过推动W靠近单位矩阵,我们有效地将投影嵌入Φ(X) W^*^推向Φ(X),这正是我们想做的事。通俗地说,我们之前有一个先验的知识,我们的数据的最好的表示是我们嵌入函数Φ(X)I~d~ =Φ(X),当我们遇到更多的训练数据的时候,我们就更新这个先验的知识。
和神经语言模型一样推理分类
现在我们将探索一种替代方法,它不仅将序列和标签嵌入到相同的潜在空间中,在那里它们的距离可以被测量,而且可以告诉我们一些关于模型外两个不同序列的兼容性的东西。
快速回顾一下,自然语言推理考虑了两个句子:一个“premise”和一个“hypothesis”。任务是确定假设在给定前提下是真(蕴含)还是假(矛盾)。
![example NLI sentences](Zero-Shot Learning in Modern NLP.assets/nli-examples.png)
当使用像BERT这样的transformer架构时,NLI数据集通常是通过序列对分类建模的。也就是说,我们通过模型将前提和假设作为不同的部分一起输入,并学习一个预测[contradiction, neutral, entailment]的分类头。
该方法由Yin等人(2019)提出,使用一个预训练的MNLI序列对分类器作为开箱即用的零样本文本分类器,实际上工作得很好。其想法是将我们感兴趣的标记序列作为“premise”,并将每个候选标记转化为“hypothesis”。如果NLI模型预测premise“蕴含”了hypothesis,我们就把这个标签当作是真的。参见下面的代码片段演示了Transformers可以很容易做到这一点。
# load model pretrained on MNLI
from transformers import BartForSequenceClassification, BartTokenizer
tokenizer = BartTokenizer.from_pretrained('bart-large-mnli')
model = BartForSequenceClassification.from_pretrained('bart-large-mnli')
# pose sequence as a NLI premise and label (politics) as a hypothesis
premise = 'Who are you voting for in 2020?'
hypothesis = 'This text is about politics.'
# run through model pre-trained on MNLI
input_ids = tokenizer.encode(premise, hypothesis, return_tensors='pt')
logits = model(input_ids)[0]
# we throw away "neutral" (dim 1) and take the probability of
# "entailment" (2) as the probability of the label being true
entail_contradiction_logits = logits[:,[0,2]]
probs = entail_contradiction_logits.softmax(dim=1)
true_prob = probs[:,1].item() * 100
print(f'Probability that the label is true: {true_prob:0.2f}%')
Probability that the label is true: 99.04%
在论文中,在Yahoo Answers上,使用最小版本的BERT微调,在Multi-genre NLI (MNLI)语料上调优,作者给出的标签加权F1值为37.9。通过简单地使用MNLI上预训练的更大、最新的Bert模型,我们能够将这个数字提高到53.7。
当有一些标注数据的时候
在少量带标注的数据点上对这个模型进行调优是无效的,因此它不是特别适合于few-shot的情况。然而,在传统的zero-shot中,我们有足够的数据,只有有限的类,这个模型会比较好。训练可以通过将一个序列通过两次模型来完成:一次使用正确的标签,一次使用随机选择的假标签,优化交叉熵。
经过调优后出现的一个问题是,该模型预测的见过标签的概率比未见过的要高得多。为了减轻这个问题,作者引入了一个处理,在测试时对训练时看到的标签进行惩罚。
当成是完形填空任务进行分类
一种正在研究中的方法是Schick et al. (2020)的模式利用训练(PET)。在本文中,作者将文本分类重新定义为一个完形填空任务。完形填空问题考虑一个部分被掩盖的序列,需要根据上下文预测缺失的值。PET需要一个专业人员来构建几个与任务相关的closize样式的模板,在主题分类的情况下,这些模板可能类似于以下内容:
用于主题分类的完形模板示例。在Yahoo answers的情况下,a和b是问题和答案,是模型必须预测的类名。
然后,一个预训练好的掩码语言模型将负责从每个完形填空句子的可能的类名中为掩码(空白)的词选择最有可能的值。
结果是对每个数据点进行一组有噪声的类预测。这个过程单独作为一个基本的zero-shot分类器。此外,还介绍了一种知识的提取方法。从完形填空任务生成一组预测后,这些预测值被用作代理标签,在其上从头开始训练一个新的分类器。我的直觉是,这个步骤是有效的,因为它允许我们对整个测试集进行集体推断,允许模型从它所预测的集合中学习,而不是单独处理每个测试点。我怀疑这一步骤将特别有助于适应新的领域,和MLM不一样的训练语料库。
在他们论文的最新版本中,作者还讨论了在PET之上的一个迭代的自我训练过程,该过程报告了在Yahoo Answers上令人印象深刻的70.7%的准确性,几乎接近最先进的监督分类方法的性能。
这让我回到了我之前的观点,即在比较不同的方法时要考虑实验参数。尽管PET的性能显著优于这里描述的其他方法,但它也利用了其他方法无法访问的数据:多个特定任务、手工完成的完形填空句子和用于提取/自学习步骤的大量未标记数据。我这么说并不是要批评PET,也不是要作者将他们自己与我在这里列出的方法进行比较,只是要强调在比较不同的方法时要注意的重要性,这些方法在某种意义上都可以被认为是“zero-shot”。
当有一些标注数据的时候
作者提出了一种成熟的方法,用于在某些训练数据可用的情况下使用PET,有效地最小化了对任何可用训练数据的优化完形预测和标准MLM损失之间的联合损失。详细信息还没有公布,所以如果你感兴趣,我强烈建议你查看一下他们的preprint, YouTube tutorial,或GitHub repo。
在少量资源的语言上
在NLP中,一个极其重要的数据缺失的情况是在少量资源的语言上。幸运的是,这是一个非常活跃的研究领域,有很多关于它的文章。对于那些对这个领域感兴趣的人,我强烈推荐Graham Neubig最近发布的[Low Resource NLP Bootcamp](https://github.com/neubig/lowresournlp-bootcamp -2020)。这是一个以GitHub repo的形式提供的极好的资源,其中包含8个讲座(加上练习),重点介绍了数据scarse语言中的NLP。另外,我建议你查看Sebastian Ruder的著作,包括“A survey of cross-lingual word embedding models”。