PaddleDetection代码解析之Transformer encoder源码实现分析(上)

2021SC@SDUSC

经过阅读paddle的源码,我理解了动态图下的Transformer encoder源码实现,由于这个实现比较复杂,因此我将通过两个博客来对Transformer encoder的源码实现进行说明。

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

  • Multi-Head Attention
  • Feed Forward

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

class 有如下几个:

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

在动态图中,网络层的实现继承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 : 可训练的偏差参数

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 x

PositionwiseFeedForwardLayer

bert中hidden_act(激活函数)是gelu。

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值