920232796 / bert_seq2seq

pytorch实现 Bert 做seq2seq任务,使用unilm方案,现在也可以做自动摘要,文本分类,情感分析,NER,词性标注等任务,支持t5模型,支持GPT2进行文章续写。

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

关于beam search预测输出,请教一个问题

oswen opened this issue · comments

大概看了一下seq2seq_model.py文件里的beam search方法的代码,有几个疑惑点请作者解答下哈:

  1. 对于预测,假设我有一个句子“你好啊”,对于模型的token_id应该是[[CLS], 你, 好, 啊, [SEP], [PAD]],那对应的segment_id是这样的吗:[0, 0, 0, 0, 0, 0](看了生成token那段代码,发现输入似乎只能这样),那么预测的第一个字是基于输入文本的[SEP]这个符号来的吗?其实,就是我没有理解预测阶段是从哪个字符开始的哈以及输入形式是怎样的。。。
  2. 看到代码这一段,seq2seq_model.py的221行:
# 用来保存累计得分        
        output_scores = torch.zeros(token_ids.shape[0], device=device)
        for step in range(self.out_max_length):
            scores = self.forward(token_ids, token_type_ids, device=device) --> 这边输出的应该是[batch_size, sen_len, vocab_size]吧
            if step == 0:
                # 重复beam-size次 输入ids
                token_ids = token_ids.view(1, -1).repeat(beam_size, 1)
                token_type_ids = token_type_ids.view(1, -1).repeat(beam_size, 1)
            ## 计算log 分值 (beam_size, vocab_size)
            logit_score = torch.log_softmax(scores, dim=-1)[:, -1] --> 不理解为什么会取最后一个的分数,按我的理解每个batch_size的文本长度应该是不一样的吧
            logit_score = output_scores.view(-1, 1) + logit_score # 累计得分
            ## 取topk的时候我们是展平了然后再去调用topk函数

上面两个箭头是我的疑问哈,希望作者解答下哈,不胜感激~

对的,输入文本的Sep对应的输出就应该是 输出文本的第一个字。 然后再次构建一个新的输入,再去输出一个新的字,这样一直到输出Sep符号为止,说明输出结束了。

这样啊,感谢解答,还想请教个问题哈,以刚才说的为例,输入[[CLS], 你, 好, 啊, [SEP],然后预测第一个字符为“你”,那么第二次的输入应该变成了[[CLS], 你, 好, 啊, [SEP], 你],对应的segment id 应该是[0, 0, 0, 0, 0, 1]这样了吧?后面每一次循环都是这样,直到达到最大预测长度或者碰到[sep]就会结束预测,这个应该就是解码过程吧?

对的。

对的。

感谢解答,已star

谢谢!

best_one = output_scores.argmax()
if end_counts[best_one] == 1: ------(1)

说明出现终止了~

return output_ids[best_one][:-1]
else:

保留未完成部分

flag = (end_counts < 1) # 标记未完成序列
if not flag.all(): # 如果有已完成的 -------(2)
token_ids = token_ids[flag]
token_type_ids = token_type_ids[flag]
new_input_ids = new_input_ids[flag]
new_token_type_ids = new_token_type_ids[flag]
output_ids = output_ids[flag] # 扔掉已完成序列
output_scores = output_scores[flag] # 扔掉已完成序列
end_counts = end_counts[flag] # 扔掉已完成end计数
beam_size = flag.sum() # topk相应变化

我发现(2)的这个if条件只有当所有的beam都完成才会执行(2)但是只要都完成就直接从(1)这边ruturn出去了;这样不是逻辑存在冲突。我的问题是: (2)这段代码是冗余代码还是别有用途? 所以希望能得到解答

你理解好像错了,可能完成一个就从(1)出去,如果没出去,再去走(2)。

我经过多次尝试end_counts 的值要么等于tensor([1, 1, 1, 1, 1], device='cuda:0')要么等于tensor([0, 0, 0, 0, 0], device='cuda:0')而且好像都没跑(2)这段代码, 所以说只会出现全部完成或者全部没完成, 不可能出现部分未完成呀。 (2)这部分的代码不就表示部分未完成吗?

所以希望能得到解答
end_counts = (output_ids == sep_id).sum(1) # 统计出现的end标记
这行代码,如果输出id里面没有sep标志,那不就是说明还有没完成的么?多次尝试是几次?是怎么尝试的?模型训练好了尝试的?调大beam-size试了吗?