【问题标题】:Estimate token probability/logits given a sentence without computing the entire sentence在不计算整个句子的情况下估计给定句子的标记概率/logits
【发布时间】:2020-10-23 11:15:02
【问题描述】:

我有一个类似的句子:"I like sitting in my new chair and _____ about life"。

我有一组特定的令牌,例如 ["watch", "run", "think", "apple", "light"]

我想计算每个标记在该不完整句子中作为下一个单词出现的概率。希望我应该知道"think" 的概率比"apple" 高。

我正在使用 pytorch-transformers(特别是 GPT2LMHeadModel),一个可能的解决方案是使用每个标记评估整个句子的分数,但是当要评估的标记数量约为 100 或 1000 时计算时间开始太长了。

必须可以只处理一次句子并以某种方式使用隐藏状态来计算标记集的概率,但我不知道该怎么做。

有什么想法吗?提前致谢


编辑:

实际代码如下所示(每次都估计完整句子的概率)。对于每个句子,运行 score() 方法大约需要 0.1 秒,如果我想评估数千个单词,则需要数小时。

from pytorch_transformers import GPT2Tokenizer, GPT2LMHeadModel
import pandas as pd

model = GPT2LMHeadModel.from_pretrained("gpt2")
model.eval()
tokenizer = GPT2Tokenizer.from_pretrained("gpt2")


def score(sentence):
    tokenize_input = tokenizer.tokenize(sentence)
    tensor_input = torch.tensor([tokenizer.convert_tokens_to_ids(tokenize_input)])
    loss = model(tensor_input, labels=tensor_input)
    return -loss[0].item()


candidates = ["watch", "run", "think", "apple", "light"]
sent_template = "I like sitting in my new chair and {} about life"
print({candidate: score(sent_template.format(candidate)) for candidate in candidates})

【问题讨论】:

  • 也许您可以使用past 参数,但我不确定。你能分享一些你目前在做什么的代码吗?
  • 感谢@cronoik 的提示。我已经阅读了一些关于过去参数的信息,但我也无法使其工作。我已经编辑了问题,包括我当前使用的代码。提前非常感谢
  • 你可能想关注this,但我也会尽快回复。
  • 非常感谢,非常感谢。

标签: python nlp huggingface-transformers


【解决方案1】:

您的示例产生了以下输出,并且在我的环境中完成了 282 名候选人大约需要 48.5 秒(我只进行了 3 次运行):

{'watch': -5.406847953796387
, 'run': -5.533411502838135
, 'think': -4.525279521942139
, 'apple': -6.158637046813965
, 'light': -5.835141658782959}

正如 cmets 中所述,我认为您可以使用 past 参数和快速 tokenizer 进行一些计算,如下面的注释示例所示:

import torch

from  transformers import GPT2TokenizerFast, GPT2LMHeadModel
from torch.nn import CrossEntropyLoss

model = GPT2LMHeadModel.from_pretrained("gpt2")
model.eval()
tokenizer = GPT2TokenizerFast.from_pretrained("gpt2")

###We calculate the hidden_states and the past of the common left part of the sentence
past = "I like sitting in my new chair and"
past_tokenize_input = tokenizer.tokenize(past)
past_tensor_input = torch.tensor([tokenizer.convert_tokens_to_ids(past_tokenize_input)])

past_last_hidden_state, past = model.transformer(past_tensor_input)

def score(sentence, past, past_last_hidden_state, past_tensor_input):
    tokenize_input = tokenizer.tokenize(sentence, )
    tensor_input = torch.tensor([tokenizer.convert_tokens_to_ids(tokenize_input)])

    ###the following code is slightly modified from https://github.com/huggingface/transformers/blob/09a2f40684f77e62d0fd8485fe9d2d610390453f/src/transformers/modeling_gpt2.py#L604
    ###now we calculate the right part of the sentence with the already calculated past
    transformer_outputs = model.transformer(
            tensor_input,
            past=past,
            attention_mask=None,
            token_type_ids=None,
            position_ids=None,
            head_mask=None,
            inputs_embeds=None,
            use_cache=None,
            output_attentions=None,
            output_hidden_states=None,
        )
    ###and concatenate the output of with the hidden_state of the left part of the sentence
    hidden_states = torch.cat((past_last_hidden_state, transformer_outputs[0]), dim=1)
    
    ###the following part is exactly the same as https://github.com/huggingface/transformers/blob/09a2f40684f77e62d0fd8485fe9d2d610390453f/src/transformers/modeling_gpt2.py#L604
    lm_logits = model.lm_head(hidden_states)

    labels_input = torch.cat((past_tensor_input, tensor_input), dim=1)

    # Shift so that tokens < n predict n
    shift_logits = lm_logits[..., :-1, :].contiguous()
    shift_labels = labels_input[..., 1:].contiguous()
    # Flatten the tokens
    loss_fct = CrossEntropyLoss()
    loss = loss_fct(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))
    return -loss.item()

candidates = ["watch", "run", "think", "apple", "light"]

sent_template = " {} about life"

print({candidate: score(sent_template.format(candidate), past, past_last_hidden_state, past_tensor_input) for candidate in candidates})

输出:

{'watch': -5.406846046447754
, 'run': -5.533413887023926
, 'think': -4.525280952453613
, 'apple': -6.158637046813965
, 'light': -5.835141181945801}

这里的运行时间是 40.5 秒,有 282 个候选者(又是 3 个周期)。你也看到我失去了一些精确度。

非常感谢patrickvonplaten,他给了我一个很好的explanation 关于过去的实施。

【讨论】:

  • 非常感谢。假期回来后,我会尽快测试一下。
  • 感谢您的解决方案。它工作正常。时间的减少并不是很大,但它确实将计算时间从 0.1 秒/句减少到了 0.08 秒/句。感谢您的帮助
  • @JacoboLansac 使用fast tokenizer 可以节省更多时间。我已经调整了答案。
  • 我完全按照您在解决方案中提出的程序使用您的程序,使用 FastTokenizer,我得到了前面评论中提到的速度。也许是因为我没有使用 GPU。
  • 实际上,我们俩获得的改进百分比非常匹配。我们都获得了大约 20% 的速度提升。绝对时间可能取决于我们的处理器以及我们目前正在运行的其他作业。你得到:48.5 秒 /282 名候选人 = .18 秒。 40.5 秒 /282 名候选人 = .144 秒。也就是说,每位候选人减少了 0,036 秒。在百分比 = 0,036/0,18 = 20% 的时间减少中,我首先得到每个候选人 0.1 秒,然后是 0.08 秒。减少 0.02 秒。百分比 0.02/0.1 = 20 % 时间减少 :)
猜你喜欢
  • 1970-01-01
  • 2010-11-01
  • 2012-02-27
  • 1970-01-01
  • 2011-02-16
  • 2020-08-30
  • 1970-01-01
  • 1970-01-01
  • 2021-12-18
相关资源
最近更新 更多