2021-10-27

[软件工程应用与实践]lingvo代码阅读

2021SC@SDUSC
阅读上一篇笔记时,发现代码截图略显冗余。因此这节只放部分代码截图,重点放在自己对代码的理解以及从代码中学习到的知识。

lingvo.core.ops包

beam_search_step_op_kernels.cc

lingvo.core.ops.beam_search_step(scores, atten_probs, best_scores, cumulative_scores, in_scores, in_hyps, in_prev_hyps, in_done_hyps, in_atten_probs, in_beam_done, is_last_chunk, cur_step, eos_id, beam_size, num_hyps_per_beam, eoc_id=- 1, valid_eos_max_logit_delta=5, local_eos_threshold=- 100, merge_paths=False, allow_empty_terminated_hyp=True, ensure_full_beam=False, force_eos_in_last_step=False, force_eos_in_top_k=False, force_last_chunk_eoc_in_top_k=False, merged_topk_buffer_size_factor=2, beam_independence=False, atten_vecs_in_hypothesis_protos=True, name=None)

波束搜索向前推进一步
设“b”为波束数, “k”为每个波束的hyps数, “t”为最大解码步长.
在第一个解码步骤之前分配了以下数据结构,并从当前步骤传递到下一个步骤:

in_scores

形状为[t, k * b]的张量. In_scores [i, j]为第i步解码时第j个hyp的局部分数.

in_hyps

形状为[t, k * b]的张量。In_hyps [i, j]为第i步解码时第j个hyp的令牌id。

in_prev_hyps

形状为[t, k * b]的张量。In_prev_hyps [i, j]存储时间步长i的第j个hyp的指针到上一个时间步长(i - 1)的hyp。0 <= In_prev_hyps [i, j] < k * b。

in_done_hyps

形状为[t, k * b]的张量。in_done_hyps[i, j]可以是一个空字符串,也可以是一个序列化的Hypothesis原型。终止的hyps将从波束中移除,并移动到相应的in_done_hyps槽中。

in_atten_probs

形状为[t, k * b, s_len]的张量。In_atten_probs [i, j,…]是第j个hyp在第i个时间步长的源单词上的注意probs。

in_beam_done

bool类型的形状[b]张量,是否每一束都做过。

这些张量在这个op调用中被修改(包含cur_step timestep的内容),并被传递给相应的输出张量。

在这里插入图片描述

参数

  • scores : float32型张量。形状为[k * b, vocab_size]的矩阵,其中b为有源波束的数量,k为每个波束中hyps的数量。当前时间步长的本地分数。
  • atten_probs : float32型张量。形状为[k * b, source_len]的矩阵。注意力机制时间步长的概率。
  • best_scores : float32型张量。一个大小为[b]的矢量,是迄今为止每个光束中终止的hyps的最好分数。
  • cumulative_scores : float32型张量。大小为[k * b]的向量当前步骤之前每个活跃的hyp的累计得分。
  • in_scores : float32型张量。
  • in_hyps : float32型张量。
  • in_prev_hyps : int32型张量。
  • in_done_hyps : string型张量。
  • in_atten_probs : float32型张量。
  • in_beam_done : bool型张量。向量[b],是否每个波束都是以前处理过的。
  • is_last_chunk : bool型张量。形状[k * b]的张量。神经传感器用来判断当前假设是否到达了最后一个块,是否应该将下一个块结束符号作为句子结束符。
  • cur_step : int32型张量。当前步骤id。
  • eos_id : int。特殊序列结束令牌的令牌id。
  • beam_size : float。如果波束中主动hyps的分数与最佳分数之间的差值超过这个阈值,搜索将终止。
  • num_hyps_per_beam : int。hyps的数量。
  • eoc_id : 块标记的特殊结束的标记id。
  • valid_eos_max_logit_delta : 我们只允许在其logit与最佳候选logit的距离不超过valid_eos_max_logit_delta的情况下终止hyp。
  • local_eos_threshold : 如果的局部评分大于local_eos_threshold,则允许终止hyp。
  • merge_paths : 如果为真,当删除时相同的hyps将被合并为一个单独的hyp。合并hyp的概率将是各组成hyps的概率之和。这只适用于发射模型(RNN-T和NT)。
  • allow_empty_terminated_hyp : 是否可以考虑一个只包含的hypp终止。默认情况下这是正确的,因为一个话语可能包含沉默。当EMBR训练epsilon-emitting模型(例如RNN-T)时,它应该被设为false,因为即使在没有沉默的情况下,这些模型也容易发出全epsilon hyps。注意,在EOS中终止的hyp不会被认为是空的,所以这个标志对非发射模型没有影响。
  • ensure_full_beam : 如果为True,我们不会将all_done输出设置为True,直到我们找到’ num_hyps_per_beam ‘终止的hyps,并且没有活动的hyps在’ beam_size '范围内的最佳终止的hyps。如果为False,只有第二个条件必须满足。一般来说,这应该是假的,除非波束搜索是作为最低字错误率训练的一部分运行。
  • force_eos_in_last_step : 如果为真,则如果解码即使在(max - 1)步后也没有终止,则eos符号被注入结果,并返回部分假设(最后有一个有效的eos符号)。对于这些偏导,All_done设置为true。如果为false(默认行为),则返回空假设,并在终止时将all_done设置为false。
  • force_eos_in_top_k : 是否始终认为eos代币在每一步都在k代币之列。当为False时,hyps只能在eos令牌是k的一部分的情况下终止。valid_eos_max_logit_delta和local_eos_threshold始终适用,无论这个值是多少。
  • force_last_chunk_eoc_in_top_k : 是否始终认为最后一块eoc令牌在前k个令牌中。这只有在解码到达输入的最后一帧时才有效。当为True时,hyps可以在eoc的最后一帧终止,即使eoc得分还没有高到足以进入最高k。请注意,无论如何,p.valid_eos_max_logit_delta和p.local_eos_threshold总是适用的。
  • merged_topk_buffer_size_factor : 当剪枝每个hyp顶部k扩展以形成每个梁顶部k扩展时的缓冲区大小因子。如果当eoc_id >= 0时,该因子设置为大于或等于num_hyps_per_beam + 2,则在执行所有可能的路径合并之前不会进行修剪(如果merge_paths=True)。为了提高内存效率(即在剪枝期间保持更少的hyps),合理的值是2。
  • beam_independence : 当启用时,当且仅当in_beam_done[beam_id] == True时,此步骤将成为beam_id的无操作。
  • atten_vecs_in_hypothesis_protos : 是否在返回的假设原中填充atten_vecs字段。
  • name : 操作的名称。

