阅读 1172

怎样用机器学习8步解决90%的自然语言处理问题?

欢迎关注我们,AI教程、趣味科普、论文解读,一网打尽!


文本数据无处不在

不管你身处已经开张的公司还是即将开展的新业务,都可以利用文本数据去验证、优化和扩展产品性能。而从文本数据中学习和提取价值的研究科学就叫做自然语言处理(NLP)。

NLP 每天都能产生新奇而又令人兴奋的结果,也是一个很大的领域。然而,纵观很多公司对NLP 的应用,下面这些应用频繁出现:

  • 识别不同类型的用户/顾客(例如,预测波动状况,终身价值,产品偏好)

  • 精准检测和提取不同种类的反馈(负面或正面评论/意见,提及的特别属性,如衣服尺码/合身与否)

  • 根据目标分类文本数据(例如,用户请求的是基本帮助还是紧急问题)

虽然当前网上已经有很多 NLP 论文和教程,但是很少介绍怎样从头开始有效解决这些问题的指南和窍门。

本文将怎样帮到你

本文会解释如何用机器学习方法解决上面提到的问题,我们会介绍一些最简单也有效的方法,然后再谈谈复杂些的方法比如特征工程、词向量和深度学习。

阅读本文后,你会知道怎样:

  • 收集、准备和检查数据

  • 搭建简单的模型,如果需要,开始和转移至深度学习

  • 演绎和理解你的模型,确保你在获取正确的信息而不是噪声数据

本文是一篇逐步解决问题的教程(建议先收藏,后面一步步动手实践),也可以看作解决 NLP 问题的标准方法的高度概括。

第一步:收集数据

样本数据来源

每个机器学习问题都是以数据开始,比如邮件列表、博文或社交网站发布的状态等。

常见文本数据来源包括:

  • 产品评论(如电商网站、点评网站、应用商店上的评论)

  • 用户生成内容(微博、推特、脸书上发布的状态;Quora、知乎等问答网站的问题)

  • 疑难解答(顾客请求,支持问题,聊天记录)

“社交媒体灾难”数据集(“Disasters on Social Media”)

在本文,我们使用 CrowdFlower 提供的一个数据集,叫做 “社交媒体灾难”

该数据集收集了超过1万条推特推文,包含“着火”“隔离”“混乱”这样的词汇,然后注明推文中提及的灾难事件是否发生过。

我们的任务是检测哪些推文是和真实灾难有关,而不是某个不相关的话题比如灾难电影。为什么是这样?社交网站上可能会添加一项功能专门提醒执法官员出现了紧急情况,但同时不会将灾难电影的评论也考虑在内。这项任务的一个主要挑战就是上面这两种推文都包含同样的搜索关键词,因此我们必须利用微妙的差异去区分它们。

以下我们会将关于灾难的推文引述为“灾难推文”,将关于其它事情的推文引述为“无关推文”。

标签

我们已经将数据打上标签,因此我们知道哪些推文属于哪个类别。如图 Richard Socher 如下所说,找到和标记足够的数据来训练模型,往往比试图优化一个很复杂的无监督式方法要快得多、简单的多,也省时省力的多。

第2步:清洗数据

我们首先要清楚:“你的模型质量始终和数据的质量一致”

数据科学家的一项核心技能就是知道下一步是该处理数据还是模型。一个好方法就是先检查数据,然后清洗数据。一个干净的数据集会让模型学习到有意义的特征,而不会过拟合无关的噪声数据。

下面是清洗数据的方法列表:

  • 删除所有无关紧要的文字比如任何非字符数值的文字。

  • 将文本数据分割为单独文字,也就是将文本数据进行tokenize(将一行拆分为单词长度的片段)。

  • 将所有文字转换为小写字母,目的是将“Hello”“hello”“HELLO”这样的字词一视同仁。

  • 考虑将拼错的单词或交替拼写的单词合并为一个单独的表达方式(比如cool/kewl/coool)。

  • 考虑词形还原(例如将“am”“are”“is”这样的词缩减为常见的表达形式“be”)。

点击链接可查看上述步骤的代码。

遵循以上步骤后,再检查一下其它的错误,然后我们就可以开始清理和标记数据用于训练模型了!

第三步:找到合适的数据表示形式

机器学习模型将数值作为输入。例如,模型在处理图像时,会让一个矩阵表示每个颜色通道中每个像素的密度。

我们的数据集是一系列的句子,所以为了能让我们的算法提取出数据的模式,我们首先需要找到一个合适方式表示数据,能让我们的算法理解它,比如,一列数字。

独热编码(One-hot encoding)(词袋模型)

为计算机表示文本数据的一个自然方式就是将每个字单独编码为一个数字(例如 ASCII)。如果我们用这种简单表示形式的数据输入分类器,那么它必须从头开始根据我们的数据学习词汇的结构,这对大多数数据集来说是行不通的。我们需要一种更高水平的方法。

