使用Python和keras进行文本分类(下)

实战教程

作者:Nikolai Janakiev

编辑整理:萝卜兔

目录

选择数据集

定义基线模型

深度神经网络入门

Keras简介

Keras安装

编写你的第一个Keras模型

什么是Word Embedding

One-Hot编码

Word Embedding

Keras嵌入层

使用预训练的文本嵌入

卷积神经网络

总结

扩展阅读

什么是Word Embedding

文本被认为是一种序列数据形式,类似于天气数据或金融数据中的时间序列数据。在前面的BOW模型中,已经学习了如何将整个单词序列表示为单个特征向量。现在我们将学习如何将每个单词表示为向量。文本矢量化有多种方法,例如:

每个单词表示为向量的单词

每个字符表示为向量

N-gram单词/字符表示为向量

在本教程中,将学习如何将单词表示为向量,这是在神经网络中使用文本的常见方式。将一个单词表示为向量的两种方式:一是one-hot encoding,二是Word Embedding 。

One-Hot Encoding

将单词表示为向量的第一种方式是通过创建one-hot encoding,这种方式将单词表示为词汇表长度的一个向量,每个单词在对应的位置为1,其他所有位置都是0,可以想象,这会使得每个单词的向量维度都很高,并且不会提供单词之间的相关信息。

以城市列表为例:

我们可以使用scikit-learn和LabelEncoder将城市列表编码分类为整数值,如下所示:

使用这种表示,可以使用scikit - learn提供的OneHotEncoder将我们之前得到的分类值编码成one-hot编码。OneHotEncoder希望每个分类值都在一个单独的行中,因此需要重新调整数组,然后应用编码器:

可以看到向量在类别值的位置是1,其他位置是0。当有一个不能用数值表示的类别特征,但仍然希望在机器学习中使用它时,经常会用这种方法。这种编码的一个用例当然是文本中的单词,但是它主要用于类别。这些类别可以是例如城市、部门或其他类别。

Word Embeddings

这种方法将单词表示为稠密向量(也称为word embeddings),这些矢量需要经过训练,不同于硬编码的one-hot encoding。这意味着embedding一词将更多信息收集到更少的维度中。

请注意,word embedding并不像人类那样理解文本,而是映射语料库中使用的语言的统计结构。他们的目的是将语义映射到几何空间。这个几何空间被称为嵌入空间(embedding space)。

这将使得语义相近的词在嵌入空间更近,如数字或颜色。如果嵌入很好地捕捉了词与词之间的关系,那么就可能实现矢量运算。这个研究领域的一个著名例子是绘制King-Man+Woman=Queen

怎么能嵌入这样一个词呢?你有两个选择。一种方法是在训练神经网络的过程中训练word embedding。另一种方法是使用预处理的word embedding,在模型中直接使用。现在,我们需要将数据转换为可由word embeddings使用的格式。keras为文本预处理和序列预处理提供了一些方便的方法,可以用来准备文本。

我们可以从使用Tokenizer开始,这个类可以将文本语料库矢量化为整数列表。每个整数映射到字典中对整个语料库进行编码的值,字典中的键值是词汇本身。还可以添加参数num_words,它负责设置词汇表的大小。然后将保留最常见的num_words单词。我已经从前面的例子中准备了测试和训练数据:

索引按文本中最常见的单词排序,通过索引1的单词可以看到。值得注意的是,索引0是保留的,不会分配给任何单词。这个零索引用于填充,稍后我会介绍。

未知单词(不在词汇表中的单词)在Keras中用word_count+1表示,因为它也可以保存一些信息。可以通过查看Tokenizer对象的word_index字典来查看每个单词的索引:

注意

注意关注此技术与scikit-learn的CountVectorizer生成的X_train之间的区别。

使用CountVectorizer,我们有字数统计的堆叠向量,每个向量的长度相同(总语料库词汇量的大小)。使用Tokenizer,得到的向量等于每个文本的长度,数字不表示计数,而是对应于字典tokenizer.word_index中的单词值。

