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

How to compare sentence similarities using embeddings from BERT

我正在使用 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)

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

为了计算两个向量的余弦相似度,它们的长度必须相同。我怎么能在这里做到这一点?像第一次对 axis=1 求和这样幼稚的东西还能用吗?我还有哪些其他选择?

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

这是嵌入的第一个标记。

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

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

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

重要的是,通常在你想看什么样的相似性方面也有更细粒度的区分。专门为此,在 SemEval 2014(SICK 数据集)的 task papers 之一中也有一个很好的讨论,其中对此进行了更详细的讨论。根据您的任务描述,我假设您已经在使用来自后来的 SemEval 任务之一的数据,该任务还将其扩展到多语言相似性。

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

Jacob Devlin's comment: I'm not sure what these vectors are, since BERT does not generate meaningful sentence vectors. It seems that this is is doing average pooling over the word tokens to get a sentence vector, but we never suggested that this will generate meaningful sentence representations. And even if they are decent representations when fed into a DNN trained for a downstream task, it doesn't mean that they will be meaningful in terms of cosine distance. (Since cosine distance is a linear space where all dimensions are weighted equally). (https://github.com/google-research/bert/issues/164#issuecomment-441324222)

From Sentence-BERT paper: The results show that directly using the output of BERT leads to rather poor performances. Averaging the BERT embeddings achieves an average correlation of only 54.81, and using the CLS token output only achieves an average correlation of 29.19. Both are worse than computing average GloVe embeddings. (https://arxiv.org/pdf/1908.10084.pdf)

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