如何利用深度学习做好文本分类(text classification)

目录

1.简述

2.分析任务

3.构建baseline模型

3.1 数据清洗与预处理

3.2 选取合适的模型 

3.2.1 模型选取方法

   3.2.2 GELE模型为baseline

4. 评估指标

5.baseline的优化

5.1 采用预训练好的词向量

5.2 label smoothing

5.3 损失函数的选择

5.4 数据增强和数据噪音的处理

5.5 引入先验知识

5.6 模型超参数调整



1.简述

文本分类是自然语言处理中一项基本的任务,它的应用非常广泛,比如:垃圾过滤,情感分析,话题分类。它和其他的分类没有本质的区别,核心方法为首先提取分类数据的特征,然后选择最优的匹配,从而分类。但是文本也有自己的特点,根据文本的特点,文本分类的一般流程为:1.数据预处理;2.文本表示及特征选择;3.构造分类器;4.分类。

通常来讲,文本分类任务是指在给定的分类体系中,将文本指定分到某个或某几个类别中。被分类的对象可以是短文本,例如句子、标题、商品评论等等,也可以是长文本,如文章等。分类体系一般人工划分,例如:

1)政治、体育、娱乐、经济。

2)好评、中性、差评。

3)正面、负面。

因此,对应的分类模式可以分为:二分类,多分类,以及多标签分类。

文本分类的应用十分广泛,可以将其应用在:

  • 垃圾邮件的判定:是否为垃圾邮件
  • 新闻文章的分类:政治、体育、娱乐、经济等,可能是一个多标签分类。
  • 电商商品评论分析等等类似的应用:消极、积极
  • 自动问答系统中的问句分类。

最早的文本分类是用基于知识的方法,构建规则完成文本分类,随之机器学习的发展,慢慢采用基于统计和机器学习的方法,由于机器学习参数的有限性,深度学习地快速发展,现在越来越多的采用深度学习来做文本分类,本文将主要如何用GELE模型(《Enhancing Local Feature Extraction with Global Representation for Neural Text Classification》论文 EMNLP 2019)作为baseline完成文本分类。

2.分析任务

当给定的文本分类任务,首先需要结合数据和业务需求,分析现有文本分类任务需要从多个维度分析,需要清楚的几个点:

  1. 是否有标样本,如果没有有标样本能否构建;
  2. 一般的文本分类还是更具有领域性的情感分类;
  3. 是长文本分类还是短文本分类;
  4. 是大规模数据分类,还是小样本分类;
  5. 是二分类、多分类还是多标签分类;
  6. 如果是多标签分类,标签类别是大规模的还是少量的。
  7. 样本均衡还是不均衡。

3.构建baseline模型

在模块2的基础上,已经熟悉了要完成文本任务的性质,需要构建baseline模型。

3.1数据清洗与预处理

对于模块2提到的第一个问题,是否有有标样本,如果是有标样本,可以采用如下几种方式进行清洗:

  1. 去除非文本数据,很多时候爬取下来的文本数据往往会附带有HTML标签、URL地址等非文本内容,所以需要清除这部分内容对分类没有什么帮助的内容。
  2. 替换中文文本中长串的数字,例如手机号、身份证号、车牌号、用户名ID等文本内容,在非特定的文本分类情境下是可以去除。或者将其转换为归一化的特征,如是否出现长串数字的布尔值特征HAS_DIGITAL。值得一提的是,有时候表情代号常常作为长串数字或字母出现时,却能在情感分析中却能起到巨大作用。
  3. 去除无意义文本,对于文本当中的诸如广告内容、版权信息和个性签名的部分,可以选择去掉。
  4. 去除停用词和标点符号。

如果没有有标样本,可以采用半监督和无监督学习来做文本分类,可以采用这篇博客数据清洗方法进行数据清洗。

3.2选取合适的模型 

3.2.1模型选取方法

