解读 PaddlePaddle Transformer encoder实现源码

[ github 源码地址 ]

本文基于PaddlePaddle 1.7版本,解析动态图下的Transformer encoder源码实现。

Transformer的每个Encoder子层(bert_base中包含12个encoder子层)包含 2 个小子层 :

  • Multi-Head Attention
  • Feed Forward

(Decoder中还包含Masked Multi-Head Attention)

class 有如下几个:

PrePostProcessLayer 用于添加残差连接、正则化、dropout
PositionwiseFeedForwardLayer 全连接前馈神经网络
MultiHeadAttentionLayer 多头注意力层
EncoderSubLayer encoder子层
EncoderLayer transformer encoder层

在Paddle动态图中,网络层的实现继承paddle.fluid.dygraph.Layer,类内方法__init__是对网络层的定义,forward是跑前向时所需的计算。

具体实现如下,对代码的解释在注释中:

一些必要的导入

"dygraph transformer layers"

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import numpy as np

import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph import Embedding, LayerNorm, Linear, Layer 

PrePostProcessLayer

可选模式:{ a: 残差连接,n: 层归一化,d: dropout}

残差连接

图中Add+Norm层。每经过一个模块的运算, 都要把运算之前的值和运算之后的值相加, 从而得到残差连接,残差可以使梯度直接走捷径反传到最初始层。

残差连接公式:

y=f(x)+x

x 表示输入的变量,实际就是跨层相加。

层归一化

LayerNorm实际就是对隐含层做层归一化,即对某一层的所有神经元的输入进行归一化(沿着通道channel方向),使得其加快训练速度:

层归一化公式:

x : 该层神经元的向量表示

H : 层中隐藏神经元个数

ϵ : 添加较小的值到方差中以防止除零

g : 可训练的比例参数

b : 可训练的偏差参数

[ PaddlePaddle 对应 api 文档 ]

dropout

丢弃或者保持x的每个元素独立。Dropout是一种正则化手段,通过在训练过程中阻止神经元节点间的相关性来减少过拟合。根据给定的丢弃概率,dropout操作符按丢弃概率随机将一些神经元输出设置为0,其他的仍保持不变。

dropout op可以从Program中删除,提高执行效率。

class PrePostProcessLayer(Layer):
    """
    PrePostProcessLayer
    """

    def __init__(self, process_cmd, d_model, dropout_rate, name):
        super(PrePostProcessLayer, self).__init__()
        self.process_cmd = process_cmd # 处理模式 a n d, 可选多个
        self.functors = [] # 处理层
        self.exec_order = ""
        # 根据处理模式,为处理层添加子层
        for cmd in self.process_cmd:
            if cmd == "a":  # add residual connection
                self.functors.append(lambda x, y: x + y if y else x)
                self.exec_order += "a"
            elif cmd == "n":  # add layer normalization
                self.functors.append(
                    self.add_sublayer(
                        # name
                        "layer_norm_%d" % len(
                            self.sublayers(include_sublayers=False)),
                        LayerNorm(
                            normalized_shape=d_model, # 需规范化的shape,如果是单个整数,则此模块将在最后一个维度上规范化(此时最后一维的维度需与该参数相同)。
                            param_attr=fluid.ParamAttr(  # 权重参数
                                name=name + "_layer_norm_scale",
                                # 常量初始化函数,通过输入的value值初始化输入变量
                                initializer=fluid.initializer.Constant(1.)),
                            bias_attr=fluid.ParamAttr( # 偏置参数
                                name=name + "_layer_norm_bias",
                                initializer=fluid.initializer.Constant(0.)))))
                self.exec_order += "n"
            elif cmd == "d":  # add dropout
                if dropout_rate:
                    self.functors.append(lambda x: fluid.layers.dropout(
                        x, dropout_prob=dropout_rate, is_test=False))
                    self.exec_order += "d"
    def forward(self, x, residual=None):
        for i, cmd in enumerate(self.exec_order):
            if cmd == "a":
                x = self.functors[i](x, residual)
            else:
                x = self.functors[i](x)
        return

PositionwiseFeedForwardLayer

class PositionwiseFeedForwardLayer(Layer):
    """
    PositionwiseFeedForwardLayer
    """

    def __init__(self,
                 hidden_act, # 激活函数
                 d_inner_hid, # 中间隐层的维度
                 d_model, # 最终输出的维度
                 dropout_rate,
                 param_initializer=None,
                 name=""):
        super(PositionwiseFeedForwardLayer, self).__init__()

        # 两个fc层
        self._i2h = Linear(
            input_dim=d_model,
            output_dim=d_inner_hid,
            param_attr=fluid.ParamAttr(
                name=name + '_fc_0.w_0', initializer=param_initializer),
            bias_attr=name + '_fc_0.b_0',
            act=hidden_act)

        self._h2o = Linear(
            input_dim=d_inner_hid,
            output_dim=d_model,
            param_attr=fluid.ParamAttr(
                name=name + '_fc_1.w_0', initializer=param_initializer),
            bias_attr=name + '_fc_1.b_0')

        self._dropout_rate = dropout_rate
    def forward(self, x):
        """
        forward
        :param x:
        :return:
        """
        hidden = self._i2h(x)
        # dropout
        if self._dropout_rate:
            hidden = fluid.layers.dropout(
                hidden,
                dropout_prob=self._dropout_rate,
                upscale_in_train="upscale_in_train",
                is_test=False)
        out = self._h2o(hidden)
        return out

 

MultiHeadAttentionLayer

EncoderLayer

猜你喜欢

转载自www.cnblogs.com/shona/p/12955288.html