返回值

一个张量对象的元组(out_best_scores, out_cumulative_scores, out_scores, out_hyps, out_prev_hyps, out_done_hyps, out_atten_probs, out_beam_done, all_done)。

  • out_best_scores : 更新了每一束的最佳分数。
  • out_cumulative_scores : 大小为[k * b]的向量在当前解码步骤之后,新hyps的累积分数。
  • out_scores
  • out_hyps
  • out_prev_hyps
  • out_done_hyps
  • out_atten_probs
  • out_beam_done : 大小为[b]的向量,每个波束是否在此步骤之后完成。下面的all_done是out_beam_done在所有波束上的逻辑与。
  • all_done : 标量,是否对所有波束终止解码。

实践启示——注意力机制

注意力模型(AM)最初被用于机器翻译,现在已成为神经网络领域的一个重要概念。lingvo作为自然语言处理框架,内置机器翻译、语音识别等模块,自然需要使用AM相关算法。

个人对注意力机制的理解

Attention本质上是一个寻址的过程。
当我们翻译一句话时,如csdn个人中心这句
在这里插入图片描述

“您还不是会员身份”,我们在不同阶段会把注意力放在不同的汉字/单词上。
在这里插入图片描述
通过计算出q与k的相关度,得到现在最应该“注意”的单词
step1 信息输入
用X = [x1, · · · , xN ]表示N 个输入信息;
step2 注意力分布计算
令Key=Value=X,则可以给出注意力分布alpha
step3 信息加权平均
注意力分布 [公式] 可以解释为在上下文查询q时,第i个信息受关注的程度,采用一种“软性”的信息选择机制对输入信息X进行编码
软注意力机制的两种编码格式

  • Self-Attention
    在这里插入图片描述

self-attention只关注输入序列间的关系,在序列内部进行attention计算,捕捉文本的内在联系。

  • Multi-Head Attention
    在Self-Attention的基础上,用多种变换生成的Q、K、V进行运算,再将它们对相关性的结论综合起来,增强Self-Attention的学习效果。

Beam Search

beam search是对greedy search的一个改进算法。相对greedy search扩大了搜索空间,但远远不及穷举搜索指数级的搜索空间,是二者的一个折中方案。
穷举搜索需遍历所有可能性,求出概率最大序列,最有可能得到全局最优解,但费时费力;贪心搜索每一步只搜索一次,省时省力但准确度低。beam search折中二者,设定贪心搜索次数,保留竖宽范围内的所有序列,较穷举相比省力,较贪心相比准确。
beam search有一个超参数beam size(束宽),设为 k 。第一个时间步长,选取当前条件概率最大的 k 个词,当做候选输出序列的第一个词。之后的每个时间步长,基于上个步长的输出序列,挑选出所有组合中条件概率最大的 k 个,作为该时间步长下的候选输出序列。始终保持 k 个候选。最后从 k 个候选中挑出最优的。

beam search代码实现

def beam_search(decoder, num_beams, max_len, *input):
    """
    a beam search implementation about seq2seq with attention
    :param decoder:
    :param num_beams: number of beam, int
    :param max_len: max length of result
    :param input: input of decoder
    :return: list of index
    """
    # init
    state = input[0]  # state of decoder
    outputs = input[1]  # outputs of encoder
    src_len = input[2]  # length of encode sequence
    beams = [[[1], 1, state]]

    cur_pro = 0
    cur_seq = None
    for i in range(max_len):
        results = []
        for beam in beams:
            tgt = torch.LongTensor(beam[0][-1:]).unsqueeze(0).cuda()
            input = [tgt, beam[2], outputs, src_len, 1]
            output, state = decoder(*input)
            v, i = torch.topk(output.view(-1).data, k=num_beams)
            for m, n in zip(v, i):
                gen_seq = beam[0] + [n.item()]
                pro = beam[1] * m.item()
                results.append([gen_seq, pro, state])

                if n.item() == 2 and pro > cur_pro:  # eos_token = 2
                    cur_pro = pro
                    cur_seq = gen_seq

        # filter beams
        beams = []
        for gen_seq, pro, state in results:
            if pro > cur_pro:
                beams.append([gen_seq, pro, state])
        # cut
        if len(beams) > num_beams:
            results = []
            pros = []
            for beam in beams:
                pros.append(beam[1])
            pros_idx = np.array(pros).argsort()[-1*num_beams:]
            for pro_idx in pros_idx:
                results.append(beams[pro_idx])
            beams = results

        if len(beams) == 0:
            return cur_seq

    if cur_seq is not None:
        return cur_seq
    else:
        max_pro = 0
        max_seq = None
        for beam in beams:
            if beam[1] > max_pro:
                max_pro = beam[1]
                max_seq = beam[0]
    return max_seq
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值