基于tensorflow2.x的文本分类任务(二)

        上一篇博客主要介绍了在文本在输入到模型前做的一系列必不可少的数据预处理操作。本篇博客主要介绍一下作为baseline的文本分类任务的模型在tf2.x框架下是如何构建的。

        提到文本分类,现在基本都是想到深度学习,然后准确率比较高的方法就是预训练语言模型加微调,然后比较经典一点还有text_cnn,lstm等;本篇博客主要将text_cnn的构建情况,至于bert这类的预训练模型后续会专门再整体总结一下。

        本篇博文主要将text_cnn,作为比较经典的模型,具备速度快,在短文本领域的准确率高等特点,是非常适合作为baseline的模型。下面先讲讲text_cnn原理,比较常见的一张论文图片也就是下图:

        当原始句子进入模型后,首先经过预处理变成embedding的格式,在上图中每个词就是5维的向量,一个句子就变成了sequence_length*5的向量形式,然后这个向量会通过几种卷积核提取浅层语义特征,例如上图所示的三种不同形状的矩阵,它们的列维度和embedding的维度保持一致,但是行长度不一样,分别为2,3,4;这三种长度是比较常用的提取语义特征的卷积核向量,同时卷积核也可以设置不同的数量,像上图这样的就是每个卷积核的数量有两种,可以提取出两个特征向量。

        接下来就是卷积计算和池化操作,卷积核与输入矩阵相乘得到特征向量,会得到(sequence_length-filter_size+1)*1的向量,然后每个向量进行最大池化操作,即取向量中数值最大的作为池化结果,最终会得到filter_size*filter_num个结果,将所有的结果拼接就得到最终的向量。

        最后就是全连接层,也就是将上一步卷积池化等一系列计算后得到的最终向量首先通过全连接层降维到目标结果维度,例如上图中的任务是情感二分类,最终就要得到一个二维的向量,最后通过激活函数做数值缩放,将数值映射到0-1之间的范围。也就是可以成为概率的数值。

        下面就是基于tf2.x的版本代码实现了,相比与1.x版本,2.x版本的代码相对清爽了很多,主要是继承tf.keras.Model这个父类进行子类模型构建,代码如下:

