NLP学习之:BERT 模型复现(4)模型实现

原理

数据集输入

  • 每次两个句子,用 [CLS], [SEP] 分隔,
  • 这两个句子中的每个词都有 15% 的概率被 [MASK]
    • 被选中 MASK 的词中 80% 真的用 [MASK] 符号替换原词
    • 10% 换成其他随机词(引入噪声)
    • 剩余 10% 啥也不干,虚晃一枪

Embedding

  • 需要 embedding 的有:
    • token : 将原本的 tokenembedding 规定的空间维度中表示(例如 embedding 的空间是 768 维空间)
    • position:将位置信息在 embedding 的空间维度中表示
    • segment:将当前词属于第一句话还是第二句话的信息在 embedding 的空间中表示

Attention 机制

  • 单个 attention 是通过 Q , K , V Q, K, V Q,K,V 三个矩阵计算出对于一个单词,其他单词与他的相关程度,并把这些相关关系编码到最终的 Q K T V Q K^{T}V QKTV 输出的矩阵中
  • 多头注意力机制就是将上述的 attention 重复了 n h e a d n_{head} nhead 次,这样映射到多个空间中编码词之间的相关关系会更加充分地利用输入信息

模型结构

主体结构

  • 就是 Transformer 网络的编码端;所以借鉴了 Transformer 的基础 block 结构

class LayerNorm(nn.Module):
    """Construct a layernorm module (See citation for details).
    Layer 标准化
    """
    def __init__(self, features, eps=1e-6):
        super(LayerNorm, self).__init__()
        self.a_2 = nn.Parameter(torch.ones(features))
        self.b_2 = nn.Parameter(torch.zeros(features))
        self.eps = eps

    def forward(self, x):
        mean = x.mean(-1, keepdim=True)
        std = x.std(-1, keepdim=True)
        return self.a_2 * (x - mean) / (std + self.eps) + self.b_2


class SublayerConnection(nn.Module):
    """
    A residual connection followed by a layer norm.
    Note for code simplicity the norm is first as opposed to last.
    """

    def __init__(self, size, dropout):
        super(SublayerConnection, self).__init__()
        self.norm = LayerNorm(size)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x, sublayer):
        "Apply residual connection to any sublayer with the same size."
        return x + self.dropout(sublayer(self.norm(x)))


class PositionwiseFeedForward(nn.Module):
    "Implements FFN equation."
    def __init__(self, d_model, d_ff, dropout=0.1):
        """

        :param d_model: 词向量的维度
        :param d_ff:
        :param dropout:
        """
        super(PositionwiseFeedForward, self).__init__()
        self.w_1 = nn.Linear(d_model, d_ff)
        self.w_2 = nn.Linear(d_ff, d_model)
        self.dropout = nn.Dropout(dropout)
        self.activation = GELU()

    def forward(self, x):
        return self.w_2(self.dropout(self.activation(self.w_1(x))))

class TransformerBlock(nn.Module):
    """
    Bidirectional Encoder = Transformer (self-attention)
    Transformer = MultiHead_Attention + Feed_Forward with sublayer connection
    """

    def __init__(self, hidden, attn_heads, feed_forward_hidden, dropout):
        """
        :param hidden: hidden size of transformer
        :param attn_heads: head sizes of multi-head attention
        :param feed_forward_hidden: feed_forward_hidden, usually 4*hidden_size
        :param dropout: dropout rate
        """

        super(TransformerBlock, self).__init__()
        self.attention = MultiHeadedAttention(h=attn_heads, d_model=hidden)
        self.feed_forward = PositionwiseFeedForward(d_model=hidden, d_ff=feed_forward_hidden, dropout=dropout)
        self.input_sublayer = SublayerConnection(size=hidden, dropout=dropout)
        self.output_sublayer = SublayerConnection(size=hidden, dropout=dropout)
        self.dropout = nn.Dropout(p=dropout)

    def forward(self, x, mask):
        x = self.input_sublayer(x, lambda _x: self.attention.forward(_x, mask=mask))
        x = self.output_sublayer(x, self.feed_forward)
        return self.dropout(x)

激活函数

  • 使用了作者提出的 GELU 激活函数

class GELU(nn.Module):
    """
    Paper Section 3.4, last paragraph notice that BERT used the GELU instead of RELU
    在论文的 3.4 节中,作者重写设计了 GELU 激活函数来代替 RELU
    """

    def forward(self, x):
        return 0.5 * x * (1 + torch.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * torch.pow(x, 3))))

BERT 网络代码


class BERT(nn.Module):
    """
    BERT model : Bidirectional Encoder Representations from Transformers.
    """

    def __init__(self, vocab_size, hidden=768, n_layers=12, attn_heads=12, dropout=0.1):
        """
        :param vocab_size: vocab_size of total words
        :param hidden: BERT model hidden size
        :param n_layers: numbers of Transformer blocks(layers)
        :param attn_heads: number of attention heads
        :param dropout: dropout rate
        """

        super(BERT, self).__init__()
        self.hidden = hidden
        self.n_layers = n_layers
        self.attn_heads = attn_heads

        # paper noted they used 4*hidden_size for ff_network_hidden_size
        self.feed_forward_hidden = hidden * 4

        # embedding for BERT, sum of positional, segment, token embeddings
        self.embedding = BERTEmbedding(vocab_size=vocab_size, d_model=hidden)

        # multi-layers transformer blocks, deep network
        self.transformer_blocks = nn.ModuleList(
            [TransformerBlock(hidden, attn_heads, hidden * 4, dropout) for _ in range(n_layers)])

    def forward(self, x, segment_info, position_ids):
        # attention masking for padded token
        # torch.ByteTensor([batch_size, 1, seq_len, seq_len)
        mask = (x > 0).unsqueeze(1).repeat(1, x.size(1), 1).unsqueeze(1)

        # embedding the indexed sequence to sequence of vectors
        x = self.embedding(x, segment_info, position_ids)

        # running over multiple transformer blocks
        for transformer in self.transformer_blocks:
            x = transformer.forward(x, mask)

        return x
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
BERT(Bidirectional Encoder Representations from Transformers)是一种预训练的自然语言处理模型,由Google公司研发。它的特点是双向编码器(Bidirectional Encoder),能够同时考虑上下文信息,因此在多项自然语言处理任务上表现优异。 下面介绍基于BERT模型自然语言处理实战案例: 1. 文本分类 文本分类是自然语言处理的常见任务之一,可以通过BERT模型实现。可以使用BERT对文本进行预训练,然后再将其Fine-tuning到特定的文本分类任务上。具体步骤如下: (1)使用BERT模型对大量文本进行预训练。 (2)将预训练的BERT模型Fine-tuning到特定的文本分类任务上。 (3)使用Fine-tuning后的模型对新的文本进行分类。 2. 问答系统 问答系统是自然语言处理中的另一个重要应用场景。可以使用BERT模型来构建问答系统。具体步骤如下: (1)使用BERT模型对大量文本进行预训练。 (2)使用预训练好的BERT模型对问题和文本进行编码。 (3)将编码后的问题与文本进行匹配,并输出答案。 3. 命名实体识别 命名实体识别是自然语言处理中的一个重要任务,可以使用BERT模型实现。具体步骤如下: (1)使用BERT模型对大量文本进行预训练。 (2)使用预训练好的BERT模型对文本进行编码。 (3)使用CRF等算法对编码后的文本进行标注,识别出文本中的命名实体。 总之,BERT模型自然语言处理中有着广泛的应用,可以用于文本分类、问答系统、命名实体识别等任务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暖仔会飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值