我们遇到的一个问题是,每个文本序列在大多数情况下具有不同长度的单词。为了解决这个问题,我们可以使用pad_sequence(),用零填充单词序列。默认情况下会填充零,一般是在末尾附加,不过前置还是后面附加都无关紧要。

此外,还需要添加一个maxlen参数来指定序列的长度。这会削减超过该数量的序列。在下面的代码中,将看到如何使用Keras填充序列。

第一个值表示从前面的示例中学到的词汇表的索引。我们可以看到生成的特征向量主要包含0,因为这些句子很短。下一部分我们将讲解如何在Keras中使用单词嵌入。

Keras Embedding Layer

注意,此时,我们的数据仍然是hardcoded。我们还没有告诉Keras通过后续任务学习新的embedding space。现在,我们可以使用Keras的Embedding Layer,它采用先前计算的整数并将它们映射到嵌入的dense vector。定义以下参数:

input_dim: the size of the vocabulary(词汇的大小)

output_dim: the size of the dense vector(dense vector的大小)

input_length: the length of the sequence(句子的长度)

使用嵌入层,现在有几个选项。一种方法是获取嵌入层的输出并将其插入Dense层。我们需要在它们之间添加一个Flatten图层来为Dense图层准备顺序输入:

结果如下:

我们可以看到有87350个新参数需要训练。这个数字是vocab_size乘以embedding_dim。嵌入层的这些权重用随机权重初始化,然后在训练期间通过反向传播进行调整。该模型将单词按照句子的顺序作为输入向量。您可以使用以下方进行训练:

结果如下:

正如在性能结果中所看到的,这通常是一种不太可靠的方式来处理Sequential data。处理Sequential data时,需要关注局部信息和序列顺序信息,而不是绝对位置信息。

使用嵌入的另一种方法是在嵌入后使用MaxPooling1D / AveragePooling1D或GlobalMaxPooling1D / GlobalAveragePooling1D图层。您可以将池化层视为对输入特征进行下采样(一种减小大小的方法)。

在max pooling(最大池化)的情况下,对每个特征维度在每个局部区域取最大值。在average pooling(平均池化)的情况下,取平均值,但最大池化似乎更常用,因为它突出显示大值。

全局最大/平均池化取整个特征的最大值/平均值,而在另一种情况下,您必须定义pool大小。 Keras还有自己的层,可以在Sequential模型中添加:

结果显示如下:

训练程序不变:

结果如下:

我们可以发现模型得到了改进,下面使用预训练的Word Embedding。

Using Pretrained Word Embeddings

我们刚刚看了一个示例,而另一种方法是使用预先计算的embedding space,这个embedding space使用更大的语料库。可以通过简单地在大量文本上训练来预先计算单词嵌入。最受欢迎的方法包括由Google开发的Word2Vec和由Stanford NLP Group开发的GloVe。

注意,这些是具有相同目标的不同方法。 Word2Vec通过采用神经网络实现了这一点,GloVe通过co-occurrence matrix和使用矩阵分解实现了这一点。在这两种情况下,都在处理降维,但Word2Vec更准确,GloVe计算速度更快。

在本教程中,我们将了解如何使用Stanford NLP Group的GloVe单词嵌入,因为它们的大小比Google提供的Word2Vec单词嵌入更易于管理。在这里下载训练的数据:

http://nlp.stanford.edu/data/glove.6B.zip

我们还可以在GloVe主页面上找到其他单词嵌入。可以在此处找到Google预训练的Word2Vec嵌入。如果想训练自己的单词嵌入,可以使用使用Word2Vec进行计算的gensim Python包有效地完成。有关如何在此处执行此操作的详细信息:

https://radimrehurek.com/gensim/models/word2vec.html

现在我们可以开始在模型中使用word embedding了。我们将在下一个示例中讲解如何加载嵌入矩阵。文件中的每一行都以单词开头,后面跟着特定单词的嵌入向量。

