R 机器学习示例(三)
原文:
annas-archive.org/md5/099c261ea8cfb1acbd5544b92a953b97译者:飞龙
第七章:社交媒体分析 – 分析 Twitter 数据
“连接”是描述 21 世纪生活的词汇。尽管有许多因素促成了这个术语,但有一个方面发挥了关键作用。那就是网络。网络使距离变得无关紧要,模糊了社会经济界限,它本身就是一个世界,我们都是其中的一部分。特别是网络或互联网在这个数据驱动革命中是一个核心实体。正如我们在前面的章节中看到的,对于大多数现代问题,网络/互联网(以下将互换使用)是数据来源。无论是电子商务平台还是金融领域,互联网每秒都为我们提供大量数据。在这个虚拟世界中,还有另一个数据海洋,它以非常个人化的方式触及我们的生活。社交网络,或社交媒体,是信息巨无霸,也是本章的主题。
在上一章中,我们讨论了金融领域,在那里我们分析了并预测了某家银行客户的信用风险。我们现在转换方向,进入社交媒体领域,看看机器学习和 R 如何使我们能够从这个数据海洋中揭示洞察力。
在本章中,我们将涵盖以下主题:
-
社交网络的数据挖掘具体方法
-
不同数据可视化的重要性和用途
-
如何连接和收集 Twitter 数据的概述
-
利用 Twitter 数据揭示惊人的洞察力
-
看看社交网络如何对数据挖掘过程提出新的挑战
社交网络(Twitter)
我们每天都使用社交网络。有无数社交网络迎合各种意识形态和哲学,但 Facebook 和 Twitter(除少数几个外)已经成为社交网络本身的同义词。这两个社交网络之所以受欢迎,不仅因为它们的独特性和服务质量,还因为它们使我们能够以非常直观的方式互动。正如我们在电子商务网站中使用的推荐引擎(见第四章)所看到的,“建立产品推荐系统”,社交网络在 Facebook、Twitter 甚至互联网出现之前就已经存在。
社交网络对科学家和数学家都产生了兴趣。这是一个跨学科的话题,它跨越但不限于社会学、心理学、生物学、经济学、传播学和信息科学。已经发展出各种理论来分析社交网络及其对人类生活的影响,这些影响以影响经济、人口统计、健康、语言、读写能力、犯罪等因素的形式出现。
早在 19 世纪末进行的研究构成了我们今天所说的社会网络的基础。正如其词本身所表明的,社会网络是节点或实体之间的一种连接/网络,这些节点或实体由人类和影响社会生活的元素所代表。更正式地说,它是一个描绘关系和互动的网络。因此,看到各种图理论和算法被用来理解社会网络并不令人惊讶。在 19 世纪和 20 世纪,这些理论仅限于理论模型和艰苦的社会实验,而 21 世纪的技术为这些理论的测试、微调和建模打开了大门,以帮助理解社会互动的动态。尽管通过某些社会网络(称为社会实验)测试这些理论引起了争议,但这些话题超出了本书的范围。我们将限制自己在算法/数据科学领域,并将争议留给专家讨论。
备注
米尔格拉姆实验,或称为小世界实验,是在 20 世纪 60 年代末进行的,旨在考察美国人的平均路径长度。作为该实验的一部分,随机挑选的人被选为邮件链的起点。这些随机挑选的人被要求将邮件发送给下一个人,以便邮件更接近其目的地(波士顿某地),依此类推。这个著名实验记录的平均跳数是六步。都市传说表明,“六度分隔”这个短语起源于这个实验,尽管米尔格拉姆博士本人从未使用过这个术语!他进行了许多更多的实验;去搜索并惊叹吧。
来源:
www.simplypsychology.org/milgram.html
在我们深入具体内容之前,让我们尝试理解选择 Twitter 作为本章节和下一章节分析点的理由。让我们从了解 Twitter 是什么以及为什么它对终端用户和数据科学家都如此受欢迎开始。
如我们所知,Twitter 是一个社交网络/微博服务,它允许用户发送和接收最多 140 个字符的推文。但使 Twitter 如此受欢迎的是它满足基本的人类本能的方式。我们人类是好奇的生物,有着不断被听到的需求。对我们来说,有一个地方或某个人可以表达我们的观点是很重要的。我们喜欢分享我们的经历、成就、失败和想法。在某种程度上,我们也想知道我们的同龄人在做什么,名人忙于什么,或者新闻上有什么。Twitter 正是解决了这些问题。
在 Twitter 出现之前,就已经存在多个社交网络,Twitter 并没有取代其他服务。在我们看来,是 Twitter 组织信息和用户的方式吸引了人们的注意。其独特的关注关系模型满足了我们对好奇心的渴望,而其简短、免费、高速的通信平台使用户能够发声并被全球听到。通过允许用户关注感兴趣的人或实体,它使我们能够跟上他们的最新动态,而无需其他用户反过来关注我们。关注模型使 Twitter 的关系更倾向于兴趣图谱,而不是像 Facebook 这样的社交网络中通常发现的友谊模式。
Twitter 因其信息(以及谣言)的超级快速传播而闻名并被全球使用。在某些以前无法想象的情况下,它被创新地使用,例如在地震或台风等自然灾害时期寻找人们。它被用来传播信息,范围之广,深度之深,以至于达到了病毒般的规模。不对称的关系和高速度的信息交换有助于使 Twitter 成为一个如此动态的实体。如果我们仔细分析和研究这个社交网络的数据和动态,我们可以揭示许多见解。因此,它是本章的主题。
注意
有趣的链接:
www.technologyreview.com/s/419368/how-twitter-helps-in-a-disaster/
www.citylab.com/tech/2015/04/how-twitter-maps-can-be-useful-during-disasters/391436/
www.tandfonline.com/doi/abs/10.1080/1369118X.2012.696123
www.psmag.com/nature-and-technology/how-to-use-social-media-usefully
让我们用#RMachineLearningByExample!来对推文应用一些数据科学!
数据挖掘 @社交网络
我们已经通过这本书的章节走过了很长的路,理解了各种概念,并学习了一些令人惊叹的算法。我们甚至参与了在我们的日常生活中有应用的项目。简而言之,我们已经在没有明确使用术语的情况下进行了数据挖掘。现在,让我们抓住这个机会,正式定义数据挖掘。
在传统意义上,采矿指的是从地球中提取有用的矿物(如煤矿开采)。将这一概念置于信息时代的大背景下,采矿则指的是从大量数据中提取有用的信息。因此,如果我们仔细观察,知识挖掘或从数据中发现知识(KDD)似乎比“数据挖掘”这个术语更能准确地表达。正如许多关键词一样,简洁明了往往能吸引人的注意。因此,你可能会在很多地方看到“从数据中发现知识”和“数据挖掘”这两个术语被交替使用,这是完全正确的。数据挖掘的过程,类似于采矿,包括以下步骤:
-
数据清洗以去除噪声和不需要的数据
-
数据转换以将数据转换为适合分析的相关形式
-
数据/模式评估以揭示有趣的洞察
-
数据展示以可视化有用的知识形式
注意
数据挖掘并不是使用搜索引擎获取信息,比如关于蛇的信息。相反,它涉及到揭示隐藏的洞察,比如蛇是唯一一种在除南极洲以外的每个大陆都能找到的生物!
如果我们花点时间理解前面的步骤,我们就可以看到我们在所有项目中使用了完全相同的过程。请记住,我们只是将我们在章节中一直遵循的过程进行了形式化和展示,并没有遗漏或修改之前章节中完成的任何步骤。
采矿社交网络数据
现在我们已经正式定义了数据挖掘,并看到了将数据转换为知识所涉及的步骤,让我们专注于社交网络的数据。虽然数据挖掘方法与数据来源无关,但有一些需要注意的事项,这可能导致更好的处理和改进的结果。
就像采矿任何其他类型的数据一样,领域知识对于采矿社交网络数据来说绝对是一个加分项。尽管社交网络分析是一个跨学科的主题(如前所述),但它主要涉及分析与用户或实体及其互动相关的数据。
在前面的章节中,我们看到了来自电子商务平台、银行以及与花卉特征相关的各种数据。我们所看到的数据具有不同的属性和特征。但如果我们仔细观察,这些数据都是某种测量或事件捕获的结果。
进入社交网络的领域,游戏场域略有不同,如果不是完全不同。与我们所看到的不同,社交媒体平台的数据极其动态。当我们说动态时,我们指的是数据点的实际内容,而不是其结构。数据点本身可能(也可能不)是结构化的,但内容本身不是。
让我们具体谈谈包含在推文中的数据。一个样本推文可能看起来像这样:
图片来源:twitter.com/POTUS/status/680464195993911296
如我们所知,推文是一个 140 个字符的消息。由于消息是由用户(通常)生成的,实际消息的长度、语言可能不同,或者可能包含图片、链接、视频等。因此,推文是一个包含用户名(@POTUS)、用户名(奥巴马总统)、消息(来自奥巴马家族...)以及与推文时间(2015 年 12 月 26 日)、点赞数和转发数相关的结构化数据点。推文还可能包含嵌入在消息中的标签、超链接、图片和视频。正如我们将在接下来的章节中看到的,推文除了前面讨论的属性外,还包含大量的元数据(关于数据的数据)。同样,其他社交网络的数据也包含比肉眼所见多得多的信息。
单条推文就能产生如此多的信息,再加上全球范围内每秒有数百万用户疯狂地发推,这产生了大量具有有趣模式的数据,等待被发现。
在其真正意义上,Twitter 的数据(以及社交网络的一般数据)很好地代表了大数据的 3V(体积、种类和速度)。
注意
2013 年 8 月 3 日,日本播出电影《天空之城》期间,每秒产生了 143,199 条推文,这是记录下的一个记录。平均每秒的推文数量通常约为 5700;这个记录是它的 25 倍!更多关于这个记录的信息可以在 Twitter 博客上阅读:blog.twitter.com/2013/new-tweets-per-second-record-and-how
因此,从社交网络中挖掘数据涉及理解数据点的结构,社交网络(如 Twitter 用于快速交换信息,而 LinkedIn 用于专业网络)的潜在哲学或用途,生成数据的速度和数量,以及数据科学家的大脑。
在本章的结尾,我们还将探讨社交网络对传统挖掘方法提出的挑战。
数据和可视化
当数据量每分钟以指数级增长时,数据挖掘活动的结果必须能够使决策者快速识别行动点。结果应该是无噪声/多余信息的,同时足够清晰和完整,以便可以使用。
将信息以最便捷和可用的形式呈现给目标受众(可能是不懂技术的受众),以便他们轻松消费,这是数据挖掘过程中的一个重要方面。到目前为止,在这本书中,我们已经分析了数据,并利用了折线图、条形图、直方图和散点图来揭示和展示洞察。在我们使用本章中的这些以及一些更多的可视化/图表之前,让我们先尝试理解它们的重要性,并明智地使用它们。
在处理数据挖掘作业时,我们通常会如此专注于数据、其复杂性、算法等,以至于我们往往会忽视我们需要使结果易于消费而不是难以阅读的数字和术语表格的部分。除了确保最终报告/文档包含正确和经过验证的数字外,我们还需要确保这些数字以易于最终用户使用的方式呈现。为了使信息/知识易于消费,我们借助不同的可视化。
由于这不是一本关于可视化的书,所以我们有选择性地跳过了通常的折线图、条形图、饼图、直方图和其他细节。在我们接下来使用这些可视化之前,让我们先了解一些非传统但广为人知/使用的可视化。
词云
社交网络以不同的形式和格式生成数据。这些平台上的数据可能被创建、共享、修改、引用或以各种不同的方式使用。为了表示复杂的关系,社交网络数据最广泛使用的可视化之一是标签云或词云。例如,这些平台上的文本、图像、视频和博客等对象通常会被频繁标记。因此,标签云/词云代表了用户生成标签的统计数据。这些标签可能代表单词使用的相对频率或它们在多个对象中的存在。使用不同的字体大小和颜色来区分单词/标签,以表示选择的统计数据(通常是频率)。
展示一组推文中常用词汇的词云
树状图
为了表示高维数据,通常不可能同时可视化所有维度。树状图就是这样一种可视化类型,它将所有维度划分为子集,并以分层的方式呈现。具体来说,树状图将维度划分为一组嵌套的矩形。树状图最常引用的例子之一是新闻地图,它可视化由谷歌新闻聚合的新闻,并以不同颜色显示不同的类别;颜色渐变表示文章的出现(在时间尺度上),而矩形的大小表示新闻条目的流行度。
展示由谷歌新闻聚合的新闻的树状图
图片来源:newsmap.jp/
像素导向地图
可视化不仅使结果更容易理解,而且非常实用。大多数时候,分析过程的结果是多维的。要在二维屏幕/纸张上图形化地表示这些数据是一个挑战。这就是像素导向可视化出现的地方。对于一个 n 维数据集,像素导向可视化将每个 n 维数据点映射到 n 个不同的子窗口中的单个像素。因此,每个数据点被分散在 n 个窗口中,每个窗口对应一个维度。这些帮助我们在一个可视化中映射大量数据。像素导向可视化看起来是这样的:
样本像素导向地图
图片来源:bib.dbvis.de/uploadedFiles/163.pdf
其他可视化
除了已经提到的可视化之外,还有许多其他有趣的可视化,这些可视化在不同的用例中非常有用。例如,箱线图等可视化对于理解数据分布和异常检测非常有用。同样,还有 Chernoff 面孔、散点图、网络图等可视化,它们各有其优点和用例。
请注意,可视化本身就是一个研究领域,本节只是试图触及冰山一角。我们鼓励读者阅读章节“参考文献”部分中分享的书籍/在线内容,以了解更多相关信息。
开始使用 Twitter API
Twitter 对于使用 Twitter 发推文的 tweple(人们)和数据科学家来说都同样令人愉悦。API 和文档都得到了很好的更新,易于使用。让我们从 API 开始吧。
概述
Twitter 拥有最简单但最强大的 API 集合之一,这是任何社交网络都有的。这些 API 已被 Twitter 本身和数据科学家用来理解 Twitter 世界的动态。Twitter API 使用四个不同的对象,即:
-
推文: 推文是定义 Twitter 本身的中心实体。正如前文所述,推文包含的信息(元数据)远不止推文的内容/信息。
-
用户: 任何可以发推文、关注或执行 Twitter 任何操作的任何人或事物都是用户。Twitter 在用户定义上具有独特性,不一定是人类。
@MarsCuriosity就是这样一种非人类流行的 Twitter 账号,拥有超过 200 万粉丝! -
实体: 这些是从推文对象本身提取的结构化信息片段。这可能包括有关 URL、标签、用户提及等信息。这些对象使处理更快,无需解析推文文本。
-
地点:一条推文也可能附有位置信息。这些信息可能用于各种目的,例如显示“您附近的趋势话题”或定向营销。
Twitter API 中的前述对象已在网站dev.twitter.com/上进行了详细解释。我们敦促读者阅读以更好地理解对象和 API。
Twitter 在所有主要编程语言/平台上都有可用的库。我们将使用 TwitteR,即 Twitter 为 R 提供的库。
小贴士
Twitter 最佳实践
Twitter 在其开发者网站dev.twitter.com/上明确指定了一套最佳实践和一系列的“可以做”和“不可以做”的事项,其中讨论了安全性/身份验证、隐私等。由于 Twitter 支持庞大的客户群并具有高可用性,它还跟踪其 API 的使用情况,以保持其系统健康。对 API 查询次数有明确的速率限制。请阅读最佳实践,并成为一个#gooddeveloper!
注册应用程序
现在我们对 Twitter 及其 API 对象有了足够的背景知识,让我们动手实践。开始使用 API 的第一步是通知 Twitter 关于您的应用程序。Twitter 使用标准的开放认证(OAuth)协议来授权第三方应用程序。OAuth 使用应用程序的消费者密钥、消费者密钥、访问令牌和访问令牌密钥,允许它使用连接服务的 API 和数据。
以下快速步骤将为我们设置游戏做好准备:
-
前往 Twitter 的应用管理控制台
apps.twitter.com/,使用您的凭据登录或如果您还没有账户,则创建一个账户。 -
点击创建新应用并填写应用的名称、网站等详细信息。在我们的用途中,我们将命名我们的应用为
TwitterAnalysis_rmre。对于回调 URL,请使用http://127.0.0.1:1410指向您的本地系统。您也可以选择其他端口号。 -
点击创建您的 Twitter 应用程序以完成流程。您的应用程序管理控制台将类似于以下截图:
Twitter 应用程序页面
恭喜,您的应用程序已创建并注册到 Twitter。但在我们能够使用它之前,还有一件事情要做。我们需要创建访问令牌,为此我们需要执行以下步骤。
-
前往 Twitter 应用详情页面上的密钥和访问令牌链接。
-
滚动并点击创建我的访问令牌以为您个人资料生成访问令牌。
-
在完成前述步骤后,密钥和访问令牌页面将类似于以下截图:
应用密钥和访问令牌
我们将使用与下一章相同的同一个应用。请记住消费者密钥、消费者秘密、访问令牌和访问秘密;我们将在我们的应用中需要这些。
注意
为 OAuth 生成的密钥和秘密是敏感信息。它们使您的应用能够访问 Twitter 的数据。请像保管您的密码(甚至更安全)一样保管它们。#安全第一。
连接/认证
现在我们已经在 Twitter 端准备好了所有东西,让我们在 R 端也设置一下。在我们开始处理 Twitter 的数据之前,第一步将是使用我们刚刚创建的应用通过 R 进行连接和认证。
我们将利用 Jeff Gentry 的 R 的 TwitteR 库。这个库或客户端允许我们通过 R 使用 Twitter 的 Web API。我们将使用setup_twitter_oauth()方法使用我们的应用凭证(密钥和访问令牌)连接到 Twitter。请将以下代码中的XXXX替换为您在之前步骤中生成的访问密钥/令牌:
> # load library
> library(twitteR)
> # set credentials
> consumerSecret = "XXXXXXXXXXXXX"
> consumerKey = "XXXXXXXXXXXXXXXXXXXXXXXXXx"
No to it:
这将打开您的浏览器,并要求您使用 Twitter 凭证登录并授权此应用,如下面的截图所示:
授权应用以获取数据
一旦授权,浏览器将重定向到我们在 Twitter 上创建应用时提到的回调 URL。您也可以为用户使用一个更具信息量的 URL。
恭喜,您现在已连接到推文的海洋。
提取样本推文
现在我们已通过 R 连接到 Twitter,是时候提取一些最新的推文并分析我们得到的结果了。为了提取推文,我们将使用 Twitter 账号 001(Twitter 的创始人及第一位用户)Jack Dorsey 的账号@jack。以下代码片段将从他那里提取最新的 300 条推文:
> twitterUser <- getUser("jack")
> # extract jack's tweets
> tweets <- userTimeline(twitterUser, n = 300)
> tweets
由于 Twitter 内容丰富,输出包含文本、不可打印的字符和 URL。我们将在稍后查看推文的元数据,但在那之前,提取的信息看起来如下:
样本推文
要查看可用于分析和操作每条推文的属性和函数,请使用以下getClass方法:
> # get tweet attributes
> tweets[[1]]$getClass()
>
> # get retweets count
> tweets[[1]]$retweetCount
>
> # get favourite count
> tweets[[1]]$favoriteCount
将生成以下输出:
Twitter 数据挖掘
现在我们已经测试了我们的工具、库和与 Twitter API 的连接,是时候开始寻找 Twitter 领域的隐藏宝藏了。让我们戴上数据挖掘者的帽子,开始挖掘吧!
在本节中,我们将处理从搜索关键词(或 Twitter 词汇中的标签)和用户时间线收集的 Twitter 数据。使用这些数据,我们将通过使用 TwitteR 和其他 R 包的不同函数和实用工具来揭示一些有趣的见解。
注意
请注意,我们的过程将隐式遵循数据挖掘中概述的步骤。为了简洁起见,我们可能不会明确提及每个步骤。我们正在挖掘一些“镀金”的见解;请放心,没有任何步骤被遗漏!
每年,我们都带着新的热情去实现伟大的成就,并改进我们的不足。我们大多数人会以新年决心的形式给自己许下承诺。让我们来看看 2016 年用户是如何处理他们的决心的!
注意
注意:Twitter 数据变化非常快,你的结果/图表可能与本章中描述的不同。
我们将使用相同的应用程序及其凭证来连接并获取 Twitter 数据。以下代码与我们在上一节中提取样本推文的方式完全相同:
library(twitteR)
library(ggplot2)
library(stringr)
library(tm)
library(wordcloud)
consumerSecret = "XXXXXXXXX"
consumerKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
setup_twitter_oauth(consumer_key = consumerKey,consumer_secret = consumerSecret)
除了连接到 Twitter,我们还加载了所需的包,如ggplot、stringr、tm和wordcloud。随着我们的进展,我们将看到这些包在哪里以及如何有用。
一旦连接到我们的数据源,我们就可以开始收集所需的数据。由于我们计划了解用户及其新年决心,我们将提取#ResolutionsFor2016标签的数据。我们也可以使用任何标签,例如#NewYearResolutions、#2016Resolutions或标签的组合来获取相关的推文。以下代码不仅提取推文,还将推文/状态对象的列表转换为 R 数据框。我们还把每条推文转换为 UTF-8 格式,以处理不同语言的文本。
注意
惊人事实:Twitter 有 48 种不同的语言,并且还在不断增加!
# trending tweets
trendingTweets = searchTwitter("#ResolutionsFor2016",n=1000)
trendingTweets.df = twListToDF(trendingTweets)
trendingTweets.df$text <- sapply(trendingTweets.df$text,function(x) iconv(x,to='UTF-8'))
正如我们在上一节中看到的,一条推文包含的信息远不止文本本身。众多属性之一是状态源。状态源表示发布推文的设备。它可能是一部手机、平板电脑等等。在我们应用主要转换和清理推文对象之前,我们首先对状态源进行快速转换,将其转换为有意义的格式:
trendingTweets.df$tweetSource = sapply(trendingTweets.df$statusSource,function(sourceSystem) enodeSource(sourceSystem))
上述代码将statusSource从类似<a href=\"http://twitter.com/download/android\" rel=\"nofollow\">Twitter for Android</a>的值转换为简单的“Android”,并将其分配给一个名为tweetSource的新属性。
一旦我们有了数据,数据挖掘过程中的下一步就是清理数据。我们使用文本挖掘包tm来进行转换和清理。特别是Corpus函数帮助我们将推文/状态对象作为文档集合来处理。然后我们使用来自同一包的tm_map实用工具来应用/映射转换,例如将所有文本转换为小写,删除标点符号、数字和停用词。停用词是一系列最常用的词,如 a、an、the 等,在分析文本时可以安全地删除,而不会失去意义。
# transformations
tweetCorpus <- Corpus(VectorSource(trendingTweets.df$text))
tweetCorpus <- tm_map(tweetCorpus, tolower)
tweetCorpus <- tm_map(tweetCorpus, removePunctuation)
tweetCorpus <- tm_map(tweetCorpus, removeNumbers)
# remove URLs
removeURL <- function(x) gsub("http[[:alnum:]]*", "", x)
tweetCorpus <- tm_map(tweetCorpus, removeURL)
# remove stop words
twtrStopWords <- c(stopwords("english"),'resolution','resolutions','resolutionsfor','resolutionsfor2016','2016','new','year','years','newyearresolution')
tweetCorpus <- tm_map(tweetCorpus, removeWords, twtrStopWords)
tweetCorpus <- tm_map(tweetCorpus, PlainTextDocument)
在我们进行下一步分析数据以寻找隐藏的模式/见解之前,最后的转换是一个术语-文档矩阵。正如其名所示,术语-文档矩阵是一种矩阵表示,其中术语作为行,而列则代表文档。矩阵中的每个条目表示一个术语在给定文档中的出现次数。更正式地说,术语-文档矩阵是一种描述文档集中术语频率的矩阵表示。这种表示在自然语言处理应用中非常有用。它是一种优化的数据结构,能够实现快速搜索、主题建模等。以下是一个简单示例,说明了如何使用这种数据结构,其中我们有两个文本文档,TD1和TD2:
样本术语-文档矩阵
tm 包为我们提供了一个易于使用的实用工具,称为术语-文档矩阵(TermDocumentMatrix也是可用的),我们使用它将我们的Corpus对象转换为所需的形式:
# Term Document Matrix
> twtrTermDocMatrix <- TermDocumentMatrix(tweetCorpus, control = list(minWordLength = 1))
常用词汇和关联
因此准备好的术语-文档矩阵包含每个推文(在清理和转换之后)中的词汇作为行,而列则代表推文本身。
作为快速检查,让我们看看在我们的数据集中哪些词汇使用得最频繁。将阈值设置为30次或更多。我们使用 apply 实用工具迭代术语-文档矩阵中的每个术语并计算其出现次数。该函数帮助我们过滤掉出现 30 次或更多的术语。
# Terms occuring in more than 30 times
> which(apply(twtrTermDocMatrix,1,sum)>=30)
结果将如以下截图所示:
在推文中出现 30 次或更多的术语
如前一个截图所示,诸如健康、启发和积极等词汇出现在 30 次或更多出现的词汇列表中。说到年度目标,我们大家有很多共同之处,不是吗?
前面的操作是一个快速检查,看看我们是否真的有可以帮助我们了解新年愿望的推文。现在让我们采取正式的方法,并识别数据集中的频繁术语。我们还将尝试以创新且易于理解的方式呈现信息。为了获取数据集中最频繁的术语,我们再次使用tm包中的findFreqTerms函数。此函数为我们提供了一个比之前使用的快速修复更高级的抽象。findFreqTerms还允许我们设置术语频率的最小和最大阈值。在我们的情况下,我们只提到下限并查看结果:
# print the frequent terms from termdocmatrix
> (frequentTerms<-findFreqTerms(twtrTermDocMatrix,lowfreq = 10))
结果看起来像以下截图:
我们得到大约 107 个术语,最小出现次数为 10。如果你仔细看,我们看到的至少 30 次频率的术语也出现在这个列表中,这是理所当然的。
现在我们确定确实有一些术语/单词的频率超过 10 次,让我们创建一个数据框,并按照我们之前决定的方式绘制术语与其频率的关系图。我们使用rowSums函数计算每个术语/单词的总出现次数。然后我们选择出现次数超过 10 次的术语子集,并使用ggplot进行绘图:
# calculate frequency of each term
term.freq <- rowSums(as.matrix(twtrTermDocMatrix))
# picking only a subset
subsetterm.freq <- subset(term.freq, term.freq >= 10)
# create data frame from subset of terms
frequentTermsSubsetDF <- data.frame(term = names(subsetterm.freq), freq = subsetterm.freq)
# create data frame with all terms
frequentTermsDF <- data.frame(term = names(term.freq), freq = term.freq)
# sort by subset DataFrame frequency
frequentTermsSubsetDF <- frequentTermsSubsetDF[with(frequentTermsSubsetDF, order(-frequentTermsSubsetDF$freq)), ]
# sort by complete DataFrame frequency
frequentTermsDF <- frequentTermsDF[with(frequentTermsDF, order(-frequentTermsDF$freq)), ]
# words by frequency from subset data frame
ggplot(frequentTermsSubsetDF, aes(x = reorder(term,freq), y = freq)) + geom_bar(stat = "identity") +xlab("Terms") + ylab("Frequency") + coord_flip()
以下代码块生成了以下频率图:
分析前面的图表后,我们可以迅速得到一些有趣的点:
-
妈妈、当选、总统和亿万富翁这些词出现在前十位。这个组合很奇怪,但很有趣。关于这一点,我们稍后再详细讨论。
-
健康在列表中排名很高,但并未进入前十。因此,看起来健康是势在必行,但并不是特别突出。健身和饮食也是如此。
-
列表中的大多数单词在本质上都是积极的。例如,快乐、希望、积极、改变等单词都指向了乐观的情绪,在迎接新年决心时!
尽管前面的图表以很好的布局方式为我们提供了很多关于单词及其频率的信息,但它仍然没有展示出完整的画面。记住,我们在生成这个图表之前,故意从数据集中提取了一个子集?我们这样做是有目的的,否则图表会变得过长,频率较低的单词会使得整个图表显得杂乱。这个图表遗漏的另一个点是频率之间的相对差异。
如果我们的目标是看到频率之间的相对差异,我们需要一种不同的可视化方式。这时,词云就派上用场了。使用wordcloud库,我们可以轻松地从数据框中生成词云,只需一行代码:
# wordcloud
> wordcloud(words=frequentTermsDF$term, freq=frequentTermsDF$freq,random.order=FALSE)
使用完整数据集生成的词云看起来大致如下:
前面的词云按照频率递减的顺序显示单词。每个单词的大小强调其频率。你可以尝试使用wordcloud函数生成一些有趣的视觉或艺术作品!
前面的图表中出现了很多单词,但看到亿万富翁这个词出现在前十位,难道不是很有趣吗?这背后的原因是什么?是机器人发出的垃圾邮件,还是某个名人爆红的推文,或者是完全不同的事情?让我们查看这个列表中的顶级推文,看看它是否包含亿万富翁这个词:
# top retweets
> head(subset(trendingTweets.df$text, grepl("trillionaire",trendingTweets.df$text) ),n=1)
以下截图是您会得到的结果:
结果证明我们的猜测是正确的。这是一条名人发布的、迅速走红的新年决心推文。在 Twitter 上快速搜索,我们发现这条推文:
图片来源:twitter.com/mishacollins?lang=en
进一步搜索发现,Misha Collins 是电视剧《超自然力量》中的著名演员。我们还可以看到,上述决议被转发惊人的 5k 次!值得注意的是,点赞数达到 14k,超过了转发数。我们能推断出推友们更喜欢点赞/心形符号而不是转发吗?我们还可以看到,诸如 mom、learn、trillionaire、elected 和 President 等词汇无疑都是最常见的词汇。间接地,我们也可以推断出《超自然力量》在 Twitter 上拥有庞大的粉丝群,而 Castiel(Misha 在电视剧中的角色)是该剧中一个受欢迎的角色。他决定学习钩针,这有点令人惊讶吗?
从超自然的事物转移到健身辩论。健身对我们大多数人来说都很重要。像锻炼或去健身房这样的活动在年初的头几个月/几周会激增。让我们看看 Twitter 上的朋友们有多注重健康!
由于许多词汇如健康、饮食、健身、健身房等都与健康的生活方式相关,让我们尝试找到与“健身”一词本身相关的词汇。findAssocs是一个方便的函数,它可以帮助我们从词-文档矩阵中找到与给定词汇至少有指定程度相关性的词汇。我们将使用该函数的输出结果,使用ggplot准备一个词-关联(相关性)图。这个过程与准备前面的频率图类似:
# Associatons
(fitness.associations <- findAssocs(twtrTermDocMatrix,"fitness",0.25))
fitnessTerm.freq <- rowSums(as.matrix(fitness.associations$fitness))
fitnessDF <- data.frame(term=names(fitnessTerm.freq),freq=fitnessTerm.freq)
fitnessDF <- fitnessDF[with(fitnessDF, order(-fitnessDF$freq)), ]
ggplot(fitnessDF,aes(x=reorder(term,freq),y=freq))
+geom_bar(stat = "identity") +xlab("Terms")
+ ylab("Associations")
+ coord_flip()
与“健康”一词最密切相关的词汇如下:
同样的数据以图形形式更易于阅读,如下所示:
如前图所示,诸如减肥、锻炼、getfit等术语证明了我们的观点,即推友们对健康的关注程度与我们一样。值得注意的是,列表中出现了“yogavideos”这个术语。看起来在 2016 年,瑜伽似乎正在赶上其他保持健康的技术。列表中还有冥想。
流行设备
到目前为止,我们已经处理了推文的可见组件,如文本、转发次数等,并且能够提取许多有趣的见解。让我们拿出我们的精确工具,更深入地挖掘我们的数据。
如上几节所提到的几次,一条推文所包含的信息远比表面所见的多。其中一条信息就是关于推文的来源。Twitter 诞生于短信时代,其许多特征,如 140 个字符的字数限制,都让人联想到那个时代。了解人们如何使用 Twitter,即经常用来访问和发布推文的设备,将会很有趣。尽管世界已经远离了短信时代,但手机无处不在。为了获取这些信息,我们将利用我们的数据框trendingTweets.df中的属性tweetSource。我们是从tweet对象中已经存在的statusSource属性创建了这个附加属性(参见本节开头快速回顾)。
为了清晰起见,我们将使用基于转发次数的trendingTweets.df数据框的子集。我们再次使用ggplot来可视化我们的结果。
# Source by retweet count
trendingTweetsSubset.df <- subset(trendingTweets.df, trendingTweets.df$retweetCount >= 5000 )
ggplot(trendingTweetsSubset.df, aes(x =tweetSource, y =retweetCount/100)) + geom_bar(stat = "identity") +xlab("Source") + ylab("Retweet Count")
下面的图表是您的结果:
毫无疑问,iPhone 是最受欢迎的设备,其次是 Android 和网页。有趣的是,人们使用网页/网站转发推文的次数比 iPad 还要多!Windows Phone 显然在这里有一些严重的问题需要解决。我们也可以推断 iPhone 是 tweeples 的首选设备吗?或者 iPhone 为 Twitter 提供了比其他设备更好的体验?或者我们甚至可以更进一步,说 iPhone 上的 Twitter 比任何其他设备都有一个更容易访问的“转发”按钮。这样的推断还有很多,但所有这些都蕴含着大量的知识/潜力,可以被管理层、用户体验团队等用来改进和改变事物。
层次聚类
我们在之前的章节中已经看到了聚类和分类(参见第二章,让我们帮助机器学习),并揭示了关于手头数据的某些有趣事实。对于我们的当前用例,尽管我们的推文都与 2016 年的决心有关,但我们永远无法确定 tweeples 会做出什么样的决心。这使得层次聚类成为一个非常合适的用例。与需要预先设置集群数量的 k-means 或其他聚类算法不同,层次聚类算法在计算时不依赖于它。
在我们将层次聚类应用于我们的数据之前,让我们抓住这个机会来理解层次聚类。层次聚类,就像任何其他聚类算法一样,帮助我们将相似的项目分组在一起。这个算法的一般细节可以解释如下:
-
初始化:这是第一步,其中每个元素被分配到它自己的集群中。对于一个包含n个元素的集合,算法创建了n个不同的集群,每个集群中有一个元素。在这一步决定了一个距离/相似度度量。
-
合并:在此步骤中,根据选择的距离/相似性度量,识别最近的簇对并将它们合并成一个簇。这一步骤的结果是比迄今为止的总簇数少一个簇。
-
计算/重新计算:我们计算/重新计算在合并步骤中形成的新簇与现有簇之间的距离/相似性。
合并和计算步骤会重复进行,直到我们只剩下一个包含所有n个项目的单个簇。正如其名所示,此算法生成一个层次结构,叶子表示基于相似性/距离结合的个体元素簇,随着我们向树根靠近。输出树通常被称为树状图。
合并步骤是此算法存在变体的地方。有几种方法可以识别最近的簇。从简单的方法,如单链,它考虑两个簇中任何两个元素之间的最短距离作为距离度量,到复杂的方法,如 Ward 的方法,它使用方差来找到最紧凑的簇,有几种方法可以根据用例采用。
回到 Twitter 世界,让我们使用层次聚类来查看哪些术语/推文是最接近的。对于我们的当前用例,我们将使用单一方法作为合并标准。您可以尝试不同的算法并观察差异。
为了执行层次聚类,我们首先处理我们的数据集以去除稀疏术语,以便于清晰。为此,removeSparseTerms函数帮助我们删除具有低于指定限制的稀疏性的数据行。然后我们使用hclust实用程序来形成簇。此实用程序输出的结果可以直接绘制。让我们为此编写一些代码:
# remove sparse terms
twtrTermDocMatrix2 <- removeSparseTerms(twtrTermDocMatrix, sparse = 0.98)
tweet_matrix <- as.matrix(twtrTermDocMatrix2)
# cluster terms
distMatrix <- dist(scale(tweet_matrix))
fit <- hclust(distMatrix,method="single")
plot(fit)
输出的树状图非常简单易懂:
如果您观察右侧第二个簇,它包含术语万亿富翁、当选、妈妈、打电话等等。将这些术语映射回 Mischa Collins 的顶转发推文,所有这些术语都在那条推文中被提及,并且我们的算法正确地将它们聚类在一起。聪明,不是吗?作为一个小练习,观察其他簇并看看这些术语在包含它们的推文中是如何出现的。在这里的一个重要观察是,树状图正确地将所有频繁术语映射到单个根下,这再次证实了所有这些术语都指向我们 2016 年决议的中心主题!
主题建模
到目前为止,我们的分析主要关于来自世界各地的用户有关新年决心的推文。我们已经分析了与我们选择的主题相关的推文。忽略垃圾邮件和其他噪声推文,我们的数据大致符合一个单一的主题。这个主题本身构成了一组单词(如健康、亿万富翁、健身、饮食、妈妈等),这些单词广泛描述了不同的决心。为了拓宽我们的分析范围并发现更多见解,让我们来谈谈主题建模的概念。
主题建模是一个发现未标记文本语料库中模式的过程,它代表了语料库的精髓。一个主题本身可以描述为一组共同出现的单词,用来描述大量文本。
在一次关于主题建模的会议期间提到的另一个定义:
图片来源:twitter.com/footnotesrising/status/264823621799780353
主题建模的目的是自动识别语料库的潜在主题,因此对于需要基于主题进行信息检索的应用程序(但在没有已知关键词的情况下)是有用的。例如,通过使用“一国与另一国的关系”这样的主题而不是搜索关键词然后跟随链接,从报纸档案中了解两个国家之间的关系,这不是很令人惊讶吗?请注意,通过跟随链接来发现信息同样强大,但它还有很多不足之处。
执行主题建模的一种方式是通过潜在狄利克雷分配(LDA);它是功能最强大且应用最广泛的模型之一。
LDA 由 David M Blie 在 2003 年的论文《概率主题模型导论》中提出。正如他的论文所说,LDA 可以被定义为一个生成模型,它允许通过未观察到的组来解释一组观察结果,这些组解释了为什么数据的一些部分是相似的。LDA 基于这样的假设,即文档表现出多个主题。
LDA 是一个概率模型,其数学相当复杂,超出了本书的范围。以非数学的方式,LDA 可以被解释为一个模型/过程,它有助于识别导致一组文档生成的主题。
注意
对于进一步阅读,请参阅 Blei 的论文。
www.cs.princeton.edu/~blei/papers/Blei2011.pdf
一篇用简单语言解释一切的博客:
tedunderwood.com/2012/04/07/topic-modeling-made-just-simple-enough/
对于我们的目的/用例,我们可以假设 LDA 是一个模型/过程,它帮助我们从一个未标记文本的语料库中识别潜在(隐藏/潜在)主题。幸运的是,R 将大部分数学细节以名为topicmodels的库的形式抽象出来。
为了进行主题建模,我们将使用一组新的推文。国际空间站(ISS)有多个 Twitter 账号,其中之一是@ISS_Research,它特别针对来自 ISS 的研究相关推文。让我们通过分析其时间线上的推文来探索@ISS_Research最近在忙些什么。我们将分析这些推文,以识别 ISS 研究背后的主题。为此,我们将使用与之前相同的过程提取推文并进行转换/清理。以下代码片段就是这样做的:
# set user handle
atISS <- getUser("ISS_Research")
# extract iss_research tweets
tweets <- userTimeline(atISS, n = 1000)
tweets.df=twListToDF(tweets)
tweets.df$text <- sapply(tweets.df$text,function(x) iconv(x,to='UTF-8'))
#Document Term Matrix
twtrDTM <- DocumentTermMatrix(twtrCorpus, control = list(minWordLength = 1))
document-term matrix, unlike last time where we prepared a *term-document matrix*.
一旦我们有了所需格式的推文,topicmodels包中的LDA实用程序帮助我们揭示隐藏的主题/模式。LDA 实用程序需要输入主题数量以及文档-术语矩阵。我们现在将尝试八个主题。以下代码使用LDA为八个主题中的每一个提取六个术语:
#topic modeling
# find 8 topics
ldaTopics <- LDA(twtrDTM, k = 8)
#first 6 terms of every topic
ldaTerms <- terms(ldaTopics, 6)
# concatenate terms
(ldaTerms <- apply(ldaTerms, MARGIN = 2, paste, collapse = ", "))
使用 LDA 生成的主题列表如下:
一个视觉表示将更容易理解。我们可以利用qplot快速在面积图上按时间绘制主题,如下所示:
# first topic identified for every tweet
firstTopic <- topics(ldaTopics, 1)
topics <- data.frame(date=as.Date(tweets.df$created), firstTopic)
qplot(date, ..count.., data=topics, geom="density",fill=ldaTerms[firstTopic], position="stack")+scale_fill_grey()
生成的图表看起来如下截图所示:
让我们现在分析输出结果。LDA 生成的每个主题的术语列表似乎给我们提供了对 ISS 上正在进行的工作/研究的一些很好的洞察。诸如火星、微重力、花朵、Cygnus 等术语告诉我们主要的研究领域或至少科学家/宇航员在 ISS 上讨论的主题。诸如 stationcdrkelly 和 astrotimpeake 之类的术语看起来更像是 Twitter 账号。
注意
一个快速练习是使用当前的@ISS_Research时间线数据,挖掘如stationcdrkelly这样的处理,以发现更多信息。谁知道呢,这可能会变成一个很好的宇航员名单来关注!
qplot输出为我们的普通主题列表添加了时间维度。分析时间维度上的主题有助于我们了解特定研究主题何时被讨论,或者何时宣布了令人惊叹的事情。列表中的第二个主题,或者图例顶部的第四个主题包含单词 flower。由于科学家最近在太空中成功培育了一些橙色花朵,上面的图表帮助我们得出结论,新闻最早在 1 月 15 日左右在 Twitter 上发布。快速查看 Twitter/新闻网站确认,新闻是在 2016 年 1 月 18 日通过推文发布的……非常接近!
提示
彩色面积图
尝试从qplot中移除scale_fill_grey()选项,以获得一些比纯灰色更容易阅读的美丽图表。
因此,我们最终学习了使用 LDA 在 ISS 数据上进行的主题建模,并发现了科学家和宇航员在太空中所做的一些令人惊叹的事情。
社会网络数据挖掘的挑战
在我们结束这一章之前,让我们看看社交网络对数据挖掘过程提出的不同挑战。以下是一些论点、问题和挑战:
-
毫无疑问,社交网络生成数据在各个方面都归类为大数据。它具有所有体积、速度和多样性,足以压倒任何系统。然而,有趣的是,如此庞大的数据源所面临的挑战是足够细粒度数据的可用性。如果我们放大我们的数据集,并尝试基于每个用户使用数据,我们会发现没有足够的数据来完成一些最常见的工作,比如做出推荐!
-
如 Twitter 这样的社交网络每秒处理数百万用户创建和分享的大量数据。为了确保他们的系统始终运行,他们会对通过 API 获取的数据量设置限制(安全性也是这些限制背后的一个主要原因)。这些限制使数据科学工作陷入困境,因为很难获得足够的数据样本来正确/完整地代表总体。样本不足可能会导致错误的模式或完全错过模式。
-
社会网络分析中的预处理和结果评估也是一个挑战。在预处理数据时,我们会移除噪声内容。由于数据以各种形状和大小涌入,确定噪声内容比简单地移除停用词更具挑战性。由于大多数情况下没有可用的基准事实,以及由于此处和其它方面的限制,评估结果也是一个挑战,很难有信心确定结果的可靠性。
上文提出的论点/挑战要求数据科学家设计出创新和创造性的方法,这也是他们的工作有趣且极具回报性的原因。
参考文献
一些关于可视化的知名书籍如下:
-
www.amazon.in/Information-Dashboard-Design-At-Glance/dp/1938377001 -
www.amazon.com/Visual-Display-Quantitative-Information/dp/096139210X -
www.amazon.com/Information-Visualization-Second-Interactive-Technologies/dp/1558608192
关于这个主题的一些知名博客如下:
-
Tableau 特定:
www.jewelloree.com/ -
D3:
d3js.org/
摘要
社交网络分析是数据科学领域的一个热门话题。正如我们在本章中看到的,这些平台不仅为我们提供了连接的方式,而且也为我们提供了一个独特的机会来研究全球范围内的人类动态。通过本章,我们学习了一些有趣的技术。我们首先从理解社交网络环境中的数据挖掘开始,接着讨论了可视化的重要性。我们专注于 Twitter,并了解了不同的对象和 API 来操作它们。我们使用了 R 的各种包,如TwitteR和TM,来连接、收集和操作我们的分析数据。我们使用 Twitter 的数据来了解频率分布。最后,我们展示了社交网络词汇和关联、推特用户常用的流行设备、层次聚类甚至触及了主题建模所提出的挑战。我们使用了ggplot2和wordcloud来可视化我们的结果,以及数据挖掘过程。在总结本章时,我们确信你现在可以欣赏到这些平台背后的惊人动态以及 R 分析这些动态的能力。我们还没有结束对@Twitter的分析,请继续关注你的#sentiments!
第八章。Twitter 数据的情感分析
| *"他塑造了公众舆论...使得制定法律和决策成为可能或不可能。" | ||
|---|---|---|
| --亚伯拉罕·林肯 |
人们的想法不仅对政治家和名人很重要,对我们大多数社会人也是如此。这种了解自己观点的需求影响了人们很长时间,并被前面的著名引言恰当地总结。意见的困扰不仅影响我们的观点,还影响我们使用产品和服务的方式。正如在学习市场篮子分析和推荐引擎时讨论的那样(参见第三章,使用市场篮子分析预测客户购物趋势和第四章,构建产品推荐系统分别),我们的行为可以通过观察具有类似特征(如价格敏感度、颜色偏好、品牌忠诚度等)的一组人的行为来近似或预测。我们在前面的章节中也讨论了,长期以来,我们在做出下一个重大购买决策之前,会向我们的朋友和亲戚征求他们的意见。虽然这些意见对我们个人来说很重要,但我们可以从这样的信息中得出更多有价值的见解。
仅仅说万维网的到来只是加速和扩大了我们的社交圈,这还不足以表达其影响。无需重复,值得一提的是,网络为分析人类行为开启了新的途径。
在上一章中,社交网络是讨论的主题。我们不仅使用社交网络作为工具来获取洞察,还讨论了这些平台满足我们天生的好奇心,想知道别人在想什么或做什么。社交网络为我们提供了一个平台,我们可以表达自己的观点并被人听到。其中“被人听到”这一方面定义和操作起来有些棘手。例如,我们对这些平台上某人或某事的观点和反馈(假设它们是真实的)肯定会直接或间接地被我们圈子中的人听到,但它们可能或可能不会被它们旨在影响的人或组织听到。尽管如此,这样的观点或反馈确实会影响与之相关的人及其随后的行为。这种观点的影响以及我们对人们想法的一般好奇心,加上更多这样的用例,正是本章的动机。
在本章中,我们将:
-
了解情感分析及其关键概念
-
探讨情感分析的应用和挑战
-
理解执行意见挖掘的不同方法
-
在 Twitter 数据上应用情感分析的概念
理解情感分析
互联网公司及其首席执行官作为全球经济体中最有利可图的实体之一,这充分说明了世界是如何被技术和互联网所驱动的,以及是如何被塑造的。与其他任何媒介不同,互联网已经无处不在,渗透到我们生活的方方面面。我们使用和依赖互联网以及基于互联网的解决方案来获取建议和推荐,这并不奇怪,除了用于许多其他目的之外。
正如我们在前几章所看到的,互联网与电子商务和金融机构等领域的联系非常深远。但我们对在线世界的使用和信任并不仅限于此。无论是预订你邻里的新餐厅的桌子,还是决定今晚要看哪部电影,我们都会在做出最终决定之前,从互联网上获取他人的意见或分享的内容。正如我们稍后将看到的,这样的决策辅助工具不仅限于商业平台,还适用于许多其他领域。
意见挖掘或情感分析(正如它被广泛且可互换地称呼)是使用自然语言处理、文本分析和计算语言学自动识别文本中主观性的过程。情感分析旨在使用这些技术识别说话者的正面、负面或中性意见、情感或态度。情感分析(以下简称情感挖掘)在从商业到服务领域的全球范围内都有应用。
情感分析的关键概念
现在,我们将探讨与情感分析相关的关键术语和概念。这些术语和概念将帮助我们使接下来的讨论更加规范化。
主观性
意见或情感是个人对观点和信念的表达。此外,主观性(或主观文本)表达了我们对于产品、人物、政府等实体的情感。例如,一个主观句子可能是“我喜欢使用 Twitter”,这表明一个人对某个特定社交网络的喜爱,而一个客观句子则是“Twitter 是一个社交网络”,第二个例子只是陈述了一个事实。情感分析围绕主观文本或主观性分类展开。同样重要的是要理解并非所有主观文本都表达情感。例如,“我刚创建了 Twitter 账户”。
情感极性
一旦我们有一段主观性(并表达某种情感)的文本,接下来的任务就是将其分类为正面或负面情感类别之一(有时也考虑中性)。这项任务还可能涉及将文本的情感放置在连续(或离散)的极性尺度上,从而定义积极程度(或情感极性)。情感极性分类可能根据上下文处理不同的类别。例如,在电影评分系统中,情感极性可能被定义为喜欢与不喜欢,或在辩论中观点可能被分类为支持与反对。
意见总结
从一段文本中提取观点或情感是情感分析过程中的一个重要任务。这通常随后是对情感的总结。为了从与同一主题(例如,某部电影的评论)相关的不同文本中得出见解或结论,将情感汇总(或总结)成可消费的形式以得出结论(电影是票房大作还是失败之作)是很重要的。这可能涉及到使用可视化来推断整体情感。
特征提取
如我们在各章节中看到的,特征识别和提取是使机器学习算法成功或失败的关键。它是在数据本身之后最重要的因素。让我们看看在解决情感分析问题中使用的某些特征集:
-
TF-IDF:信息检索大量使用词频-逆文档频率(tf-idf)来实现快速的信息检索和分析。在 tf-idf 的上下文中,一段文本被表示为一个包含单词作为其组成部分的特征向量。最近的研究也表明,在情感分析的上下文中,与单词的频率相比,单词的存在可以提高性能和准确性。
注意
来源:
Bo Pang, Lillian Lee, 和 Shivakumar Vaithyanathan. Thumbs up? Sentiment classification using machine learning techniques. In Proceedings of the Conference on Empirical Methods in Natural Language Processing (EMNLP), pages 79–86, 2002.
TF-IDF 表示为:
其中,
tf(t,d)是文档d中术语t的词频。idf(t,D)是文档集D中术语t的逆文档频率。例如,我们有以下两个文档及其术语和相应频率的截图:
在其最简单的形式中,术语
Twitter的TF-IDF可以表示为:可以使用不同的权重方案来计算
tfidf;前面的例子使用以 10 为底的对数来计算idf。 -
n-Grams:计算语言学和概率将文本语料库视为连续的术语序列,这些术语可以是音素、字母、单词等。基于 n-gram 的建模技术源于信息理论,其中下一个字符或单词的可能性基于前n个术语。根据n的值,特征向量或模型被称为单语(对于n=1)、双语(对于n=2)、三元语(对于n=3)等等。n-grams 对于处理词汇表外的单词和近似匹配特别有用。例如,考虑一个单词序列,一个像A chapter on sentiment analysis这样的句子会有诸如a chapter、chapter on、on sentiment、sentiment analysis等双语。注意
Google 关于使用 n-grams 的有趣工作:
googleresearch.blogspot.in/2006/08/all-our-n-gram-are-belong-to-you.html。 -
词性(POS):理解和利用语言的基本结构进行分析具有明显的优势。词性是用于创建句子、段落和文档的语言规则。在其最简单的形式中,形容词通常是主观性的良好指标(尽管并非总是如此)。许多方法在分类主观文本时利用了形容词的极性。使用包含形容词的短语已被证明可以进一步提高性能。对使用其他词性(如动词和名词)的研究,以及与形容词一起使用,也显示出积极的结果。
注意
参考文献:
Peter Turney. Thumbs up or thumbs down? Semantic orientation applied to unsupervised classification of reviews. In Proceedings of the Association for Computational Linguistics (ACL), pages 417–424, 2002.
以下示例显示了在样本句子中标记的词性(形容词、名词等),例如,We saw the yellow dog:
-
否定:在情感分析的情况下,否定起着重要的作用。例如,像I like oranges和I don't like oranges这样的句子,它们之间的区别仅在于单词don't,但否定词翻转了句子的极性到相反的类别(分别是正极和负极)。否定可以作为次要特征集使用,其中原始特征向量按原样生成,但后来根据否定词翻转极性。还有其他变体,与不考虑否定影响的方法相比,它们在结果上有所改进。
-
主题特定特征:主题在设置上下文中起着重要作用。由于情感分析涉及说话者的观点,因此主观性受到所讨论主题的影响。在分析主题与文本语料库情感之间的关系方面进行了大量研究。
注意
参考文献:
Tony Mullen 和 Nigel Collier. 使用支持向量机和多种信息源进行情感分析。在自然语言处理实证方法会议(EMNLP)论文集中,第 412–418 页,2004 年 7 月。海报论文。
方法
现在我们已经对情感分析领域的核心概念有了基本的了解,让我们来看看解决这个问题的不同方法。
情感分析主要在以下两个抽象级别上进行:
-
文档级别:在这个抽象级别,任务是分析给定的文档,以确定其整体情感是积极、消极(或在某些情况下是中性的)。基本假设是整个文档表达了对单个实体的意见。例如,给定一个产品评论,系统会分析它以确定评论是积极的还是消极的。
-
句子级别:句子级别分析是情感分析的一种更细粒度的形式。这种粒度级别抵消了这样一个事实,即文档中的所有句子并不都是主观的,因此更好地利用主观性分类来确定每句话的情感。
与其他机器学习技术类似,情感分析也可以使用监督和无监督方法来解决:
-
监督方法:情感分析的研究已经进行了很长时间。虽然早期研究受限于标记数据集的可用性,并且进行了相当浅的分析,但现代监督学习方法在情感分析方面取得了显著进展,无论是在利用这些技术的系统数量上,还是在由于标记数据集的可用性而导致的系统整体性能上。例如,WordNet、SentiWordNet、SenticNet、新闻稿、Epinions 等数据集极大地帮助研究人员通过提供包含极性词汇、分类文档、用户意见等数据集来改进监督算法。朴素贝叶斯、支持向量机(SVM)等算法,如第六章(part0046_split_000.html#1BRPS1-973e731d75c2419489ee73e3a0cf4be8 "第六章. 信用风险检测和预测 – 预测分析")中讨论的,以及基于最大熵的分类算法,是监督学习方法的经典例子。
-
无监督方法(Unsupervised Approach): 无监督情感分析算法通常从构建或学习情感词典开始,然后确定文本输入的极性。词典生成是通过诸如语言启发式、自举等技术完成的。Turney 在他的 2002 年论文中详细描述了一种著名的方法,其中他使用一些基于词性(POS)的固定句法模式进行无监督情感分析。
注意:
参考:
语言启发式(Linguistic heuristics): Vasileios Hatzivassiloglou 和 Kathleen McKeown. 预测形容词的语义方向。在联合 ACL/EACL 会议论文集中,第 174-181 页,1997 年。
自举(Bootstrapping): Ellen Riloff 和 Janyce Wiebe. 学习主观表达式的提取模式。在自然语言处理实证方法会议(EMNLP)论文集中,2003 年。
Turney: Peter Turney. 点赞还是踩?语义方向在无监督分类评论中的应用。在计算语言学协会(ACL)会议论文集中,第 417-424 页,2002 年。
应用:
正如我们一直在讨论的,我们对在线意见的依赖是一种惊喜。在购买产品、下载软件、选择应用程序或选择餐厅之前,我们有意或无意地检查这些意见或受其影响。情感分析或意见挖掘在许多领域都有应用;它们可以概括为以下广泛类别:
-
在线和离线商业(Online and Offline Commerce): 顾客的偏好可以在一瞬间决定品牌的命运。要使产品成为热销商品,包括定价、包装和营销在内的所有方面都必须完美无缺。顾客会对与产品相关的所有方面形成看法,从而影响其销售。这不仅适用于在线商业,顾客在购买前会在多个网站或博客上查看产品评论,而且口碑和其他类似因素也会影响离线商业中的顾客意见。因此,情感分析成为品牌或公司跟踪和分析以保持领先地位的重要因素。对社交媒体内容,如推文、Facebook 帖子、博客等的分析为品牌提供了洞察顾客如何看待其产品的见解。在某些情况下,品牌会推出特定的营销活动来设定关于产品的普遍情绪或炒作。
-
治理(Governance): 在大多数活动都有在线对应物的世界中,政府也不例外。全球各国政府都开展了利用情感分析在政策制定和安全(通过分析和监控任何敌意或负面通信的增加)方面的问题的项目。分析人员还使用情感分析来确定或预测选举的结果。例如,eRuleMaking 等工具将情感分析作为关键组成部分。
除了上述两个类别之外,意见挖掘在推荐引擎和通用预测系统等领域的应用中充当一种增强技术。例如,意见挖掘可以与推荐引擎结合使用,排除那些意见或情感低于某些阈值的产品的推荐列表。情感分析也可能在预测即将上映的电影是否会成为票房炸弹方面找到创新的应用,这基于与明星阵容、制作公司、电影主题等相关联的情感。
挑战
理解他人的观点和/或情感是一个固有的困难任务。能够以算法方式处理这样的问题同样困难。以下是在执行情感分析时面临的一些挑战:
-
理解和建模自然语言结构:情感分析本质上是一个自然语言处理(NLP)问题,尽管是受限的。尽管情感分析是一种受限的自然语言处理形式,涉及对积极、消极或中性的分类,但它仍然面临诸如指称消解、词义消歧和否定处理等问题。近年来,自然语言处理以及情感分析方面的进步在一定程度上帮助解决了这些问题,但在我们能够完美地模拟自然语言的规则之前,还有很长的路要走。
-
讽刺:情感可以通过相当微妙的方式表达。这不仅仅是负面情感;积极的情感也可以在讽刺的句子中巧妙地隐藏。由于理解讽刺是一种只有少数人能够掌握的技巧,因此很难对讽刺进行建模并正确识别情感。例如,评论“这样一个简单易用的产品,你只需要阅读手册中的 300 页”,虽然只包含积极的词汇,但带有一种不易建模的负面味道。
-
评审和评审质量:每个人的观点都不尽相同。我们中的一些人可能会非常强烈地表达自己的观点,而其他人可能不会。另一个问题是,无论是否了解某个主题,每个人都有自己的观点。这导致了评审和评审质量的问题,可能会影响整体分析。例如,一个普通读者可能不是最合适的人选来评审一本新书。同样,让一位新作者的书被评论家评审可能也不太合适。这两种极端情况可能会导致结果有偏见或得出错误的见解。
-
意见数据规模和偏差:网络上有大量的博客和网站为用户提供了一个平台,让他们可以就地球上以及更远的地方的任何可能的事情发表和分享意见。然而,在细粒度层面上,意见数据仍然是一个问题。正如我们在上一章讨论的那样,与特定上下文(比如一个品牌或一个人)相关的数据量非常有限,这影响了整体分析。此外,由于偏见、错误的事实或谣言,可用的数据有时会偏向(或反对)某些实体。
对推文的情感分析
既然我们已经掌握了情感分析领域的核心术语和概念,让我们将我们的理论付诸实践。我们已经看到了情感分析的一些主要应用领域以及一般面临的挑战。在本节中,我们将进行情感分析,分为以下类别:
-
极性分析:这将涉及使用标记的正面和负面词汇列表对情感极性进行评分和汇总。
-
基于分类的分析:在这种方法中,我们将利用 R 丰富的库来执行基于可供公众使用的标记推文的分类。我们还将讨论它们的性能和准确性。
R 有一个非常强大的库,名为TwitteR,用于从 Twitter 中提取和操作信息。正如我们在上一章所看到的,在我们能够使用TwitteR或任何其他用于情感分析的库之前,我们首先需要使用 Twitter 的应用程序管理控制台创建一个应用程序。对于本章,我们将重用上一章中的应用程序(请妥善保管您的应用程序密钥和密码)。此外,在接下来的章节中,我们将以前几章中的代码为基础,以更结构化的格式使用我们的代码,以便重用并遵循#bestCodingPractices。
在我们开始分析之前,让我们首先重构我们现有的代码并编写一些辅助函数,这些函数将在以后派上用场。正如我们所知,可以使用搜索词或从用户的时序中提取 Twitter 的数据。以下两个辅助函数帮助我们以可重用的方式完成相同的任务:
#extract search tweets
extractTweets <- function(searchTerm,tweetCount){
# search term tweets
tweets = searchTwitter(searchTerm,n=tweetCount)
tweets.df = twListToDF(tweets)
tweets.df$text <- sapply(tweets.df$text,function(x) iconv(x,to='UTF-8'))
return(tweets.df)
}
#extract timeline tweets
extractTimelineTweets <- function(username,tweetCount){
# timeline tweets
twitterUser <- getUser(username)
tweets = userTimeline(twitterUser,n=tweetCount)
tweets.df = twListToDF(tweets)
tweets.df$text <- sapply(tweets.df$text,function(x) iconv(x,to='UTF-8'))
return(tweets.df)
}
函数extractTweets接受search词和要提取的推文数量作为输入,并返回一个包含转换为 UTF8 编码的文本的数据框。同样,函数extractTimelineTweets接受用户名和推文数量作为输入,并返回一个包含转换为 UTF8 编码的文本的数据框。因此,前两个函数将帮助我们多次提取推文(基于不同的search词或用户),而无需反复重写相同的代码行。
继续同一主题,我们将编写另一个辅助函数来清理和转换我们的数据集。正如我们在上一章中看到的,R 的 tm 库为我们提供了各种实用函数,可以快速清理和转换文本语料库。在这个函数中,我们将使用 tm_map 来转换我们的推文:
# clean and transform tweets
transformTweets <- function(tweetDF){
tweetCorpus <- Corpus(VectorSource(tweetDF$text))
tweetCorpus <- tm_map(tweetCorpus, tolower)
tweetCorpus <- tm_map(tweetCorpus, removePunctuation)
tweetCorpus <- tm_map(tweetCorpus, removeNumbers)
# remove URLs
removeURL <- function(x) gsub("http://[[:alnum:]]*", "", x)
tweetCorpus <- tm_map(tweetCorpus, removeURL)
# remove stop words
twtrStopWords <- c(stopwords("english"),'rt','http','https')
tweetCorpus <- tm_map(tweetCorpus, removeWords, twtrStopWords)
tweetCorpus <- tm_map(tweetCorpus, PlainTextDocument)
#convert back to dataframe
tweetDataframe <- data.frame(text=unlist(sapply(tweetCorpus,
``, "content")), stringsAsFactors=F)
#split each doc into words
splitText <- function(x) {
word.list = str_split(x, '\\s+')
words = unlist(word.list)
}
# attach list of words to the data frame
tweetDataframe$wordList = sapply(
tweetDataframe$text,
function(text) splitText(text))
return (tweetDataframe)
}
除了常见的转换,例如去除停用词、转换为小写、去除标点符号等,函数 transformTweets 在单词级别对每条推文进行分词,并将每条推文中的单词列表附加到对象上。此外,该函数返回一个数据框中的转换后的推文,以便进行进一步的操作。
极性分析
如在 关键概念 部分所述,极性是对考虑中的文本片段的正、负或中性分类。类标签可能根据上下文(喜欢与不喜欢或有利与不利)而变化。极性也可能附有一个程度,将分析文本放置在极性(或离散)的连续(或离散)尺度上(例如从 -5 到 5)。这种极性程度有助于我们分析文本中积极(或消极)的程度(或程度)。这在比较研究中特别有用,因为我们有机会参考某些基准来查看分析文本。
在本节中,我们将分析推文并根据每条推文中确定的极性词对它们进行评分。简单的易于编码的算法概述如下步骤:
-
根据选定的搜索词或推特用户名提取推文。
-
清理和转换推文,使其适合分析,将推文分词成单词列表。
-
加载用于极性词识别的正面和负面词列表。
-
对于每条推文,计算与前一步骤 3 中获得的正面和负面词列表匹配的正面和负面词的数量。
-
根据前一步中正负匹配的差异,为每条推文分配一个极性分数。
前面的步骤可以用以下图示表示:
![极性分析
一旦数据集中的每条推文都被评分,我们可以汇总这些评分来了解与搜索词或推特用户名相关的整体情感分布。正值定义了积极情感;更大的数字表示更积极的程度,对于消极情感也是如此。中立立场由分数为 0 表示。例如,这辆车速度惊人,非常漂亮 的积极程度高于 这是一辆好车,尽管两者都是积极的句子。
让我们使用这个算法通过搜索词和 Twitter 用户名来分析情感。如前所述,情感挖掘不仅对品牌至关重要,对政府也是如此。每个实体都希望了解其目标受众对其及其倡议的看法,政府也不例外。最近,印度政府有效地利用了 Twitter 和其他社交媒体平台来接触其受众,并让他们了解其倡议和政策。其中一个这样的倡议是最近推出的“印度制造”倡议。考虑这样一个场景,一个人被要求分析此类倡议的有效性和公众意见。为了分析随时间动态变化的公众意见,Twitter 是一个不错的选择。因此,为了分析“印度制造”倡议的情感,让我们分析一些推文。
如前所述,我们首先连接到 Twitter,提取与搜索词印度制造相关的推文。这之后是预处理步骤,其中我们删除停用词、URL 等,将推文转换为可用的格式。我们还对每个推文进行分词,将其分解成构成词列表,用于后续步骤。一旦我们的数据集准备就绪,并以可消费的格式存在,我们就加载预编译的正面和负面词列表。该列表可在www.cs.uic.edu/~liub/FBS/sentiment-analysis.html找到。
我们首先编写一个可重用的analyzeTrendSentiments函数,该函数接受搜索词和要提取的推文数量作为输入。它利用extractTweets和transformTweets函数来完成工作:
analyzeTrendSentiments <- function(search,tweetCount){
#extract tweets
tweetsDF <- extractTweets(search,tweetCount)
# transformations
transformedTweetsDF <- transformTweets(tweetsDF)
#score the words
transformedTweetsDF$sentiScore = sapply(transformedTweetsDF$wordList,function(wordList) scoreTweet(wordList))
transformedTweetsDF$search <- search
return(transformedTweetsDF)
}
然后,我们使用analyzeTrendSentiments函数获取一个包含使用预编译的极性词列表评分的推文的 DataFrame。我们同时使用twitteR、ggplot2、stringr和tm库:
library(twitteR)
library(stringr)
library(tm)
library(ggplot2)
consumerSecret = "XXXXXXXXXX"
consumerKey = "XXXXXXXXXXXXXXXXXXXXXXXXX"
setup_twitter_oauth(consumer_key = consumerKey,consumer_secret = consumerSecret)
# list of positive/negative words from opinion lexicon
pos.words = scan(file= 'positive-words.txt', what='character', comment.char=';')
neg.words = scan(file= 'negative-words.txt', what='character', comment.char=';')
#extract 1500 tweets on the given topic
makeInIndiaSentiments <- analyzeTrendSentiments("makeinindia",1500)
#plot the aggregated scores on a histogram
qplot(makeInIndiaSentiments $sentiScore)
在上一章中,我们学习了使用不同的可视化来掌握分析中隐藏的洞察力。继续同样的思考过程,我们生成一个聚合得分的直方图。可视化看起来是这样的:
直方图易于解释。它在 x 轴上显示了推文在极性尺度上的分布,在 y 轴上显示了推文的频率。结果显示为正态分布,整体倾向于正方向。这似乎表明该倡议得到了其受众的积极反响。
在分析本身深入一些,让我们分析相同搜索词的情感,看看意见是如何随时间变化的。
注意
用于此分析的所有推文都是在该倡议启动当天以及之后一天提取的。由于推特动态性的特点,您可能会观察到结果有所不同。您可能会在本章的其他示例中观察到结果差异。我们敦促您发挥创意,在处理本章的示例时尝试其他热门话题。
输出看起来像这样:
前两个直方图显示了两天内观点的变化。如果你当时在关注新闻,在这个倡议的一个事件中突然发生了火灾,整个舞台被烧毁。顶部的图表是基于火灾发生后发布的推文,而标记为makeinindia_yday的图表则指的是前一天发布的推文。尽管情感的变化并不剧烈,但很明显,变化更多地偏向于正面(一些推文的得分甚至达到了 6+)。这会不会是因为推文作者开始赞扬紧急救援队伍和警察的快速行动,防止了伤亡?嗯,看起来推特不仅仅是人们随意发泄的地方!
注意
世界领袖
推特吸引了名人和政治家的狂热。作为一个快速练习,尝试分析来自世界领袖的推特账号,如@potus、@pmoindia和@number10gov的推文,看看我们的领导人在推特上传达了什么样的观点。如果他们的时间线是中性的,请不要感到惊讶……哦,外交啊!
基于分类的算法
分类问题需要根据每个类别的定义特征对输入数据进行标记,以便将其归类到所需的类别中(有关详细信息,请参阅第二章,让我们帮助机器学习)。在情感分析的情况下,类别是正面和负面(或在某些情况下是中性)。在前几章中,我们已经学习了不同的分类算法,并看到了它们如何被用于各个领域来解决分类和分类问题,而情感分析又是这些算法高度有用的另一个领域。
在本节中,我们将使用 SVM 和提升等分类算法进行观点挖掘。我们还将简要介绍集成方法,看看它们如何帮助提高性能。请注意,对于本节,我们将仅集中讨论正面和负面极性,但这种方法足够通用,可以轻松扩展以包括中性极性。
标记数据集
由于这是一个监督学习方法,我们需要标记数据来训练和测试算法的性能。为了本章的目的,我们将利用来自www.sentiment140.com/的标记数据集。它包含标记为 0、2 和 4 的推文,分别代表负面、中性和正面情感。除了情感标签之外,还有各种属性,如tweet ID、date、search query、username和tweet text。在我们的案例中,我们只考虑推文文本及其相应的标签。
注意
另一个标记推文的来源是github.com/guyz/twitter-sentiment-dataset。这个来源使用 Python 脚本下载大约 5000 条标记推文,同时考虑到 Twitter API 指南。
在我们深入算法的特定细节之前,让我们先查看标记数据集,并执行收集和将我们的数据转换为所需形式的初始步骤。我们将使用caret和RTextTools等库来完成这些步骤。
如前所述,数据集包含标记为 0、2 和 4 的极性,分别代表负面、中性和正面。我们将使用 R 加载csv文件,并快速转换标签为正面和负面。一旦极性被转换为可理解的名字,我们将过滤掉包含中性情感的数据行。此外,我们只保留极性和推文文本的列,并删除其余部分。
# load labeled dataset
labeledDSFilePath = "labeled_tweets.csv"
labeledDataset = read.csv(labeledDSFilePath, header = FALSE)
# transform polarity labels
labeledDataset$V1 = sapply(labeledDataset$V1,
function(x)
if(x==4)
x <- "positive"
else if(x==0)
x<-"negative"
else x<- "none")
#select required columns only
requiredColumns <- c("V1","V6")
# extract only positive/negative labeled tweets
tweets<-as.matrix(labeledDataset[labeledDataset$V1
%in% c("positive","negative")
,requiredColumns])
tweets对象现在作为一个矩阵可用,其中每一行代表一条推文,列则分别指代极性和推文文本。在我们将这个矩阵转换为分类算法所需的格式之前,我们需要将我们的数据分为训练集和测试集(参见第二章,让我们帮助机器学习,了解更多相关信息)。由于训练集和测试集都应该包含足够好的所有类别的样本分布,以便进行训练和测试,我们使用caret包中提供的createDataPartition函数。对于我们的用例,我们将数据分为 70/30 的训练集和测试集:
indexes <- createDataPartition(tweets[,1], p=0.7, list = FALSE)
train.data <- tweets[indexes,]
test.data <- tweets[-indexes,]
我们快速检查了原始数据集中正负类别以及训练集和测试集的数据分布情况,结果将在下面的屏幕截图中展示:
如我们所见,createDataPartition在训练集和测试集之间保持了相似的极性分布。
接下来是文档词矩阵转换。正如我们在第七章, 社交媒体分析 – 分析 Twitter 数据中看到的,文档词矩阵将给定的数据集转换为表示文档的行和表示术语(单词/句子)的列。与上一章不同,我们使用了tm库的DocumentTermMatrix函数进行转换,并使用tm_map应用了各种转换,对于当前的使用案例,我们将使用RTextTools库中的create_matrix函数。这个函数是tm对应函数的抽象。我们还将使用tfidf作为特征为每个术语分配权重。create_matrix方法还帮助我们处理将句子拆分为单词、去除停用词和数字,以及词干提取。以下是操作步骤:
train.dtMatrix <- create_matrix(train.data[,2],
language="english" ,
removeStopwords=TRUE,
removeNumbers=TRUE,
stemWords=TRUE,
weighting = tm::weightTfIdf)
test.dtMatrix <- create_matrix(test.data[,2],
language="english" ,
removeStopwords=TRUE,
removeNumbers=TRUE,
stemWords=TRUE,
weighting = tm::weightTfIdf,
originalMatrix=train.dtMatrix)
test.data.size <- nrow(test.data)
注意
RTextTools v1.4.2中的create_matrix方法有一个小错误,使用originalMatrix选项时阻止了权重分配。以下这个小技巧可以用来修复问题,直到库更新:
> trace("create_matrix",edit=T)
滚动到第 42 行,并将缩写更新为缩写。
查看以下链接以获取更多详细信息以及处理此问题的其他方法:
github.com/timjurka/RTextTools/issues/4
stackoverflow.com/questions/16630627/recreate-same-document-term-matrix-with-new-data
现在我们已经有了训练集和测试集,都是以DocumentTermMatrix格式,我们可以继续进行分类算法,让我们的机器学习并构建情感分类器!
支持向量机
支持向量机,或通常所说的SVM,是分类中最灵活的监督学习算法之一。SVM 以这种方式构建模型,即不同类别的数据点被一个清晰的间隙分开,这个间隙被优化到最大可能的分离距离。边缘上的样本被称为支持向量,它们被一个超平面分开(详见第六章, 信用风险检测与预测 – 预测分析了解更多细节)。
sentiment classifier using the default values and then prints a confusion matrix, along with other statistics for evaluation, as shown in the following code snippet:
svm.model <- svm(train.dtMatrix, as.factor(train.data[,1]))
## view inital model details
summary(svm.model)
## predict and evaluate results
svm.predictions <- predict(svm.model, test.dtMatrix)
true.labels <- as.factor(test.data[,1])
confusionMatrix(data=svm.predictions, reference=true.labels, positive="positive")
如下生成的混淆矩阵显示,分类器的准确率仅为50%,这和掷硬币一样糟糕,完全没有对负面情绪进行预测!看起来分类器无法从训练数据集中推断或学习到很多东西。
为了构建性能更好的模型,我们现在将深入底层并调整一些参数。e1071中的svm实现提供了一个名为tune的出色实用工具,通过在给定的参数范围内进行网格搜索来获得超参数的优化值:
## hyperparameter optimizations
# run grid search
cost.weights <- c(0.1, 10, 100)
gamma.weights <- c(0.01, 0.25, 0.5, 1)
tuning.results <- tune(svm, train.dtMatrix, as.factor(train.data[,1]), kernel="radial",
ranges=list(cost=cost.weights, gamma=gamma.weights))
# view optimization results
print(tuning.results)
# plot results
plot(tuning.results, cex.main=0.6, cex.lab=0.8,xaxs="i", yaxs="i")
注意
radial bias kernel (or rbf for short) for hyperparameter optimization. The motivation for using rbf was due to its better performance with respect to *specificity* and *sensitivity* even though the overall accuracy was comparable to *linear* kernels. We urge our readers to try out linear kernels and observe the difference in the overall results. Please note that, for text classification, linear kernels usually perform better than other kernels, not only in terms of accuracy but in performance as well
参数调整导致超参数cost和gamma的优化值分别为10和0.01;以下图表证实了这一点(最暗的区域对应最佳值)。
以下代码片段使用最佳模型进行预测并准备混淆矩阵,如下所示:
# get best model and evaluate predictions
svm.model.best = tuning.results$best.model
svm.predictions.best <- predict(svm.model.best, test.dtMatrix)
confusionMatrix(data=svm.predictions.best, reference=true.labels, positive="positive")
以下混淆矩阵显示了从改进后的模型中得出的预测。从仅仅 50%的准确率到舒适的 80%以上是一个很大的飞跃。让我们检查此模型的 ROC 曲线,以确认准确率确实足够好:
为了准备 ROC 曲线,我们将重用我们的实用脚本performance_plot_utils.R,来自第六章,信用风险检测和预测 – 预测分析,并将优化模型的预测传递给它:
# plot best model evaluation metric curves
svm.predictions.best <- predict(svm.model.best, test.dtMatrix, decision.values = T)
svm.prediction.values <- attributes(svm.predictions.best)
$decision.values
predictions <- prediction(svm.prediction.values, true.labels)
par(mfrow=c(1,2))
plot.roc.curve(predictions, title.text="SVM ROC Curve")
plot.pr.curve(predictions, title.text="SVM Precision/Recall Curve")
ROC 曲线也证实了一个学习良好的模型,其 AUC 为 0.89。因此,我们可以使用此模型将推文分类为正面或负面类别。我们鼓励读者尝试基于 ROC 的优化,并观察模型是否还有进一步的改进。
集成学习方法
简而言之,监督式机器学习算法是关于学习底层函数或模式,这些函数或模式帮助我们根据历史数据准确预测(在一定的范围内)。在本书的整个过程中,我们遇到了许多这样的算法,尽管 R 使得编码和测试这些算法变得容易,但值得一提的是,学习一个高度准确的功能或模式并不是一件容易的事情。构建高度复杂的模型会导致过拟合和欠拟合等问题。在所有这些混乱中,值得注意的是,学习简单的规则和函数总是很容易的。
例如,为了将一封电子邮件分类为垃圾邮件或非垃圾邮件,机器学习算法需要学习多个规则,例如:
-
包含类似“现在购买”等文本的电子邮件是垃圾邮件
-
包含五个以上超链接的电子邮件是垃圾邮件
-
地址簿中的联系人发送的电子邮件不是垃圾邮件
以及许多类似的规则。给定一个训练数据集,比如说T个标记的电子邮件,机器学习算法(特别是分类算法)将生成一个分类器C,这是一个底层函数或模式的假设。然后我们使用这个分类器C来预测新电子邮件的标签。
另一方面,分类器集成被定义为输出以某种方式组合以对新示例进行分类的分类器集合。在基于机器学习的集成领域的主要发现是,集成比它们所组成的单个分类器表现得更好。
集成优于其组成部分的必要且充分条件是它们应该是准确和多样的。如果一个分类器的预测比随机猜测更好,则称其为准确(参见弱学习者)。而如果两个分类器在相同的数据点上做出不同的错误,则称它们为多样。
我们可以将弱学习者定义为预测和决策至少比随机猜测更好的学习者。弱学习者也被称为基学习器或元学习器。
以下框图可视化了集成分类器的概念:
如前述框图所示,训练数据集被分成 n 个数据集(这种数据集的分割或生成取决于集成方法),弱学习器(相同的或不同的弱学习器,同样取决于集成方法)在这些数据集上建立模型。然后根据加权或无权投票将这些模型组合起来,以准备一个最终模型,该模型用于分类。集成为何有效力的数学证明相当复杂,超出了本书的范围。
提升法
构建集成分类器(或回归器)的方法有很多,提升法就是其中之一。提升法作为罗伯特·沙皮雷在 1990 年发表的开拓性论文《弱学习能力的力量》中的答案出现,这篇论文的标题是《弱学习能力的力量》,他在其中优雅地描述了提升集成,同时回答了凯尔斯和瓦利亚恩在 1989 年发表的论文中提出的问题,该论文讨论了能够创建单个强学习者的多个弱学习者。
注意
《弱学习能力的力量》:www.cs.princeton.edu/~schapire/papers/strengthofweak.pdf。
凯尔斯和瓦利亚恩:关于学习布尔学习和有限自动机的密码学限制:dl.acm.org/citation.cfm?id=73049
提升法的原始算法由弗里德和沙皮雷修订,并命名为AdaBoost或自适应****提升法。这个算法是实际可实现的,并且经验上提高了泛化性能。该算法可以如下数学表示:
来源:www.cs.princeton.edu/picasso/mats/schapire02boosting_schapire.pdf
这里:
-
X 是训练集
-
Y 是标签集
-
Dt 是在第 t 次迭代中对训练示例 i 的权重分布
-
h[t] 是在第 t 次迭代中获得的分类器
-
α 是强度参数或 h[t] 的权重
-
H 是最终的或组合分类器。
简而言之,提升(Boosting)通常从最初对所有训练样本分配相同的权重开始。然后,它在假设空间中迭代,以学习加权样本上的假设 h[t]。在每个这样的假设学习之后,权重以这种方式进行调整,即正确分类的样本的权重减少。这种权重的更新有助于弱学习者在后续迭代中更多地关注错误分类的数据点。最后,将每个学习的假设通过加权投票来得到最终的模型,H。
现在我们已经对集成方法和提升有了概述,让我们使用 R 中的 RTextTools 库提供的提升实现来对推文进行正面或负面分类。
我们将重用为基于 SVM 的分类创建的训练-测试文档项矩阵 train.dtMatrix 和 test.dtMatrix,以及容器对象 train.container 和 test.container。
为了构建基于提升集成的分类器,RTextTools 提供了一个易于使用的实用函数 train_model。它内部使用 LogitBoosting 来构建分类器。我们为构建我们的提升集成使用 500 次迭代。
boosting.model <- train_model(train.container, "BOOSTING"
, maxitboost=500)
boosting.classify <- classify_model(test.container, boosting.model)
然后,我们准备一个混淆矩阵来查看我们的分类器在测试数据集上的表现。
predicted.labels <- boosting.classify[,1]
true.labels <- as.factor(test.data[,1])
confusionMatrix(data = predicted.labels,
reference = true.labels,
positive = "positive")
下面的混淆矩阵显示,我们的基于提升的分类器以 78.5%的准确率工作,考虑到我们没有进行任何性能调整,这是一个相当不错的成绩。将其与 SVM 的初始迭代进行比较,我们得到的准确率仅为 50%多。
如前所述,集成方法(特别是提升)提高了泛化性能,也就是说,它们有助于在不过度拟合训练数据的情况下实现接近 0 的训练错误。为了理解和评估我们的提升分类器在这些参数上,我们将使用一种称为 交叉验证 的模型评估技术。
交叉验证
交叉验证是一种模型评估技术,用于评估模型的泛化性能。它也被称为 旋转估计。与残差方法相比,交叉验证是验证模型泛化性能的更好指标,因为对于传统的验证技术,训练集和测试集的错误(如 均方根误差/RMSE)并不能正确地表示模型的表现。交叉验证可以通过以下方式执行:
-
保留法:最简单的交叉验证技术。数据被分为训练集和测试集。模型在训练集上拟合,然后使用测试集(模型之前未见过)来计算平均绝对测试误差。这个累积误差用于评估模型。这种技术由于依赖于训练-测试划分的方式,因此具有高方差。
-
K 折交叉验证方法:这是对保留法的一种改进。数据集被分为k个子集,然后使用k个子集中的一个作为测试集,其余的k-1个子集作为训练集,重复使用保留法k次。由于每个数据点至少有一次进入测试集,而有k-1次进入训练集,这种方法具有较低的方差。缺点是由于迭代次数较多,需要更多的计算时间。K 折交叉验证的一种极端形式是留一法交叉验证,其中除了一个数据点外,所有数据点都用于训练。这个过程重复N(数据集大小)次。
我们可以使用cross_validate函数轻松地在我们的提升分类器上执行 K 折交叉验证。通常使用 10 折交叉验证:
# Cross validation
N=10
set.seed(42)
cross_validate(train.container,N,"BOOSTING"
, maxitboost=500)
结果表明,分类器已经很好地泛化,整体平均准确率为 97.8%。
提升法是构建基于弱学习者的集成分类器的一种方法。例如,袋装法、贝叶斯最优分类器、桶装法和堆叠法等都是一些具有各自优缺点的变体。
注意
构建集成
RTextTools是一个健壮的库,它提供了train_models和classify_models等函数,通过结合各种基学习器来准备集成。它还提供了生成分析的工具,以非常详细的方式评估此类集成的性能。请参阅journal.r-project.org/archive/2013-1/collingwood-jurka-boydstun-etal.pdf的详细说明。
摘要
Twitter 是数据科学的一个宝库,其中有趣的模式和见解遍布其中。其用户生成内容的持续流动,加上基于兴趣的独特关系,为近距离理解人类动态提供了机会。情感分析就是这样一个领域,Twitter 提供了理解我们如何表达和分享对产品、品牌、人物等观点的合适成分。
在本章中,我们探讨了情感分析的基础、关键术语以及应用领域。我们还探讨了在执行情感分析时遇到的各项挑战。我们研究了各种常用的特征提取方法,如 tf-idf、Ngrams、POS、否定等,用于执行情感分析(或一般文本分析)。我们基于上一章的代码库,对可重用的实用函数进行了简化和结构化。我们使用 Twitter 搜索词进行了极性分析,并看到了公众对某些活动的看法如何被轻松追踪和分析。然后,我们转向监督学习算法进行分类,其中我们使用了 SVM 和 Boosting,利用caret、RTextTools、ROCR、e1071等库构建情感分类器。在结束最后一章之前,我们还简要介绍了高度研究和广泛使用的集成方法领域,以及基于交叉验证的模型评估。
还有许多其他算法和分析技术可以应用于从 Twitter 和其他互联网来源中提取更多有趣的见解。在本章(以及本书)中,我们仅仅尝试触及这个巨大冰山的一角!数据科学不仅仅是将算法应用于解决问题或得出见解。它需要创造性思维和大量的尽职调查,除了领域理解、特征工程和收集数据来尝试解决尚未知的问题之外。
总结一下,思考一下唐纳德·拉姆斯菲尔德的这句话:
"已知已知的事物。这些是我们知道我们知道的。已知未知的事物。也就是说,有些事情我们知道我们不知道。但还有未知未知的事物。有些事情我们不知道我们不知道。"
数据科学是一个学习已知事物和探索未知未知事物的旅程,而机器学习是帮助实现这一目标的强大工具。#KeepMining!