class TextCnn(tf.keras.Model):
    '''
    构建textcnn网络结构
    '''
    def __init__(self, config, vocab_size, word_vectors):
        self.config = config
        self.vocab_size = vocab_size
        self.word_vectors = word_vectors

        #输入层
        word_ids = tf.keras.layers.Input(shape=(None, ), dtype=tf.int64, name='input_word_ids')

        #embedding层
        class GatherLayer(tf.keras.layers.Layer):
            def __init__(self, config, vocab_size, word_vectors):
                super(GatherLayer, self).__init__()
                self.config = config

                self.vocab_size = vocab_size
                self.word_vectors = word_vectors

            def build(self, input_shape):
                with tf.name_scope('embedding'):
                    if not self.config['use_word2vec']:
                        self.embedding_w = tf.Variable(tf.keras.initializers.glorot_normal()(
                            shape=[self.vocab_size, self.config['embedding_size']],
                            dtype=tf.float32), trainable=True, name='embedding_w')
                    else:
                        self.embedding_w = tf.Variable(tf.cast(self.word_vectors, tf.float32), trainable=True,
                                                       name='embedding_w')
                self.build = True

            def call(self, indices):
                return tf.gather(self.embedding_w, indices, name='embedded_words')

            def get_config(self):
                config = super(GatherLayer, self).get_config()

                return config

        # 利用词嵌入矩阵将输入数据转成词向量,shape=[batch_size, seq_len, embedding_size]
        embedded_words = GatherLayer(config, vocab_size, word_vectors)(word_ids)

        #卷积输入是四维向量,需要增加维度[batch_size, height, width, channels]
        expanded_words = tf.expand_dims(embedded_words, axis=-1, name='expanded_words')

        # 进行卷积和池化操作
        pooled_outputs = []
        # 创建几种不同尺寸的卷积核提取文本信息,通常使用长度为3,4,5等长度
        for filter in self.config['conv_filters']:
            # 初始化卷积核权重和偏置
            # [filter_height, filter_width, in_channels, out_channels],先矩阵相乘然后求和
            # filter_size = [filter, self.config['embedding_size'], 1, self.config['num_filters']]
            #kenel_size [height, width]
            kernel_size = [filter, self.config['embedding_size']]
            #output_size
            output_size = self.config['num_filters']
            # conv_w = tf.keras.initializers.truncated_normal(filter_size, stddev=0.1)
            # conv_b = tf.constant(0.1, shape=self.config['num_filters'])
            h = tf.keras.layers.Conv2D(
                filters=output_size,
                kernel_size=kernel_size,
                strides=(1,1),
                kernel_initializer=tf.keras.initializers.truncated_normal(),
                bias_initializer=tf.keras.initializers.constant(0.1),
                activation='relu',
                padding='VALID',
                data_format="channels_last",
                input_shape=(self.config['seq_len'], self.config['embedding_size'], 1)
            )(expanded_words)
            # relu函数的非线性映射,得到卷积的结果[batch_size, seq_len-filter+1, 1, num_filters]
            # h = tf.keras.layers.Activation.relu(tf.keras.layers.Layer.bias_add(conv, conv_b))
            # 池化层,最大池化
            pooled = tf.keras.layers.MaxPooling2D(
                # ksize,shape[batch_size, height, width, channels]
                pool_size=[self.config['seq_len'] - filter+ 1, 1],
                strides=(1,1),
                padding='VALID',
                data_format="channels_last"
            )(h)
            # 每种filter得到num_filters个池化结果
            pooled_outputs.append(pooled)


        # 最终输出的cnn长度
        total_cnn_output = self.config['num_filters'] * len(self.config['conv_filters'])

        # 将不同的卷积核根据channel concat起来
        h_pool = tf.concat(pooled_outputs, 3)

        # 摊平成二维输入到全连接层[batch_size, total_cnn_output]
        h_pool_flat = tf.reshape(h_pool, [-1, total_cnn_output])

        # dropout层
        h_drop_out = tf.keras.layers.Dropout(self.config['dropout_rate'])(h_pool_flat)

        # 全连接层的输出
        with tf.name_scope('output'):
            output_w = tf.keras.initializers.glorot_normal()(shape=[total_cnn_output, self.config['num_classes']])
            output_b = tf.constant(0.1, shape=[self.config['num_classes']])
            # self.l2_loss += tf.nn.l2_loss(output_w)
            # self.l2_loss += tf.nn.l2_loss(output_b)

            # self.logits = tf.matmul(h_drop_out, output_w) + output_b
            self.logits = tf.keras.layers.Dense(self.config["num_classes"])(h_drop_out)
        #输出为[batch_size, num_classes]
        outputs = dict(logits=self.logits)


        super(TextCnn, self).__init__(inputs=[word_ids], outputs=outputs)

 完整的工程代码可以到https://github.com/dextroushands/nlp_tasks进行使用,包括模型训练保存预测一系列步骤。

参考文献:

