【问题标题】:Get probability of multi-token word in MASK position获取 MASK 位置的多标记词的概率
【发布时间】:2019-12-21 09:24:46
【问题描述】:

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

在下面的代码中,我添加了许多 cmets 来解释发生了什么,以及打印出打印语句的给定输出。您会看到预测诸如“爱”和“恨”之类的标记很简单,因为它们在标记器的词汇表中。但是,'reprimand' 不是,所以它不能在单个掩码位置被预测——它由三个子词单元组成。那么我们如何预测蒙面位置的“训斥”呢?

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?

【问题讨论】:

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


    【解决方案1】:

    由于字典中不存在拆分词,BERT 根本不知道它的概率,因此在标记化之前没有使用掩码。

    而且你不能通过利用链规则来获得它的概率,请参阅 J.Devlin 的response。为了说明这一点,让我们举一个更通用的例子。尝试估计一些二元组在位置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("reprimand"|...) ~= P("rep"|...)*P("##rim"|...)*P("##and"|...)

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

    【讨论】:

    • “BERT 不存储此类信息”是什么意思?我不太明白为什么 BERT 不能对二元模型进行建模。还有其他语言模型可以吗?
    • 1) 二元组的概率是P(w1,w2)=P(w1)P(w2|w1)!=P(w1)*P(w2)。 BERT 不存储每个单词的条件概率。 BERT 在其传统意义上不是一种语言模型。 BERT 无法提供特定句子的概率。 2)您可以采用(例如)n-gram 语言模型来获得二元概率。但是无论你采用什么模型,在对稀有词进行建模时都会遇到问题。由于它们的性质,很难(您需要大量数据)估计它们的条件概率。所以通常所有稀有词都会得到一些最小概率,并且可以正常工作。
    【解决方案2】:

    而不是 sentence = f"I {tokenizer.mask_token} you", 预测: "I [MASK] [MASK] you""I [MASK] [MASK] [MASK] you" 并过滤结果,删除整个单词标记链,以便您只找到合适的子词链。当然,如果你提供两个以上的上下文词,你会得到更好的结果。

    但在开始之前,请重新考虑您的 softmax。使用维度=0,它对所有标记列所有标记行进行 softmax 计算——而不仅仅是您想要 softmax 概率的单个标记:

    In [1]: import torch                                                                                                                      
    In [2]: m = torch.nn.Softmax(dim=1) 
       ...: input = torch.randn(2, 3) 
       ...: input                                                                                                                        
    Out[2]: 
    tensor([[ 1.5542,  0.3776, -0.8047],
            [-0.3856,  1.1327, -0.1252]])
    
    In [3]: m(input)                                                                                                                          
    Out[3]: 
    tensor([[0.7128, 0.2198, 0.0674],
            [0.1457, 0.6652, 0.1891]])
    
    In [4]: soft = torch.nn.Softmax(dim=0) 
       ...: soft(input)                                                                                                                       
    Out[4]: 
    tensor([[0.8743, 0.3197, 0.3364],
            [0.1257, 0.6803, 0.6636]])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-30
      • 2019-02-21
      • 1970-01-01
      • 2012-07-22
      • 2017-07-27
      • 2017-08-08
      • 1970-01-01
      • 2014-09-21
      相关资源
      最近更新 更多