不论是已经成熟的企业或是正要上线新业务,你都可以利用文本数据来验证、提升以及扩展产品的功能。这种从文本数据中提取含义并进行学习的科学叫做自然语言处理(Natural Language Processing, NLP),是当前非常热门的研究课题。
NLP 是一个非常大的研究领域且每天都在产生一些新的激动人心的成果。经过和上百家企业的合作,我们 Insight 团队从中发现了几个关键的实际应用,这些应用会有更高的出现频率:
识别使用者 / 客户不同的用户群(例如预测用户流失、生命周期价值、产品偏好等)
精准检测和提取反馈分类(例如正面和负面的评论 / 观点,对诸如衣服尺寸、舒适度等一些特别属性的提及)
根据意图进行文本分类(例如请求普通帮助,紧急问题处理)
鉴于 NLP 的文章和指南很多都存在于网络上,我们发现很难找到对处理这些问题彻底有效的指导或建议。
我们在这一年里做了数百个项目,并吸取了全美顶尖团队的建议,整理出这篇文章来说明如何使用机器学习解决上述问题。我们将从最简单可行的方法说起,然后进阶到更细致的解决方案,例如特征工程、词向量以及深度学习等。
读完这篇文章,你将会知道如何去:
收集、准备和检测数据
从建立简单模型开始,必要时使用深度学习来过渡
解释和理解模型,来确保你实际捕获的是有效信息而不是噪声
这篇文章是一份循序渐进的操作指南,也是一份对高效标准化过程的高度概括。
文章还附带了一份互动笔记,里面对所有涉及到的技术都进行了论证和应用。你可以尝试边运行代码边看下文。
每一个机器学习问题都始于数据,比如一组邮件、帖子或是推文。文本信息的常见来源包括:
商品评价(来自 Amazon、Yelp 以及其他 App 商城)
用户产出的内容(推文、Facebook 的帖子、StackOverflow 的提问等)
问题解决(客户请求、技术支持、聊天记录)
在这篇文章中,我们将使用 CrowdFlower 提供的一个数据集,名为“社交媒体中的灾难(Disasters on Social Media)”。
贡献者们查看了超过 10000 条具有类似“着火”、“隔离”、“混乱”等搜索关键词的推文,然后标记这个推文是否和灾难事件有关(与之相反的是一些玩笑、电影点评或是一些非灾难性的事件)。
我们的任务是分辨出哪些推文是真正和灾难事件相关的,而不是一些类似电影描述的不相关话题。为什么呢?一个潜在的应用是针对突发事件对执法人员进行专门的提醒,而不会被其他无关信息,比如 Adam Sandler 新上映的电影所干扰。这项任务中一个特别的挑战是这两种情况在搜索推文的时候都用到了相同的检索词,所以我们只能通过细微的差别去区分他们。
在下面的文章中,我们将把与灾难事件相关的推文称为“灾难”,将其他推文称为“不相关的”。
我们已经标注过数据,所以知道推文是如何分类的。如 Richard Socher 所说,比起优化一个复杂的无监督学习方法,寻找和标记足够多的数据来训练模型会更加快捷、简单和廉价。
Richard Socher 的高级技巧
我们要遵循的首要原则是:“你的模型必须和你的数据一样好。”
数据科学家的一个必备技能是知道自己的下一步操作是处理模型还是数据。有一个好的经验法则是先观察数据然后进行数据清洗。一个干净的数据集能使模型学习到有意义的特征而不会被一些不相关的噪声影响。
可以借鉴下方的列表来进行数据清洗:(查看代码获取更多信息)
去除一切不相关的字符,比如任何非字母数字的字符
标记你的文本,将他们拆分为独立的单词
去除不相关的词语,比如 @这类提醒或是 url 链接
将所有字母转换成小写,这样“hello”,“Hello”,“HELLO”就会被当做同样的单词处理
将拼错的单词或是多种拼法的单词与某个特定的表达绑定(比如:“cool”/“kewl”/“cooool”)
考虑词形还原(比如将“am”,“are”,“is”都看做“be”)
完成这些步骤并检查完其他错误后,我们就可以使用这些干净的、标记过的数据进行模型训练了!
机器学习模型会使用数值作为输入。例如处理图像的模型会用矩阵来表示每个颜色通道像素的亮度。
用数字矩阵表现的笑脸
我们的数据集是一系列的句子,为了使我们的算法能从数据中提取特征,首先需要找到一种算法能够理解的表达方式,比如一串数字。
通常为计算机解释文本的方法是将每一个字符都编为一个独立的数字(例如 ASCII 码)。如果使用这种简单的表达来做分类器,需要我们的数据从头开始学习词语的结构,这对大多数数据集来说是很难实现的。所以我们需要一种更上层的方法。
例如,我们可以为数据集中的所有单词制作一张词表,然后将每个单词和一个唯一的索引关联。每个句子都是由一串数字组成,这串数字是词表中的独立单词对应的个数。通过列表中的索引,我们可以统计出句子中某个单词出现的次数。这种方法叫做 词袋模型,它完全忽略了句子中单词的顺序。如下图所示:
用词袋模型表示句子。句子在左边,模型表达在右边。向量中的每一个索引代表了一个特定的单词。
在“社交媒体中的灾难”样本词表中大概会有 20000 个单词,这意味着每句句子都会用一个长度为 20000 的向量来表示。向量的 大部分会被 0 填充,因为每句话只包含了词表中很小的一个子集。
为了看出嵌入的工作是否真正抓住了和问题相关的信息(比如推文是否与灾难相关),有一个好方法是将它们可视化,然后观察结果是否有很好的分布。考虑到词表通常很大,而且用 20000 维的数据做可视化是基本不可能的,所以我们使用了 PCA 这种技术将数据降到二维。绘制如下:
词袋模型嵌入的可视化
两个分类看起来没有很好的分离,这可能是我们选择的嵌入方法的特征或是单纯因为维度的减少引起的。为了了解词袋模型的特征是否会起一些作用,我们可以试着基于它训练一个分类器。
当初次接触一个问题,通常来说最好的方法是先挑选一个能解决问题的最简单的工具。当提到数据分类时,一般最受欢迎的是通用性和可解释性兼具的逻辑回归算法。这种算法很容易训练而且结果也是可解释的,你可以很轻松地从模型中提取出最重要的一些系数。
我们将数据分为两个集合,训练集用于匹配模型,测试集用于观察应用在未知数据上的效果。训练后我们得到了 75.4% 的精确度。结果还不错!推测出现最多的类(“不相关”)只能达到 57%。但是,即使是 75% 的精确度也已经足够好了,我们决不能在还没有理解模型的情况下就开始应用它。
第一步是理解我们的模型会产生哪些错误类型,哪些错误是我们最不希望出现的。在例子中,误报(false positive)是指将不相关的推文归为灾难,漏报(false negative)是指将真实灾难归为不相关的。如果需要优先响应所有潜在事件,我们需要降低漏报率。如果是资源有限,我们就需要优先降低误报率来减少错误告警。我们可以使用混淆矩阵来可视化这些信息,它能将模型的预测与真实的标签进行比较。理想状态下,矩阵将形成一条贯穿左上角至右下角的对角线(此时预测值和真实值完美匹配)。
混淆矩阵(绿色是高比例,蓝色是低比例)
我们的分类器生成的漏报要比误报多(按比例)。换句话说,我们模型最常见的错误是将灾难事件错误地划分到了不相关的分类。如果误报会造成法律实施上的高昂成本,这对我们的分类器来说可能是个好事。
为了验证我们的模型并解释它的预测,就需要观察它是用哪些词来做决策的。如果我们的数据本身有偏差,分类器能基于样例数据做出准确的预测,但是将模型应用在真实世界中的结果就不会很理想。我们绘制出了灾难类和不相关类中最重要的一些词。用词袋模型和逻辑回归来绘制单词重要性非常简单,我们只需要将模型预测时使用的系数进行提取和排序。
词袋模型:单词重要性
我们的分类器正确地找出了一些特征(hiroshima, massacre),但很显然在一些无意义的词上出现了过拟合(heyoo, x1392)。现在,我们的词袋模型处理着大量的词汇,且每个单词都是被平等对待的。但是,有些词出现的频率非常高,却只会对预测提供噪声。接下来,我们会尝试一种能解释词频的方法来表达句子,来看是否能从数据中获取更多信息。
为了使我们的模型能更多地关注有意义的单词,我们可以在词袋模型上进行 TF-IDF(Term Frequency, Inverse Document Frequency)评分。TF-IDF 根据单词在数据集中的稀有程度打分,会对一些出现太过频繁且只会增加噪声的词进行削减。下图是我们新嵌入的 PCA 投影图:
TF-IDF 嵌入的可视化
上图中我们可以看到,两种颜色有了更清晰的区分。这能使我们的分类器能更容易地区分两个组。让我们看看这样是否会形成更好的结果。用我们新的嵌入训练的另一个逻辑回归模型得到了 76.2% 的精确度。
这是一个非常微小的提升。我们的模型开始注意到更重要的单词了吗?如果我们在保证模型没有“欺骗”行为的情况下得到了更好的结果,那么可以认为这个模型有了提升。
TF-IDF: 单词重要性
被选出的单词看起来更相关了!虽然我们测试集的矩阵只有略微的增长,但是我们对模型选用的词汇有了更多的信心,因此能更放心地将它部署到一些需要和客户交互的系统中。
我们最新的模型设法注意到了高层的信号词。但是如果我们部署了这个模型,很有可能会遇到在训练集中没有出现过的单词。那么之前的模型可能就无法准确地为这些推文分类,即使在训练时已经遇到过类似的词语。
为了解决这个问题,我们需要获取到词语的语义,也就是说我们需要理解“good”和“positive”比“apricot”和“continent”更接近。我们会使用一个叫做 Word2Vec 的工具来帮助我们获取含义。
Word2Vec 是一种用来为单词寻找连续嵌入的技术。它通过读取大量文本并记忆出现在相似上下文中的单词来进行学习。在经过足够的数据训练后,它会为词表中的每个单词生成一个 300 维的向量,具有相似含义的单词会靠的更近。
这篇文章的作者开源了一个在很大的语料库上预先训练过的模型,我们可以利用它来为我们的模型引入一些语义的知识。预先训练的向量可以在这篇文章的资源库中找到。
一种让句子快速嵌入我们分类器的方法是对句子中所有单词取 Word2Vec 分数的平均值。用的是和之前一样的词袋方法,但是这次我们在保留一些语义信息的时候,仅丢失了句子的语法。
Word2Vec 句子嵌入
下面是使用之前所说的技术形成的新嵌入的可视化:
Word2Vec 嵌入可视化
两组颜色区分的更明显了,这次新的嵌入会帮助我们的分类器找到两类之间的分隔。在对同一个模型训练了三次之后(逻辑回归),我们得到了 77.7% 的准确率,这是至今最棒的结果了!是时候检验模型了。
鉴于在之前的模型中,我们的嵌入不是表达成每个单词对应一个一维向量,这就更难判断出哪些单词和我们的分类是最相关的。但是我们仍然可以使用逻辑回归的系数,因为它和我们嵌入的 300 个维度相关而不和单词的索引相关。
对于准确性上如此微弱的提升,丢失所有可解释性似乎是一种很苛刻的权衡。但是在使用更复杂的模型时,我们可以利用 LIME这类黑盒解释器 来查看我们的分类器是怎么工作的。
LIME 可以在 Github 的开源包里获取到。黑盒解释器允许用户利用一个典型的案例来解释任何一个分类器的决定,可以通过扰动输入(在我们的案例中,是从句子中移除单词)来观察预测的变化。
来看一下我们数据集中句子的一组解释。
正确的灾难词汇被分类为“相关的”
这里,单词对分类的影响似乎不明显
但我们没有时间去探索数据集中成千上万的案例。取而代之的是我们会用 LIME 跑在具有代表性的测试样本上来看哪些单词会一直有很强的贡献。使用这种方法,我们就能像之前的模型一样得到单词重要性得分,并验证模型的预测。
Word2Vec: 单词重要性
似乎能找出高度相关词语的模型就意味着能够做出可理解的决策。这些应该是使用之前模型产出的最相关的单词,因此我们能更安心地应用到生产中了。
之前文章的内容涵盖了用于生成紧凑句子嵌入的快速有效的方法。但是,由于忽略了单词的顺序,我们失去了句子中所有语法信息。如果这些方法无法提供足够有效的结果,你可以用一些更复杂的模型,它们会把整句句子作为输入,并在不需要构建中间表达的情况下预测标签。通常的做法是用 Word2Vec 或是一些更新的方法,比如 GloVe 或 CoVe,把句子当做一系列独立的单词向量。我们下面便会这么做。
一个高效的端到端架构(源)
用于句子分类的卷积神经网络(Convolutional Neural Networks,CNN)训练起来非常快,是非常好用的入门级深度学习架构。CNN 广为人知的是它在图像数据上的表现,但是它在处理文本相关的任务时也能提供极好的效果,而且通常会比大多数复杂的 NLP 算法训练的更快(例如 LSTMs 和 Encoder/Decoder 架构)。这个模型保留了单词的顺序,并且从我们可预测单词顺序的目标类中学习有价值的信息。与之前的模型相反,他可以区分出“Alex eats plants”和“Plants eat Alex”。
训练这个模型并不会比之前的方法花费更多功夫(更详细的内容可以参考代码),但效果会比之前的都好,精确度能达到 79.5%!用着上述的模型,我们下一步的操作将是探索和解释用我们描述的方法做出的预测,来证实这的确是能够交付给用户的最好的模型。现在,你可以放心地尝试亲自操作了。
让我们把之前提到过的成功方法快速的过一下:
从一个快速简单的模型开始
解释它的预测
理解它产生的错误
用这些信息来判断你的下一步操作,是否对你的数据起作用,或是需要一个更复杂的模型
这些方法应用到了特殊的案例上,用的是理解和处理像推文一样小段文字的模型,但是这种思考模式可以广泛地应用到其他问题上。希望这篇文章可以对你有所帮助,也希望可以从你那里听到一些建议或是咨询!请在下方尽情地留言,也可以在这里或是 Twitter 上联系 EmmanuelAmeisen。
想看更多这类文章, 请给我们点个赞吧!