《A Sensitivity Analysis of (and Practitioners’ Guide to) Convolutional Neural Networks for Sentence Classification》

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然,我很乐意帮你完成这个任务。以下是一个基于 TensorFlow 2.0 的 LSTM 中文文本分类示例: 首先,我们需要导入必要的库: ```python import tensorflow as tf import numpy as np import pandas as pd import jieba from sklearn.model_selection import train_test_split ``` 接下来,我们需要加载数据集。这里我们使用 THUCNews 数据集,它包含了 14 个不同类别的新闻文章。你可以在这里下载数据集:[THUCNews](http://thuctc.thunlp.org/message) ```python # 加载数据集 def load_data(): # 读取数据集 df = pd.read_csv('thucnews.csv') # 分词 df['content'] = df['content'].apply(lambda x: ' '.join(jieba.cut(x))) # 将标签转换为数字 label2id = {label: idx for idx, label in enumerate(set(df['label']))} df['label'] = df['label'].apply(lambda x: label2id[x]) # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(df['content'], df['label'], test_size=0.2, random_state=42) return X_train, X_test, y_train, y_test ``` 接下来,我们需要对文本进行预处理。这里我们使用 TensorFlowTextVectorization 层来将文本转换为数字序列。 ```python # 对文本进行预处理 def preprocess(X_train, X_test): # 定义 TextVectorization 层 vectorizer = tf.keras.layers.experimental.preprocessing.TextVectorization(max_tokens=5000, output_sequence_length=500) # 适配训练集 vectorizer.adapt(X_train.to_numpy()) # 转换训练集和测试集 X_train = vectorizer(X_train.to_numpy()) X_test = vectorizer(X_test.to_numpy()) return X_train, X_test ``` 接下来,我们可以定义 LSTM 模型。这里我们使用一个简单的 LSTM 模型,包含一个嵌入层、一个 LSTM 层和一个全连接层。 ```python # 定义 LSTM 模型 def build_model(): model = tf.keras.Sequential([ tf.keras.layers.Embedding(input_dim=5000, output_dim=64), tf.keras.layers.LSTM(64), tf.keras.layers.Dense(14, activation='softmax') ]) model.compile(loss='sparse_categorical_crossentropy', optimizer='adam', metrics=['accuracy']) return model ``` 最后,我们可以训练模型并评估其性能。 ```python # 训练模型 def train_model(X_train, y_train): model = build_model() model.fit(X_train, y_train, epochs=10, batch_size=64) return model # 评估模型 def evaluate_model(model, X_test, y_test): loss, accuracy = model.evaluate(X_test, y_test) print('Test accuracy:', accuracy) ``` 完整代码如下: ```python import tensorflow as tf import numpy as np import pandas as pd import jieba from sklearn.model_selection import train_test_split # 加载数据集 def load_data(): # 读取数据集 df = pd.read_csv('thucnews.csv') # 分词 df['content'] = df['content'].apply(lambda x: ' '.join(jieba.cut(x))) # 将标签转换为数字 label2id = {label: idx for idx, label in enumerate(set(df['label']))} df['label'] = df['label'].apply(lambda x: label2id[x]) # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(df['content'], df['label'], test_size=0.2, random_state=42) return X_train, X_test, y_train, y_test # 对文本进行预处理 def preprocess(X_train, X_test): # 定义 TextVectorization 层 vectorizer = tf.keras.layers.experimental.preprocessing.TextVectorization(max_tokens=5000, output_sequence_length=500) # 适配训练集 vectorizer.adapt(X_train.to_numpy()) # 转换训练集和测试集 X_train = vectorizer(X_train.to_numpy()) X_test = vectorizer(X_test.to_numpy()) return X_train, X_test # 定义 LSTM 模型 def build_model(): model = tf.keras.Sequential([ tf.keras.layers.Embedding(input_dim=5000, output_dim=64), tf.keras.layers.LSTM(64), tf.keras.layers.Dense(14, activation='softmax') ]) model.compile(loss='sparse_categorical_crossentropy', optimizer='adam', metrics=['accuracy']) return model # 训练模型 def train_model(X_train, y_train): model = build_model() model.fit(X_train, y_train, epochs=10, batch_size=64) return model # 评估模型 def evaluate_model(model, X_test, y_test): loss, accuracy = model.evaluate(X_test, y_test) print('Test accuracy:', accuracy) # 加载数据集 X_train, X_test, y_train, y_test = load_data() # 对文本进行预处理 X_train, X_test = preprocess(X_train, X_test) # 训练模型 model = train_model(X_train, y_train) # 评估模型 evaluate_model(model, X_test, y_test) ``` 希望这个示例对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值