大数据分析实践指南-全-
大数据分析实践指南(全)
原文:
annas-archive.org/md5/622c4ec47faab3ce38900f0c3c942e11
译者:飞龙
前言
本书向读者介绍了与企业中使用的大数据相关的广泛主题。大数据是一个广阔的领域,涵盖了技术、统计学、可视化、商业智能以及许多其他相关学科的元素。为了从那些通常因体积或技术限制而无法访问的数据中获得真正的价值,企业必须在软件和硬件层面上都利用合适的工具。
为此,本书不仅涵盖了大数据的理论和实践方面,还补充了高阶话题,例如大数据在企业中的应用、大数据和数据科学计划,以及关键考虑事项,如资源、硬件/软件堆栈和其他相关主题。这些讨论对于计划实施或升级组织的大数据和/或数据科学平台的 IT 部门将非常有用。
本书重点关注三个主要领域:
1. 大规模数据集上的数据挖掘
大数据在今天无处不在,就像数据仓库这一术语在不久前曾经无处不在一样。行业中有无数的解决方案。特别是,Hadoop 及其生态系统中的产品已经在企业中变得既受欢迎又越来越普遍。此外,像 Apache Spark 这样的最新创新也在企业中占据了永久地位——Hadoop 客户意识到他们可能不需要 Hadoop 框架的复杂性,因此大量转向 Spark。最后,NoSQL 解决方案,如 MongoDB、Redis、Cassandra,以及商业解决方案如 Teradata、Vertica 和 kdb+,已经取代了更传统的数据库系统。
本书将以较深的程度涵盖这些领域。已涉及 Hadoop 及相关产品,如 Hive、HBase、Pig Latin 等。我们还涵盖了 Spark,并解释了 Spark 中的关键概念,如 Actions 和 Transformations。NoSQL 解决方案,如 MongoDB 和 KDB+,也已在一定程度上进行探讨,并提供了实用教程。
2. 机器学习与预测分析
第二个主题是机器学习,也被称为预测分析、统计学习等其他名称。本书提供了详细的解释,并提供了使用 R 和 R 中的机器学习包编写的相应机器学习代码。讨论了如随机森林、支持向量机、神经网络、随机梯度提升、决策树等算法。此外,机器学习中的关键概念,如偏差与方差、正则化、特征选择、数据预处理等也被涵盖。
3. 企业中的数据挖掘
通常,讨论理论主题的书籍很少涉及大数据的高层次内容——例如,成功的大数据项目所需的关键要求。本书包含了来自 IT 高管的调查结果,并突出了行业中普遍存在的共享需求。书中还包括了如何根据从大规模 IT 部门部署生产解决方案中获得的经验,逐步选择合适的应用场景,无论是针对大数据还是机器学习。
我们相信,拥有这三个领域的扎实基础知识,任何从业者都能够成功交付大数据和/或数据科学项目。这也是本书整体结构和内容的主要目的。
本书的读者群
本书面向广泛的读者群体,尤其是那些希望从整体层面理解大数据、数据科学和/或机器学习概念的读者,尤其是它们之间如何相互关联的读者,将从本书中获益最大。
技术读者:对于技术导向的读者,本书包含了关于大数据和机器学习领域的关键行业工具的详细解释。书中涉及了使用 Hadoop 的实践练习,使用 R 编程语言开发机器学习应用场景,使用 R Shiny 构建全面的生产级仪表盘等内容。同时,还包括了 Spark 和 NoSQL 的其他教程。除了这些实践方面的内容,本书还解释了这些关键技术的理论基础。
商业读者:关于大数据的广泛理论和实践内容,已经通过一些高层次的话题得到了补充,重点探讨了在工作场所部署和实施稳健的大数据解决方案的细节。IT 管理、CIO 组织、商业分析和其他负责定义公司数据战略的小组,将发现这些信息非常有用,并且可以直接应用。
本书内容简介
第一章,大数据的温和入门,介绍了大数据和机器学习的基本概念和使用的工具,并概述了大数据分析的相关内容。
第二章,大数据挖掘入门,介绍了企业中的大数据挖掘概念,并提供了企业大数据的软件和硬件架构堆栈介绍。
第三章,分析工具包,讨论了用于大数据和机器学习的各种工具,并提供了逐步指导,介绍了用户如何下载和安装 R、Python、Hadoop 等工具。
第四章,使用 Hadoop 的大数据,探讨了 Hadoop 的基本概念,并深入分析了 Hadoop 生态系统的技术细节。本章解释了 Hadoop 的核心组件,如 Hadoop 分布式文件系统(HDFS)、Hadoop Yarn、Hadoop MapReduce 以及 Hadoop 2 中的概念,如 ResourceManager、NodeManager、Application Master 等。本章还包括了通过 Cloudera Hadoop 发行版(CDH)使用 Hive 的逐步教程。
第五章,使用 NoSQL 进行大数据分析,探讨了各种新兴且独特的数据库解决方案,这些解决方案被广泛称为 NoSQL,它颠覆了传统的关系数据库模型。我们将讨论 NoSQL 的核心概念和技术细节。本节内容涵盖了多种 NoSQL 系统类型,如内存型、列式、基于文档的、键值型、图形型等。此外,还包括了与 MongoDB 及其 MongoDB Compass 界面相关的教程,以及一个关于如何使用 kdb+创建生产级 R Shiny 仪表盘的非常全面的教程。
第六章,大数据分析中的 Spark,介绍了如何使用 Spark 进行大数据分析。涵盖了高层次的概念以及技术性话题。关键概念如 SparkContext、有向无环图、操作与转换等都已被详细介绍。此外,还有一个完整的教程,介绍如何在 Databricks 平台上使用 Spark,用户可以通过该平台利用 Spark。
第七章,机器学习概念简明介绍,讲解了机器学习中的基本概念。此外,还讨论了监督学习与非监督学习、分类、回归、特征工程、数据预处理和交叉验证等核心概念。本章的最后包含了一个关于使用 R 库进行神经网络操作的简短教程。
第八章,机器学习深度探讨,深入研究了机器学习中一些更为复杂的方面。讨论了算法、偏差、方差、正则化以及机器学习中的各种其他概念。本章还包括了诸如随机森林、支持向量机、决策树等算法的解释。本章的结尾提供了一个创建基于 Web 的机器学习应用程序的全面教程。
第九章,企业数据科学,讨论了部署企业级数据科学和大数据解决方案的技术考虑因素。我们还将探讨全球各地企业实施其大数据战略的多种方式,包括基于云的解决方案。本章还提供了关于如何使用 AWS(Amazon Web Services)的逐步教程。
第十章,大数据的总结思考,讨论了企业大数据和数据科学战略,并总结了一些让大数据相关项目成功的建议。
附录 A,大数据进一步阅读,提供了关于大数据更广泛理解的链接。
如何最大化本书的价值
-
一般的 Unix 知识会非常有帮助,虽然并非必需
-
需要一台能连接互联网的计算机,以便下载练习中使用的必要工具和软件
-
假设读者没有相关领域的先验知识
-
所有软件和工具的安装说明已在 第三章,分析工具包 中提供。
下载示例代码文件
你可以从 www.packtpub.com 的账户中下载本书的示例代码文件。如果你是在其他地方购买的本书,你可以访问 www.packtpub.com/support 并注册以直接通过邮件获取文件。
你可以按照以下步骤下载代码文件:
-
在 www.packtpub.com 登录或注册。
-
选择“支持”标签。
-
点击“代码下载与勘误”。
-
在搜索框中输入书名并按照屏幕上的指示操作。
文件下载完成后,请确保使用最新版本的解压缩工具解压或提取文件夹:
-
Windows 上的 WinRAR/7-Zip
-
Mac 上的 Zipeg/iZip/UnRarX
-
Linux 上的 7-Zip/PeaZip
本书的代码包也托管在 GitHub 上,地址是 github.com/PacktPublishing/Practical-Big-Data-Analytics
。我们还提供了来自我们丰富书籍和视频目录的其他代码包,访问 github.com/PacktPublishing/
看看吧!
下载彩色图像
我们还提供了一份 PDF 文件,里面包含了本书中截图/图表的彩色图像。你可以在这里下载: www.packtpub.com/sites/default/files/downloads/PracticalBigDataAnalytics_ColorImages.pdf
。
使用的约定
本书中使用了多种文本约定。
CodeInText
:表示文本中的代码词汇、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 账号。示例:“结果存储在 HDFS 中,路径为/user/cloudera/output
。”
代码块设置如下:
"_id" : ObjectId("597cdbb193acc5c362e7ae97"),
"firstName" : "Nina",
"age" : 53,
"frequentFlyer" : [
"Delta",
"JetBlue",
"Delta"
任何命令行输入或输出都写作如下:
$ cd Downloads/ # cd to the folder where you have downloaded the zip file
粗体:表示一个新术语、重要词汇或屏幕上显示的词汇。例如,菜单或对话框中的词汇会以这种方式出现在文本中。以下是一个示例:“这种额外的开销可以通过使用虚拟机(VMs)轻松缓解。”
警告或重要说明会以这样的方式出现。
提示和技巧以这种方式出现。
联系我们
我们始终欢迎读者的反馈。
一般反馈:通过电子邮件[email protected]
联系我们,并在邮件主题中注明书名。如果您对本书的任何部分有问题,请通过[email protected]
向我们发送邮件。
勘误:尽管我们已尽最大努力确保内容的准确性,但难免会出现错误。如果您在本书中发现错误,请向我们报告。请访问www.packtpub.com/submit-errata,选择您的书籍,点击“勘误提交表单”链接,并输入相关详情。
盗版:如果您在互联网上发现我们作品的任何非法复制品,请提供相关位置地址或网站名称。请通过[email protected]
与我们联系,并附上该材料的链接。
如果您有兴趣成为作者:如果您在某个领域拥有专业知识,并且有兴趣编写或贡献书籍,请访问authors.packtpub.com。
评论
请留下评论。阅读并使用本书后,为什么不在您购买本书的网站上留下评论呢?潜在读者可以通过您的客观意见来做出购买决策,我们 Packt 能够了解您对我们产品的看法,作者们也能看到您对他们书籍的反馈。谢谢!
想了解更多关于 Packt 的信息,请访问packtpub.com。
第一章:太大还是不太大
大数据分析包括与大规模数据集的挖掘、分析和预测建模相关的广泛功能。信息的快速增长和技术发展为全球的个人和企业提供了独特的机会,利用大规模分析重新定义传统的商业模式,从中获取利润并发展新能力。本章旨在提供大数据显著特征的温和概述,为后续章节的深入探讨打下基础。
总的来说,本书将提供大数据分析系统的理论知识和行业实践经验。书中将首先讨论大数据及与大数据相关的平台,如 Hadoop、Spark 和 NoSQL 系统,接着讨论机器学习,其中包括实践和理论主题,最后全面分析大数据的应用,特别是数据科学在行业中的应用。本书将涵盖以下主题:
-
大数据平台:Hadoop 生态系统和 Spark Cassandra 等 NoSQL 数据库 高级平台如 KDB+
-
机器学习:基本算法和概念 在 Python 中使用 R 和 scikit-learn C/C++ 和 Unix 中的高级工具 现实世界中的神经网络机器学习 大数据基础设施
-
企业云架构与 AWS(亚马逊 Web 服务) 本地企业架构 高性能计算用于高级分析 大数据分析与机器学习的商业与企业用例 构建世界级的大数据分析解决方案
为了推进讨论,我们将在本章中澄清以下概念:
-
大数据的定义
-
如果数据一直存在,为什么我们现在要讨论大数据?
-
大数据的简史
-
大数据的类型
-
你应该从哪里开始寻找大数据解决方案?
什么是大数据?
大 这个词是相对的,通常根据不同情况,在规模和应用上有不同的含义。一个简单、虽然有些天真的大数据定义是:大量信息的集合,无论是存储在个人笔记本电脑中的数据,还是需要使用现有或传统工具进行复杂分析的大型企业服务器数据。
今天,行业通常将数据按兆字节(terabytes)、拍字节(petabytes)及更大规模视为大数据。在本章中,我们将讨论导致大数据范式出现的原因及其广泛特征。随后,我们将详细探讨不同的领域。
数据的简史
计算历史是一段迷人的故事,从 19 世纪 30 年代中期的查尔斯·巴贝奇分析机到今天的超级计算机,计算技术推动了全球性的变革。由于篇幅限制,无法涵盖所有领域,但提供了一个关于数据和数据存储的高层次介绍,作为历史背景。
信息时代的黎明
大数据一直存在。美国国会图书馆,世界上最大的图书馆,收藏了 1.64 亿项资料,包括 2400 万本书籍和 1.25 亿件非分类资料[来源: www.loc.gov/about/general-information/
]。
机械数据存储可以追溯到 1880 年由赫尔曼·霍勒里斯发明的打孔卡。霍勒里斯的打孔卡在一定程度上是基于巴西尔·布什霍恩(Basile Bouchon)之前的工作,布什霍恩在 1725 年发明了用于控制织布机的打孔带。霍勒里斯的打孔卡提供了一种执行数据汇总甚至打印的接口。
IBM 率先实现了打孔卡的工业化,并很快成为了存储信息的事实上的首选。
艾伦·图灵博士与现代计算
打孔卡建立了强大的存在感,但仍然缺少一个关键元素——这些机器虽然设计复杂,但不能算作计算设备。一种可以足够通用并能够解决各种问题的正式通用机器尚未被发明。
1936 年,图灵在剑桥大学金斯学院毕业后,发表了一篇开创性的论文《可计算数及其在决策问题中的应用》,在该论文中,他基于库尔特·哥德尔的不完备性定理,形式化了我们今天所称的数字计算的概念。
存储程序计算机的诞生
第一台实现存储程序计算机的设备是曼彻斯特小规模实验机(SSEM),该设备于 1948 年在曼彻斯特大学维多利亚校区开发[来源: en.wikipedia.org/wiki/Manchester_Small-Scale_Experimental_Machine
]。它引入了今天计算机中随机存取存储器(RAM,或更一般地,内存)的概念。在 SSEM 之前,计算机拥有的是固定存储;即所有功能必须预先接入系统中。能够在临时存储设备如 RAM 中动态存储数据意味着机器不再受限于存储设备的容量,而是可以存储任意量的信息。
从磁性设备到固态硬盘
在 1950 年代初,IBM 推出了磁带存储技术,基本上利用磁化的金属带来存储数据。紧接着在 1956 年,硬盘驱动器相继问世,它们使用磁性盘片而非磁带来存储数据。
第一代硬盘的容量不到 4MB,大小相当于大约两个中型冰箱,价格超过 36,000 美元——比今天的硬盘贵了 300 万倍。随后,磁化表面成为了二级存储的标准,并且至今,类似的技术已被应用于各种可移动设备,如 90 年代末的软盘、光盘和 DVD。
固态硬盘(SSD),即硬盘的继任者,最早是由 IBM 在 1950 年代中期发明的。与硬盘不同,SSD 通过非易失性存储器来存储数据,使用带电的硅基底来存储数据。由于没有机械活动部件,SSD 中存储的数据的检索时间(寻址时间)比硬盘等设备快了一个数量级。
如果数据一直存在,为什么我们现在才谈论大数据?
到了 2000 年代初,计算机和存储等技术的快速发展,使得用户能够以前所未有的效率收集和存储数据。互联网进一步推动了这一趋势,它提供了一个平台,具有全球范围内交换信息的无限能力。技术以惊人的速度发展,带来了由社交媒体、智能手机等联网设备、宽带连接的普及以及用户参与等推动的重大范式转变,甚至在遥远的地方也不例外。
总的来说,这些数据中的大多数是由基于网络的来源生成的信息,例如社交网络(如 Facebook)和视频分享网站(如 YouTube)。在大数据术语中,这也被称为非结构化数据;即不是以固定格式(如电子表格)存储的数据,或者不能轻松存储在传统数据库系统中的数据。
计算能力的同步进步意味着,尽管数据生成的速度非常高,但分析这些数据在计算上仍是可行的。曾经由于数据量和算法复杂性使得无法处理的机器学习算法,现在可以使用各种新的范式(如集群或多节点处理)以更简单的方式进行分析,这在过去需要特定用途的机器才能完成。
每分钟生成的数据图表。来源:DOMO Inc.
大数据的定义
总的来说,所生成的数据量已被称为大数据,而涵盖从基本数据挖掘到先进机器学习的分析被称为大数据分析。由于量化何为“足够大”以符合大数据分析标准具有相对性,因此没有确切的定义。从一般意义上讲,对大规模数据集(从几十 GB 到 PB 级别)进行分析,可以称之为大数据分析。这可以简单到找出大数据集中的行数,也可以复杂到在其上应用机器学习算法。
大数据分析的构建模块
从根本上讲,大数据系统可以认为有四个主要层次,每一层都是不可或缺的。许多这样的层次已在各种教科书和文献中列出,因此可能存在一定的模糊性。然而,在高层次上,这里定义的层次既直观又简洁。
大数据分析层次
这些层次可以分解如下:
-
硬件:提供计算支撑的服务器、存储数据的存储设备以及不同服务器组件之间的网络连接,构成了硬件堆栈的定义元素。从本质上讲,提供计算和存储能力的系统,以及支持这些设备互操作性的系统,构成了构建模块的基础层。
-
软件:促进对硬件层托管数据集进行分析的软件资源,如 Hadoop 和 NoSQL 系统,代表了大数据堆栈的下一层。分析软件可以被分类为多个子类别。其中,分析软件的两个主要高层次分类是:
-
数据挖掘:提供聚合、数据集之间的连接以及大数据集上的透视表功能的软件属于这一类。标准的 NoSQL 平台,如 Cassandra、Redis 等,都是用于大数据分析的高级数据挖掘工具。
-
统计分析:提供超越简单数据挖掘的分析能力的平台,例如运行算法,从简单回归到先进的神经网络(如谷歌 TensorFlow 或 R),都属于这一类。
-
-
数据管理:数据加密、治理、访问、合规性以及其他对任何企业和生产环境至关重要的特性,构成了下一个基本层次。尽管它们不像硬件或软件那样具体,但数据管理工具提供了一个明确的框架,借此组织可以履行诸如安全性和合规性等义务。
-
最终用户:分析软件的最终用户构成了大数据分析项目的最后一个方面。毕竟,一个数据平台的价值只有在能够高效利用并解决业务特定用例时才能体现出来。这就是分析平台使用者在从中获取价值时的角色所在。数据科学家这一术语通常用于指代那些实现大数据分析功能的人员,而业务用户则从更快的访问速度和传统系统无法提供的分析能力中受益。
大数据类型
数据可以广泛分类为结构化、非结构化或半结构化。虽然这些分类一直存在,但随着大数据的兴起,数据分类变得更加显著。
结构化
结构化数据,顾名思义,是指具有明确组织结构的数据集,例如 Microsoft Excel 或 CSV 文件。用数据库术语来说,数据应当能通过模式表示。举个例子,以下表格展示的是联合国在其 2017 年《世界幸福报告》排名中公布的五个最幸福的国家,这是一个典型的结构化数据表示。
我们可以清楚地定义各列的数据类型——排名、分数、人均 GDP、社会支持、健康寿命预期、信任、慷慨指数和虚无感是数值型列,而国家则用字母表示,或者更具体地说,是字符串。
请参阅以下表格以获得更多清晰的信息:
排名 | 国家 | 分数 | 人均 GDP | 社会支持 | 健康寿命预期 | 慷慨指数 | 信任 | 虚无感 |
---|---|---|---|---|---|---|---|---|
1 | 挪威 | 7.537 | 1.616 | 1.534 | 0.797 | 0.362 | 0.316 | 2.277 |
2 | 丹麦 | 7.522 | 1.482 | 1.551 | 0.793 | 0.355 | 0.401 | 2.314 |
3 | 冰岛 | 7.504 | 1.481 | 1.611 | 0.834 | 0.476 | 0.154 | 2.323 |
4 | 瑞士 | 7.494 | 1.565 | 1.517 | 0.858 | 0.291 | 0.367 | 2.277 |
5 | 芬兰 | 7.469 | 1.444 | 1.54 | 0.809 | 0.245 | 0.383 | 2.43 |
《世界幸福报告》,2017 [来源: en.wikipedia.org/wiki/World_Happiness_Report#cite_note-4
]
商业数据库如 Teradata、Greenplum 以及开源领域的 Redis、Cassandra 和 Hive,都是能够管理和查询结构化数据的技术示例。
非结构化
非结构化数据包括任何没有预定义组织结构的数据集,如前一节中的表格。口语、音乐、视频,甚至书籍,包括这本书,都被视为非结构化数据。这并不意味着内容没有组织。事实上,书籍有目录、章节、小节和索引——从这个角度来看,它是有明确组织的。
然而,将每个单词和句子表示为一套严格的规则是没有意义的。一个句子可以包含单词、数字、标点符号等,并且不像电子表格那样具有预定义的数据类型。要让它是结构化的,这本书中的每个句子必须具有一组精确的特征,这既不合理也不切实际。
来自社交媒体的数据,例如 Twitter 上的帖子、Facebook 上的朋友消息和 Instagram 上的照片,都是非结构化数据的例子。
非结构化数据可以以多种格式存储。它们可以是二进制大对象(Blobs),或者在文本数据的情况下,是存储在数据存储介质中的自由格式文本。对于文本数据,通常使用像 Lucene/Solr、Elasticsearch 等技术来进行查询、索引和其他操作。
半结构化
半结构化数据是指既包含组织模式元素,又包含任意部分的数据。个人电话簿(如今越来越少见!)中包含姓名、地址、电话号码和备注等栏目,可以看作是半结构化数据集。用户可能不了解所有人的地址,因此某些条目可能只有电话号码,反之亦然。
类似地,备注栏可能包含额外的描述性信息(例如传真号码、与该人相关的亲属姓名等)。这是一个任意字段,允许用户添加补充信息。姓名、地址和电话号码栏可以被视为结构化的,因为它们可以以表格格式呈现,而备注部分则是非结构化的,因为它可能包含无法在日记本其他栏位中表示的任意描述性信息。
在计算中,半结构化数据通常通过像 JSON 这样的格式来表示,这种格式既可以封装结构化的数据,也可以包含无模式或任意关联的数据,通常使用键值对。一个更常见的例子可能是电子邮件消息,它既有结构化部分,例如发件人姓名、接收时间等,这些是所有电子邮件消息共有的,也有非结构化部分,即电子邮件的正文或内容。
Mongo 和 CouchDB 等平台通常用于存储和查询半结构化数据集。
大数据的来源
现今的技术使我们能够以惊人的速度收集数据——无论是数据量还是数据种类。虽然有很多数据来源,但在大数据的背景下,主要来源如下:
-
社交网络:可以说,我们今天所知的所有大数据的主要来源是过去 5-10 年间蓬勃发展的社交网络。这主要是非结构化数据,通过全球用户在网上的互动每秒产生数百万的社交媒体发布和其他数据。全球范围内对互联网的访问增加对社交网络数据增长的自我实现起到了推动作用。
-
媒体:主要是社交网络增长的结果,媒体代表每天发生的数百万甚至数十亿的音频和视觉上传。在 YouTube 上传的视频、在 SoundCloud 上的音乐录音以及在 Instagram 上发布的图片都是媒体的典型例子,其数量继续以不受限制的方式增长。
-
数据仓库:公司长期投资于专门的数据存储设施,通常被称为数据仓库。DW 基本上是公司希望保留和目录化的历史数据集合,以便轻松检索,无论是供内部使用还是监管目的。随着行业逐渐向存储数据的平台(如 Hadoop 和 NoSQL)转移,越来越多的公司将数据从现有的数据仓库转移到一些更新的技术平台上。公司的电子邮件、会计记录、数据库和内部文件等都是现在被转移到 Hadoop 或类似平台上的 DW 数据的例子,这些平台利用多个节点提供高可用性和容错性平台。
-
传感器:大数据领域最近的一个现象是从传感器设备收集数据。虽然传感器一直存在,例如石油和天然气等行业几十年来一直在使用钻井传感器测量油井的数据,但可穿戴设备的出现(也称为物联网设备,如 Fitbit 和 Apple Watch)意味着现在每个个体都可以以与 10 年前几个油井相同的速度流式传输数据。
可穿戴设备可以在任何时间点收集个人的数百种测量数据。虽然还不是一个真正的大数据问题,但随着行业的发展,传感器相关的数据很可能会变得更类似于通过社交网络活动生成的那种即时数据。
大数据的 4V
在大数据的背景下,4V 的话题已经被过度使用,开始失去一些最初的魅力。尽管如此,为了在谈话中继续保持背景上下文的意识,了解这些 V 的含义仍然是有帮助的。
总体而言,4V 表示以下内容:
-
容量:正在生成的数据量
-
多样性:不同类型的数据,例如文本、媒体和传感器或流数据
-
速度:数据生成的速度,例如在社交网络上任何时间点可能交换的数百万条信息。
-
真实性:这是最近加入 3Vs 的一项指标,表示数据中固有的噪声,比如记录信息中的不一致性,这需要额外的验证。
你怎么知道自己遇到了大数据问题?又该从哪里开始寻找大数据解决方案?
最后,大数据分析是指将数据付诸实践的过程——换句话说,就是通过使用合适的技术,从大量数据中提取有用信息的过程。许多用于描述不同类型分析的术语并没有明确的定义,因为它们可以有不同的解释,因此意义是主观的。
然而,这里提供了一些参考或起点,帮助你形成初步印象:
-
数据挖掘:数据挖掘是指通过执行查询或基本汇总方法(如聚合)从数据集中提取信息的过程。例如,从包含一百万个产品销售记录的数据集中找出销量前十的产品,这就是数据挖掘的过程——从数据集中提取有用的信息。Cassandra、Redis 和 MongoDB 等 NoSQL 数据库是具备强大数据挖掘能力的工具的典型代表。
-
商业智能:商业智能指的是像 Tableau、Spotfire、QlikView 等工具,它们提供前端仪表板,帮助用户通过图形化界面查询数据。随着数据的增长,仪表板产品的突出地位逐渐上升,因为用户希望提取信息。具有查询和可视化功能的易用界面,既适用于技术用户,也适用于非技术用户,为数据分析的普及奠定了基础。
-
可视化:数据可以通过易于理解的可视化图形,简明直观地表达结果。可视化在更好地理解数据方面起到了至关重要的作用,特别是在进行更深入的分析之前,用于分析数据集的性质及其分布。JavaScript 的发展,尤其是在经历了长时间的沉寂后重新兴起,如 D3.js 和百度的 ECharts,都是开源领域中可视化工具的代表例子。大多数商业智能工具都具备先进的可视化能力,因此,它已成为任何成功分析产品中不可或缺的资产。
-
统计分析:统计分析指的是允许终端用户对数据集进行统计操作的工具或平台。这些工具虽然存在多年,但随着大数据的兴起以及大量数据在执行高效统计操作时所带来的挑战,逐渐得到了更多关注。像 R 语言和 SAS 产品就是计算统计领域中常见的工具的典型代表。
-
机器学习:机器学习,通常被称为预测分析、预测建模等,实质上是应用超越传统统计学范畴的高级算法的过程。这些算法不可避免地涉及数百次或数千次迭代。此类算法不仅本质复杂,而且计算密集度极高。
技术的进步是推动机器学习在分析领域增长的关键因素,甚至到了如今已成为行业内广泛使用的术语。诸如自动驾驶汽车、基于交通模式调整的地图交通数据,以及 Siri 和 Cortana 等数字助手,都是机器学习在物理产品中的商业化例子。
总结
大数据无疑是一个庞大的主题,乍一看可能显得过于复杂。实践成就完美,研究大数据亦是如此——你参与得越多,相关的主题和术语就越熟悉,主题也就越容易掌握。
对大数据分析各个维度的深入研究将帮助你培养对这一主题的直觉感知。本书旨在提供该主题的整体概述,并将涵盖广泛的领域,如 Hadoop、Spark、NoSQL 数据库,以及基于硬件设计和云基础设施的主题。在下一章中,我们将介绍大数据挖掘的概念,并讨论大数据技术的技术要素和选择标准。
第二章:大数据挖掘面向大众
在企业环境中实施大数据挖掘平台,以满足特定的业务需求并非易事。虽然构建大数据平台相对简单,但由于工具的新颖性,如何让习惯于传统数据挖掘方法的业务用户采用这些工具,成为了一个挑战。最终,这决定了平台在组织内的成功程度。
本章介绍了与大数据分析相关的一些关键特征,适用于分析工具的实践者和最终用户。这将包括以下主题:
-
什么是大数据挖掘?
-
企业中的大数据挖掘:
-
构建使用案例
-
解决方案的利益相关者
-
实施生命周期
-
-
大数据挖掘中的关键技术:
-
选择硬件栈:
-
单节点/多节点架构
-
基于云的环境
-
-
选择软件栈:
-
Hadoop、Spark 和 NoSQL
-
基于云的环境
-
-
什么是大数据挖掘?
大数据挖掘是大数据分析的两大广泛类别之一,另一个是预测分析,我们将在后续章节中讨论。简单来说,大数据挖掘指的是处理大规模数据集的整个生命周期,从数据获取到相应工具的实施与分析。
接下来的几章将说明任何大型数据项目在组织中实施时的一些高级特征。
企业中的大数据挖掘
在中型到大型企业中实施大数据解决方案可能是一项具有挑战性的任务,因为需要考虑的因素极为动态且多样,其中最重要的就是确定该解决方案将解决哪些具体的业务目标。
为大数据战略构建案例
大数据挖掘最重要的方面之一是确定平台将要解决的适当使用案例和需求。任何大数据平台的成功在很大程度上取决于能否找到业务部门中相关的问题,从而为部门或组织创造可衡量的价值。用于收集大量传感器或流数据的解决方案的硬件和软件栈,与用于分析大量内部数据的栈,实际上是截然不同的。
以下是一些建议的步骤,根据我的经验,这些步骤在构建和实施企业大数据战略时特别有效:
-
谁需要大数据挖掘:确定哪些业务部门将从大数据挖掘解决方案中获得最大的收益是这一过程的第一步。通常来说,这些部门是那些已经在处理大规模数据集、对业务至关重要且直接影响收入的部门,并且通过优化数据访问或分析信息所需的时间来提高流程效率,将对日常工作产生重要影响。
例如,在制药组织中,这可能包括商业研究、流行病学、卫生经济学和结果。在金融服务组织中,这可能包括算法交易台、定量研究,甚至是后台办公。
-
确定用例:在前一步中确定的部门可能已经有一个平台,能够满足该组的需求。对多个用例和部门(或其集合)进行优先排序,要求对各业务组的工作有个人熟悉度。
大多数组织遵循等级结构,在这种结构中,业务同事之间的互动主要沿职级线进行。确定有影响力的分析用例需要从实践者和利益相关者之间的密切合作;也就是说,既包括负责一个部门的管理人员,也包括执行实际分析的工作人员。业务利益相关者可以阐明其业务的哪些方面最能从更高效的数据挖掘和分析环境中获益。实践者则提供有关操作层面现有挑战的见解。整合操作性和管理性方面的增量改进,以确定最佳结果,必定能更快、更好地交付成果。
-
利益相关者的支持:利益相关者的支持——换句话说,即决策者和能做出独立预算决策的人员之间的共识——应在开始处理用例之前建立。通常,应为冗余目的确保多个支持,以便拥有一组主要和次要来源,可以为将任何早期成功案例扩展到更广泛目标提供适当的支持和资金。支持过程不必是确定性的,并且在大多数情况下这可能是不可能的。相反,关于某个用例将带来价值的一般共识,有助于建立一个可以在成功执行用例时加以利用的基准。
-
早期成功案例与努力回报比:一旦确定了适当的用例,寻找那些具有最佳努力回报比的用例就变得至关重要。一个相对较小的用例,可以在较短的时间内用较小的预算实施,以优化一个特定的、业务至关重要的功能,有助于展示早期成功案例,从而为大数据解决方案增加可信度。我们无法精确量化这些无形特性,但我们可以假设:
在这种情况下,努力是实施使用案例所需的时间和工作量。这包括诸如采购与解决方案相关的硬件和/或软件所需的时间,实施解决方案所需的资源或相应的人力小时,以及整体的运营开销等方面。与实施可能涉及公司长时间采购和风险分析的商业解决方案相比,开源工具可能具有更低的入门门槛。同样,跨部门的项目通常需要多个已经参与其他项目的资源的时间,因此比一个由单一部门员工执行的项目需要更长的时间。如果净努力足够低,且不会影响项目质量,则可以并行运行多个任务。
- 利用早期胜利:在早期胜利阶段成功实施一个或多个项目,通常为制定一个更大的大数据分析平台战略奠定了基础,这一战略远远超出了单一部门的需求,并对整个组织产生广泛的影响。因此,早期胜利作为确立大数据价值的第一步,对于可能对其可行性和相关性持怀疑态度的受众而言,至关重要。
实施生命周期
如前所述,实施过程可能包括多个步骤。这些步骤通常是迭代性质的,需要通过试错的方法进行。这将需要相当的毅力和坚持,因为大多数任务都会有不同程度的成功和失败。
实际上,大数据战略将包括多个利益相关者,协作式的方法通常能取得最佳结果。业务赞助商、业务支持和 IT 与分析是三个广泛的利益相关者类别,它们共同构建一个适当的统一解决方案,以满足业务需求,前提是预算和 IT 能力允许。
解决方案的利益相关者
大数据解决方案的利益相关者的具体性质是主观的,可能会根据使用场景和问题领域的不同而有所变化。一般来说,以下可以视为一个高层次的表示:
-
业务赞助商:为项目提供支持和/或资金的个人或部门。在大多数情况下,这个实体也将是解决方案的受益者。
-
实施团队:从实际操作角度实施解决方案的团队。这通常是大多数公司中的 IT 或分析部门,负责平台的设计和部署。
-
IT 采购:大多数组织中的采购部门负责审核解决方案,以评估其竞争定价和从组织角度的可行性。遵守内部 IT 政策以及评估其他方面,如许可成本,是采购部门提供的一些服务,特别是对于商业产品。
-
法律:除非是内部开发的产品,否则所有产品肯定会有相关的使用条款和条件。开源产品可能具有广泛的特性,这些特性定义了使用的许可性和限制性。与 GNU GPL(通用许可协议)相比,像 Apache 2.0、MIT 和 BSD 这样的开源软件许可证通常更加宽松。对于商业解决方案,过程更为复杂,因为它需要分析供应商特定的协议,并且根据许可条款和条件的性质,评估和批准的时间可能会很长。
实施解决方案
解决方案的最终实施是实施小组、业务受益者和辅助部门之间合作的结晶。根据早期成功的章节说明,大多数小型项目从开始到结束的时间可以从 3 到 6 个月不等。较大的项目可能需要几个月到几年的时间才能完成,这些项目通常以敏捷框架的产品管理为标志,在实施和部署期间逐步增加功能。
以下截图帮助我们更好地理解这一概念:
显示工作流的高级图像
这些图片和图标来自:
-
图标由 Freepik 制作(
www.freepik.com
),来源于 www.flaticon.com,并且遵循 CC 3.0 BY 许可 -
图标由 Vectors Market 制作(
www.flaticon.com/authors/vectors-market
),来源于www.flaticon.com,并且遵循 CC 3.0 BY 许可 -
图标由 Prosymbols 制作(
www.flaticon.com/authors/prosymbols
),来源于www.flaticon.com,并且遵循 CC 3.0 BY 许可 -
向量图来自 Vecteezy(
www.vecteezy.com
)
大数据平台的技术元素
到目前为止,我们的讨论主要集中在企业环境中大数据解决方案的设计和部署的高级特性。接下来,我们将把注意力转向这些工作中的技术层面。适时地,我们将在讨论中加入高层次的信息,并结合技术基础。
在技术层面上,主要有两个关键考虑因素:
-
硬件堆栈的选择
-
软件和BI(商业智能)平台的选择
在过去的 2-3 年里,越来越多的公司将其流程迁移到基于云的环境中,作为对内部基础设施的补充解决方案。因此,基于云的部署变得非常普遍,因此添加了关于本地部署与云部署的额外章节。请注意,On-premises可以与In-house、On-site以及其他类似术语互换使用。
你经常会听到On-premise这个术语,它是On-premises的替代说法。正确的术语是On-premises。Premise在《钱伯斯词典》中的定义是前提名词 1(也作 premises)指作为进一步陈述依据的假设为真之事物。而Premises则是用于表示建筑物(等其他事物)的术语,显然它更有意义。
硬件堆栈的选择
硬件的选择通常取决于所选解决方案的类型以及硬件所在的位置。正确的选择取决于几个关键指标,如数据类型(结构化、非结构化或半结构化)、数据大小(千兆字节、太字节或拍字节),以及一定程度上数据更新的频率。最佳选择需要对这些变量进行正式评估,并将在本书后续部分进行讨论。从高层次来看,我们可以推测出三种广泛的硬件架构模型:
-
多节点架构:这通常意味着多个节点(或服务器)互联并基于多节点或分布式计算的原则工作。一个典型的多节点架构示例是 Hadoop,其中多个服务器保持双向通信以协调任务。其他技术,如 NoSQL 数据库 Cassandra 和搜索分析平台 Elasticsearch,也基于多节点计算架构。它们中的大多数利用普通服务器,即企业标准下的相对低端机器,这些机器协同工作以提供大规模的数据挖掘和分析能力。多节点架构适用于托管范围在太字节及以上的数据。
-
单节点架构:单节点指的是在单台服务器上进行计算。这在多节点计算工具出现后相对不常见,但仍然在分布式计算架构中保持巨大优势。分布式计算的谬误概述了一系列与分布式系统实现相关的断言或假设,例如网络的可靠性、延迟成本、带宽以及其他考虑因素。
如果数据集是结构化的,主要包含文本数据,并且大小在 1-5 TB 之间,那么在当今的计算环境中,完全有可能通过使用特定技术在单节点机器上托管这样的数据集,正如后续章节中所展示的那样。
-
基于云的架构:在过去几年中,行业中出现了许多基于云的解决方案。这些解决方案大大降低了进入大数据分析的门槛,通过提供一个平台,使得根据任务需求按需配置硬件资源变得极为简单。这大大减少了采购、管理和维护物理硬件以及将其托管在内部数据中心设施中的重大开销。
像亚马逊 Web 服务、微软的 Azure 和谷歌计算环境这样的云平台允许企业以每小时 1 美分的低成本为每个实例提供从几十到成千上万个节点。
随着云供应商在传统的实体托管设施中占据主导地位,出现了若干补充服务来管理客户的云环境。
例如,包括像 Altiscale 这样的云管理公司,提供大数据即服务解决方案,以及 IBM Cloud Brokerage,帮助选择和管理多种基于云的解决方案。
硬件成本的指数下降:过去几年,硬件成本呈指数下降。以 Statistical Brain 的研究为例,2013 年硬盘存储的成本约为每 GB 4 美分。与 2000 年每 GB 7 美元相比,以及 1980 年代初期每 GB 超过 10 万美元,差距明显。考虑到商业软件许可的高成本,往往超过硬件成本,因此将足够的预算分配用于采购高性能硬件解决方案是明智的选择。软件需要合适的硬件来提供最佳性能,因此,硬件选择与选择合适的软件同样重要。
软件栈的选择
数据挖掘的软件栈选择根据个人情况有所不同。与数据挖掘相关的最流行选项以及一些替代方案(虽然不那么知名,但同样能够管理大规模数据集)一起展示如下:
-
Hadoop 生态系统:大数据术语可以说是在 Hadoop 问世时进入了大众领域。Hadoop 生态系统由多个在 Apache 软件基金会支持下运行的项目组成。Hadoop 支持几乎所有大数据领域中常见的各种类型的数据集——例如结构化、非结构化和半结构化数据。其蓬勃发展的辅助工具生态系统不断添加新功能,以及一个快速发展的市场,各公司争相展示大数据领域的下一个“重大突破”,意味着 Hadoop 将在可预见的未来继续存在。除了大型生态系统中的项目外,Hadoop 的四个主要组件如下:
-
Hadoop Common:支持其他 Hadoop 模块的常用工具
-
Hadoop 分布式文件系统(HDFS™):一个分布式文件系统,提供对应用程序数据的高吞吐量访问
-
Hadoop YARN:用于作业调度和集群资源管理的框架
-
Hadoop MapReduce:一个基于 YARN 的大数据集并行处理系统
-
-
Apache Spark™:Apache Spark 是一个多节点计算框架的项目,最初在加利福尼亚大学伯克利分校的 AMPLab 提出,作为一个提供无缝接口来运行并行计算并克服 Hadoop MapReduce 框架限制的平台。特别地,Spark 内部利用了一个称为DAG—有向无环图—的概念,它表示一种将一组操作优化为更小或更高效的计算操作的功能。此外,Spark 还公开了几个API—应用程序编程接口—供常用语言使用,如 Python(PySpark)和 Scala(本地可用接口)。这消除了进入 Hadoop 领域的一个门槛,即必须掌握 Java 语言。
最后,Spark 引入了一种叫做Resilient Distributed Datasets(RDD)的数据结构,它提供了一种在内存中存储数据的机制,从而显著提高数据检索和随后的处理时间:
-
- 集群管理器:构成 Spark 集群的节点通过集群管理器进行通信,集群管理器负责管理集群中各个节点的总体协调。截止到写作时,集群管理器可以是独立的 Spark 集群管理器、Apache Mesos 或 YARN。此外,还有一个额外的功能是通过 spark-ec2 在 AWS EC2 实例上运行 Spark,spark-ec2 自动设置环境来运行 Spark 程序。
-
- 分布式 存储:Spark 可以访问多种底层分布式存储系统,如 HDFS、S3(AWS 存储)、Cassandra、HBase、Hive、Tachyon 以及任何 Hadoop 数据源。需要注意的是,Spark 可以作为独立产品使用,并不需要 Hadoop 来进行操作。很多 Spark 初学者往往认为 Hadoop,或者更具体地说,是 HDFS 文件系统,必须用于 Spark 操作。事实并非如此。Spark 可以支持多种类型的集群管理器和后端存储系统,正如本节所示。
-
NoSQL 和传统数据库:选择软件栈时的第三个考虑因素是 NoSQL 数据库。NoSQL 这一术语是最近才出现的,旨在区分那些不遵循传统关系型数据库模型的数据库。NoSQL 数据库既有开源版本,也有商业版本,甚至还有基于云的选项,且这些选项变得越来越普遍。NoSQL 数据库有多种广泛的分类,以下是一些常见的范式:
-
键值存储:这些 NoSQL 数据库基于哈希原理存储数据——一个唯一的键用于标识一组关于该键的属性。在这种说法中,键的一个例子可以是个人的身份证号码(例如美国的社会保障号码 SSN 或印度的 Aadhaar)。这可以与个人的姓名、地址、电话号码以及其他变量相关联。数据库的最终用户通过 ID 号码进行查询,直接访问该个人的信息。像 Redis 这样的开源键值数据库和像 Riak 这样的商业数据库非常流行。
-
内存存储:虽然使用内存存储的数据库(例如将缓存存储在内存中,以提供相对于磁盘存储的更快访问速度)一直存在,但随着大数据的兴起,这些数据库被更广泛地采用。在内存中访问数据的速度比从磁盘访问相同的信息快得多(大约 100 纳秒 vs 1-10 毫秒,慢 100,000 倍)。一些 NoSQL 数据库,如 Redis 和 KDB+,利用临时的内存存储来提供对频繁使用数据的更快访问。
-
列式存储:这些数据库通过附加多个数据列而不是行来创建表。列式存储相对于行式存储的主要优势在于,列式布局提供了更快速的数据访问方式,并且减少了 I/O 开销,特别适用于分析场景。通过将数据划分为单独的列,数据库查询可以通过扫描适当的列来检索数据,而不是逐行扫描整个表,并能很好地利用并行处理功能。著名的列式数据库包括 Cassandra、Google BigTable 等。
-
面向文档: 在许多方面,面向文档的数据库被认为是比纯键值存储更先进的技术。面向文档的数据库存储的数据不遵循任何特定的模式,例如新闻文章等非结构化文本。这些数据库提供了一种方式,可以将信息封装在多个键值对中,这些键值对不一定在结构上对所有其他条目保持一致。因此,像 MongoDB 这样的文档数据库在媒体相关的组织中得到了广泛应用,如纽约时报和《福布斯》,以及其他主流公司。
-
-
基于云的解决方案: 最后,像 AWS Redshift、Azure SQL 数据仓库和 Google Bigquery 这样的基于云的大规模数据挖掘解决方案,允许用户直接在云服务提供商的平台上查询数据集,而无需自己构建架构。尽管最终用户可以选择拥有自己的内部专家,如 Redshift 系统管理员,但基础设施的管理、维护和日常例行任务大多由服务提供商负责,从而减少了客户方的运营负担。
摘要
在本章中,我们对大数据以及在企业中实施大数据解决方案的一些组成部分进行了高层次的概述。大数据需要选择一个最优的软件和硬件堆栈,这是一项非平凡的工作,尤其是因为行业中有数百种解决方案可供选择。尽管大数据战略可能被认为是一个管理层讨论的主题,而非技术观众的内容,但理解其中的细微差别仍然至关重要。
请注意,没有一个适当的、明确定义的战略和相应的高层支持,IT 部门将无法充分提供成功的解决方案。此外,解决方案,包括硬件和软件堆栈,应该能够被现有的 IT 资源有效管理和支持。大多数公司会发现,招聘新员工来实施大数据是必不可少的。由于此类实施需要评估各种因素——商业需求、预算、资源和其他变量,因此通常需要几个月到一年甚至更长的时间,具体取决于规模和范围。
这些主题将在后续章节中深入讨论,本节作为该主题的初步介绍。
第三章:分析工具包
目前有多种平台用于大规模数据分析。从广义上讲,这些平台分为两类:一类主要用于数据挖掘,例如使用 NoSQL 平台分析大规模数据集;另一类则用于数据科学——即机器学习和预测分析。通常,解决方案可能具有这两种特点——一个用于存储和管理数据的强大平台,以及建立在其上的解决方案,提供数据科学的附加能力。
在本章中,我们将向您展示如何安装和配置您的分析工具包,这是一组我们将在接下来的章节中使用的软件:
-
分析工具包的组成部分
-
系统推荐
-
在笔记本电脑或工作站上安装
-
在云端安装
-
-
安装 Hadoop
-
Hadoop 发行版
-
Cloudera 发行版 Hadoop (CDH)
-
-
安装 Spark
-
安装 R 和 Python
分析工具包的组成部分
本书将使用几种关键技术,这些技术广泛应用于大数据挖掘和数据科学领域。我们的分析工具包包括 Hadoop 和 Spark,它们可以同时安装在用户的本地机器和云端平台;此外,它还包括 R 和 Python,二者都可以安装在用户的本地机器或云平台上。您的分析工具包将包括:
软件/平台 | 用于数据挖掘 | 用于机器学习 |
---|---|---|
Hadoop | X | |
Spark | X | X |
Redis | X | |
MongoDB | X | |
开源 R | X | X |
Python (Anaconda) | X | X |
Vowpal Wabbit | X | |
LIBSVM, LIBLINEAR | X | |
H2O | X |
系统推荐
如果您在本地机器上安装 Hadoop,建议系统至少具备 4-8 GB 的 RAM(内存),并且至少有 50 GB 的可用磁盘空间。理想情况下,8 GB 或更多内存足以满足大多数应用的需求。低于这个配置,性能会较差,但不会妨碍用户完成本书中的练习。请注意,这些数字是估算值,适用于本书中提到的练习。在生产环境中,要求会更高,相关内容将在后续章节中讨论。
安装分析软件,尤其是像 Hadoop 这样的平台,往往在技术复杂性上具有较高的挑战性,用户通常会遇到各种错误,这些错误需要耗费大量时间解决。用户往往花费比理想中更多的时间来解决错误和修复安装问题。这种额外的开销可以通过使用虚拟机(VMs)或者近期流行的容器技术如 Docker 来轻松减轻。对于像 R 和 Python 这样较简单的平台,我们将使用开源版本,这些版本已经预装了各种库。
在笔记本电脑或工作站上安装
本书中的练习可以在任何 Windows、macOS 或 Linux 机器上执行。用户需要安装 Oracle VirtualBox(可以从 www.virtualbox.org/wiki/Downloads
下载)来开始安装分析工具包所需的软件。
在云端安装
安装软件的替代方案是使用基于云的服务。AWS(Amazon Web Services)和微软的 Azure 等云服务提供了一种非常灵活和多功能的环境,可以按需提供服务器,使用费用从每小时几美分到几美元不等。虽然云端安装超出了本书的范围,但创建一个免费的 AWS 账户并使用它安装本书中讨论的不同分析软件非常简单。请注意,如果你使用 AWS/Azure 或其他云服务,你需要使用 Cloudera Hadoop 发行版的 Docker 版本。
安装 Hadoop
安装 Hadoop 有几种方式。最常见的方式包括:
-
从
hadoop.apache.org
安装 Hadoop -
使用来自商业供应商(如 Cloudera 和 Hortonworks)的开源发行版进行安装
在本练习中,我们将安装 Cloudera 分发版的 Apache Hadoop(CDH),这是一个集成平台,包含多个与 Hadoop 和 Apache 相关的产品。Cloudera 是一个流行的商业 Hadoop 供应商,除了发布自己的 Hadoop 版本外,还为企业级 Hadoop 部署提供托管服务。在我们的案例中,我们将安装 HDP Sandbox 于虚拟机环境中。
安装 Oracle VirtualBox
虚拟机环境本质上是一个现有操作系统的副本,可能已经预装了一些软件。虚拟机可以以单个文件的形式提供,这使得用户通过启动该文件来复制整个机器,而无需重新安装操作系统并配置它以模拟另一个系统。虚拟机在一个自包含的环境中运行;也就是说,它不依赖于宿主操作系统来提供其功能。
要安装 CDH 快速启动虚拟机(Quickstart VM),我们将使用 Oracle VirtualBox,它用于从虚拟机文件启动虚拟机。
在 VirtualBox 中安装 CDH 的步骤:
-
下载适合你系统的 Oracle VirtualBox(Windows、macOS 或 Linux)版本,网址为
www.virtualbox.org/wiki/Downloads
(如果该链接无法访问,请访问www.virtualbox.org/
并选择适当的链接进入 下载 页面)。 -
双击 Oracle VirtualBox 可执行文件并按照屏幕上的提示进行安装(可以接受屏幕上显示的默认设置)。
-
安装完 VirtualBox 后,您还需要安装可从
www.oracle.com/technetwork/server-storage/virtualbox/downloads/index.html#extpack
下载的Oracle VM VirtualBox 扩展包。
下载与您的环境相关的扩展包文件,并点击/双击该文件。这将打开 Oracle VM VirtualBox 应用程序,并在 VirtualBox 环境中安装扩展包。
下载并安装 CDH Quickstart 虚拟机:CDH 的 Quickstart 虚拟机或 Docker 镜像可以从 Cloudera 网站下载。步骤如下:
- 访问
www.cloudera.com
,然后点击页面顶部 下载 菜单中的 Quickstart 虚拟机。如果导航发生了变化,可以通过 Google 搜索 "Cloudera Quickstart VM" 通常会直接进入 Cloudera 下载页面:
Cloudera 首页上的 QuickStart 虚拟机链接
- 这将打开 CDH 下载页面。在选择平台菜单中,选择VirtualBox。填写出现的表单并点击继续。下载的文件将是
.zip
格式。解压文件以提取.ova 或 .ovf
文件:
在 Cloudera Quickstart 下载选项中选择 VirtualBox
我们看到以下登录界面:
CDH 注册界面
首先会显示条款和条件:
CDH 许可证条款接受
CDH 的虚拟机下载开始:
CDH 虚拟机超过 5 GB,下载可能需要一些时间
解压该文件。该文件夹将包含如下图所示的文件:
如果下载文件是 Zip 格式,请解压
下载文件超过 5 GB,下载时间会根据您的互联网连接速度有所不同
- 下载完成后,双击
.ova 或 .ovf
文件,它将会在 Oracle VirtualBox 中打开:
在 VirtualBox 中选择虚拟机选项
您也可以通过启动 Oracle VirtualBox,进入 文件 | 导入设备,然后选择您下载的 .ova/.ovf
文件作为导入的设备,来手动打开文件。
保持所有选项为默认设置,然后点击导入按钮,开始导入过程。在此阶段,Cloudera CDH Quickstart 虚拟机正在加载到您的 Oracle VirtualBox 环境中。
- 文件导入后,您可以通过点击窗口顶部的绿色箭头来启动它:
Oracle VirtualBox 中加载了 CDH 虚拟机
- 当操作系统初始化时,保持默认设置不变:
CDH 主操作系统页面
我们在安装的最后步骤会看到以下屏幕:
CDH 网页用于管理 Hadoop 及其他 CDH 组件
本文结束了使用 Hortonworks 数据平台安装 Hadoop 环境的过程。
在其他环境中安装 CDH
CDH Quickstart 虚拟机也可以通过 VMWare、Docker 和云平台进行安装。相关说明可以在接下来的页面中的链接找到。
安装 Packt 数据科学虚拟机
我们还为书中的某些练习创建了一个独立的虚拟机。
从 gitlab.com/packt_public/vm
下载 Packt 数据科学虚拟机 Vagrant 文件
要加载虚拟机,首先从 www.vagrantup.com/downloads.html
下载 Vagrant。
Vagrant 下载页面
下载完成后,运行已下载的 Vagrant 安装文件来安装 Vagrant。安装完成后,系统会提示你重启计算机。重启你的系统后,继续下一步加载 vagrant 文件:
完成 Vagrant 安装
在最后一步点击确认以重启:
重启系统
在终端或命令提示符下,进入你下载了 Packt 数据科学 Vagrant 文件的目录,并运行以下命令(以 Windows 系统为例):
$ vagrant box add packtdatascience packtdatascience.box ==> box: Box file was not detected as metadata. Adding it directly...
==> box: Adding box 'packtdatascience' (v0) for provider:
box: Unpacking necessary files from: file://C:/Users/packt/Downloads/packt_public_vm/packtdatascience.box
box: Progress: 100% (Rate: 435M/s, Estimated time remaining: --:--:--)
==> box: Successfully added box 'packtdatascience' (v0) for 'virtualbox'! $ vagrant box list packtdatascience (virtualbox, 0)
C:UsersNataraj DasguptaDownloadspackt_public_vm>vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'packtdatascience'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: packt_public_vm_default_1513453154192_57570
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
default: Adapter 1: nat
==> default: Forwarding ports...
default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
...
如果一切顺利,你应该在 Oracle VirtualBox 中看到一个新的条目:
配合 Packt 数据科学虚拟机使用的 Oracle VirtualBox
双击该条目的名称以启动(并测试)它。使用 packt/packt
作为用户名和密码登录:
Packt 虚拟机的登录界面
安装 Spark
CDH Quickstart 虚拟机包括 Spark 作为其中的一个组件,因此无需单独安装 Spark。我们将在专门讨论 Spark 的章节中详细说明。
此外,我们的 Spark 教程将使用 Databricks Community Edition,可以通过 community.cloud.databricks.com/
访问。关于创建帐户和执行必要步骤的说明已在 第六章中提供,大数据分析中的 Spark。
安装 R
R 是一种统计语言,在过去 3-5 年中变得极为流行,尤其是作为一个可以用于各种用途的平台,从简单的数据挖掘到复杂的机器学习算法。根据 2016 年中期发布的 IEEE Spectrum 文章,R 排在全球前十大语言中的第五位。
开源的 R 可以通过 www.r-project.org
下载,位于 cran.r-project.org/mirrors.html
的 CRAN 站点提供下载。
或者,您可以从 mran.microsoft.com/rro/
的 Microsoft R Open 页面下载 R。此前称为 Revolution R Open,是由 Revolution Analytics 发布的增强版开源 R。微软在 2015 年收购 Revolution Analytics 后,进行了重新品牌化。
Microsoft R Open 包含了所有 R 的功能,还包括以下内容:
-
默认安装了许多 R 包,以及由微软公司发布的一组专业包,这些包补充了开源 R 的现有功能
-
用于 R 的多线程数学库进行多线程计算
-
名为 MRAN 的固定 CRAN 软件库。CRAN 是 Comprehensive R Archive Network 的集合站点,其中包含 R 二进制文件、软件包及相关内容。CRAN 上的软件包可以持续更新。MRAN 对 CRAN 进行固定快照,保持不变直至下一个发布,从而实现可重现性和一致性。
下载和安装 Microsoft R Open 的步骤
我们按照以下步骤进行:
- 前往
mran.microsoft.com
,并点击 现在下载 按钮:
Microsoft Open R 主页
- 选择适合您系统的分发版(Windows、macOS 或 Linux):
Microsoft Open R 版本
-
下载完成后,双击下载的文件安装 Microsoft R Open。
-
请注意,在 macOS 上,您可能会收到以下错误消息:
绕过 OS X 的消息(使用下面的方法绕过)
- 如果出现这种情况,请右键单击下载的文件,并从弹出的菜单中选择“打开”。这将允许您手动打开文件并进行安装:
绕过前面显示的 OS X 消息
- 安装后,双击 Microsoft R Open 启动应用程序:
Microsoft R 控制台
安装 RStudio
RStudio 是由 rstudio.org 发布的应用程序,提供功能强大且功能丰富的图形化 IDE(集成开发环境)。
以下是安装 RStudio 的步骤:
R Studio 版本
- 点击适用于您操作系统的链接,下载并安装相应的文件:
下载 RStudio
- 请注意,在 macOS 上,你只需将下载的文件移动到应用程序文件夹即可。在 Windows 和 Linux 操作系统上,双击下载的文件以完成安装步骤:
在 Mac 上安装 RStudio(复制到应用程序文件夹)
安装 Python
我们按以下步骤继续安装:
-
与 R 类似,Python 因其多功能和丰富的包而广受欢迎。Python 通常作为大多数现代基于 Linux 的操作系统的一部分提供。对于我们的练习,我们将使用来自 Continuum Analytics® 的 Anaconda,它通过安装许多与数据挖掘和机器学习相关的包来增强基础的开源 Python 功能,这些包作为平台的一部分本地安装。这避免了用户手动下载和安装包的麻烦。从这个意义上讲,它在概念上与 Microsoft R Open 类似。正如 Microsoft R 在基础开源 R 功能的基础上增加了额外的功能,Anaconda 也对基础开源 Python 进行了改进,提供了新的功能。
-
安装 Anaconda Python 的步骤
Python Anaconda 首页
- 下载适合你系统的发行版。请注意,我们将下载 Python v2.7(而不是 3.x 版本):
选择 Python Anaconda 安装程序
- 安装完成后,你应该能够打开终端窗口(或 Windows 系统中的命令窗口),然后输入 Python,Anaconda 就会启动:
在控制台启动 Python Anaconda
这就完成了安装 Hadoop(CDH)、Spark、R 和 Python 的过程。在后续章节中,我们将进一步探讨这些平台的详细内容。
总结
本章介绍了一些用于数据科学的关键工具,特别是展示了如何下载和安装 Cloudera Hadoop(CDH)、Spark、R、RStudio 和 Python 的虚拟机。尽管用户可以下载 Hadoop 的源代码并将其安装在例如 Unix 系统上,但这通常会遇到一些问题,并需要大量的调试工作。相比之下,使用虚拟机可以让用户以最小的努力开始使用和学习 Hadoop,因为它是一个完整的预配置环境。
此外,R 和 Python 是机器学习和一般分析中最常用的两种语言。它们可以在所有流行的操作系统上使用。尽管它们可以安装在虚拟机中,但如果可行,建议用户尝试将它们安装在本地计算机(笔记本/工作站)上,因为这样性能通常会更高。
在下一章中,我们将深入探讨 Hadoop 及其核心组件和概念的细节。
第四章:使用 Hadoop 处理大数据
Hadoop 已成为大数据领域的事实标准,尤其是在过去三到四年里。Hadoop 最初是 Apache Nutch 的一个子项目,诞生于 2006 年,介绍了与分布式文件系统和分布式计算(也称为 MapReduce)相关的两个关键特性,这些特性在开源社区中迅速获得了广泛的关注。如今,已经有成千上万的新产品基于 Hadoop 的核心特性开发,并且它已演变成一个庞大的生态系统,包含了 150 多个相关的主要产品。可以说,Hadoop 是启动大数据和分析行业的主要催化剂之一。
在本章中,我们将讨论 Hadoop 的背景及核心概念,Hadoop 平台的组件,并深入探讨 Hadoop 生态系统中的主要产品。我们将了解分布式文件系统和分布式处理的核心概念,以及提高 Hadoop 部署性能的优化方法。最后,我们将通过使用Cloudera 分发版 Hadoop(CDH)进行实际操作练习。我们将涵盖的主题包括:
-
Hadoop 基础知识
-
Hadoop 的核心组件
-
Hadoop 1 和 Hadoop 2
-
Hadoop 分布式文件系统
-
基于 MapReduce 的分布式计算原理
-
Hadoop 生态系统
-
Hadoop 生态系统概述
-
Hive、HBase 等
-
Hadoop 企业级部署
-
内部部署
-
云端部署
-
使用 Cloudera Hadoop 进行实践
-
使用 HDFS
-
使用 Hive
-
使用 MapReduce 进行 WordCount
Hadoop 基础知识
2006 年,Hadoop 的创始人 Doug Cutting 当时在 Yahoo! 工作。他积极参与了一个开源项目,名为 Nutch,该项目涉及开发一个大规模的网页爬虫。网页爬虫从高层次上讲,本质上是可以在互联网上自动浏览并索引网页的软件。直观地说,这涉及到对大量数据的高效管理和计算。在 2006 年 1 月底,Doug 正式宣布了 Hadoop 的开始。请求的第一行,仍然可以在互联网上找到,地址为 issues.apache.org/jira/browse/INFRA-700,
,内容为 Lucene PMC 已投票决定将 Nutch 的一部分拆分为一个新的子项目,命名为 Hadoop。于是,Hadoop 应运而生。
起初,Hadoop 有两个核心组件:Hadoop 分布式文件系统(HDFS)和 MapReduce。这是 Hadoop 的第一个版本,现在也被称为 Hadoop 1。后来,在 2012 年,加入了第三个组件,称为 YARN(Yet Another Resource Negotiator),它将资源管理和作业调度过程解耦。在我们更详细地探讨核心组件之前,了解 Hadoop 的基本前提将有所帮助:
Doug Cutting 在issues.apache.org/jira/browse/NUTCH-193
的帖子中宣布了他将Nutch 分布式文件系统(NDFS)和 MapReduce 分离到一个名为 Hadoop 的新子项目中的计划。
Hadoop 的基本前提
Hadoop 的基本前提是,与其尝试在单一大型机器上执行任务,不如将任务划分为更小的部分,然后将这些部分分配给多个较小的机器。这些所谓的较小机器将会在其各自的数据部分上执行任务。一旦这些较小的机器完成了各自分配的任务,产生的结果就会被汇总,最终得到总结果。
尽管从理论上讲,这看起来相对简单,但有许多技术考虑因素需要牢记。例如:
-
网络是否足够快,以便从每个单独的服务器收集结果?
-
每个单独的服务器是否能从磁盘读取数据足够快?
-
如果一个或多个服务器发生故障,我们是否需要从头开始?
-
如果有多个大型任务,它们应该如何排序优先级?
在处理这种分布式架构时,还必须考虑更多此类技术因素。
Hadoop 的核心模块
Hadoop 的核心模块包括:
-
Hadoop Common:Hadoop 所需的库和其他常用辅助工具
-
HDFS:一个分布式、高可用、容错的文件系统,用于存储数据
-
Hadoop MapReduce:一种涉及在普通服务器(或节点)上进行分布式计算的编程范式
-
Hadoop YARN:一个作业调度和资源管理框架
在这些核心组件中,YARN 于 2012 年引入,以解决 Hadoop 第一次发布时的一些不足。Hadoop 的第一个版本(或者说是 Hadoop 的第一个模型)使用 HDFS 和 MapReduce 作为其主要组件。随着 Hadoop 的流行,使用超出 MapReduce 提供的功能的需求变得越来越重要。这以及其他一些技术因素促使了 YARN 的开发。
现在让我们来看看之前列出的 Hadoop 的显著特点。
Hadoop 分布式文件系统 - HDFS
HDFS 构成了所有 Hadoop 安装的基础。文件,或者更一般地说,数据,是存储在 HDFS 中并由 Hadoop 的节点访问的。
HDFS 执行两个主要功能:
-
命名空间:提供包含集群元数据的命名空间,即 Hadoop 集群中数据的位置
-
数据存储:作为 Hadoop 集群中数据的存储
文件系统被称为分布式的,因为数据被分散存储在多个服务器的块中。通过一个简单的例子可以直观地理解 HDFS,具体如下:假设一本大书包含了 A 到 Z 的章节。在普通文件系统中,整本书会作为一个单一文件存储在磁盘上。而在 HDFS 中,这本书会被分成更小的块,比如 A 到 H 的章节为一个块,I 到 P 为另一个块,Q 到 Z 为第三个块。这些块会被存储在不同的机架上(就像这个类比中的书架一样)。此外,这些章节会被复制三次,以确保每个章节有三个副本。
假设整本书的大小为 1GB,每个章节大约为 350MB:
HDFS 的书架类比
以这种方式存储书籍达到了几个重要目标:
-
由于这本书已经按章节分成了三部分,并且每部分被复制了三次,这意味着我们的进程可以通过从不同服务器查询各部分来并行读取这本书。这减少了 I/O 竞争,恰好展示了并行处理的正确使用示例。
-
如果某些机架不可用,我们可以从其他任何机架中提取章节,因为每个章节在不同机架上都有多个副本。
-
如果我被分配的任务只需要访问一个章节,例如 B 章节,我只需要访问与 A 到 H 章节相对应的文件。由于 A 到 H 章节的文件大小是整本书的三分之一,访问和读取该文件所需的时间将会大大减少。
-
这种模型还可以实现其他好处,例如对不同章节组的选择性访问权限等。
这可能是对实际 HDFS 功能的一个过于简化的类比,但它传达了该技术的基本原理——即大型文件被划分为块(块)并分布在多个服务器上,以高可用性冗余配置进行存储。现在我们将更详细地了解实际的 HDFS 架构:
Hadoop 的 HDFS 后端包括:
-
NameNode:可以认为这是主节点。NameNode 包含集群元数据,并且知道数据存储在什么位置——简而言之,它保存了命名空间。它将整个命名空间存储在内存中,当请求到达时,会提供哪个服务器存储了所需数据的信息。在 Hadoop 2 中,可以有多个 NameNode。可以创建一个辅助的 Secondary NameNode,它作为主节点的辅助节点来工作。因此,它不是备份 NameNode,而是帮助保持集群元数据最新的节点。
-
DataNode:DataNodes 是负责存储数据块并在接收到新请求时执行计算操作的独立服务器。这些通常是资源和容量较小的商品服务器,而不像存储集群元数据的 NameNode 那样强大。
HDFS 中的数据存储过程
以下几点有助于理解数据存储的过程:
HDFS 中的所有数据都是以块的形式写入的,通常大小为 128 MB。因此,一个 512 MB 的文件会被拆分为四个块(4 * 128 MB)。这些块随后会被写入到 DataNode。为了保持冗余性和高可用性,每个块都会被复制,形成多个副本。通常,Hadoop 安装的复制因子为三,这意味着每个数据块会被复制三次。
这确保了冗余性,在某个服务器发生故障或停止响应时,始终会有第二个甚至第三个副本可用。为了确保该过程无缝进行,DataNode 会将副本放在独立的服务器上,并且可以确保这些块被放置在数据中心不同机架上的服务器上。因为即使所有副本都在独立的服务器上,但如果这些服务器都在同一机架上,机架电力故障就会导致没有副本可用。
写入数据到 HDFS 的一般过程如下:
-
NameNode 收到写入新文件到 HDFS 的请求。
-
由于数据必须以块或块状单元的形式写入,HDFS 客户端(即发出请求的实体)开始将数据缓存到本地缓冲区,一旦缓冲区达到分配的块大小(例如 128 MB),它会通知 NameNode,表示准备好写入第一个数据块(块)。
-
NameNode 根据其所掌握的 HDFS 集群状态信息,回应并提供需要存储数据块的目标 DataNode 信息。
-
HDFS 客户端将数据写入目标 DataNode,并在数据块写入完成后通知 NameNode。
-
随后,目标 DataNode 开始将其数据块的副本复制到第二个 DataNode,该节点将作为当前块的副本。
-
一旦第二个 DataNode 完成写入过程,它会将数据块发送给第三个 DataNode。
-
这一过程会一直重复,直到所有数据块(或等同的文件)都复制到不同的节点上。
注意,块的数量将取决于文件的大小。下图展示了数据在 5 个数据节点之间的分布。
主节点和数据节点
Hadoop 第一个版本中,HDFS 的架构(也被称为 Hadoop 1)具有以下特点:
-
单一 NameNode:在早期版本中只有一个 NameNode,这使得它成为单点故障的源头,因为它存储了所有集群的元数据。
-
存储数据块、处理客户端请求并对数据块执行 I/O 操作(如创建、读取、删除等)的多个 DataNode。
-
Hadoop 第二版的 HDFS 架构,也称为 Hadoop 2,提供了原始 HDFS 设计的所有优点,并增加了一些新特性,最显著的是能够拥有多个 NameNode,它们可以作为主 NameNode 和副 NameNode。其他特性包括可以拥有多个命名空间以及 HDFS Federation。
-
HDFS Federation 值得特别提及。以下摘自
hadoop.apache.org
的内容以非常精确的方式解释了这一主题:
NameNode 是分布式的;这些 NameNode 是独立的,并且不需要相互协调。DataNode 被所有 NameNode 用作块的公共存储。每个 DataNode 都会向集群中的所有 NameNode 注册。DataNode 会定期发送心跳信号和块报告。
副 NameNode 并不是一个备份节点,意味着当 NameNode 无法使用时,它不能执行与 NameNode 相同的任务。然而,它通过执行一些管理操作,使 NameNode 的重启过程更加高效。
这些操作(例如将 HDFS 快照数据与数据变更信息合并)通常由 NameNode 在重启时执行,且根据自上次重启以来的数据变更量,可能需要较长时间。然而,副 NameNode 可以在主 NameNode 仍在运行时执行这些管理操作,从而确保在重启时主 NameNode 能够更快地恢复。由于副 NameNode 本质上会定期对 HDFS 数据进行检查点操作,因此它也被称为检查点节点。
Hadoop MapReduce
MapReduce 是 Hadoop 的标志性特性之一,它在推动 Hadoop 崛起方面起到了至关重要的作用。MapReduce 的工作原理是将较大的任务划分为更小的子任务。与其将单台机器分配给处理一个大任务,不如利用一组小型机器来完成这些小任务。通过这种方式分配工作,相比于单机架构,任务的完成效率会大大提高。
这与我们日常工作中完成任务的方式并没有太大不同。一个例子有助于更清楚地说明这一点。
MapReduce 的直观介绍
假设有一个虚拟的组织,包含 CEO、董事和经理。CEO 想要了解公司有多少新员工加入。CEO 向他的董事发送请求,要求他们汇报各自部门的新员工数量。董事则向各自部门的经理发送请求,要求经理提供新员工数量。经理将数字提供给董事,董事再将最终值反馈给 CEO。
这可以被视为 MapReduce 的一个现实世界的例子。在这个类比中,任务是找出新员工的数量。CEO 没有单独收集所有数据,而是将任务委托给了各个部门的董事和经理,他们提供了各自部门的个体数字,正如下面的图所示:
MapReduce 的概念
在这个相当简单的场景中,将一个大任务(在整个公司内寻找新员工)拆分成更小的任务(在每个团队内寻找新员工),然后再最终重新汇总这些个体数字,类似于 MapReduce 的工作原理。
MapReduce 的技术理解
MapReduce,如其名字所示,包含了 map 阶段和 reduce 阶段。map 阶段通常是对输入的每个元素应用的一个函数,从而修改其原始值。
MapReduce 生成键值对作为输出。
键值对: 键值对建立了一种关系。例如,如果约翰 20 岁,一个简单的键值对可以是(John, 20)。在 MapReduce 中,map 操作会生成这样的键值对,包含一个实体和分配给该实体的值。
实际中,map 函数可以很复杂,并涉及高级功能。
reduce 阶段会接受来自 map 函数的键值输入,并执行汇总操作。例如,考虑一个 map 操作的输出,其中包含学校中不同年级学生的年龄:
学生姓名 | 班级 | 年龄 |
---|---|---|
John | Grade 1 | 7 |
Mary | Grade 2 | 8 |
Jill | Grade 1 | 6 |
Tom | Grade 3 | 10 |
Mark | Grade 3 | 9 |
我们可以创建一个简单的键值对,举例来说,取班级和年龄的值(可以是任何东西,但我只是取这些来提供示例)。在这种情况下,我们的键值对将是(Grade 1, 7)、(Grade 2, 8)、(Grade 1, 6)、(Grade 3, 10)和(Grade 3, 9)。
一个计算每个年级学生年龄平均值的操作可以被定义为一个 reduce 操作。
更具体地说,我们可以对输出进行排序,然后将对应于每个年级的元组发送到不同的服务器。
例如,服务器 A 会接收元组(Grade 1, 7)和(Grade 1, 6),服务器 B 会接收元组(Grade 2, 8),服务器 C 会接收元组(Grade 3, 10)和(Grade 3, 9)。然后,A、B 和 C 服务器将分别计算这些元组的平均值并报告(Grade 1, 6.5)、(Grade 2, 8)和(Grade 3, 9.5)。
请注意,在这个过程中有一个中间步骤,涉及将输出发送到特定的服务器,并对输出进行排序,以确定应将其发送到哪个服务器。确实,MapReduce 需要一个 shuffle 和 sort 阶段,在该阶段,键值对会被排序,以确保每个 reducer 接收到一组固定的唯一键。
在这个例子中,如果说,假设不是有三台服务器,而是只有两台,服务器 A 可以被分配来计算与成绩 1 和 2 对应的键的平均值,服务器 B 则可以被分配来计算成绩 3 的平均值。
在 Hadoop 中,MapReduce 过程中会发生以下操作:
-
客户端发送任务请求。
-
NameNode 分配将执行映射操作和归约操作的 DataNode(独立服务器)。注意,DataNode 服务器的选择取决于所需的数据是否 本地存在于服务器上。数据所在的服务器只能执行映射操作。
-
DataNode 执行映射阶段并生成键值对 (k,v)。
当映射器生成 (k,v) 对时,它们会根据节点分配的 键 被发送到这些 reduce 节点进行计算。键分配到服务器的方式取决于分区器函数,这个函数可以像计算键的哈希值一样简单(在 Hadoop 中,这是默认的方式)。
一旦 reduce 节点接收到与其负责计算的键相对应的数据集,它就会应用 reduce 函数并生成最终的输出。
Hadoop 最大化了数据本地性的好处。映射操作由本地持有数据的服务器执行,即在磁盘上。更准确地说,映射阶段仅会由持有与文件对应块的服务器执行。通过委派多个独立的节点独立执行计算,Hadoop 架构可以有效地进行大规模的数据处理。
块大小以及映射器和归约器的数量
在 MapReduce 过程中,一个重要的考虑因素是理解 HDFS 块大小,即文件被分割成的块的大小。需要访问某个文件的 MapReduce 任务需要对表示该文件的每个块执行映射操作。例如,给定一个 512 MB 的文件和一个 128 MB 的块大小,存储整个文件需要四个块。因此,MapReduce 操作至少需要四个映射任务,其中每个映射操作应用于数据的每个子集(即四个块中的每一个)。
然而,如果文件非常大,并且需要 10,000 个块来存储,这意味着我们将需要进行 10,000 次映射操作。但如果我们只有 10 台服务器,那么每台服务器将需要执行 1,000 次映射操作。这可能是次优的,因为它可能导致由于磁盘 I/O 操作和每次映射的资源分配设置而产生较高的惩罚。
所需的 reducer 数量在 Hadoop Wiki 上有很好的总结 (wiki.apache.org/hadoop/HowManyMapsAndReduces
)。
理想的 reducers 应该是最优值,使它们尽可能接近:
- 块大小的倍数 * 任务时间在 5 到 15 分钟之间 * 尽可能创建最少的文件
除此之外,意味着你的 Reducer 的性能很可能不太好。用户常常倾向于使用一个非常高的值(“更多并行意味着更快!”)或一个非常低的值(“我不想超出我的命名空间配额!”)。这两者都是非常危险的,可能导致以下情况之一:
- 工作流的下一个阶段性能差 * 由于洗牌过程导致性能差 * 由于过载了 NameNode 并处理了最终无用的对象,导致整体性能差 * 不合理的破坏磁盘 IO * 由于处理大量的 CFIF/MFIF 工作,导致大量网络传输
Hadoop YARN
YARN 是 Hadoop 2 中引入的一个模块。在 Hadoop 1 中,作业管理和监控是由名为 JobTracker 和 TaskTracker 的进程执行的。运行 JobTracker 守护进程(进程)的 NameNode 会将作业提交给运行 TaskTracker 守护进程(进程)的 DataNode。
JobTracker 负责协调所有 MapReduce 作业,并作为中央管理员管理进程,处理服务器故障,重新分配给新的 DataNode 等。TaskTracker 监控其本地 DataNode 实例上的作业执行情况,并向 JobTracker 提供状态反馈,如下所示:
JobTracker 和 TaskTrackers
这种设计在很长一段时间内都运作良好,但随着 Hadoop 的发展,对更复杂和动态功能的需求也相应增加。在 Hadoop 1 中,NameNode 以及相应的 JobTracker 进程同时负责作业调度和资源监控。如果 NameNode 发生故障,集群中的所有活动将立即停止。最后,所有作业都必须以 MapReduce 的方式表示——也就是说,所有代码必须用 MapReduce 框架编写才能执行。
Hadoop 2 解决了所有这些问题:
-
作业管理、调度和资源监控的过程被解耦并委托给一个名为 YARN 的新框架/模块。
-
可以定义一个辅助的 NameNode,作为主 NameNode 的辅助节点。
-
此外,Hadoop 2.0 将支持 MapReduce 以外的框架。
-
代替固定的 map 和 reduce 插槽,Hadoop 2 将利用容器。
在 MapReduce 中,所有数据都必须从磁盘读取,这对于大数据集的操作是可行的,但对于小数据集的操作并不理想。实际上,任何需要非常快速处理(低延迟)、交互式的任务,或有多个迭代(因此需要多次从磁盘读取相同数据)的任务都会变得极为缓慢。
通过消除这些依赖关系,Hadoop 2 允许开发人员实现支持具有不同性能需求的作业的新编程框架,例如低延迟和交互式实时查询、机器学习所需的迭代处理、流数据处理等不同拓扑结构、内存数据缓存/处理等优化。
一些新术语变得突出:
-
ApplicationMaster:负责管理应用程序所需的资源。例如,如果某个作业需要更多内存,ApplicationMaster 将负责确保获得所需的资源。这里所说的应用程序是指应用执行框架,如 MapReduce、Spark 等。
-
Containers:资源分配的单位(例如,1GB 内存和四个 CPU)。一个应用程序可能需要多个这样的容器来执行。ResourceManager 为执行任务分配容器。容器分配完成后,ApplicationMaster 请求 DataNodes 启动已分配的容器,并接管容器的管理。
-
ResourceManager:YARN 的一个组件,主要负责向应用程序分配资源,并作为 JobTracker 的替代者。ResourceManager 进程与 JobTracker 一样运行在 NameNode 上。
-
NodeManagers:是 TaskTracker 的替代者,NodeManagers 负责向 ResourceManager(RM)报告作业状态,并监控容器的资源利用情况。
下图展示了 Hadoop 2.0 中 ResourceManager 和 NodeManagers 的高级视图:
Hadoop 2.0
Hadoop 2 中固有的突出概念已在下图中展示:
Hadoop 2.0 概念
YARN 中的作业调度
大型 Hadoop 集群中,多个作业同时运行并不罕见。当多个部门提交多个作业时,资源分配成为一个重要且有趣的话题。例如,如果 A 部门和 B 部门同时提交作业,而每个请求都要求最大可用资源,应该优先处理哪个请求呢?通常,Hadoop 使用先进先出(FIFO)策略。也就是说,谁先提交作业,谁就先使用资源。但如果 A 部门先提交了作业,但 A 的作业需要五个小时才能完成,而 B 的作业只需要五分钟呢?
为了应对作业调度中的这些细节和变量,已经实现了多种调度方法。以下是三种常用的方法:
-
FIFO:如上所述,FIFO 调度使用队列来优先处理作业。作业按照提交的顺序执行。
-
容量调度器:容量调度器根据每个部门可以提交的作业数量进行分配,其中一个部门可以表示一组逻辑用户。这是为了确保每个部门或组都能访问 Hadoop 集群并能够使用最低限度的资源。如果服务器上有未使用的资源,调度器还允许部门在其分配的容量之外进行扩展,直到达到每个部门设定的最大值。因此,容量调度器的模型提供了保证,确保每个部门可以基于确定性访问集群。
-
公平调度器:这些调度器尝试在不同应用之间均衡地分配资源。虽然在某些时刻完全均衡可能不可行,但通过公平调度器,可以在一段时间内实现资源分配的均衡,使得各个应用的平均资源使用量更为接近。
这些以及其他调度器提供了细粒度的访问控制(例如按用户或按组分配),并主要利用队列来优先分配资源。
Hadoop 中的其他主题
Hadoop 的其他一些方面值得特别提及。由于我们已详细讨论了最重要的主题,本节提供了其他一些相关主题的概述。
加密
数据加密是官方法规对各种数据类型的强制要求。在美国,涉及患者信息的数据需要符合 HIPAA 规定,这些规定决定了此类记录应该如何存储。HDFS 中的数据可以在静态(磁盘上)和/或传输过程中进行加密。用于解密数据的密钥通常由密钥管理系统(KMSs)进行管理。
用户认证
Hadoop 可以使用服务器的本地用户认证方法。例如,在基于 Linux 的机器上,用户可以根据系统的/etc/passwd
文件中定义的 ID 进行认证。换句话说,Hadoop 继承了服务器端设置的用户认证。
通过 Kerberos 进行用户认证,Kerberos 是一种跨平台认证协议,在 Hadoop 集群中也常常被使用。Kerberos 基于票证的概念,票证授予用户临时的、按需的权限。票证可以使用 Kerberos 命令作废,从而根据需要限制用户访问集群资源的权限。
请注意,即使用户被允许访问数据(用户认证),他或她仍然可能因为另一种称为授权的特性而受到限制,无法访问某些数据。该术语意味着,即使用户可以通过认证登录系统,用户可能只能访问其被授权访问的数据。此级别的授权通常通过本地 HDFS 命令来执行,修改目录和文件的所有权,将其分配给指定用户。
Hadoop 数据存储格式
由于 Hadoop 涉及存储大规模数据,选择适合特定用例的存储类型至关重要。数据可以以几种格式存储在 Hadoop 中,选择最佳存储格式取决于读/写 I/O 速度、文件是否能够根据需求高效压缩和解压缩,以及文件是否容易拆分,因为数据最终将以块的形式存储。
一些常见和广泛使用的存储格式如下:
-
文本/CSV:这些是纯文本 CSV 文件,类似于 Excel 文件,但以纯文本格式保存。由于 CSV 文件按行存储记录,因此自然容易将文件拆分成数据块。
-
Avro:Avro 旨在提高异构系统之间数据共享的效率。它使用数据序列化将模式和实际数据存储在一个紧凑的二进制格式中。Avro 使用 JSON 存储模式,使用二进制格式存储数据,并将它们序列化为单一的 Avro 对象容器文件。多种语言(如 Python、Scala、C/C++等)具有原生 API,可以读取 Avro 文件,因此它非常便携,适用于跨平台的数据交换。
-
Parquet:Parquet 是一种列式数据存储格式。这有助于提高性能,有时通过允许按列存储和访问数据,从而显著提升效率。直观地说,如果你在处理一个 1GB 的文件,文件有 100 列和 100 万行,且你只想查询其中一列的数据,那么能够仅访问这一列会比必须访问整个文件更高效。
-
ORCFiles:ORC 代表优化行列式。在某种意义上,它是纯列式格式(如 Parquet)的一层额外优化。ORCFiles 不仅按列存储数据,还按行存储数据,这些行被称为条带(stripes)。一个以表格格式存储的数据文件可以拆分成多个较小的条带,每个条带包含原始文件中的一部分行。通过这种方式拆分数据,如果用户任务只需要访问数据的一个小部分,进程可以查询包含该数据的特定条带。
-
SequenceFiles:在 SequenceFiles 中,数据以键值对的形式表示,并以二进制序列化格式存储。由于序列化,数据可以以紧凑的二进制格式表示,不仅减少了数据大小,而且提高了 I/O 效率。当存在多个小文件(例如音频文件)时,Hadoop,特别是 HDFS,效率较低。SequenceFiles 通过将多个小文件作为一个单元或 SequenceFile 存储来解决这个问题。它们还非常适合用于可拆分的并行操作,并且总体上对于 MapReduce 作业效率较高。
-
HDFS 快照: HDFS 快照允许用户在给定时间点以只读模式保留数据。用户可以在 HDFS 中创建快照,本质上是某一时刻的数据副本,以便稍后需要时可以恢复。这确保了在文件损坏或其他影响数据可用性的故障发生时,数据能够恢复。因此,它也可以被视为备份。这些快照会保存在创建它们的
.snapshot
目录中。 -
节点故障处理: 大型 Hadoop 集群可能包含数万台节点。因此,任何一天都会发生服务器故障。因此,为了让 NameNode 了解集群中所有节点的状态,DataNodes 会定期向 NameNode 发送心跳信号。如果 NameNode 检测到某台服务器故障,即停止接收心跳信号,NameNode 会将该服务器标记为失败,并将该服务器上的所有数据复制到新实例上。
Hadoop 3 中预期的新功能
在写这本书时,Hadoop 3 仍处于 Alpha 阶段。有关 Hadoop 3 的新变化的详细信息可以在互联网上找到。例如,hadoop.apache.org/docs/current/
提供了有关架构变化的最新信息。
Hadoop 生态系统
本章应命名为 Apache 生态系统。Hadoop,和本节中将讨论的所有其他项目一样,都是一个 Apache 项目。Apache 这个名称通常作为 Apache 软件基金会支持的开源项目的简称。它最初源于 1990 年代初期 Apache HTTP 服务器的开发,如今是一个全球性的协作项目,完全由志愿者组成,这些志愿者参与发布开源软件,服务于全球技术社区。
Hadoop 最初作为 Apache 生态系统中的一个项目起步,并且至今仍然是。由于其流行性,许多其他也属于 Apache 的项目直接或间接地与 Hadoop 关联,它们在 Hadoop 环境中支持关键功能。尽管如此,重要的是要记住,这些项目在大多数情况下可以作为独立产品存在,并且可以在没有 Hadoop 环境的情况下运行。它们是否提供最佳功能则是另一个话题。
在本节中,我们将介绍一些对 Hadoop 的增长和可用性产生重要影响的 Apache 项目,这些项目已成为标准的 IT 企业解决方案,具体内容见下图:
产品 | 功能 |
---|---|
Apache Pig | Apache Pig,也被称为 Pig Latin,是一种专门设计用于通过简洁的语句表示 MapReduce 程序的语言,这些语句定义了工作流。以传统方法(如 Java)编写 MapReduce 程序可能非常复杂,而 Pig 提供了一种简化的抽象,可以通过简单的语法表达 MapReduce 工作流和复杂的提取-转换-加载(ETL)过程。Pig 程序通过 Grunt shell 执行。 |
Apache HBase | Apache HBase 是一个分布式列式数据库,位于 HDFS 之上。它是基于 Google 的 BigTable 模型设计的,其中数据以列式格式表示。HBase 支持在包含数十亿条记录的表之间进行低延迟的读写,非常适合需要直接随机访问数据的任务。更具体地说,HBase 按照行、列和时间戳三个维度对数据进行索引。它还提供了一种表示具有任意数量列的数据的方法,因为列值可以在 HBase 表的单元格中作为键值对表示。 |
Apache Hive | Apache Hive 提供了类似 SQL 的方言来查询存储在 HDFS 中的数据。Hive 将数据存储为序列化的二进制文件,采用类似文件夹的结构存储在 HDFS 中。与传统数据库管理系统中的表类似,Hive 将数据存储为基于用户选择的属性分区的表格格式,分区因此成为更高级目录或表的子文件夹。还有一个由桶(buckets)概念提供的第三层抽象,桶引用了 Hive 表的分区中的文件。 |
Apache Sqoop | Sqoop 用于从传统数据库提取数据到 HDFS。大企业如果将数据存储在关系型数据库管理系统中,可以使用 Sqoop 将数据从数据仓库转移到 Hadoop 实现中。 |
Apache Flume | Flume 用于大规模日志数据的管理、汇聚和分析。 |
Apache Kafka | Kafka 是一个基于发布/订阅的中间件系统,可以用来实时分析并将流式数据持久化(在 HDFS 中)。 |
Apache Oozie | Oozie 是一个工作流管理系统,用于调度 Hadoop 作业。它实现了一个称为有向无环图(DAG)的关键概念,稍后将在我们关于 Spark 的章节中讨论。 |
Apache Spark | Spark 是 Apache 中最重要的项目之一,旨在解决 HDFS-MapReduce 模型的一些不足之处。它最初是加州大学伯克利分校的一个相对较小的项目,迅速发展成为 Hadoop 在分析任务中最显著的替代方案之一。Spark 已在业界得到了广泛采用,并且包含了多个子项目,提供了额外的能力,如机器学习、流式分析等。 |
亲自操作 CDH
在本节中,我们将利用 CDH QuickStart 虚拟机来处理当前章节中讨论的一些主题。练习不一定需要按时间顺序执行,也不依赖于其他练习的完成。
本节中我们将完成以下练习:
-
使用 Hadoop MapReduce 进行单词计数
-
操作 HDFS
-
使用 Apache Hive 下载并查询数据
使用 Hadoop MapReduce 进行单词计数
在这个练习中,我们将尝试统计有史以来最厚重小说之一中每个单词的出现次数。为了进行练习,我们选择了由 Georges 和/或 Madeleine de Scudéry 于 1649-1653 年间所写的小说《Artamène ou le Grand Cyrus》。该小说被认为是有史以来第二长的小说,相关列表可参考维基百科(en.wikipedia.org/wiki/List_of_longest_novels
)。这本小说包含了 13,905 页,分为 10 卷,近 200 万个单词。
首先,我们需要在 VirtualBox 中启动 Cloudera Distribution of Hadoop Quickstart 虚拟机,并双击 Cloudera Quickstart 虚拟机实例:
启动过程需要一些时间,因为它会初始化所有与 CDH 相关的进程,如 DataNode、NameNode 等:
一旦过程启动,它将启动一个默认的登录页面,其中包含许多与 Hadoop 相关的教程。我们将在 Unix 终端中编写 MapReduce 代码。本节中的终端可以从左上角的菜单启动,如下图所示:
现在,我们需要按照以下步骤操作:
-
创建一个名为
cyrus
的目录。这是我们存储包含书籍文本的所有文件的地方。 -
按照步骤 4 中所示运行
getCyrusFiles.sh
。这将把书籍下载到cyrus
目录中。 -
按照所示运行
processCyrusFiles.sh
。书中包含各种 Unicode 和不可打印字符。此外,我们希望将所有单词转换为小写,以避免对大小写不同但相同的单词进行重复计数。 -
这将生成一个名为
cyrusprint.txt
的文件。该文档包含了整本书的文本。我们将在此文本文件上运行我们的 MapReduce 代码。 -
准备
mapper.py
和reducer.py
。顾名思义,mapper.py
负责运行 MapReduce 过程中的 map 部分。同样,reducer.py
负责运行 MapReduce 过程中的 reduce 部分。mapper.py
文件会将文档分割成单词,并为每个单词分配一个值 1。reducer.py
文件会读取mapper.py
的排序输出,并对相同单词的出现次数进行求和(通过首先将该单词的计数初始化为 1,并在每次该单词出现时递增)。最终输出是一个包含文档中每个单词计数的文件。
步骤如下:
- 创建
getCyrusFiles.sh
- 该脚本将用于从网上获取数据:
[cloudera@quickstart ~]$ mkdir cyrus
[cloudera@quickstart ~]$ vi getCyrusFiles.sh
[cloudera@quickstart ~]$ cat getCyrusFiles.sh
for i in `seq 10`
do
curl www.artamene.org/documents/cyrus$i.txt -o cyrus$i.txt
done
- 创建
processCyrusFiles.sh
- 该脚本将用于连接并清理在上一步骤下载的文件:
[cloudera@quickstart ~]$ vi processCyrusFiles.sh
[cloudera@quickstart ~]$ cat processCyrusFiles.sh
cd ~/cyrus;
for i in `ls cyrus*.txt` do cat $i >> cyrusorig.txt; done
cat cyrusorig.txt | tr -dc '[:print:]' | tr A-Z a-z > cyrusprint.txt
- 将权限更改为 755,以便在命令提示符下使
.sh
文件可执行:
[cloudera@quickstart ~]$ chmod 755 getCyrusFiles.sh
[cloudera@quickstart ~]$ chmod 755 processCyrusFiles.sh
- 执行
getCyrusFiles.sh
:
[cloudera@quickstart cyrus]$ ./getCyrusFiles.sh
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 908k 100 908k 0 0 372k 0 0:00:02 0:00:02 --:--:-- 421k
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1125k 100 1125k 0 0 414k 0 0:00:02 0:00:02 --:--:-- 471k
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1084k 100 1084k 0 0 186k 0 0:00:05 0:00:05 --:--:-- 236k
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1048k 100 1048k 0 0 267k 0 0:00:03 0:00:03 --:--:-- 291k
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1116k 100 1116k 0 0 351k 0 0:00:03 0:00:03 --:--:-- 489k
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1213k 100 1213k 0 0 440k 0 0:00:02 0:00:02 --:--:-- 488k
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1119k 100 1119k 0 0 370k 0 0:00:03 0:00:03 --:--:-- 407k
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1132k 100 1132k 0 0 190k 0 0:00:05 0:00:05 --:--:-- 249k
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1084k 100 1084k 0 0 325k 0 0:00:03 0:00:03 --:--:-- 365k
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1259k 100 1259k 0 0 445k 0 0:00:02 0:00:02 --:--:-- 486k
[cloudera@quickstart cyrus]$ ls
cyrus10.txt cyrus3.txt cyrus6.txt cyrus9.txt
cyrus1.txt cyrus4.txt cyrus7.txt getCyrusFiles.sh
cyrus2.txt cyrus5.txt cyrus8.txt processCyrusFiles.sh
- 执行
processCyrusFiles.sh
:
[cloudera@quickstart cyrus]$ ./processCyrusFiles.sh
[cloudera@quickstart cyrus]$ ls
cyrus10.txt cyrus3.txt cyrus6.txt cyrus9.txt getCyrusFiles.sh
cyrus1.txt cyrus4.txt cyrus7.txt cyrusorig.txt processCyrusFiles.sh
cyrus2.txt cyrus5.txt cyrus8.txt cyrusprint.txt
[cloudera@quickstart cyrus]$ ls -altrh cyrusprint.txt
-rw-rw-r-- 1 cloudera cloudera 11M Jun 28 20:02 cyrusprint.txt
[cloudera@quickstart cyrus]$ wc -w cyrusprint.txt
1953931 cyrusprint.txt
- 执行以下步骤,将最终文件(命名为
cyrusprint.txt
)复制到 HDFS,并创建mapper.py
和reducer.py
脚本:
mapper.py
和reducer.py
文件可以在 Glenn Klockwood 的网站上找到(www.glennklockwood.com/data-intensive/hadoop/streaming.html
),该网站提供了关于 MapReduce 和相关主题的丰富信息。
以下代码展示了mapper.py
的内容:
[cloudera@quickstart cyrus]$ hdfs dfs -ls /user/cloudera
[cloudera@quickstart cyrus]$ hdfs dfs -mkdir /user/cloudera/input
[cloudera@quickstart cyrus]$ hdfs dfs -put cyrusprint.txt /user/cloudera/input/
[cloudera@quickstart cyrus]$ vi mapper.py
[cloudera@quickstart cyrus]$ cat mapper.py
#!/usr/bin/env python
#the above just indicates to use python to intepret this file
#This mapper code will input a line of text and output <word, 1> #
import sys
sys.path.append('.')
for line in sys.stdin:
line = line.strip()
keys = line.split()
for key in keys:
value = 1
print ("%s\t%d" % (key,value))
[cloudera@quickstart cyrus]$ vi reducer.py # Copy-Paste the content of reducer.py as shown below using the vi or nano Unix editor.
[cloudera@quickstart cyrus]$ cat reducer.py
#!/usr/bin/env python
import sys
sys.path.append('.')
last_key = None
running_total = 0
for input_line in sys.stdin:
input_line = input_line.strip()
this_key, value = input_line.split("\t", 1)
value = int(value)
if last_key == this_key:
running_total += value
else:
if last_key:
print("%s\t%d" % (last_key, running_total))
running_total = value
last_key = this_key
if last_key == this_key:
print( "%s\t%d" % (last_key, running_total) )
[cloudera@quickstart cyrus]$ chmod 755 *.py
- 执行 mapper 和 reducer 脚本,这些脚本将执行 MapReduce 操作以生成单词计数。你可能会看到如下错误消息,但为了本练习的目的(以及生成结果),可以忽略这些错误:
[cloudera@quickstart cyrus]$ hadoop jar /usr/lib/hadoop-mapreduce/hadoop-streaming.jar -input /user/cloudera/input -output /user/cloudera/output -mapper /home/cloudera/cyrus/mapper.py -reducer /home/cloudera/cyrus/reducer.py
packageJobJar: [] [/usr/lib/hadoop-mapreduce/hadoop-streaming-2.6.0-cdh5.10.0.jar] /tmp/streamjob1786353270976133464.jar tmpDir=null
17/06/28 20:11:21 INFO client.RMProxy: Connecting to ResourceManager at /0.0.0.0:8032
17/06/28 20:11:21 INFO client.RMProxy: Connecting to ResourceManager at /0.0.0.0:8032
17/06/28 20:11:22 INFO mapred.FileInputFormat: Total input paths to process : 1
17/06/28 20:11:22 INFO mapreduce.JobSubmitter: number of splits:2
17/06/28 20:11:23 INFO mapreduce.JobSubmitter: Submitting tokens for job: job_1498704103152_0002
17/06/28 20:11:23 INFO impl.YarnClientImpl: Submitted application application_1498704103152_0002
17/06/28 20:11:23 INFO mapreduce.Job: The url to track the job: http://quickstart.cloudera:8088/proxy/application_1498704103152_0002/
17/06/28 20:11:23 INFO mapreduce.Job: Running job: job_1498704103152_0002
17/06/28 20:11:30 INFO mapreduce.Job: Job job_1498704103152_0002 running in uber mode : false
17/06/28 20:11:30 INFO mapreduce.Job: map 0% reduce 0%
17/06/28 20:11:41 INFO mapreduce.Job: map 50% reduce 0%
17/06/28 20:11:54 INFO mapreduce.Job: map 83% reduce 0%
17/06/28 20:11:57 INFO mapreduce.Job: map 100% reduce 0%
17/06/28 20:12:04 INFO mapreduce.Job: map 100% reduce 100%
17/06/28 20:12:04 INFO mapreduce.Job: Job job_1498704103152_0002 completed successfully
17/06/28 20:12:04 INFO mapreduce.Job: Counters: 50
File System Counters
FILE: Number of bytes read=18869506
FILE: Number of bytes written=38108830
FILE: Number of read operations=0
FILE: Number of large read operations=0
FILE: Number of write operations=0
HDFS: Number of bytes read=16633042
HDFS: Number of bytes written=547815
HDFS: Number of read operations=9
HDFS: Number of large read operations=0
HDFS: Number of write operations=2
Job Counters
Killed map tasks=1
Launched map tasks=3
Launched reduce tasks=1
Data-local map tasks=3
Total time spent by all maps in occupied slots (ms)=39591
Total time spent by all reduces in occupied slots (ms)=18844
Total time spent by all map tasks (ms)=39591
Total time spent by all reduce tasks (ms)=18844
Total vcore-seconds taken by all map tasks=39591
Total vcore-seconds taken by all reduce tasks=18844
Total megabyte-seconds taken by all map tasks=40541184
Total megabyte-seconds taken by all reduce tasks=19296256
Map-Reduce Framework
Map input records=1
Map output records=1953931
Map output bytes=14961638
Map output materialized bytes=18869512
Input split bytes=236
Combine input records=0
Combine output records=0
Reduce input groups=45962
Reduce shuffle bytes=18869512
Reduce input records=1953931
Reduce output records=45962
Spilled Records=3907862
Shuffled Maps =2
Failed Shuffles=0
Merged Map outputs=2
GC time elapsed (ms)=352
CPU time spent (ms)=8400
Physical memory (bytes) snapshot=602038272
Virtual memory (bytes) snapshot=4512694272
Total committed heap usage (bytes)=391979008
Shuffle Errors
BAD_ID=0
CONNECTION=0
IO_ERROR=0
WRONG_LENGTH=0
WRONG_MAP=0
WRONG_REDUCE=0
File Input Format Counters
Bytes Read=16632806
File Output Format Counters
Bytes Written=547815
17/06/28 20:12:04 INFO streaming.StreamJob: Output directory: /user/cloudera/output
- 结果存储在 HDFS 的
/user/cloudera/output
目录下,文件以part-
为前缀:
[cloudera@quickstart cyrus]$ hdfs dfs -ls /user/cloudera/output
Found 2 items
-rw-r--r-- 1 cloudera cloudera 0 2017-06-28 20:12 /user/cloudera/output/_SUCCESS
-rw-r--r-- 1 cloudera cloudera 547815 2017-06-28 20:12 /user/cloudera/output/part-00000
- 要查看文件内容,使用
hdfs dfs -cat
命令并提供文件名。在这个例子中,我们查看输出的前 10 行:
[cloudera@quickstart cyrus]$ hdfs dfs -cat /user/cloudera/output/part-00000 | head -10
! 1206
!) 1
!quoy, 1
' 3
'' 1
'. 1
'a 32
'appelloit 1
'auoit 1
'auroit 10
使用 Hive 分析石油进口价格
在本节中,我们将使用 Hive 来分析 1980-2016 年间世界各国的石油进口价格。这些数据可以通过OECD(经济合作与发展组织)的网站获取,网址如以下截图所示:
实际的 CSV 文件可以通过以下链接获取:stats.oecd.org/sdmx-json/data/DP_LIVE/.OILIMPPRICE.../OECD?contentType=csv&detail=code&separator=comma&csv-lang=en
。
由于我们将在 Hive 中加载数据,因此通过终端在 Cloudera Quickstart CDH 环境中将文件下载到我们的主目录是有意义的。我们将执行以下步骤:
- 将 CSV 文件下载到 CDH 环境中:
# Download the csv file
cd /home/cloudera;
wget -O oil.csv "https://stats.oecd.org/sdmx-json/data/DP_LIVE/.OILIMPPRICE.../OECD?contentType=csv&detail=code&separator=comma&csv-lang=en"
- 清理 CSV 文件。数据清理是数据科学中的核心领域。实际上,接收需要某种程度清理的文件是非常常见的。这是因为列中可能会有无效字符或值、缺失数据、缺少或多余的分隔符等等。我们注意到各种值被双引号(")包围。在 Hive 中,我们可以通过在创建表时指定
quoteChar
属性来忽略这些引号。由于 Linux 也提供了简单易行的方法来移除这些字符,我们使用了sed
命令来去除引号:
[cloudera@quickstart ~]$ sed -i 's/\"//g' oil.csv
此外,在我们下载的文件oil.csv
中,我们发现存在非打印字符,这可能导致问题。我们通过执行以下命令将其移除:
[cloudera@quickstart ~]$ tr -cd '\11\12\15\40-\176' oil_.csv > oil_clean.csv
(来源: alvinalexander.com/blog/post/linux-unix/how-remove-non-printable-ascii-characters-file-unix
)
最后,我们将新文件(oil_clean.csv
)复制到oil.csv
。由于oil.csv
文件已经存在于同一文件夹中,我们收到了覆盖提示并输入了yes
:
[cloudera@quickstart ~]$ mv oil_clean.csv oil.csv
mv: overwrite `oil.csv'? yes
- 登录到 Cloudera Hue:
在浏览器的书签栏中点击 Hue。这将弹出 Cloudera 登录界面。使用 ID cloudera
和密码 cloudera
登录:
- 在 Hue 登录窗口顶部的快速启动下拉菜单中点击 Hue:
- 创建表架构,加载 CSV 文件
oil.csv
,并查看记录:
CREATE TABLE IF NOT EXISTS OIL
(location String, indicator String, subject String, measure String,
frequency String, time String, value Float, flagCode String)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
STORED AS TEXTFILE
tblproperties("skip.header.line.count"="1");
LOAD DATA LOCAL INPATH '/home/cloudera/oil.csv' INTO TABLE OIL;
SELECT * FROM OIL;
-
加载油文件。
-
现在,表已加载到 Hive 中,你可以使用 HiveQL 运行各种 Hive 命令。这些命令的完整集可以在
cwiki.apache.org/confluence/display/Hive/LanguageManual
中找到。
例如,要查找 1980-2015 年间(数据集的日期范围)每个国家油价的最大值、最小值和平均值,我们可以使用熟悉的 SQL 操作符。查询将如下所示:
SELECT LOCATION, MIN(value) as MINPRICE, AVG(value) as AVGPRICE,
MAX(value) as MAXPRICE
FROM OIL
WHERE FREQUENCY LIKE "A"
GROUP BY LOCATION;
以下是相同的截图:
以类似的方式,我们可以使用其他 SQL 命令。Hive 手册深入介绍了这些命令以及数据保存、查询和检索的各种方法。
Hue 包含一系列有用的功能,如数据可视化、数据下载等,允许用户对数据进行临时分析。
要访问可视化功能,请点击结果部分网格图标下方的可视化图标,如下图所示:
选择散点图。在 Hue 中,这种图表类型,也被更广泛地称为散点图,允许用户非常容易地创建多变量图表。可以选择 x 轴和 y 轴的不同数值,以及散点大小和分组,如下图所示:
以下是通过在下拉菜单中选择“饼图”来构建的一个简单饼图:
在 Hive 中连接表
Hive 支持高级连接功能。以下示例展示了使用左连接的过程。如图所示,原始表格包含每个国家的数据,国家以三字母代码表示。由于 Hue 支持地图图表,我们可以添加纬度和经度值,将油价数据叠加到世界地图上。
为此,我们需要下载一个包含纬度和经度值的数据集:
# ENTER THE FOLLOWING IN THE UNIX TERMINAL
# DOWNLOAD LATLONG CSV FILE
cd /home/cloudera;
wget -O latlong.csv "https://gist.githubusercontent.com/tadast/8827699/raw/7255fdfbf292c592b75cf5f7a19c16ea59735f74/countries_codes_and_coordinates.csv"
# REMOVE QUOTATION MARKS
sed -i 's/\"//g' latlong.csv
下载并清理完文件后,定义架构并将数据加载到 Hive 中:
CREATE TABLE IF NOT EXISTS LATLONG
(country String, alpha2 String, alpha3 String, numCode Int, latitude Float, longitude Float)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
STORED AS TEXTFILE
TBLPROPERTIES("skip.header.line.count"="1");
LOAD DATA LOCAL INPATH '/home/cloudera/latlong.csv' INTO TABLE LATLONG;
将油价数据与纬度/经度数据连接:
SELECT DISTINCT * FROM
(SELECT location, avg(value) as AVGPRICE from oil GROUP BY location) x
LEFT JOIN
(SELECT TRIM(ALPHA3) AS alpha3, latitude, longitude from LATLONG) y
ON (x.location = y.alpha3);
我们现在可以继续进行地理空间可视化。需要记住的是,这些是 Hue 中的初步可视化,它们提供了一种非常方便的方式来查看数据。更深入的可视化可以使用形状文件、多边形和其他高级图表方法来开发。
从下拉菜单中选择渐变地图,并输入适当的值来创建图表,如下图所示:
下一个图表是使用下拉菜单中的“标记地图”选项开发的。它使用三字符的国家代码来在相应地区上放置标记和相关的数值,如下图所示:
总结
本章提供了 Hadoop 的技术概述。我们讨论了 Hadoop 的核心组件和核心概念,如 MapReduce 和 HDFS。我们还探讨了使用 Hadoop 时的技术挑战和注意事项。虽然从概念上看它可能显得简单,但 Hadoop 架构的内部工作原理和正式的管理过程可能相当复杂。本章中我们强调了其中的一些问题。
我们通过使用 Cloudera 分发版的 Hadoop 进行实践练习做了总结。对于本教程,我们使用了从 Cloudera 官网之前下载的 CDH 虚拟机。
在下一章中,我们将介绍 NoSQL,作为 Hadoop 的替代方案或补充解决方案,具体取决于您的个人和/或组织需求。虽然 Hadoop 提供了更丰富的功能集,但如果您的使用案例可以通过简单的 NoSQL 解决方案完成,后者可能在所需的工作量方面是一个更容易的选择。
第五章:使用 NoSQL 进行大数据挖掘
NoSQL这个术语最早由 Carlo Strozzi 于 1998 年使用,他发布了 Strozzi NoSQL 开源关系数据库。在 2000 年代末期,数据库架构出现了新的范式,其中许多并不遵循关系型数据库系统所要求的严格约束。这些数据库由于不符合标准数据库的约定(如 ACID 合规性),很快被归为一个统称为 NoSQL 的广泛类别。
每个 NoSQL 数据库都声称在某些使用场景下最为优化。尽管其中很少有数据库能符合作为通用数据库管理系统的要求,但它们都在 NoSQL 系统的范围内利用了一些共同的主题。
在这一章节中,我们将探讨一些广泛的 NoSQL 数据库管理系统的类别。我们将讨论促使迁移到 NoSQL 数据库系统的主要驱动因素,以及这些数据库如何解决特定的业务需求,这些需求促使了它们的广泛应用,并以一些 NoSQL 的实操练习作为结尾。
本章节涵盖的主题包括:
-
为什么选择 NoSQL?
-
NoSQL 数据库
-
内存数据库
-
列式数据库
-
面向文档的数据库
-
键值数据库
-
图数据库
-
其他 NoSQL 类型与总结
-
NoSQL 系统的实操练习
为什么选择 NoSQL?
NoSQL 一词通常表示不仅仅是 SQL:即底层数据库具有与常见传统数据库系统不同的属性。因此,NoSQL 数据库并没有明确的界定标准,唯一的区别就是它们不提供 ACID 合规性等特性。因此,了解 ACID 属性的性质是有帮助的,这些属性一直是数据库系统的核心,并简要讨论 BASE 和 CAP 这两个今天数据库领域中的核心术语。
ACID、BASE 和 CAP 属性
我们首先来讲解 ACID 和 SQL。
ACID 和 SQL
ACID 代表原子性、一致性、隔离性和持久性:
-
原子性:这表示数据库事务要么完全执行,要么根本不执行。换句话说,要么所有事务都应该被提交,即完全持久化,要么根本不提交。事务不能部分执行。
-
一致性:数据管理中的约束条件,即决定数据库内数据管理规则的规则,将在整个数据库中保持一致。不同的实例不会遵循与其他数据库实例不同的规则。
-
隔离性:此属性定义了并发操作(事务)如何读取和写入数据的规则。例如,如果某个记录在更新时,另一个进程读取了同一记录,那么数据库系统的隔离级别将决定哪个版本的数据会返回给用户。
-
持久性:数据库系统的持久性通常意味着已提交的事务即使在系统故障的情况下也会保持持久性。这通常通过使用事务日志来管理,数据库在恢复过程中可以参考这些日志。
读者可能会注意到,这里定义的所有特性主要与数据库事务相关。事务是遵循上述规则并对数据库进行更改的操作单元。例如,从 ATM 取款的典型逻辑流程如下:
-
用户从 ATM 机取款
-
银行检查用户当前的余额
-
数据库系统从用户账户中扣除相应金额
-
数据库系统更新用户账户中的金额以反映变化
因此,在 1990 年代中期之前,诸如 Oracle、Sybase、DB2 等流行的数据库大多被优化用于记录和管理事务数据。在此之前,大多数数据库都负责管理事务数据。90 年代中期互联网的快速发展带来了新的数据类型,这些数据类型不一定要求严格遵守 ACID 一致性要求。YouTube 上的视频、Pandora 上的音乐以及公司邮件记录等,都是在这些用例中,事务型数据库除了作为存储数据的技术层外并没有带来更多的价值。
NoSQL 的BASE特性
到了 2000 年代末,数据量激增,显然需要一种新的替代模型来管理数据。这个新模型被称为 BASE,成为一个基础性话题,取代了 ACID 作为首选的数据库管理系统模型。
BASE代表Basically Available Soft-state Eventually 一致性。这意味着数据库大多数时间是基本可用的;也就是说,可能会有一些服务不可用的时间段(因此需要实施额外的冗余措施)。软状态意味着系统的状态不能得到保证——同一数据的不同实例可能包含不同的内容,因为它可能尚未捕捉到集群其他部分的最新更新。最后,最终一致性意味着尽管数据库在任何时候可能不处于相同的状态,但它最终会达到相同的状态;也就是说,变得一致。
CAP 定理
CAP 定理是由 Eric Allen Brewer 在 1990 年代末提出的,它对分布式数据库系统的约束,或者更广泛地说,是分布式数据库系统的特性进行了分类。简言之,CAP 定理认为严格来说,数据库系统只能保证 CAP 定义的三个特性中的两个,具体如下:
-
一致性:数据在所有数据库实例中应该保持一致,因此,当查询时,应该在所有节点上提供一致的结果。
-
可用性:无论任何单个节点的状态如何,系统在执行查询时总是会给出结果(无论是否为最新的提交)
-
分区容忍性:这意味着,当节点分布在网络上时,即使某个节点失去与另一个节点的连接,系统也应该继续正常运行。
从这一点来看,可以明显看出,既然在集群中节点通过网络连接,而网络本身可能会发生中断,因此必须保证分区容忍性,以确保系统能够继续正常运行。在这种情况下,争论的焦点在于选择一致性还是可用性。例如,如果系统必须保证一致性;也就是说,在所有节点中显示最新的提交,那么所有节点就无法在同一时间内都是可用的,因为某些节点可能没有最新的提交。在这种情况下,新的更新查询将不会执行,直到所有节点都更新了新数据。而在保证可用性的情况下,类似地,我们无法保证一致性,因为为了始终可用,某些节点可能没有与其他节点相同的数据,如果某个节点未写入新更新的数据。
在确保一致性和确保可用性之间的选择中,存在着大量的困惑和争论,因此数据库被分类为CP或AP。为了本次讨论,我们不必纠结于这些术语,因为那样会引入一种相对抽象和哲学性的讨论。提供上述术语的主要目的是为了反映一些推动数据库发展的基础理论。
对 NoSQL 技术的需求
虽然大多数数据库系统最初是为了管理事务而设计的,但互联网相关技术的增长以及新型数据的出现,这些数据并不需要事务系统的严格要求,因此需要开发替代的框架。
例如,存储以下类型的数据并不一定需要复杂的事务数据库:
-
电子邮件
-
媒体文件,例如音频/视频文件
-
社交网络消息
-
网站 HTML 页面
-
许多其他特性
此外,用户数量的增加,以及由此带来的数据量增加,意味着需要开发更为强大的架构,具备以下特点:
-
可扩展性,以应对不断增长的数据量
-
利用普通硬件来减少对昂贵硬件的依赖
-
提供跨多个节点的分布式处理能力,以处理大规模数据集
-
具备容错能力/提供高可用性以应对节点和站点故障
可扩展性意味着系统能够通过增加节点数量来容纳数据量的增长,也就是通过横向扩展来实现。此外,增加节点数量应该对系统的性能影响最小。
容错性意味着系统应该能够处理节点故障,这在拥有数百甚至数千个节点的大型分布式系统中并不罕见。
这促使了多种开创性和有影响力的系统的开发,其中最著名的可能是 Google Bigtable 和 Amazon Dynamo。
Google Bigtable
Bigtable 是一个在 2004 年启动的项目,旨在管理 Google 各个项目中使用的数据的可扩展性和性能。描述该系统特性的开创性论文于 2006 年发布 (static.googleusercontent.com/media/research.google.com/en//archive/bigtable-osdi06.pdf
),标题为 Bigtable: A Distributed Storage System for Structured Data。从本质上讲,Bigtable 是一个 列式存储(稍后会详细介绍),其中每个值都可以通过行键、列键和时间戳唯一标识。它是首批主流数据库之一,体现了将数据存储在列式格式中的优势,而非使用更常见的行式布局。尽管在 Bigtable 之前,像 kdb+ 和 Sybase IQ 这样的列式数据库已经存在,但行业领导者使用该方法管理 PB 级别的信息,使得这一概念备受关注。
Bigtable 官方网站总结了其关键价值主张:
Bigtable 被设计用来处理大规模工作负载,保持一致的低延迟和高吞吐量,因此它是操作性和分析性应用的理想选择,包括物联网、用户分析和金融数据分析。
自从 Bigtable 推出以来,其他一些 NoSQL 数据库也采纳了列式数据布局的惯例;最著名的有 HBase 和 Accumulo,它们都是 Apache 项目。
现在,Bigtable 解决方案可以通过 cloud.google.com/bigtable/
使用,并可按订阅方式购买。对于较小的数据量,费用非常低廉且合理,而较大的安装则需要更为复杂的实施方案。
Amazon Dynamo
在 Google 宣布 Bigtable 后不久,Amazon 在 2007 年 10 月举行的第 21 届 ACM 操作系统原理研讨会上宣布了其内部的 Dynamo 数据库 (www.sosp2007.org
)。
在论文中(现已在 Werner Vogels 的网站上发布,网址为 www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf
),亚马逊描述了一种名为 Dynamo 的键值存储,用于支撑亚马逊一些最关键的内部服务,如 AWS 上的 S3。论文提出了一些关键概念,如键值存储、一致性哈希和向量时钟等,这些概念都在 Dynamo 中得到了实现。
因此,Dynamo 提供了一种替代 Bigtable 列式存储的大规模数据集存储方法,采用了一种根本不同的方法,利用了键值对的关联。
在接下来的几节中,我们将讨论各种类型的 NoSQL 技术,以及它们各自具有的特点,使它们在某些用例中最为优化。NoSQL 引领了数据库处理方式的范式转变,为大规模数据管理提供了此前不可行的、迫切需要的替代视角。
NoSQL 数据库
在讨论 NoSQL 类型和数据库时,我们将主要关注以下几个 NoSQL 数据库的特点:
-
内存数据库
-
列式数据库
-
面向文档的数据库
-
键值数据库
-
图数据库
-
其他 NoSQL 类型及总结
目前业界使用的大多数 NoSQL 类型都属于这些类别中的一种或多种。接下来的几节将讨论每种 NoSQL 提供的高级特性、主要优势,以及市场上符合这些类别的产品。
内存数据库
内存数据库,顾名思义,利用计算机内存,也就是 RAM,来存储数据集。在深入了解内存数据库如何工作之前,回顾一下数据如何在典型计算机中传输是很有意义的:
简单数据流计算机层次结构
如上图所示,数据从磁盘传输到内存,再到 CPU。这是对实际过程的高度概括,实际上,在某些情况下,CPU 不需要发送指令来从内存读取数据(例如,当数据已经存在于 CPU L2 缓存中——CPU 内部用于缓存数据的内存区域时),但基本上,CPU、RAM 和磁盘之间的过程是线性的。
存储在磁盘上的数据可以以一定的速率转移到内存中,这个速率取决于磁盘的 I/O(输入/输出)吞吐量。从磁盘访问数据大约需要 10-20 毫秒(ms)。虽然具体数字会根据数据的大小而有所不同,但磁盘寻址时间(磁盘找到数据位置的时间)大约为 10-15 毫秒。与此相比,从内存中获取数据的时间大约为 100 纳秒。最后,从 CPU L2 缓存读取数据大约需要 7 纳秒。
为了更直观地理解,磁盘访问时间 15 毫秒,即 15,000,000 纳秒,比从内存访问数据的时间要慢150,000 倍。换句话说,已经存在内存中的数据读取速度是磁盘的 15 万倍。这对于读取随机数据来说尤其真实。虽然读取顺序数据的时间可能不那么震撼,但仍然快了将近一个数量级。
如果将磁盘和内存比作汽车,那么内存的汽车将一路飞到月球,并且在磁盘汽车仅行驶不到两英里的时间内已经返回。这就是差距的巨大。
因此,从这一点得出结论,如果数据存储在内存中,尤其是在处理更大数据集的情况下,访问时间将显著降低,因此处理数据的时间(至少在 I/O 层面上)将大幅减少。
传统上,所有数据库中的数据都存储在磁盘上。随着互联网的到来,业界开始利用memcached,它通过 API 提供了一种将数据以键值对形式存储在内存中的方式。例如,MySQL 数据库通常使用 memcached API 将对象缓存到内存中,以优化读取速度并减少对主数据库(MySQL)的负载,这在过去和现在都很常见。
然而,随着数据量的增加,使用数据库和 memcached 方法的复杂性开始显现,专门设计用于存储内存数据(有时是同时存储在磁盘和内存中的数据库)的解决方案也在迅速发展。
因此,像 Redis 这样的内存数据库开始取代 memcached,成为驱动网站的快速缓存存储。以 Redis 为例,尽管数据会作为键值对保存在内存中,但它提供了将数据持久化到磁盘的选项。这使其与像 memcached 这样的仅限内存缓存解决方案有所不同。
向内存数据库迁移的主要驱动力可总结如下:
-
管理越来越多数据(如网页流量)的复杂性,例如,传统的 MySQL + memcached 组合
-
降低了内存成本,使得购买更大内存成为可能
-
整个行业向 NoSQL 技术的推动导致了更多的关注和社区参与,促进了新的创新数据库平台的发展。
-
在内存中更快速的数据操作为那些要求超高速、低延迟数据处理的场景提供了减少 I/O 开销的手段。
今天,业内提供内存能力的领先数据库选项包括:
开源 | 商业 |
---|---|
Redis | Kdb+ |
memcacheDB | Oracle TimesTen |
Aerospike | SAP HANA |
VoltDB | HP Vertica |
Apache Ignite | Altibase |
Apache Geode | Oracle Exalytics |
MonetDB | MemSQL |
请注意,这些数据库有些支持混合架构,数据既可以驻留在内存中,也可以存储在磁盘上。一般来说,数据会从内存转移到磁盘中以实现持久化。另外,需要注意的是,一些商业内存数据库提供了社区版,可以在符合各自许可证的前提下免费下载并使用。在这些情况下,它们既是开源的,又是商业的。
列式数据库
列式数据库自 90 年代以来就已经存在,但在 Google Bigtable 发布后(如前所述)才开始受到广泛关注。它们本质上是一种数据存储方式,相较于基于行/元组的存储方式,它优化了查询大量数据的速度和效率。
列式数据库的好处,或者更具体地说,将每列数据独立存储,可以通过一个简单的例子来说明。
假设有一个包含 1 亿个家庭地址和电话号码的表。还假设有一个简单查询,要求用户找到纽约州、奥尔巴尼市且建造年份大于 1990 年的家庭数量。我们将创建一个假设的表来说明按行查询与按列查询数据之间的差异。
硬件特性:
平均磁盘读取速度:每秒 200 MB
数据库特性:
表名:housedb
-
总行数 = 1 亿
-
状态为纽约(State NY)的总行数 = 两百万
-
状态为纽约(State NY)且城市为奥尔巴尼(City Albany)的总行数 = 10,000
-
状态为纽约(State NY)、城市为奥尔巴尼(City Albany)且建造年份大于 1990 年的总行数 = 500
数据大小:
假设每行数据的大小如下:
-
PlotNumber, YearBuilt 每个 = 8 字节 = 总计 16 字节
-
Owner、Address、State 和 City 每个 = 12 字节 = 总计 48 字节
-
每行的净大小(字节)= 16 + 48 = 64 字节
请注意,实际大小会更高,因为还有其他几个因素需要考虑,如索引、其他表的优化和相关的开销,我们为了简化起见这里不做考虑。
我们还假设列式数据库维持一个隐式的行索引,允许在每个列的向量中查询某些索引的数据。
以下表格显示前 4 条记录:
PlotNumber | Owner | Address | State | City | YearBuilt |
---|---|---|---|---|---|
1 | John | 1 Main St. | WA | Seattle | 1995 |
2 | Mary | 20 J. Ave. | NY | Albany | 1980 |
3 | Jane | 5 45^(th) St. | NY | Rye Brook | 2001 |
4 | John | 10 A. Blvd. | CT | Stamford | 2010 |
总的来说,该表有 1 亿条记录。最后几条记录如下所示:
PlotNumber | Owner | Address | State | City | YearBuilt |
---|---|---|---|---|---|
99999997 | Jim | 23 B. Lane | NC | Cary | 1995 |
99999998 | Mike | 5 L. Street | NY | Syracuse | 1993 |
99999999 | Tim | 10 A. Blvd. | NY | Albany | 2001 |
100000000 | Jack | 10 A. Blvd. | CT | Stamford | 2010 |
我们将在此数据集上运行的查询如下:
select * from housedb where State like 'NY' and City like 'Albany' and YearBuilt > 1990
场景 A:按行搜索
在第一个场景中,如果我们进行简单的逐行搜索,由于每列的数据并没有单独存储,而是扫描每行的数据,我们必须遍历以下内容:
1 亿 * 64 字节(每行数据的大小)= 64 亿字节 = 约 6000 MB 的数据
假设磁盘读取速度为 200 MBps,这意味着读取所有记录找到匹配项大约需要 6000 / 200 = 30 秒。
场景 B:逐列搜索
假设每列数据分别存储在代表各自列的单独文件中,我们将分别查看每个 Where 子句:
select * from housedb where State like 'NY' and City like 'Albany' and YearBuilt > 1990
- Where 子句部分 1:
where State like 'NY'
如前所述,State 列有 1 亿条,每条记录大小为 12 字节。
在这种情况下,我们只需要遍历以下内容:
1 亿 * 12 字节 = 12 亿字节 = 1000 MB 的数据。
在 200 MBps 的数据读取速率下,这将读取 200 MB 数据,读取该数据列需要 1000 / 200 = 5 秒。
这将返回 200 万条记录(如前所述,数据库特性)
- Where 子句部分 2:
City like 'Albany'
在前一步中,我们已将搜索窗口缩小为符合 State NY 条件的 200 万条记录。在第二个 Where 子句步骤中,现在我们不需要遍历所有 1 亿条记录。相反,我们只需查看符合条件的 200 万条记录,确定哪些属于 City Albany。
在这种情况下,我们只需要遍历以下内容:
200 万 * 12 字节 = 2400 万字节 = 约 20 MB 的数据。
在 200 MBps 的数据读取速率下,这将花费 0.1 秒。
这将返回 1 万条记录(如前所述,数据库特性)。
- Where 子句部分 3:
YearBuilt > 1990
在前一步中,我们进一步将搜索窗口缩小为符合 State NY 和 City Albany 条件的 1 万条记录。在此步骤中,我们将查询 YearBuilt 列中的 1 万条记录,找出哪些记录符合 YearBuilt > 1990 的条件。
在这种情况下,我们只需要遍历以下内容:
1 万 * 16 字节 = 16 万字节 = 约 150 KB 的数据。
在 200 MBps 的数据读取速率下,这将花费 0.00075 秒,我们可以将其四舍五入为零秒。
因此,查询数据的净时间为:
-
Where 子句部分 1:
where State like 'NY'
- 五秒 -
Where 子句部分 2:
City like 'Albany'
- 0.1 秒 -
Where 子句部分 3:
YearBuilt > 1990
- 零秒
读取数据的净时间 = 5.1 秒。
重要提示:请注意,实际的读取或更具体地说,扫描性能,取决于其他多个因素。元组的大小(行)、重建元组的时间(元组重建)、内存带宽(数据从主内存读取到 CPU 的速度,等等)、缓存行大小以及其他因素。在实践中,由于各种抽象层的存在,实际性能可能会更慢。此外,还有其他因素,如硬件架构和并行操作,这些也会影响整体性能,可能是积极的,也可能是负面的。这些话题属于更高级的内容,需要专门阅读。这里的分析仅专注于磁盘 I/O,这是整体性能中的一个关键方面。
上述示例演示了从查询性能或效率角度,基于数据大小,从列中存储的数据进行查询的好处。列式数据还提供了另一个好处,即它允许以列的形式存储可能具有任意模式的表。
考虑前表的前四行。例如,如果某些行缺少信息,这将导致列稀疏:
PlotNumber | Owner | Address | State | City | YearBuilt |
---|---|---|---|---|---|
1 | John | 1 Main St. | NULL | Seattle | 1995 |
2 | Mary | 20 J. Ave. | NY | NULL | NULL |
3 | Jane | NULL | NY | Rye Brook | NULL |
4 | John | 10 A. Blvd. | CT | NULL | NULL |
与其填充 NULL 值,不如创建一个名为 Column Family
的列族,名为 Complete_Address
,其中可以包含任意数量的键值对,仅对应那些有数据的字段:
PlotNumber | Owner | Complete_Address | YearBuilt | |
---|---|---|---|---|
1 | John | 地址:1 Main St. | 城市:Seattle | 1995 |
2 | Mary | 地址:20 J. Ave. | 州:NY | NULL |
3 | Jane | 州:NY | 城市:Rye Brook | NULL |
4 | John | 地址:10 A. Blvd. | 州:CT | NULL |
列式数据库提供的第三个、也是非常重要的好处是,能够基于三个关键字来检索数据:行键、列键和唯一标识每条记录的时间戳,这使得我们可以非常快速地访问相关数据。
例如,由于业主字段在财产(PlotNumber)出售时可能会发生变化,我们可以添加另一个字段来表示记录的日期;即该记录对应的日期。这样我们就能区分那些发生过所有权变更的房产,尽管其他数据保持不变:
PlotNumber | Owner | Address | State | City | YearBuilt | RecordDate |
---|---|---|---|---|---|---|
1 | John | 1 Main St. | WA | Seattle | 1995 | 2001.04.02 |
2 | Mary | 20 J. Ave. | NY | Albany | 1980 | 2007.05.30 |
3 | Jane | 5 45^(th) St. | NY | Rye Brook | 2001 | 2001.10.24 |
4 | John | 10 A. Blvd. | CT | Stamford | 2010 | 2003.07.20 |
由于每个 PlotNumber 可能有多条记录来适应所有权变更,我们现在可以定义三个键来唯一标识每条记录中的每个数据单元,具体如下:
-
行键:
PlotNumber
-
列键:列名
-
时间戳键:
RecordDate
每个记录表中的每个单元格将具有唯一的三值对,这使其与其他单元格区别开来。
如 Bigtable、Cassandra 等数据库采用这种方法,在大规模数据分析中既快速又高效。
以下是一些流行的列式数据库。请注意,可能会有重复项,因为数据库可以具有多种 NoSQL 属性(例如,既支持内存数据库也支持列式数据库):
开源 | 商业 |
---|---|
Apache Parquet | Kdb+ |
MonetDB | Teradata |
MariaDB | SAP HANA |
Druid | HP Vertica |
HBase | Oracle Exadata |
Apache Kudu | ParAccel |
Apache Arrow | Actian Vector |
面向文档的数据库
基于文档或文档导向的数据库成为了一种存储具有可变结构的数据的主要方式;也就是说,每条记录并不总是符合固定模式。此外,文档中可能同时包含结构化和非结构化部分。
结构化数据本质上是可以以表格格式存储的数据,例如电子表格中的数据。存储在 Excel 电子表格或 MySQL 表中的数据都属于结构化数据集。无法以严格的表格格式表示的数据,如书籍、音频文件、视频文件或社交网络消息,都被视为非结构化数据。因此,在面向文档的数据库中,我们主要处理结构化和非结构化文本数据。
解释数据的直观方式可以通过电话日志的例子来理解,数据可以同时包含结构化和非结构化文本。尽管随着数字数据存储的发展,这类日志变得越来越稀少,但我们中的许多人仍会记得曾经在口袋本上写下电话号码的时代。下图展示了我们如何在电话日志中存储数据:
地址簿(半结构化数据集)
在前面的例子中,以下字段可以被视为结构化字段:
-
姓名
-
地址
-
电话和传真
在地址字段下方有一条空白线,用户可以在此输入任意信息,例如:2015 年在会议上见过,现工作于 abc 公司。这本质上是日记维护者在输入具体信息时写下的注释。由于这种自由格式字段没有明确的特征,它也可以包含第二个电话号码、备用地址等信息,这类信息就属于非结构化文本。
此外,由于其他字段并不相互依赖,用户可以只填写地址而不填写电话号码,或者填写姓名和电话号码而不填写地址。
文档型数据库凭借其存储无模式数据的能力;即,存储不符合任何固定模式的数据,如具有固定数据类型的固定列,因此成为存储此类信息的合适平台。
因此,由于电话日记包含的数据量要小得多,在实践中我们可以将其存储在其他格式中,但当我们处理包含结构化和非结构化信息的大规模数据时,文档型数据集的必要性变得显而易见。
以电话日记为例,数据可以以 JSON 格式存储在文档型数据集中,具体如下:
(
{
"name": "John",
"address": "1 Main St.",
"notes": "Met at conference in 2015",
"tel": 2013249978,
},
{
"name": "Jack",
"address": "20 J. Blvd",
"notes": "Gym Instructor",
"tel": 2054584538,
"fax": 3482274573
}
)
JSON,即JavaScript Object Notation,是一种以便携文本格式表示数据的方式,采用键值对的形式。今天,JSON 数据在整个行业中无处不在,并已成为存储没有固定模式的数据的标准。它也是交换结构化数据的理想媒介,因此在此类数据集的使用中非常常见。
上面的插图提供了一个基本示例,传达了文档型数据库是如何工作的。因此,这是一个非常简单且希望直观的示例。实际上,文档型数据库如 MongoDB 和 CouchDB 用于存储数千兆字节和数万兆字节的信息。
例如,考虑一个存储用户及其电影偏好的网站。每个用户可能有多个他们观看过的电影、评分、推荐的电影、添加到愿望清单的电影以及其他类似的条目。在这种情况下,数据集中的各个元素是任意的,其中许多是可选的,并且许多可能包含多个值(例如,用户推荐的多个电影),此时使用 JSON 格式来捕捉信息变得最为理想。这就是文档型数据库提供一个优越且理想的平台来存储和交换数据的原因。
更具体地说,像 MongoDB 这样的数据库以 BSON 格式存储信息——BSON 是 JSON 文档的二进制版本,具有额外的优化,以适应数据类型、Unicode 字符和其他功能,从而提高基本 JSON 文档的性能。
存储在 MongoDB 中的 JSON 文档的一个更全面的例子可能是存储有关航空乘客的数据,包含与个人乘客相关的多个属性信息,例如:
{
"_id" : ObjectId("597cdbb193acc5c362e7ae96"),
"firstName" : "Rick",
"age" : 66,
"frequentFlyer" : (
"Delta"
),
"milesEarned" : (
88154
)
}
{
"_id" : ObjectId("597cdbb193acc5c362e7ae97"),
"firstName" : "Nina",
"age" : 53,
"frequentFlyer" : (
"Delta",
"JetBlue",
"Delta"
),
"milesEarned" : (
59226,
62025,
27493
)
}
每条记录都由_id
字段唯一标识,这使得我们能够直接查询与特定用户相关的信息,并无需跨数百万条记录进行查询即可检索数据。
今天,文档型数据库被用于存储各种各样的数据集。以下是一些例子:
-
日志文件和与日志文件相关的信息
-
文章和其他基于文本的出版物
-
地理定位数据
-
用户/用户帐户相关信息
-
更多适合文档/JSON 存储的用例
知名的文档导向数据库包括以下几种:
开源 | 商业 |
---|---|
MongoDB | Azure Cosmos DB |
CouchDB | OrientDB |
Couchbase Server | Marklogic |
键值数据库
键值数据库基于将数据结构化为与键对应的值对的原则。为了突显键值数据库的优势,回顾一下哈希映射的意义会很有帮助,哈希映射是计算机科学中常见的术语,用来指定一种独特的数据结构,该结构提供了常数时间查找键值对的能力。
一个直观的哈希表示例如下:
假设有 500 本书和五个书架,每个书架有五个书架层。书籍可以随意排列,但这样会使得找到特定的书籍变得极其困难,你可能需要翻阅数百本书才能找到你需要的那一本。一种分类书籍的方法是给每个书架分配字母范围,例如 A-E、F-J、K-O、P-T、U-Z,并利用书籍名称的首字母将其分配到特定的书架。然而,假设有很多书籍的名称以 A-E 字母开头,那么 A-E 这一类书架的书籍数量将大大超过其他书架。
更优雅的替代方法是为每本书分配一个值,并利用该值来确定书籍属于哪个书架或书架层。为了给每本书分配一个数字值,我们可以通过将书名中每个字母的对应数字相加,使用 1-26 的范围表示 A-Z 字母:
我们的简单哈希映射
由于我们有五个书架,每个书架有五个书架层,因此我们总共有 25 个书架层。为书籍分配特定书架的一种方法是将书籍的数字值通过求和标题中的字母并除以 26 来获得。任何数字除以 25 后会得到 0 到 25 之间的余数;也就是说,26 个独特的值。我们可以用这个值来为书籍分配一个特定的书架。这就变成了我们自创的哈希函数。
在 25 个书架中,每个书架现在都被分配了一个与数字值 0-25 相对应的数字值,最后一个书架被分配了 24 和 25 的值。例如,书架零分配给存储数字值除以 26 等于零的书籍,书架一分配给存储数字值除以 26 等于一的书籍,而书架 25 分配给存储数字值除以 26 等于 24 或 25 的书籍。
一个例子将有助于更具体地说明这一概念。
书名:哈姆雷特
标题的数字值:
哈希值
数字值的总和 = 8 + 1 + 13 + 12 + 5 + 20 = 59
将数字除以 26 = 2,余数为 7
因此,这本书被分配到第七个书架层。
我们本质上找到了一种有条理地为每本书分配书架的方法,由于我们有一个固定的规则,当新的借书请求到达时,我们几乎可以瞬间找到它,因为我们知道与书籍对应的书架。
上述方法展示了哈希的概念,实际上,我们会使用一个哈希函数来为每本书找到一个唯一的值,并且假设我们可以得到任意数量的书架和插槽来放置书籍,我们可以直接使用书本的数字值来识别它应该放在的书架上。
会有这种情况,即两本书有相同的数字值,在这种情况下,我们可以将书堆叠在对应数字的槽位中。在计算机科学中,这种多个值对应一个键的现象称为冲突,在这种情况下,我们可以通过列表或类似的数据类型来分配多个项目。
在实际应用中,我们需要处理的项比简单的书本示例要复杂得多。通常,我们会使用更复杂的哈希函数,降低冲突的机会,并相应地分配键值对。数据会存储在内存中的连续数组中,因此,当某个键的请求到达时,我们可以通过使用哈希函数来确定数据所在内存位置,从而瞬间找到该值。
因此,使用键值对存储数据可以非常强大,因为检索与某个键对应的信息的时间非常快,因为不需要在长长的列表中查找匹配的键。
键值数据库采用相同的原则,为每条记录分配唯一的键,并将与每个键对应的数据存储在相应的位置。在我们对 MongoDB 的讨论中,我们看到记录被分配了一个由每条记录中的 _id
值标识的键。在实践中,我们可以使用这个值以常数时间检索对应的数据。
如前所述,memcached 曾是用于存储键值对的首选方法,适用于需要非常快速访问频繁使用数据的 web 服务。实际上,它作为一个内存缓存,用于存储临时信息。随着 NoSQL 数据库的出现,新平台扩展了 memcached 限制性用例的应用。像 Redis 这样的解决方案不仅提供了在内存中以键值对存储数据的能力,还提供了将数据持久化到磁盘的能力。此外,这些键值存储还支持水平扩展,使得可以将键值对分布到成百上千的节点上。
键值存储的缺点是数据不能像标准数据库那样灵活地进行查询,后者支持多层次的索引和更丰富的 SQL 命令集。然而,常数时间查找的好处意味着,对于需要键值结构的使用场景,几乎没有其他解决方案能在性能和效率上与其相匹配。例如,一个拥有成千上万用户的购物网站可以将用户资料信息存储在键值数据库中,并通过应用一个与用户 ID 相对应的哈希函数,轻松查找单个用户的信息。
今天,键值数据库使用多种方法存储数据:
-
SSTables:一种已排序的键值对文件,表示为字符串(并直接映射到谷歌文件系统(GFS))。
-
B 树:平衡树,其中值是通过遍历叶子/节点来识别的。
-
布隆过滤器:一种更优化的键值方法,适用于键数量较多的情况。它使用多个哈希函数将对应于键的数组中的位值设置为 1。
-
分片:一种将数据分布到多个节点上的过程。
知名的键值数据库包括:
开源 | 商业 |
---|---|
Redis | Amazon DynamoDB |
Cassandra | Riak |
Aerospike | Oracle NoSQL |
Apache Ignite | Azure Cosmos DB |
Apache Accumulo | Oracle Berkeley DB |
图形数据库
图形数据库 提供了一种高效的数据表示方式,记录之间具有相互关系。典型的例子有你的社交网络好友列表、LinkedIn 联系人、Netflix 电影订阅者。通过利用优化的算法在基于树/图的数据结构上进行搜索,图形数据库能够以一种与其他 NoSQL 解决方案不同的方式定位信息。在这样的结构中,离散的信息和属性被表示为叶子、边缘和节点。
下图显示了一个非典型的网络表示,可以使用图形数据库查询来发现或查找复杂的相互关系。在实际应用中,生产环境中的图形数据库包含数百万个节点:
图形数据库
尽管图形数据库平台不像其他类型的 NoSQL 数据库那样普及,但它们在业务关键领域有广泛应用。例如,信用卡公司使用图形数据库,通过查询数百万个数据点来发现可能感兴趣的新产品,评估与其他拥有类似消费模式的持卡人的购买行为。社交网络网站使用图形数据库来计算相似度分数、提供好友推荐及其他相关指标。
知名的图形数据库包括以下几种:
开源 | 商业 |
---|---|
Apache Giraph | Datastax Enterprise Graph |
Neo4j | Teradata Aster |
JanusGraph | Oracle Spatial and Graph |
Apache Ignite |
其他 NoSQL 类型和数据库类型总结
本节描述了一些当前常见的 NoSQL 范式。还有一些新兴平台,具有其自身的优势和独特特征。以下是一些平台的简要概述:
类型 | 特性 |
---|---|
面向对象数据库 | 利用面向对象编程概念存储作为对象表示的数据的数据库。 |
云数据库 | 云服务商如亚马逊、微软和谷歌提供的数据库,只能在各自的云平台上使用,例如亚马逊 Redshift、Azure SQL 数据库和谷歌 BigQuery。 |
GPU 数据库 | 数据库世界中较新的一个成员,利用 GPU(图形处理单元)卡来处理数据。例子包括 MapD、Kinetica 等。 |
FPGA 加速数据库 | 随着英特尔即将发布带有嵌入式 FPGA 的新芯片,百度等公司已经开始开发利用 FPGA 处理能力的 FPGA 加速系统,以提升 SQL 查询性能。 |
流处理/物联网数据库 | 针对流数据处理进行优化的数据库,或更广泛地说是平台,通常用于处理来自医疗设备和传感器的数据。Apache Storm 就是这种系统的一个非常流行的例子。 |
经常有人问,是否存在一种 NoSQL 数据库,适用于所有使用场景。虽然一些数据库具有支持多个 NoSQL 系统元素的特性(通常称为多模式数据库),但在实际应用中,能够在广泛的使用场景中表现良好的单一解决方案是非常罕见的。在实际的使用案例中,公司通常会实施多个解决方案以满足数据挖掘的需求。在下一节中,我们将通过实际数据集进行一些动手练习,使用本章讨论的 NoSQL 解决方案。
使用 MongoDB 分析诺贝尔奖得主数据
在第一个练习中,我们将使用MongoDB,这是领先的面向文档的数据库之一,用于分析从 1902 年至今的诺贝尔奖得主数据。MongoDB 提供了一个简单直观的界面来处理 JSON 文件。正如之前所讨论的,JSON 是一种灵活的格式,允许以结构化方式表示数据。
JSON 格式
请参考以下表格:
名 | 姓 | 信息 |
---|---|---|
John | 15 | 学科:历史,成绩 B |
Jack | 18 | 学科:物理,成绩 A |
Jill | 17 | 学科:物理,成绩 A+ |
信息字段包含一个列,其中包含多项值,按学科和成绩分类。这种包含多重数据的列也称为具有嵌套数据的列。
可移植性一直是将数据从一个系统转移到另一个系统的一个重要方面。通常,ODBC 连接器用于在数据库系统之间传输数据。另一个常见的格式是 CSV 文件,其中数据以逗号分隔值的形式表示。CSV 文件最适合结构化数据,且不包含更复杂的数据结构,如嵌套值。在这种情况下,JSON 提供了一种最佳的结构化方式来捕捉和保存信息,使用键值对语法。
在 JSON 表示中,表格可以按如下方式定义:
(
{
"Firstname":"John",
"Age":15,
"Information":{
"Subject":"History",
"Grade":"B"
}
},
{
"Firstname":"Jack",
"Age":18,
"Information":{
"Subject":"Physics",
"Grade":"A"
}
},
{
"Firstname":"Jill",
"Age":17,
"Information":{
"Subject":"Physics",
"Grade":"A+"
}
}
)
请注意,Information
键包含两个键,Subject
和 Grade
,每个键都有相应的值。
今天,大多数产品开发者和供应商都支持摄取 JSON 格式的数据。此外,由于复杂关系能够以简单的方式表达并以文本格式交换,JSON 在全球开发者社区中已经变得非常流行。
MongoDB 以 JSON 格式捕捉数据。它内部将数据存储为 BSON——JSON 数据的优化二进制表示形式。
安装和使用 MongoDB
MongoDB 支持所有主要平台,如 Windows、Linux 和 OS X 平台。
MongoDB 的安装细节可以在其官方网站上找到,网址是 docs.mongodb.com/manual/installation/
。请注意,我们将使用 MongoDB 社区版。
对于本练习,我们将重用来自 Cloudera Hadoop 发行版虚拟机的 Linux CentOS 环境。
然而,本练习与安装 MongoDB 的平台无关。一旦安装完成,您可以在任何其他支持的平台上执行本章中指示的命令。如果您有一台独立的 Linux 机器,您也可以使用它。
我们将访问 MongoDB 的一些常见语义,并下载两个数据集,计算按大洲分组的诺贝尔奖最高得奖人数。关于诺贝尔奖得主的完整数据转储可以从 nobelprize.org 获取。数据包含所有得主的主要属性。我们希望将这些数据与相应国家的人口统计信息结合,以提取更多有趣的分析信息:
- 下载 MongoDB:MongoDB 可以从
www.mongodb.com/download-center#community
下载。
为了确定适用的版本,我们检查了 CDH 虚拟机上安装的 Linux 版本:
(cloudera@quickstart ~)$ lsb_release -a
LSB Version: :base-4.0-amd64:base-4.0-noarch:core-4.0-amd64:core-4.0-noarch
Distributor ID: CentOS
Description: CentOS release 6.7 (Final)
Release: 6.7
Codename: Final
- 根据这些信息,我们必须使用 MongoDB 的 CentOS 版本,并按照
docs.mongodb.com/manual/tutorial/install-mongodb-on-red-hat/
上的说明安装软件,如下所示:
The first step involved adding the repo as follows. Type in sudo nano /etc/yum.repos.d/mongodb-org-3.4.repo on the command line and enter the text as shown.
(root@quickstart cloudera)# sudo nano /etc/yum.repos.d/mongodb-org-3.4.repo
### Type in the information shown below and press CTRL-X
### When prompted to save buffer, type in yes
(mongodb-org-3.4)
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.4/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-3.4.asc
以下截图展示了文件的内容:
设置 MongoDB 仓库
如下截图所示,输入Y
表示是:
保存 .repo 文件
按照图示保存文件。这将允许我们安装mongo-db
:
编写并保存 .repo 文件
# Back in terminal, type in the following
(cloudera@quickstart ~)$ sudo yum install -y mongodb-org
(...)
Installing:
mongodb-org x86_64 3.4.6-1.el6 mongodb-org-3.4 5.8 k
Installing for dependencies:
mongodb-org-mongos x86_64 3.4.6-1.el6 mongodb-org-3.4 12 M
mongodb-org-server x86_64 3.4.6-1.el6 mongodb-org-3.4 20 M
mongodb-org-shell x86_64 3.4.6-1.el6 mongodb-org-3.4 11 M
mongodb-org-tools x86_64 3.4.6-1.el6 mongodb-org-3.4 49 M
Transaction Summary
=====================================================================
Install 5 Package(s)
Total download size: 91 M
Installed size: 258 M
Downloading Packages:
(1/5): mongodb-org-3.4.6-1.el6.x86_64.rpm | 5.8 kB 00:00
(...)
Installed:
mongodb-org.x86_64 0:3.4.6-1.el6
Dependency Installed:
mongodb-org-mongos.x86_64 0:3.4.6-1.el6 mongodb-org-server.x86_64 0:3.4.6-1.el6
mongodb-org-shell.x86_64 0:3.4.6-1.el6 mongodb-org-tools.x86_64 0:3.4.6-1.el6
Complete!
### Attempting to start mongo without first starting the daemon will produce an error message ###
### You need to start the mongo daemon before you can use it ###
(cloudera@quickstart ~)$ mongo MongoDB shell version v3.4.6
connecting to: mongodb://127.0.0.1:27017
2017-07-30T10:50:58.708-0700 W NETWORK (thread1) Failed to connect to 127.0.0.1:27017, in(checking socket for error after poll), reason: Connection refused
2017-07-30T10:50:58.708-0700 E QUERY (thread1) Error: couldn't connect to server 127.0.0.1:27017, connection attempt failed :
connect@src/mongo/shell/mongo.js:237:13
@(connect):1:6
exception: connect failed
### The first step is to create the MongoDB dbpath - this is where MongoDB will store all data
### Create a folder called, mongodata, this will be the mongo dbpath ###
(cloudera@quickstart ~)$ mkdir mongodata
### Start mongod ###
(cloudera@quickstart ~)$ mongod --dbpath mongodata
2017-07-30T10:52:17.200-0700 I CONTROL (initandlisten) MongoDB starting : pid=16093 port=27017 dbpath=mongodata 64-bit host=quickstart.cloudera
(...)
2017-07-30T10:52:17.321-0700 I INDEX (initandlisten) build index done. scanned 0 total records. 0 secs
2017-07-30T10:52:17.321-0700 I COMMAND (initandlisten) setting featureCompatibilityVersion to 3.4
2017-07-30T10:52:17.321-0700 I NETWORK (thread1) waiting for connections on port 27017
打开一个新的终端并下载如以下截图所示的 JSON 数据文件:
从 Mac OS X 的 Terminal 应用中选择 Open Terminal
# Download Files
# laureates.json and country.json ###
# Change directory to go to the mongodata folder that you created earlier
(cloudera@quickstart ~)$ cd mongodata
(cloudera@quickstart mongodata)$ curl -o laureates.json "http://api.nobelprize.org/v1/laureate.json"
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 428k 0 428k 0 0 292k 0 --:--:-- 0:00:01 --:--:-- 354k
### Clean the file laureates.json
### Delete content upto the first ( on the first line of the file
### Delete the last } character from the file
### Store the cleansed dataset in a file called laureates.json
注意,文件需要稍作修改。代码如下图所示:
修改我们应用程序的 .json 文件
(cloudera@quickstart mongodata)$ cat laureates.json | sed 's/^{"laureates"://g' | sed 's/}$//g' > mongofile.json
### Import the file laureates.json into MongoDB
### mongoimport is a utility that is used to import data into MongoDB
### The command below will import data from the file, mongofile.json
### Into a db named nobel into a collection (i.e., a table) called laureates
(cloudera@quickstart mongodata)$ mongoimport --jsonArray --db nobel --collection laureates --file mongofile.json 2017-07-30T11:06:35.228-0700 connected to: localhost
2017-07-30T11:06:35.295-0700 imported 910 documents
为了将laureate.json
中的数据与特定国家的信息结合,我们需要从geonames.org下载countryInfo.txt
。接下来,我们将下载本次练习所需的第二个文件country.json
。我们将使用laureates.json
和country.json
来进行练习。
### country.json
:从www.geonames.org
下载(许可证:creativecommons.org/licenses/by/3.0/
)。修改 JSON 字符串的开始和结束部分,以便按以下所示导入到 MongoDB:
# The file country.json contains descriptive information about all countries
# We will use this file for our tutorial
### Download country.json
(cloudera@quickstart mongodata)$ curl -o country.json "https://raw.githubusercontent.com/xbsd/packtbigdata/master/country.json"
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 113k 100 113k 0 0 360k 0 --:--:-- --:--:-- --:--:-- 885k
### The file, country.json has already been cleaned and can be imported directly into MongoDB
(cloudera@quickstart mongodata)$ mongoimport --jsonArray --db nobel --collection country --file country.json 2017-07-30T12:10:35.554-0700 connected to: localhost
2017-07-30T12:10:35.580-0700 imported 250 documents
### MONGO SHELL ###
(cloudera@quickstart mongodata)$ mongo MongoDB shell version v3.4.6
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.6
Server has startup warnings:
(...)
2017-07-30T10:52:17.298-0700 I CONTROL (initandlisten)
### Switch to the database nobel using the 'use <databasename>' command
> use nobel switched to db nobel
### Show all collections (i.e., tables)
### This will show the tables that we imported into MongoDB - country and laureates
> show collections country
laureates
>
### Collections in MongoDB are the equivalent to tables in SQL
### 1\. Common Operations
### View collection statistics using db.<dbname>.stats()
> db.laureates.stats()
"ns" : "nobel.laureates", # Name Space
"size" : 484053, # Size in Bytes
"count" : 910, # Number of Records
"avgObjSize" : 531, # Average Object Size
"storageSize" : 225280, # Data size
# Check space used (in bytes)
> db.laureates.storageSize() 225280
# Check number of records
> db.laureates.count() 910
### 2\. View data from collection
###
### There is an extensive list of commands that can be used in MongoDB. As such discussing them all is outside the scope of the text. However, a few of the familiar commands have been given below as a marker to help the reader get started with the platform.
### See first record for laureates using findOne()
### findOne() will show the first record in the collection
> db.laureates.findOne()
{
"_id" : ObjectId("597e202bcd8724f48de485d4"),
"id" : "1",
"firstname" : "Wilhelm Conrad",
"surname" : "Röntgen",
"born" : "1845-03-27",
"died" : "1923-02-10",
"bornCountry" : "Prussia (now Germany)",
"bornCountryCode" : "DE",
"bornCity" : "Lennep (now Remscheid)",
"diedCountry" : "Germany",
"diedCountryCode" : "DE",
"diedCity" : "Munich",
"gender" : "male",
"prizes" : (
{
"year" : "1901",
"category" : "physics",
"share" : "1",
"motivation" : "\"in recognition of the extraordinary services he has rendered by the discovery of the remarkable rays subsequently named after him\"",
"affiliations" : (
{
"name" : "Munich University",
"city" : "Munich",
"country" : "Germany"
}
)
}
)
}
### See all records for laureates
> db.laureates.find()
{ "_id" : ObjectId("597e202bcd8724f48de485d4"), "id" : "1", "firstname" : "Wilhelm Conrad", "surname" : "Röntgen", "born" : "1845-03-27", "died" : "1923-02-10", "bornCountry" : "Prussia (now Germany)", "bornCountryCode" : "DE", "bornCity" : "Lennep (now Remscheid)"
(...)
...
### MongoDB functions accept JSON formatted strings as parameters to options
### Some examples are shown below for reference
### Query a field - Find all Nobel Laureates who were male
> db.laureates.find({"gender":"male"})
(...)
{ "_id" : ObjectId("597e202bcd8724f48de485d5"), "id" : "2", "firstname" : "Hendrik Antoon", "surname" : "Lorentz", "born" : "1853-07-18", "died" : "1928-02-04", "bornCountry" : "the Netherlands", "bornCountryCode" : "NL", "bornCity" : "Arnhem", "diedCountry" : "the Netherlands", "diedCountryCode" : "NL", "gender" : "male", "prizes" : ( { "year" : "1902", "category" : "physics", "share" : "2", "motivation" : "\"in recognition of the extraordinary service they rendered by their researches into the influence of magnetism upon radiation phenomena\"", "affiliations" : ( { "name" : "Leiden University", "city" : "Leiden", "country" : "the Netherlands" } ) } ) }
(...)
查询字段 - 查找所有出生在美国并获得诺贝尔物理奖的诺贝尔奖得主。注意,这里有一个嵌套字段(如所示,category 位于 prizes 下)。因此,我们将使用点符号(dot notation),如下面的图像所示。
图像说明了category
,一个嵌套字段:
嵌套 JSON 字段
> db.laureates.find({"bornCountryCode":"US", "prizes.category":"physics", "bornCity": /Chicago/})
{ "_id" : ObjectId("597e202bcd8724f48de48638"), "id" : "103", "firstname" : "Ben Roy", "surname" : "Mottelson", "born" : "1926-07-09", "died" : "0000-00-00", "bornCountry" : "USA", "bornCountryCode" : "US", "bornCity" : "Chicago, IL",
...
### Check number of distinct prize categories using distinct
> db.laureates.distinct("prizes.category") (
"physics",
"chemistry",
"peace",
"medicine",
"literature",
"economics"
)
### Using Comparison Operators
### MongoDB allows users to chain multiple comparison operators
### Details on MongoDB operators can be found at: https://docs.mongodb.com/manual/reference/operator/
# Find Nobel Laureates born in either India or Egypt using the $in operator
> db.laureates.find ( { bornCountryCode: { $in: ("IN","EG") } } )
{ "_id" : ObjectId("597e202bcd8724f48de485f7"), "id" : "37", "firstname" : "Sir Chandrasekhara Venkata", "surname" : "Raman", "born" : "1888-11-07", "died" : "1970-11-21", "bornCountry" : "India", "bornCountryCode" : "IN", "bornCity" : "Tiruchirappalli", "diedCountry" : "India", "diedCountryCode" : "IN", "diedCity" : "Bangalore", "gender" : "male", "prizes" : ( { "year" : "1930", "category" : "physics", "share" : "1", "motivation" : "\"for his work on the scattering of light and for the discovery of the effect named after him\"", "affiliations" : ( { "name" : "Calcutta University", "city" : "Calcutta", "country" : "India" } ) } ) }
...
### Using Multiple Comparison Operators
### Find Nobel laureates who were born in either US or China and won prize in either Physics or Chemistry using the $and and $or operator
> db.laureates.find( {
$and : ({ $or : ( { bornCountryCode : "US" }, { bornCountryCode : "CN" } ) },
{ $or : ( { "prizes.category" : "physics" }, { "prizes.category" : "chemistry" } ) }
)
} )
{ "_id" : ObjectId("597e202bcd8724f48de485ee"), "id" : "28", "firstname" : "Robert Andrews", "surname" : "Millikan", "born" : "1868-03-22", "died" : "1953-12-19", "bornCountry" : "USA", "bornCountryCode" : "US", "bornCity" : "Morrison, IL", "diedCountry" : "USA", "diedCountryCode" : "US", "diedCity" : "San Marino, CA", "gender" : "male", "prizes" : ( { "year" : "1923", "category" : "physics", "share" : "1", "motivation" : "\"for his work on the elementary charge of electricity and on the photoelectric effect\"", "affiliations" : ( { "name" : "California Institute of Technology (Caltech)", "city" : "Pasadena, CA", "country" : "USA" } ) } ) }
...
### Performing Aggregations is one of the common operations in MongoDB queries
### MongoDB allows users to perform pipeline aggregations, map-reduce aggregations and single purpose aggregations
### Details on MongoDB aggregations can be found at the URL
### https://docs.mongodb.com/manual/aggregation/
### Aggregation Examples
### Count and aggregate total Nobel laureates by year and sort in descending order
### Step 1: Use the $group operator to indicate that prize.year will be the grouping variable
### Step 2: Use the $sum operator (accumulator) to sum each entry under a variable called totalPrizes
### Step 3: Use the $sort operator to rank totalPrizes
> db.laureates.aggregate(
{$group: {_id: '$prizes.year', totalPrizes: {$sum: 1}}},
{$sort: {totalPrizes: -1}}
);
{ "_id" : ( "2001" ), "totalPrizes" : 15 }
{ "_id" : ( "2014" ), "totalPrizes" : 13 }
{ "_id" : ( "2002" ), "totalPrizes" : 13 }
{ "_id" : ( "2000" ), "totalPrizes" : 13 }
(...)
### To count and aggregate total prizes by country of birth
> db.laureates.aggregate(
{$group: {_id: '$bornCountry', totalPrizes: {$sum: 1}}},
{$sort: {totalPrizes: -1}}
);
{ "_id" : "USA", "totalPrizes" : 257 }
{ "_id" : "United Kingdom", "totalPrizes" : 84 }
{ "_id" : "Germany", "totalPrizes" : 61 }
{ "_id" : "France", "totalPrizes" : 51 }
...
### MongoDB also supports PCRE (Perl-Compatible) Regular Expressions
### For more information, see https://docs.mongodb.com/manual/reference/operator/query/regex
### Using Regular Expressions: Find count of nobel laureates by country of birth whose prize was related to 'radiation' (as indicated in the field motivation under prizes)
> db.laureates.aggregate(
{$match : { "prizes.motivation" : /radiation/ }},
{$group: {_id: '$bornCountry', totalPrizes: {$sum: 1}}},
{$sort: {totalPrizes: -1}}
);
{ "_id" : "USA", "totalPrizes" : 4 }
{ "_id" : "Germany", "totalPrizes" : 2 }
{ "_id" : "the Netherlands", "totalPrizes" : 2 }
{ "_id" : "United Kingdom", "totalPrizes" : 2 }
{ "_id" : "France", "totalPrizes" : 1 }
{ "_id" : "Prussia (now Russia)", "totalPrizes" : 1 }
#### Result: We see that the highest number of prizes (in which radiation was mentioned as a key-word) was the US
### Interestingly, we can also do joins and other similar operations that allow us to combine the data with other data sources
### In this case, we'd like to join the data in laureates with the data from country information obtained earlier
### The collection country contains many interesting fields, but for this exercise, we will show how to find the total number of nobel laureates by continent
### The Left Join
### Step 1: Use the $lookup operator to define the from/to fields, collection names and assign the data to a field named countryInfo
### We can join the field bornCountryCode from laureates with the field countryCode from the collection country
> db.laureates.aggregate(
{$lookup: { from: "country", localField: "bornCountryCode", foreignField: "countryCode", as: "countryInfo" }})
{ "_id" : ObjectId("597e202bcd8724f48de485d4"), "id" : "1", "firstname" : "Wilhelm Conrad", "surname" : "Röntgen", "born" : "1845-03-27", "died" : "1923-02-10", "bornCountry" : "Prussia (now Germany)", "bornCountryCode" : "DE", "bornCity" : "Lennep (now (..) "country" : "Germany" } ) } ), "countryInfo" : ( { "_id" : ObjectId("597e2f2bcd8724f48de489aa"), "continent" : "EU", "capital" : "Berlin", "languages" : "de", "geonameId" : 2921044, "south" : 47.2701236047002, ...
### With the data joined, we can now perform combined aggregations
### Find the number of Nobel laureates by continent
> db.laureates.aggregate(
{$lookup: { from: "country", localField: "bornCountryCode", foreignField: "countryCode", as: "countryInfo" }},
{$group: {_id: '$countryInfo.continent', totalPrizes: {$sum: 1}}},
{$sort: {totalPrizes: -1}}
);
... );
{ "_id" : ( "EU" ), "totalPrizes" : 478 }
{ "_id" : ( "NA" ), "totalPrizes" : 285 }
{ "_id" : ( "AS" ), "totalPrizes" : 67 }
...
This indicates that Europe has by far the highest number of Nobel Laureates.
还有许多其他操作可以执行,但前一部分的目的是以简单的使用案例来高层次地介绍 MongoDB。本章中提供的网址包含有关使用 MongoDB 的更深入信息。
业界还有多个可视化工具,用于与 MongoDB 集合中的数据进行交互和可视化,采用的是点选式界面。一个简单而强大的工具 MongoDB Compass 可以从 www.mongodb.com/download-center?filter=enterprise?jmp=nav#compass.
获取。
导航到先前提到的网址并下载适合您环境的 Compass 版本:
下载 MongoDB Compass
安装后,您将看到欢迎屏幕。点击“Next”直到看到主控制面板:
MongoDB Compass 截图
点击“Performance”查看 MongoDB 的当前状态:
MongoDB 性能屏幕
通过点击左侧边栏单词旁边的箭头来扩展诺贝尔数据库。你可以点击并拖动条形图的不同部分,并运行临时查询。如果你想全面了解数据集,而不必手动运行所有查询,这非常有用,如下图所示:
在 MongoDB Compass 中查看我们的文件
使用真实数据追踪医生支付情况
医生和医院都接受来自各种外部组织的支付,例如制药公司雇佣销售代表不仅教育医生了解他们的产品,还提供礼品或现金等支付。理论上,支付给医生的礼品或款项并不旨在影响他们的处方行为,制药公司采取谨慎措施来确保支付给医疗服务提供者的款项受到监督和制衡。
2010 年,奥巴马总统签署的平价医疗法案(ACA),也被大众称为“奥巴马医改”,正式生效。与 ACA 一起,另一项名为阳光法案的立法也要求制药公司及其他组织必须报告所有具有货币价值的项目(无论是直接还是间接)。虽然过去也有类似规定,但这些规则极少公开可用。阳光法案通过公开所有支付给医生的详细记录,带来了前所未有的透明度,特别是在医疗服务提供者涉及金钱交易时。
这些数据可以在 CMS 开放支付网站上自由获取,网址为openpaymentsdata.cms.gov
。
该网站提供了查询数据的接口,但没有任何用于进行大规模数据聚合的功能。例如,如果用户想查找康涅狄格州(CT)的总支付金额,默认的网页工具没有简单便捷的方式来运行此查询。提供该功能的 API 是可用的,但需要一定的熟悉程度和技术知识才能有效使用。有第三方产品提供这类功能,但大多数情况下它们价格昂贵,并且最终用户无法根据自己的需求修改软件。
在本教程中,我们将开发一个快速且高效的基于网页的应用程序,用于分析 2016 年支付给医生的数千万条记录。我们将结合使用 NoSQL 数据库、R 和 RStudio 来创建最终产品——通过该产品,最终用户可以实时查询数据库。
我们将使用以下技术来开发该应用程序:
-
Kdb+ NoSQL 数据库:
www.kx.com
-
R
-
RStudio
对于本教程,我将使用我们为 Hadoop 练习下载的 VM 镜像。工具也可以安装在 Windows、Mac 和其他 Linux 机器上。选择虚拟机主要是为了提供一个一致且不依赖于操作系统的本地平台。
安装 kdb+、R 和 RStudio
提供了一个 Packt Data Science VM 下载,其中包含本章所需的所有必要软件。然而,如果你更喜欢在本地计算机上安装软件,可以参考以下部分的说明。你可以跳过安装部分,直接进入开发开放支付应用部分。
安装 kdb+
kdb+ 是一个时间序列、内存中、列式数据库,已在金融行业使用近 20 年。它是执行大规模数据挖掘时最快的数据库平台之一,但由于它几乎仅被对冲基金和投资银行使用,因此不像其他 NoSQL 工具那样为人所知。特别是,由于其处理海量数据的速度和低开销,它被高频交易的算法交易部门广泛使用。
使用 kdb+,在笔记本电脑上分析数千万甚至上亿条记录相对简单。主要的限制在硬件层面——例如可用的内存、磁盘空间和 CPU,这些都是处理数据的关键因素。在本教程中,我们将安装可供非商业使用的免费 32 位版本 kdb+。
kdb+不是开源的,但学术机构可以通过写信至[email protected]
免费使用 64 位许可证。
kdb+有一些关键特性,使其非常适合大规模数据分析:
-
低级实现:该数据库使用 C 语言编写,因此减少了大多数现代 NoSQL 数据库常见的性能问题,这些数据库通常依赖 Java,并实现了多个抽象层来提供处理能力。
-
架构简单性:整个 kdb+数据库的二进制文件约为 500-600KB,只有一首 MP3 歌曲的一小部分,即使在拨号连接下也能轻松下载。
-
MapReduce:该数据库实现了一个内部的 MapReduce 过程,允许查询在多个核心上同时执行。
-
无需安装:该数据库不需要系统级权限,用户可以在大多数系统中直接使用其用户账户启动 kdb+。
-
企业级准备:该数据库已经使用了近 20 年,是一个非常成熟的产品,广泛应用于全球企业环境中,分析高频交易数据等应用。
-
接口广泛:该数据库提供多种语言的接口,如 C、C++、C#、Java、R、Python、MATLAB 等,便于与现有软件集成。
安装 kdb+的步骤如下所示。请注意,如果你使用的是 Packt Data Science 虚拟机,则无需额外安装。以下说明主要是为那些希望全新安装该软件的用户提供的。
尽管说明是针对 Linux 的,但对于 Windows 和 Mac,安装过程同样非常简单。这里的说明是针对 Packt Data Science 虚拟机的。关于如何下载 Packt Data Science 虚拟机的说明,已在第三章,The Analytics Toolkit中提供。
- 访问www.kx.com,并从“Connect with us”菜单项中点击Download下拉选项。你也可以直接访问下载页面
kx.com/download/
:
Kx 系统主页
下载页面如下所示:
下载 KDB+
-
在下一页面点击下载按钮。
-
你将被带到
kx.com/download/
页面,那里你可以在同意条款后选择你需要的下载版本。如果你使用的是虚拟机,下载Linux-86 版本。 -
选择“保存文件”将下载的 ZIP 文件保存在你的下载文件夹中:
KDB+ 32 位许可条款
转到文件下载的位置,将 ZIP 文件复制到你的主目录下:
KDB+ ZIP 文件下载
对于 Mac 或 Linux 系统,这将是~/
文件夹。在 Windows 中,将 ZIP 文件复制到C:\
下并解压以提取q
文件夹。以下说明主要适用于基于 Linux 的系统:
$ cd Downloads/ # cd to the folder where you have downloaded the zip file
$ unzip linuxx86.zip
Archive: linuxx86.zip
inflating: q/README.txt
inflating: q/l32/q
inflating: q/q.q
inflating: q/q.k
inflating: q/s.k
inflating: q/trade.q
inflating: q/sp.q
$ mv ~/Downloads/q ~/
$ cd ~/q
$ cd l32
$ ./q KDB+ 3.5 2017.06.15 Copyright (C) 1993-2017 Kx Systems
l32/ 1()core 3830MB cloudera quickstart.cloudera 10.0.2.15 NONEXPIRE
Welcome to kdb+ 32bit edition
For support please see http://groups.google.com/d/forum/personal-kdbplus
Tutorials can be found at http://code.kx.com/wiki/Tutorials
To exit, type \\
To remove this startup msg, edit q.q
q)\\
/NOTE THAT YOU MAY NEED TO INSTALL THE FOLLOWING IF YOU GET AN ERROR MESSAGE STATING THAT THE FILE q CANNOT BE FOUND. IN THAT CASE, INSTALL THE REQUISITE SOFTWARE AS SHOWN BELOW
$ sudo dpkg --add-architecture i386
$ sudo apt-get update
$ sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386
安装 R
应用程序的前端将使用 R 开发。安装 R 有三种选择,以完成本教程:
-
如果你已经从第三章安装了 Microsoft R,并且将使用本地机器进行本教程,则无需进行进一步安装。
-
或者,如果你将使用 Packt Data Science Virtualbox 虚拟机,则无需进行进一步安装。
-
如果你计划从官方 R 网站安装 R,可以从列在
cran.r-project.org/mirrors.html
的任何下载站点(镜像站)下载二进制文件:
安装开源 R
安装 RStudio
我们将使用 RStudio 来构建我们的基于 Web 的应用程序。你可以从网站下载 RStudio 的二进制文件,或者通过终端安装它。RStudio 有两个版本——RStudio 桌面版和 RStudio 服务器版。两个版本都可以用于构建应用程序。服务器版提供一个可以供多个用户使用的接口,而桌面版通常在用户本地机器上使用。
相关说明也出现在第三章,数据分析工具包中,已在此处提供以供参考。
完成 R 教程安装有两种方法:
-
如果你将使用 Packt 数据科学虚拟机,则无需进一步安装。
-
如果你将在本地机器上进行教程,可以从
www.rstudio.com/products/rstudio/download/#download
下载 RStudio 桌面版,或者从www.rstudio.com/products/rstudio/download-server/
下载 RStudio 服务器版(仅适用于 Linux 用户)。
以下说明适用于希望从供应商网站下载 RStudio 并进行全新安装的用户:
访问www.rstudio.com
网站,并点击产品 | RStudio:
开源 RStudio 桌面版版本
在 RStudio 页面,点击下载 RStudio 桌面版:
选择 RStudio 桌面版
选择 RStudio 桌面版的免费版本:
选择开源 RStudio 桌面版
RStudio 可用于 Windows、Mac 和 Linux。
下载适合你系统的可执行文件并继续安装:
RStudio 二进制文件(版本)
CMS Open Payments 门户
在本节中,我们将开始为 CMS Open Payments 开发应用程序。
Packt 数据科学虚拟机包含本教程所需的所有软件。要下载该虚拟机,请参考第三章,数据分析工具包。
下载 CMS Open Payments 数据
CMS Open Payments 数据可以通过 CMS 网站直接作为 Web 下载。我们将使用 Unix 的 wget 工具下载数据,但首先需要在 CMS 网站注册并获得自己的 API 密钥:
- 访问
openpaymentsdata.cms.gov
,然后点击页面右上角的登录链接:
CMS OpenPayments 主页
点击注册:
CMS OpenPayments 注册页面
输入你的信息并点击创建我的账户按钮:
CMS OpenPayments 注册表单
登录到你的账户:
登录到 CMS OpenPayments
点击 Manage 下的 Packt Developer's Applications。请注意,此处的“应用程序”指的是你可以创建的,用于查询 CMS 网站上可用数据的应用程序:
创建“应用程序”
为应用程序指定一个名称(以下图片中展示了示例):
定义应用程序
你将收到通知,提示 Application Token 已创建:
创建应用令牌
系统将生成一个 App Token。复制 App Token:
应用令牌
- 现在,作为用户 packt 登录到 Packt 数据科学虚拟机,并在将
YOURAPPTOKEN
替换为分配给你的令牌(它将是一个很长的字符/数字字符串)后,执行以下 shell 命令。请注意,对于本教程,我们将只下载部分列并将数据限制为仅包含医生(另一个选项是医院)。
你可以通过将命令末尾的限制值设为较小的数字来减少下载的数据量。在该命令中,我们使用了 12000000
(1200 万),这将允许我们下载整个 2016 年的代表医生支付的数据集。如果你只下载大约 100 万条记录,而不是约 1100-1200 万条记录,应用程序仍然能够正常工作。
注:以下展示了两种方法。一种是使用令牌,另一种是未使用令牌。应用令牌允许用户拥有更高的流量限制。更多信息请参考 dev.socrata.com/docs/app-tokens.html
# Replace YOURAPPTOKEN and 12000000 with your API Key and desired record limit respectively
cd /home/packt;
time wget -O cms2016.csv 'https://openpaymentsdata.cms.gov/resource/vq63-hu5i.csv?$$app_token=YOURAPPTOKEN&$query=select Physician_First_Name as firstName,Physician_Last_Name as lastName,Recipient_City as city,Recipient_State as state,Submitting_Applicable_Manufacturer_or_Applicable_GPO_Name as company,Total_Amount_of_Payment_USDollars as payment,Date_of_Payment as date,Nature_of_Payment_or_Transfer_of_Value as paymentNature,Product_Category_or_Therapeutic_Area_1 as category,Name_of_Drug_or_Biological_or_Device_or_Medical_Supply_1 as product where covered_recipient_type like "Covered Recipient Physician" limit 12000000'
重要:也可以在不使用应用令牌的情况下下载文件。但应谨慎使用此方法。未使用应用令牌下载文件的 URL 如下所示:
# Downloading without using APP TOKEN
wget -O cms2016.csv 'https://openpaymentsdata.cms.gov/resource/vq63-hu5i.csv?$query=select Physician_First_Name as firstName,Physician_Last_Name as lastName,Recipient_City as city,Recipient_State as state,Submitting_Applicable_Manufacturer_or_Applicable_GPO_Name as company,Total_Amount_of_Payment_USDollars as payment,Date_of_Payment as date,Nature_of_Payment_or_Transfer_of_Value as paymentNature,Product_Category_or_Therapeutic_Area_1 as category,Name_of_Drug_or_Biological_or_Device_or_Medical_Supply_1 as product where covered_recipient_type like "Covered Recipient Physician" limit 12000000'
创建 Q 应用程序
本节描述了创建 kdb+/Q 应用程序的过程,从加载数据库中的数据开始,然后创建将作为应用程序后端的脚本。
加载数据
使用 ID packt
登录虚拟机(密码:packt
):
登录到 Packt 虚拟机
# We will start KDB+ - the NoSQL database that we'll use for the tutorial
# Launch the Q Console by typing:
packt@vagrant:~$ rlwrap ~/q/l32/q -s 4 -p 5001
KDB+ 3.5 2017.06.15 Copyright (C) 1993-2017 Kx Systems
l32/ 1()core 3951MB packt vagrant 127.0.1.1 NONEXPIRE
Welcome to kdb+ 32bit edition
For support please see http://groups.google.com/d/forum/personal-kdbplus
Tutorials can be found at http://code.kx.com/wiki/Tutorials
To exit, type \\
To remove this startup msg, edit q.q
q)
# Enter the following at the Q console. Explanations for each of the commands have been provided in the comments (using /):/change to the home directory for user packt
\cd /home/packt/ /Define the schema of the cms table
d:(`category`city`company`date`firstName`lastName`payment`paymentNature`product`state)!"SSSZSSFSSS"; /Read the headersfrom the cms csv file. These will be our table column names
columns:system "head -1 cms2016.csv";
columns:`$"," vs ssr(raze columns;"\"";""); /Run Garbage Collection
.Q.gc(); /Load the cms csv file
\ts cms2016:(d columns;enlist",")0:`:cms2016.csv; /Add a month column to the data
\ts cms2016: `month`date xasc update month:`month$date, date:`date$date from cms2016
.Q.gc(); /Modify character columns to be lower case. The data contains u
\ts update lower firstName from `cms2016
\ts update lower lastName from `cms2016
\ts update lower city from `cms2016
\ts update lower state from `cms2016
\ts update lower product from `cms2016
\ts update lower category from `cms2016
\ts update lower paymentNature from `cms2016
\ts update lower company from `cms2016
.Q.gc()
cms2016:`month`date`firstName`lastName`company`state`city`product`category`payment`paymentNature xcols cms2016
count cms2016 /11 million /Function to save the data that was read from the CMS csv file
savedata:{show (string .z.T)," Writing: ",string x;cms::delete month from select from cms2016 where month=x; .Q.dpft(`:cms;x;`date;`cms)}
/Save the data in monthly partitions in the current folder
savedata each 2016.01m +til 12
后端代码
一旦脚本完成,通过输入 \\
并按下 Enter 键退出 Q 提示符。
将以下文本复制到名为 cms.q
的文件中:
system "p 5001"
system "l /home/packt/cms"
/firstCap: Takes a string (sym) input and capitalizes the first letter of each word separated by a blank space
firstCap:{" " sv {@(x;0;upper)} each (" " vs string x) except enlist ""}
/VARIABLES AND HELPER TABLES
/alldata: Aggregates data from the primary cms database
alldata: distinct `company`product xasc update showCompany:`$firstCap each company, showProduct:`$firstCap each product from ungroup select distinct product by company from cms where not null product
/minDate: First month
minDate:exec date from select min date from cms where month=min month
/maxDate: Last month
maxDate:exec date from select max date from cms where month=max month
/companyStateCity: Cleans and normalises the company names (capitalisations, etc)
companyStateCity:select asc upper distinct state, asc `$firstCap each distinct city by company from cms
/FUNCTIONS
/getShowProduct: Function to get product list from company name getShowProduct:{$((`$"Select All") in x;raze exec showProduct from alldata;exec showProduct from alldata where showCompany in x)}
/getShowState: Function to get state list from company name getShowState:{$((`$"Select All") in x;raze exec state from companyStateCity;exec state from companyStateCity where company = exec first company from alldata where showCompany in x)}
/getShowCity: Function to get city list from company name
getShowCity:{$((`$"Select All") in x;raze exec city from companyStateCity;exec city from companyStateCity where company = exec first company from alldata where showCompany in x)}
/getShowInfo: Generic Function for Product, State and City
getShowInfo:{y:`$"|" vs y;:asc distinct raze raze $(x~`product;getShowProduct each y;x~`state;getShowState each y;x~`city;getShowCity each y;"")}
/Example: Run this after loading the entire script after removing the comment mark (/) from the beginning
/getShowInfo(`state;"Abb Con-cise Optical Group Llc|Select All|Abbott Laboratories")
/Convert encoded URL into a Q dictionary
decodeJSON:{.j.k .h.uh x}
/Convert atoms to list
ensym:{$(0>type x;enlist x;x)}
/Date functions
withinDates:{enlist (within;`date;"D"$x(`date))}
withinMonths:{enlist (within;`month;`month$"D"$x(`date))}
/Helper function to remove null keys
delNullDict:{kx!x kx:where {not x~0n} each x}
/If showdata=enlist 1,
/Function to process the data for displaying results only
getData:{"x is the dictionary from web";d:`$dx:lower delNullDict x; enz:`$delete showData,date,columns from dx; ?(`cms;(withinMonths x),(withinDates x),{(in;x 0;enlist 1_x)} each ((key enz),'value enz);0b;(dc)!dc:ensym `$x`columns)}
/Aggregation Function
aggDict:(`$("Total Payment";"Number of Payments";"Minimum Payment";"Maximum Payment";"Average Payment"))!((sum;`payment);(#:;`i);(min;`payment);(max;`payment);(avg;`payment))
/Function to aggregate the data
getDataGroups:{(aggDict;x) "x is the dictionary from web";d:`$dx:lower delNullDict x; enz:`$delete showData,date,columns,aggVars,aggData from dx; ?(`cms;(withinMonths x),(withinDates x),{(in;x 0;enlist 1_x)} each ((key enz),'value enz);xv!xv:ensym `$x`aggVars;xa!aggDict xa:ensym `$x`aggData)}(aggDict;)
/Generic Function to create error messages
errtable:{tab:(()Time:enlist `$string .z.Z;Alert:enlist x);(tab;"Missing Fields")}
/Validation for input
initialValidation:{$(0n~x(`company);:errtable `$"Company must be selected";(`aggVars in key x) and ((0=count x(`aggVars)) or 0n~x(`aggData));:errtable `$"Both Metric and Aggregate Data field should be selected when using Aggregate Data option";x)}
/Special Handling for some variables, in this case month specialHandling:{0N!x;$(`month in cols x; update `$string month from x;x)}
/Normalise Columns
columnFix:{(`$firstCap each cols x) xcol x}
/Use comma separator for numeric values
commaFmt: {((x<0)#"-"),(reverse","sv 3 cut reverse string floor a),1_string(a:abs x)mod 1}
/Wrapper for show data and aggregate data options
getRes:{0N!x;.Q.gc();st:.z.t;x:decodeJSON x; if (not x ~ ix:initialValidation x;:ix); res:$(`aggData in key x;getDataGroups x;getData x);res:specialHandling res; res:columnFix res;ccms:count cms; cres:count res; en:.z.t; .Q.gc();:(res;`$(string en),": Processed ",(commaFmt ccms)," records in ",(string en - st)," seconds. Returned result with ",(commaFmt cres)," rows.\n")
创建前端 Web 门户
R Shiny 是一个旨在简化基于 Web 的应用程序开发的包,自 2012-2013 年左右推出以来,逐渐获得了广泛关注。通常,R 开发者并不擅长前端开发,因为他们的主要工作领域通常与统计学或类似学科相关。
随着数据科学作为一种职业和主流活动的流行,创建复杂的基于 Web 的应用程序变得必要,作为一种在动态环境中向最终用户交付结果的方式。
JavaScript 几乎失去了其原有的吸引力,但它令人惊讶地复苏了,自 2010-2011 年起,Web 世界便热烈讨论各种领先的 JavaScript 包,诸如 D3、Angular、Ember 等,用于 Web 开发和可视化。
但这些工具主要被经验丰富的 JavaScript 开发人员使用,而这些开发人员中只有少数人也精通 R。开发一个能够弥合 JavaScript Web 应用程序开发和 R 编程之间差距的解决方案,成为了 R 开发者展示和分享他们工作的必要工具。
R Shiny 平台面向开发人员
R Shiny 为 R 开发人员提供了一个平台,使他们能够创建基于 JavaScript 的 Web 应用程序,而无需参与或甚至精通 JavaScript。
为了构建我们的应用程序,我们将利用 R Shiny 并创建一个界面,以连接我们在前一部分中设置的 CMS Open Payments 数据。
如果你使用的是本地 R 安装,你需要安装一些 R 包。请注意,如果你使用的是 Linux 工作站,可能还需要安装一些额外的 Linux 包。例如,在 Ubuntu Linux 中,你需要安装以下包。你可能已经安装了其中一些包,这种情况下你将收到一条消息,指示不需要对相应的包进行进一步更改:
sudo apt-get install software-properties-common libssl-dev libcurl4-openssl-dev gdebi-core rlwrap
如果你使用的是 Packt 数据科学虚拟机,你可以直接开始开发应用程序,因为这些 Linux 包已经为你安装好了。
Shiny 应用程序需要一些额外的 R 包来提供所有功能。请注意,R 包与前面描述的 Linux 包不同。R 包有成千上万种,提供特定领域的专用功能。对于 Web 应用程序,我们将安装一些 R 包,以便利用 Web 应用程序中的某些功能。
以下步骤概述了创建 Web 门户的过程:
- 登录 RStudio。如果你使用的是 Packt 数据科学虚拟机,请访问
http://localhost:8787/auth-sign-in
。使用用户 ID packt 和密码 packt(与用户 ID 相同)登录。
请注意,如果你在本地安装了 RStudio,则不会有单独的登录界面。该说明仅适用于 Packt 数据科学虚拟机:
登录 RStudio Server(仅适用于 Packt 虚拟机)
如果收到错误信息提示网站无法加载,可能是因为没有设置端口转发。要解决此问题,请进行以下更改:
-
在 VirtualBox 中,右键点击虚拟机并选择“设置”。
-
在设置中点击“网络”,并展开高级旁边的箭头:
设置虚拟机参数
- 点击端口转发并添加一条规则,将端口 8787 从虚拟机转发到主机。必须添加标记为“Packt Rule”的规则,如下所示:
配置端口转发
- 登录后,你将看到以下界面。这是 RStudio 的界面,你将使用它来完成练习。我们将在后面的章节中更详细地讨论 R 和 RStudio,而本节则展示了创建基本 Web 应用程序的过程:
RStudio 控制台
-
安装必要的 R 包。点击“文件”|“R 脚本”,然后复制并粘贴下面的代码。
-
然后,点击“源”以执行以下代码:
install.packages(c("shiny","shinydashboard","data.table",
"DT","rjson","jsonlite","shinyjs","devtools"))
library(devtools)
devtools::install_github('kxsystems/rkdb', quiet=TRUE)
通过 RStudio 在 R 中安装所需的包
- 点击“文件”|“新建文件”|“Shiny Web App”:
创建一个新的 RShiny 应用程序
- 在
application name
下输入cmspackt
并点击“创建”:
为 RShiny 应用程序命名
这将在主目录中创建一个cmspackt
文件夹,如下所示:
R Shiny 应用程序的 app.R 文件
- 将以下代码复制并粘贴到
app.R
部分:
# # This is a Shiny web application. You can run the application by clicking # the 'Run App' button above. # # Find out more about building applications with Shiny here: # # http://shiny.rstudio.com/
#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
# http://shiny.rstudio.com/
#
library(shiny)
library(shinydashboard)
library(data.table)
library(DT)
library(rjson)
library(jsonlite)
library(shinyjs)
library(rkdb)
ui <- dashboardPage (skin="purple", dashboardHeader(title = "CMS Open Payments 2016"),
dashboardSidebar(
useShinyjs(),
sidebarMenu(
uiOutput("month"),
uiOutput("company"),
uiOutput("product"),
uiOutput("state"),
uiOutput("city"),
uiOutput("showData"),
uiOutput("displayColumns"),
uiOutput("aggregationColumns"),
actionButton("queryButton", "View Results")
)
),dashboardBody(
tags$head(tags$link(rel = "stylesheet", type = "text/css", href = "packt.css")),
textOutput("stats"),
dataTableOutput("tableData")
),
title = "CMS Open Payments Data Mining"
)
# Define server logic required to draw a histogram
server <- function(input, output, session) {
h <- open_connection("localhost","5001")
minDate <- execute(h,"minDate")
maxDate <- execute(h,"maxDate")
startDate <- minDate
endDate <- startDate + 31
cmsdata <- data.table(dbColumns=c("month","date","firstName","lastName","city","state","company","product","category","payment","paymentNature"), webColumns=c("Month","Date","First Name","Last Name","City","State","Company","Product","Category","Payment","Payment Nature"))
companyData <- execute(h,"exec distinct showCompany from alldata")
gbyVars <- c("Company","Product","State","City","Category","Payment Nature")
PLACEHOLDERLIST <- list(
placeholder = 'Please select an option below',
onInitialize = I('function() { this.setValue(""); }')
)
PLACEHOLDERLIST2 <- list(
placeholder = 'Select All',
onInitialize = I('function() { this.setValue(""); }')
)
output$month <- renderUI({
dateRangeInput("date", label = 'PAYMENT DATE', start = startDate, end = endDate, min = minDate, max = maxDate)
})
output$company <- renderUI({
selectizeInput("company","COMPANY" , companyData, multiple = TRUE,options = PLACEHOLDERLIST)
})
output$product <- renderUI({
productQuery <- paste0("getShowInfo(`product;\"",paste(input$company,collapse="|"),"\")")
productVals <- execute(h,productQuery)
selectizeInput("product", "DRUG/PRODUCT" , productVals, multiple = TRUE,options = PLACEHOLDERLIST2)
})
output$state <- renderUI({
stateQuery <- paste0("getShowInfo(`state;\"",paste(input$company,collapse="|"),"\")")
stateVals <- execute(h,stateQuery)
selectizeInput("state", "STATE" , stateVals, multiple = TRUE,options = PLACEHOLDERLIST2)
})
output$city <- renderUI({
cityQuery <- paste0("getShowInfo(`city;\"",paste(input$company,collapse="|"),"\")")
cityVals <- execute(h,cityQuery)
selectizeInput("city", "CITY" , cityVals, multiple = TRUE,options = PLACEHOLDERLIST2)
})
output$showData <- renderUI({
selectInput("showData", label = "DISPLAY TYPE", choices = list("Show Data" = 1, "Aggregate Data" = 2), selected = 1)
})
output$displayColumns <- renderUI({
if (is.null(input$showData)) {selectInput("columns", "SHOW DATA",cmsdata$webColumns, selectize = FALSE, multiple = TRUE, size=11)}
else if(input$showData == 1) {selectInput("columns", "SHOW DATA",cmsdata$webColumns, selectize = FALSE, multiple = TRUE, size=11) }
else if(input$showData == 2) {selectInput("aggVars", "AGGREGATE DATA",gbyVars, selectize = FALSE, multiple = TRUE, size=6) }
})
output$aggregationColumns <- renderUI ({ conditionalPanel(
condition = "input.showData != 1",
selectInput("aggData", "CALCULATE METRICS" , c("Total Payment","Number of Payments","Minimum Payment","Maximum Payment","Average Payment"), selectize = TRUE, multiple = TRUE)
)})
getTableData <- eventReactive(input$queryButton, {
disable("queryButton")
queryInfo <- (list(date=as.character(input$date),company=input$company, product=input$product, state=input$state, city=input$city,columns=cmsdata$dbColumns(cmsdata$webColumns %in% input$columns),showData=input$showData))
if (input$showData !=1) {queryInfo <- c(queryInfo, list(aggVars=cmsdata$dbColumns(cmsdata$webColumns %in% input$aggVars), aggData=input$aggData))} else {queryInfo <- c(queryInfo)}
JSON <- rjson::toJSON(queryInfo)
getQuery <- paste0("getRes \"",URLencode(JSON),"\"")
finalResults <- execute(h,getQuery)
enable("queryButton")
print (finalResults)
fres <<- finalResults
print (class(finalResults((1))))
print (finalResults)
finalResults
})
output$tableData <- renderDataTable({ datatable(getTableData()((1)))})
output$stats <- renderText({(getTableData())((2))})
}
# Run the application
shinyApp(ui = ui, server = server)
- 点击右下角的“新建文件夹”:
为 CSS 文件创建一个文件夹
- 将新文件夹重命名为
cmspackt/www
,如下所示:
为文件夹命名
- 点击“文件”|“新建文件”|“文本文件”:
创建 CSS 文件
- 复制并粘贴以下代码:
.shiny-text-output, .shiny-bount-output {
margin: 1px;
font-weight: bold;
}
.main-header .logo {
height: 20px;
font-size: 14px;
font-weight: bold;
line-height: 20px;
}
.main-header .sidebar-toggle {
padding: 0px;
}
.main-header .navbar {
min-height: 0px !important;
}
.left-side, .main-sidebar {
padding-top: 15px !important;
}
.form-group {
margin-bottom: 2px;
}
.selectize-input {
min-height: 0px !important;
padding-top: 1px !important;
padding-bottom: 1px !important;
padding-left: 12px !important;
padding-right: 12px !important;
}
.sidebar {
height: 99vh;
overflow-y: auto;
}
section.sidebar .shiny-input-container {
padding: 5px 15px 0px 12px;
}
.btn {
padding: 1px;
margin-left: 15px;
color:#636363;
background-color:#e0f3f8;
border-color:#e0f3f8;
}
.btn.focus, .btn:focus, .btn:hover {
color: #4575b4;
background-color:#fff;
border-color:#fff;
}
pre {
display: inline-table;
width: 100%;
padding: 2px;
margin: 0 0 5px;
font-size: 12px;
line-height: 1.42857143;
color: rgb(51, 52, 53);
word-break: break-all;
word-wrap: break-word;
background-color: rgba(10, 9, 9, 0.06);
border: 1px rgba(10, 9, 9, 0.06);
/* border-radius: 4px */
}
.skin-red .sidebar a {
color: #fff;
}
.sidebar {
color: #e0f3f8;
background-color:#4575b4;
border-color:#4575b4;
}
- 点击“文件”|“另存为”保存文件,如下所示:
选择“另存为”CSS 文件
- 保存为
/home/packt/cmspackt/www/packt.css
,如下所示:
保存 CSS 文件
你的应用程序现在已准备好使用!
将一切整合在一起——CMS Open Payments 应用程序
在前面的章节中,我们已经学习了如何:
-
下载数据集
-
创建后端数据库
-
创建后端数据库的代码
-
设置 RStudio
-
创建 R Shiny 应用程序
要启动应用程序,请完成以下步骤:
-
启动 Q 应用程序,确保你在主目录中。输入 pwd 并按 Enter 键。这将显示当前工作目录
/home/packt
,如下图所示。 -
接下来,输入
q
并按 Enter 键。 -
在
q
提示符下,键入\l cms.q
。
请注意,cms.q
是我们在前一部分开发 Q 应用程序时创建的文件。
脚本将加载数据库并返回到 q)
提示符:
将所有内容整合:在 KDB+ 会话中加载 CMS KDB+ Q 脚本
-
启动 CMS Open Payment 应用程序
-
在 RStudio 中,打开
app.R
文件(包含 R 代码),然后点击右上角的 Run App 按钮,如下所示:
运行 RShiny 应用程序
这将启动 Web 应用程序,如下所示:
RShiny 应用程序
我们现在已经完成了完整的 CMS Open Payments 应用程序的开发,该程序允许最终用户筛选、聚合和分析数据。现在,您可以通过在屏幕上选择各种选项来运行查询。应用程序具有两种功能:
-
数据筛选(默认视图)
-
聚合数据(您可以通过从显示类型菜单中选择 Aggregate Data 切换到此选项)
应用程序
一个筛选示例:要查看某公司在纽约州为某种药物支付的费用:
使用 RShiny 应用程序
请注意,系统在 21 毫秒内处理了 1100 万条记录,如头部消息所示。截图中公司和产品的名称已被隐去以保护隐私,但您可以自由尝试为这两个字段选择不同的选项。
请注意,在默认虚拟机中,我们仅使用一个核心且内存非常有限,即使是在资源极为有限的笔记本上,使用 kdb+ 处理数据的速度也轻松超过了许多商业解决方案的性能。
一个聚合示例:要查看某公司和产品按州、支付类别和支付性质分组的总支付金额,请选择 Aggregate Data 和 Calculate Metrics 字段的选项。请注意,截图中公司和产品的名称仅因隐私原因而被隐藏。
请注意顶部的消息,显示如下:
日志消息,指示查询和应用程序的性能
这表示底层 kdb+ 数据库处理数据的速度。在这种情况下,它对给定选项进行了筛选并 在 22 毫秒内聚合了 1100 万条记录。
CMS OpenPayments 应用程序截图
总结
本章介绍了 NoSQL 的概念。近年来,随着其与 大数据 分析的相关性和直接应用,NoSQL 一词变得越来越流行。我们讨论了 NoSQL 的核心术语、各种类型及其在行业中使用的流行软件。最后,我们通过几个 MongoDB 和 kdb+ 的教程进行了总结。
我们还使用 R 和 R Shiny 构建了一个应用程序,创建了一个动态网页界面,用于与加载在 kdb+中的数据进行交互。
下一章将介绍数据科学中另一种常见技术——Spark。它是当今全球数据科学家使用的又一工具包。
第六章:大数据分析中的 Spark
随着 Hadoop 及其相关技术在各自生态系统中的使用日益突出,Hadoop 操作模型的一些明显和显著的缺陷也变得显而易见。特别是对 MapReduce 范式的固有依赖以及与 MapReduce 相关的其他方面,使得仅有那些深度投资于相应技术的主要公司才能真正功能化地使用 Hadoop 生态系统。
在 2011 年的加州大学伯克利分校电子工程与计算机科学系(EECS)年度研究研讨会上,由伊恩·斯托伊卡教授在演讲中宣布了一项新的研究小组的愿景(amplab.cs.berkeley.edu/about/
)。它奠定了未来将深刻改变大数据格局的关键单位的基础。AMPLab于 2011 年 2 月成立,旨在通过集成算法、机器和人力提供可扩展且统一的解决方案,以满足未来需求,而无需进行任何重大的重新工程化工作。
从 AMPLab 倡议中演化而来的最著名和最广泛使用的项目之一是 Spark,可以说是 Hadoop 生态系统的一个更优越的选择或更确切地说是扩展。
在本章中,我们将介绍 Spark 的一些显著特征,并以一个关于如何使用 Spark 的实际教程结束。我们将涵盖的主题包括:
-
Spark 的出现
-
Spark 中的理论概念
-
Spark 的核心组件
-
Spark 架构
-
Spark 解决方案
-
Spark 教程
Spark 的出现
当 2014 年首次发布 Spark 时,自 2009 年以来,Hadoop 在商业空间已经享有多年的增长。尽管 Hadoop 有效地解决了分析大规模 TB 级数据集的主要难题,并使用广泛可访问的分布式计算方法,但仍然存在一些短板,限制了其更广泛的接受度。
Hadoop 的局限性
Hadoop 存在一些常见的局限性,其中几个如下:
-
I/O Bound 操作:由于依赖于本地磁盘存储来保存和检索数据,Hadoop 中执行的任何操作都会产生 I/O 开销。在涉及跨数百台服务器的数千个数据块的大型数据集的情况下,问题变得更加严重。公平地说,通过 HDFS 协调并发 I/O 操作(在 Hadoop 世界中)构建了分布式计算的基础。然而,利用这种能力并在不同的用例和数据集中有效地调整 Hadoop 集群,需要极高且可能不成比例的专业水平。因此,工作负载的 I/O 绑定特性成为阻碍使用 Hadoop 处理极大数据集的因素。例如,需要数百次迭代操作的机器学习用例意味着系统每次迭代都会产生 I/O 开销。
-
MapReduce 编程(MR)模型:正如本书前面的部分所讨论的,Hadoop 中的所有操作都需要使用 MapReduce 编程模型来表达问题——即用户必须通过键值对来表达问题,每个键值对可以独立计算。在 Hadoop 中,编写高效的 MapReduce 程序(主要是用 Java)并不简单,尤其对于那些不熟悉 Java 或 Hadoop(或两者)的新手来说。
-
非 MR 用例:由于依赖于 MapReduce,其他更常见和简单的概念(如过滤器、连接等)也必须通过 MapReduce 程序来表达。因此,在两个文件之间通过主键进行连接时,也必须采用键值对方法。这意味着,无论是简单操作还是复杂操作,都难以在没有显著编程努力的情况下实现。
-
编程 API:Hadoop 采用 Java 作为主要编程语言,这意味着为了能够正确管理和使用 Hadoop,开发人员必须具备扎实的 Java 知识以及相关主题(如 JVM 调优、垃圾回收等)的掌握。这也意味着其他流行语言如 R、Python 和 Scala 的开发人员在重新使用或至少用自己最擅长的语言实现解决方案时,几乎没有选择。
-
总的来说,尽管 Hadoop 世界推动了大数据革命,但它未能广泛实现将大数据技术普及化的目标。
AMPLab 团队早期就意识到这些不足,并着手创建 Spark 来解决这些问题,并在此过程中,希望能开发出一种新的、更优越的替代方案。
克服 Hadoop 的局限性
我们现在将回顾之前部分讨论的一些局限性,了解 Spark 如何解决这些问题,正因如此,它提供了一个优于 Hadoop 生态系统的替代方案。
需要注意的一个关键区别是,Spark 并不需要 Hadoop 才能运行。实际上,Spark 访问数据的底层后端可以是 HBase、Hive 和 Cassandra 等技术,除了 HDFS。
这意味着,希望利用独立 Spark 系统的组织,可以在没有现有 Hadoop 基础设施的情况下,直接使用 Spark。
Spark 的解决方案如下:
-
I/O 绑定操作:与 Hadoop 不同,Spark 可以存储和访问存储在内存中的数据,也就是 RAM——如前所述,这比从磁盘读取数据要快 1,000 多倍。随着 SSD 驱动器的出现,这已成为今天企业系统中的标准,这种差异已经显著减少。近期的 NVMe 驱动器能够提供每秒 3-5 GB(千兆字节)的带宽。然而,RAM 的读取速度大约为每秒 25-30 GB,仍然比新型存储技术快 5-10 倍。因此,能够将数据存储在 RAM 中为 Spark 操作读取数据的时间提供了 5 倍或更多的提升。这是对 Hadoop 操作模型的显著改进,后者依赖磁盘读取来完成所有操作。特别是涉及迭代操作的任务(如机器学习)从 Spark 提供的内存存储和读取能力中受益匪浅。
-
MapReduce 编程(MR)模型:虽然 MapReduce 是用户通过 Hadoop 平台受益的主要编程模型,但 Spark 并不需要遵循这一要求。这对于更复杂的用例特别有帮助,例如涉及无法轻松并行化的计算的定量分析(如机器学习算法)。通过将编程模型与平台解耦,Spark 允许用户使用多种语言编写和执行代码,而无需强制要求任何特定的编程模型作为前提条件。
-
非 MapReduce 用例:Spark SQL、Spark Streaming 以及 Spark 生态系统中的其他组件提供了一整套丰富的功能,使用户可以执行常见任务,如 SQL 连接、聚合及相关的数据库操作,而无需依赖其他外部解决方案。Spark SQL 查询通常执行的是存储在 Hive 中的数据(JSON 也是一种选择),这些功能也可以在其他 Spark API 中使用,如 R 和 Python。
-
编程 API:Spark 中最常用的 API 是 Python、Scala 和 Java。对于 R 程序员,还有一个名为
SparkR
的单独包,可以直接通过 R 访问 Spark 数据。这是 Hadoop 和 Spark 之间的一个主要区别,通过暴露这些语言的 API,Spark 能够立刻为更大范围的开发者社区所使用。在数据科学和分析领域,Python 和 R 是最受欢迎的编程语言,因此,任何 Python 或 R 程序员都可以更轻松地使用 Spark,相较于 Hadoop,其学习曲线更为平缓。此外,Spark 还包含一个用于临时分析的交互式 shell。
Spark 中的理论概念
以下是 Spark 中的核心概念:
-
弹性分布式数据集
-
有向无环图
-
SparkContext
-
Spark 数据帧
-
操作和转换
-
Spark 部署选项
弹性分布式数据集
弹性分布式数据集,通常被称为RDD,是 Spark 中使用的主要数据结构。RDD 本质上是一组记录,这些记录以分布式方式存储在 Spark 集群中。RDD 是不可变的,意味着它们一旦创建就不能修改。分布在各个节点上的 RDD 可以并行访问,因此本地支持并行操作。
用户无需编写单独的代码即可获得并行化的好处,只需通过运行 Spark 平台本身的特定命令,就能享受到数据的操作和转换带来的好处。由于 RDD 也可以存储在内存中,作为额外的好处,并行操作可以直接在内存中的数据上进行,而无需产生昂贵的 I/O 访问延迟。
定向无环图
在计算机科学和数学术语中,定向无环图表示一对节点(也称为顶点)通过边(或线)连接,且这些边是单向的。也就是说,给定节点 A 和节点 B,边可以连接 A → B 或 B → A,但不能同时连接两者。换句话说,任意一对节点之间不会形成循环关系。
Spark 利用 DAG 的概念来构建一个内部工作流程,划定 Spark 作业中不同的处理阶段。从概念上讲,这类似于创建一个虚拟流程图,描述获取特定输出所需的各个步骤。例如,如果所需输出是计算文档中的单词数量,那么中间步骤 map-shuffle-reduce 可以表示为一系列操作,最终得出结果。通过维护这样的map,Spark 能够跟踪操作中的依赖关系。更具体地说,RDD 是节点,而后面会讨论的转换操作是 DAG 的边。
SparkContext
SparkContext 是所有 Spark 操作的入口点,它是应用程序连接到 Spark 集群资源的方式。它初始化一个 Spark 实例,之后可以用来创建 RDD、对 RDD 进行操作和转换,并提取数据和其他 Spark 功能。SparkContext 还会初始化进程的各种属性,例如应用程序名称、核心数、内存使用参数以及其他特性。这些属性总称为 SparkConf 对象,并作为参数传递给 SparkContext。
SparkSession
是用户启动连接到 Spark 的全新抽象,它是 Spark 2.0.0 之前SparkContext
功能的超集。然而,实践者仍然常常互换使用SparkSession
和SparkContext
,指代相同的实体;即与Spark.SparkSession
交互的主要方式,实际上结合了 SparkContext 和HiveContext
的功能。
Spark DataFrames
在 Spark 中,DataFrame 是将原始数据组织成行和列的数据结构。从概念上讲,这类似于 CSV 文件或 SQL 表格。通过 R、Python 和其他 Spark API,用户可以使用常见的 Spark 命令来操作 DataFrame,实现过滤、聚合和更一般的数据处理。DataFrame 中的数据物理上分布在 Spark 集群的多个节点上。然而,通过将数据表示为 DataFrame,它们看起来是一个统一的数据单元,而无需暴露底层操作的复杂性。
请注意,DataFrame 与 Dataset 是不同的,Dataset 是 Spark 中的另一个常用术语。Dataset 指的是分布在 Spark 集群中的实际数据。而 DataFrame 则是 Dataset 的表格表示。
从 Spark 2.0 开始,DataFrame 和 Dataset 的 API 被合并,本质上,DataFrame 现在表示一个包含行的 Dataset。也就是说,DataFrame 仍然是用户使用 Python 和 R 与 Spark 数据交互时的主要抽象。
操作和转换
Spark 操作有 2 种类型:
-
转换
-
操作
转换指定了常见的数据操作,例如过滤数据、连接数据、进行聚合、采样数据等等。转换在执行包含转换操作的代码行时不会返回任何结果。相反,执行时,该命令会将相应的操作请求添加到 Spark 的内部 DAG 中。常见的转换包括:map
、filter
、groupBy
、union
、coalesce
等等。
操作,另一方面,会返回结果。即,它们会执行用户可能在相应 RDD 上指定的一系列转换(如果有的话),并生成输出。换句话说,操作触发了 DAG 中步骤的执行。常见的操作包括:reduce
、collect
、take
、aggregate
、foreach
等等。
请注意,RDD 是不可变的。它们不能被更改;转换和操作总是会生成新的 RDD,而不会修改现有的 RDD。
Spark 部署选项
Spark 可以在多种模式下部署。最重要的模式包括:
-
独立模式:作为一个独立的集群,不依赖任何外部集群管理器
-
Amazon EC2:在亚马逊 Web 服务的 EC2 实例上,它可以访问来自 S3 的数据
-
Apache YARN:Hadoop 资源管理器
其他选项包括 Apache Mesos 和 Kubernetes。
更多详情可以在 Spark 文档网站上找到,spark.apache.org/docs/latest/index.html
。
Spark API
Spark 平台通过 Python、Scala、R 和 Java 中可用的 Spark API 轻松访问。它们一起使得在 Spark 中处理数据变得简单且广泛可用。在 Spark 项目开始时,它只支持 Scala/Java 作为主要 API。然而,鉴于 Spark 的一个主要目标是为不同开发者提供简易的接口,因此 Scala API 后便推出了 Python 和 R API。
在 Python 中,PySpark 包已经成为 Python 开发者社区编写 Spark 应用程序的广泛使用标准。在 R 中,用户通过 SparkR 包与 Spark 进行交互。这对那些可能也有兴趣使用存储在 Spark 生态系统中的数据的 R 开发者非常有用。这两种语言在数据科学社区中都非常流行,因此 Python 和 R API 的引入为在 Spark 上进行 大数据 分析的分析性用例奠定了基础。
Spark 中的核心组件
以下组件在 Spark 中非常重要:
-
Spark Core
-
Spark SQL
-
Spark Streaming
-
GraphX
-
MLlib
Spark Core
Spark Core 提供了 Spark 中的基本功能,如操作 RDD、执行动作和转换,以及更多的管理任务,如存储、高可用性和其他话题。
Spark SQL
Spark SQL 为用户提供了通过标准 SQL 命令查询存储在 Apache Hive 中的数据的能力。这为开发者提供了一种通过 Spark SQL 接口使用常见 SQL 术语与数据集互动的方式,增加了额外的可访问性。底层数据的平台不仅限于 Apache Hive,还可以包括 JSON、Parquet 等。
Spark Streaming
Spark 的流处理组件允许用户与流数据进行交互,如与网络相关的内容等。它还包括企业级特性,如高可用性。Spark 可以从各种中间件和数据流服务读取数据,例如 Apache Kafka、Apache Flume 以及 Amazon Web Services 等云解决方案提供商。
GraphX
Spark 的 GraphX 组件支持基于图的操作,类似于支持专用数据结构的图数据库等技术。这些技术使得使用、访问和表示互联数据点变得容易,例如社交网络。除了数据分析外,Spark GraphX 平台还支持图算法,适用于需要大规模表示关系的商业用例。例如,信用卡公司使用类似于 Spark 的 GraphX 组件的基于图的数据库来构建推荐引擎,检测具有相似特征的用户。这些特征可能包括购买习惯、位置、人口统计信息以及其他定性和定量因素。在这些案例中使用图系统可以让公司构建包含代表个人的节点和代表关系度量的边的网络,从而找到它们之间的共同特征。
MLlib
MLlib 是 Spark 生态系统的旗舰组件之一。它提供了一个可扩展的高性能接口,用于在 Spark 中执行资源密集型的机器学习任务。此外,MLlib 可以本地连接到 HDFS、HBase 和 Spark 支持的其他底层存储系统。由于这种多功能性,用户无需依赖现有的 Hadoop 环境即可开始使用内置于 MLlib 中的算法。MLlib 中支持的一些算法包括:
-
分类:逻辑回归
-
回归:广义线性回归、生存回归等
-
决策树、随机森林和梯度提升树
-
推荐:交替最小二乘法
-
聚类:K-means、高斯混合等
-
主题建模:潜在狄利克雷分配
-
Apriori:频繁项集,关联规则
ML 工作流实用工具包括:
-
特征转换:标准化、归一化等
-
ML 管道构建
-
模型评估和超参数调整
-
ML 持久化:保存和加载模型与管道
Spark 的架构
Spark 由三个主要的架构组件组成:
-
SparkSession / SparkContext
-
集群管理器
-
执行进程所在的工作节点
SparkSession/SparkContext,或更广义的 Spark Driver,是所有 Spark 应用程序的入口点。如前所述,SparkContext 将用于创建 RDD 并对 RDD 执行操作。SparkDriver 向工作节点发送指令以调度任务。
集群管理器 在概念上类似于 Hadoop 中的资源管理器,事实上,支持的解决方案之一是 YARN。其他集群管理器包括 Mesos。Spark 还可以在独立模式下运行,此时不需要 YARN/Mesos。集群管理器协调工作节点之间的通信,管理节点(如启动、停止等),并执行其他管理任务。
工作节点是托管 Spark 应用程序的服务器。每个应用程序都有一个独特的 执行器进程,即执行实际操作和转换任务的进程。通过分配专用的执行器进程,Spark 确保某个特定应用程序中的问题不会影响其他应用程序。工作节点由执行器、JVM 和 Spark 应用程序所需的 Python/R/其他应用进程组成。请注意,在 Hadoop 中,工作节点和数据节点是同一个概念:
Spark 解决方案
Spark 作为开源解决方案,可以直接从 spark.apache.org 获取。Databricks 是 Spark 商业解决方案的领先提供商。对于那些熟悉 Python、R、Java 或 Scala 编程的人来说,由于高效的接口(如 PySpark API 允许用户仅用 Python 即可在 Spark 中工作),开始使用 Spark 所需的时间最小。
基于云的 Spark 平台,如 Databricks 社区版,提供了一种简单易用的方式来使用 Spark,而无需额外的安装和配置工作。因此,想要使用 Spark 进行编程及相关任务的用户,可以在不花费时间进行管理任务的情况下迅速上手。
Spark 实践
在本节中,我们将创建一个 Databricks 社区版账户,并完成一个实践练习,带领读者了解一般的操作、转换以及 RDD 概念的基础。
注册 Databricks 社区版
以下步骤概述了注册 Databricks 社区版 的过程:
- 点击“今天开始”按钮并输入您的信息:
- 确认您已阅读并同意弹出菜单中的条款(向下滚动至底部以找到同意按钮):
- 检查您的电子邮件,查收来自 Databricks 的确认邮件,并点击其中的链接确认您的账户:
- 点击确认账户链接后,您将被引导到一个登录界面,您可以使用注册账户时的电子邮件地址和密码进行登录:
- 登录后,点击“集群”以设置 Spark 集群,如下图所示:
- 输入
Packt_Exercise
作为集群名称,然后点击页面顶部的“创建集群”按钮:
- 这将启动一个 Spark 集群,我们将在上面使用 iPython 笔记本执行 Spark 命令。iPython 笔记本是一个常用的集成开发环境(IDE)——一个基于 Web 的开发应用程序,用于编写和测试 Python 代码。该笔记本还可以通过使用内核支持其他语言,但在本教程中,我们将专注于 Python 内核。
稍等一会儿,状态将从“待处理”变为“运行中”:
状态将在几分钟后变为“运行中”:
- 点击工作区(在左侧栏),选择选项,用户 | (
您的用户 ID
),然后点击您电子邮件地址旁边的下拉箭头。选择“创建”|“笔记本”:
- 在弹出窗口中,输入
Packt_Exercise
作为笔记本名称,并点击“创建”按钮:
- 一旦点击创建按钮,您将直接进入如下所示的笔记本。这是 Spark 笔记本,您将在这里执行接下来的代码。代码应输入在笔记本的单元格中,如下所示。输入代码后,按Shift + Enter来执行对应的单元格:
-
在接下来的几个练习中,您可以将文本复制粘贴到笔记本的单元格中。或者,您也可以导入笔记本并直接加载到您的工作区。如果这样做,您将不需要输入命令(尽管输入命令会让您更熟悉操作)。
-
复制粘贴命令的替代方法:您可以通过点击“导入”来导入笔记本,如下所示:
- 在弹出菜单中输入以下URL(选择从导入为URL选项):
- 笔记本将显示在您的电子邮件 ID 下。点击笔记本名称以加载它:
Spark 练习 - 与 Spark 动手实践(Databricks)
本笔记本基于 Databricks 提供的教程 (databricks.com/
)。教程将使用 Databricks 的社区版 Spark 进行,用户可以通过databricks.com/try-databricks
进行注册。Databricks 是 Spark 的商业版和企业版的领先提供商。
本教程将介绍一些 Spark 中的基本命令。我们鼓励用户尝试更多的 Spark 教程和笔记本,网上有许多更详细的示例。
Spark 的 Python API 文档可以在spark.apache.org/docs/latest/api/python/pyspark.html#pyspark.sql
找到。
本书的数据已导入 Databricks 的 Spark 平台。有关导入数据的更多信息,请参阅导入数据 - Databricks (docs.databricks.com/user-guide/importing-data.html
)。
# COMMAND ----------
# The SparkContext/SparkSession is the entry point for all Spark operations
# sc = the SparkContext = the execution environment of Spark, only 1 per JVM
# Note that SparkSession is now the entry point (from Spark v2.0)
# This tutorial uses SparkContext (was used prior to Spark 2.0)
from pyspark import SparkContext
# sc = SparkContext(appName = "some_application_name") # You'd normally run this, but in this case, it has already been created in the Databricks' environment
# COMMAND ----------
quote = "To be, or not to be, that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take Arms against a Sea of troubles, And by opposing end them: to die, to sleep No more; and by a sleep, to say we end the heart-ache, and the thousand natural shocks that Flesh is heir to? 'Tis a consummation devoutly to be wished. To die, to sleep, To sleep, perchance to Dream; aye, there's the rub, for in that sleep of death, what dreams may come, when we have shuffled off this mortal coil, must give us pause."
# COMMAND ----------
sparkdata = sc.parallelize(quote.split(' '))
# COMMAND ----------
print "sparkdata = ", sparkdata
print "sparkdata.collect = ", sparkdata.collect
print "sparkdata.collect() = ", sparkdata.collect()[1:10]
# COMMAND ----------
# A simple transformation - map
def mapword(word):
return (word,1)
print sparkdata.map(mapword) # Nothing has happened here
print sparkdata.map(mapword).collect()[1:10] # collect causes the DAG to execute
# COMMAND ----------
# Another Transformation
def charsmorethan2(tuple1):
if len(tuple1[0])>2:
return tuple1
pass
rdd3 = sparkdata.map(mapword).filter(lambda x: charsmorethan2(x))
# Multiple Transformations in 1 statement, nothing is happening yet
rdd3.collect()[1:10]
# The DAG gets executed. Note that since we didn't remove punctuation marks ... 'be,', etc are also included
# COMMAND ----------
# With Tables, a general example
cms = sc.parallelize([[1,"Dr. A",12.50,"Yale"],[2,"Dr. B",5.10,"Duke"],[3,"Dr. C",200.34,"Mt. Sinai"],[4,"Dr. D",5.67,"Duke"],[1,"Dr. E",52.50,"Yale"]])
# COMMAND ----------
def findPayment(data):
return data[2]
print "Payments = ", cms.map(findPayment).collect()
print "Mean = ", cms.map(findPayment).mean() # Mean is an action
# COMMAND ----------
# Creating a DataFrame (familiar to Python programmers)
cms_df = sqlContext.createDataFrame(cms, ["ID","Name","Payment","Hosp"])
print cms_df.show()
print cms_df.groupby('Hosp').agg(func.avg('Payment'), func.max('Payment'),func.min('Payment'))
print cms_df.groupby('Hosp').agg(func.avg('Payment'), func.max('Payment'),func.min('Payment')).collect()
print
print "Converting to a Pandas DataFrame"
print "--------------------------------"
pd_df = cms_df.groupby('Hosp').agg(func.avg('Payment'), func.max('Payment'),func.min('Payment')).toPandas()
print type(pd_df)
print
print pd_df
# COMMAND ----------
wordsList = ['to','be','or','not','to','be']
wordsRDD = sc.parallelize(wordsList, 3) # Splits into 2 groups
# Print out the type of wordsRDD
print type(wordsRDD)
# COMMAND ----------
# Glom coallesces all elements within each partition into a list
print wordsRDD.glom().take(2) # Take is an action, here we are 'take'-ing the first 2 elements of the wordsRDD
print wordsRDD.glom().collect() # Collect
# COMMAND ----------
# An example with changing the case of words
# One way of completing the function
def makeUpperCase(word):
return word.upper()
print makeUpperCase('cat')
# COMMAND ----------
upperRDD = wordsRDD.map(makeUpperCase)
print upperRDD.collect()
# COMMAND ----------
upperLambdaRDD = wordsRDD.map(lambda word: word.upper())
print upperLambdaRDD.collect()
# COMMAND ----------
# Pair RDDs
wordPairs = wordsRDD.map(lambda word: (word, 1))
print wordPairs.collect()
# COMMAND ----------
# #### Part 2: Counting with pair RDDs
# There are multiple ways of performing group-by operations in Spark
# One such method is groupByKey()
#
# ** Using groupByKey() **
#
# This method creates a key-value pair whereby each key (in this case word) is assigned a value of 1 for our wordcount operation. It then combines all keys into a single list. This can be quite memory intensive, especially if the dataset is large.
# COMMAND ----------
# Using groupByKey
wordsGrouped = wordPairs.groupByKey()
for key, value in wordsGrouped.collect():
print '{0}: {1}'.format(key, list(value))
# COMMAND ----------
# Summation of the key values (to get the word count)
wordCountsGrouped = wordsGrouped.map(lambda (k,v): (k, sum(v)))
print wordCountsGrouped.collect()
# COMMAND ----------
# ** (2c) Counting using reduceByKey **
#
# reduceByKey creates a new pair RDD. It then iteratively applies a function first to each key (i.e., within the key values) and then across all the keys, i.e., in other words it applies the given function iteratively.
# COMMAND ----------
wordCounts = wordPairs.reduceByKey(lambda a,b: a+b)
print wordCounts.collect()
# COMMAND ----------
# %md
# ** Combining all of the above into a single statement **
# COMMAND ----------
wordCountsCollected = (wordsRDD
.map(lambda word: (word, 1))
.reduceByKey(lambda a,b: a+b)
.collect())
print wordCountsCollected
# COMMAND ----------
# %md
#
# This tutorial has provided a basic overview of Spark and introduced the Databricks community edition where users can upload and execute their own Spark notebooks. There are various in-depth tutorials on the web and also at Databricks on Spark and users are encouraged to peruse them if interested in learning further about Spark.
摘要
在本章中,我们阅读了关于 Spark 的一些核心特性,Spark 是当今大数据领域最突出的一项技术之一。自 2014 年推出以来,Spark 迅速成熟,成为一种大数据解决方案,缓解了 Hadoop 的许多不足之处,如 I/O 争用等问题。
今天,Spark 拥有多个组件,包括专门用于流式分析和机器学习的组件,并且正在积极开发中。Databricks 是 Spark 商业支持版本的领先提供商,还提供了一个非常方便的基于云的 Spark 环境,任何用户都可以免费访问,尽管资源有限。这大大降低了入门门槛,因为用户无需安装完整的 Spark 环境就能学习和使用该平台。
在下一章中,我们将开始讨论机器学习。在本节之前的大部分内容都集中在大规模数据的管理上。有效利用数据并从中获取洞察始终是最终目标。为了做到这一点,我们需要采用今天已经普及的高级算法技术。下一章将讨论机器学习的基本原则,之后我们将在随后的章节中深入探讨该领域。
第七章:机器学习概念简介
机器学习已经成为我们日常生活中的常见话题。该领域的进展非常迅速,今天,甚至连手机都集成了先进的机器学习和人工智能相关功能,能够根据人类指令作出响应并采取行动。
曾经仅限于大学课堂的一个学科,已经转变为一个成熟的行业,渗透到我们日常生活中的方方面面,甚至是我们几年前无法想象的方式。
本章的目标是介绍机器学习的基本原理,并用简单、清晰的术语解释这些概念,帮助读者熟悉该领域的核心思想。我们将从机器学习的高层概述开始,解释不同的分类及其区分方法。我们还将解释机器学习中的一些重要概念,如数据预处理、特征工程和变量重要性。下一章将深入探讨具体的算法和理论机器学习。
我们将通过利用真实世界的数据集来进行机器学习操作,使用 R 语言进行练习,最后作结。
本章将涵盖以下主题:
-
什么是机器学习?
-
流行的兴起
-
机器学习、统计学和人工智能(AI)
-
机器学习的分类
-
机器学习的核心概念
-
机器学习教程
什么是机器学习?
机器学习 不是一门新学科;它作为一门正式学科在学术界已存在超过 70 年,但以不同的名字出现过:统计学,更广泛地说是数学,然后是人工智能(AI),今天则称为机器学习。虽然其他相关领域,如统计学和人工智能,也同样普及,机器学习却开辟了一个独立的领域,成为了一门独立的学科。
简单来说,机器学习涉及基于历史数据预测未来事件。我们在日常生活中都能看到它的体现,实际上,我们每天都在无意识地或有意识地运用机器学习的原理。
当我们随便评论一部电影是否会在票房上成功,利用我们对主演人气的了解时,我们其实已经在应用机器学习,尽管这是潜意识的。我们对主演人物的了解,是通过多年来观看他们出演的电影逐渐形成的。而当我们判断一部未来上映的电影是否成功时,实际上是运用了历史信息来进行评估。
另一个例子是,如果我们有关于温度、湿度和降水量(雨量)的数据,假设这些数据涵盖了 12 个月的时间,我们是否可以利用这些信息预测今天是否会下雨,仅凭温度和湿度的数据?
这类似于统计学中的常见回归问题。然而,机器学习涉及在这一过程中应用更高层次的严谨性,通过不仅仅依赖理论计算,而且通过反复验证这些计算数百或数千次来得出结论,最终作出决定。
需要在此指出并澄清的是,机器学习这一术语与通常在计算设备上执行的算法或程序有关,其目的是预测结果。这些算法构建数学模型,随后可以用来做出预测。一个常见的误解是,机器学习字面上指的是机器在学习。正如刚才所解释的,实际含义远没有那么戏剧化。
机器学习的演变
机器学习的时间线,在维基百科上有提供(en.wikipedia.org/wiki/Timeline_of_machine_learning
),提供了该领域演变的简明而深刻的概述。其根源可以追溯到 18 世纪中期,当时托马斯·贝叶斯在伦敦皇家学会发表了关于逆概率的论文。逆概率,今天更常被称为概率分布,涉及到在给定一组先前事件的情况下,确定系统状态的问题。例如,如果一个盒子里有牛奶巧克力和白巧克力,你随机取出一些,结果得到了两块牛奶巧克力和三块白巧克力,那么我们能推测出盒子里各有多少块巧克力吗?
换句话说,给定一些数据点,我们可以推测出什么关于未知的内容,从而提出一个正式的理论?贝叶斯的工作进一步发展为贝叶斯定理,由皮埃尔-西蒙·拉普拉斯在他的著作《概率论分析理论》中完成。
在 20 世纪初,安德烈·马尔可夫对普希金的诗歌《叶甫盖尼·奥涅金》进行的分析,旨在确定俄语文学中辅音和元音的头韵现象,这导致了马尔可夫链技术的发展,今天被用来模拟涉及随机事件的复杂情况。谷歌的 PageRank 算法就是实现了一种形式的马尔可夫链。
机器学习的首次正式应用,或者更广泛地说,人工智能(AI)的出现及其作为一门学科的形成,应归功于艾伦·图灵。他开发了图灵测试——一种用来确定机器是否足够智能以模仿人类行为的方法。图灵在他的论文《计算机器与智能》中提出了这一点,文章开头写道:
我提议考虑这个问题:“机器能思考吗?”这应该从“机器”和“思考”这两个术语的定义开始。定义可以尽量反映这些词语的常规用法,但这种态度是危险的。如果通过考察这些词语的常用意义来理解“机器”和“思考”的含义,几乎难以避免得出结论,认为“机器能思考吗?”这个问题的答案应通过诸如盖洛普民意调查之类的统计调查来寻找。然而,这显然是荒谬的。在无法定义的情况下,我将用另一个与之密切相关且表达相对明确的问题来替代该问题。[...]
在文章的后面,图灵写道:
“机器能思考吗?”这个原始问题,我认为过于没有意义,甚至不值得讨论。然而,我相信,在本世纪末,随着语言的使用和普遍的教育意见的变化,人们将能够谈论机器思考,而不必担心遭到反驳。我还相信,隐瞒这些信念并不会起到任何有用的作用。
图灵在人工智能领域的工作之后,机器学习和人工智能领域发生了一系列具有开创性意义的事件。第一个神经网络由马文·明斯基于 1951 年开发,亚瑟·塞缪尔于 1952 年开始研究第一个能下跳棋的机器学习程序,而罗森布拉特在 1957 年发明了感知器——神经网络的基本单元。像李欧·布雷曼、杰罗姆·弗里德曼、弗拉基米尔·瓦普尼克、阿列克谢·切尔沃涅基斯、杰夫·辛顿和扬·勒昆等先驱者在 1990 年代末做出了重大贡献,使机器学习成为备受关注的研究领域。我们深感荣幸能借助他们的工作和贡献,使得机器学习如今成为一个独立且显著的研究领域。
1997 年,IBM 的深蓝战胜了卡斯帕罗夫,并立即成为全球轰动的新闻。机器能够击败世界顶级象棋冠军,这可不是一项普通的成就。这个事件为机器学习赢得了必要的信誉,使其成为图灵设想中的智能机器的强有力竞争者。
机器学习成功的因素
鉴于机器学习作为一个学科已经存在了数十年,这就引出了一个问题:为什么它没有像今天这样早早地变得流行起来呢?事实上,到 1990 年代末,神经网络等复杂机器学习算法的理论已经广为人知,而且其基础在此之前的理论领域已奠定。
有一些因素可以归因于机器学习的成功:
-
互联网:网络在信息民主化和人们连接方面发挥了至关重要的作用。它使信息交换变得简单,而这种简单是通过以前的印刷媒体交流方式无法实现的。网络不仅改变并彻底革新了信息传播方式,还开辟了新的机会。正如之前提到的,谷歌的 PageRank 是应用统计模型开发成功的第一个大规模且高度可见的例子,它为创建一个高度成功的网络企业提供了支持。
-
社交媒体:虽然网络提供了一个交流的平台,但它缺乏一种类似人们在现实世界中互动的灵活性。存在一个明显但被低估的、可以说是未被充分探索的空白。IRC 和 Usenet 等工具是社交网络网站的前身,例如 Myspace,它是最早旨在创建个人网络的基于网络的平台之一。到 2000 年代中期,Facebook 已经成为社交网络的领导者。这些平台为利用互联网收集个人层面的数据提供了独特的机会。每个用户都留下了一连串消息,适合通过自然语言处理等技术进行收集和分析。
-
计算硬件:用于计算机的硬件以指数级的速度发展。机器学习算法本质上需要大量的计算和资源,也就是说,它们根据数据的大小需要强大的 CPU、快速的硬盘和大量的内存。固态硬盘(SSDs)的发明是从传统的旋转硬盘存储过程的一次飞跃。更快的访问速度意味着数据可以以更快的速度传递给 CPU,从而减少了传统计算中一直是瓶颈的 I/O 问题。更快的 CPU 使得能够按时执行机器学习算法要求的成百上千次迭代。最后,需求推动了计算资源价格的降低,使得更多人能够负担得起以前价格过高的计算硬件。虽然算法早已存在,但资源终于能够在合理的时间和成本内执行这些算法。
-
编程语言与包:像 R 和 Python 这样的开发者社区抓住了机会,个人开始发布包,将他们的工作展现给更广泛的编程社区。特别是提供机器学习算法的包立即受到欢迎,并激励其他从业者发布他们的个人代码库,使得像 R 这样的平台成为一个真正的全球协作努力。今天,R 中已经有超过 10,000 个包,而在 2010 年只有 2000 个。
机器学习、统计学与人工智能
机器学习是一个具有多种同义词的术语——这些名字通常来源于企业的营销活动或只是被交替使用的术语。虽然有人可能认为它们有不同的含义,但它们最终都指向机器学习这一学科,它通过利用历史信息来预测未来事件。
常听到的与机器学习相关的术语包括预测分析、预测分析学、预测建模等。因此,除非发布材料的实体解释了他们对该术语的定义,并且更具体地说明其不同之处,否则通常可以假定它们指的是机器学习。这通常是那些初学者感到困惑的原因,主要是由于技术术语的误用和滥用。
另一方面,统计学是一个独立的学科领域,已有 200 多年历史。该词源自新拉丁语,statisticum collegium(在英语中为国家议会)和意大利语单词statista,意为政治家或国会议员。你可以访问en.wikipedia.org/wiki/History_of_statistics#Etymology
查看更多关于这一主题的详细信息。机器学习实现了各种统计模型,由于涉及的计算复杂性,它不同于经典统计学分支。
人工智能与机器学习密切相关,但它是一个更广泛的主题。它可以被宽泛地定义为在不确定性的情况下,能够以(通常)负责任且具有社会意识的方式做出具体决策的系统(软件/硬件),以实现预定的目标。换句话说,人工智能的目标是通过系统地处理既有已知因素又有未知(潜在)因素的情况来产生行动。
人工智能让人联想到科幻电影中那些聪明且有时叛逆的机器人,也让人想起像 IBM Watson 这样的智能系统,它们能够解析复杂问题并处理模糊陈述,找到具体答案。
机器学习具有一些相同的特征——通过训练数据逐步开发模型,并使用测试数据来衡量准确性。然而,人工智能已经存在了数十年,并且是家喻户晓的词汇。美国的机构,如卡内基梅隆大学,在建立人工智能的关键原则和指南方面处于领先地位。
关于人工智能与机器学习的在线资源/文章似乎无法提供如何区分它们的确切答案。然而,大学的人工智能课程大纲使这些差异非常明显。你可以在cs.brown.edu/courses/csci1410/lectures.html
学习更多关于人工智能的内容。
人工智能涉及广泛的研究领域,包括:
-
约束优化:在给定约束或限制的情况下,达到最佳结果
-
博弈论:例如零和博弈、均衡等——根据决策如何影响未来决策并影响期望的最终目标来做出衡量决策
-
不确定性/贝叶斯规则:在给定先验信息的情况下,已发生某些事件后,发生其他事件的可能性
-
规划:制定行动计划 = 一组路径(图形)来应对某种情况/实现最终目标
-
机器学习:通过使用设计来处理不确定性并模仿人类推理的算法实现前述目标。通常用于人工智能的机器学习算法包括:
-
神经网络/深度学习(发现隐藏因素)
-
自然语言处理(NLP)(使用语气、语言学等理解上下文)
-
视觉物体识别
-
概率模型(例如,贝叶斯分类器)
-
马尔可夫决策过程(针对随机事件的决策,例如赌博)
-
各种其他机器学习算法(聚类、支持向量机)
-
-
社会学:研究机器学习决策如何影响社会,并采取纠正措施来解决问题
机器学习的分类
亚瑟·塞缪尔(Arthur Samuel)在 1959 年于 IBM 时创造了机器学习这一术语。机器学习的一个流行定义来自亚瑟,他被认为将机器学习称为一门让计算机无需明确编程便能学习的计算机科学领域。
汤姆·米切尔(Tom Mitchell)在 1998 年为机器学习添加了一个更具体的定义,称其为:研究那些通过经验 E 改进在某个任务 T 上表现 P 的算法。
一个简单的解释有助于阐明这一概念。到目前为止,我们大多数人都熟悉电子邮件中的垃圾邮件概念。大多数电子邮件帐户中也包含一个名为垃圾邮件、Spam或相关术语的单独文件夹。简单检查这些文件夹通常会发现几封电子邮件,其中许多显然是未经请求的,并且包含无意义的信息。
单纯将电子邮件分类为垃圾邮件并将其移到文件夹的任务,涉及机器学习的应用。安德鲁·吴在他流行的机器学习 MOOC 课程中优雅地强调了这一点。
用米切尔的术语来说,垃圾邮件分类过程包括:
-
任务 T:将电子邮件分类为垃圾邮件/非垃圾邮件
-
表现 P:准确识别为垃圾邮件的电子邮件数量
-
经验 E:模型通过提供标记为垃圾邮件/非垃圾邮件的电子邮件,并利用这些信息来判断新电子邮件是否为垃圾邮件
广义上讲,机器学习有两种截然不同的类型:
-
有监督的机器学习
-
无监督机器学习
我们将在此依次讨论它们。
有监督与无监督机器学习
让我们首先从有监督机器学习开始。
有监督的机器学习
监督学习是指涉及通过标签数据来预测结果的机器学习练习。标签数据简单来说就是指我们用于进行预测的数据集(以及我们将要预测的结果)有一个明确的值(无论是什么)。例如,将电子邮件分类为垃圾邮件或非垃圾邮件、预测温度、识别图像中的人脸等,都是监督学习的例子。
车辆里程、数字识别和其他例子
给定一个包含每加仑英里数、气缸数等各种汽车信息的数据集,我们能否预测如果只知道其他值的情况下,每加仑英里数的值会是多少?
在这种情况下,我们的结果是mpg
,而我们使用其他变量如cyl
(气缸数)、hp
(马力)、gear
(档位数)等来构建一个模型,该模型可以应用于一个数据集,其中mpg
的值被标记为MISSING
。该模型读取数据中前五行的这些列的信息,并根据这些信息预测其他行中mpg
的值,如下图所示:
之所以将其视为监督学习,是因为在构建机器学习模型的过程中,我们为模型提供了关于结果的信息。其他的例子包括:
-
识别字母和数字:在这种情况下,模型的输入是字母和数字的图像,输出是图像上显示的字母数字值。一旦模型构建完成,就可以将其应用于图片,识别并预测图片中显示的数字。这是一个简单的例子,但非常强大。想象一下,如果你得到了 100,000 张带有房屋编号的房子图像,人工识别房号的方法就是逐个查看每张图像并写下房号。机器学习模型使我们能够完全自动化整个操作。你不需要逐个手动查看图像,而只需将模型应用于这些图像,就能在很短的时间内得到结果。
-
自动驾驶汽车:算法的输入是已经识别出图像中物体的图像,例如人、路标、汽车、树木、商店和其他元素。一旦展示了足够数量的图像,算法学习识别并区分不同的元素,之后即便给出一张未标记的图像(即物体没有被识别的图像),也能单独识别这些物体。公平地说,这是对一个非常复杂话题的高度简化的解释,但总体原理是一样的。
用于数字识别的 MNIST 数据集:
无监督学习
无监督机器学习涉及没有标签结果的数据集。以预测汽车的 mpg 值为例,在无监督的练习中,我们的数据集可能是这样的:
如果所有结果都是缺失的,那么就无法知道这些值可能是什么。回想一下,机器学习的基本前提是利用历史信息来对结果未知的数据集进行预测。但如果历史信息本身没有任何已标识的结果,那么就无法构建模型。没有其他信息可用的情况下,表格中的 mpg 值可能全是 0,也可能全是 100;我们无法判断,因为没有任何数据点能帮助我们得出这些值。
这就是无监督机器学习应用的地方。在这种类型的机器学习中,我们并不是试图预测结果,而是试图确定哪些项目彼此最为相似。
这种练习的常见名称是聚类,也就是说,我们试图找到聚类或最为相似的记录组。我们可以在什么地方使用这些信息,以及无监督学习的例子有哪些?
网上有各种新闻聚合器——这些网站本身不发布信息,而是收集来自其他新闻源的信息。一个这样的聚合器是谷歌新闻。如果我们需要搜索关于卫星卡西尼拍摄的最后一批土星图像的信息,我们可以在谷歌新闻上进行简单搜索,使用如下短语:news.google.com/news/?gl=US&ned=us&hl=en
。这里展示了一个示例:
请注意,在新闻文章的底部有一个“查看全部”链接。点击该链接将带你到一个页面,列出所有相关的新闻文章。当然,谷歌并没有手动将这些文章分类为属于特定的搜索词。实际上,谷歌并不知道用户会搜索什么。搜索词可能是来自太空的土星环图像。
那么,谷歌是如何知道哪些文章属于特定搜索词的呢?答案在于聚类或无监督学习原理的应用。无监督学习通过检查特定数据集的属性,来确定哪些文章彼此最为相似。为了做到这一点,算法甚至不需要了解上下文背景。
假设你被给出了两套没有封面的书,一套是关于园艺的书,另一套是关于计算机编程的书。虽然你可能不知道书的标题,但要区分计算机书籍和园艺书籍却相当容易。一套书会有大量与计算机相关的术语,而另一套书则会有大量与植物相关的术语。通过书中的图片,就能很容易地区分这两类书籍,即使是一个假设不懂计算机或园艺的读者。
其他非监督学习的例子包括恶性肿瘤和良性肿瘤的检测,基因测序等。
细分监督学习
监督学习可以进一步细分为涉及以下任何一个的练习:
-
分类
-
回归
这些概念相当简单。
分类涉及一个具有离散结果的机器学习任务——一个类别结果。所有的名词都是类别变量,如水果、树木、颜色和真/假。
分类练习中的结果变量也被称为离散变量或类别变量。
一些例子包括:
-
根据大小、重量和形状识别水果
-
根据一组数字图像识别数字(如前章所示)
-
识别街道上的物体
-
识别扑克牌的花色,如方块、黑桃、红桃和梅花
-
根据学生的成绩识别学生的班级排名
-
最后一个可能看起来不太明显,但排名,即 1(st)、2(nd)、3^(rd),表示一个固定的类别。一个学生可能排名第 1 或第 2,但不能有第 1.5 名!
一些典型分类例子的图片如下所示:
![]() |
![]() |
---|
回归,另一方面,涉及计算数值结果。任何可以进行数值运算(如加法、减法、乘法和除法)的结果都构成回归问题。
回归的例子包括:
-
预测每日温度
-
计算股票价格
-
预测住宅物业销售价格等
一些典型回归例子的图片如下所示。在这两种情况下,我们处理的是连续的定量数值数据。因此,回归的结果变量也被称为定量变量或连续变量。
![]() |
![]() |
---|
请注意,分类或回归的概念不适用于无监督学习。由于无监督学习没有标签,因此严格意义上没有离散的分类或回归。然而,由于无监督学习将数据分成多个簇,簇中的对象通常被认为属于同一类(与同一簇中的其他对象一样)。这类似于分类,只不过是事后创建的,而在对象被分类到各个簇之前并没有存在类。
机器学习中的常见术语
在机器学习中,你会经常听到特征、预测变量和因变量这些术语。它们其实是一样的,都指代用于预测结果的变量。在我们之前的汽车示例中,cyl(气缸数)、hp(马力)、wt(重量)和gear(齿轮数)是预测变量,而mpg(每加仑英里数)是结果变量。
更简单地说,拿电子表格的例子来说,列名本质上就是特征、预测变量和因变量。举个例子,如果我们得到一个收费亭费用的数据集,任务是根据时间和其他因素预测收费金额,一个假设的示例如下:
在这个电子表格中,列 date(日期)、time(时间)、agency(机构)、type(类型)、prepaid(预付)、rate(费率)是特征或预测变量,而列 amount(金额)是我们的结果或因变量(我们要预测的内容)。
数值的大小依赖于其他变量的值(因此这些变量被称为独立变量)。
简单的方程式也反映了显著的区别,例如在方程式 y = a + b + c 中,左侧(LHS)是因变量/结果变量,而 a、b 和 c 是特征/预测变量。
总结:
机器学习中的核心概念
机器学习中有许多重要的概念,我们将讨论一些常见的主题。机器学习涉及一个多步骤的过程,首先是数据采集、数据挖掘,最终建立预测模型。
构建模型的关键方面包括:
-
数据预处理:预处理和特征选择(例如,居中和缩放、类别不平衡以及变量重要性)
-
训练集、测试集划分和交叉验证:
-
创建训练集(例如,80%的数据)
-
创建测试集(大约占数据的 20%)
-
执行交叉验证
-
-
创建模型,获取预测结果:
-
你应该尝试哪些算法?
-
你希望优化哪些准确性指标?
-
你应该使用哪些调参参数?
-
机器学习中的数据管理步骤
预处理,或更广泛地说,数据处理,是大多数机器学习任务中不可或缺的一部分。你开始时的数据集很少会以你将要构建机器学习模型的精确格式存在;在大多数情况下,它不可避免地需要经过相当数量的清洗。事实上,数据清洗通常是整个过程最耗时的部分。在本节中,我们将简要介绍一些在实践中你可能遇到的主要数据处理步骤。
预处理和特征选择技术
数据预处理,顾名思义,涉及整理数据,使其适合机器学习任务。有多种预处理方法,以下展示了几种较常见的方法。
请注意,数据预处理应作为交叉验证步骤的一部分执行,也就是说,预处理不应在事前进行,而应在模型构建过程中进行。这将在后续详细解释。
中心化和缩放
对数值列应用中心化和缩放函数通常是为了标准化数据,并消除数值幅度或差异较大时的影响。你可能在大学或课程中遇到过这种情况,在这种情况下,学生的成绩会基于标准化或曲线来评分。
比如说,假设一张试卷异常难,10 名学生中有一半的成绩低于 60——课程设定的及格线。教授可以选择 a)决定 50%的学生需要重新修习这门课程,或者 b)将成绩标准化,查看学生之间的相对表现。
假设班级成绩为:
45,66,66,55,55,52,61,64,65,49
设定及格分数为 60,这意味着得分为 45、55、55、52 和 49 的学生将无法顺利完成课程。
然而,这可能并不能准确反映他们的相对优劣。教授可以选择使用一种中心化和缩放的方法,通常称为标准化,其过程如下:
-
计算所有成绩的均值
-
从成绩中减去均值
-
将结果除以所有成绩的标准差
下面是该操作的示意图,供参考。
成绩的均值为 57.8。因此,将 57.8 从每个数字中减去,得到第二行中的数字。但这还不是最终结果。我们需要将这些数字除以成绩的标准差,以获得最终的标准化值:
除以标准差(SD)后,显示只有两名学生的得分低于所有测试成绩范围内的一个标准差。因此,基于原始分数,原本应该有五名学生无法成功完成课程,但我们可以将范围缩小到只有两名学生。
虽然这只是一个非常简单的操作,但不难看出,它在平滑数据中大幅波动方面非常有效。
可以通过以下scale
命令在 R 中轻松地执行居中和缩放操作:
> scores <- c(45,66,66,55,55,52,61,64,65,68)
> scale(scores)
[,1]
[1,] -1.9412062
[2,] 0.8319455
[3,] 0.8319455
[4,] -0.6206578
[5,] -0.6206578
[6,] -1.0168223
[7,] 0.1716713
[8,] 0.5678358
[9,] 0.6998907
[10,] 1.0960552
attr(,"scaled:center")
[1] 59.7
attr(,"scaled:scale")
[1] 7.572611
近零方差函数
nearZeroVar
函数(位于R
包caret
中)用于识别方差几乎为零或完全没有方差的变量。考虑一个包含 10,000 个数字的集合,其中只有三个不同的值。这样的变量可能对算法的贡献非常小。为了使用nearZeroVar
函数,首先在 RStudio 中安装caret
包(我们在第三章中已进行设置,分析工具包)。使用nearZeroVar
的效果的确切代码如下所示:
> library(caret)
Loading required package: lattice
Loading required package: ggplot2
Need help getting started? Try the cookbook for R: http://www.cookbook-r.com/Graphs/
> repeated <- c(rep(100,9999),10) # 9999 values are 100 and the last value is 10
>random<- sample(100,10000,T) # 10,000 random values from 1 - 100
>data<- data.frame(random = random, repeated = repeated)
>nearZeroVar(data)
[1] 2
> names(data)[nearZeroVar(data)]
[1] "repeated"
如示例所示,该函数能够正确检测满足标准的变量。
移除相关变量
相关变量可能会产生过度强调变量贡献的结果。在回归分析中,这会导致 R² 值的增加,但并不能准确表示模型的实际表现。尽管许多类型的机器学习算法能够抵抗相关变量的影响,但它仍然值得提及,因为它是该学科中的一个常见话题。
移除这些变量的前提与冗余变量不会为模型增添增量价值的事实相关。例如,如果数据集中同时包含英寸和米为单位的身高,这些变量之间的相关性几乎为 1,使用其中一个变量和使用另一个变量一样好。涉及我们无法直观判断的变量的实践练习,通过使用移除相关变量的方法,可以大大帮助简化模型。
以下示例说明了移除相关变量的过程。数据集皮马印第安糖尿病包含关于皮马印第安人饮食的基本统计信息和一个名为diabetes
的结果变量。
在接下来的章节示例中,我们将频繁引用此数据集。数据集不同列的高层次概述如下:
pregnant Number of times pregnant
glucose Plasma glucose concentration (glucose tolerance test)
pressure Diastolic blood pressure (mm Hg)
triceps Triceps skin fold thickness (mm)
insulin 2-Hour serum insulin (mu U/ml)
mass Body mass index (weight in kg/(height in m)\²)
pedigree Diabetes pedigree function
age Age (years)
diabetes Class variable (test for diabetes)
我们希望了解除了糖尿病(作为我们的结果变量)之外,是否有其他变量是相关的。如果是这样,移除冗余变量可能会很有帮助。
在 RStudio 中安装mlbench
和corrplot
包,并执行如下命令:
install.packages("mlbench")
install.packages("corrplot")
library(corrplot)
library(mlbench)
data (PimaIndiansDiabetes)
diab <- PimaIndiansDiabetes # To produce a correlogram
corrplot(cor(diab[,-ncol(diab)]), method="color", type="upper") # To get the actual numbers
corrplot(cor(diab[,-ncol(diab)]), method="number", type="upper")
该命令将使用来自www.sthda.com/english/wiki/visualize-correlation-matrix-using-correlogram
的corrplot
包生成如下图:
![]() |
>![]() |
---|
阴影越深,相关性越高。在这种情况下,它显示年龄和怀孕有较高的相关性。我们可以通过使用method="number"
来查看具体数值,如所示。你还可以在www.sthda.com/english/wiki/visualize-correlation-matrix-using-correlogram
查看图形。
我们还可以使用以下函数直接找到相关的变量,而无需绘制相关图:
correlated_columns<- findCorrelation(cor(diab[,-ncol(diab)]), cutoff = 0.5)
correlated_columns
其他常见的数据转换
还有其他几种数据转换方法适用于不同的情况。这些转换的总结可以在caret
包的文档网站上找到,位置在预处理部分:topepo.github.io/caret/pre-processing.html
。
可以通过在 RStudio 中运行命令?preProcess
来查看caret
的预处理函数中提供的选项。它的代码如下:
Method
a character vector specifying the type of processing.
Possible values are "BoxCox", "YeoJohnson", "expoTrans", "center", "scale", "range", "knnImpute", "bagImpute", "medianImpute", "pca", "ica", "spatialSign", "corr", "zv", "nzv", and "conditionalX" (see Details below)
数据采样
你可能会遇到那些具有高度不平衡结果类别的数据集。例如,如果你在处理关于罕见疾病的数据集,且结果变量为“真”或“假”,由于疾病的罕见性,你可能会发现标记为“假”(即该人没有罕见疾病)的观察值远高于标记为“真”(即该人患有罕见疾病)的观察值。
机器学习算法试图最大化性能,在许多情况下,这可能是预测准确性。假设在 1000 条记录中,只有 10 条标记为真,剩余的990
条观察值为假。
如果有人随机将所有观察值都标记为假,那么准确率将是:
(990/1000) * 100 = 99%
但是,这项工作的目标是找到患有罕见疾病的个体。我们已经很清楚,由于该疾病的性质,大多数个体将不属于这一类别。
数据采样本质上是最大化机器学习度量指标,如特异性、敏感性、精确度、召回率和 kappa 值的过程。稍后将讨论这些指标,但为了本节的目的,我们将展示一些方法,通过这些方法你可以采样数据,从而生成一个更加平衡的数据集。
R 包caret
包括几个有用的函数,可以从不平衡数据集中创建类别的平衡分布。
在这些情况下,我们需要重新采样数据,以便获得更好的类别分布,从而构建一个更有效的模型。
一些常见的方法包括:
-
上采样:增加具有较少样本的类别的实例
-
下采样:减少具有较多样本的类别的实例
-
创建合成样本(例如,SMOTE(合成少数类过采样技术))
-
随机过采样(例如,(ROSE) 随机过采样示例)
我们将使用之前示例中的相同数据来创建一个模拟数据集,其中 95%的行将被标记为负面:
library(mlbench)
library(caret)
diab<- PimaIndiansDiabetes
diabsim<- diab
diabrows<- nrow(diabsim)
negrows<- floor(.95 * diabrows)
posrows<- (diabrows - negrows)
negrows
[1] 729
posrows
[1] 39
diabsim$diabetes[1:729] <- as.factor("neg")
diabsim$diabetes[-c(1:729)] <- as.factor("pos")
table(diabsim$diabetes)
neg. pos
729 39
# We observe that in this simulated dataset, we have 729 occurrences of positive outcome and 39 occurrences of negative outcome
# Method 1: Upsampling, i.e., increasing the number of observations marked as 'pos' (i.e., positive)
upsampled_simdata<- upSample(diabsim[,-ncol(diabsim)], diabsim$diabetes)
table(upsampled_simdata$Class)
negpos
729 729
# NOTE THAT THE OUTCOME IS CALLED AS 'Class' and not 'diabetes'
# This is because of the use of the variable separately
# We can always rename the column to revert to the original name
# Method 2: Downsampling, i.e., reducing the number of observations marked as 'pos' (i.e., positive)
downsampled_simdata<- downSample(diabsim[,-ncol(diabsim)], diabsim$diabetes)
table(downsampled_simdata$Class)
neg pos
39 39
-
SMOTE(合成少数类过采样技术)是第三种方法,它不同于简单的上采样/下采样,而是通过少数类的最近邻创建合成记录。在我们的模拟数据集中,显然
neg
是少数类,也就是出现次数最少的类别。SMOTE 函数的帮助文件简要地解释了这个概念:
不平衡的分类问题对许多学习算法造成了困扰。这些问题的特点是每个类别可用的样本比例不均。
SMOTE(Chawla 等人,2002)是一个著名的算法,用来解决这个问题。该方法的一般思想是通过使用这些案例的最近邻,人工生成少数类的新样本。此外,多数类样本也会被下采样,从而得到一个更加平衡的数据集:
# Method 3: SMOTE
# The function SMOTE is available in the R Package DMwR
# In order to use it, we first need to install DmWR as follows
install.packages ("DMwR")
# Once the package has been installed, we will create a synthetic
# Dataset in which we will increase the number of 'neg' records
# Let us check once again the distribution of neg/pos in the dataset
table(diabsim$diabetes)
negpos
729 39
# Using SMOTE we can create synthetic cases of 'pos' as follows
diabsyn<- SMOTE(diabetes ~ ., diabsim, perc.over = 500, perc.under = 150)
# perc.over = 500 means, increase the occurrence of the minority
# class by 500%, i.e., 39 + 5*39 = 39 + 195 = 234
# perc.under = 150 means, that for each new record generated for the
# Minority class, we will generate 1.5 cases of the majority class
# In this case, we created 195 new records (500% of 39) and hence
# we will generate 150% of 195 records = 195 * 150% = 195 * 1.5
# = 292.5, or 292 (rounded down) new records
# We can verify this by running the table command against the newly
# Created synthetic dataset, diabsyn
table(diabsyn$diabetes)
negpos
292 234
- ROSE(随机过采样示例),本节中的最后一种方法,可以通过 R 中的 ROSE 包使用。像 SMOTE 一样,它是一种生成合成样本的方法。ROSE 的帮助文件中简要说明了该函数的高级使用方法:
通过随机过采样示例生成的合成数据通过扩大少数类和多数类样本的特征空间来创建合成数据样本。在操作上,这些新样本是从这两类的条件核密度估计中抽取的,正如 Menardi 和 Torelli(2013)中所描述的那样。
install.packages("ROSE")
library(ROSE)
# Loaded ROSE 0.0-3
set.seed(1)
diabsyn2 <- ROSE(diabetes ~ ., data=diabsim)
table(diabsyn2$data$diabetes)
# negpos
# 395 373
数据插补
有时,你的数据可能会有缺失值。这可能是由于数据收集过程中的错误、真正缺失的数据或其他任何原因,结果就是信息不可用。在现实中,缺失数据的例子可以在调查中找到,例如受访者没有回答调查中的某个特定问题。
你可能有一个包含 1,000 条记录和 20 个列的数据集,其中某一列有 100 个缺失值。你可以选择完全丢弃这一列,但这也意味着丢弃了 90%的信息。你仍然可以使用其他 19 列中有完整数据的列。另一种选择是直接排除该列,但这意味着你无法利用该列中可用数据所带来的好处。
存在几种数据插补方法,即填补缺失数据的过程。我们不知道确切的值是什么,但通过查看表中的其他条目,我们可能能够对这些值做出有根据且系统的评估。
数据插补中的一些常见方法包括:
-
均值、中位数、众数插补:使用列的均值、中位数或众数值来替代缺失值。然而,这种方法的缺点是会增加被插补变量之间的相关性,这在多元分析中可能并不理想。
-
K 近邻插补:kNN 插补是一种使用机器学习方法(最近邻)来插补缺失值的过程。其通过找到与缺失值记录最相似的 k 条记录,并使用与 k 条记录的欧氏距离加权平均来进行计算。
-
回归模型插补:回归方法使用 R 中的标准回归方法来预测缺失变量的值。然而,正如维基百科中关于回归插补的相关章节所指出的
en.wikipedia.org/wiki/Imputation_(statistics)#Regression
,回归插补的问题在于插补数据的估计中没有包括误差项。因此,估算值完美地符合回归线,没有任何残差方差。这导致关系被过度识别,并暗示插补值的精度高于实际所需。 -
热 deck 插补:另一种使用数据集本身的观测值来填补缺失值的技术。尽管这种方法非常流行,但它的局限性在于,如果将一个值赋给大量的缺失值,可能会在观测值中引入显著的偏差,并产生误导性的结果。
这里提供了一个简短的示例,展示如何使用 kNN 插补进行插补。我们通过将 PimaIndiansDiabetes
数据集中的大量值更改为 NA 来模拟缺失数据。
我们在这个过程中使用以下因素:
-
我们使用均值来填补 NA 值。
-
我们使用 kNN 插补填补缺失值。然后,我们比较这两种方法的表现:
library(DMwR)
library(caret)
diab<- PimaIndiansDiabetes
# In the dataset, the column mass represents the body mass index
# Of the individuals represented in the corresponding row
# mass: Body mass index (weight in kg/(height in m)\²)
# Creating a backup of the diabetes dataframe
diabmiss_orig<- diab
# Creating a separate dataframe which we will modify
diabmiss<- diabmiss_orig
# Saving the original values for body mass
actual <- diabmiss_orig$mass
# Change 91 values of mass to NA in the dataset
diabmiss$mass[10:100] <- NA
# Number of missing values in mass
sum(is.na(diabmiss$mass))
# 91
# View the missing values
diabmiss[5:15,]
我们得到的输出如下:
# Test with using the mean, we will set all the missing values
# To the mean value for the column
diabmiss$mass[is.na(diabmiss$mass)] <- mean(diabmiss$mass,na.rm = TRUE)
# Check the values that have been imputed
data.frame(actual=actual[10:100], impute_with_mean=diabmiss$mass[10:100])
上述代码的输出如下:
# Check the Root-Mean-Squared-Error for the entire column
# Root Mean Squared Error provides an estimate for the
# Difference between the actual and the predicted values
# On 'average'
diabmissdf<- data.frame(actual=actual, impute_with_mean=diabmiss$mass)
rmse1 <- RMSE(diabmissdf$impute_with_mean,actual)
rmse1
# [1] 3.417476
# We will re-run the exercise using knnImputation (from package DMwR)
# Change the value of the records back to NA
diabmiss<- diabmiss_orig
diabmiss$mass[10:100] <- NA
# Perform knnImputation
diabknn<- knnImputation(diabmiss,k=25)
# Check the RMSE value for the knnImputation method
rmse2 <- RMSE(diabknn$mass,actual)
rmse2
# [1] 3.093827
# Improvement using the knnImputation methods in percentage terms
100 * (rmse1-rmse2)/rmse1
[1] 22.20689
尽管它可能不会代表一个显著的变化,但总比使用简单的均值或常数值等天真的方法要好。
R 中有多个用于数据插补的包,以下是几个著名的包:
- Amelia II:时间序列数据中的缺失信息
- 使用 R 包进行热 deck 插补:HotDeckImputation 和 hot.deck
cran.r-project.org/web/packages/HotDeckImputation/
cran.r-project.org/web/packages/hot.deck/
- 多元插补(链式方程法)
cran.r-project.org/web/packages/mice/index.html
- 在贝叶斯框架中使用 R 包进行缺失值填充:mi
cran.r-project.org/web/packages/mi/index.html
变量的重要性
在建模过程中,数据集可能包含数十个变量。并不是所有的变量都会为预测模型增加价值。将数据集缩小到一个子集,允许机器学习程序员将更多的时间用于优化选定的变量和建模过程并不罕见。减少数据集中变量的数量也有技术上的理由。对非常大的数据集(即高维数据集)进行机器学习建模可能会非常耗费计算资源,即可能需要大量的时间、CPU 和内存来执行数值运算。这不仅使某些算法的应用变得不切实际,还会导致不必要的延迟。因此,方法性的变量选择有助于分析时间和算法分析的计算需求。
变量选择也叫做特征选择/属性选择。像随机森林和套索回归这样的算法将变量选择作为其算法操作的一部分。但是,变量选择也可以作为一个独立的练习进行。
R 包caret
提供了一个非常简单易用且直观的界面用于变量选择。由于我们还没有讨论建模过程,我们将在这里学习如何找到重要变量,并在下一章深入探讨这个主题。
我们将使用一个常见的、广为人知的算法,叫做RandomForest
,它用于构建决策树。这个算法将在下一章中详细描述,但在这里使用它的目的是仅仅为了展示如何进行变量选择。这个例子说明了整个过程的一般步骤。
我们将重新使用之前使用过的数据集,即来自mlbench
包的PimaIndiansDiabetes
数据。我们还没有讨论模型训练过程,但在这里已经使用该数据集来推导变量重要性值。此案例中的结果变量是糖尿病,其他变量则作为自变量使用。换句话说,我们是否可以使用现有的数据预测某人是否患有糖尿病:
diab<- PimaIndiansDiabetes
# We will use the createDataPartition function from caret to split
# The data. The function produces a set of indices using which we
# will create the corresponding training and test sets
training_index<- createDataPartition(diab$diabetes, p = 0.80, list = FALSE, times = 1)
# Creating the training set
diab_train<- diab[training_index,]
# Create the test set
diab_test<- diab[-training_index,]
# Create the trainControl parameters for the model
diab_control<- trainControl("repeatedcv", number = 3, repeats = 2, classProbs = TRUE, summaryFunction = twoClassSummary)
# Build the model
rf_model<- train(diabetes ~ ., data = diab_train, method = "rf", preProc = c("center", "scale"), tuneLength = 5, trControl = diab_control, metric = "ROC")
# Find the Variable Importance
varImp(rf_model)
rf variable importance
Overall
glucose 100.000
mass 52.669
age 39.230
pedigree 24.885
pressure 12.619
pregnant 6.919
insulin 2.294
triceps 0.000
# This indicates that glucose levels, body mass index and age are the top 3 predictors of diabetes.
# caret also includes several useful plot functions. We can visualize the variable importance using the command:
plot(varImp(rf_model))
上述代码的输出如下所示。它表明葡萄糖、体重和年龄是对创建模型(预测糖尿病)贡献最大的变量。
训练集、测试集拆分和交叉验证概念
训练集、测试集划分和交叉验证集是机器学习中的基本概念。这是纯统计方法与机器学习方法在本质上不同的一个领域。虽然在统计建模任务中,可能会进行回归、参数化/非参数化检验并应用其他方法,但在机器学习中,算法方法是通过对结果的反复评估与每次迭代中模型的即兴改进来补充的。
将数据分为训练集和测试集
每个机器学习建模任务都从数据清洗过程开始,如前所述。下一步是将数据分为训练集和测试集。这通常通过从数据中随机选择行来创建模型,未被选择的行将用于测试最终模型。
通常的数据集划分比例在 70%到 80%之间(训练数据与测试数据)。在 80-20 的划分中,80%的数据将用于创建模型,剩余的 20%将用于测试最终生成的模型。
我们在前面的部分应用过这个方法,但我们可以再次回顾代码。createDataPartition
函数与参数p = 0.80
一起使用,以便对数据进行划分。training_index
变量保存了我们将使用的训练集索引(来自dataset
,diab
):
training_index<- createDataPartition(diab$diabetes, p = 0.80, list = FALSE, times = 1)
length(training_index) # Number of items that we will select for the train set [1] 615
nrow(diab) # The total number of rows in the dataset [1] 768
# Creating the training set, this is the data we will use to build our model
diab_train<- diab[training_index,]
# Create the test set, this is the data against which we will test the performance of our model
diab_test<- diab[-training_index,]
我们不一定非要使用createDataPartition
函数,相反,使用简单的 R 命令创建的随机样本,如这里所示,也可以满足需求:
# Create a set of random indices representing 80% of the data
training_index2 <- sample(nrow(diab),floor(0.80*nrow(diab)))
# Check the size of the indices just created
length(training_index2) [1] 614
# Create the training set
diab_train2 <- diab[training_index2,]
# Create the test set
diab_test2 <- diab[-training_index2]
交叉验证参数
交叉验证将训练-测试集划分概念提升到了一个新的阶段。机器学习任务的目标,本质上是找到一组模型参数,从而提供最佳的性能。模型参数指的是函数(即模型)所接受的参数。例如,对于决策树模型,参数可能包括模型应构建的深度层数、划分数等。如果有n个不同的参数,每个参数有k个不同的值,那么总的参数组合数将是k^n。我们通常为每个参数选择固定的一组组合,可能最终有 100 到 1000+种组合。我们将测试每个参数下模型的表现(例如,正确预测结果的准确性)。
使用简单的训练-测试集划分,比如,如果我们选择了 500 个参数组合,我们只需要将它们应用于训练数据集,并确定哪个组合表现最佳。
使用交叉验证时,我们将训练集进一步拆分成更小的子集,例如,常用三折或五折。如果使用三折,即将训练集拆分为三个子集,我们将其中一个折叠(如 Fold 2)留出,使用 Fold 1 和 Fold 3 的数据集构建模型,并测试其在 Fold 2 上的准确性。这一步会重复多次,每次迭代代表在不同的折叠集合上执行训练-测试过程并收集准确度度量。最终,我们会通过选择表现最佳的模型参数组合来得到最优解。
标准方法可以总结如下:
-
创建一个 80-20 的训练-测试拆分
-
使用不同的模型参数组合执行你的模型
-
选择显示最佳整体性能的模型参数并创建最终模型
-
在测试集上应用最终模型以查看结果
交叉验证方法要求我们将训练数据集进一步拆分成更小的子集。这些子集通常称为折叠(folds),统称为k-折叠(k-folds),其中 k 代表拆分的数量:
-
创建一个 80-20 的训练-测试拆分
-
将训练集拆分成 k 折,例如,三折
-
将 Fold 1 留出,使用 Fold 2 和 Fold 3 构建模型
-
在 Fold 1 上测试你的模型性能(例如,准确结果的百分比)
-
将 Fold 2 留出,使用 Fold 1 和 Fold 3 构建模型
-
在 Fold 2 上测试你的模型性能
-
将 Fold 3 留出,使用 Fold 1 和 Fold 2 构建模型
-
在 Fold 3 上测试你的模型性能
-
计算模型在所有三个折叠上的平均性能
-
对 每组模型参数 重复步骤 1
-
选择显示最佳整体性能的模型参数并创建最终模型
-
在测试集上应用最终模型以查看结果
这张图说明了使用不带交叉验证的方式与使用交叉验证的方式之间的区别。交叉验证方法可以说更为稳健,涉及对模型的严格评估。话虽如此,通常还是有必要先尝试不使用交叉验证来构建一个初步的模型,以便大致了解可能期望的性能。例如,如果一个使用 2-3 次训练-测试拆分构建的模型显示的准确率为 30%,那么不太可能通过任何其他方法,包括交叉验证,使其准确率提升到 90%。换句话说,标准方法有助于大致了解可能期望的性能。由于交叉验证可能计算密集且耗时,获取初步的性能反馈有助于对整体过程进行初步分析。
R 中的 caret 包提供了一种非常用户友好的方法,通过交叉验证来构建模型。回想一下,数据预处理必须传递或作为交叉验证过程的一个组成部分。所以,如果我们需要对数据集进行中心化和标准化,并执行五折交叉验证,我们所需要做的就是在 caret 的 trainControl
函数中定义我们希望使用的采样类型。
Caret 的 trainControl
网页提供了关于功能的详细概述,并附有示例,topepo.github.io/caret/model-training-and-tuning.html#basic-parameter-tuning
。
我们在之前的练习中使用了这种方法,在该练习中我们使用 RandomForest
构建了一个模型,数据集是 PimaIndiansDiabetes
。这里再次展示它是为了说明该技巧的应用:
# Create the trainControl parameters for the model
# The parameters indicate that a 3-Fold CV would be created
# and that the process would be repeated 2 times (repeats)
# The class probabilities in each run will be stored
# And we'll use the twoClassSummary* function to measure the model
# Performance
diab_control<- trainControl("repeatedcv", number = 3, repeats = 2, classProbs = TRUE, summaryFunction = twoClassSummary)
# Build the model
# We used the train function of caret to build the model
# As part of the training process, we specified a tunelength** of 5
# This parameter lets caret select a set of default model parameters
# trControl = diab_control indicates that the model will be built
# Using the cross-validation method specified in diab_control
# Finally preProc = c("center", "scale") indicate that the data
# Would be centered and scaled at each pass of the model iteration
rf_model<- train(diabetes ~ ., data = diab_train, method = "rf", preProc = c("center", "scale"), tuneLength = 5, trControl = diab_control, metric = "ROC")
你可以从 cran.r-project.org/web/packages/caret/vignettes/caret.pdf
获取关于 summaryFunction
的更详细解释。
summaryFunction
参数用于传入一个函数,该函数接收观察值和预测值并估计某些性能度量。该包中已经包含了两个这样的函数:defaultSummary
和 twoClassSummary
。后者将计算特定于二分类问题的度量,如 ROC 曲线下面积、敏感性和特异性。由于 ROC 曲线是基于预测的类别概率(这些概率不会自动计算),因此需要另一个选项。classProbs = TRUE
选项用于包含这些计算。
这是来自 caret
的 train 函数帮助文件中关于 tuneLength
的解释。
tuneLength
是一个整数,表示调优参数网格的粒度。默认情况下,此参数是每个调优参数应由 train 生成的级别数量。如果 trainControl
具有选项 search = random
,则这是随机搜索将生成的调优参数组合的最大数量。
请注意,如果提供了该参数,它必须具有名称。
创建模型
创建模型后的最终步骤是使用该模型对测试数据集进行预测。通常,这使用 R 中的 predict
函数来完成,第一个参数是已创建的模型,第二个参数是你想要预测的目标数据集。
以我们的 PimaIndiansDiabetes
数据集为例,在模型构建完成后,我们可以对测试数据集进行预测,如下所示:
# Install the R Package e1071, if you haven't already
# By running install.packages("e1071")
# Use the predict function and the rf_model that was previously built
# To get the predictions on the test dataset
# Note that we are not including the column diabetes in the test
# dataset by using diab_test[,-ncol(diab_test)]
predictions<- predict(rf_model, diab_test[,-ncol(diab_test)])
# First few records predicted
head(predictions)
[1] negnegpospospospos
Levels: negpos
# The confusion matrix allows us to see the number of true positives
# False positives, True negatives and False negatives
cf<- confusionMatrix(predictions, diab_test$diabetes)
cf
# Confusion Matrix and Statistics
#
# Reference
# Prediction negpos
# neg 89 21
# pos 11 32
#
# Accuracy : 0.7908
# 95% CI : (0.7178, 0.8523)
# No Information Rate : 0.6536
# P-Value [Acc> NIR] : 0.0001499
#
# Kappa : 0.5167
# Mcnemar's Test P-Value : 0.1116118
#
# Sensitivity : 0.8900
# Specificity : 0.6038
# PosPredValue : 0.8091
# NegPredValue : 0.7442
# Prevalence : 0.6536
# Detection Rate : 0.5817
# Detection Prevalence : 0.7190
# Balanced Accuracy : 0.7469
#
# 'Positive' Class :neg
让我们检查混淆矩阵告诉我们什么:
# This indicates that of the records that were marked negative (neg)
# We predicted 89 of them as negative and 11 as positive (i.e., they
# were negative but we incorrectly classified them as a positive
# We correctly identified 32 positives but incorrectly classified
# 21 positives as negative
#
# Reference
# Prediction neg pos
# neg 89 21
# pos 11 32
# The overall accuracy was 79%
# This can be improved (significantly) by using more
# Accuracy : 0.7908
# We can plot the model using plot(rf_model) as follows
plot(rf_model)
情节如下:
# And finally we can also visualize our confusion matrix using the
# inbuilt fourfoldplot function in R
fourfoldplot(cf$table)
我们得到的图表如下:
根据fourfoldplot
的文档 [来源: stat.ethz.ch/R-manual/R-devel/library/graphics/html/fourfoldplot.html
],二元行列变量之间的关联(比 1 的比值比)通过在一个方向上,斜对角的单元格与另一个方向的单元格在大小上的差异来表示;颜色用来显示这种方向。比值比的置信环允许对无关联的零假设进行视觉测试;相邻象限的环重叠当且仅当观察到的计数与零假设一致时。
在模型中利用多核处理
在上一节中的练习在这里被重复,改用 PimaIndianDiabetes2 数据集。这个数据集包含一些缺失值。因此,我们将首先对缺失值进行插补,然后运行机器学习示例。
该练习已经进行了重复,并加入了一些额外的细节,比如使用多核/并行处理来加快交叉验证的运行速度。
要利用多核处理,使用以下代码安装doMC
包:
Install.packages("doMC") # Install package for multicore processing
Install.packages("nnet") # Install package for neural networks in R
现在,我们将按照这里的代码运行程序:
# Load the library doMC
library(doMC)
# Register all cores
registerDoMC(cores = 8)
# Set seed to create a reproducible example
set.seed(100)
# Load the PimaIndiansDiabetes2 dataset
data("PimaIndiansDiabetes2",package = 'mlbench')
diab<- PimaIndiansDiabetes2
# This dataset, unlike PimaIndiansDiabetes has 652 missing values!
> sum(is.na(diab)) [1] 652
# We will use knnImputation to fill in the missing values
diab<- knnImputation(diab)
# Create the train-test set split
training_index<- createDataPartition(diab$diabetes, p = .8, list = FALSE, times = 1)
# Create the training and test dataset
diab_train<- diab[training_index,]
diab_test<- diab[-training_index,]
# We will use 10-Fold Cross Validations
diab_control<- trainControl("repeatedcv", number = 10, repeats = 3, search = "random", classProbs = TRUE)
# Create the model using methodnnet (a Neural Network package in R)
# Note that we have changed the metric here to "Accuracy" instead of # ROC
nn_model<- train(diabetes ~ ., data = diab_train, method = "nnet", preProc = c("center", "scale"), trControl = diab_control, tuneLength = 10, metric = "Accuracy")
predictions<- predict(nn_model, diab_test[,-ncol(diab_test)])
cf<- confusionMatrix(predictions, diab_test$diabetes)
cf
# >cf
# Confusion Matrix and Statistics
#
# Reference
# Prediction negpos
# neg 89 19
# pos 11 34
#
# Accuracy : 0.8039
# 95% CI : (0.7321, 0.8636)
# No Information Rate : 0.6536
# P-Value [Acc> NIR] : 3.3e-05
#
即使有 650 多个缺失值,我们的模型也能达到 80%以上的准确率。
它当然可以改进,但作为基准,它展示了机器学习模型可以达到的表现。
在一个二分结果变量的情况下,随机猜测有 50%的机会是准确的。80%的准确率显著高于仅凭猜测可能达到的准确率:
plot(nn_model)
结果图如下所示:
fourfoldplot(cf$table)
结果如下图所示:
总结
在本章中,我们学习了机器学习的基本原理,包括监督学习和无监督学习等不同类型,以及数据预处理、数据插补、管理不平衡类和其他主题的主要概念。
我们还学习了今天常被交替使用的术语之间的关键区别,特别是“人工智能”和“机器学习”这两个术语。我们了解到,人工智能涉及广泛的主题,如博弈论、社会学、约束优化和机器学习;与机器学习相比,人工智能的范围要广泛得多。
机器学习促进了人工智能的实现;即,机器学习算法用于创建人工智能系统,但它们在范围上有所不同。回归问题(给定一组点,找出最佳拟合线)可以被视为一种机器学习算法,但它更不可能被看作是一种 AI 算法(概念上,尽管从技术上讲它也可以是)。
在下一章,我们将探讨机器学习中的其他一些概念,如偏差、方差和正则化。我们还将学习一些重要的算法,并了解如何使用 R 中的机器学习包应用它们。
第八章:机器学习深入探讨
前一章关于机器学习提供了该主题的初步概述,包括该领域中的不同类别和核心概念。本章将深入探讨机器学习的理论方面,例如算法的限制以及不同算法的工作原理。
机器学习是一个广泛而复杂的主题,因此本章侧重于不同主题的广度,而非深度。概念以高层次的方式介绍,读者可以参考其他资源进一步了解这些主题。
我们将从讨论机器学习中的几个基础理论开始,例如梯度下降和 VC 维度。接下来,我们将探讨偏差和方差,这两者是任何建模过程中最重要的因素,并讨论偏差-方差平衡的概念。
然后我们将讨论各种机器学习算法,它们的优点和应用领域。
我们将通过利用现实世界的数据集进行练习,使用 R 进行机器学习操作,最后得出结论。
本章将涵盖以下主题:
-
偏差、方差和正则化属性
-
梯度下降与 VC 维度理论
-
机器学习算法
-
教程:使用 R 进行机器学习
偏差、方差和正则化属性
偏差、方差和密切相关的正则化在机器学习领域中占有非常特殊和基础的位置。
偏差发生在机器学习模型过于“简单”时,导致结果与实际值始终存在偏差。
方差发生在模型过于“复杂”时,导致模型在测试数据集上非常准确,但在未见过/新的数据集上表现不佳。
一旦用户熟悉了创建机器学习模型的过程,似乎这个过程非常简单——获取数据,创建训练集和测试集,创建模型,将模型应用于测试数据集,练习就完成了。创建模型很容易;创建一个好的模型则是一个更具挑战性的话题。但是,如何测试模型的质量呢?也许更重要的是,如何构建一个“好的”模型?
答案在于一个叫做正则化的术语。它可能是一个华丽的词,但它的意思就是在创建模型的过程中,通过对训练数据集上过度良好的表现进行惩罚,同时对表现不佳的模型进行放松,从而获得更好的模型。
要理解正则化,了解过拟合和欠拟合的概念会有所帮助。为此,我们来看一个简单但熟悉的例子——拟合最佳线条。对于那些使用过 Microsoft Excel 的人来说,可能注意到有一个选项可以绘制最佳拟合线——本质上,给定一组点,你可以画出一条代表数据的线,并近似表示这些点所代表的函数。
以下表格展示了几处房产的价格与平方英尺的关系。为了确定房价与房屋大小之间的关系,我们可以绘制一条最佳拟合线或趋势线,如下所示:
平方英尺 | 价格 ($) |
---|---|
862 | 170,982 |
1235 | 227,932 |
932 | 183,280 |
1624 | 237,945 |
1757 | 275,921 |
1630 | 274,713 |
1236 | 201,428 |
1002 | 193,128 |
1118 | 187,073 |
1339 | 202,422 |
1753 | 283,989 |
1239 | 228,170 |
1364 | 230,662 |
995 | 169,369 |
1000 | 157,305 |
如果我们使用线性趋势线绘制最佳拟合线,图表可能会像这样:
Excel 提供了一个有用的附加功能,允许用户绘制趋势线的扩展,这可以提供一个估算值,或者一个预测,来预测未知变量。在这种情况下,延长趋势线将基于该函数向我们展示 1,800-2,000 平方英尺范围内房屋的价格。
描述数据的线性函数如下:
y=126.13x + 54,466.81
以下的图表显示了一个扩展的趋势线,表明价格最有可能在$275,000
到$300,000
之间:
然而,有人可能会认为这条线并不是最佳的近似,并且可能通过增加 R²值(在本例中为 0.87)来获得更好的结果。一般来说,R²越高,描述数据的模型越好。R²值有多种不同的类型,但在本节中,我们假设 R²越高,模型越好。
在下一部分中,我们将绘制一个具有更高 R²的新的趋势线,但使用多项式函数。这个函数的 R²更高(0.91 与 0.87),并且在视觉上看起来平均更接近这些点。
在这种情况下,该函数是一个 6^(次)阶多项式:
y = -0.00x⁶ + 0.00x⁵ - 0.00x⁴ + 2.50x³ - 2,313.40x² + 1,125,401.77x - 224,923,813.17
但是,尽管该线的 R²较高,如果我们延长趋势线,目的是找到 1,800-2,000 平方英尺范围内房屋的价格,我们会得到以下结果。
1,800-2,000 平方英尺范围内的房屋价格大约从$280,000 到负$200 万(在 2,000 平方英尺时)。换句话说,购买 1,800 平方英尺房屋的人预计将花费$280,000,而根据这个函数,购买 2,000 平方英尺房屋的人在具有'更高 R²'的情况下,可能会得到$200 万!这当然并不准确,但我们刚刚看到的现象被称为过拟合。下图展示了这一现象。
另一端是欠拟合。当构建的模型无法描述数据时,就会发生这种情况。在下面的图表中,函数 y = 0.25x - 200 就是一个这样的例子:
简而言之,本节可以简要概括如下:
-
一个拟合得太好的函数,能够近似训练数据集中的几乎所有点,被认为是过拟合。
-
一个完全无法拟合数据的函数,换句话说,远离训练数据集中的实际点的函数,被认为是欠拟合。
-
机器学习是一个在数据的过拟合和欠拟合之间平衡的过程。这无疑是一项不容易的任务,这也是为什么即使构建一个模型可能很简单,构建一个合理的、效果较好的模型则是更具挑战性的原因。
-
欠拟合是指你的函数根本没有思考——它有很高的偏差。
-
过拟合是指你的函数思考得太过——它有很高的方差。
-
欠拟合和过拟合的另一个例子将在接下来的例子中给出。
假设我们的任务是判断一堆水果是橙子还是苹果,并且已经给出了它们在果篮中的位置(左侧或右侧)、大小和重量:
![]() |
![]() |
---|---|
篮子 1(训练数据集) | 篮子 2(测试数据集) |
过拟合的一个例子可能是,基于训练数据集,在篮子 1 中我们可能会得出结论,认为篮子右侧的唯一水果是橙子,左侧的所有水果都是苹果。
一个欠拟合的例子可能是我得出结论认为篮子里只有橙子。
模型 1:在第一种情况下——对于过拟合——我实际上已经记住了位置。
模型 2:在第二种情况下——对于欠拟合——我根本没有准确记住任何东西。
现在,给定第二个篮子——测试数据集,其中苹果和橙子的位置信息已经交换——如果我使用模型 1,我会错误地得出结论,认为右侧的所有水果都是橙子,左侧的所有水果都是苹果(因为我记住了训练数据)。
如果我使用模型 2,我会再次错误地得出结论,认为所有水果都是橙子。
然而,确实有一些方法可以管理欠拟合和过拟合之间的平衡——或者换句话说,管理高偏差和高方差之间的平衡。
常用的一种偏差-方差权衡方法是正则化。这是指惩罚模型(例如回归中的模型系数)的过程,以产生一个在多个数据点上都能很好地泛化的输出。
下一页的表格展示了偏差和方差的一些关键概念,并展示了在模型具有高偏差或高方差时的修正步骤:
在建模过程中,高偏差通常表现为训练集误差和测试集误差始终保持较高的水平。对于高方差(过拟合),训练集误差迅速下降,但测试集误差保持不变。
梯度下降和 VC 维度理论
梯度下降和 VC 维度是机器学习中的两个基本理论。通常,梯度下降为寻找函数的最优系数提供了一种结构化的方法。一个函数的假设空间可能非常大,而使用梯度下降时,算法会试图找到一个最小值(极小值),使得代价函数(例如,误差的平方和)最小。
VC 维度提供了系统中可以分类的最大点数的上限。它本质上是衡量函数丰富性的指标,并提供了一种结构化的方式来评估假设的极限。可以被函数或假设精确分类的点数称为该假设的 VC 维度。例如,一个线性边界可以准确分类 2 或 3 个点,但不能分类 4 个点。因此,这个二维空间的 VC 维度是 3。
VC 维度,像许多计算学习理论中的其他主题一样,既复杂又有趣。它是一个较少为人知(也讨论较少)的主题,但它有着深远的影响,因为它试图解答关于学习极限的问题。
流行的机器学习算法
机器学习算法有各种不同的类别。因此,由于算法可以同时属于多个“类别”或“类”,在概念层面上很难明确指出一个算法仅属于单一类别。在本节中,我们将简要讨论一些最常用和最知名的算法。
这些包括:
-
回归模型
-
关联规则
-
决策树
-
随机森林
-
提升算法
-
支持向量机
-
K-means
-
神经网络
请注意,在这些示例中,我们展示了使用整个数据集的 R 函数的基本用法。实际上,我们会将数据分为训练集和测试集,一旦构建了满意的模型,就会将相同的模型应用于测试数据集,以评估模型的表现。
回归模型
回归模型从常用的线性回归、逻辑回归和多重回归算法到岭回归和套索回归等模型不等,这些回归模型通过对系数的惩罚来提高模型性能。
在我们之前的示例中,当我们创建趋势线时,我们看到了线性回归的应用。多重线性回归指的是创建模型的过程需要多个自变量。
例如:
总广告费用 = x * 印刷广告,这将是一个简单的线性回归;而
总广告费用 = X + 平面广告 + 广播广告 + 电视广告,由于有多个独立变量(平面广告、广播广告和电视广告),因此这是一个多元线性回归。
逻辑回归是另一种常用的统计回归建模技术,用于预测离散类别值的结果,主要用于结果变量为二元的情况(例如,0 或 1,Yes 或 No,等等)。然而,也可以有超过两个离散结果(例如,纽约州、纽约州、新泽西州),这种类型的逻辑回归称为多项式逻辑回归。
岭回归和套索回归在普通线性回归的基础上增加了正则化项(λ)。正则化项(岭回归)的效果是减少 β 系数(从而“惩罚”这些系数)。在套索回归中,正则化项通常将一些系数缩减为 0,从而消除了该变量对最终模型的影响:
# Load mlbench and create a regression model of glucose (outcome/dependent variable) with pressure, triceps and insulin as the independent variables.
> library("mlbench")
>lm_model<- lm(glucose ~ pressure + triceps + insulin, data=PimaIndiansDiabetes[1:100,])
> plot(lm_model)
关联规则
关联规则挖掘,或称apriori,试图寻找数据集中变量之间的关系。关联规则通常用于各种实际的现实世界应用中。给定一组变量,apriori 可以指示出事务性数据集中的模式。我们的一些教程将基于实现一个 R Shiny 应用程序来进行 apriori,因此,本节中将更多强调这一部分内容。
例如,假设一个超市连锁正在决定物品在货架上的排列顺序。对包含销售交易的数据库运行 apriori 算法将识别出最常一起购买的物品。例如,这可以帮助超市确定哪些物品在彼此靠近的地方摆放,能够产生最大的销售额。这也通常被称为市场篮子分析。
一个简单的例子可以是这样的:
# The LHS (left-hand side) leads to the RHS (right-hand side) in the relationships shown below.
# For instance, {Milk, Bread} --> {Butter} indicates that someone purchasing milk and bread is also likely to purchase butter.
{Milk, Bread} --> {Butter}
{Butter, Egg} --> {Baking Tray}
{Baking Tray, Butter} --> {Sugar}
...
在所有这些情况下,左侧的购买行为会导致右侧表达式中提到的物品被购买。
也可以从不一定包含交易的数据库中推导关联规则,而是使用滑动窗口通过时间属性沿事件进行遍历,比如使用 WINEPI 算法。
apriori 中有 3 个主要的度量指标。为了说明它们,让我们使用一个包含 4 个独立交易中的物品的示例数据集:
交易 | 物品 1 | 物品 2 | 物品 3 |
---|---|---|---|
1 | 牛奶 | 面包 | 黄油 |
2 | 牛奶 | 鸡蛋 | 黄油 |
3 | 面包 | 鸡蛋 | 奶酪 |
4 | 黄油 | 面包 | 鸡蛋 |
信度
信度指的是当左侧有效时,右侧的 apriori 表达式在多大程度上有效。例如,给定一个表达式:
{Milk} à {Bread}
我们想知道在购买牛奶时,面包被购买的频率是多少。
在这种情况下:
-
交易 1:牛奶和面包都存在
-
交易 2:牛奶存在,但面包不存在
-
交易 3 和 4:牛奶不存在
因此,基于我们看到的情况,有 2 个交易中牛奶是存在的,其中 1 个交易中有面包。因此,规则 {牛奶} à {面包} 的置信度为 ½ = 50%
取另一个表达式:
{Bread} à {Butter}
我们想知道,当面包被购买时,黄油有多频繁地被一起购买?
-
交易 1:面包和黄油都存在
-
交易 2:没有面包(黄油存在,但我们的参考点是面包,因此这不算)
-
交易 3:面包存在,但没有黄油
-
交易 4:面包和黄油都存在
因此,我们在 3 个交易中都有面包,在 3 个交易中的 2 个交易中都有面包和黄油。因此,在这种情况下,规则 {面包} à {黄油}
的“置信度”是 2/3 = 66.7。
支持度
支持度是指规则满足的次数相对于数据集中交易总数的比例。
例如:
{牛奶} --> {面包},出现在 4 个交易中的 1 个交易中(在交易 1 中)。因此,该规则的支持度为 ¼ = 0.25(或 25%)。
{面包} --> {黄油},出现在 4 个交易中的 2 个交易中(在交易 1 和 4 中)。因此,该规则的支持度为 ½ = 0.50(或 50%)。
提升度
提升度可以说是 3 个指标中最重要的一个;它衡量规则的支持度相对于表达式各个部分支持度的比率;换句话说,它衡量了该规则在随机出现的左侧(LHS)和右侧(RHS)条件下的强度。它的正式定义为:
提升度 = 支持度(规则)/(支持度(左侧) * 支持度(右侧))
提升度值较低(例如小于或等于 1)表示左侧和右侧的发生是独立的,而较高的提升度则表示联合发生是显著的。
在我们之前的例子中,
{面包} --> {黄油} 的提升度为:
支持度({面包} --> {黄油})
支持 {面包} * 支持 {黄油}
= 0.50/((3/4) * (3/4)) = 0.50/(0.75 * 0.75) = 0.89。
这表明,尽管规则的置信度较高,但该规则本身相较于可能高于 1 的其他规则并不显著。
提升度高于 1 的规则示例是:
{项目 1:面包} --> {项目 3:奶酪}
这个提升度为:
支持 {项目 1:面包 --> 项目 3:奶酪}/(支持 {项目 1:奶酪} * 支持 {项目 3:奶酪})
= (1/4)/((1/4)*(1/4)) = 4。
决策树
决策树是一种预测建模技术,它生成规则,通过前期结果的可能性推导出某个结果的可能性。一般来说,决策树的构建类似于 流程图,由一系列节点和叶子组成,表示父子关系。没有连接到其他节点的节点被称为叶子节点。
决策树属于一种算法类别,通常被称为CART(分类与回归树)。如果感兴趣的结果是一个分类变量,则属于分类任务;如果结果是一个数值,则称为回归树。
一个例子有助于更清楚地理解这一概念。请看图表:
该图表展示了一个假设场景:如果学校关闭/未关闭。矩形框(蓝色)代表节点。第一个矩形框(学校关闭)代表根节点,而内部矩形框代表内部节点。具有倾斜边缘的矩形框(绿色和斜体字母)代表‘叶子’(或终端节点)。
决策树易于理解,是为数不多的非“黑箱”算法之一。像神经网络这样的算法通常被认为是黑箱,因为很难——如果不是不可能——凭直觉确定最终结果是通过何种路径得出的,因为模型的复杂性使得这一过程难以推断。
在 R 中,有多种方法可以创建决策树。一个常用的库是rpart
,它可以用来在 R 中创建决策树。我们将再次查看PimaIndiansDiabetes
数据集,看看如何使用该包创建决策树。
我们想要创建一个模型,以确定血糖、胰岛素、(体重)质量和年龄与糖尿病之间的关系。请注意,在数据集中,糖尿病是一个分类变量,响应为是/否。
为了可视化决策树,我们将使用rpart.plot
包。相关代码如下:
install.packages("rpart")
install.packages("rpart.plot")
library(rpart)
library(rpart.plot)
rpart_model<- rpart (diabetes ~ glucose + insulin + mass + age, data = PimaIndiansDiabetes)
>rpart_model
n= 768
node), split, n, loss, yval, (yprob)
* denotes terminal node
1) root 768 268 neg (0.6510417 0.3489583)
2) glucose< 127.5 485 94neg (0.8061856 0.1938144) *
3) glucose>=127.5 283 109 pos (0.3851590 0.6148410)
6) mass< 29.95 76 24neg (0.6842105 0.3157895)
12) glucose< 145.5 41 6 neg (0.8536585 0.1463415) *
13) glucose>=145.5 35 17pos (0.4857143 0.5142857)
26) insulin< 14.5 21 8 neg (0.6190476 0.3809524) *
27) insulin>=14.5 14 4 pos (0.2857143 0.7142857) *
7) mass>=29.95 207 57pos (0.2753623 0.7246377)
14) glucose< 157.5 115 45pos (0.3913043 0.6086957)
28) age< 30.5 50 23neg (0.5400000 0.4600000)
56) insulin>=199 14 3 neg (0.7857143 0.2142857) *
57) insulin< 199 36 16pos (0.4444444 0.5555556)
114) age>=27.5 10 3 neg (0.7000000 0.3000000) *
115) age< 27.5 26 9 pos (0.3461538 0.6538462) *
29) age>=30.5 65 18pos (0.2769231 0.7230769) *
15) glucose>=157.5 92 12pos (0.1304348 0.8695652) *
>rpart.plot(rpart_model, extra=102, nn=TRUE)
# The plot shown below illustrates the decision tree that the model, rpart_model represents.
从顶部开始,图表显示数据集中有 500 个diabetes=neg
的案例(总共有 768 条记录)。
> sum(PimaIndiansDiabetes$diabetes=="neg")
[1] 500
在数据集中(共 768 条记录)血糖值小于 128 的记录中,共有 485 条记录被标记为阴性。在这些记录中,模型正确预测了 391 个案例为阴性(节点编号 2,从底部往左数的第一个节点)。
对于血糖值大于 128 的记录,共有 283 条记录被标记为阳性(节点编号 3,紧接着根节点的节点)。该模型正确分类了 174 个案例。
另一个更现代的直观决策树包是FFTrees(快速简洁的决策树)。以下示例仅供参考:
install.packages("FFTrees")
library(caret)
library(mlbench)
library(FFTrees)
set.seed(123)
data("PimaIndiansDiabetes")
diab<- PimaIndiansDiabetes
diab$diabetes<- 1 * (diab$diabetes=="pos")
train_ind<- createDataPartition(diab$diabetes,p=0.8,list=FALSE,times=1)
training_diab<- diab[train_ind,]
test_diab<- diab[-train_ind,]
diabetes.fft<- FFTrees(diabetes ~.,data = training_diab,data.test = test_diab)
plot(diabetes.fft)
# The plot below illustrates the decision tree representing diabetes.fft using the FFTrees package.
决策树通过递归地拆分数据,直到达到停止标准,如达到某个深度或案例数低于指定值。每次拆分都基于能产生“更纯子集”的变量。
原则上,我们可以从给定的一组变量中生长无限数量的树,这使得这个问题变得特别复杂且难以处理。存在许多算法提供高效的决策树拆分和创建方法,其中之一是亨特算法(Hunt's Algorithm)。
有关该算法的更多详细信息可以在此处找到:www-users.cs.umn.edu/~kumar/dmbook/ch4.pdf
。
随机森林扩展
随机森林是我们刚才讨论的决策树模型的扩展。实际上,决策树简单易懂,易于解释,使用现有算法快速创建,且总体上直观。然而,决策树对数据中的微小变化敏感,只允许沿着一个轴(线性划分)进行分裂,并可能导致过拟合。为了减轻决策树的一些缺点,同时仍然能享受到其简洁性的好处,诸如随机森林之类的算法通过创建多个决策树并随机选择特征来利用并构建一个聚合模型。
随机森林的原理是自助聚合(bootstrap aggregating)或袋装法(bagging)。自助法是一个统计学术语,表示带有替换的随机抽样。自助抽样一组记录意味着从中随机抽取记录,并可能多次包含相同的记录。随后,用户会在样本上衡量他们感兴趣的指标,并重复这个过程。通过这种方式,从多次随机抽样计算得到的指标值的分布预计能够代表总体的分布,从而代表整个数据集。
对一组 3 个数字(如 1,2,3,4)进行袋装法的示例是:
(1,2,3),(1,1,3),(1,3,3),(2,2,1),以及其他组合。
自助聚合(Bootstrap Aggregating),或称为袋装法,意味着利用投票方法,同时使用多个自助样本,在每个单独的样本(n 条记录)上建立一个模型,然后最终聚合结果。
随机森林还实现了超越简单袋装法的另一个操作层次。它还会在每次分裂时随机选择要包含在模型构建过程中的变量。例如,如果我们使用PimaIndiansDiabetes
数据集,并包括怀孕、葡萄糖、血压、三头肌、胰岛素、体重、家族史、年龄和糖尿病变量,在每次自助抽样(抽取 n 条记录)中,我们会选择一个随机的特征子集来构建模型——例如,葡萄糖、血压和胰岛素;胰岛素、年龄和家族史;三头肌、体重和胰岛素;等等。
在 R 中,常用的随机森林包名为 RandomForest。我们可以通过该包直接使用它,或者通过 caret 包来使用。两种方法如下所示:
- 使用 RandomForest 包来进行随机森林建模:
> rf_model1 <- randomForest(diabetes ~ ., data=PimaIndiansDiabetes) > rf_model1 Call: randomForest(formula = diabetes ~ ., data = PimaIndiansDiabetes)
Type of random forest: classification Number of trees: 500 No. of variables tried at each split: 2 OOB estimate of error rate: 23.44% Confusion matrix: negposclass.error neg430 70 0.1400000 pos 110 158 0.4104478
- 使用 caret 包通过
method="rf"
函数来使用随机森林:
> library(caret)
> library(doMC)
# THE NEXT STEP IS VERY CRITICAL - YOU DO 'NOT' NEED TO USE MULTICORE
# NOTE THAT THIS WILL USE ALL THE CORES ON THE MACHINE THAT YOU ARE
# USING TO RUN THE EXERCISE
# REMOVE THE # MARK FROM THE FRONT OF registerDoMC BEFORE RUNNING
# THE COMMAND
># registerDoMC(cores = 8) # CHANGE NUMBER OF CORES TO MATCH THE NUMBER OF CORES ON YOUR MACHINE
>rf_model<- train(diabetes ~ ., data=PimaIndiansDiabetes, method="rf")
>rf_model
Random Forest
768 samples
8 predictor
2 classes: 'neg', 'pos'
No pre-processing
Resampling: Bootstrapped (25 reps)
Summary of sample sizes: 768, 768, 768, 768, 768, 768, ...
Resampling results across tuning parameters:
mtry Accuracy Kappa
2 0.7555341 0.4451835
5 0.7556464 0.4523084
8 0.7500721 0.4404318
Accuracy was used to select the optimal model using the largest value.
The final value used for the model was mtry = 5\.
>getTrainPerf(rf_model)
TrainAccuracyTrainKappa method
1 0.7583831 0.4524728rf
也可以查看原始随机森林模型(未使用 caret)中每棵树的分割情况及其他相关信息。这可以通过如下方式使用getTree
函数来完成:
>getTree(rf_model1,1,labelVar = TRUE)
left daughter right daughter split var split point status prediction
1 2 3 mass 27.8500 1 <NA>
2 4 5 age 28.5000 1 <NA>
3 6 7 glucose 155.0000 1 <NA>
4 8 9 age 27.5000 1 <NA>
5 10 11 mass 9.6500 1 <NA>
6 12 13 pregnant 7.5000 1 <NA>
7 14 15 insulin 80.0000 1 <NA>
8 0 0 <NA> 0.0000 -1 neg
9 16 17 pressure 68.0000 1 <NA>
10 0 0 <NA> 0.0000 -1 pos
11 18 19 insulin 131.0000 1 <NA>
12 20 21 insulin 87.5000 1 <NA>
[...]
Boosting 算法
Boosting 是一种使用权重和一组弱学习器(如决策树)来提高模型性能的技术。Boosting 根据模型的错误分类为数据分配权重,随后学习器(在 Boosting 机器学习过程中创建的)将重点关注错误分类的例子。正确分类的例子将被重新分配新的权重,通常低于那些未正确分类的例子。权重可以基于成本函数,例如使用数据子集的多数投票。
用简单且非技术性的术语来说,Boosting 使用一系列弱学习器,每个学习器从前一个学习器的错误中“学习”。
与 Bagging 相比,Boosting 通常更受欢迎,因为它根据模型性能分配权重,而不是像 Bagging 那样对所有数据点分配相等的权重。这在概念上类似于加权平均与无权重平均之间的区别。
在 R 中有多个用于 Boosting 算法的包,其中一些常用的如下:
-
Adaboost
-
GBM(随机梯度提升)
-
XGBoost
其中,XGBoost 是一个广泛流行的机器学习包,已经在 Kaggle 等竞争性机器学习平台中得到了非常成功的应用。XGBoost 有一种非常优雅且计算高效的方式来创建集成模型。由于其高精度和极快的计算速度,用户常常在计算密集型的机器学习挑战中使用 XGBoost。你可以在www.kaggle.com
了解更多关于 Kaggle 的信息。
# Creating an XGBoost model in R
library(caret)
library(xgboost)
set.seed(123)
train_ind<- sample(nrow(PimaIndiansDiabetes),as.integer(nrow(PimaIndiansDiabetes)*.80))
training_diab<- PimaIndiansDiabetes[train_ind,]
test_diab<- PimaIndiansDiabetes[-train_ind,]
diab_train<- sparse.model.matrix(~.-1, data=training_diab[,-ncol(training_diab)])
diab_train_dmatrix<- xgb.DMatrix(data = diab_train, label=training_diab$diabetes=="pos")
diab_test<- sparse.model.matrix(~.-1, data=test_diab[,-ncol(test_diab)])
diab_test_dmatrix<- xgb.DMatrix(data = diab_test, label=test_diab$diabetes=="pos")
param_diab<- list(objective = "binary:logistic",
eval_metric = "error",
booster = "gbtree",
max_depth = 5,
eta = 0.1)
xgb_model<- xgb.train(data = diab_train_dmatrix,
param_diab, nrounds = 1000,
watchlist = list(train = diab_train_dmatrix, test = diab_test_dmatrix),
print_every_n = 10)
predicted <- predict(xgb_model, diab_test_dmatrix)
predicted <- predicted > 0.5
actual <- test_diab$diabetes == "pos"
confusionMatrix(actual,predicted)
# RESULT
Confusion Matrix and Statistics
Reference
Prediction FALSE TRUE
FALSE 80 17
TRUE 21 36
Accuracy : 0.7532
95% CI : (0.6774, 0.8191)
No Information Rate : 0.6558
P-Value [Acc> NIR] : 0.005956
Kappa : 0.463
Mcnemar's Test P-Value : 0.626496
Sensitivity : 0.7921
Specificity : 0.6792
PosPredValue : 0.8247
NegPredValue : 0.6316
Prevalence : 0.6558
Detection Rate : 0.5195
Detection Prevalence : 0.6299
Balanced Accuracy : 0.7357
'Positive' Class : FALSE
支持向量机
支持向量机,通常称为SVMs,是另一类机器学习算法,用于通过一个叫做超平面的概念将数据分类到不同类别中,超平面用于标定点之间的线性边界。
例如,给定一组黑白点在 x-y 轴上的分布,我们可以找到多条线来将它们分开。在这种情况下,线条代表了划定每个点所属类别的函数。在下图中,H1 和 H2 两条线都能准确分开这些点。那么,如何判断 H1 和 H2 哪一条线是最佳的分割线呢?:
直观上,我们可以说与点最接近的直线——例如垂直线 H1——可能不是分隔这些点的最佳直线。由于这条线与点太接近,因此对于给定数据集中的点来说,过于具体。如果一个新点稍微偏离直线的左右侧,它可能会被误分类。换句话说,这条线对数据中的小变化过于敏感(这些小变化可能是由于随机/确定性噪声,比如数据中的不完美所引起的)。
另一方面,直线 H2 成功地分隔了数据,并且保持了与最接近直线的点之间的最大可能距离。数据中的轻微不完美不太可能像直线 H1 那样影响点的分类。本质上,这描述了最大分隔边距的原则,如下图所示。
与直线,也称为超平面,接近的点被称为“支持向量”(因此得名)。在图中,位于虚线上的点就是支持向量。
然而,在现实世界中,并非所有的点都是“线性可分”的。支持向量机(SVM)利用了一个叫做“核技巧”的概念。本质上,可能无法线性分隔的点可以通过投影或映射到更高维的表面。例如,给定一组在二维 x-y 平面上的点,它们是不可线性分隔的,但如果我们将这些点投影到三维空间中,可能就能将它们分开。如下面的图片所示,那些用红色标出的点在二维线性分隔中无法分开,但当映射到三维表面时,它们可以通过超平面分开,如下图所示:
R 中有几个包可以让用户使用支持向量机(SVM),例如 kernlab
、e1071
、klaR
等。这里,我们展示了如何使用 e1071
包中的 SVM,代码如下:
library(mlbench)
library(caret)
library(e1071)
set.seed(123)
data("PimaIndiansDiabetes")
diab<- PimaIndiansDiabetes
train_ind<- createDataPartition(diab$diabetes,p=0.8,list=FALSE,times=1)
training_diab<- diab[train_ind,]
test_diab<- diab[-train_ind,]
svm_model<- svm(diabetes ~ ., data=training_diab)
plot(svm_model,training_diab, glucose ~ mass)
# The plot below illustrates the areas that are classified 'positive' and 'negative'
# Creating and evaluating the Confusion Matrix for the SVM model
svm_predicted<- predict(svm_model,test_diab[,-ncol(test_diab)])
confusionMatrix(svm_predicted,test_diab$diabetes)
Confusion Matrix and Statistics
Reference
Prediction negpos
neg 93 26
pos7 27
Accuracy : 0.7843
95% CI : (0.7106, 0.8466)
No Information Rate : 0.6536
P-Value [Acc> NIR] : 0.0003018
Kappa : 0.4799
Mcnemar's Test P-Value : 0.0017280
Sensitivity : 0.9300
Specificity : 0.5094
PosPredValue : 0.7815
NegPredValue : 0.7941
Prevalence : 0.6536
Detection Rate : 0.6078
Detection Prevalence : 0.7778
Balanced Accuracy : 0.7197
'Positive' Class :neg
K-Means 机器学习技术
K-Means 是最流行的无监督机器学习技术之一,用于创建聚类,从而对数据进行分类。
一个直观的例子可以这样提出:
假设一所大学正在提供一门关于美国历史和亚洲历史的新课程。该大学保持 15:1 的师生比例,即每 15 个学生配 1 名教师。它进行了一项调查,调查内容是每位学生根据自己对学习美国历史或亚洲历史的偏好,给出了一个 10 分制的数字评分。
我们可以使用 R 中内置的 K-Means 算法创建 2 个聚类,并且通过每个聚类中的点的数量,可以推测每门课程可能会有多少学生报名。相应的代码如下所示:
library(data.table)
library(ggplot2)
library()
historyData<- fread("~/Desktop/history.csv")
ggplot(historyData,aes(american_history,asian_history)) + geom_point() + geom_jitter()
historyCluster<- kmeans(historyData,2) # Create 2 clusters
historyData[,cluster:=as.factor(historyCluster$cluster)]
ggplot(historyData, aes(american_history,asian_history,color=cluster)) + geom_point() + geom_jitter()
# The image below shows the output of the ggplot command. Note that the effect of geom_jitter can be seen in the image below (the points are nudged so that overlapping points can be easily visible)
以下图像可能提供一个直观的估算,展示可能报名参加每门课程的学生数量(从而确定可能需要多少教师):
K-Means 算法有几种变体,但标准且最常用的是 Lloyd 算法。算法步骤如下:
给定一组 n 个点(假设在 x-y 坐标轴上),为了找到 k 个聚类:
-
从数据集中随机选择 k 个点作为 k 个聚类的中点(即初始质心)。
-
计算每个其他点与选定的 k 个点(代表 k 个聚类)之间的距离,并将其分配给距离该点最近的聚类。
-
聚类中心被重新计算为聚类中所有点的均值。
-
再次计算质心与所有其他点之间的距离,如步骤 2 所示,并按照步骤 3 重新计算新的质心。以这种方式,步骤 2 和步骤 3 会重复进行,直到没有新的数据被重新分配。
存在多种距离和相似度度量用于聚类,例如欧几里得距离(直线距离)、余弦相似度(向量间角度的余弦值)、汉明距离(通常用于分类变量)、马氏距离(以 P.C.马哈拉诺比斯命名;它度量一个点与分布均值之间的距离)等。
尽管最优的聚类数并非总是可以明确确定,但有多种方法试图找到一个估算值。一般来说,聚类可以通过以下方式衡量:聚类内点之间的距离有多近(即聚类内的方差,如平方和 WSS),以及聚类之间的距离有多远(较大的聚类间距使得聚类更容易区分)。其中一种用于确定最优聚类数的方法叫做肘部法则。以下图表说明了这一概念:
图表显示了 WSS(我们试图最小化的聚类内平方和)与聚类数的关系。显然,从 1 个聚类增加到 2 个聚类时,WSS 值大幅下降。WSS 值在增加到第 4 或第 5 个聚类时快速减少,之后再增加聚类并没有显著改善 WSS 值。通过视觉评估,机器学习实践者可以得出结论,理想的聚类数在 3 到 5 之间,如图所示。
请注意,较低的 WSS 分数不足以确定最优的聚类数。必须通过检查度量的改进来完成。WSS 最终会降到 0,当每个点成为一个独立的聚类时。
与神经网络相关的算法
神经网络相关算法已经存在了几十年。第一个计算模型由沃伦·麦卡洛克和沃尔特·皮茨于 1943 年在《数学生物物理学公报》中描述。
你可以在pdfs.semanticscholar.org/5272/8a99829792c3272043842455f3a110e841b1.pdf
和en.wikipedia.org/wiki/Artificial_neural_network
上了解更多这些概念。
许多物理世界中的人造物体,如飞机,都从自然中获得了灵感。神经网络本质上是人类神经系统中轴突和树突(也称为树突)的数据交换现象的表现。就像数据从一个神经元传递到多个其他神经元以做出复杂决策一样,人工神经网络以类似的方式创建一个神经元网络,接收来自其他神经元的输入。
从高层次看,一个人工神经网络由四个主要组成部分构成:
-
输入层
-
隐藏层
-
输出层
-
节点和权重
这在下图中得到了体现:
图中的每个节点都基于来自前一层的输入产生输出。输出是通过激活函数生成的。激活函数有多种类型,输出的结果取决于使用的函数类型。例如包括二值阶跃函数(0 或 1)、tanh 函数(-1 到+1 之间)、sigmoid 函数等。
以下图示说明了这一概念:
值 x1 和 x2 是输入,w1 和 w2 表示权重,节点表示输入及其权重被评估并通过激活函数产生特定输出的点。因此,输出 f 可以表示为:
这里,f 表示激活函数,b 表示偏置项。偏置项独立于权重和输入值,允许用户调整输出,以实现更好的模型性能。
拥有多个隐藏层(通常为 2 层或更多)的神经网络计算量大,近年来,拥有多个隐藏层的神经网络,也被称为深度神经网络或更广义上的深度学习,已经变得非常流行。
许多由机器学习和人工智能推动的行业发展,直接源自这些多层神经网络的实现。
在 R 中,nnet
包提供了一个易于使用的神经网络接口。尽管实际上,神经网络通常需要复杂的硬件、GPU 卡等来进行展示,但为了示范,我们利用了nnet
包在PimaIndiansDiabetes
数据集上运行了先前的分类练习。在这个例子中,我们将利用 caret 来执行nnet
模型:
library(mlbench)
library(caret)
set.seed(123)
data("PimaIndiansDiabetes")
diab<- PimaIndiansDiabetes
train_ind<- createDataPartition(diab$diabetes,p=0.8,list=FALSE,times=1)
training_diab<- diab[train_ind,]
test_diab<- diab[-train_ind,]
nnet_grid<- expand.grid(.decay = c(0.5,0.1), .size = c(3,5,7))
nnet_model<- train(diabetes ~ ., data = training_diab, method = "nnet", metric = "Accuracy", maxit = 500, tuneGrid = nnet_grid)
# Generating predictions using the neural network model
nnet_predicted <- predict(nnet_model, test_diab)
> plot (nnet_model)
# Confusion Matrix for the Neural Network model
confusionMatrix(nnet_predicted,test_diab$diabetes)
Confusion Matrix and Statistics
Reference
Prediction negpos
neg 86 22
pos 14 31
Accuracy : 0.7647
95% CI : (0.6894, 0.8294)
No Information Rate : 0.6536
P-Value [Acc> NIR] : 0.001988
Kappa : 0.4613
Mcnemar's Test P-Value : 0.243345
Sensitivity : 0.8600
Specificity : 0.5849
PosPredValue : 0.7963
NegPredValue : 0.6889
Prevalence : 0.6536
Detection Rate : 0.5621
Detection Prevalence : 0.7059
Balanced Accuracy : 0.7225
'Positive' Class :neg
教程 - 使用 CMS 数据进行关联规则挖掘
本教程将实现一个接口,用于访问使用 R 中的 Apriori 包创建的规则。
我们将从 CMS OpenPayments 网站下载数据。该网站提供了有关公司向医生和医院支付的款项数据:
该网站提供了多种下载数据的方式。用户可以选择感兴趣的数据集并手动下载。在我们的案例中,我们将使用所有用户都可以访问的基于 Web 的 API 来下载数据。
下载数据
该数据集可以通过 Unix 终端(在虚拟机中)或直接通过浏览器访问该网站进行下载。如果您是在虚拟机中下载数据集,请在终端窗口中运行以下命令:
time wget -O cms2016_2.csv 'https://openpaymentsdata.cms.gov/resource/vq63-hu5i.csv?$query=select Physician_First_Name as firstName,Physician_Last_Name as lastName,Recipient_City as city,Recipient_State as state,Submitting_Applicable_Manufacturer_or_Applicable_GPO_Name as company,Total_Amount_of_Payment_USDollars as payment,Nature_of_Payment_or_Transfer_of_Value as paymentNature,Product_Category_or_Therapeutic_Area_1 as category,Name_of_Drug_or_Biological_or_Device_or_Medical_Supply_1 as product where covered_recipient_type like "Covered Recipient Physician" and Recipient_State like "NY" limit 1200000'
或者,如果您是通过浏览器下载数据,请在浏览器窗口中输入以下 URL 并按Enter:
如下图所示:
编写 Apriori 的 R 代码
如前所述,Apriori 算法允许用户发现数据集中的关系或模式。为此,我们将使用 R/RStudio 中的 arules 包。代码将读取下载的数据集(在示例中为cms2016_2.csv
)并运行 Apriori 算法来查找关联规则。
在 RStudio 中创建一个新的 R 文件,并输入以下代码。确保将你下载的 CSV 文件的位置更改为存储文件的适当目录:
library(data.table)
library(arules)
cms<- fread("~/cms2016_2.csv") # CHANGE THIS TO YOUR LOCATION OF THE DATA
cols <- c("category","city","company","firstName","lastName","paymentNature","product")
cms[ ,(cols) := lapply(.SD, toupper), .SDcols = cols]
cms[,payment:=as.numeric(payment)]
quantile_values<- quantile(cms$payment,seq(0,1,.25))
interval_values<- findInterval(cms$payment,quantile_values,rightmost.closed=TRUE)
cms[,quantileVal:=factor(interval_values, labels=c("0-25","25-50","50-75","75-100"))]
rules_cols<- c("category","city","company","paymentNature","product","quantileVal")
cms[ ,(rules_cols) := lapply(.SD, factor), .SDcols = rules_cols]
cms_factor<- cms[,.(category,city,company,paymentNature,product,quantileVal)]
rhsVal<- paste0("quantileVal","=",c("0-25","25-50","50-75","75-100"))
cms_rules<- apriori(cms_factor,parameter=list(supp=0.001,conf=0.25,target="rules",minlen=3))
cms_rules_dt<- data.table(as(cms_rules,"data.frame"))
cms_rules_dt[, c("LHS", "RHS") := tstrsplit(rules, "=>", fixed=TRUE)]
num_cols<- c("support","confidence","lift")
cms_rules_dt[,(num_cols) := lapply(.SD, function(x){round(x,2)}), .SDcols = num_cols]
saveRDS(cms_rules_dt,"cms_rules_dt.rds")
saveRDS(cms_factor,"cms_factor_dt.rds")
Shiny(R 代码)
在 RStudio 中,选择文件 | 新建文件 | Shiny Web 应用:
在app.R
中输入以下代码:
# Packt: Big Data Analytics
# Chapter 8 Tutorial
library(shiny)
library(shinydashboard)
library(data.table)
library(DT)
library(shinyjs)
cms_factor_dt<- readRDS("~/r/rulespackt/cms_factor_dt.rds")
cms_rules_dt<- readRDS("~/r/rulespackt/cms_rules_dt.rds")
# Define UI for application that draws a histogram
ui<- dashboardPage (skin="green",
dashboardHeader(title = "Apriori Algorithm"),
dashboardSidebar(
useShinyjs(),
sidebarMenu(
uiOutput("company"),
uiOutput("searchlhs"),
uiOutput("searchrhs"),
uiOutput("support2"),
uiOutput("confidence"),
uiOutput("lift"),
downloadButton('downloadMatchingRules', "Download Rules")
)
),dashboardBody(
tags$head(
tags$link(rel = "stylesheet", type = "text/css", href = "packt2.css"),
tags$link(rel = "stylesheet", type = "text/css", href = "//fonts.googleapis.com/css?family=Fanwood+Text"),
tags$link(rel = "stylesheet", type = "text/css", href = "//fonts.googleapis.com/css?family=Varela"),
tags$link(rel = "stylesheet", type = "text/css", href = "fonts.css"),
tags$style(type="text/css", "select { max-width: 200px; }"),
tags$style(type="text/css", "textarea { max-width: 185px; }"),
tags$style(type="text/css", ".jslider { max-width: 200px; }"),
tags$style(type='text/css', ".well { max-width: 250px; padding: 10px; font-size: 8px}"),
tags$style(type='text/css', ".span4 { max-width: 250px; }")
),
fluidRow(
dataTableOutput("result")
)
),
title = "Aprior Algorithm"
)
# Define server logic required to draw a histogram
server <- function(input, output, session) {
PLACEHOLDERLIST2 <- list(
placeholder = 'Select All',
onInitialize = I('function() { this.setValue(""); }')
)
output$company<- renderUI({
datasetList<- c("Select All",as.character(unique(sort(cms_factor_dt$company))))
selectizeInput("company", "Select Company" ,
datasetList, multiple = FALSE,options = PLACEHOLDERLIST2,selected="Select All")
})
output$searchlhs<- renderUI({
textInput("searchlhs", "Search LHS", placeholder = "Search")
})
output$searchrhs<- renderUI({
textInput("searchrhs", "Search RHS", placeholder = "Search")
})
output$support2 <- renderUI({
sliderInput("support2", label = 'Support',min=0,max=0.04,value=0.01,step=0.005)
})
output$confidence<- renderUI({
sliderInput("confidence", label = 'Confidence',min=0,max=1,value=0.5)
})
output$lift<- renderUI({
sliderInput("lift", label = 'Lift',min=0,max=10,value=0.8)
})
dataInput<- reactive({
print(input$support2)
print(input$company)
print(identical(input$company,""))
temp <- cms_rules_dt[support > input$support2 & confidence >input$confidence& lift >input$lift]
if(!identical(input$searchlhs,"")){
searchTerm<- paste0("*",input$searchlhs,"*")
temp <- temp[LHS %like% searchTerm]
}
if(!identical(input$searchrhs,"")){
searchTerm<- paste0("*",input$searchrhs,"*")
temp <- temp[RHS %like% searchTerm]
}
if(!identical(input$company,"Select All")){
# print("HERE")
temp <- temp[grepl(input$company,rules)]
}
temp[,.(LHS,RHS,support,confidence,lift)]
})
output$downloadMatchingRules<- downloadHandler(
filename = "Rules.csv",
content = function(file) {
write.csv(dataInput(), file, row.names=FALSE)
}
)
output$result<- renderDataTable({
z = dataInput()
if (nrow(z) == 0) {
z <- data.table("LHS" = '', "RHS"='', "Support"='', "Confidence"='', "Lift" = '')
}
setnames(z, c("LHS", "RHS", "Support", "Confidence", "Lift"))
datatable(z,options = list(scrollX = TRUE))
})
} shinyApp(ui = ui, server = server)
下图展示了代码被复制并保存到名为app.R
的文件中的过程。
为应用程序使用自定义 CSS 和字体
对于我们的应用程序,我们将使用自定义 CSS 文件。同时,我们还将使用自定义字体,以便为应用程序提供良好的外观和感觉。
你可以从本书的软件库下载自定义 CSS 文件。
CSS、字体和其他相关文件应该存储在你创建 R Shiny 应用程序的目录中的名为www
的文件夹里:
运行应用程序
如果一切顺利,你现在应该能够通过点击页面顶部的“运行应用程序”选项来运行应用程序,具体请参考下图:
点击“运行”按钮后,用户将看到一个弹出窗口,类似于下方所示的内容。请注意,浏览器需要启用弹窗功能才能正常运行。
该应用程序有多个控件,如下所示:
-
搜索 LHS/RHS:在规则的左侧(LHS)或右侧(RHS)输入你希望过滤的任何测试内容。
-
支持度:表示规则在数据集中的普及程度。
-
置信度:规则中的精确匹配数量。
-
Lift:定义规则重要性的变量。大于 1 的数字被认为是显著的。
只要它们以类似于 R 脚本部分中描述的方式处理,你可以将此应用程序用于任何其他规则文件。
总结
机器学习从业者通常认为创建模型很容易,但创建一个好的模型则要困难得多。事实上,创建一个好的模型很重要,但或许更重要的是,知道如何识别一个好的模型,这才是成功的机器学习项目与不太成功的项目之间的关键区别。
在本章,我们学习了一些机器学习中的深层理论概念。我们通过实例解释了偏差、方差、正则化等常见概念,并在需要时进行了详细说明。通过附带的 R 代码,我们还学习了如随机森林、支持向量机等常见的机器学习算法。最后,我们通过一个教程,讲解了如何为 CMS OpenPayments 数据创建一个全面的基于 Web 的关联规则挖掘应用程序。
在下一章,我们将了解一些正在企业中使用的技术,这些技术既适用于大数据,也适用于机器学习。我们还将讨论云计算的优点,以及它们如何影响企业软件和硬件堆栈的选择。
第九章:企业数据科学
到目前为止,我们已经讨论了有关数据挖掘和机器学习的各种主题。大多数展示的示例设计得足够简单,以至于任何拥有标准计算机的人都能够运行它们并完成练习。在现实世界中,数据集的规模通常远大于一般家庭使用中遇到的数据。
传统上,我们依赖 SQL Server、Oracle 等知名数据库技术来进行组织数据仓库和数据管理。NoSQL 和基于 Hadoop 的解决方案的出现,对这一操作模式带来了重大变化。虽然公司最初不愿意采用这些工具,但它们的流行吸引力变得不可忽视,如今,大多数(如果不是全部的话)大组织都在其企业数据需求中采用一种或多种非传统的现代解决方案。
此外,云计算的出现已彻底改变了大多数企业,内部数据中心正在迅速被基于云的基础设施所取代。云计算领域的主要市场领导者是亚马逊(Amazon Web Services)、微软(Azure),以及在一定程度上谷歌(Google Compute Engine)。
数据仓储、数据科学和机器学习需求主要通过这些平台提供。
本节我们将介绍在企业市场中常见的各种技术平台,分析它们的优缺点、应用场景以及潜在的陷阱。此外,我们还将通过使用 AWS 进行教程,使用试用账户按需启动新实例。
本章将涵盖以下主题:
-
企业数据科学概览
-
企业数据挖掘
-
企业人工智能和机器学习
-
企业基础设施
-
其他考虑因素,例如数据策略、治理和工具选择
-
亚马逊 Web 服务教程
企业数据科学概览
数据科学在企业 IT 和分析领域相对较新。传统上,研究人员和分析师大致分为两类:
-
高度专业的研究人员,他们使用复杂的计算语言和/或硬件来完成他们的专业任务
-
分析师能够使用如 Excel 和商业智能平台等工具进行简单和复杂的数据分析
组织在 2000 年代末开始关注大数据,以及更广泛的,数据科学平台。到 2013 年,随着 Hadoop 和 NoSQL 平台等解决方案的发布,它已经获得了巨大的发展势头。下表展示了数据科学的发展:
年份 | 发展 |
---|---|
1970 年代至 1990 年代末 | 关系数据库管理系统的广泛使用。实体关系模型、结构化查询语言(SQL)及其他发展最终导致了 90 年代末数据库的快速扩展。 |
2000 年代初 | Y2K 的反高潮,尽管代价昂贵,且互联网泡沫的破裂,导致了一段停滞期。就数据库或更广义上的数据挖掘平台而言,这意味着公司不再像之前那样专注于新创新,而是将更多精力集中在维持业务运转上。 |
| 2005-2010 年 | 行业逐渐恢复,但直到 2005 年,新的发展才开始出现。一些值得注意的事件包括:
-
2006 年:发布 Google BigTable 论文
-
2006 年:亚马逊云服务平台推出
-
2007 年:发布亚马逊 Dynamo 论文
-
2008 年:Facebook 将 Cassandra 开源
-
2009 年:MongoDB 发布
-
2009 年:Redis 发布
|
2010-2012 年 | 2010 年:NoSQL 会议和相关活动开始获得关注,NoSQL成为一个被广泛接受的技术术语。同时,Hadoop 开始广泛流行,几乎所有主要公司都开始实施与 Hadoop 相关的技术。2011 年:市场领导者开始采纳大数据并制定大数据战略。大量关于大数据巨大潜力的文章和研究论文使其变得非常流行。麦肯锡发布了关于大数据的论文,并将其称为创新、竞争和生产力的下一个前沿。2012 年 10 月的《哈佛商业评论》对数据科学家的前景给予了非常积极的评价,并迅速流行起来。 |
---|---|
2013-2015 年 | 大数据技术的发展催生了一个名为数据科学的概念,将关注点从数据本身转向数据的价值。结合机器学习的发展以及 R、Python 等数据科学平台的流行,行业将焦点从单纯的数据管理转向了从数据中获取洞察。机器学习成为新的热词。 |
2016 年-至今 | 智能设备、可穿戴设备、支持 AI 的手机、自动驾驶汽车等创新解决方案的演变,为现有的大数据和机器学习趋势增添了人工智能的新元素。制造商开始广泛宣传技术解决方案的智能能力,而不仅仅是机器学习能力。 |
实施大数据平台或更广义上的大数据计划的责任通常由公司的 IT 或分析部门承担(如果存在此类部门的话)。
在对大数据和数据科学团队在组织中的普遍调查中,我们观察到,在大多数情况下,首席信息官(CIO)或首席数据/数字官负责企业大数据战略,如以下图所示:
尽管分析和 IT 团队发挥了重要作用,但毫不奇怪,最终的责任还是由公司高层管理层承担。
企业在大数据方面的投资也各不相同,大多数组织的投资额在$100k 到$1M 之间。对中型/大型组织的分析得出了预期的结果。然而,显而易见的是,几乎所有受访者至少在大数据方面做出了一定的投资:
大多数组织还报告称,他们有高级分析的公司任务。这帮助确保了获得所需的预算,以实施和推进组织内的分析状态。
此外,数据科学的收入潜力预测大大帮助了向高层管理层展示,适当的对大数据的投资对公司未来的增长至关重要。
随着大数据和商业分析的收入目前已经呈指数级增长,超过了 1500 亿美元,企业在实施这些能力上的压力——至少是在初步层面上——巨大。
组织对大数据作为公司使命的认知与接受度的另一个方面是对这些工具效用的文化认知。在一项针对大公司 C 级管理层的调查中,大多数受访者表示,管理人员在其部门中使用了分析工具,但各部门之间的参与度并不统一,正如下图所示:
此外,调查显示,组织内部的大数据合作伙伴关系战略也未能达到商业成功所需的结构化程度。调查中的受访者表示,分析计划的跨职能协作伙伴关系定义不明确:
尽管对某些问题的回答存在偏差,其中一个类别的回应占压倒性多数,但关于大数据和分析的一般组织挑战的反馈却表现出广泛的一致性。以下图表展示了调查中每个参与者对分析挑战的反馈:
所有这些都导致了企业中一个有趣但充满悖论的分析困境。尽管大数据和分析的价值被广泛理解和接受,但关于适当方法的模糊感仍然存在,正如下图所示:
企业分析成功的路线图
根据我们的经验,分析这个相对于数据仓库等已经建立的术语而言较为新的术语,需要谨慎的处理方法,以确保既能取得即时成功,又能保证该计划的长期可持续性。
试图通过大规模、高预算的投入过早完成初始分析项目的项目,如果项目结果未如预期,可能会危及整个计划。
此外,在此类项目中,结果衡量标准并不明确。换句话说,衡量结果的价值是模糊的。有时,这甚至无法量化。这是因为分析项目的成功不仅仅有即时的货币或技术能力收益。一个成功的分析项目通常有助于提升高层对部门执行此类项目能力的信心,这反过来可能会导致更大的项目。
与大数据分析相关的一般挑战如下:
-
几乎每家公司都在投资大数据、机器学习和人工智能。
-
通常,公司有一个企业任务。
-
寻找合适的使用案例可能具有挑战性。
-
即使你找到了它们,结果可能仍然不确定(这是否会引起共鸣,多久才能完成,等等)。
-
即使你实现了它们,是否识别出最佳目标仍然可能难以捉摸(例如,在仅用于存储数据时使用 HDFS)。
现在,让我们看看一些数据科学和分析项目的通用指南:
-
与公司内的业务伙伴召开会议和一对一评审,审视他们的工作流程,并反馈数据分析和/或数据挖掘能够提供最大价值的地方。
-
识别与公司运营相关且重要的具体方面,这些方面与公司的收入来源相关;一旦完成,使用案例将产生可衡量的影响。
-
使用案例不必是复杂的;它们可以是简单的任务,例如机器学习或数据挖掘。
-
直观、易于理解,你可以向朋友和家人解释。
-
理想情况下,今天使用常规方法完成该使用案例需要一定的努力。解决方案不仅应惠及广泛的用户群体,还应具有高层可见性。
-
识别低难度 - 高价值(短期)与高难度 - 高价值(长期)使用案例。
-
教育业务赞助商,分享想法,展示热情(就像一场长时间的工作面试)。
-
为低难度 - 高价值赢得早期胜利,创建最小可行解决方案,并在进一步增强已开发的解决方案之前获得管理层的支持。(这需要时间)
早期的胜利充当催化剂,a) 促进高层信任,b) 也使预算更容易获得,从而使得转向高难度 - 高价值任务变得更加容易。
最后两点很重要,因为识别低难度 - 高价值项目是至关重要的。这可能是一个对经验丰富的从业者来说显得基础的任务,但对最终用户来说却非常有价值。
一位大型企业分析团队的高管曾经提到,今年最成功的项目是电子邮件报告发送时机的变化。报告的发送时间由早晨改为下午晚些时候。结果表明,报告在改变发送时机后,用户的参与度变得更加活跃。早晨的日程通常非常繁忙,而下午发送报告则能给收件人更多时间,以更加轻松的节奏审阅报告。
一些低 难度但潜在高价值的项目例子可能包括:
-
自动化业务团队经常进行的手动任务;例如,在 Excel 中创建的报告,可以通过结合开源工具和数据库轻松实现自动化。
-
将手动的股票分析转换为自动化版本,使用编程脚本进行。这可能涉及创建常规表格、透视表和图表,这些内容通常在 Excel 中创建,但可以转变为自动化流程。
-
使用 R Shiny 为业务应用创建 Web 界面,并实现预测分析功能。
-
将某些 IT 基础设施的部分迁移到云平台。这看起来可能有些反直觉,特别是当组织不习惯在云环境中工作时。然而,管理云部署的简便性和轻松性可能意味着总体拥有成本和运营开销的降低。
最终选择使用的案例将取决于多个因素,之前提到的一些案例是为了给出一个大致的方向,帮助理解可能尝试的项目类型以及可能带来积极结果的工作流程。在接下来的部分中,我们将讨论行业中用于数据科学的具体软件和硬件解决方案。
企业中的数据科学解决方案
如前所述,通常我们可以将数据科学广泛地分为两个主要部分:
-
企业数据仓库和数据挖掘
-
企业数据科学:机器学习,人工智能
在这一部分,我们将分别探讨这些内容,并讨论行业中用于实现这些能力的软件和硬件解决方案。
企业数据仓库和数据挖掘
如今,行业中有许多被宣传为能够运行复杂分析查询的 NoSQL 数据库系统。它们大多数都具备典型 NoSQL 系统的一个或多个特点,如列式存储、内存存储、键值存储、面向文档、基于图的等。接下来的部分将重点介绍当前一些关键的企业级 NoSQL 系统。
传统的数据仓库系统
传统数据仓库可能是一个误称,因为大多数传统系统也已经融入了 NoSQL 的核心概念。然而,在这种情况下,这个术语旨在指代在 NoSQL 系统出现之前就已存在的数据库,并且这些数据库也添加了能够与企业数据科学要求相符的功能。
Oracle Exadata、Exalytics 和 TimesTen
Oracle Exadata、Exalytics 和 Exalogic 属于 Oracle 的 Exa 产品家族。Exadata 是 Oracle 高性能的工程化数据库平台,专为资源密集型查询设计。通常,它预期能显著提高相较于非 Exadata 系统的查询性能,并支持先进的软件功能,如内存计算、独立的行和列过滤、以及其他硬件功能,比如对最新存储设备(包括 NVMe)、内存容错等的支持。
Exalytics 是一个补充性解决方案,主要用于 BI 工作负载。Exa 产品家族被认为是工程化系统,而非设备。后者通常指预设且预加载的软件硬件堆栈,而工程化系统则预期能够提供更高的灵活性,支持根据客户需求有选择地安装组件。Exalytics 在企业部署中常见的一个关键组件是 OBIEE(Oracle Business Intelligence 企业版)。这是一个完整的 BI 套件,得益于底层的内存数据库 Times Ten,后者也是 Exalytics 生态系统的一部分。
业务使用案例:Oracle Exadata 用于 OLTP 事务性工作负载,速度和性能至关重要。
Exalytics 则用于分析性工作负载。集成的 OBIEE 界面与 TimesTen 一起提供了一个紧密耦合的分析环境。Oracle Exadata 也可以作为云服务提供。
HP Vertica
HP 的 Vertica 是一个列式存储的大规模并行处理数据库系统,具备一些关键软件特性,如支持数据库内机器学习,并原生集成开源系统,如 Apache Kafka 和 Apache Spark,通常部署在多节点硬件架构上。Vertica 支持流行的云环境,如亚马逊 Web 服务(AWS)、Google 和 Azure。Vertica 提供标准的交互式 SQL 接口,因此与大多数现代 BI 工具兼容。
HP Vertica 有趣的是,它是少数几款同时提供社区版的企业数据库之一。这意味着用户可以在其许可范围内尝试 Vertica,或仅仅是了解更多关于该平台的信息,并可以从 HP Vertica 的官网免费下载社区版。
商业用例:与本节中提到的其他数据库类似,Vertica 包含多个显著特点,如数据库内处理、并行处理能力等。Vertica 支持多种分析工作负载,并且伴随有相关的商业许可费用(与所有其他商业数据库产品一样)。提供社区版,同时,HP 通常愿意为大规模部署提供概念验证,提供了丰富的机会,供企业在做出决策之前,尝试并测试平台是否适合公司特定的用例。
Teradata
Teradata 被广泛认为是企业数据库技术的领导者。其数据库也以同名命名,拥有与其他竞争产品相似的多个特性。Teradata 的一些关键特点包括与许多开源解决方案(如 R、RStudio、Jupyter 和 SAS)的本地集成;对时间序列的支持;内置的机器学习和人工智能分析功能;支持多种数据类型,如 CSV、JSON、文本以及空间/时间数据。
分析平台,也称为 Aster Analytics,可从aster-community.teradata.com/community/download
下载社区版。
传统上,Teradata 作为一种硬件和软件一体化的解决方案存在,即数据库和硬件作为一个整体提供。但如今,使用 Teradata Everywhere,用户也可以在云端使用 Teradata。该软件可以部署在混合架构中(即既可以在本地部署,也可以在云端部署),还可以在 AWS 和 Microsoft Azure 等公共云环境中使用。提供捆绑服务、订阅服务以及即服务选项。Teradata Intellicloud 是 Teradata 推出的一种基于订阅的云服务,包含多个 Teradata 生态系统中的产品,且以托管环境提供。
商业用例:Teradata 已成为企业数据库的流行选择,且在大型组织中具有较高的信誉。近年来,Teradata 积极与开源系统(如 R、Jupyter 及其他产品)集成,使其更具吸引力,并且可以说也提高了其知名度。Teradata 设备相对昂贵,且与其他商业选项一样,要求进行适当的概念验证,以评估其是否适用于特定组织的用例。
IBM 数据仓库系统(前身为 Netezza 设备)
IBM Netezza 曾作为一个独立的数据仓库设备进行销售,在某些方面类似于 Teradata 的市场营销方式。最近,Netezza 已经被纳入 IBM 数据仓库系统的更广泛分类中,这与当今大数据对管理海量数据的需求更加契合。IBM Pure Systems、PureSystems for Analytics 和 IBM Integrated Analytics System 是一些较新的解决方案,它们在一个集成生态系统中提供与 Netezza 几乎相同的功能。
集成分析环境包括嵌入式 Apache Spark 用于机器学习、Jupyter Notebooks 用于数据科学工作负载、连接到其他 NoSQL 和 Hadoop 实现的通用 SQL 引擎,以及支持在高性能架构上部署,并提供托管云环境的选项。
业务应用场景:Netezza 被那些传统上强烈依赖 IBM 相关技术(如 DB2、AIX 以及 IBM 的其他产品)的公司所青睐。新的集成产品环境提供了一个机会,既能继续使用现有产品,又能为组织中的 IBM 投资添加数据科学能力。
PostgreSQL
PostgreSQL 在这一节中是一个有趣的选择,因为从技术上讲,PostgreSQL 并没有一个独立的 NoSQL 版本,而是 PostgreSQL 在最近的版本中增加了多种功能,将 NoSQL 能力添加到现有的 Postgres 实现中。
PostgreSQL 的支持者正确指出,它是一项更为成熟且经过时间考验的技术,因为它最早在 1990 年代中期发布。Postgres现在支持层次化文档数据存储、JSON、一个键值存储(称为HStore)以及分片,还包括与多种编程语言以及各种数据源的接口。换句话说,PostgreSQL 已被扩展以支持类似 NoSQL 的功能,同时保持其作为传统关系型数据库管理系统(RDBMS)的现有能力。
PostgreSQL 作为一个功能齐全的开源产品提供。
业务应用场景:虽然这一节中的大多数技术都是在商业授权下提供的(需要获取它们的所有功能),但作为开源软件的 PostgreSQL,是以非常具成本效益的方式尝试成熟数据库的选择,而无需进行大规模的初始投资。它还可以作为一个测试平台,在做出最终决定之前,试用 NoSQL 功能,比如处理 JSON 数据。无论如何,PostgreSQL 是一个强大的平台,可以支持企业需求。PostgreSQL 也有商业衍生版本——构建在 PostgreSQL 之上的数据库,比如 Greenplum,它也可以作为开源产品使用。
Greenplum
Greenplum 数据库® 基于 PostgreSQL 构建,增加了一些重要的分析能力。这些包括创新的基于成本的查询优化器、与 Apache MADlib 的集成,以及行存储或列存储的选择。它为流行的编程语言如 R、Python 和 Java 提供本地接口,并支持大规模并行架构。Greenplum 可免费下载,网址为 greenplum.org/download/
。
业务应用场景:Pivotal 提供带有完整支持的 Greenplum 商业版。Greenplum 非常成功,尤其是在处理大型企业工作负载方面表现出色。商业支持的可用性对那些需要专门支持和服务级别协议 (SLA) 的组织非常有帮助,这些协议保障了关键业务操作。
SAP Hana
SAP Hana 是 SAP 提供的列式内存数据库,支持 NoSQL 特性。Hana 支持多核并行操作、多租户,并完全符合 ACID 标准,能够处理多种分析性工作负载,包括预测建模、流式分析、时间序列分析以及空间、文本和图形分析。你还可以在 SAP Hana 系统内管理基于 JSON 的非结构化数据。
Hana 还与其他 SAP 产品本地兼容,如 SAP Fiori,它包含了广泛的 SAP 用户体验应用程序,广泛应用于人力资源、财务、会计等部门。
业务应用场景:SAP 在企业组织中已经存在数十年,被广泛用于多种应用,最为显著的可能是制造和财务/会计需求。SAP Hana 为现有的 SAP 安装增添了一个强大的高性能数据库。一般而言,由于企业级部署的高成本,SAP 主要用于业务关键需求。对于那些依赖 SAP 的大型组织而言,Hana 带来的好处可能超出了其成本。此外,虽然 Hana 可以提供广泛的 NoSQL 能力,但公司可能会发现,最终会根据预算、性能需求和其他因素,选择两种或更多不同的解决方案。
企业级和开源 NoSQL 数据库
前一部分概述了一些知名的传统数据库/RDBMS 解决方案,这些解决方案已经添加了企业级 NoSQL 功能。接下来的部分将探讨一些更为小众的业务应用场景,专注于数据库解决方案。
Kdb+
来自 Kx Systems 的kdb+是全球最快、最高效且轻量级的数据库之一,已在高频交易及其他类似环境中使用近二十年。它在金融领域之外的受欢迎程度较低,但不可否认的是,它无疑是数据库领域中设计和优化最为高效的系统之一。
Kdb+从一开始就支持内存列式存储,技术上是q编程语言的扩展。kdb+中的表本质上是 q 语言中的数据结构。然而,与其他编程语言中的类似概念不同,kdb+表是企业级的,能够轻松处理 TB 级和 PB 级的数据。
由于其固有的编程语言,使用 q 编写的代码可以针对存储在 kdb+中的数据进行运行,因此可以以非常小的工作量在数据库中运行自定义的函数。
此外,kdb+二进制文件的大小约为 500 到 600KB,足够小,可以容纳在大多数现代 CPU 的 L3 缓存中。
Kdb+还包括内置的 MapReduce 功能,可以使查询自动在多核 CPU 上并行执行。
业务应用场景:Kdb+是企业环境中最强大的数据库之一。它传统上仅提供永久核心授权,但最近公司已增加了对基于订阅和按需授权的支持。其低资源占用和简便的使用方式使其非常适合企业需求。然而,这也有一个警告。q 语言非常简洁,对于新用户来说可能显得晦涩。可以说,这种语言的学习曲线比其他语言稍陡峭,需要实践和第一手经验。尽管如此,网上有大量的资源可以帮助学习并利用该数据库的功能。R、Python、C、Java 等编程语言的本地接口以及用于机器学习的库,使其特别适用于处理大型数据集的数据科学工作负载。
虽然 kdb+并非开源产品,但一般可以免费用于个人用途,下载地址为:kx.com/download/
.
MongoDB
MongoDB是文档导向数据库领域的市场领导者,专门用于存储 JSON 格式的数据。它支持按需查询、索引和聚合,并且具有丰富的 Python、Java、JavaScript 等编程语言的接口。其他功能,如水平扩展与分片、高可用性、集成的数据探索工具 Compass 等,进一步增强了该数据库的现有能力。
业务应用场景:考虑将数据库用于存储非结构化或半结构化数据的公司,可能会发现 MongoDB 的特性非常适合查询此类数据集。该数据库无需在开始时定义固定的模式,使其灵活且可扩展,以支持添加到现有数据中的新属性。MongoDB 可以从www.mongodb.com/download-center
免费下载,也可以通过 MongoDB Atlas 作为托管的云解决方案进行实施。支持内存存储和加密存储等功能的企业版也可以按订阅方式获得。
Cassandra
Cassandra 是最成功和最广泛使用的企业级 NoSQL 系统之一。它结合了列式和键值概念,并将数据存储在基于行的分区中。每个分区反过来都是一个主键。行可以拥有多个列,并且列的数量可能因行而异。
Cassandra 数据库可以通过 CQL 进行查询,CQL 使用类似 SQL 的语法,使数据查询、保存以及其他常见任务变得更加简单。Cassandra 还采用去中心化架构;它没有任何单点故障,并支持多节点架构。
除了 Cassandra 数据库管理系统的标准水平扩展性外,该平台还支持弹性扩展,能够根据需要透明地分配和解除节点。总体而言,Cassandra 是企业级 NoSQL 系统中最强大的选项之一,并且在全球多个大型企业的生产环境中被广泛使用。
商业应用案例:Cassandra 是一个完全开源的解决方案,实施了 NoSQL 系统的多个关键特性。它被全球范围内的生产工作负载所使用,并且已经发展成为一个稳定、企业级的开源平台。换句话说,Cassandra 非常适合管理大型组织需求,并且不需要额外的许可费用。Cassandra 也提供商业版、许可版和收费版,由 Datastax 提供:www.datastax.com/products/datastax-enterprise
。它被称为DSE(Datastax Enterprise)。DSE 结合了多种企业功能,如安全性和搜索,并且可以通过 Datastax 托管云环境访问,支持 AWS 等流行的云服务提供商。
Neo4j
Neo4j 是一个基于图形的数据库,用于建模不同实体之间的关系。该数据库使用图论中的常见概念来创建基于树的表示(包括节点和关系)以表示互联的主题。它最常用于与推荐引擎配合使用。从概念上讲,一个 Neo4j 图形数据库可以将个体表示为节点,这些节点通过他们之间的分隔度相连接。这将假设性地允许最终用户追踪任意节点或个体之间的分隔度。
Neo4j 平台提供了各种基于图形的表示方式,例如加权、定向、单向和标签化等。
商业应用案例:需要深入客户级或用户级分析的公司,如社交网络或推荐系统(如 Netflix),通过部署基于图形的数据库(如 Neo4j)可以获得巨大的好处。如今,该平台支持人工智能和机器学习、物联网、实时推荐等众多企业中常用的功能。
虽然 Neo4j 作为开源软件可以从 neo4j.com/download/?ref=hro
获取,但也有一个商业授权版本,名为 Neo4j 企业版。
云数据库
如其名称所示,云数据库指的是由云服务商提供的数据仓库或数据库系统,例如 Amazon、Google 和 Microsoft。
亚马逊 Redshift、Redshift Spectrum 和 Athena 数据库
最常用的云数据仓库平台之一是 Amazon Redshift。它是云生态系统中最突出的数据管理平台。它基于 PostgreSQL,主要用于分析工作负载。Redshift 高度可扩展,相比具有相似特性的本地数据库,它所需的工作量显著较少。它可以通过 AWS 控制台直接部署(在注册 AWS 账户后)。用户可以通过 AWS 控制台无缝地增加或减少节点,以相应地增减容量。
最近发布的 Redshift Spectrum 允许查询存储在 Amazon S3(AWS 的标准存储层)中的数据。这意味着用户可以直接查询存储在磁盘上的数据,而无需将其加载到 Redshift 特定的实例中。总体而言,Redshift 速度较快、成本低廉,更重要的是易于使用和部署。Redshift Spectrum 采用按查询付费的模式——用户仅为执行的查询付费,每扫描一个 TB 数据收取名义费用。
Amazon Athena 在许多方面与 Amazon Redshift Spectrum 相似,都用于查询存储在 S3 上的数据。然而,尽管 Amazon Redshift Spectrum 的功能在未购买 Amazon Redshift 的情况下无法使用,但用户可以按需使用 Amazon Athena,无需预留额外的硬件资源。另一方面,由于 Amazon Redshift Spectrum 与 Redshift 生态系统紧密集成,用户可以在这两种解决方案之间分配工作负载。需要更快处理的数据可以保留在 Amazon Redshift 中,而不常用/不重要的数据可以存储在 S3 上并使用 Redshift Spectrum 查询。
你可以通过 aws.amazon.com/redshift/spectrum/
了解更多关于 Amazon Redshift Spectrum 的信息。
Google BigQuery 和其他云服务
Google BigQuery 类似于 Amazon Redshift,都是大型的完全云端的数据仓库系统。然而,Redshift 需要单独的配置(AWS 集群和 Redshift 资源),而 Google BigQuery 是相当于即插即用的解决方案。使用 BigQuery 时,用户只需要在 bigquery.cloud.google.com
创建账户,加载数据集后即可开始运行查询。
与 Redshift 相比,BigQuery 的计费方式也有所不同。用户每月可以免费查询累计 1 TB 的数据。BigQuery 采用按需付费模式,查询会产生相应的费用。实际上,BigQuery 抽象了数据库设置的复杂性,允许最终用户专注于编写查询和/或执行分析,而无需设置基础设施的额外工作。扩展查询、分配资源,以及可能需要手动干预的任务(例如由数据库管理员进行的操作),因此变得不再必要。
谷歌也在其云平台上提供了一系列其他 NoSQL 产品,包括 Google Cloud Datastore(一款基于文档的 NoSQL 数据库)、Google BigTable、Google Spanner 等。下图展示了 Google BigQuery 数据库:
Azure CosmosDB
Azure CosmosDB 是微软的一款基于云的 NoSQL 数据库。Azure 中的其他 NoSQL 系统包括 Table Storage、Azure Redis Cache 等。CosmosDB 被认为是一个多模型数据库;它可以支持键值对、基于文档的查询、基于图形的模型以及关系型数据库查询。
传统的微软数据库,如 SQL Server,也可以使用,并且在 Azure 平台上作为完全托管的解决方案提供支持。你可以在 azure.microsoft.com/en-in/services/cosmos-db/
上了解更多关于 Azure 平台的信息。下图展示了 Microsoft Azure 平台的解决方案窗口:
GPU 数据库
GPU 数据库 是随着图形处理单元(GPU)卡在数据科学相关任务(如机器学习)中应用的增长而发展起来的一种较新的技术。当查询可以并行化时,GPU 的表现最佳。这是因为 GPU 含有数千个核心。通过将每个核心分配给数据的一个小子集,GPU 通常能够以极快的速度进行计算,远超基于 CPU 的查询性能。
Brytlyt
Brytlyt 是进入 GPU 数据库领域的最新产品。它还配套了一款名为 Spotlyt 的可视化产品。早期的测试表明,Brytlyt 超越了几个具有挑战性的基准测试。然而,它在其他使用案例中的表现如何,仍有待观察。
Brytlyt 可在 Amazon AWS Marketplace 上使用 (aws.amazon.com/marketplace/pp/B074JZNSWZ?qid=1513342415797&sr=0-1&ref_=srh_res_product_title
),供那些希望尝试它的用户使用。
MapD
MapD是早期开发商业 GPU 数据库平台的公司之一。与 Brytlyt 类似,它也展示了令人印象深刻的初步结果。然而,由于基于 GPU 的数据库仍处于初期阶段,普及程度和采用情况最终将决定它们是否会成为企业中的常见工具。
基于 GPU 的数据库的主要挑战之一是需要正确配置 GPU 系统。这可能需要专业技能,因为使用 GPU 卡进行计算与使用 GPU 卡执行常见任务(如图像渲染)是截然不同的。因此,希望在正式版本发布前尝试 GPU 数据库的用户,可以利用 AWS 中的预配置镜像(AMI 镜像),这将需要最小的系统配置。
其他常见数据库
还有其他各种类型的数据库,如用于分析流数据的数据库(Amazon Kinesis),以及使用百度的 FPGA 加速卡处理数据的数据库。
企业数据科学 – 机器学习和人工智能
数据科学解决方案在过去 4 到 5 年内迅速发展,类似于数据科学其他领域(如 NoSQL、Hadoop 和其他数据挖掘解决方案)的发展。尽管许多先前的数据库系统也结合了数据科学的关键特性,如机器学习等,但本节将高层次地介绍一些主要用于机器学习和/或人工智能的解决方案,而不是数据管理。
确实,大数据产品与数据科学产品之间的区别已变得模糊,因为最初为大数据处理而设计的产品已经融入了数据科学的关键特性,反之亦然。
R 编程语言
正如我们在前面的章节中所见,R是最初为统计编程设计的环境。它源于新西兰大学的一个项目,Ross Ihanka和Robert Gentleman将 R 开发为 S 编程语言的一个变种,S 编程语言由 Bell Labs 的 John Chambers 开发。虽然 R 最初是为统计编程设计的,但在过去 7 到 8 年里,它已经发展成一个成熟的多面向语言,增强了对机器学习、高性能计算、可视化、计量经济学、时间序列分析等多个相关学科的支持。其中一些领域也在cran.r-project.org/web/views/
提供了相关信息。
R 的商业版本和企业支持曾由 Revolution Analytics 提供。在 2015 年,它被重新命名为Microsoft R Open(开源版本)和Microsoft R Server(商业版本)。
尽管以微软品牌推广,但请注意,Microsoft R 也可在 Linux 和 Mac OS 上使用。
R 中流行的机器学习包包括 e1071
、randomForest
、gbm
、kernlab
、arules
等。可以在 cran.r-project.org/web/views/MachineLearning.html
上找到这些包的详细信息。另一个流行的包是 caret,它作为多个算法包的包装器,提供了一个统一的接口来运行算法,而无需遵循各个包的细节。
R 也支持通过如 multicore
、doMC
等包进行多核编程。详细信息可以在 cran.r-project.org/web/views/HighPerformanceComputing.html
上找到。
Python
Python 中的 scikit-learn
包可以说是所有平台中最全面的机器学习包,涵盖了广泛的机器学习算法。与 R 相比,它的执行速度也更快,且是多个企业组织的首选工具。以下截图展示了我们可以下载 scikit-learn
包的网页:
一款商业支持的企业版 Python,预配置了有用的机器学习和数据挖掘包,叫做 Anaconda
,由 Continuum Analytics 提供。Anaconda 的云版本,称为 Anaconda Cloud,允许新用户在无需单独下载和安装的情况下,直接开始利用 Anaconda Python 的功能。
OpenCV、Caffe 等
图像识别是机器学习中较为成功的领域之一。虽然大多数机器学习任务需要相对较长的时间才能衡量和量化其真正的好处,但图像识别是一个熟悉的主题,容易理解。本质上,它涉及识别对象并正确分类。它有多种应用,从识别车牌号码到面部识别,在移动设备和机器人中都有广泛应用。
OpenCV 提供了一个标准接口,用于各种图像识别任务,还可以利用硬件加速功能来优化性能。
其他知名的图像处理机器学习软件包括 Caffe、cuDNN、TensorFlow 等。请注意,这些软件包不仅限于图像识别,还可用于其他深度学习应用场景。
Spark
Spark 中的 MLlib 库提供了各种机器学习算法的正式实现,可在 Spark 平台上使用。pySpark 的可用性使得拥有 Python 编程知识的人更容易使用这些功能。如果组织已经拥有现有的 Spark 平台,那么探索 MLlib 中的机器学习功能是值得的。
以下截图简要概述了 MLlib:
深度学习
拥有多个隐藏层(通常超过两个)和/或节点的神经网络通常被归类为 深度学习。机器学习中的一些现代进展,如自动驾驶汽车,直接源于深度学习在实际日常任务中的应用。
有多种深度学习框架/包,其中一些值得注意的包括:
-
TensorFlow
-
cuDNN
-
Theano
-
Torch
-
百度的 PaddlePaddle
H2O 和 Driverless AI
H2O 是 Kaggle 竞赛的一个流行平台,提供一个大规模可扩展、实时机器学习接口,并且本地集成了 R、Python、Spark 等多种工具。可以从 www.h2o.ai/h2o/
免费下载。
Driverless AI 是 H2O 产品系列的最新成员。它通过实现一个自动化接口,试图创建模型并通过构建和评估多个模型来优化准确性,从而使机器学习对从业者更为简便。以下截图展示了 H2O 平台的首页:
Datarobot
从概念上讲,Datarobot 类似于 H2O 的 Driverless AI,因为它也试图通过创建和评估多个模型来自动构建机器学习模型,从而自动化处理给定数据集。
然而,与 H2O 不同,尽管它非常强大,Datarobot 需要支付许可费用,对于小型公司来说可能非常昂贵。
命令行工具
有多种在 Unix 命令行中执行的机器学习工具。一些工具在 R、Python 和其他语言中已有现成的接口,允许用户在不使用 Unix 终端的情况下利用它们的功能。一些流行的命令行工具包括:
-
LIBSVM
-
LIBLINEAR
-
Vowpal Wabbit
-
MLPACK
-
libFM
Apache MADlib
其中一个鲜为人知但功能丰富的平台是 Apache MADlib,它旨在 数据库内 执行分析和算法,即可以在不需要外部编程接口的情况下本地执行函数。它支持并行处理,并且能够与多个数据源(如 Greenplum、PostgreSQL 等)无缝协作。
例如,可以通过简单地运行一个 SQL 命令来创建一个 apriori 模型,如此处所示,来自 madlib.apache.org/docs/latest/group__grp__assoc__rules.html:
SELECT * FROM madlib.assoc_rules(.25, -- Support
.5, -- Confidence
'trans_id', -- id col
'product', -- Product col
'test_data', -- Input data
NULL, -- Output schema
TRUE -- Verbose output
);
关于 Apache MADlib 的更多信息(下图为网站截图),请访问 madlib.apache.org
机器学习即服务
与其他云资源集成的基于云的机器学习平台也不断涌现。一些知名的平台包括 AzureML、BigML、IBM Watson 等。
以下截图来自 IBM Watson,它是最著名的机器学习和人工智能平台之一。该平台因在 2011 年赢得《Jeopardy》冠军而声名鹊起 [来源: www.techrepublic.com/article/ibm-watson-the-inside-story-of-how-the-jeopardy-winning-supercomputer-was-born-and-what-it-wants-to-do-next/
]。当时,机器学习的趋势处于初步阶段,Watson 是第一批令世界震惊的 AI 技术之一。它证明了人工智能可以是强大且具备能力的资产。如今,用户可以通过在网站上注册账户,利用 IBM Watson 的一些相同计算能力,www.ibm.com/watson/
。
企业基础设施解决方案
正确选择基础设施在决定组织数据科学平台的效率上也起着关键作用。资源太少,算法执行时间过长;资源过多,则可能有大量资源未被利用。因此,后者要优于前者,避免由于资源不足而阻碍进展,影响机器学习研究者高效完成任务的能力。
云计算
在过去的 5 至 7 年里,组织逐渐将其资源转向云平台,如亚马逊 AWS、微软 Azure 和谷歌计算引擎。如今,这些平台都包含极其复杂且广泛的架构,支持企业级的机器学习、数据挖掘以及一般的数据科学,以满足各类规模组织的需求。
此外,镜像的概念,如亚马逊 AWS 中的 AMI 镜像,允许用户启动一个预先构建的操作系统快照,内含预装的组件。这样,用户几乎可以完全避免在尝试新平台前的设置开销。
Hadoop 和 MapReduce 操作在 AWS 中也得到了广泛支持。AWS 中的EMR(弹性 MapReduce)和 Azure 中的 HDInsight 是两个知名且非常受欢迎的大数据框架。
本章末尾的教程将演示如何设置 AWS 账户并开始使用示例 AMI 镜像。
虚拟化
虚拟化——在更大的主机内部创建隔离的、独立的环境的过程,使得组织能够整合服务器并大幅减少数据中心的占地面积。例如,如果一个组织为其网站使用六台服务器,其中两台使用频繁,而其他的在大多数时间负载较低,可能将所有服务器整合到最多一两台服务器中。在这方面,像 Dell EMC 的 VxBLOCK 等技术是知名的企业级虚拟化硬件,广泛应用于物理数据中心。这也使得公司能够构建自己的私有云基础设施。然而,这可能相当昂贵,需要对成本效益比进行适当评估。
用于创建公有云和私有云的开源软件是 Openstack。它是一个企业级生态系统,包含多个产品,可以在 Openstack 平台内无缝协作。有关 Openstack 的更多细节,请访问www.openstack.org:
用户可能熟悉 Oracle Virtualbox,它本质上也是一种虚拟化软件,允许用户创建隔离的环境。这使得用户能够在 Windows 中运行 Linux,反之亦然。
专用软件或硬件,称为虚拟机监控程序(hypervisors),用于管理和管理员虚拟机。
容器 – Docker、Kubernetes 和 Mesos
容器和虚拟化一样,创建隔离的客系统,但虚拟机创建的是完全独立的环境,而容器与主机系统共享相同的内核,因此被认为更接近硬件。虚拟化和容器都由于多个抽象层(主机和客操作系统之间的功能转换)而带来性能损失。然而,容器通常具有更高的性能,因为它们依赖并直接使用客操作系统的特性,而不是创建一个独立的操作系统生态系统。
常见的容器包括 Docker、CoreOS 等。如今,容器主要用于大规模管理与 web 相关的服务。与虚拟机相比,容器可以更容易地按需启动和关闭,主流云提供商已经为容器添加了专门的支持,使得仅通过几行代码就能启动数千个容器来处理 web 请求。像 Kubernetes 这样的编排软件为管理容器提供了企业级功能。此外,像 Mesos 这样的平台不仅支持管理容器,还增加了对管理其他传统硬件的能力,以便进行应用感知调度和其他服务:
本地硬件
最后,本地硬件,如传统数据中心,在现代计算中仍然占有一席之地。使用物理数据中心,用户不需要为云服务支付持续费用。对于没有大型管理开销的小型至中型组织,或不需要高性能/专业计算能力的组织,本地系统完全能够提供具有成本效益的永久性解决方案。
像 ScaleMP 这样的公司提供专用硬件,供高性能计算使用。这类硬件的消费者通常有特定的需求,而云供应商无法提供这些需求:
以下是本地部署和基于云的系统之间一些差异的总结:
本地部署 | 云 |
---|---|
你拥有硬件 | 你租用硬件 |
需要完全维护 | 维护由云托管提供商管理 |
需要 IT 资源来管理计算硬件资源 | 管理计算硬件资源的开销较小,因为它们可以在云中按需添加 |
对于小型到中型环境,且数据中心运营成本低或没有运营成本,成本效益高 | 对于寻求简化数据中心运营成本的大型组织,成本效益高 |
使用硬件没有持续费用,除非是管理硬件所需的资源 | 使用硬件时有持续费用;采用订阅定价模式 |
主要是静态架构;Hadoop 的新需求将需要一整套新的采购 | 极为灵活;公司可以按需配置成千上万的服务器,支持多种操作系统 |
易于被组织、法律和相关部门接受 | 面临障碍,特别是来自法律部门的障碍,因为管理工作委托给第三方/云托管提供商 |
企业大数据
大型组织中大数据实施的总体战略取决于组织的具体需求。如今,在大数据、数据科学、机器学习和最近的 AI 供应商之间,有数百种选择可供选择。
因此,在大型组织实施大数据时,有两个主要考虑因素:
-
技术:选择合适的软件和硬件堆栈
-
运营:组织数据的管理、创建正式的数据治理战略,以及创建适当的数据管理框架
除此之外,招聘合适的人才,并可能为公司的大数据/数据科学实施创建明确定义的角色,是额外但同样重要的任务。
在制定此类战略时,以下是一些关键问题:
-
软件/硬件授权是基于规模还是核心数?如果是基于数据大小,而我的数据量增加了,那么我的三年/五年成本会是多少?
-
该解决方案是否提供企业支持?
-
我们是否需要雇佣外部资源?
-
新功能将回答哪些商业问题?
-
我们是否进行了短期和长期的成本效益分析?
-
目前组织未满足的需求是什么,新解决方案能够解答这些问题?
-
这个解决方案是否足够可扩展,能够满足我未来的潜在需求?
在技术需求方面,尽管市场上有许多解决方案,但在实践中,使用实际数据进行测试或概念验证是至关重要的,因为解决方案最终将应用于这些数据。我们常常会遇到声称具有强大能力的解决方案,但却未能达到预期。换句话说,至关重要的是收集全面的经验性结果,而不是仅仅基于市场营销宣传做出购买决定。
最后,由于大数据/数据科学领域不断发展,解决方案的长期可扩展性和适应性需要得到适当评估。考虑到云端选项能够提供高效的途径来轻松且实惠地访问和使用新兴解决方案,因此应该将其作为一个选择。
教程 – 在云端使用 RStudio
以下教程将展示如何在AWS(亚马逊云服务)上创建账户,加载 RStudio 的 AMI 镜像,并在此基础上使用 RStudio,所有操作均为免费。对于有云平台使用经验的读者,可能会觉得这些步骤比较基础。对于其他用户,教程应该提供了使用 AWS 的有用初步指导。
请在继续操作之前阅读下面的警告信息。
警告:请注意,AWS 注册需要信用卡。用户必须小心,只选择免费的免费层选项。AWS 协议允许亚马逊对产生的费用进行收费。由于这一原因,用户应谨慎使用该平台,以避免因服务器或服务未关闭而产生意外的高额费用**。
目前,Azure 和 Google Cloud 提供了用户注册时的避免不小心收费的条款。然而,AWS 在所有云服务提供商中市场份额最大,用户很可能在大多数工作场景中遇到 AWS。因此,本教程重点介绍 AWS,而非其他选择。
本教程结尾也提供了如何关闭账户的说明,以防您希望停止使用 AWS(从而避免任何费用):
- 访问
aws.amazon.com/
,然后点击右上角的创建 AWS 账户按钮:
- 一个 AWS 账户通常包括 12 个月的初始免费层访问权限。输入您的信息并点击继续:
- 选择您的账户类型(如个人账户),并输入您的联系信息:
- 输入支付信息。AWS 注册时需要信用卡。请注意,用户必须非常谨慎地使用 AWS 资源,以确保没有无意间产生费用:
- 一旦验证了支付信息,您将收到确认:
- 选择基本计划(免费):
选择基本计划后的确认页面如下:
- 使用您的凭证登录 AWS:
- 第一页展示了 AWS 中的一些服务。右上角显示的是您的实例所在的区域。AWS 支持多个区域,用户可以从多个地理位置选择:
- 点击左上角的服务下拉菜单,将显示可用的不同服务。这里突出了几个重要的服务:
- 点击 EC2:
- AWS 提供了启动多个不同操作系统的选项。虽然我们可以选择一个新的操作系统来启动一个新的实例,但这里我们将使用一个 AMI 镜像。AMI 是预先配置好的镜像,包含已安装的软件。请注意,使用 AMI 镜像不是强制要求的,但本教程中使用了一个 AMI 镜像:
- 点击左侧菜单栏中的社区 AMIs,并搜索RStudio。选择第一个选项并点击选择按钮:
- 选择免费套餐选项(t2.micro),然后点击下一步:配置实例详细信息:
- 在下一个页面选择默认选项,然后点击下一步:添加存储:
- 选择默认存储选项并点击添加标签:
- 点击下一步:配置安全组:
- 安全组指定了服务器的网络访问规则。对于本教程,我们将选择 All TCP,然后点击审查并启动:
- 点击启动:
- 选择创建新密钥对,然后点击下载密钥对。密钥下载完成后,点击启动实例按钮:
- 点击实例 ID:
- 一旦实例 ID 的状态显示为正在运行,复制服务器名称,可以在底部面板查看到:
- 打开一个新浏览器,输入服务器名称作为网址,并按 Enter 键。这将打开 RStudio。使用 ID rstudio 和密码 rstudio 登录:
- 这将打开 RStudio 控制台。它是一个完整的 R 环境,你可以像在本地安装的 R 和 RStudio 中一样执行 R 代码:
- 使用完 RStudio 后,确保
terminate
实例。终止实例会停止计费过程。即使我们使用的是免费套餐账户,完成工作后停止或终止实例依然是一个好的实践:
- 点击“Sign Out”退出 AWS 控制台:
Azure 也提供免费的账户注册,链接为azure.microsoft.com/en-us/free/
,如下截图所示:
Google 的免费云注册表单可以在cloud.google.com/free/
找到,以下截图显示了该页面:
总结
在本章中,我们讨论了部署企业级数据科学基础设施的需求,包括软件和硬件层面。我们分享了关于此类举措的管理层常见问题。接下来是关于在大公司中用于数据挖掘和机器学习的关键企业解决方案的广泛介绍。
本教程介绍了在亚马逊网络服务(AWS,一种基于云的系统)上启动 RStudio Server。AWS 已成为目前全球领先的云服务提供商,本次演示展示了如何在几秒钟内启动整个虚拟机。文中提到了关于谨慎且合理使用 AWS 以防止高额费用的优缺点。
下一章也是最后一章将包括一些总结性想法、下一步计划,以及一些链接到有用资源,你可以通过这些资源进一步了解本书中讨论的主题。
第十章:大数据的总结性思考
到目前为止,我们已经涵盖了广泛的主题。我们探讨了用于大数据、数据科学和机器学习的技术。我们了解了公司如何实施其大数据企业战略。我们还在过程中开发了一些实际应用。
本章讨论了公司在实施大数据或数据科学计划时的实际考虑。随着新技术、新的开源工具以及数据挖掘新概念的引入,这一领域不断发展。因此,各种规模的组织都面临共同的挑战。
数据科学的成功故事在媒体中随处可见。事实上,今天在技术领域发生的大多数投资,都与数据科学的某些方面有联系。实际上,它已经成为 IT 发展的一个不可或缺的组成部分。
在本章中,我们将讨论实施数据科学的一些共同主题、面临的挑战以及如何使你的计划取得成功。此外,我们还将探讨数据科学中的重大成功案例,以及数据科学未能兑现承诺的例子。我们还将提供一系列链接,供你进一步了解相关主题。
本章将涵盖以下主题:
-
企业大数据与数据科学战略
-
道德考量
-
硅谷与数据科学
-
人为因素
-
进一步阅读的链接
企业大数据与数据科学战略
你在报纸上读到过它,你在晚间新闻中看到过它,你从朋友那里听说过它——大数据和数据科学无处不在,它们已经成为不可或缺的一部分。
来自硅谷的成功故事使这一影响更加明显。谁能想到,一款共享出行和打车应用程序——Uber,竟然能够成为全球最受欢迎的公司之一,估值接近 700 亿美元。像 Airbnb 这样的网站和应用将公寓共享变成了蓬勃发展的商业,成为第二大估值公司,价值 300 亿美元。
这些以及其他类似事件将大数据和数据科学的话题从纯粹的理论和技术性学科转变为人们已习惯与无边投资成功联系在一起的常见术语。
由于几乎所有主要技术供应商都开始添加被归类为大数据的功能,今天几乎所有投资技术的公司都在使用某些方面的大数据,无论是有意识的还是无意识的。
然而,实施过程定义得非常宽泛。因此,除了可能的 Hadoop 之外,没有明确的框架,Hadoop 已成为大多数公司采纳的事实框架。高级管理层通常了解大局,即大数据能够为组织带来的价值。然而,通向实现愿景的道路充满挑战,因此没有明确的解决方案可以保证成功。
广义上讲,实施过程分为三个阶段:
-
休眠阶段:当公司尚未建立明确的指令,但已开始讨论大数据时
-
被动阶段:讨论开始变得更正式,通常会导致委派一个或多个团队评估对组织的影响和价值
-
积极阶段:公司开始评估技术并积极实施
大数据和/或数据科学战略的所有权可能会有些混乱。这是因为该领域涵盖了分析和技术两个方面。前者,即分析,通常由面向业务的部门拥有,而技术则由 IT 部门拥有。然而,在数据科学中,两者都是必需的。数据专家,也就是那些对领域非常了解并有相关数据经验的人,通常是优秀的业务领域专家。他们可能还能够全面识别理想的使用案例,以及如何最有效地利用数据。然而,如果没有强大的技术能力,识别出实现愿景所需的正确工具将会非常困难。
同样,IT 经理可能对大数据相关技术非常熟悉,但仍然需要来自业务利益相关者的反馈,才能有效地判断哪些解决方案能满足组织的具体即时和长期需求。
换句话说,多个跨学科的团队需要合作,才能实现真正有效的组织大数据生态系统。
实现战略的过程通常是自上而下或自下而上的。然而,与其采用僵化的方向性方法,协作性、迭代性和敏捷性的过程通常是最好的解决方案。在评估大数据需求的过程中,会出现决策,并根据新的需求和发现对决策进行调整,之前的评估可能需要进行修改,以适应调整后的目标。
自下而上的方法从 IT 层面开始构建决策。相对而言,更常见的自上而下的方法则是从管理层开始决策。两者通常都不是最优的。理想的方法是一个持续反馈循环,基于发现过程来调整需求:
相比之下,自上而下的方法如下:
无论是自下而上的方法,还是自上而下的方法,都不是成功实施大数据项目的最佳选择。一个更好的选择是一个协作过程,它考虑到不同部门在实施大数据平台过程中不断变化的需求和多样化的要求,这些部门能够从中受益:
伦理考虑
大数据通常涉及收集大量可能包含用户个人信息的数据。像 Facebook 和 Google 这样的公司,通过分析个人信息来精准投放广告并执行其他类型的营销,繁荣发展。这显然带来了伦理困境。个人数据应该收集到什么程度?多少才算是过多?当然,这些问题并没有正确答案。如今,黑客攻击事件屡见不鲜,数亿用户账户信息被泄露,我们几乎已经对这些后果麻木了。
2017 年 10 月,Yahoo!披露了 30 亿个账户——事实上,Yahoo!上的每一个账户——都遭遇了数据泄露。美国最大的信用报告公司之一Equifax也遭遇了数据泄露,暴露了超过 1.4 亿消费者的个人信息。还有许多类似的事件,在所有这些事件中,常见的一个特点是,这些公司都收集了某种程度的用户个人信息,无论是直接收集还是通过第三方。
本质上,每当涉及用户相关信息时,就需要实施适当的 IT 安全措施,以确保数据不被泄露。其后果不仅仅是数据丧失,还包括因声誉和信誉受损而造成的附带损害,除此之外,真实的个人数据遭到泄露也是一个关键问题。
因此,当大数据涉及敏感信息和/或任何个人信息时,其安全性变得至关重要。云服务提供商,如 AWS 和 Azure,部分原因是它们拥有非常严格的安全标准以及允许组织将责任转移到一个可信且强大的实体的认证,因而得到了广泛应用。
欧盟GDPR(通用数据保护条例)自 2018 年 5 月起生效,是保护公民个人数据的重要一步。简而言之,GDPR 规范了个人数据的使用。在这一背景下,术语“任何”极为广泛,甚至包括个人的名字。违反规定的行为将被罚款最多 2000 万欧元,或者是违规组织全球营业额的 4%。
虽然这显然会减少大数据数据集的可用性,特别是那些与个人数据相关的部分,但它也可能引发关于如何在限制条件下最有效利用数据的辩论,换句话说,就是如何在不使用个人信息的情况下从数据中获取价值。
另一方面,美国等国家已经放宽了个人数据收集的相关法律。2017 年初,美国取消了互联网服务提供商收集个人信息的隐私保护,事实上,还使得像 AT&T 这样的 ISP 合法化了不仅收集而且销售用户的浏览和应用数据的行为。
硅谷与数据科学
我们今天在大数据领域看到的几项关键创新,源自硅谷。这个地区已经是科技中心数十年,推出了一些最成功的公司,如苹果、谷歌、Facebook 和 eBay。加利福尼亚大学伯克利分校等大学的存在,也使得人才的获取相对容易。
尽管如此,随着大数据和数据科学行业的增长,硅谷的生活成本已经飙升。如今,一间一居室公寓的平均租金已经超过了每月$3,500,甚至比纽约市还要贵。
然而,硅谷是成功的代名词,许多新创业者纷纷涌向这个地区。初创公司如雨后春笋般涌现,其中许多公司获得了数千万美元的风险投资。然而,创业者们应该注意统计数据警告,因为初创企业的失败率很高。拥有一个伟大的创意并具有潜在的高商业价值是一回事,但将这个创意转化为商业成功则需要另一种技能和商业头脑。
对于感兴趣的人来说,Crunchbase 网站提供了一个非常全面的视角,展示了初创公司领域的各种活动。下图展示了在任意七天周期内,投资的平均数据,这些投资可能高达数百亿美元:
人为因素
尽管大数据和数据科学具有显著优势,且已经取得了突破性增长,但仍然需要牢记,人类思维这一元素在所有事业中都是不可或缺的。
大数据技术将使我们能够更高效地分析数据。但我们仍然需要运用正确的判断来决定理想的使用场景。这并非小事。大型公司发现,与经验丰富的大数据专业人士一样,推出新项目的挑战是巨大的(尽管规模更大)。
类似地,数据科学和机器学习可以通过复杂的算法和代码帮助我们进行预测和获取前瞻性洞察。然而,最终还是需要用户来评估结果,并做出决策,而不仅仅是依据预测结果。用户在做出这种评估时,应该运用常识和经验。如果 GPS 指示司机在一个雪夜走某条路,而司机知道那条路雪橇车没有清扫过,那么盲目跟随 GPS 指示就不明智。
一个最近的例子说明了这一概念,就是 2016 年大选期间人类与机器的脱节。通过非常复杂的算法估算,预测希拉里有很高的概率赢得选举,但这些模型全都被证明是错误的。著名的数据科学家和领先的统计组织估计希拉里当选总统的几率为 80-90%。我们所忽视的是决策中的人类因素。我们过于依赖模型的复杂性,而没有尝试理解人们的真实感受,以及他们在非正式调查中的表态是否与最终的候选人选择和真实感受一致。
成功项目的特点
数据科学项目本质上需要较长时间才能实现投资回报。尤其是,对于涉及长期预测的项目,很难准确衡量其成功。正如前面章节所提到的,要推动数据科学的进步,各部门必须展示早期的成功。通常,a) 短期的;b) 有可衡量的结果;c) 能够惠及广泛用户并且将被使用的项目是帮助建立信誉和确保数据科学相关项目成功的关键因素。
这样的项目的一个例子是 Arterys,这是一家基于云的公司,在 2016 年底开发了一种深度学习算法,该算法能够在比传统扫描仪更短的时间内评估心脏的血流。这满足了所有关键的成功标准。其好处几乎立竿见影,因为该算法提供了一个可以直接衡量的结果,因为你可以将其结果与扫描仪的结果进行对比,而且它对广泛的用户群体——即患者——非常有用。此外,这个话题也非常直观。
任何做过血液检测的人都能直观地理解如何测量血流。这样的产品好处显而易见。能够在极短的时间内获得结果有可能挽救生命。2016 年 11 月,FDA 批准了该算法。这是一个具有里程碑意义的成就。
摘要
总体而言,尽管通往大数据和数据科学成功的道路看起来可能颇为艰难,但希望前面的章节已经为大数据的各个主题提供了全面的概述。我们讨论了数据挖掘和机器学习,了解了各自领域中的各种工具和技术,并基于真实世界的数据开发了应用程序,还提供了关于组织大数据和数据科学计划细节的告别思考。
接下来的几页列出了读者可能觉得有用的一些资源链接,供其进一步学习相关领域的内容。
第十一章:外部数据科学资源
这本书涵盖了非常广泛的内容,从 Hadoop 到 NoSQL,从机器学习到企业数据科学。信息已经相当详尽,但由于篇幅限制,我们没有深入探讨任何一个特定领域。
在接下来的几个部分中,我们提供了外部材料的链接,以供进一步阅读。如果你想深入了解相关主题,这些内容可能会对你有帮助。
这里的几个解决方案都是开源或企业软件的社区版本。如果你正在考虑大数据和/或数据科学的企业解决方案,后者尤其有用。
大数据资源
Apache Hadoop:
Apache Spark:
Databricks Spark:社区访问 Spark 集群:
community.cloud.databricks.com/
NoSQL 产品
Redis:内存中的键值 NoSQL 数据库:
Cassandra:最强大的生产级 NoSQL 系统之一:
MongoDB:基于文档的 NoSQL 数据库:
Vertica:社区版:
Teradata:Aster 社区版:
aster-community.teradata.com/community/download
语言和工具
开源 R:
Microsoft Open R(附加企业功能):
RStudio:R 的开发界面:
开源 Python:
Anaconda Python:预安装了数据科学及其他重要包的 Python:
创建仪表盘
R Shiny:R 中的仪表盘:
www.rstudio.com/products/shiny/
Dash:Python 中的仪表盘:
笔记本
Jupyter:R、Python 和其他多种语言的笔记本界面:
Beaker Notebook:类似于 Jupyter,但使用较少:
可视化库
Bokeh:一个非常优秀的 Python 绘图库:
Plotly:仪表盘和报告:企业版和开源版:
RCharts:R 中广泛使用的绘图包:
HTMLWidgets:使用 JavaScript 在 R 中创建交互式仪表盘:
ggplot2:终极 R 图形库:
R 课程
edX 上的 R 课程:
www.edx.org/course?search_query=R+programming
简洁的 R 教程:
Coursera:大数据和 R 课程:
www.coursera.org/specializations/big-data
www.coursera.org/courses?languages=en&query=r+programming
机器学习课程
哈佛 CS109:使用 Python 的最全面的机器学习课程(根据作者的说法):
cs109.github.io/2015/pages/videos.html
Caltech 学习数据:最全面的机器学习理论 MOOC:
work.caltech.edu/telecourse.html
Coursera:各种机器学习课程:
www.coursera.org/learn/machine-learning
斯坦福:由 Trevor Hastie 和 Rob Tibshirani 主讲的统计学习:
lagunita.stanford.edu/courses/HumanitiesSciences/StatLearning/Winter2016/about
Andrew Ng 的机器学习:最广为人知的机器学习 MOOC 之一:
www.coursera.org/learn/machine-learning
机器学习和深度学习链接
Scikit-Learn:Python 中最全面的机器学习包:
Tensorflow:谷歌的著名深度学习解决方案:
MLPACK:使用 C++ 和 Unix 命令行的机器学习:
Word2Vec:著名的自然语言处理包之一:
Vowpal Wabbit:在许多 Kaggle 比赛中使用的优秀机器学习软件:
github.com/JohnLangford/vowpal_wabbit/wiki/Tutorial
LIBSVM 和 LIBLINEAR:广受好评的命令行机器学习工具:
www.csie.ntu.edu.tw/~cjlin/libsvm/
www.csie.ntu.edu.tw/~cjlin/liblinear/
LIBFM:矩阵分解:
PaddlePaddle:百度的深度学习平台:
github.com/PaddlePaddle/Paddle
CuDNN:NVIDIA 提供的深度学习/神经网络解决方案:
Caffe:来自伯克利的深度学习框架:
Theano:在 Python 中使用 GPU 加速的机器学习:
deeplearning.net/software/theano/
Torch:在 Lua 中的高性能机器学习:
Keras:开源神经网络应用:
基于 Web 的机器学习服务
AzureML:微软 Azure 云中的机器学习:
azure.microsoft.com/en-us/services/machine-learning/
H2O: 高性能机器学习平台:支持 R、Python 等多种语言:
BigML:视觉上吸引人的基于 Web 的机器学习平台:
电影
模仿游戏:关于艾伦·图灵的电影:
美丽心灵:关于约翰·纳什的电影:
2001:太空漫游:
点球成金:关于赛伯计量学的电影:
机械姬:关于人工智能的电影:
终结者 2:一部已经成为经典的电影:
Packt 出版的机器学习书籍
Giancarlo Zaccone 的《TensorFlow 入门》:
www.packtpub.com/big-data-and-business-intelligence/getting-started-tensorflow
Sebastian Raschka 的《机器学习与深度学习》:
www.amazon.com/Python-Machine-Learning-scikit-learn-TensorFlow/dp/1787125939
Brett Lantz 的《R 语言机器学习》:
www.amazon.com/Machine-Learning-techniques-predictive-modeling/dp/1784393908
休闲阅读书籍
逻辑与数学的经典之作:
www.amazon.com/Gödel-Escher-Bach-Eternal-Golden/dp/0465026567
简单解释哥德尔不完备定理:
www.amazon.com/Gödels-Proof-Ernest-Nagel/dp/0814758371/
罗杰·彭罗斯谈人工智能及其他话题:
www.amazon.com/Emperors-New-Mind-Concerning-Computers/dp/0198784929/