NLP【07】transformer原理、实现及如何与词向量做对接进行文本分类(附代码详解)

本文详细介绍了如何使用Transformer模型,特别是注意力机制和位置编码,替代传统RNN/CNN进行文本分类。讲解了Transformer的组成部分,包括Positional Encoding、多头注意力和残差连接,并提供了代码实例。重点展示了Transformer在NLP领域的演变,从Word2Vec到BERT的发展过程。
摘要由CSDN通过智能技术生成

上一篇:NLP【06】RCNN原理及文本分类实战(附代码详解)

下一篇:NLP【08】深度学习模型在NLP中的发展——从Word2vec到Bert的演变过程

完整代码下载:https://github.com/ttjjlw/NLP/tree/main/Classify%E5%88%86%E7%B1%BB/rnn-cnn/tf1.x/models

一、前言

当前,bert横行,而bert的基础是transformer,自然,掌握transformer成为了基操。论文中的transformer是seq2seq模型,分为编码和解码,而在本文中,我所讲的transformer主要是transformer中的attention,也就是利用transformer中的attention与位置编码,替换cnn或者rnn部分完成文本分类。建议在看我这篇之前,先整体看一篇trnasformer的介绍,细节没看明白的,再来瞧瞧我这篇。

二、transformer的简单介绍

transformer分为encoder和decoder,这里主要讲encoder的三部分Positional Encoding、multi-head attention以及残差连接。Positional Encoding即位置编码也就是每个位置利用一个向量表示,具体公式如下:

这个公式怎么理解呢?pos就是词的位置,2i或2i+1就是 词向量的维度上的偶数或奇数的位置,举个例子:定义一个长度为100的位置向量,位置向量的维度为64,那么最终这个位置向量pos_embs的shape即为(100,64),那么这个位置向量表怎么得到呢,就是通过上面的公式。具体就是pos_embs[pos][i%2==0]=sin()这一串,而pos_embs[pos][i%2==1]=cos()这一串。而pos取值是从0到99,i取值是从0到63。我想看到这,应该都明白了,如果没明白,可以结合下面的代码实现来理解。

第二部分就是multi-head attention,首先,对比一下以前self.attention的做法,在做文本分类时,lookup词嵌入矩阵后,再经cnn或者rnn,会得到shape为(batch-size,seq_len,dim)的向量,记为M,然后我们是怎么做self.attention的呢?

1、先初始化一个可以训练的权重,shape为(batch_size, dim,1),记为W 

2、然后M和W做矩阵相乘就的得到shape为(batch_size,seq_len)然后经softmax处理,就得到了每个词的权重 

3、再把这个权重和原来的M做相乘(multiply),最后在seq_len的维度上做reduce_sum(),也就是output=reduce_sum(M,axis=1),则output的shape变为(batch-size,dim),也就是attention的最后输出,以上省略了所有的reshape过程。

该过程实现可参考https://github.com/ttjjlw/NLP/blob/main/Classify%E5%88%86%E7%B1%BB/rnn-cnn/tf1.x/models/bilstmatten.py。那transformer中的self.attention又是怎么做的呢?

1、把M复制三份,命名为query,key,value

2、分别初始化三个矩阵q_w,k_w,v_w,然后query,keyvalue与对应的矩阵做矩阵相乘,得Q,K,V,此时三者的shape都为(batch_size,seq_len,dim)

3、如果head为1的话,那就是similarity=matmul(Q,K的转置),所以similarity的shape为(batch_size,seq_len,seq_len),其这个矩阵记录就是每个词与所有词的相似性

4、output=matmul(similarity,V),所以output 的shape为(batch_size,seq_len,dim)

第三部分残差连接,残差连接就简单了:公式为 :H(x) = F(x) + x,这里就是H(x)=query+output,然后给H(x)进行层归一化。

整个过程大概就是如此,其中省略些细节,如similarity的计算其实还要除于根号dim,防止softmax(similarity)后非0即1,不利于参数学习。举个例子就明白了,softmax([1,10]) —> [1.2339458e-04, 9.9987662e-01] 而 softmax([0.1,1.0]) —> [0.2890505, 0.7109495]。

 三、代码详解

1、位置编码

    def _position_embedding(self):
        """
        生成位置向量
        :return:
        """
        batch_size = self.config["batch_size"]
        sequence_length = self.config["sequence_length"]
        embedding_size = self.config["embedding_size"]

        # 生成位置的索引,并扩张到batch中所有的样本上
        position_index = tf.tile(tf.expand_dims(tf.range(sequence_length), 0), [batch_size, 1])
        position_embedding = np.zeros([sequence_length, embedding_size])
        for pos in range(sequence_length):
            for i in range(embedding_size):
                denominator = np.power(10000.0, i/ embedding_size)
                if i % 2 == 0:
                    position_embedding[pos][i] = np.sin(pos / denominator)
                else:
                    position_embedding[pos][i] = np.cos(pos / denominator)
        position_embedding = tf.cast(position_embedding, dtype=tf.float32)

        # 得到三维的矩阵[batchSize, sequenceLen, embeddingSize]
        embedded_position = tf.nn.embedding_lookup(position_embedding, position_index)

        return embedded_position

 lookup后的词向量与位置向量相加形成新的向量

embedded_words = tf.nn.embedding_lookup(embedding_w, self.inputs)
embedded_position = self._position_embedding()
embedded_representation = embedded_words + embedded_position

 把添加了位置向量的词向量,输入到self._multihead_attention()中(该方法就是依次经过attention,残差连接与层归一化得到最终的向量,就是上面详细介绍的2,3步过程),然后再经过self._feed_forward(), self._multihead_attention()与self._feed_forward()组成一层transformer

        with tf.name_scope("transformer"):
            for i in range(self.config["num_blocks"]):
                with tf.name_scope("transformer-{}".format(i + 1)):
                    with tf.name_scope("multi_head_atten"):
                        # 维度[batch_size, sequence_length, embedding_size]
                        multihead_atten = self._multihead_attention(inputs=self.inputs,
                                                                    queries=embedded_representation,
                                                                    keys=embedded_representation)
                    with tf.name_scope("feed_forward"):
                        # 维度[batch_size, sequence_length, embedding_size]
                        embedded_representation = self._feed_forward(multihead_atten,
                                                                     [self.config["filters"],
                                                                      self.config["embedding_size"]])

            outputs = tf.reshape(embedded_representation,
                                 [-1, self.config["sequence_length"] * self.config["embedding_size"]])

        output_size = outputs.get_shape()[-1].value

其中num_blocks就是设置要过几层transformer,output就是最终的结果。

完整代码下载:https://github.com/ttjjlw/NLP/tree/main/Classify%E5%88%86%E7%B1%BB/rnn-cnn/tf1.x/models

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值