传统的机器学习方法主要利用自然语言处理中的n-gram概念对文本进行特征提取,并且使用TFIDF对n-gram特征权重进行调整,然后将提取到的文本特征输入到Logistics回归、SVM等分类器中进行训练。但是,上述的特征提取方法存在数据稀疏维度爆炸等问题,这对分类器来说是灾难性的,并且使得训练的模型泛化能力有限。因此,往往需要采取降维策略,但是效果往往不理想。

随着word2vec的技术出现,词向量能够有效的表示词语之间的相似度,而深度学习网络可以很好有效地提取文本特征,提高模型精度。

从上述分析,可见特征提取在文本分类模型中非常关键,那么如何选取最优的特征提取模型作为文本分类模型呢?可以从ACL(The Association for Computational Linguistics)会议查找文本分类相关论文,ACL网站建立了ACL Anthology页面,支持绝大部分国际学术会议的论文的免费下载,甚至包含了其他组织主办的学术会议。

首先找出文本分类最近一两年的最新论文,通过阅读摘要,可以大致了解论文解决了什么问题以及提出哪些新的方案,带着目标去阅读论文,如果是一种新的特征提取方法,可以再继续阅读以下论文中图以及表格,通常图会给出模型的框架,表格会给出实验结果以及与其他模型的相关结果比对,如果实验效果提升很明显则可以选择尝试作为baseline。

通过此类方法,发现EMNLP 2019论文《Enhancing Local Feature Extraction with Global Representation for Neural Text Classification》模型框架灵活,实验结果不管是多标签分类、分类、情感分类任务上优于先前提出的模型框架。

   3.2.2GELE模型为baseline

论文《Enhancing Local Feature Extraction with Global Representation for Neural Text Classification》提出了一种新颖的Encoder1-Encoder2架构, 将全局信息纳入从头开始提取局部特征的过程中。特别地,Encoder1提供全局信息,而Encoder2则充当局部特征提取器,并直接输入分类器。 同时,还设计了两种模式进行交互。

模型框架如下图

                                                                                            gele 模型框架

GELE 模型灵活的原因是encoder1可以选择rnn,cnn,attention中的一种,encoder2也可以选择cnn或者drnn,而且encoder1和encoder2交互也可以选在same或者attened两种模式,从论文给出的实验结果,也明显优于其他方法。

鉴于上述优点选取GELE作为baseline。

最终在我自己的数据集上,实验结果比lstm_attention模型要高3%。如果感兴趣的话,可以去看看论文。

4. 评估指标

模型评估可以参考这个链接,里面讲的特别详细和全面。不同类型的文本分类往往有不同的评价指标,具体如下:

  • 二分类:accuracy,precision,recall,f1-score, f_beta(beta可调,根据任务,调整准确率和召回率的权重)...
  • 多分类:Micro-Averaged-F1, Macro-Averaged-F1 ... 
  • 多标签分类:Jaccard相似系数 ...

5.baseline的优化

采用baeline预训练好模型以后,可以对测试集的实验结果进行分析,对于模型误判的如何进行优化,也可以从以下几个方面进行优化。

5.1 采用预训练好的词向量

可以选用Google 预训练bert词向量,在训练过程,保持词向量静态,相对于基线模型,可以提高3%;如果对模型线上运行效率有要求,可以采用word2vce预训练好的词向量,相对于基线模型可以提高1%。

5.2 label smoothing

标签平滑是一种损失函数的修正,已被证明是非常有效的训练深度学习网络的方法。标签平滑提高了图像分类、翻译甚至语音识别的准确性。简单的解释是,它将神经网络的训练目标从“1”调整为“1-label smoothing adjustment”,这意味着神经网络被训练得对自己的答案不那么自信。默认值通常是 0.1,这意味着目标答案是 0.9(1 - 0.1)而不是 1。参考链接。在我自己的数据集上,标签平滑提高了0.5%。

5.3 损失函数的选择

今年来,越来越多的工作在模型loss上一个改进,一方面loss改进只会影响训练过程,并不影响预测过程。

对于样本不均衡问题,已经很多方法去解决这个问题,目前用的比较多的是focal loss。

