[软件工程应用与实践]lingvo学习笔记

2021SC@SDUSC

类名

class TransformerAttentionLayer(base_layer.BaseLayer)

方法
def _FinishExtendStep(self, theta, query_vec, unnormalized_query_vec, extended_packed_src, t=None)

参数列表:

  • theta: .NestedMap 对象,该对象包含此层及其子层的权重值。
  • query_vec: [target_batch, dim]
  • unnormalized_query_vec: [target_batch, dim]
  • extended_packed_src: A .NestedMap 包含source_vecssource_contextssource_paddingsource_segment_ids的对象
  • t: 一个标量,当前时间步长,以0为基础。

函数作用
通过多余一个时间步完成前缀扩展。将这个函数从ExtendStep中分离出来,可以将自我注意推广到对其他输入的因果注意。

源码

def _FinishExtendStep(self,
                        theta,
                        query_vec,
                        unnormalized_query_vec,
                        extended_packed_src,
                        t=None):

    p = self.params

计算per_step_source_padding。填充已补全,要屏蔽的时间索引接收填充权重为1.0。

    query_batch_size = py_utils.GetShape(query_vec)[0]
    source_seq_len = py_utils.GetShape(extended_packed_src.source_vecs)[0]
    zero_padding = tf.fill([source_seq_len],
                           tf.constant(0.0, dtype=query_vec.dtype))
    ones_padding = tf.ones_like(zero_padding, dtype=query_vec.dtype)
    if t is not None:
      per_step_source_padding = tf.where(
          tf.less(tf.range(source_seq_len), tf.fill([source_seq_len], t + 1)),
          zero_padding, ones_padding)
      per_step_source_padding = tf.tile(
          tf.expand_dims(per_step_source_padding, axis=0),
          [query_batch_size, 1])

可以用 N-gram进行maks屏蔽

预期加速应该伴随 ‘per_step_source_padding =None’ , 每次更新prefix_states, 不仅扩展了一个时间步长, 也只保留prefix_states 从开始到最近N步的前缀状态。

    elif p.is_masked and p.mask_type == 'ngram':
      assert p.mask_ngram_order
      idx = tf.maximum(0, source_seq_len - p.mask_ngram_order)
      per_step_source_padding = tf.where(
          tf.less(tf.range(source_seq_len), tf.fill([source_seq_len], idx)),
          ones_padding, zero_padding)
      per_step_source_padding = tf.tile(
          tf.expand_dims(per_step_source_padding, axis=0),
          [query_batch_size, 1])
    else:
      per_step_source_padding = None

    ctx_vec, atten_prob, _ = self.atten.ComputeContextVectorWithCachedSource(
        theta.atten,
        extended_packed_src,
        query_vec,
        per_step_source_padding=per_step_source_padding)

    ctx_vec = self.residual_dropout.FProp(theta.residual_dropout, ctx_vec)
    input_to_add = (
        unnormalized_query_vec if p.add_unnormalized_input else query_vec)
    input_after_sublayer = tf.reshape(ctx_vec, py_utils.GetShape(query_vec))
    if p.residual_function is None:
      h = input_to_add + input_after_sublayer
    else:
      h = self.residual_function.FProp(theta.residual_function, input_to_add,
                                       input_after_sublayer)

    if not p.pre_layer_norm:
      h = self.layer_norm.FProp(theta.layer_norm, h)

    new_states = py_utils.NestedMap(
        key=extended_packed_src.source_vecs,
        value=extended_packed_src.source_contexts)
    return h, atten_prob, new_states

def ExtendStep(self, theta, query_vec, prefix_state, t=None)

参数列表:

  • theta: .NestedMap 对象,该对象包含此层及其子层的权重值。
  • query_vec: [target_batch, dim]
  • prefix_state: dict, 包含先前注意的结果的张量,用于快速解码。
  • t: 一个标量,当前时间步长,以0为基础。
def ExtendStep(self, theta, query_vec, prefix_state, t=None):
    p = self.params

!! 必须是随机注意 !!

    assert p.is_masked  
    unnormalized_query_vec = query_vec
    if p.pre_layer_norm:
      query_vec = self.layer_norm.FProp(theta.layer_norm, query_vec)

    cached_packed_src = py_utils.NestedMap(
        source_vecs=prefix_state.key,
        source_contexts=prefix_state.value,
        source_padding=None,
        source_segment_id=None)
    extended_packed_src = self.atten.ExtendSourcePacked(theta.atten, query_vec,
                                                        query_vec, None, None,
                                                        cached_packed_src, t)
    return self._FinishExtendStep(theta, query_vec, unnormalized_query_vec,
                                  extended_packed_src, t)

