# load the sentence-bert model from the HuggingFace model hub!pip install transformersfrom transformers import AutoTokenizer, AutoModelfrom torch.nn import functional as Ftokenizer = 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 representationsinputs = 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 sentencesimilarities = 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 是最小参数量版本的Sentence-BERT模型。我们的实验使用的是较大的模型,目前仅在sentence-transformers(https://github.com/UKPLab/sentence-transformers)中可用,我们希望不久后可以在Hugging Face model hub中使用。这种方法存在一个问题,就是Sentence-BERT模型关注的是句子级的语义,而不是像类名那种单字或词语级别的语义(*意味着类名的向量不准确)。因此可以合理地假设,标签的向量不像流行的词级别的向量抽取方法那么准确(比如word2vec)。在下面的t-SNE可视化图像中可以看到,数据点似乎准确地按照类别(颜色)聚类在了一起,但是类别名称十分不准确。如果我们要使用词向量当作标签的表示,那就需要标注数据去学习怎样对齐Sentence-BERT的序列向量和word2vec的词向量表示。在我们自己的一些内部实验中,我们通过以下过程解决了此问题:1. 在word2vec模型的词表中取出top K个出现频率最高的单词V;2. 用word2vec获得每个词的词向量 ;3. 用Sentence-BERT获得每个词的词向量 ;4. 学习带L2正则化的从 到 的最小二乘线性投影矩阵 (*大概是这种最小二乘法)https://developer.aliyun.com/article/242069由于这个投影矩阵仅学习了单个单词的向量,因此我们不能指望它学到了一个有效的从Sentence-BERT的序列向量到word2vec的词向量的映射。相反,我们仅在分类中使用 作为对序列和标签的Sentence-BERT向量的转换:该过程可以被认为是一种降维。从下面的t-SNE可视图中可以看出,这种投影使标签向量与其对应的数据簇能更好地对齐,同时也保持了Sentence-BERT与的优越效果。重要的是,此过程除了按单词频率排序的word2vec词典之外,不需要任何其他的数据。在Yahoo Answer主题分类任务中,无投影步骤的F1值为31.2,而有投影步骤的F1值为46.9。该数据集的数据一共分为10个类别,有监督的模型可以达到的准确率为75上下。[7]当有一些可用的标注数据时此技术非常灵活,可以轻松地应对以下情况:有限数量的可用标注数据(小样本学习few-shot learning),或者仅有一部分感兴趣数据的子集已标注(传统的零样本学习)。可以简单地学习一个最小二乘投影矩阵,将任何可用标注数据的向量投影到其对应的数据簇向量中。但是,最重要的是我们采用的方式不能过拟合有限的数据。我们的向量在其自身上表现良好,因此需要在它们之间找到一个投影,在学习训练数据的同时,可以利用到这些表示的语义丰富性。为此,我们添加了L2正则化的变体,该变体将权重推向单位矩阵,而不是降低其范数。定义 为训练数据, 为训练标签, 为上述嵌入函数,那正则化目标则为 等价于以单位矩阵为中心的权重和由 控制的方差采用高斯先验的贝叶斯线性回归。我们想通过推动 靠近单位矩阵,有效地将投影嵌入 推向 。通俗来讲,我们的先验知识是,最能表示我们的数据的是嵌入函数 ,并且我们只在拿到更多训练数据时,才会更新该知识。
03
NLI(Natural Language Inference)分类现在我们探索一种可代替的方法,这种方法不仅能将序列和标签映射到同一空间以测量它们之间的距离,还能告诉我们两个不同序列之间的一致性。简略来看,NLI[8]将两个句子当作:一个“前提”和一个“假设”。其任务是确定在给定的“前提”下,这个“假设”是正确的(蕴含)还是错误的(矛盾)。当使用BERT类似的transformer架构时,NLI数据集是一个很经典的对一对序列进行分类的任务。也就是我们会将“前提”和“假设”当作一对不同的切片喂给模型,训练一个分类器预测是[蕴含,中立,矛盾]中的哪一类。Yin et al. (2019)[9]用了预训练的MNLI(Multi-genre NLI)序列对分类模型作为开箱即用的零样本学习模型,事实上效果还不错。它的想法是将我们感兴趣的标签序列作为“前提”,并将每个候选标签变成一个“假设”。如果NLI模型预测前提蕴含假设,那么我们就认为标签是正确的。下面的代码片段展示了用HuggingFace Transformers能够轻易做到:
# load model pretrained on MNLIfrom transformers import BartForSequenceClassification, BartTokenizertokenizer = BartTokenizer.from_pretrained('facebook/bart-large-mnli')model = BartForSequenceClassification.from_pretrained('facebook/bart-large-mnli') # pose sequence as a NLI premise and label (politics) as a hypothesispremise = 'Who are you voting for in 2020?'hypothesis = 'This text is about politics.' # run through model pre-trained on MNLIinput_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() * 100print(f'Probability that the label is true: {true_prob:0.2f}%')Probability that the label is true: 99.04%论文的作者称,最少参数版本的BERT仅在MNLI语料库上微调,就能在Yahoo Answer上达到标签加权F1:37.9。我们简单地把更大更新的Bart模型在MNLI语料库上预训练后,能把分数提高到53.7。尝试一下我们的Demo!(http://35.208.71.201:8000/) 输入要分类的序列和任何感兴趣的标签,观看Bart实时进行魔术操作。当有一些可用的标注数据时用少量带标注的数据微调此模型没什么效果,所以它不适合小样本学习。但是在传统的零样本学习中,对于有限数量的类,我们有足够的数据,该模型是非常出色的。可以通过给模型输入两次序列来进行训练:一次使用正确的标签,一次使用随机选择的错误标签,以优化交叉熵。微调后出现的一个问题是,模型预测的时候,它见过的标签的概率要比没见过的标签高得多。为了减轻这个问题,作者引入了一种在测试时对训练时见过的标签进行惩罚的方法。详见论文[10]和作者Github(https://github.com/yinwenpeng/BenchmarkingZeroShot)。
4.《An embarrassingly simple approach to zero-shot learning》 http://proceedings.mlr.press/v37/romera-paredes15.pdf
5.《Zero-Shot Learning Through Cross-Modal Transfer》 https://arxiv.org/abs/1301.3666
6.《Using Semantic Similarity for Multi-Label Zero-Shot Classification of Text Documents》 https://www.elen.ucl.ac.be/Proceedings/esann/esannpdf/es2016-174.pdf