例如,我们可以给数据集中所有特殊的词创建一个词表,为词表中每个词关联一个特殊索引。然后将每个句子表示为一个列,和词表中特殊词的数目一样长。在这个列的每个索引上,我们标记出给定词在句子中出现的次数。这种方法就叫做词袋模型,因为它的表示形式完全忽略了句子中词汇的顺序。该模型如下图所示:

可视化词嵌入模型

在本文的“社交媒体灾难”样本中,我们的词表中大约有 2 万个词汇,这意味着每个句子会表示为一个长度为 20000 的向量。该向量大部分情况下元素为 0,因为每个句子只是词表的一个非常小的子集。

为了看清我们的嵌入是否获取了和我们的问题相关的信息(例如,推文是否关于灾难),将它们可视化是个很好的方式,可以看清是否很好地进行了分类。由于词表通常很大,因而可视化 2 万个维度的数据是不可能的,像 PCA(主成分分析)这样的方法有助于将数据降至 2 个维度,如下图所绘:

这两个类别看起来并没有很好的分离,这可能是因为我们嵌入的某个特征,或仅仅是降低的特征维度。要想看看词袋模型学习到的特征有没有用,我们可以根据它们训练一个分类器。

第四步:分类

当解决一个问题时,通常最好的实践方法就是先以能解决问题的最简单的工具开始。不管是什么时候分类数据,从功能性和可解释性的角度讲,最常见的方式是逻辑回归。训练逻辑回归非常简单,结果也是可解释的,我们可以很容易地从模型中提取最重要的系数。

我们将我们的数据切分为一个用于训练模型的训练集,和一个用于检验模型性能的测试集。在训练后,我们的模型准确率达到了 75.4%。emmmm...还不赖!不过,即便 75%的准确率足以满足我们的要求,我们也必须在应用模型前对它有个全面的了解。

第五步:检查

混淆矩阵

第一步是理解我们模型所犯错误的类型,以及我们最不希望出现哪种错误。在本文例子中,假阳性是将“不相关推文”分类为“灾难推文”,假阴性是将“灾难推文”分类为“不相关推文”。如果优先选项是对每个潜在的事件做出反应,我们会希望降低假阴性。然而,如果资源受限,我们可能会优先降低假阳性,以降低错误警报。将这种信息可视化的一个好方法就是用混淆矩阵,它会将我们模型的预测和实际标签进行比较。理想状况下,矩阵会是一个自左上角至右下角的对角线(我们模型的预测和实际情况匹配的很好)。

我们的分类器创建的假阴性样本要多于假阳性样本。换句话说,我们模型的常见错误是没有准确地将“灾难推文”分类为“不相关推文”。如果假阳性样本代表执法人员的成本很高,那么这种错误可以算是我们模型的一个好的偏差。

解释和演绎模型

为了验证模型和演绎模型的预测,查看模型使用什么词汇做出决策非常重要。如果我们的数据存在偏置,那么分类器会对样本数据做出准确的预测,但模型在现实情况中不会很好地泛化未见数据。这里我们为“灾难推文”和“不相关推文”划分出最重要的词汇。使用词袋模型和逻辑回归划出词汇的重要性很简单,因为我们只需提取模型用于做出预测的系数,并将系数排名。

我们的分类器正确地识别出一些模式,但很明显在某些无意义的词上存在过拟合现象。现在,我们的词袋模型能处理庞大词汇表内的不同词汇,并对所有的词汇分配相同的权重。然而,其中有些词出现的非常频繁,只会给模型的预测带来干扰。下一步,我们会尝试用能够计算词汇频率的方式表示句子,以此弄清我们是否能从数据中获取更多的信号。

第六步:计算词汇结构

TF-IDF 嵌入模型

为了帮助我们的模型更多的关注有意义的词汇,我们可以在词袋模型上使用 TF-IDF 评分(词频-逆向文件频率)。TF-IDF 会根据词汇在数据集中的稀有程度为词汇赋予权值,不考虑词汇是否过于频繁。下面是我们 TF-IDF 嵌入模型的 PCA 映射:

从上图我们可以看到,两种颜色之间的不同更加清晰。这能让我们的分类器更容易地将两组数据分离。我们看看这是否会提高模型的性能。用新模型上训练另一个逻辑回归后,我们得到了 76.2%的正确率。

虽然稍微有些改善,但我们的模型开始选择更重要的词汇了吗?如果我们在防止模型“作弊”的同时,得到的结果也越来越好,那么我们就可以真的相信模型确实得到了改进。

模型选择的词汇看起来比之前相关多了!虽然我们测试集中的权值仅增加了一点点,但我们对模型有足够的信心,这样就可以放心大胆地部署模型了。

第七步:利用语义信息

Word2Vec

TF-IDF 嵌入模型能够学习到信号更高频的词汇,但是如果我们部署这个模型,我们很可能会遇到在训练集中从未见过的词汇。之前的模型就不能准确分类这些推文,即便在训练中见过非常相近的词汇。

