【干货教程】自然语言处理入门:手把手教你解决90%的NLP问题
选择“星标”公众号
重磅干货,第一时间送达!
文本数据无处不在
无论您是一个成熟的公司,还是致力于推出一个新服务,您都可以利用文本数据来验证、改进和扩展您的产品的功能。从文本数据中提取有意义的信息并对其进行学习是自然语言处理(NLP)的一个研究活跃的课题。NLP领域每天都会产生新的令人兴奋的结果,在与数百家公司合作之后,Insight团队发现一些比较关键的实际应用比其他应用出现得更为频繁,具体来说有以下几种:(1)识别不同的用户/客户群体(如预测流动率、寿命值、产品偏好)(2)准确地检测和提取不同类别的反馈(正面和负面的评论/意见,提到的特定属性,如衣服尺寸/健康等)(3)根据意图对文本进行分类(例如,请求基本帮助,紧急问题)
在每年领导数百个项目的经验,并从美国各地的顶级团队那里获得建议之后,我们撰写了这篇文章,解释了如何构建机器学习解决方案来解决上面提到的问题。我们将从最简单的方法开始,然后转向更细致的解决方案,比如特征工程、单词向量和深度学习。读完这篇文章,你会知道如何:(1)收集、准备和检查数据;(2)建立简单的模型,并在必要时向深度学习过渡;(3)解释和理解你的模型,以确保你实际上是在获取信息而不是噪音。我们把这篇文章作为一个循序渐进的指南,它也可以作为高度有效的标准方法的高级概述,这篇文章附有一个交互式 notebook,你可以随意运行代码同步学习:https://github.com/hundredblocks/concreteNLPtutorial/blob/master/NLP_notebook.ipynb
第1步:收集你的数据
示例数据来源每一个机器学习问题都是从数据开始的,比如电子邮件、帖子或推文(微博)。文本信息的来源包括:(1)产品评论(在亚马逊,Yelp和各种应用商店)(2)用户发布的内容(Tweets,Facebook帖子,StackOverflow问题)(3) 其他类信息(客户请求、支持票据、聊天记录)
本文我们将使用由 CrowdFlower提供的一个名为「社交媒体中出现的灾难」的数据集,其中:编者查看了超过 1万条推文,其中包括「着火」、「隔离」和「混乱」等各种搜索,然后看推文是否是指灾难事件(排除掉用这些单词来讲笑话或评论电影等没有发生灾难的情况)。
我们的任务是检测哪些推文关于灾难性事件,排除像电影这种不相关的话题。为什么?一个可能的应用是仅在发生紧急事件时(而不是在讨论最近 Adam Sandler 的电影时)通知执法官员。这篇文章的其它地方,我们将把关于灾难的推文称为「灾难」,把其它的推文称为「不相关事件」。
第2步:清洗数据
我们遵循的第一条规则是:“你的模型会受你数据的影响。”数据科学家的关键技能之一是知道下一步的工作对象应该是模型还是数据。对于数据清洗,一个好的经验法则是先查看数据然后再进行清理。一个干净的数据集将使得一个模型学到有意义的特性,而不是过度拟合无关的噪音。以下是用来清理你的数据的清单:(1)删除所有不相关的字符,例如任何非字母数字字符;(2)把你的文章分成一个个单独的单词;(3)删除不相关的单词,比如“@”twitter或网址;(4)将所有字符转换为小写,以处理诸如“hello”、“Hello”和“HELLO”等单词;(5)考虑将拼错的单词或拼写单词组合成一类(如:“cool”/“kewl”/“cooool”);(6)考虑词性还原(将「am」「are」「is」等词语统一为常见形式「be」)。在遵循这些步骤并检查额外的错误之后,我们就可以开始使用干净的、标记的数据来训练模型!
第3步:寻找好的数据表示
机器学习模型以数值作为输入。例如,在图像上工作的模型,可以采用表示每个颜色通道中每个像素强度的矩阵。
一个以数字矩阵表示的笑脸。
我们的数据集是一个句子的列表,所以为了让我们的算法从数据中提取模式,我们首先需要找到一种方法来使得以我们的算法能够理解的方式来表示它,也就是一个数字列表。
One-hot encoding (词袋模型)表示计算机文本的一种自然方法是将每个字符单独编码为一个数字(例如ASCII编码)。如果我们要将这个简单的表示输入到分类器中,那么它必须只能根据我们的数据来学习单词的结构,这对于大多数数据集来说是不可能的,我们需要使用更高级的方法。例如,我们可以在我们的数据集中建立一个所有的单词的词汇表,并将一个唯一的索引与词汇表中的每个单词联系起来。每个句子被表示为一个列表,只要我们的词汇表中有不同单词的数量。在这个列表中的每个索引中,我们标记出在我们的句子中出现了多少次给定的单词。这被称为词袋模型,因为它是一种完全无视我们句子中词语顺序的表现形式,如下图所示:
将句子表示为词袋。左边为句子,右边为对应的表示,向量中的每个数字(索引)代表一个特定的单词。
可视化词嵌入在“社交媒体中出现的灾难”例子中,我们的词汇量大约有2万个单词,这意味着每一个句子都将被表示成一个长度为2万的向量。这个向量几乎包含了0,因为每个句子只包含了我们词汇量的一小部分。为了查看我们的嵌入是否捕获了与我们的问题相关的信息(例如,tweet是否与灾难有关),可视化它们并查看这些类的分离程度,这是一个好主意。由于词汇表通常是非常大的,并且在20,000个维度中可视化数据是不可能的,因此需要主成分分析(PCA)这样的技术将有助于将数据压缩到两个维度,如下图所示:
将嵌入的词袋可视化
这两个类看起来并没有很好地分离,这可能是我们嵌入的一个特性,或者仅仅是我们的维度缩减。为了了解这些词的特点是否有任何用途,我们可以根据它们来训练一个分类器。
第4步:分类
面对一个问题时,一般好的做法是从最简单的工具开始由浅入深地解决问题。当提到数据分类时,人们最喜欢方法是的 Logistic 回归(Logistic Regression),这是一种非常简单的训练方法,结果是可以解释的,因为你可以很容易地从模型中提取最重要的参数。我们将数据分解为一个训练集,用于拟合我们的模型和一个测试集,以查看它对不可见的数据的概括程度。经过训练,我们的准确率达到了75.4%,不至于太难看。然而,即使75%的精度满足了我们的需求,我们也不应该在不理解模型的情况下发布模型。
第5步:检查
混淆矩阵(Confusion Matrix)
第一步是了解我们模型所出的错误类型,以及哪些错误是最不可取的。在我们的例子中, false positive将一个无关的推文归类为灾难,而 false negtive则将灾难分类为不相关的tweet。如果首要任务是对每一个潜在事件优先处理,我们就想降低我们的false negtive结果。如果我们优先考虑资源受限的问题,我们可能会优先考虑降低false positive率以减少误报的提醒。一个很好的可视化这个信息的方法是使用一个混淆矩阵,它比较了我们的模型和真实的标签的预测。理想情况下,矩阵将是一条从左上到右下的对角线(即我们的预测完全符合事实)。
我们的分类器创造了更多的false negtive,而不是false positive(比例)。换句话说,我们的模型最常见的错误是错误地将灾难分类为无关紧要的。如果误报是执法的高成本,我们更倾向于使用这个分类器。
解释我们的模型 为了验证我们的模型并解释它的预测,重要的是看一下它用哪些单词在预测中起主要作用。如果我们的数据有偏差,我们的分类器会在样本数据中做出准确的预测,但是模型在实际应用效果并不理想。在这里,我们给出了灾难和不相关事件的重要词汇。用词袋模型和Logistic 回归来描绘单词的重要性是很简单的,因为我们可以提取和排列模型用于预测的系数。
我们的分类器正确地选择了一些模式(广岛,大屠杀),但显然似乎是过度拟合一些无意义的术语(heyoo, x1392)。现在,我们的词袋模型是处理大量不同单词的词汇,并对所有单词一视同仁。然而,有些词是非常频繁的,而且只会对我们的预测造成干扰。接下来,我们将尝试着用一个方法来表示词汇出现的频率,看我们能否从数据中获得更多的信号。
第6步:词汇统计
TF-IDF 为了帮助我们的模型更多地关注有意义的单词,我们可以在我们的单词模型包上使用TF-IDF评分(词频-逆文档频率)。TF-IDF 通过对数据集中词汇出现的频率来加权,并减小高频但只是增加噪音的单词的权重,这是我们新嵌入的 PCA 预测。
TF-IDF 嵌入可视化
我们可以看到,这两种颜色之间有更明显的区别,这将使我们的分类器更容易区分两个组。让我们看看这会不会带来更好的性能。在我们新嵌入的逻辑回归,我们得到了76.2%的精确度。一个非常轻微的改善,我们的模型是否可以选择更重要的词汇?如果我们在防止模型从“欺骗”中得到更好的结果,那么我们就可以说这个模型得到了优化。
TF-IDF:重要词汇它挑选的单词看起来更加相关了!虽然我们在测试集上的指标只稍微增加了一点,但是我们对我们的模型使用的词汇更加关键,因此在将它部署到与客户交互的系统中会更舒适有效。
第7步:利用语义信息
Word2Vec 我们的最新型号成功地挑出了高信号词。然而,很有可能的是,如果我们部署这个模型,我们将会遇到以前在我们的训练中没有看到的单词。之前的模型将无法准确地对这些推文进行分类,即使在训练过程中看到了非常相似的单词。为了解决这个问题,我们需要抓住词语的语义含义,这意味着我们需要理解像“good”和“positive”更接近而不是“apricot”和“continent”。我们用来帮助我们捕捉单词含义的工具叫做Word2Vec。
使用预训练的单词 Word2Vec 是寻找单词连续 embedding 的技术。它可以从阅读大量的文本中学习,并记住在类似的语境中出现的单词。在对足够的数据进行训练之后,它会在词汇表中为每个单词生成一个300维的向量,而单词之间的意思相近。
句子层面表示 一个快速得到分类器的 sentence embedding 的方法是平均对句子中的所有单词的 Word2Vec 评估。这是一个像以前一样的词袋模型,但是这次我们只会忽略句子的语法,同时保留一些语义信息。
下面是我们使用以前的技术实现的新嵌入的可视化:
可视化 Word2Vec 嵌入
这两组颜色看起来更加分离,我们的新嵌入应该帮助分类器找到两个类之间的分离。在训练了相同的模型第三次(Logistic 回归)后,我们得到了77.7%的准确率,这是我们最好的结果!是时候检查我们的模型了。
复杂性/ 可解释性权衡 由于我们的embedding不是像之前的模型那样以每个单词表示为一个维度,因此很难看出哪些单词与我们的分类最相关。虽然我们仍然可以使用Logistic回归的系数,但它们与我们的embedding 的300个维度有关,而不是单词的索引。对于如此低的精确度,失去所有的解释能力似乎是一种苛刻的取舍。但是,对于更复杂的模型,我们可以利用像LIME这样的黑盒解释器来了解我们的分类器是如何工作的。
第8步:利用端到端(end-to-end)的方法
我们已经介绍了快速和有效的方法来生成紧凑的句子嵌入。然而省略了单词的顺序,我们跳过了句子所有的句法信息。如果这些方法没有提供足够的结果,我们可以使用更复杂的模型,将整个句子作为输入并预测标签,而不需要构建中间表示。一种常见的方法是使用Word2Vec或更类似的方法,如GloVe或CoVe,将句子看作一个单词向量的序列,这就是我们接下来要做的。
高效的端到端结构论文《Convolutional Neural Networks for Sentence Classification》中的模型训练分类模型非常快,并且它是作为入门级的深度学习体系效果非常理想。虽然卷积神经网络(CNN)主要以其在图像数据上的性能而著称,但它们在与文本相关的任务上同样也展示了出色的结果,而且通常比大多数复杂的NLP方法(例如LSTMs和Encoder/Decoder结构)训练更快。这个模型保存了单词的顺序,并学习了有价值的信息,并学习了哪些单词序列可以预测目标类等有价值的信息,可以区别「Alex eats plants」和「Plants eat Alex」。训练这个模型不需要比以前的方法做更多的工作,并且效果更好,获得79.5%的准确性!见代码:https://github.com/hundredblocks/concreteNLPtutorial/blob/master/NLP_notebook.ipynb。与上面的模型一样,下一步应该使用我们描述的方法来探索和解释预测,以验证它确实是给用户提供的最佳模型。到现在为止,你应该对解决这类问题很熟练了。
最后的话
下面是我们成功使用方法的简要回顾:(1)从一个简单的模型开始;(2)解释其预测;(3)理解其错误类型;(4)使用这些知识来判断下一步的工作,无论是正在处理您的数据,还是一个更复杂的模型。这些方法只应用于一个特定的例子,使用的模型是针对理解和利用短文本(如tweet)而进行的,但是这些想法广泛适用于各种问题。希望这会对你有所帮助,我们很乐意听到你的评论和问题!