如何使用来自 BERT 的嵌入来比较句子的相似性

KOB*_*KOB 20 python nlp vector cosine-similarity huggingface-transformers

我正在使用 HuggingFace Transformers 包来访问预训练模型。由于我的用例需要英语和阿拉伯语的功能,因此我使用了bert-base-multilingual- cased 预训练模型。我需要能够使用诸如余弦相似度之类的东西来比较句子的相似度。要使用它,我首先需要为每个句子获取一个嵌入向量,然后可以计算余弦相似度。

首先,从 BERT 模型中提取语义嵌入的最佳方法是什么?在输入句子后获取模型的最后一个隐藏状态就足够了吗?

import torch
from transformers import BertModel, BertTokenizer

model_class = BertModel
tokenizer_class = BertTokenizer
pretrained_weights = 'bert-base-multilingual-cased'

tokenizer = tokenizer_class.from_pretrained(pretrained_weights)
model = model_class.from_pretrained(pretrained_weights)

sentence = 'this is a test sentence'

input_ids = torch.tensor([tokenizer.encode(sentence, add_special_tokens=True)])
with torch.no_grad():
    output_tuple = model(input_ids)
    last_hidden_states = output_tuple[0]

print(last_hidden_states.size(), last_hidden_states)
Run Code Online (Sandbox Code Playgroud)

其次,如果这是从我的句子中获得嵌入的充分方法,我现在还有另一个问题,即嵌入向量的长度取决于原始句子的长度。形状输出是[1, n, vocab_size],其中n可以有任何值。

为了计算两个向量的余弦相似度,它们需要具有相同的长度。我怎么能在这里做到这一点?像第一次求和这样幼稚的事情axis=1还能奏效吗?我还有什么其他选择?

den*_*ger 18

除了一个已经被广泛接受的答案之外,我还想向您指出sentence-BERT,它更详细地讨论了特定指标(如余弦相似度)的相似性方面和含义。他们也有一个非常方便的在线实施。这里的主要优点是,与“天真的”句子嵌入比较相比,它们似乎获得了很多处理速度,但我对实现本身还不够熟悉。

重要的是,在您想要查看的相似性方面,通常也有更细粒度的区别。专门为此,SemEval 2014(SICK 数据集)的一篇任务论文中也有一个很好的讨论,其中更详细地介绍了这一点。根据您的任务描述,我假设您已经在使用来自后来的 SemEval 任务之一的数据,这也将其扩展到多语言相似性。

  • 谢谢你!是的 - 我能够通过三元组损失模型使用句子 BERT 得到一个非常好的解决方案。 (3认同)

Swi*_*ier 10

您可以使用[CLS]令牌作为整个序列的表示。此标记通常在预处理步骤中添加到您的句子中。此令牌通常用于分类任务(参见BERT 论文中的图 2 和第 3.2 段)。

它是嵌入的第一个标记。

或者,您可以采用序列的平均向量(就像您在第一个(?)轴上所说的那样),根据Huggingface 文档(第三个技巧)可以产生更好的结果。

请注意,BERT 不是为使用余弦距离的句子相似性而设计的,但根据我的经验,它确实产生了不错的结果。

  • 好的,我明白了 - 非常有趣。因此,要尝试您概述的两种方法,例如在通过模型输入一个句子(如我上面所示)之后,然后我提取的“last_hidden_​​states”的形状为“[1,9,768]”,那么我可以(1) 使用 `[CLS]` 标记作为 last_hidden_​​states[0][0],给我一个长度为 768 的向量,或者 (2) 使用 `last_hidden_​​states.mean(1)` 获取中轴的平均值,也给出一个长度为 768 的向量? (2认同)

Cri*_*aga 8

您不应该使用 BERT 的输出作为语义相似性的句子嵌入。BERT 没有针对语义相似性进行预训练,这会导致结果很差,甚至比简单的 Glove Embeddings 还要糟糕。请参阅下面 Jacob Devlin(BERT 论文第一作者)的评论和 Sentence-BERT 论文中的一篇文章,其中详细讨论了句子嵌入。

Jacob Devlin 的评论:我不确定这些向量是什么,因为 BERT 不会生成有意义的句子向量。这似乎是对单词标记进行平均池化以获得句子向量,但我们从未建议这将生成有意义的句子表示。即使它们在被输入到为下游任务训练的 DNN 中时是不错的表示,但这并不意味着它们在余弦距离方面是有意义的。(因为余弦距离是一个线性空间,其中所有维度的权重相等)。(https://github.com/google-research/bert/issues/164#issuecomment-441324222

来自Sentence-BERT论文:结果表明,直接使用BERT的输出会导致性能相当差。对 BERT 嵌入进行平均仅实现 54.81 的平均相关性,而使用 CLS 令牌输出仅实现 29.19 的平均相关性。两者都比计算平均 GloVe 嵌入要差。(https://arxiv.org/pdf/1908.10084.pdf

您应该使用专门针对句子相似度进行预训练的模型,例如 Sentence-BERT。Sentence-BERT 和其他几个用于句子相似性的预训练模型可在句子转换器库 ( https://www.sbert.net/docs/pretrained_models.html ) 中找到,它与令人惊叹的 HuggingFace 转换器库完全兼容。使用这些库,您只需一行代码即可获得句子嵌入。