class TransformerMultiSourceAttentionLayer(TransformerAttentionLayer)

Multi-source multi-headed attention 多源多头注意力机制
注意力机制模型的整体结构
由编码器和解码器组成,在编码器的一个网络块中,由一个多头attention子层和一个前馈神经网络子层组成,整个编码器栈式搭建了N个块。类似于编码器,只是解码器的一个网络块中多了一个多头attention层。为了更好的优化深度网络,整个网络使用了残差连接和对层进行了规范化(Add&Norm)。

Query,Key,Value首先进过一个线性变换,然后输入到放缩点积attention,注意这里要做h次,其实也就是所谓的多头,每一次算一个头。而且每次Q,K,V进行线性变换的参数W是不一样的。然后将h次的放缩点积attention结果进行拼接,再进行一次线性变换得到的值作为多头attention的结果。可以看到,google提出来的多头attention的不同之处在于进行了h次计算而不仅仅算一次,论文中说到这样的好处是可以允许模型在不同的表示子空间里学习到相关的信息,后面还会根据attention可视化来验证。
多头注意力机制模型
首先在编码器到解码器的地方使用了多头attention进行连接,K,V,Q分别是编码器的层输出(这里K=V)和解码器中多头attention的输入。其实就和主流的机器翻译模型中的attention一样,利用解码器和编码器attention来进行翻译对齐。然后在编码器和解码器中都使用了多头自注意力self-attention来学习文本的表示。Self-attention即K=V=Q,例如输入一个句子,那么里面的每个词都要和该句子中的所有词进行attention计算。目的是学习句子内部的词依赖关系,捕获句子的内部结构。

参考论文: attention is all you need

**class TransformerFeedForwardLayer(base_layer.BaseLayer)**

前馈层

该类实现了转换器层的第二个子层。首先,输入通过带有一个隐藏层的前馈神经网络,然后投影回原输入维数应用残差。
输出层,然后进行规范化。

源码:

def __init__(self, params):
    super().__init__(params)
    p = self.params
    assert p.name
    assert p.input_dim
    assert symbolic.ToStatic(p.hidden_dim) > 0

初始化前馈层

    params = p.fflayer_tpl.Copy()
    params.name = 'fflayer'
    params.input_dim = p.input_dim
    params.activation = [p.activation, 'NONE']
    if p.output_dim == 0:
      params.hidden_layer_dims = [p.hidden_dim, p.input_dim]
    else:
      params.hidden_layer_dims = [p.hidden_dim, p.output_dim]

      if p.output_dim != p.input_dim:
        pj = p.res_proj_tpl.Copy()
        pj.name = 'res_proj'
        pj.input_dim = p.input_dim
        pj.output_dim = p.output_dim
        pj.activation = 'NONE'
        self.CreateChild('res_proj_layer', pj)

    params.dropout = [
        params.dropout.cls.Params().Set(keep_prob=1.0 - p.relu_dropout_prob),
        params.dropout.cls.Params().Set(keep_prob=1.0)
    ]
    self.CreateChild('fflayer', params)

初始化前馈层准则函数

    params = p.ln_tpl.Copy()
    params.name = 'fflayer_ln'
    params.input_dim = p.input_dim
    self.CreateChild('layer_norm', params)

    dropout_tpl = p.residual_dropout_tpl.Copy()
    dropout_tpl.keep_prob = (1.0 - p.residual_dropout_prob)
    self.CreateChild('residual_dropout', dropout_tpl)

    if p.residual_droppath_prob > 0:
      assert p.add_skip_connection
      droppath_p = StochasticResidualLayer.Params().Set(
          name='residual_droppath',
          survival_prob=1.0 - p.residual_droppath_prob)
      self.CreateChild('residual_droppath', droppath_p)

在前馈神经网络中,各神经元分别属于不同的层。每一层的神经元可以接收前一层神经元的信号,并产生信号输出到下一层。第 0 层叫输入层,最后一层叫输出层,其它中间层叫做隐藏层,相邻两层的神经元之间为全连接关系,也称为全连接神经网络(F N N)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值