gpt4 book ai didi

python - 如何从 BERT 模型中获取词嵌入的余弦相似度

转载 作者:行者123 更新时间:2023-12-05 00:43:29 29 4
gpt4 key购买 nike

我很感兴趣如何从 BERT 模型中获得不同句子中词嵌入的相似性(实际上,这意味着词在不同场景中具有不同的含义)。

例如:

sent1 = 'I like living in New York.'
sent2 = 'New York is a prosperous city.'

我想从 send1 和 sent2 中获取 cos(New York, New York) 的值,即使短语 'New York' 相同,但它出现在不同的句子中。我从 https://discuss.huggingface.co/t/generate-raw-word-embeddings-using-transformer-models-like-bert-for-downstream-process/2958/2 得到了一些直觉

但是我仍然不知道我需要提取哪个层的嵌入以及如何计算我上面示例的 cos 相似度。

提前感谢您的任何建议!

最佳答案

好的,让我们开始吧。

首先您需要了解 BERT 有 13 层。第一层基本上只是 BERT 在初始训练期间通过的嵌入层。您可以使用它,但可能不想使用它,因为它本质上是静态嵌入,而您正在使用动态嵌入。为简单起见,我将只使用 BERT 的最后一个隐藏层。

您在这里使用了两个词:“New”和“York”。您可以在预处理期间将其视为一个,并将其组合成“New-York”或其他如果您真的想要的东西。在这种情况下,我会将其视为两个单独的单词,并对 BERT 产生的嵌入进行平均。

这可以用几个步骤来描述:

  1. 标记输入
  2. 确定标记器在哪里有纽约和纽约的 word_id(suuuuper 很重要)
  3. 通过 BERT
  4. 平均
  5. 余弦相似度

首先需要导入的东西:from transformers import AutoTokenizer, AutoModel

现在我们可以创建分词器和模型了:

tokenizer = AutoTokenizer.from_pretrained('bert-base-cased')
model = model = AutoModel.from_pretrained('bert-base-cased', output_hidden_states=True).eval()

确保在评估模式下使用模型,除非您尝试微调!

接下来我们需要标记化(步骤 1):

tok1 = tokenizer(sent1, return_tensors='pt')
tok2 = tokenizer(sent2, return_tensors='pt')

Step 2. 需要确定单词的索引在哪里匹配

# This is where the "New" and "York" can be found in sent1
sent1_idxs = [4, 5]
sent2_idxs = [0, 1]

tok1_ids = [np.where(np.array(tok1.word_ids()) == idx) for idx in sent1_idxs]
tok2_ids = [np.where(np.array(tok2.word_ids()) == idx) for idx in sent2_idxs]

上面的代码检查了分词器生成的 word_ids() 与原始句子中的单词索引重叠的位置。这是必要的,因为分词器会拆分稀有词。因此,如果您有类似“土豚”之类的东西,当您对其进行标记并查看它时,您实际上会得到:

In [90]: tokenizer.convert_ids_to_tokens( tokenizer('aardvark').input_ids)
Out[90]: ['[CLS]', 'a', '##ard', '##var', '##k', '[SEP]']

In [91]: tokenizer('aardvark').word_ids()
Out[91]: [None, 0, 0, 0, 0, None]

步骤 3. 通过 BERT

现在我们抓取 BERT 在我们生成的 token ID 中生成的嵌入:

with torch.no_grad():
out1 = model(**tok1)
out2 = model(**tok2)

# Only grab the last hidden state
states1 = out1.hidden_states[-1].squeeze()
states2 = out2.hidden_states[-1].squeeze()

# Select the tokens that we're after corresponding to "New" and "York"
embs1 = states1[[tup[0][0] for tup in tok1_ids]]
embs2 = states2[[tup[0][0] for tup in tok2_ids]]

现在您将有两个嵌入。每个都是形状 (2, 768)。第一个大小是因为我们正在查看两个词:“New”和“York”。第二个大小是 BERT 的嵌入大小。

第 4 步。平均

好的,所以这不一定是您想要做的,但这将取决于您如何处理这些嵌入。我们有两个 (2, 768) 形状的嵌入。您可以将 New 与 New 进行比较,将 York 与 York 进行比较,也可以将 New York 合并为一个平均值。我会这样做,但如果它更适合你的任务,你可以轻松地做另一个。

avg1 = embs1.mean(axis=0)
avg2 = embs2.mean(axis=0)

第 5 步。余弦模拟

使用 torch 很容易实现余弦相似度:

torch.cosine_similarity(avg1.reshape(1,-1), avg2.reshape(1,-1))

# tensor([0.6440])

这很好!它们指向同一个方向。它们不完全是 1,但可以通过多种方式进行改进。

  1. 您可以对训练集进行微调
  2. 您可以尝试平均不同的层,而不是像我一样只计算最后一个隐藏层
  3. 您可以尝试创造性地结合纽约和纽约。我取了平均值,但也许有更好的方法可以满足您的确切需求。

关于python - 如何从 BERT 模型中获取词嵌入的余弦相似度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70057975/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com