处理机器学习问题
学完了本书介绍的所有强大的方法,你现在可能很想马上行动,开始用你最喜欢的算法来解决数据相关的问题。但这通常并不是开始分析的好方法。机器学习算法通常只是更大的数据分析与决策过程的一小部分。为了有效地利用机器学习,我们需要退后一步,全面地思考问题。首先,你应该思考想要回答什么类型的问题。你想要做探索性分析,只是看看能否在数据中找到有趣的内容?或者你已经有了特定的目标?通常来说,你在开始时有一个目标,比如检测欺诈用户交易、推荐电影或找到未知行星。如果你有这样的目标,那么在构建系统来实现目标之前,你应该首先思考如何定义并衡量成功,以及成功的解决方案对总体业务目标或研究目标有什么影响。假设你的目标是欺诈检测。
然后就出现了下列问题:
- 如何度量欺诈预测是否实际有效?
- 我有没有评估算法的合适数据?
- 如果我成功了,那么我的解决方案会对业务造成什么影响?
正如第 5 章中所述,你最好能使用商业指标直接度量算法的性能,比如增加利润或减少损失。但这通常难以做到。一个更容易回答的问题是:“如果我构建出完美的模型,那么会怎么样?”如果完美地检测出所有欺诈行为可以为你的公司每月节省 100 美元,那么这些省下来的钱可能都不够保证让你开始动手开发算法。如果模型可能为你的公司每月节省上万美元,那么这个问题就值得探索。
假设你已经定义好了要解决的问题,知道一种解决方案可能对你的项目产生重大影响;此外,你还确信拥有合适的信息来评估模型是否成功。接下来的步骤通常是获取数据并构建工作原型。本书中我们讨论过许多你可以使用的模型,以及如何正确地评估和调节这些模型。但在尝试这些模型时请记住,这只是更大的数据科学工作流程中的一小部分,模型构建通常是“收集新数据、清洗数据、构建模型和分析模型”这个反馈环路的一部分。分析模型所犯的错误通常告诉我们:数据中缺失了哪些内容、还可以收集哪些额外数据,或者如何重新规划任务使机器学习更加高效。收集更多数据或不同的数据,或者稍微改变任务规划,可能会比连续运行网格搜索来调参的回报更高。
参与决策过程的人
你还应该考虑是否应该让人参与决策过程,以及如何参与。有些过程(比如无人驾驶汽车的行人检测)需要立即做出决定。其他过程可能不需要立刻的响应,所以可以让人来决定不确定的决策。举个例子,医疗应用可能需要非常高的精度,单靠机器学习算法可能无法达到。但如果一个算法可以自动完成 90%、50% 甚至只有 10% 的决策过程,那么都可能已经减少了响应时间或降低了成本。许多应用都以“简单情况”为主,算法可以对其做出决策,还有相对较少的“复杂情况”,可以将其重新交给人来决定。
从原型到生产
本书中讨论的工具对许多机器学习应用来说都是很好的,可以非常快速地进行分析和原型设计。许多组织,甚至是非常大型的组织,比如国际银行和全球社交媒体公司,也将 Python 和 scikit-learn
用于生产系统。但是,许多公司拥有复杂的基础架构,将 Python 集成到这些系统中并不总是很容易。这不一定是个问题。在许多公司中,数据分析团队使用 Python 或 R 等语言,可以对想法进行快速测试,而生产团队则使用 Go、Scala、C++ 和 Java 等语言来构建鲁棒性更好的可扩展系统。数据分析的需求与构建实时服务并不相同,所以这些任务使用不同的语言是有道理的。一个相对常见的解决方案是使用一种高性能语言在更大的框架内重新实现分析团队找到的解决方案。这种方法比嵌入整个库或整个编程语言并与不同数据格式互相转换要更加简单。
无论你能否在生产系统中使用 scikit-learn
,重要的是要记住,生产系统的要求与一次性的分析脚本不同。如果将一个算法部署到更大的系统中,那么会涉及软件工程方面的很多内容,比如可靠性、可预测性、运行时间和内存需求。对于在这些领域表现良好的机器学习系统来说,简单就是关键。请仔细检查数据处理和预测流程中的每一部分,并问你自己这些问题:每个步骤增加了多少复杂度?每个组件对数据或计算基础架构的变化的鲁棒性有多高?每个组件的优点能否使其复杂度变得合理?如果你正在构建复杂的机器学习系统,我们强烈推荐阅读 Google 机器学习团队的研究者发布的这篇论文:“Machine Learning: The High Interest Credit Card of Technical Debt”(http://research.google.com/pubs/pub43146.html)。这篇文章重点介绍了在大规模生产中创建并维护机器学习软件的权衡。
虽然技术债问题在大规模的长期项目中特别紧迫,但即使是短期和较小的系统,吸取的教训也有助于我们构建更好的软件。
测试生产系统
在这本书中,我们介绍了如何基于事先收集的测试集来评估算法的预测结果。这被称为离线评估(offline evaluation)。但如果你的机器学习系统是面向用户的,那么这只是评估算法的第一步。下一步通常是在线测试(online testing)或实时测试(live testing),对在整个系统中使用算法的结果进行评估。改变网站向用户呈现的推荐结果或搜索结果,可能会极大地改变用户行为,并导致意想不到的结果。为了防止出现这种意外,大部分面向用户的服务都会采用 A/B 测试(A/B testing),这是一种盲的(blind)用户研究形式。在 A/B 测试中,在用户不知情的情况下,为选中的一部分用户提供使用算法 A 的网站或服务,而为其余用户提供算法 B。对于两组用户,在一段时间内记录相关的成功指标。然后对算法 A 和算法 B 的指标进行对比,并根据这些指标在两种方法中做出选择。使用 A/B 测试让我们能够在实际情况下评估算法,这可能有助于我们发现用户与模型交互时的意外后果。通常情况下,A 是一个新模型,而 B 是已建立的系统。在线测试中还有比 A/B 测试更为复杂的机制,比如 bandit 算法。John Myles White 的 Bandit Algorithms for Website Optimization(O’Reilly 出版社,http://shop.oreilly.com/product/0636920027393.do)一书对这一主题做出了很好的介绍。
构建你自己的估计器
本书包含 scikit-learn
中实现的大量工具和算法,可用于各种类型的任务。但是,你通常需要对数据做一些特殊处理,这些处理方法没有在 scikit-learn
中实现。在将数据传入 scikit-learn
模型或管道之前,只做数据预处理可能也足够了。但如果你的预处理依赖于数据,而且你还想使用网格搜索或交叉验证,那么事情就变得有点复杂了。
我们在第 6 章中讨论过将所有依赖于数据的处理过程放在交叉验证循环中的重要性。那么如何同时使用你自己的处理过程与 scikit-learn
工具?有一种简单的解决方案:构建你自己的估计器!实现一个与 scikit-learn
接口兼容的估计器是非常简单的,从而可以与 Pipeline
、GridSearchCV
和 cross_val_score
一起使用。你可以在 scikit-learn
文档中找到详细说明(http://scikit-learn.org/stable/developers/contributing.html#rolling-your-own-estimator),但下面是其要点。实现一个变换器类的最简单的方法,就是从 BaseEstimator
和 TransformerMixin
继承,然后实现 __init__
、fit
和 predict
函数,如下所示。
In[1]:
实现一个分类器或回归器的方法是类似的,你只需要从 ClassifierMixin
或 RegressorMixin
继承,而不是 TransformerMixin
。此外,你还要实现 predict
,而不必实现 transform
。
从上面的例子中可以看出,实现你自己的估计器需要很少的代码,随着时间的推移,大部分 scikit-learn
用户都会构建出一组自定义模型。
### 下一步怎么走
本书对机器学习进行了介绍,并让你成为一名高效的从业者。但是,如果你想要进一步提高机器学习技能,下面是一些关于书籍和更专业的资源的建议,以便你进一步深入研究。
理论
在本书中,我们试图直观地解释大多数常见机器学习算法的工作原理,而不要求你在数学或计算机科学方面具有坚实的基础。但是,我们讨论的许多模型都使用了概率论、线性代数和最优化方面的理论。虽然没有必要理解这些算法的所有实现细节,但我们认为,了解算法背后的一些理论知识可以让你成为更优秀的数据科学家。关于机器学习理论有许多好书,如果我们所讲的内容激起了你对机器学习可能性的兴趣,那么我们建议你挑选至少一本书深入阅读。我们在前言中已经提到过 Hastie、Tibshirani 和 Friedman 合著的《统计学习基础》一书,这里值得重新推荐一下。另一本相当好读的书是 Stephen Marsland 的 Machine Learning: An Algorithmic Perspective(Chapman and Hall/CRC 出版社),它还附带有 Python 代码。还有两本强烈推荐的经典著作:一本是 Christopher Bishop 的 Pattern Recognition and Machine Learning(Springer 出版社),着重于概率框架;另一本是 Kevin Murphy 的 Machine Learning: A Probabilistic Perspective(MIT 出版社),全面论述了机器学习方法(1000 多页),深入介绍了最先进的方法,内容比本书要丰富得多。
其他机器学习框架和包
虽然 scikit-learn
是我们最喜欢的机器学习软件包1,Python 也是我们最喜欢的机器学习语言,但还有许多其他选择。根据你的需求,Python 和 scikit-learn
可能不是你在特定情况下的最佳选择。通常情况下,Python 很适合尝试与评估模型,但更大型的 Web 服务和应用更常用 Java 或 C++ 编写,部署模型可能需要与这些系统进行集成。你想要考虑 scikit-learn
之外的选择可能还有一个原因,就是你对统计建模和推断比对预测更感兴趣。在这种情况下,你应该考虑使用 Python 的 statsmodels
包,它用更具有统计学意义的接口实现了多种线性模型。如果你还没有专情于 Python,那么还可以考虑使用 R,这是数据科学家的另一种语言。R 是专为统计分析设计的语言,因其出色的可视化功能和许多可用的统计建模包(通常是非常专业化的)而闻名。
另一个常用的机器学习软件包是 vowpal wabbit
(通常简称为 vw
,以避免绕口),一个用 C++ 编写的高度优化的机器学习包,还有命令行界面。vw
对大型数据集和流数据特别有用。对于在集群上分布式运行的机器学习算法,在写作本书时最常用的解决方案之一是 mllib
,一个基于 spark
分布式计算环境构建的 Scala 库。
排序、推荐系统与其他学习类型
本书是一本入门书,所以我们重点介绍最常见的机器学习任务:监督学习中的分类与回归,无监督学习中的聚类和信号分解。还有许多类型的机器学习,都有很多重要的应用。有两个特别重要的主题没有包含在本书中。第一个是排序问题(ranking),对于特定查询,我们希望检索出按相关性排序的答案。你今天可能已经使用过排序系统,它是搜索引擎的运行原理。你输入搜索查询并获取答案的有序列表,它们按相关性进行排序。Manning、Raghavan 和 Schuütze 合著的 Introduction to Information Retrieval 一书给出了对排序问题的很好介绍。第二个主题是推荐系统(recommender system),就是根据用户偏好向他们提供建议。你可能已经在“您可能认识的人”“购买此商品的顾客还购买了”或“您的最佳选择”等标题下遇到过推荐系统。关于这一主题有大量文献,如果想立刻投身于这一主题,你可能对目前经典的“Netflix 大奖挑战”(Netflix prize challenge,http://www.netflixprize.com/)感兴趣。Netflix 视频流网站发布了关于电影偏好的大型数据集,并对给出最佳推荐的团队奖励一百万美元。另一种常见的应用是时间序列预测(比如股票价格),这方面也有大量的文献。还有许多类型的机器学习任务,比我们这里列出的要多得多,我们建议你从书籍、研究论文和在线社区中获取信息,以找到最适合你实际情况的范式。
概率建模、推断与概率编程
大部分机器学习软件包都提供了预定义的机器学习模型,每种模型应用了一种特定算法。但是,许多现实世界的问题都具有特殊的结构,如果将这种结构正确地纳入模型,则可以得到性能更好的预测。通常来说,具体问题的结构可以用概率论的语言来表述。这种结构通常来自于你想要预测的情况的数学模型。为了理解结构化问题的含义,请思考下面这个例子。
假设你想要构建一个在户外空间提供非常详细的位置估计的移动应用,以帮助用户定位历史遗迹。手机提供了许多传感器来帮你获取精确的位置测量,比如 GPS、加速度计和指南针。你可能还有该区域的精确地图。这个问题是高度结构化的。你从地图中知道了感兴趣地点的位置和路径。你还从 GPS 中得到了粗略的位置,而用户设备中的加速度计和指南针可以为你提供非常精确的相对测量。但是,将所有这些工具全部放入黑盒机器学习系统中来预测位置,可能并不是最好的主意。这将会丢掉你已经知道的关于现实世界如何运行的所有信息。如果指南针和加速度计告诉你一名用户正在向北走,而 GPS 告诉你该用户正在向南走,你可能不相信 GPS。如果位置估计告诉你用户刚刚走过一堵墙,你应该也会非常怀疑。可以使用概率模型来表述这种情况,然后再使用机器学习或概率推断来找出你应该对每种测量方法的信任程度,并推断出该用户位置的最佳猜测。
一旦你用正确的方式对现状和不同因素共同作用的模型进行表述,那么就有一些方法可以利用这些自定义模型直接计算出预测结果。这些方法中最普遍的方法被称为概率编程语言,它们提供了一种非常优雅又非常紧凑的方法来表述学习问题。概率编程语言的常见例子有 PyMC
(可用于 Python)和 Stan
(可用于多种语言的框架,包括 Python)。虽然这些软件包需要你对概率论有一些了解,但它们大大简化了新模型的创建过程。
神经网络
虽然我们在第 2 章和第 7 章都简要涉及了神经网络的主题,但这是机器学习快速发展的领域,每周都会发布新方法和新应用。机器学习和人工智能领域的最新突破都由这些进步所驱动,比如 Alpha Go 程序在围棋比赛中战胜人类冠军、语音理解的性能不断提高,以及接近实时的语音翻译的出现。虽然这一领域的进步非常迅速,以致当前对最新进展的任何参考很快都会过时,但 Ian Goodfellow、Yoshua Bengio 和 Aaron Courville 的新书 Deep Learning(MIT 出版社)对这一主题进行了全面介绍。2
推广到更大的数据集
在本书中我们总是假设,所处理的数据可以被存储为内存(RAM)中的一个 NumPy 数组或 SciPy 稀疏矩阵。即使现代服务器通常都具有数百 GB 的 RAM,但这也是对你所能处理数据大小的根本限制。不是所有人都买得起这么大型的机器,甚至连从云端供应商租一台都负担不起。不过在大多数应用中,用于构建机器学习系统的数据量相对较小,很少有机器学习数据集包含数百 GB 以上的数据。在多数情况下,这让扩展内存或从云端供应商租一台机器变成可行的解决方案。但是,如果你需要处理 TB 级别的数据,或者需要节省处理大量数据的费用,那么有两种基本策略:核外学习(out-of-core learning)与集群上的并行化(parallelization over a cluster)。
核外学习是指从无法保存到主存储器的数据中进行学习,但在单台计算机上(甚至是一台计算机的单个处理器)进行学习。数据从硬盘或网络等来源进行读取,一次读取一个样本或者多个样本组成的数据块,这样每个数据块都可以读入 RAM。然后处理这个数据子集并更新模型,以体现从数据中学到的内容。然后舍弃该数据块,并读取下一块数据。scikit-learn
中的一些模型实现了核外学习,你可以在在线用户指南中找到相关细节(http://scikit-learn.org/stable/modules/scaling_strategies.html#scaling-with-instances-using-out-of-core-learning)。因为核外学习要求所有数据都由一台计算机处理,所以在非常大的数据集上的运行时间可能很长。此外,并非所有机器学习算法都可以用这种方法实现。
另一种扩展策略是将数据分配给计算机集群中的多台计算机,让每台计算机处理部分数据。对于某些模型来说这种方法要快得多,而可以处理的数据大小仅受限于集群大小。但是,这种计算通常需要相对复杂的基础架构。目前最常用的分布式计算平台之一是在 Hadoop 之上构建的 spark
平台。spark
在 MLLib
包中包含一些机器学习功能。如果你的数据已经位于 Hadoop 文件系统中,或者你已经使用 spark
来预处理数据,那么这可能是最简单的选项。但如果你还没有这样的基础架构,建立并集成一个 spark
集群可能花费过大。前面提到的 vw
包提供了一些分布式功能,在这种情况下可能是更好的解决方案。
磨练你的技术
与生活中的许多事情一样,只有实践才能让你成为本书所介绍主题方面的专家。对于不同的任务和不同的数据集,特征提取、预处理、可视化和模型构建可能差异很大。你或许足够幸运,已经能够访问大量数据集和任务。如果你还没有想到什么任务,那么一个好的起点是机器学习竞赛,它会发布一个数据集和一个给定任务,许多团队为得到最佳预测结果而展开竞争。许多公司、非盈利组织和大学都会举办这种比赛。要想找到这些比赛,最常去的地方之一是 Kaggle(https://www.kaggle.com/),这是一个定期举办数据科学比赛的网站,其中一些比赛会提供大量奖金。
Kaggle 论坛也是关于机器学习最新工具和技巧的很好的信息来源,在网站上可以找到大量数据集。在 OpenML 平台(http://www.openml.org/)上可以找到更多的数据集及相关任务,该平台拥有 20 000 多个数据集,以及 50 000 多个相关的机器学习任务。处理这些数据集可以提供练习机器学习技能的好机会。比赛的一个缺点是,提供了特定的指标来优化,通常还提供了一个固定的、已经预处理过的数据集。请记住,定义问题和收集数据也是现实世界问题的重要方面,用正确的方式表示问题可能比努力提高分类器精度的最后一个百分点要重要得多。
### 总结
我们希望已经让你相信了机器学习在大量应用中的实用性,以及在实践中实现机器学习的简单性。继续挖掘数据,同时不要忽视大局。