两大创新点:

  1. 通过控制alpha, 调节正负样本对总的loss的共享权重。
  2. 增加,通过减少易分类样本的权重,从而使得模型在训练时更专注于难分类的样本。

      通过引入focal loss,实验结果确实提高了2%-3%。

     2019年阿里巴巴又提出了新的概念DR loss,参考链接,已经有源码公布出来了,值得一试。

5.4 数据增强和数据噪音的处理

针对baseline模型识别错误的样本,需要对实验结果进行分析,如果是因为数据原因造成的,可以采用数据增强的方法来增强样本。常用的数据增强方法有:

  • 简单数据增强方法:
  1. 同义词替换。
  2. 实体替换。
  3. 词语乱序、删除。
  •  复杂数据增强方法:
  1. 使用生成模型。值得注意的是,使⽤⽣成模型增强数据,被验证效果不错。生成模型应该最常用的seq2seq+attention。

如果训练样本本身就有很大的噪音,一方面可以通过数据清洗的方法去除噪音,另外也可以选择在模型loss函数加入正则项可以防止模型过拟合,增强模型对噪音的容忍程序,可要参考论文《SIMPLE AND EFFECTIVE REGULARIZATION METHODS
FOR TRAINING ON NOISILY LABELED DATA WITH GENERALIZATION GUARANTEE》。论文提出了一种简单有效的加入正则项运用在噪音数据上来完成文本分类任务。

5.5 引入先验知识

文本分类最关键的步骤在于特征的选择,采取baseline提取特征,没有很好地利用人类已知的先验知识。例如经济类别可能包含股票、证券这些词特征明显,理想模型是可以将这些词的特征加入到模型来优化模型。把这些已经知道的知识则认为是先验知识。

论文《Incorporating Priors with Feature Attribution on Text Classification》论文提出了一种特征属性的方法,将特征属性和基于特定于任务的先验值之间增加了L2距离损失。

论文《Neural Attentive Bag-of-Entities Model for Text Classification》论文提出了从语料库中的单词构建词汇表,而是使用实体以及相关知识来构建实体知识库。通过向量嵌入和与词相关的实体的向量嵌入,给出了词的最终表示。利用实体嵌入增强文章的语义表示,提高文本分类模型表现。

论文《Combining Knowledge with Deep Convolutional Neural Networks for Short Text Classification》论文引入知识库概念化短文本,将概念知识和短文本embedding结合起来。整体采用CNN架构,分别针对文本的word和concept embedding 提取特征,和针对字符embedding(输入为编码的字符序列)提取特征,最后将两种提取的特征concat。这个方法效果还不错,文本分类任务中训练过程更稳定,f1提高1%。架构我采用GELE,没有采用论文中提到的方法。