这是一个包含400000行的大文件,每行代表一个单词,后面是特征向量。例如,以下是第一行的前50个字符:

由于我们不需要所有单词,因此只需要专注于我们词汇表中的单词。由于我们的词汇表中只有有限数量的单词,因此我们可以跳过预训练单词嵌入中的大部分单词:

可以使用以下函数取回嵌入矩阵:

现在我们已准备好在训练中使用嵌入矩阵。让我们继续使用以前的网络和全局最大池,看看我们是否可以改进这个模型。当您使用预训练的单词嵌入时,您可以选择允许在训练期间更新嵌入,或者仅使用生成的嵌入向量。

首先,让我们快速查看有多少嵌入向量是非零的:

图中所示,预训练的词覆盖了95.1%的词汇。让我们看看使用GlobaMaxPool1D层的性能:

结果如下:

如果word embedding没有另外训练,结果可能会稍微差一些。现在让我们允许embedding训练trainable=True:

结果如下:

可以看到,进行嵌入训练效果是最好的。在处理大型的训练集时,它可以使训练过程比没有该训练过程快得多。在我们这个教程实验中,有所帮助,但是不明显,这有可能并不是因为嵌入预训练。

现在,是时候学习更超前的神经网络模型看看是否可以提升模型并使它比之前的模型更有优势。

选择数据集

卷积神经网络是近年来机器学习中最令人兴奋的进展。它可以从图像中提取特征,在图像分类和计算机视觉中引发了革命性的进展。在图像处理中表现的属性使它对处理序列也很方便。我们可以把CNN想象成一个特殊的神经网络可以处理特别的模式。

如果它只是另一种神经网络,那和我们之前学习的那些网络有什么区别呢?

CNN的隐藏层我们叫做卷积层。当我们想到图像时,计算机是处理一个二维的矩阵,因此我们需要一种方法来获取矩阵中的特征。这些卷积层能够检测边缘,角落和其他类型的纹理,这使它们成为一种特殊的工具。卷积层由多个过滤器组成,它们可以滑过图像并能够检测特殊特征。

这是该项技术的核心,即卷积的数学过程。网络通过每个卷积层能够检测更加复杂的模式。当我们要处理Sequential data,比如文本,我们使用一维卷积,但是思维和应用程序是不变的。我们仍然需要了解序列中的模式,这些模式随着每个添加的卷积层变得更加复杂。

下面我们来看看卷积是怎么工作的。首先,获取一个具有滤波器内核大小的输入特征patch。使用这个patch,你可以得到滤波器的相乘权重的点积。一维卷积对于平移是不变的,这意味着可以在不同位置识别某些序列。这对文本的一些模式很有帮助。

我们来看怎么在Keras中实现。Keras提供许多卷积层能够帮助我们实现这个任务。我们需要的是Conv1D层。该层有许多参数需要我们选择。首先是过滤器的数量,卷积核大小,激活函数。我们可以把卷积层添加到Embedding层和 GlobalMaxPool1D之间。

结果如下:

我们可以看到,80%的准确率,好像这个数据集只能如此了。可能的原因有:

没有足够的训练样本

拥有的数据并没有很好的概括性

缺乏对超参数的关注

CNN适合大型训练集,在大型训练集中CNN能够实现像逻辑回归这样简单模型无法实现的特征归纳。

总结

我想,大家已经学会了如何使用Keras进行文本分类,从逻辑回归模型一步一步到卷积神经网络。 我想,大家应该已经熟悉word embedding,它们有用的原因,以及如何在训练中使用预训练的单词嵌入。

我们还没有涉及的另一个重要主题是循环神经网络(RNN),更具体地说是LSTM和GRU。这些是处理文本或时间序列等顺序数据的其他强大且流行的工具。其他有趣的发展目前正在神经网络中引起注意。

我们现在学习了自然语言处理中的关键知识,可以用于各种文本分类,情绪分析等等,还有以下的一些应用:

垃圾邮件检测

自动标记文本

对新闻文章进行分类

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181107A0BRUT00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券