NLP Transformers:如何获得固定的句子嵌入向量大小?

数据挖掘 机器学习 深度学习 nlp 词嵌入 伯特
2021-09-30 16:19:34

我正在从火炬中心加载一个语言模型(CamemBERT是一个基于法国 RoBERTa 的模型),并使用它嵌入了一些句子:

import torch
camembert = torch.hub.load('pytorch/fairseq', 'camembert.v0')
camembert.eval()  # disable dropout (or leave in train mode to finetune)


def embed(sentence):
   tokens = camembert.encode(sentence)
   # Extract all layer's features (layer 0 is the embedding layer)
   all_layers = camembert.extract_features(tokens, return_all_hiddens=True)
   embeddings = all_layers[0]
   return embeddings

# Here we see that the shape of the embedding vector depends on the number of tokens in the sentence

u = embed("Bonjour, ça va ?")
u.shape # torch.Size([1, 7, 768])
v = embed("Salut, comment vas-tu ?")
v.shape # torch.Size([1, 9, 768])

现在想象一下,我想计算cosine distance向量(在我们的例子中是张量)uv

cos = torch.nn.CosineSimilarity(dim=0)
cos(u, v) #will throw an error since the shape of `u` is different from the shape of `v``

我在问,无论令牌的数量如何,为了始终获得相同的句子嵌入形状,最好的方法是什么?

=>我正在考虑的第一个解决方案是计算mean on axis=1(句子中标记的平均嵌入),因为axis=0和axis=2始终具有相同的大小:

cos = torch.nn.CosineSimilarity(dim=1) #dim becomes 1 now

u = u.mean(axis=1)
v = v.mean(axis=1)

cos(u, v).detach().numpy().item() # works now and gives 0.7269

但是,我担心在计算平均值时会伤害嵌入!

=> 第二种解决方案是将较短的句子填充出来,这意味着:

  • 给出一次嵌入的句子列表(而不是逐句嵌入)
  • 查找具有最长标记的句子并将其嵌入,得到它的形状 S
  • 对于嵌入的其余句子,然后填充零以获得相同的形状S(句子的其余维度为 0)

你觉得呢?你有没有什么想法?你会使用什么技术,为什么?

1个回答

我认为不会有明确的答案,但我怀疑使用平均方法而不是填充方法会得到更好的结果。

填充方法的一个大问题是它对词序很敏感。例如,如果我们逐字比较,“长臂猿是一种猿”和“一种猿是长臂猿”这两个句子看起来非常不同。“Gibbons”与“One”非常不同,“are”与“type”非常不同,等等。这个问题将被稍微抵消,因为 RoBERTa 嵌入是上下文敏感的,但你明白了。

另一个大问题是填充可以主导不同长度句子的相似性比较。假设我们要比较以下两个含义非常相似的句子:

Sentence1: “非常毛茸茸”是对大多数哺乳动物物种的恰当描述。

Sentence2:哺乳动物有毛发。

我们将向 sentence2 添加 8 个填充标记,以使其与 sentence1 具有相同的长度。但是相似度计算主要由填充标记而不是句子的实际内容。

对我来说,平均方法似乎更好,因为它避免了这两个问题。当然它也有缺点。通过平均单词嵌入获得的“句子嵌入”将是嘈杂的,因为每个单词被赋予相同的权重。

您可以尝试对要比较的句子运行关键字检测器。然后比较top-k关键词的意思,而不是整个句子。