为了解决这个问题,我们需要获取词汇的语义意义,也就是说我们需要理解像“good”(好的)和“positive”(正的)这样的词汇之间的语义,比“apricot”(杏子)和“continent”(大洲)之间的语义要相近的多。帮我们获取词汇语义意义的工具叫做Word2Vec。

使用预训练模型

Word2Vec 是 Google 开源的一款用于词向量计算的工具。它可以通过阅读大量的文本和记忆哪些词倾向于出现在相同语境中进行学习。在用足够多的数据训练 Word2Vec 后,它会为词表中的每个词汇生成一个 300 维度的向量,并且意思相近的词汇离得更近。

有人开源了一个用超大数据集训练而成的预训练模型,我们可以利用它将一些语义方面的知识添加进我们的模型。预训练向量可以在本教程的相关代码仓库中找到。

语句分级表示

为我们的分类器得到句式嵌入(sentence embedding)的快速方法就是用 Word2Vec 给我们句子中所有的词评分。这其实就是我们刚才用的词袋模型法,但这次我们只丢弃句子的语法,而保留语义信息。

下面是用刚才提到的方法将我们的新嵌入可视化后的效果:

这两种颜色群组看起来进一步分离,我们的新嵌入应该帮助了分类器发小两种类别的分离之处。又一次训练这个模型后,我们得到了 77.7% 的准确率,迄今最好结果!是时候检查模型了。

复杂性与可解释性之间的权衡取舍

由于新模型没有像之前模型那样,将单词表示为一个一维向量,因而查看和我们的分类最相关的词汇更难了。虽然我们仍然能获取逻辑回归的系数,但它们和我们词汇的 300 个维度相关而非词汇的索引。

虽然模型的准确度有所提高,但如果无法进行可解释性分析那就有点得不偿失了。然而,如果模型的复杂性进一步提高,我们可以用 LIME 这样的“黑盒解释器”,从而对分类器的工作原理有一点了解。

LIME

可以在 GitHub 上获取 LIME 的开源工具包

黑盒解释器可以让用户通过扰乱输入(在我们的案例中是从句子中移除单词)和查看预测的改变情况,根据一个特定的例子就能解释任何分类器做出的决策。

我们看几个对我们数据集中的语句所做的解释:

图:正确分类的灾难性词汇被归类为“相关”

图:这个词对分类的影响似乎不太明显

然而,我们并没有时间探究数据集中的数千个样本。相反,我们会在测试集的代表样本上运行 LIME ,看看哪些词对分类预测的影响一直很大。通过这种方式,我们可以像之前一样获取单词的重要性分数,验证模型的预测结果。

看来模型能够提取高度相关的词汇,表明它能做出可解释的决定。这些词的相关度在所有模型中似乎是最高的,因此我们会更愿意部署这样的模型。

第八步:用端到端的方法使用语法信息

我们已经介绍过怎样快速高效地生成紧凑的句子嵌入。不过,通过省略了词汇的顺序,我们也放弃了句子的全部语法信息。如果这些方法未能产生满意的结果,你可以使用更复杂的模型:将整个句子作为输入和预测标签,并无需创建中间表示。这么做的一个常见方法是将句子看作词向量的序列,使用 Word2Vec 或一些新工具如 GloVe 和 coVe 等都可以。

下面我们详细说说。

用于语句分类的卷积神经网络(CNN)训练起来很快,作为一个入门级的深度学习架构效果也很好。虽然 CNN 向来以处理图像数据著称,但它们在处理文本相关的任务上同样效果良好,而且常常比大多数复杂的自然语言处理方法速度快得多(比如 LSTM)。CNN 在保留词汇顺序的同时,还会学习词汇序列等有价值的信息。相较于之前的模型,它能区分出“Alex eats plants”(Alex 吃植物)和“Plants eat Alex”(植物吃 Alex)之间的不同。

相比先前模型,训练 CNN 并不需要做更多工作,点击查看详细代码 ,而且得到的模型比我们之前的模型性能还要好,准确率达到了 79.5% !

与前面介绍的步骤一样,下一步应该是用我们讲的方法探究和解释模型的预测结果,以验证确实是可以部署的最佳模型。到了这一步,你应该能自己完成这里的操作了。

最后小结

我们迅速回顾一下各步所用的方法:

  • 首先以一个简单迅速的模型着手

  • 解释模型的预测结果

  • 弄懂模型犯了什么类型的错误

  • 根据这些知识决定你的下一步——处理数据还是应用更复杂的模型

虽然本教程所用的方法只用于特定的例子——使用合适的模型进行理解和应用推文这样的短文本,但这种理念和思路适用于各种问题。希望本文对你有所帮助,如有问题和建议,欢迎提出!

向你推荐:

一文读懂CNN如何用于NLP

边看边练的简明机器学习教程

关注下面的标签,发现更多相似文章
评论