5.6 模型超参数调整

  1. 建议小的数据集采用低纬度,大的数据集适当提高表征维度。

  2. 模型参数可以参考论文提及到最优参数。

  3. 学习率衰减根据实际训练情况调整。

    但是实际情况我们还是要根据训练收敛程度进行灵活的设置方能获得更好的效果,如果收敛比较可视化,则可以加大学习率使得较快缩短训练时间,当模型在处于摇摆阶段,加快学习率衰减是有必要的。

  4. 为了防止模型过拟合,可适当采用正则化与 dropout 参数。

  5. 优化器可以选择adam和AdaDelta,这篇博客讲到各类优化器的选择,参考链接

    1. 常用的adam优化器,收敛快,可是很难达到最优;而sgd收敛慢,而且不容易跳出鞍点。所以训练模型的时候可以采用Adam自动切换到SGD优化器,从而使得模型找到一个全局最优。ICLR 2018论文《Improving Generalization Performance by Switching from Adam to SGD》提出的一种自动由Adam切换为SGD而实现更好的泛化性能的方法。

  6. batch设置

    训练过程中,batch 的大小也会影响模型的效果。一般设置为4的倍数。

  • 12
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
深度学习文本分类代码通常可以分为以下几个部分: 1. 数据预处理:首先需要对文本数据进行处理,包括文本清洗(如去除标点符号、停用词等),将文本转换成向量或矩阵的形式。 2. 模型构建:采用深度学习模型,如卷积神经网络(CNN)、循环神经网络(RNN)、长短时记忆网络(LSTM)等来进行文本分类。 3. 模型训练:使用训练数据对构建好的模型进行训练,目标是优化模型参数使得模型在训练数据上的表现更好。 4. 模型评估:使用测试数据对训练好的模型进行评估,计算模型在测试数据上的准确率、召回率、F1值等指标。 以下是一份基于TensorFlow实现的文本分类代码,可供参考: ``` import tensorflow as tf class TextCNN(object): """ A CNN for text classification. Uses an embedding layer, followed by a convolutional, max-pooling and softmax layer. """ def __init__( self, sequence_length, num_classes, vocab_size, embedding_size, filter_sizes, num_filters, l2_reg_lambda=0.0): # Placeholders for input, output and dropout self.input_x = tf.placeholder(tf.int32, [None, sequence_length], name="input_x") self.input_y = tf.placeholder(tf.float32, [None, num_classes], name="input_y") self.dropout_keep_prob = tf.placeholder(tf.float32, name="dropout_keep_prob") # Keeping track of l2 regularization loss (optional) l2_loss = tf.constant(0.0) # Embedding layer with tf.device('/cpu:0'), tf.name_scope("embedding"): W = tf.Variable( tf.random_uniform([vocab_size, embedding_size], -1.0, 1.0), name="W") self.embedded_chars = tf.nn.embedding_lookup(W, self.input_x) self.embedded_chars_expanded = tf.expand_dims(self.embedded_chars, -1) # Create a convolution + maxpool layer for each filter size pooled_outputs = [] for i, filter_size in enumerate(filter_sizes): with tf.name_scope("conv-maxpool-%s" % filter_size): # Convolution Layer filter_shape = [filter_size, embedding_size, 1, num_filters] W = tf.Variable(tf.truncated_normal(filter_shape, stddev=0.1), name="W") b = tf.Variable(tf.constant(0.1, shape=[num_filters]), name="b") conv = tf.nn.conv2d( self.embedded_chars_expanded, W, strides=[1, 1, 1, 1], padding="VALID", name="conv") # Apply nonlinearity h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu") # Maxpooling over the outputs pooled = tf.nn.max_pool( h, ksize=[1, sequence_length - filter_size + 1, 1, 1], strides=[1, 1, 1, 1], padding='VALID', name="pool") pooled_outputs.append(pooled) # Combine all the pooled features num_filters_total = num_filters * len(filter_sizes) self.h_pool = tf.concat(pooled_outputs, 3) self.h_pool_flat = tf.reshape(self.h_pool, [-1, num_filters_total]) # Add dropout with tf.name_scope("dropout"): self.h_drop = tf.nn.dropout(self.h_pool_flat, self.dropout_keep_prob) # Final (unnormalized) scores and predictions with tf.name_scope("output"): W = tf.get_variable( "W", shape=[num_filters_total, num_classes], initializer=tf.contrib.layers.xavier_initializer()) b = tf.Variable(tf.constant(0.1, shape=[num_classes]), name="b") l2_loss += tf.nn.l2_loss(W) l2_loss += tf.nn.l2_loss(b) self.scores = tf.nn.xw_plus_b(self.h_drop, W, b, name="scores") self.predictions = tf.argmax(self.scores, 1, name="predictions") # Calculate mean cross-entropy loss with tf.name_scope("loss"): losses = tf.nn.softmax_cross_entropy_with_logits(logits=self.scores, labels=self.input_y) self.loss = tf.reduce_mean(losses) + l2_reg_lambda * l2_loss # Accuracy with tf.name_scope("accuracy"): correct_predictions = tf.equal(self.predictions, tf.argmax(self.input_y, 1)) self.accuracy = tf.reduce_mean(tf.cast(correct_predictions, "float"), name="accuracy") ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值