最新干货!Transformer文本分类代码 - 知乎

03/19 20:07
阅读数 2.4K

https://zhuanlan.zhihu.com/p/105036982


作者:专知

本文分类教程在IMDb电影评论数据集上训练一个Transformer模型用于情绪分析。

地址:

https://github.com/lyeoni/nlp-tutorial/tree/master/text-classification-transformergithub.com

Transformer模型(基于论文《Attention is All You Need》)遵循与标准序列模型相同的一般模式,即从一个序列到另一个序列的注意力模型。

输入语句通过N个编码器层传递,该层为序列中的每个单词/令牌生成输出。解码器关注编码器的输出和它自己的输入(自我注意)来预测下一个单词。

实践证明,该Transformer模型在满足并行性的前提下,对许多顺序-顺序问题具有较好的求解质量。

在这里,我们要做的情感分析,不是顺序到顺序的问题。所以,只使用Transformer编码器。

位置编码

由于该模型不包含任何递归式或卷积,因此添加了位置编码,以便为该模型提供关于单词在句子中的相对位置的一些信息。

将位置编码向量添加到嵌入向量中。嵌入表示d维空间中的令牌,其中具有相似含义的令牌彼此之间更接近。但是嵌入并不编码句子中单词的相对位置。因此,在添加位置编码之后,单词之间的距离会更近,这是在d维空间中基于它们的意义和它们在句子中的位置的相似性。

位置编码的计算公式如下:

def get_sinusoid_table(self, seq_len, d_model):
    def get_angle(pos, i, d_model):
        return pos / np.power(10000, (2 * (i//2)) / d_model)

    sinusoid_table = np.zeros((seq_len, d_model))
    for pos in range(seq_len):
        for i in range(d_model):
            if i%2 == 0:
                sinusoid_table[pos, i] = np.sin(get_angle(pos, i, d_model))
            else:
                sinusoid_table[pos, i] = np.cos(get_angle(pos, i, d_model))

    return torch.FloatTensor(sinusoid_table)

编码器

输入语句通过编码器(由N个编码器层组成)传递,它为序列中的每个单词/令牌生成输出。

每个编码器层包括两个子层:

  • Multi-head attention (with padding mask)
  • Pointwise feed forward networks

多头注意力

Multi-head attention consists of four parts:

  1. Linear layers and split into heads
  2. Scaled dot-product attention (with padding mask)
  3. Concatenation of heads
  4. Final linear layer

Pointwise feed forward networks

class PositionWiseFeedForwardNetwork(nn.Module):
    def __init__(self, d_model, d_ff):
        super(PositionWiseFeedForwardNetwork, self).__init__()

        self.linear1 = nn.Linear(d_model, d_ff)
        self.linear2 = nn.Linear(d_ff, d_model)
        self.relu = nn.ReLU()

    def forward(self, inputs):
        # |inputs| : (batch_size, seq_len, d_model)

        output = self.relu(self.linear1(inputs))
        # |output| : (batch_size, seq_len, d_ff)
        output = self.linear2(output)
        # |output| : (batch_size, seq_len, d_model)
        return output

编码器层

每个子层周围都有一个残差连接,然后进行层标准化。残差连接有助于避免深度网络中的消失梯度问题。

每个子层的输出是LayerNorm(x +Sublayer(x))。在d_model(最后一个)轴上进行标准化。transformer中有N个编码器层。

class EncoderLayer(nn.Module):
    def __init__(self, d_model, n_heads, p_drop, d_ff):
        super(EncoderLayer, self).__init__()

        self.mha = MultiHeadAttention(d_model, n_heads)
        self.dropout1 = nn.Dropout(p_drop)
        self.layernorm1 = nn.LayerNorm(d_model, eps=1e-6)

        self.ffn = PositionWiseFeedForwardNetwork(d_model, d_ff)
        self.dropout2 = nn.Dropout(p_drop)
        self.layernorm2 = nn.LayerNorm(d_model, eps=1e-6)

    def forward(self, inputs, attn_mask):
        # |inputs| : (batch_size, seq_len, d_model)
        # |attn_mask| : (batch_size, seq_len, seq_len)

        attn_outputs, attn_weights = self.mha(inputs, inputs, inputs, attn_mask)
        attn_outputs = self.dropout1(attn_outputs)
        attn_outputs = self.layernorm1(inputs + attn_outputs)
        # |attn_outputs| : (batch_size, seq_len(=q_len), d_model)
        # |attn_weights| : (batch_size, n_heads, q_len, k_len)

        ffn_outputs = self.ffn(attn_outputs)
        ffn_outputs = self.dropout2(ffn_outputs)
        ffn_outputs = self.layernorm2(attn_outputs + ffn_outputs)
        # |ffn_outputs| : (batch_size, seq_len, d_model)

        return ffn_outputs, attn_weights

References

  • Attention Is All You Need
  • SEQUENCE-TO-SEQUENCE MODELING WITH NN.TRANSFORMER AND TORCHTEXT
  • Transformer model for language understanding

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部