书名:Python机器学习
ISBN:978-7-115-50135-6
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
著 [印] 阿布舍克•维贾亚瓦吉亚(Abhishek Vijayvargia)
译 宋格格
责任编辑 陈聪聪
人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
读者服务热线:(010)81055410
反盗版热线:(010)81055315
Original edition published by BPB Publications. Copyright © 2018 by BPB Publicatios. Simplified Chinese-language edition copyright © 2018 by POSTS & TELECOM PRESS. All rights reserved.
本书中文简体字版由印度BPB Publications授权人民邮电出版社出版。未经出版者书面许可,对本书任何部分不得以任何方式复制或抄袭。
版权所有,侵权必究。
本书通过解释数学原理和展示编程示例对机器学习进行了系统、全面的解析。全书共分为12章,内容涵盖了机器学习以及Python语言的基础知识、特征工程的概念与操作技术、数据可视化技术的实现、监督学习及无监督学习算法、文本分析、神经网络和深度学习、推荐系统的构建方法以及预测处理时间序列的方法等。阅读本书能够加深读者对机器学习的认识和理解,从而达到理论与实践相结合、学以致用的目的。
本书适合Python程序员、数据分析人员、对机器学习感兴趣的读者以及机器学习领域的从业人员阅读。
图1.18
图1.19
图1.20
图1.21
图1.22
图1.25
图1.26
图4.1
图4.2
图4.5
图4.6
图4.8
图4.9
图4.10
图4.22
图5.21
图6.16
图7.11
图7.12
图7.13
图7.14
图7.15
图7.22
图7.23
图7.24
图7.25
图7.26
图7.27
图7.29
图7.30
图7.31
图7.32
图7.33
图7.34
图7.35
图7.36
图7.37
图7.38
图8.2
图8.3
图8.4
图8.10
图12.2
图12.7
图12.16
图12.17
图12.18
感谢BPB出版社使这本书得以出版。
非常感谢我的家人和朋友,没有他们的支持,我将无法完成这项任务。
“无论我走多远,我的父母都会永远和我在一起。”
——Abhishek Vijayvargia
机器学习是计算机科学领域未来发展的重点之一。从制造业到电子商务、再到运输业、机器学习无处不在,并提供基于数据的解决方案。机器学习背后的理念是在决策过程中建立一个与人类一样聪明的知识模型。现在,随着计算和存储能力的增强,机器学习相比人类可以提供更好的解决方案。
本书由12章组成,每一章涉及机器学习的一个主要领域。
第1章从机器学习的基本介绍开始,读者可以结合实际应用案例来了解通用机器学习算法的思想。第2章介绍了Python语言的基础知识, Python是一种开源语言,也是一种很好的机器学习应用工具,这是选择它来编写机器学习算法的原因。第3章提供了特征工程的相关操作技术,对机器学习而言,寻找正确的特征并加以修改与算法同样重要。第4章重点介绍数据可视化技术,使用预先构建的Python库,用户可以将数据可视化并向他人展示。
第5章~第7章涵盖了监督学习算法的内容。第5章通过实例解释基本回归技术。第6章通过过拟合问题的解决方案来重点介绍高级回归技术。第7章描述了分类算法的细节,从参数化技术和非参数化技术两方面来探讨解决分类问题。第8章引入无监督学习的思想,重点介绍了聚类。
第9章~第12章提供了机器学习中一些高级概念。第9章介绍了文本分析,包含一个示例——将新闻分类到预定义类别中。第10章讨论了神经网络和深度学习,其在非结构化、图像和语音数据中得到了高度的应用。第11章介绍了构建推荐系统的方法,对每种方法给出了相应的案例。第12章讨论时间序列数据、处理和预测时间序列的方法。
本书对各学习阶段的读者都有帮助。无论读者是刚开始接触机器学习,还是想学习更深层的概念,又或者是想通过代码进行实践,本书均可满足需求。建议读者通过使用示例代码来学习概念并实践它,以掌握这本书的全部内容。
虽然其他数据科学家已经对书中的内容和代码进行了检查,但本书仍然可能存在一些缺陷。期待读者的建议和批评,我将尽力改正以求在未来的版本中不再出现这些错误。
本书由异步社区出品,社区(https://www.epubit.com/)为您提供相关资源和后续服务。
本书提供如下资源:
要获得以上配套资源,请在异步社区本书页面中点击,跳转到下载界面,按提示进行操作即可。注意:为保证购书读者的权益,该操作会给出相关提示,要求输入提取码进行验证。
作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。
当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,点击“提交勘误”,输入勘误信息,单击“提交”按钮即可。本书的作者和编辑会对您提交的勘误进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。
我们的联系邮箱是contact@epubit.com.cn。
如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。
如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区在线提交投稿(直接访问www.epubit.com/selfpublish/submission即可)。
如果您是学校、培训机构或企业,想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。
如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。
“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT技术图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT技术图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。
“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社近30年的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、 AI、测试、前端、网络技术等。
异步社区
微信服务号
从出生的那天起,我们就一直在学习中度过。随着逐渐的成长,开始学习如何走路;通过倾听周围人的谈话,学习并尝试模仿;通过学习不同单词的意思,使得在需要时懂得该如何进行表达;同时也开始区分事情的好坏。例如,在第一次靠近火源时,会因灼热而后退,从而明白要与火源保持一定的距离。
那么,现在思考一下计算机的工作原理。计算机可以在1s内遵循人类的指示处理数百万条指令,并返回结果;它可以执行人类描述的任务,但是却不能自行决定。
而机器学习此时就可以发挥作用了。如果给予计算机像人类一样的思考能力,那会发生什么呢?可以用计算机能理解的方式给出每一天的任务令其进行计算,还可以建立一些模型来帮助计算机在将来采取行动。
人类从经验中学习知识,计算机则遵循指令执行任务。然而,其实还可以直接向计算机提供经验让其学习并为行动做准备。目前我们是以结构化的方式定义经验。因此,我们将计算机从数据(经验)中进行学习的过程称为机器学习(Machine Learning)。
以买香蕉为例。你的母亲需要你去市场上买一些香蕉,同时她告诉你鲜黄色的香蕉很好吃。你到一个水果商那里,开始按照母亲的建议挑选香蕉。你买了20根香蕉后就回家了,然而回家后你注意到有些香蕉的味道并不像其他的那样好。事实上,有5根香蕉是坏的。你逐个拿起香蕉并开始作出假设。 20根香蕉中有12根大香蕉和8根小香蕉。这8根小香蕉都很美味,但是大香蕉就不一样了,在12根大香蕉中有5根味道不如预期。
你已经掌握了挑选香蕉的知识。第二天,当你到达市场时,注意到有个水果商在打折出售香蕉,这些香蕉和昨天的不同,它们的皮带有一些绿色。从这里买完香蕉回到家后,根据之前的经验将每一根香蕉分为好与坏(视口味而定),会发现大的绿色香蕉很好吃,但是小的绿色香蕉的味道不如其他的好。这样,你就学会了新规则。
你开始将自己看作是香蕉专家。有一天,你不得不去另一个城市(远离家乡)参加表亲的婚礼。现在你惊讶地发现这里所有的香蕉都很小,但是味道却都很好(像糖一样甜)。在这里你了解到来自这个区域的香蕉是畅销商品。
现在你确实已经是一名专家了。但如果你的姐姐很长时间没有回家了,并且她喜欢石榴而讨厌香蕉。你要怎么办呢?为了找到美味的石榴,你需要重新开始你的学习。
现在这个任务就可以由机器学习负责指引计算机完成了。可以使用数据点的形式向计算机提供知识。数据点的属性称为特征(Feature),这里的特征是香蕉的大小(小、中、大)、颜色、产地等。输出(Output)就是味道(好或者坏)。将这些数据输入到机器学习程序中,就可以让计算机学习如何区分香蕉的好与坏。
机器学习算法是一种根据经验来给予你决策能力的智能算法。
机器学习不仅仅是一种简单的算法,还可以将其放在其他任何地方以获得奇妙的结果。机器学习是一个从定义数据开始,最终获得一定准确率的模型的过程。在本节中,我们将学习这个过程。
机器学习的过程从定义一个商业问题开始。机器学习的需求是什么?这个任务真的需要高级的预测算法来解决吗?
问题定义是非常重要的,它提供了更正式地思考解决方案的方向。它基本上处理两个问题。
A.问题是什么?
这个问题涵盖了问题的定义,并使问题变得更加正式。假设我们想要确认图像中是否包含人。
现在定义这个任务,将其分为任务(T)、经验(E)和性能(P)。
B.为什么这个问题需要解决方案?
这个问题更侧重于商业方面,它包括解决问题的动机和益处。
假如你是一个研究者,希望解决某个问题并发表论文,使之成为他人解决问题的基准,这可能就是你的动机。
关于你的问题其他需要确定的是,在没有安全措施的情况下,夜间在银行的自动取款机上是否有人类活动(假设所要解决的问题与自动取款机的安全有关)。
还需要定义可以使用此解决方案的场景。这是一个通用型的解决方案,还是为特定任务(ATM传感器中的检测人员)所设计的呢?解决方案的失效日期是什么时候(它是终身的还是在特定的期限内)?
在定义问题之后,开始进行数据采集。收集数据有多种不同的方式,如果想把评论与评级联系起来,就要从抓取网站开始。为了分析Twitter数据并将其与情感联系起来,就要从Twitter提供的API入手,收集标签数据或者与某个公司相关联的数据。市场调查人员通过创建不同的调查表格,并将其放在网站上来收集数据。对于像Amazon、 Facebook这样拥有众多用户的公司,其数据是巨大的,而根据问题的不同,还需要收集数据和标签。假设要建立一个新闻分类器,将新闻划分为体育新闻、市场新闻和政治新闻。因此,收集到的每一条新闻都需要一个与之相关的标签,通过这些数据可以构造机器学习的分类器。
正确的数据是解决机器学习问题的关键。即使是基本算法,高质量的数据也会产生令人满意的结果。
在采集数据之后,需要专注于数据准备。收集的数据要准备成机器学习算法所能使用的格式。算法不是魔术表演,数据必须以正确的形式输入到机器学习算法中才能获得结果。根据算法库的不同,算法可以适应不同类型的输入格式。
数据准备是从数据选择开始的,并不是收集到的每个数据都对问题的解决有帮助。假设你正在分析服务器上的日志,在每个用户活动之后会生成许多与系统相关的信息,如果正在预测的是营销活动的市场反应,那么这个日志可能就不起作用了。所以,基于所要解决的问题,应将无关数据从之后的操作中删除。
在更高层次的基础上识别数据后,需要对数据进行转换或预处理,使之能更好地应用于机器学习算法。以下是数据预处理过程中的一些过程。
这个过程也称为特征处理(Feature Processing),它包括特征选择、预处理以及将其转换为对于机器学习算法有益处的格式。
机器学习算法的目标是对未知的新数据进行预测。我们使用训练数据来建立模型。在训练数据时,算法将逐渐减少训练的误差。但是,不能将训练数据的准确率(Accuracy)视为广义的准确率,其原因在于该算法可能会记住实例并对其进行相应的分类。所以,为了评估生成的模型,需要将数据分为训练数据和测试数据。利用训练数据进行算法训练,利用测试数据来计算生成模型的最终准确率。测试数据并不参与算法训练。
一般将60%~80%的数据作为训练数据,剩余的部分作为测试数据。所以,在测试数据中获得最好结果的模型可以作为目标模型。
我们从一组机器学习算法开始,并将训练数据的特征应用到算法中,算法选择依赖于问题的定义。如果从电子邮件中收集数据,并将邮件分为垃圾邮件或非垃圾邮件,则这时所需要的算法是在输入变量后输出相应的结果(垃圾邮件或者非垃圾邮件),这类算法称为分类(Classification)算法(如决策树、朴素贝叶斯、神经网络等)。如果想预测某一任意连续变量(如预测即将到来的季度销售量),则使用回归(Regression)算法(如线性回归、核回归等)。如果所解决的问题并没有相关的任何输出或反应,则可以使用聚类(Clustering)算法,根据它们的特性对其分组。每种类别中都有大量的算法,我们将在后续的章节中给出示例。
在选择算法之后,开始训练模型。训练模型是在训练数据集上进行的,大多数算法的权值/参数在训练开始时都是随机分配的,并在每次迭代中加以改进。算法训练过程中,在训练数据集上多次运行以产生结果,如图1.1所示。例如,在线性回归的情况下,算法在开始时随机放置分离线,在每次迭代之后不断地改进自身(也就是移动分离线)。
图1.1
利用训练数据生成最佳算法后,在测试数据集上对算法的性能进行评估。测试数据集并不能参与算法训练,因此测试数据并不能影响算法的决策。
在选择正确的算法后,可以尝试对其进行改进以获得更好的性能。每个算法都有不同的参数设置方式,可以对其进行配置从而更改算法性能,这称为参数调整(Parameter Tuning)。例如可以改变算法学习的速率(学习率)来提高算法性能,这些参数称为超参数(Hyper Parameter)。对于机器学习来说,参数调整更像是一门艺术。
上述所有步骤完成之后,就可以获得在训练集上训练生成,并在测试集上完成评估的模型。现在可以使用这个模型来预测新数据的值。对于生产环境,可以将模型部署到服务器,并通过API接口使用模型的预测功能。当然,这个模型并不总一样的,每当获得新数据时,都要将上面所列出的步骤重新进行一遍,以改进模型的性能。
因此,在机器学习中,从问题开始,最后以一个解决问题的预测算法结束。
通过下面的问题,我们开始尝试了解机器学习算法是如何解决问题的。
假设你需要购买一栋房子,现在开始查看市场上正在出售的房屋,并据此核对你的预算。你对将要购买的房子有多个要求,首先就是房子的面积。
在图1.2的情景中,你先考查了一栋600平方英尺(约56m2)的房子,价格是220 000英镑(约1 942 578元)。这个房子的确很不错,但是它并不满足你的要求——你将和你的家人一同居住,而这个房子面积太小,并不能让每个人都住得舒适。所以,你继续研究并找到一栋1700平方英尺(约158m2)的房子,价格是730 000英镑(约6 445 827元)。这个房子满足你的要求,但是它的价格有些超出你现在的预算——你的预算与小面积房子的价格相比要高一些,但也不像大房子的价格那么高。然而,只有当你与业主或代理人见面并提交详细资料后,才能得到房子的实际价格。但是你并不想与每一个业主或者代理人见面。
图1.2
在二维(2D)平面上分析一下你所考查的两个属性,如图1.3所示。
图1.3
现在,你在城市里闲逛,找到一栋面积在这两栋房子之间的房子。
这栋新房子的面积大约有1 250平方英尺(约116m2),但你还不知道价格。所以,你想预测房子的价格,看其是否符合预算和要求。
将新房子的信息放在同一个二维平面上,试着预测这个房子的价格,如图1.4所示。
图1.4
为了预测房屋的价格,将设置一条符合已知结果(即房屋价格和房屋面积)的直线,这里得到了如图1.5所示的一条直线。
图1.5
通过这条直线,可以预测出1250平方英尺(约116m2)的房子的价格是475 000英镑(约4 194 202.5元)。所以,我们得到了一条根据房子面积来预测房子价格的直线,这种技术称为线性回归(Linear Regression)。可以把这种技术理解为在现有数据点上寻找最佳直线。
比如,计算3个点到直线距离之和的最小值。首先,随机选择一条线,直线上方有A、B、C共3个点,如图1.6所示。
图1.6
然后,计算每个点到直线的距离,如图1.7所示。
图1.7
可以得到总距离是a+b+c。现在,移动直线的位置(向下移动)并再次计算点到直线的距离,如图1.8所示。
图1.8
改变了线的位置,但总距离a+b+c增加了。显然,这不是我们想要的结果。让我们将直线朝另一个方向(向上移动)移动,如图1.9所示。
图1.9
这条直线(见图1.9)比第一条直线(见图1.7)要更符合要求。现在移动这条线,并试着重复进行同样的步骤。最终通过这种方式结束直线的选择,从而确定直线位置,如图1.10所示。
图1.10
对于给定的3个点(A、 B、 C)来说,图1.10所示的直线是最符合条件的。在这里取3个点到直线的非负距离,找到这条线的方法称为梯度下降法。
有时在所有数据点上拟合一条直线并没有多大意义。现在思考图1.11所示的点集。
图1.11
如果尝试使用线性回归技术来拟合一条直线,它看起来如图1.12所示。
图1.12
很明显,这条线不适合用来预测。相反,可以用图1.13所示的曲线来对数据建模。
图1.13
这就是所谓的多项式回归(Polynomial Regression) —— 因为其参数是多项式。
再来看另一个例子,例如有一个售鞋网站,其中包含来自不同公司的各种鞋子,可以通过其网上商店订购鞋子。鞋子成功交付后,公司将发送电子邮件获取顾客的反馈。顾客在评论区内留言,其中有些评论是正面的,有些评论则是负面的。
该公司每天销售数千双鞋,需要跟踪每一个评论并采取相应的行动。如果顾客评论说鞋子质量不佳,则需向生产商询问有关产品的质量问题;有些鞋子反响很好,则最好将它们放在网站的首页。
为了解决这个问题,首先需要从一组顾客评论开始,将每个评论分为负面评论或者正面评论,下面给出部分示例。
正面评论
A1:质量不错!我很喜欢这双鞋子。
A2:非常好的产品。
A3:给我爸爸买的,他很喜欢。
负面评论
B1:材质不好,不适合。
B2:很不喜欢这个产品,包装也很不好。
B3:千万不要买这个产品。
分析示例中的正面评论和负面评论,会发现,如果评论中包含“喜欢”这个词,那么它更可能是一个正面评论。因此,创建这条规则并检查所有数据集,会发现60%的正面评论中包含“喜欢”这个词;另一方面,只有10%的负面评论中包含“喜欢”这个词。
同样,其他词语所占的比例如表1.1所示。
表1.1
词 |
正面评论 |
负面评论 |
---|---|---|
喜欢 |
60% |
10% |
很好 |
45% |
7% |
好 |
36% |
8% |
差 |
4% |
62% |
很差 |
2% |
23% |
因此,对于将来可能获得的评论,根据其所包含的词的复合概率,可以判定该评论是正面评论还是负面评论,这就是朴素贝叶斯分类器(Naïve Bayes Classifier)。
再列举一个向不同的人推荐杂志的例子。假设已经记录了用户的年龄、性别、位置以及他们阅读的杂志类型,表1.2所列为记录的数据。
表1.2
年龄 |
性别 |
位置 |
杂志 |
---|---|---|---|
21 |
女 |
美国 |
运动 |
15 |
男 |
美国 |
儿童 |
37 |
男 |
印度 |
政治 |
42 |
女 |
英国 |
商业 |
32 |
女 |
美国 |
运动 |
14 |
女 |
印度 |
儿童 |
53 |
男 |
印度 |
政治 |
现在,通过对这些数据进行观察,可以发现小于15岁的人喜欢阅读儿童杂志。根据其制作一个节点和它的决策,如图1.14所示。每个圆节点表示决策节点,图的边缘(箭头)则表示相应的决策,每个矩形节点表示通过图分支之后采取的决策。
图1.14
因此,可以说每个年龄小于或等于15岁的人都有可能阅读儿童杂志。现在再来处理大于15岁的读者的分支。
现在,第二个观察特征是男性,他们喜欢阅读政治杂志。为其创建相同的决策节点和分支,如图1.15所示。
图1.15
接下来,需要看一下年龄在15岁以上的女性的选择。还有一个信息可以利用——地区。因此,可以说,来自美国的女性喜欢阅读体育杂志,而其他地区的女性则喜欢商业杂志。在这里形成的节点如图1.16所示。
图1.16
正确地对每个数据点进行分类,这个过程中形成了决策树。可能存在多种创建决策树的方法。根据现有的数据,这些方法可以做出正确的预测。
图1.17所示的这棵树对数据进行了准确的分类。
图1.17
再来举另一个例子,有几个人每年申请贷款,银行根据他们的收入和贷款额从而决定是否对其提供贷款,目标是向那些在规定时间内偿还债务,且没有任何违约的人提供贷款。
如果一个人月收入是20 000美元,他申请了100 000美元的贷款,银行根据他的收入来源可以批准贷款;如果一个人月收入是3 000美元,他申请了600 000美元的贷款,则银行可能会早早地拒绝他。
因此,银行基于以往违约者的历史创建了一个数据,如图1.18所示。红点表示银行拒绝受理的申请,绿点表示银行批准的申请。横坐标是请求的贷款额度,纵坐标是月收入。
图1.18
现在,一个月收入10 000美元的人想要贷款300 000美元,银行是否会给予批准呢?可以通过一条直线来分割数据,如图1.19所示。
图1.19
根据上面的直线,可以预测银行将同意月薪20 000美元的人申请300 000美元的贷款申请。
现在,已经有一条合适的直线来分割现有的数据点。我们使用的算法(梯度下降法)与在线性回归过程中使用的算法相同。在这里,目标变量是类别而不是在线性回归情况下连续的预测值,这种技术称为逻辑回归(Logistics Regression)。
现在,银行来了一位新的经理,他要检查所有记录,他认为银行批准或拒绝贷款申请的参数是荒谬的,一些像10 000美元或20 000美元的贷款申请并没有风险,银行可以同意这部分贷款申请。所以,他改变了规则和数据,如图1.20所示。
图1.20
显然,仅仅使用一条直线并不能将红点和绿点分开,既然这样,那使用两条直线呢?如图1.21所示。
图1.21
与一条直线相比,使用两条直线可以将红点和绿点分开。这种技术被称为神经网络(Neural Network)。神经网络是基于大脑中的神经元的概念提出的。大脑中的神经元收集信息并将其传递给其他神经元。简单来说,就是基于先前神经元的输入,下一个神经元接收要求并决定输出,它还将信息传递给其他神经元。最后,通过处理不同的神经元,大脑做出决定。
这个概念可以用下面的模型(见图1.22)来理解。在这个模型中,两个神经元通过使用不同的假设建立模型,并且将它们的发现发送给另一个神经元。根据收集到的信息,输出神经元做出决策。
图1.22
在处理数据时,对于一条分割数据点的直线,可能有不同的选择。如图1.23所示的两个例子。
图1.23
与直线1相比,直线2的边距更大,其在分割数据方面似乎更好,如图1.24所示。
图1.24
现在我们要寻找最佳分割路线,而梯度下降并不能解决这个问题,需要线性优化才能实现。这种技术被称为最大间隔分类器或支持向量机(Support Vector Machine, SVM)。
在现实世界中,数据并不能完全分割开。它可能会如图1.25所示的那样。
图1.25
所以,不能通过一条直线就把红点和绿点分开。但是如果通过一个平面来区分红点和绿点,就可以用一个分类器对它们进行分类。创建一个新的维度并用这个平面来分割红点和绿点,如图1.26所示。
图1.26
现在使用一个新的维度将红点和绿点分开。这种技术被称为核函数(Kernel Trick)。
真实的数据非常复杂,而且有很多维度。带有支持向量机分类器的核函数可以用来解决这些复杂的问题。
现在来看另一个问题。如图1.27所示,有一家杂货店A2A。他们提供电话送货服务,每当接到电话时,他们就会将地址发送给派送员,派送员会赶赴送货地点并交付商品。他们设法将办公室设在城市的中心,以便为越来越多的人及时提供服务。
图1.27
这里的点代表A2A的服务需求。现在他们意识到,这种一个配送中心处理所有的需求的做法并不明智。于是,他们决定在城市中开设4个配送中心,依照不同地区来接受需求并提供服务。
因此,为了解决这个问题,首先可以随机地添加4个配送中心并给予其商品派送需求,如图1.28所示。
图1.28
显然,这不是配送中心的最佳分配位置。接下来,将每个点移动到分类点的中心。然后,将所有点分类到最近的配送中心,并将配送中心的位置移动到之前的分类中心。经过多次迭代,将得到图1.29所示的分配位置。
图1.29
因此,每个点的分配都是基于点到中心的距离。这种技术称为K-均值聚类(K-Means Clustering)。
也可以使用另一种方法进行聚类。把所有的点看作是独立的集群,而不是首先将它们分组到集群中再对其进行重新分配。然后,将两个最近的点进行分组,形成一个簇。一直这样操作,直到出现较大的距离或者形成最小数量的簇。这种技术称为层聚类(Hierarchical Clustering)。
在这一章中,列举了许多日常生活中机器学习的例子。本书将详细地介绍这里提到的每一个技术并用Python编写代码实现,同时也将涉及一些先进技术。
为了在真实环境中应用机器学习和数据科学,衍生出了许多可用的工具。将数据应用到这些工具上,便可执行一些基本的工程特征并开始建立预测模型。通过这种方式就可以解决机器学习问题。大多数工具包含所有流行的机器学习技术以解决实际问题。
思考一下汽车维修师的工作过程。首先,需要找到维修师并告知其问题。维修师拿起工具包并开始寻找合适的工具。然后,维修师使用这些工具来完成这项工作。在这项工作中并非所有的工具都是必需的,一些工具可能需要再三思考来决定是否使用。此外,应该遵循一定的顺序来满足要求。例如,维修师不能在未打开引擎前盖的前提下去检修引擎。
现实世界的问题也以类似的方式来解决。首先定义一个问题,然后寻找高层次的解决问题的方案。在确定高层次的方案之后,继而寻找低层次的解决方案,并且按照特定的顺序来使用这些解决方案以便最终解决问题。同样,编程语言也是一种工具,通过使用编程语言可以实现想法或者解决方案。
Python在数据科学家中是非常受欢迎的编程语言。第一,它免费且易学,不必为了小任务而浪费大量注意力;第二,它是面向对象的语言,这也是其被大型开发者社区支持的原因;第三,巨大的开源库支持。 Python具有相当数量的开源库,能够更迅速地实现数据科学和机器学习解决方案,而无须从头开始编写每一个算法。像Pandas、 scikit-learn、 SciPy、 NumPy、 Keras这样的库对于快速实现机器学习算法非常有用。
首先,通过一个例子了解如何使用编程语言来解决现实世界的问题。
假设举办一个聚会。你为每个进入聚会的人分配一个唯一的1~100之间的号码。现在聚会结束了,你宣布了一个消息。
“会从1~200之间获取一个随机数。如果有两个人的号码之和与这个数字相等,就会奖励这两个人。”
现在了解到,已经向x个人分配了号码。如何确定能否给其中两个人奖励呢?
可以尝试使用Python来解决这个问题。首先,从列表中获取输入数据。
numberList = list([43,23,1,67,54,2,34,56,23,65,12,9,87,4,33])
现在选择一个随机数并将其存储在一个变量中。
sumSelector = raw_input()
为了解决这个问题,必须先对数据进行排序。在Python列表中排序很简单,可以通过sort函数来完成。
numberList = numberList.sort()
通过上述语句,将排序后的数字赋值给变量。现在使用两个指针:一个指向开始处(前指针);另一个指向结束处(后指针)。检查两指针所指变量的和。如果和小于给定值(意味着当前的总和小于所需的总和),前指针向前移动一步并再次检查;如果和大于给定值(意味着当前的总和大于所需的总和),将后指针向后移动一步并再次检查。无论在任何位置,如果当前的总和等于要求的总和,便可以说,存在两个人的数值之和与给定的数值相同,需要给予奖励;如果两个指针相交并且仍然没有达到所需的总和,则可以声称并不存在两个人的数值之和与给定的数值相等。
以下是完整的解决方案[1]。
def isPrizeGiven(numberList,sumSelector):
sumOfTwo = sumSelector
i=0
j = len(numberList) -1
if(i>=j):
return False
while(i<=j):
currentSum = numberList[i]+numberList[j]
print(i,j,currentSum)
if(currentSum==sumOfTwo):
return True
if(currentSum>sumOfTwo):
j=j-1
else:
i=i+1
return False
为获得所需答案,必须向函数传递排序列表和给定的数值(即两数值的期望和)。
if (isPrizeGiven(numberList,sumSelector):
print("No Escape. Give Prize to Everyone")
else:
print("You Saved Yourself!!")
即使现在不能理解此代码,也不要担心。接下来我们还会对这段代码进行解析,并介绍程序设计语言的基本原理。
根据操作系统下载相应的Python版本。
当第一次访问这个网站时,可以看到两个不同版本的Python(2.7.x和3.x)。 Python支持多种编程风格,因此用户喜欢用Python编写脚本和面向对象程序。如果你是一个程序员新手,建议使用Python 3.x。虽然这些2.7.x和3.x的版本没有太大的不同,但是由该网站中Python 2.7的注释可知,在发布Python 3之后, Python 2.x就逐渐不再进行重要更新。 Python 3正在不断完善,并添加了更多的库,所以最好从Python 3开始学习。
下载后,运行安装文件。
然后单击“Install Now”进行安装,也可以选择自定义安装 (Customize installation)。如果是第一次使用Python,可以选择默认设置,如图2.1所示。
图2.1
安装过程如图2.2所示。完成后,图2.3展示了成功安装Python的界面。
图2.2
图2.3
可以通过图2.4中所示的命令验证安装是否成功。
图2.4
Anaconda是一个非常有用的包管理器,它包含了所有必要的数据科学库,其优点是不需要单独安装所有的库。 Anaconda附带了一些重要的库,如Pandas、 scikit-learn、 NumPy、 SciPy等,它还附带了Jupyter Notebook和在Python中用于交互开发的Spyder。通过Jupyter Notebook可以编写代码,正确注释并发布。 Anaconda还支持可视化包,在浏览器中可以看到交互式图形。
可以从官网下载Anaconda。安装后,需要运行安装程序,如图2.5所示。图2.6给出了成功安装的界面。
图2.5
图2.6
在Anaconda安装完成之后,需要在PATH中添加Anaconda文件夹和Anaconda/Scripts。
通过图2.7中所示的命令可以运行Jupyter Notebook。
图2.7
运行上述命令后, Jupyter Notebook将在默认浏览器中打开,如图2.8所示。
图2.8
现在开始学习Python。
现在让我们编写Python格式的程序并运行Hello World。 Python程序可以用不同的方式编写。
打开命令提示符,键入Python,然后按回车(Enter),如图2.9所示。
图2.9
现在便可以使用Python了。
输出“Hello World!!”,这可以通过使用Python中的print语句来完成,如图2.10所示。
图2.10
可以使用exit()命令从Python中退出。
Python代码可以在.py的扩展文件中写入。首先创建hello.py文件并添加print(‘Hello World !!’)语句。可以使用python hello.py命令运行此文件,如图2.11所示。
图2.11
这是学习和测试Python代码的有效方法。使用命令提示符打开Jupyter Notebook并创建一个新笔记本,如图2.12所示。
图2.12
编写命令并使用Shift + Enter运行程序。
print(82)
print(56.34)
print('I love Python')
a = 23
b= 'Python is Great!!'
首先在内存中为数字23创建一个空间,并将其存储在该空间中,同时将该空间标记为a。对于变量b也是同样的操作。
如果重新分配变量,将删除先前的值,并在该位置存储新值。
a = 43.34
变量名可以以字母或下划线开头。它必须由字母、数字和下划线组成,而且,变量名区分大小写[2]。 ABC、 Abc和abc是3个不同的变量。
正确的变量名:abhi、 str123、 spammer_23、 skynet。
不正确的变量名:145etl、 #Trad、 re$5%。
a = a + 5
在Python中,可以为多个变量同时赋值。
a = 9
b = 7
a ,b = 9,7
在Python中这两种方式都是相同的。
表2.1
符号 |
操作 |
---|---|
+ |
加号 |
- |
减号 |
* |
乘号 |
/ |
除号 |
** |
求幂 |
% |
求余 |
a = 10
b = 5
print(a+b)
print(a-b)
print(a*b)
print(a/b)
print(a**b)
print(a%b)
表达式按优先级来计算,其顺序如下所示。
(1)括号。
(2)求幂。
(3)求余、乘法、除法。
(4)加法、减法。
(5)从左到右。
要理解优先级规则,请参考下面给出的示例。
c = (5+2) * 4 - 7
print(c)
首先计算括号内的表达式。然后括号内求得的值乘以4,这是因为乘法比减法具有更高的优先权。最后,进行减法运算并将结果存储在c中。
is_male = True
is_senior_person = False
上述两个变量都是布尔变量。第一个变量赋值为True,而另一个则被赋值为False。
在比较运算中布尔值是很重要的。
34>12
上例的结果将输出True。可以将结果放在变量中并在条件语句中使用,具体将在下一节中介绍。
表2.2列出了比较运算符。
表2.2
操作 |
符号 |
---|---|
小于 |
< |
小于等于(不大于) |
<= |
大于 |
> |
大于等于(不小于) |
>= |
等于 |
== |
不等于 |
!= |
valid_string_1 = "I am Valid!!"
valid_string_2 ='I am Valid too !!'
可以使用“+”操作符连接字符串。
first_string = "Abhishek"
second_string = "Vijayvargia"
concat1 = first_string+second_string
concat2 = first_string+" "+second_string
可以通过使用len()函数计算字符串的大小或长度。
len(concat2)
city_description = "Pune is a beautiful city."
print(city_description.title())
输出:Pune Is A Beautiful City.
这里, title可以将句子中每个单词的首字母设置为大写。在文本处理章节中将介绍更多关于这些方法的内容。
type(c)
Python有多种类型,这里输出了其中的一部分。
print(type(12))
print(type(12.45))
print(type('hello')
print(type(True))
第一个是Integer,第二个是Float,第三个是String,第四个是Boolean。
也可以将某些类型转换为另一种类型,这里将整数转换为浮点数或字符串。
a = 56
float(a)
str(a)
也可以将浮点数转换成整数。但是这样转换后,可能会丢失一些信息。
b = 67.45
int(b)
也可以把字符串转换成浮点数或整数。
str1 = '123'
str2 = 'Hello'
float(str1)
float(str2)
第一条语句执行成功,可将123转换为浮点数。第二条语句则会失败,因为字符串“hello”不能转换为浮点数。
在Python中,可以通过input()函数获取用户输入。输入函数返回一个字符串,可以将其转换成其他类型。
name = input('Please enter your name')
结果将显示Please enter your name,然后等待用户输入。这个输入将被分配给变量name。
34/0
Jupyter Notebook将输出此声明。
----------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<iPython-input-2-47ef12753bfd> in <module>()
----> 1 34/0
ZeroDivisionError: division by zero
执行回溯(Traceback)意味着程序因为错误而停止了。每当错误发生时, Python就停止处理序列中的下一条语句并报告错误。
最后一行列出Python究竟发生了什么问题——除以零的操作并没有意义。这一行的上方带有箭头的一行显示了具体是哪一行出现了问题。
假设需要计算员工的工资。工资总额(amount)是工时数(hour)和每小时工资(pay_per_hour)的乘积。
amount = hours*pay_per_hour
假设想将这个公式用到多个文件中的多个位置,则只需复制并粘贴到这些位置即可。
有一天,公司决定修改这个公式,给每人500美元奖金,且不考虑他们的工作时间。
amount = hours*pay_per_hour + 500
这是一个难题。必须找到每一行计算工资的代码并加上500。
现在通过使用函数来实现工资计算的功能。让我们来定义函数。
def calculate_pay(hours,pay_per_hour):
amount = hours*pay_per_hour
return amount
现在想要使用这个函数,只需要使用工时数(hour)和每小时工资(pay_per_hour)来调用这个函数即可。
calculate_pay(20,120)
这样做可以在代码中很容易地发现这个代码块。如果想要修改公式,只需要转到函数定义并进行修改即可,通过这种方式便能影响整个代码。
现在了解一下函数的不同部分。
(1) def用于定义一个函数。这是告诉Python函数声明的方法。
(2) def后是一个函数名。函数名的规则与变量名相同,它必须以字母或下划线开头,只包含字母、数字和下划线。这里calculate_pay是函数名。
(3)在括号内给出了函数的参数并用逗号分隔。这些是传递给函数的值,函数可以使用这些传递的值。在这里传递了hours和pay_per_hour两个参数,函数使用这两个参数来计算工资。不必总是给出参数,如果不想向函数传递任何参数,可以放置空括号()。
def print_welcome_message():
print("Hello Stranger")
(4)函数体包含在函数调用时所需执行的代码中。每行后移4个空格或一个制表符(tab),以声明为函数的一部分。
(5)函数可以通过一个返回(return)语句来返回一个值。在返回语句中使用表达式而不是变量。
可以使用图2.13所示的流程来理解这个概念。
图2.13
第一个变量a被赋值为20。检查条件是否大于15。当条件为Yes时,“Large Number”被输出在屏幕上。
a = 20
if(a>15):
print('Large Number')
else:
print('Small Number')
if语句的语法如下。
if <condition>:
do_something
(1)使用if,可以告知Python这是一个条件语句。
(2) <condition>是条件表达式。如果是真的,则执行if语句下面的代码。
(3) condition之后含有一个冒号。
(4) if语句中的代码在开始时有4个空格或一个制表符。
price = 200
if price<250:
print('Buy !!')
在Python中,可以通过使用else和elif (else if)来创建更多的分支。 else代码紧跟在if代码块之后。如果条件为假,则执行else块。如果未执行上面的条件,则检查代码。
(1) if-else
price = 200
if price<150:
print('Buy !!')
else:
print('Sell !!')
(2) if-elif-else
price = 200
if price<150:
print('Buy !!')
elif price>=150 and price<250:
print('Hold !!')
else:
print('Sell !!')
这里使用“and”来检查这两个条件。如果有一个条件为假,则表达式的结果为假;如果两个条件都为真,表达式的结果为真。
表2.3列出了一些布尔运算。
表2.3
操作 |
结果 |
---|---|
a and b |
如果a和b都为真,则表达式为真;如果a为假,则结果为假(不检查b) |
a or b |
如果a或b中任何一个都为真,则表达式为真;如果a为false,则只检查b |
not a |
如果a为真,则not a为假;如果a为假,则not a为真 |
为每个操作列举一个例子。
a = True
b = False
if a and b:
print('And')
if a or b:
print('Or')
if not b:
print('Not')
本节我们将学习Python中的一些基本数据结构和循环。
列表(List)是Python中的一个数据类型,它使用逗号来分隔数值。值存储在方括号([ ])内。值不必是同一类型的,这意味着一个列表可以包含数字、字符串和其他列表(在一个单独列表中)。
my_first_list = [1,2,4,8,16]
可以使用索引访问列表。位置从0开始,这意味着列表的第一个元素位于索引为0的位置,列表的最后一个元素位于length-1的位置。
print(my_first_list[1])
这里将输出 2。
也可以从末尾对列表进行索引。最后一个元素位于-1位。
print(my_first_list[-1])
将输出16。
与字符串类似,列表也有len() 函数。
列表可以按如下方式切片。
my_first_list[1:4]
输出:[2,4,8]
冒号前的第一个数是开始索引(start_index),第二个数字是结束索引(end_index)。不必同时给出start_index或end_index。
my_first_list[2:]
输出:[4,8,16]
my_first_list[:3]
输出:[4,8,16]
可以通过in操作来检查元素是否存在于列表中。
if 8 in my_first_list:
print("Eight is Found.")
max(list):此函数用于报告列表中的最大元素。
max(my_first_list)
min(list):此函数用于报告列表中的最小元素。
min(my_first_list)
sorted(list):此函数返回列表的排序副本。
sorted(my_first_list)
通过将反向参数(reverse)设置为真来对列表进行反向排序。
sorted(my_first_list,reverse=True)
list.append(element):在列尾处插入元素。
my_first_list.append(32)
print(my_first_list)
list.insert(index,element):在列表指定的索引处插入元素。
my_first_list.insert(2,74)
print(my_first_list)
list.clear():删除列表中所有元素。
my_first_list.clear()
list.count(element):元素在列表中出现的次数。
my_first_list.count(16)
list.remove(element):从列表中移除元素。如果列表中并不存在该元素,则抛出错误。
my_first_list.remove(74)
list.reverse():反转列表元素。
my_first_list.reverse()
list.pop():从列表中移除最后一个元素。
my_first_list.pop()
循环对于重复执行相同的任务是很重要的。考虑一个打印数字1~100的例子,你显然不想写100次print()语句。在Python中更简洁的方法是使用循环。
for i in range(1,100):
print(i)
(1) for是Python中的一个关键字,用于声明for循环。
(2) for之后是迭代的条件。 i在1~100的范围内迭代。
(3)在迭代表达式之后是一个冒号。
(4)循环的主体,由4个空格或制表符缩进。
在列表上的for循环如下。
for element in my_first_list:
print(element+10)
while循环的运行次数不确定。当条件为假时,停止执行。
i = 1
while i<5:
print(i*i)
i = i+1
(1)关键字while用来声明while循环。
(2)控制循环的条件(i<5)。如果条件为真,则循环保持运行。
(3)冒号位于条件之后。
(4)循环的主体,由4个空格或一个制表符缩进。
使用while循环。如下面的这个例子。
i = 1
while i<5:
print(i*i)
这是持续输出1的无限循环。因为这段代码仅检查条件,却并没有更新i值。
break是用来跳出循环的。通常,它与一些条件语句一起使用。
i = 1
while i<5:
print(i*i)
i = i+1
if(i==3):
break
结果将输出数字1和4。每当i的值为3时,将会执行中断语句并跳出循环。
Continue用于跳过循环的其余部分,并在下一次迭代中将控件传递到循环的顶部。
i = 0
while i<5:
i = i+1
if(i==3):
continue
print(i*i)
结果将输出1、 4、 16和25。当i的值为3时,它将跳过剩余的代码并将控制转移到下一个迭代中。
Python中的集合是无序的唯一元素的集合。可以将其看作是未定义元素顺序的唯一元素列表。
my_duplicate_list = [1,2,3,1,2,3,5,6,3,2]
print(my_duplicate_list)
my_unique_list = set(my_duplicate_list)
print(my_unique_list)
字典(Dictionary)是Python中的关键值对。键值被存储在索引中。
dictionary = {'A':1,'B':2,'C':3}
可以使用键来访问字典元素。
dictionary['A']
还可以通过使用键来改变字典元素的值。
dictionary['A'] = 5
通过使用键来迭代字典。
for key in dictionary:
print(key,dictionary[key])
创建字典嵌套字典。
complex_dict = {'first_dict': {'A':1,'B':2,'C':3},
'second_dict': {'E':1,'F':2,'G':3}}
元组(Tuple)是有序的元素集合。元组和列表的不同之处在于元组是不可变的,但列表不是。一旦赋值,就不能插入或删除元组中的值。
dim = (45.23,67.34)
当数值需要一起使用的时候,可以使用元组。
Python模块(module)中包含了各种库文件。在使用任何模块之前,都需要将其导入。
import time
time.time()
这里,使用时间模块并获取当前时间戳。
一个模块包含多个函数。可以导入特定的函数,而不是导入该模块中的所有函数。
from math import factorial
这里,程序不是导入数学模块(math),而是只导入阶乘函数(factorial)。
可以通过“as”关键字来使用其他名称表示模块。
Import <module_name> as <new_name>
import numpy as np
可以在命令提示符中使用pip命令安装Python中的其他第三方库。
pip install <module_name>
pip install scikit-learn
文件用于在磁盘上存储数据,它可以是任何形式,如文本、 CSV、图像、音频、视频等。
可以使用read()函数来读取文件。首先,需要在读取模式下打开文件。
f = open('my_file.txt','r')
file_content = f.read()
读取文件后,可以通过close()函数关闭。
f.close()
使用write()函数来完成文件编写。在写入的模式下打开文件,然后执行写入操作并关闭文件。
f = open('my_file_w.txt','w')
f.write('Writing in File')
f.close()
使用with关键字可以读取或写入文件
with open('my_file_w.txt','r') as f:
print(f.read())
当执行完操作后,文件自动关闭。
[1] def isPrizeGiven () 原文中代码有误,故对其做了修改。新加一条赋值命令:sumOfTwo = sumSelector。——译者注
[2] 此处原文有错误,原文中指出Python中变量名不区分大小写(variable names are not case sensitive.),而实际上Python是区分大小写的。其实文中后面给出的例子也说明了Python中变量名是区分大小写的。——译者注