python - 获取 MASK 位置中多标记词的概率

标签 python pytorch transformer-model bert-language-model huggingface-transformers

根据语言模型获得标记的概率相对容易,如下面的代码片段所示。您可以获得模型的输出,将自己限制为屏蔽标记的输出,然后在输出向量中找到您请求的标记的概率。然而,这仅适用于单标记单词,例如本身就在分词器词汇表中的单词。当词汇表中不存在某个单词时,分词器会将其分成它确实知道的片段(请参见示例底部)。但是,由于输入句子仅包含一个掩码位置,而请求的标记具有比这更多的标记,我们如何才能得到它的概率呢?最终,我正在寻找一种无论单词具有多少个子词单元都有效的解决方案。

在下面的代码中,我添加了许多注释来解释正在发生的事情,以及打印出 print 语句的给定输出。您会发现,预测“爱”和“恨”等标记非常简单,因为它们位于标记生成器的词汇表中。但“谴责”则不然,因此无法在单个掩码位置中预测它 - 它由三个子词单元组成。那么我们如何预测蒙面位置的“斥责”呢?

from transformers import BertTokenizer, BertForMaskedLM
import torch

# init model and tokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertForMaskedLM.from_pretrained('bert-base-uncased')
model.eval()
# init softmax to get probabilities later on
sm = torch.nn.Softmax(dim=0)
torch.set_grad_enabled(False)

# set sentence with MASK token, convert to token_ids
sentence = f"I {tokenizer.mask_token} you"
token_ids = tokenizer.encode(sentence, return_tensors='pt')
print(token_ids)
# tensor([[ 101, 1045,  103, 2017,  102]])
# get the position of the masked token
masked_position = (token_ids.squeeze() == tokenizer.mask_token_id).nonzero().item()

# forward
output = model(token_ids)
last_hidden_state = output[0].squeeze(0)
# only get output for masked token
# output is the size of the vocabulary
mask_hidden_state = last_hidden_state[masked_position]
# convert to probabilities (softmax)
# giving a probability for each item in the vocabulary
probs = sm(mask_hidden_state)

# get probability of token 'hate'
hate_id = tokenizer.convert_tokens_to_ids('hate')
print('hate probability', probs[hate_id].item())
# hate probability 0.008057191967964172

# get probability of token 'love'
love_id = tokenizer.convert_tokens_to_ids('love')
print('love probability', probs[love_id].item())
# love probability 0.6704086065292358

# get probability of token 'reprimand' (?)
reprimand_id = tokenizer.convert_tokens_to_ids('reprimand')
# reprimand is not in the vocabulary, so it needs to be split into subword units
print(tokenizer.convert_ids_to_tokens(reprimand_id))
# [UNK]

reprimand_id = tokenizer.encode('reprimand', add_special_tokens=False)
print(tokenizer.convert_ids_to_tokens(reprimand_id))
# ['rep', '##rim', '##and']
# but how do we now get the probability of a multi-token word in a single-token position?

最佳答案

由于字典中不存在分割词,BERT 根本不知道它的概率,因此在标记化之前没有必要对其进行屏蔽。

并且你无法利用链式法则得到它的概率,参见response作者:J.德夫林。为了说明这一点,让我们举一个更通用的例子。尝试估计位置i 中某些二元组的概率。虽然您可以估计给定句子中每个单词及其位置的概率

P(w_i|w_0, w_1...w_i-1, w_i+1, ..., w_N),

P(w_i+1|w_0, w_1... w_i, wi+2, ..., w_N),

没有办法得到二元组的概率

P(w_i,w_i+1|w_0, w_1... w_i-1, wi+2, ..., w_N)

因为 BERT 不存储此类信息。

话虽如此,您可以通过乘以看到它的各个部分的概率来非常粗略估计 OOV 单词的概率。所以你会得到

P("谴责"|...) ~= P("代表"|...)*P("##rim"|...)*P("##and"| ...)

由于您的子词不是常规单词,而是一种特殊的单词,因此这并不全是错误的,因为它们之间的依赖关系是隐式的。

关于python - 获取 MASK 位置中多标记词的概率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59435020/

相关文章:

python - 在 Homebrew 软件中更新 pip 和 setuptools 不起作用

python - 更改 Bokeh 中双轴标签文本的颜色

pytorch - 如何对此类张量使用 masked select?

java - 防止 java 变压器被替换

Python-获取列表中少数类的百分比

python - PIL - TypeError : src is not a numpy array, 既不是标量

python - 对于神经网络(pytorch)中的每一层,应该有多少个偏差?

python - PyTorch 相当于 tf.dynamic_partition

java - 在 DOM 文档中创建空文本节点