机器学习Web应用

978-7-115-45852-0
作者: 【意】Andrea Isoni(爱索尼克)
译者: 杜春晓
编辑: 陈冀康

图书目录:

详情

这是一本结合Python语言讲述Web下机器学习的图书,本书内容全面,既能够让读者熟悉最基本的机器学习的相关概念,也能够了解Web下数据挖掘的工具和技术,除此之外,书中还会介绍与Django框架有关的知识以及数据库管理等内容,帮助读者掌握聚类和分类技术并用Python实现它们。

图书摘要

版权信息

书名:机器学习Web应用

ISBN:978-7-115-45852-0

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

• 著    [意] Andrea Isoni

  译    杜春晓

  责任编辑 陈冀康

• 人民邮电出版社出版发行  北京市丰台区成寿寺路11号

  邮编 100164  电子邮件 315@ptpress.com.cn

  网址 http://www.ptpress.com.cn

• 读者服务热线:(010)81055410

  反盗版热线:(010)81055315


Copyright ©2016 Packt Publishing. First published in the English language under the title

Machine Learning for the Web.

All rights reserved.

本书由英国Packt Publishing公司授权人民邮电出版社出版。未经出版者书面许可,对本书的任何部分不得以任何方式或任何手段复制和传播。

版权所有,侵权必究。


机器学习可用来处理由用户产生的、数量不断增长的Web数据。

本书讲解如何用Python语言、Django框架开发一款Web商业应用,以及如何用一些现成的库和工具(sklearn、scipy、nltk和Django等)处理和分析应用所生成或使用的数据。本书不仅涉及机器学习的核心概念,还介绍了如何将数据部署到用Django框架开发的Web应用,包括Web、文档和服务器端数据的挖掘和推荐引擎的搭建方法。

本书适合有志于成为或刚刚成为数据科学家的读者学习,也适合对机器学习、Web数据挖掘等技术实践感兴趣的读者参考阅读。


机器学习是什么?2016年,无论参加大会、研讨会,还是接受采访,很多人都让我给机器学习下个定义。人们对机器学习是什么,抱有诸多疑问。理解这一新鲜事物可能为生活带来的潜在影响以及它日后对我们有何种意义之前,天性要求我们先给出其定义。

跟其他陡升为显学的学科类似,机器学习并不是新生事物。科学社区多年来一直致力于研制算法,实现重复性工作的自动化。参数固定的算法叫作静态算法,其输出是可预测的,输出只是输入变量的函数。还有一种情况,算法的参数是动态变化的,算法的输出是外部因素(最常见的是同一算法先前的输出)的函数,这种算法叫作动态算法,其输出不仅仅是输入变量的函数。动态算法是机器学习的支柱:从先前迭代生成的数据中,学习到一组规则,以改善之后的输出。

科学家、开发人员和工程师研究和使用模糊逻辑、神经网络和其他类型的机器学习技术已有多个年头,但直到今天,随着机器学习应用离开实验室,进入市场营销、销售和金融行业,这门学科才流行起来,基本上来讲,需要重复执行相同运算的活动都可以受益于机器学习。

机器学习的影响很容易理解,它将给我们的社会带来巨大冲击。关于下一个5到10年,机器学习将给我们带来什么,我能想到的最佳描述方式是:不妨回想工业革命时期发生了什么。蒸汽机发明之前,很多人从事高度重复性的体力工作。为了赚取少得不能再少的工资,他们往往要冒着生命危险或以牺牲健康为代价。工业革命出现后,社会得以发展,机器接管了生产过程的重要步骤,这带来了产量的增加,并且产出的可预测性更强和更稳定。与之相应的是,产品质量的提升和新工种的出现,操控机器这类新兴的工作取代了体力劳动。我们将造物的责任委托给由我们设计和发明的工具,这在人类历史上可是第一次。机器学习将以相同的方式,改变执行数据运算的方式,减少人工干预的需要,将优化的工作交给机器和算法。数据处理人员将不再直接控制数据,而是通过控制算法间接控制数据。因此,运算的执行速度将会变得更快,更少的人将能控制规模更大的数据集,错误将会减少,从而结果的稳定性更高和可预测性更强。跟其他对我们生活产生重大影响的事物一样,爱慕和憎恶它的人都有。爱慕者称赞机器学习为他们生活带来便利;憎恶者批评,机器学习方法要有效,需要大量的迭代,因此需要大量数据。而通常来讲,我们“喂给”算法的数据可是我们的个人信息[1]

事实上,机器学习作为一种工具得以迅速发展,其主要应用在于提升市场营销和顾客支持的效率。为顾客提供个性化服务,促使他们购买而不只是浏览,或让他们高兴而不是失望,需要对顾客有着深入的理解。

例如,就市场营销而言,如今市场营销人员开始考虑位置、设备、购买历史、访问过的网站、天气状况等信息(仅举几个例子)来决定公司是否向一组特定顾客展示广告。

通过电视或报纸这样无法追踪的媒体传播营销信息的日子已然成为遥远的过去。如今,市场营销人员希望知道谁点击和购买了他们商品等一切信息,他们好优化创意和投入,合理分配预算,以充分利用他们手中的资源。这就要求提供前所未有的高度个性化服务,若使用合理,可以让顾客感到他们是受尊重的个体而不只是某一社会人口学分组的一部分。

机器学习既吸引人又充满挑战,但无疑下一个十年的赢家,将会是那些能够理解非结构化数据,并且能够基于这些数据以可扩展的方式做出决策的公司或个人:除了机器学习,我还没有看到哪种方式能实现这样的伟业。

Andrea Isoni的这本书朝这个世界迈出了一步;读它就好像是向下窥视兔子的洞穴,你能从中看到用机器学习技术实现的几个应用,作者将机器学习技术整合到Web应用中。访问用机器学习技术创建的个性化服务网站,顾客能从中体验到为他们个人提供的优化过的服务。

如果你想为日后的职业生涯提前做好准备,该书是你必须要读的;下一个十年跟数据打交道的任何人若想成功的话,都需要熟练掌握这些技术。

Davide Cervellin,@ingdave

eBay公司EU Analytics部门负责人

[1] 言外之意,隐私受到威胁。——译者注


20年前,IBM研制的深蓝计算机勉强战胜俄罗斯棋王卡斯帕罗夫,它在体力上的优势似乎比智力方面更明显。但刚刚过去的这一年,谷歌的AlphaGo计算机程序打败了围棋高手李世石,它的升级版Master威力更是了得,横扫中日韩高手,它擅长走快棋,招法狠毒,令人类高手胆颤。由此可见,近年来,人工智能技术随着硬件、大数据、机器学习技术的发展,取得了长足的进步。

机器学习技术作为人工智能的一个子领域,研究和应用热潮不减,研讨会、学习班和创业项目层出不穷;国内学者入选AAAI Fellow;该领域的书籍一印再印;人脸识别、自动驾驶、机器翻译、智能客服、物流无人机和家居、医疗、教育机器人等各种应用不断推向市场。从以上种种表现来看,我们处在人工智能时代的风口和前夕。作为该领域的从业者,我们不能满足于看热闹,应努力掌握背后的核心技术——机器学习,力求弄懂该技术,并努力探索其他可能的实现人工智能的方法,把人类智慧的边界向前推进一步。更令人鼓舞的是,大数据产业发展已上升到国家战略层面,我国要实现从数据大国向数据强国的转变,需要一批掌握了数据挖掘、机器学习等相关技术的人才。

本书讲解的是商业网站数据分析和挖掘所用到的机器学习理论和技术。作者先介绍了机器学习的基本概念、Python机器学习工具栈(NumPy、pandas和matplotlib等),接着分别讲解了无监督和有监督机器学习理论,每种方法都给出形式化描述,其间用到了大量概率统计、线性代数等数学知识,比如最小二乘、相关性、贝叶斯概率和奇异值分解等。作者的统计学背景在这一点上得到了很好的体现。这部分数学知识能够较好地满足有志于深入学习的读者的需要,水平高的读者可以从中感受机器学习模型的数学魅力。介绍完这两大类机器学习理论,作者又从Web结构和内容两个方面讲解了Web挖掘技术;介绍了信息检索模型、主题抽取模型LDA。讲解完机器学习理论和技术之后,作者引入了为Web开发完美主义者准备的Django框架,让昔日在幕后默默奉献的数据分析高手有机会走到台前,用自己研制的算法驱动一款Web产品。作者带领我们利用前面讲解的算法和挖掘技术,用Django框架搭建推荐系统和影评分析系统。学到这里,你会不由地感叹Python真是全栈工程师的好朋友。数据分析师用Python就能从头到尾打造一款智能Web产品,可见Python的应用范围之广。年初,Facebook更是开源了PyTorch深度学习框架,进一步巩固了Python在机器学习领域的地位。

当然,我们最终开发出的产品还比较初级,离最终面向用户的产品在用户体验上还有较大差距,但稍加打磨至少可作为一个最小可行性产品(MVP)先行投入市场,收集用户反馈,日后再图大的改进。此外,限于篇幅,作者也没有讲怎么将系统部署到生产服务器。感兴趣的读者可以试试Heroku、SAE等云应用平台,也可以尝试用Apache、mod_wsgi在自己的计算机上搭服务器。你可能还需要申请一个域名。这样,你就可以向朋友推荐自己开发的产品了,你具备了向全球用户提供智能Web产品的能力!嘿,伙计,快来看,这是我刚刚上线的Web推荐系统!用机器学习算法驱动的哦!

感谢人民邮电出版社的陈冀康编辑等为本书编校、出版辛勤付出的各位朋友。读者罗导运行了第1章的代码,并指出了原书及译者注中的几处问题。师妹瞿乔阅读了第2章译文,她本人也是一本Python图书的译者。泰安读者陈新光阅读了第6章译文,他正努力学习数据科学知识,祝他学有所成。翻译过程中,我向北京大学冷含莹、东京大学范超、上海健康学院姜萌等朋友请教过问题;我旁听了北大的统计学基础、随机过程等课程,了解了很多统计学概念,参考了市面上现有的多本著作,其中包括大名鼎鼎的西瓜书,查询了CSDN等网站的文章,在此一并表示衷心的感谢。感谢西安工业大学的李刚老师、重庆大学杨刘洋同学等读者对翻译工作的支持。最后,感谢我的家人,我翻译图书的时间是用他们的辛勤劳动换来的,因此也更加宝贵。

本人学识有限,且时间仓促,书中翻译错误、不当和疏漏之处在所难免,敬请读者批评指正。

杜春晓 

2017年2月


数据科学,尤其是机器学习,成为当下科技商业领域人们热议的议题。这类技术可用来处理用户产生的、数量在不断增长的数据。本书将讲解如何用Python语言、Django框架开发一款Web商业应用,还将讲解如何用一些现成的库(sklearn、scipy、NLTK和Django等)处理和分析(通过机器学习技术)应用生成或使用的数据。

第1章,Python机器学习实践入门,讨论机器学习的主要概念以及数据科学专业人士用Python处理数据所使用的几个库。

第2章,无监督机器学习,讲解为数据集分簇和从数据中抽取主要特征所用到的算法。

第3章,有监督机器学习,讲解预测数据集标签最常用的有监督机器学习算法。

第4章,Web挖掘技术,讨论Web数据的组织、分析和从中提取信息的主要技术。

第5章,推荐系统,详细介绍当今商业领域所使用的几种最流行的推荐系统。

第6章,开始Django之旅,介绍开发Web应用所用到的Django的主要功能和特点。

第7章,电影推荐系统Web应用,将介绍的机器学习概念付诸实践,动手实现为Web用户推荐电影的应用。

第8章,影评情感分析应用,再次通过一个实例,使用讲述的知识,分析在线影评的情感倾向和相关性。

读者应该准备一台计算机,装好Python 2.7,能够运行(和修改)书中各章讲解的代码。

任何有一定编程经验(Python)和统计学背景,对机器学习感兴趣和/或希望从事数据科学职业的读者均可从本书受益。

本书使用不同的文本样式来区分不同类别的内容。以下是常用样式及其用途说明。

正文中的代码、数据库表名、文件夹名、文件名、文件扩展名、路径名、URL地址、用户输入的内容和Twitter用户名显示方式如下:

“在终端输入以下命令,安装Django这个库:sudo pip install django。”

代码块样式如下:

INSTALLED_APPS = (
…
'rest_framework',
'rest_framework_swagger',
'nameapp',
)

所有的命令行输入或输出使用下面这种样式:

python manage.py migrate

新的术语重要的词语使用黑体。出现在屏幕上的词语,例如菜单或对话框里,样式如下“如你所见,页面上有两个输入框,输入姓名和邮箱后,单击‘添加’,将其添加到数据库”。

 

此图标表示警告或重要信息。

 

此图标表示提示或技巧。

我们热忱地欢迎读者朋友给予我们反馈,告诉我们你对于这本书的所思所想——你喜欢或是不喜欢哪些内容。大家的反馈对我们来说至关重要,将帮助我们确定到底哪些内容是读者真正需要的。

如果你有一般性建议的话,请发邮件至feedback@packtpub.com ,请在邮件主题中写清书的名称。

如果你是某一方面的专家,对某个主题特别感兴趣,有意向自己或是与别人合作写一本书,请到www.packtpub.com/authors 查阅我们为作者准备的帮助文档。

为自己拥有一本Packt出版的书而自豪吧!为了让你的书物有所值,我们还为你准备了以下内容。

如果你是从www.packtpub.com 网站购买的图书,用自己的账号登录后,可以下载所有已购图书的示例代码。如果你是从其他地方购买的,请访问http://www.packtpub.com/support网站并注册,我们会用邮件把代码文件直接发给你。也可以访问www.epubit.com.cn来下载示例代码。

代码文件下载步骤如下。

1.用邮箱和密码登录或注册我们的网站。

2.鼠标移动到页面顶部的SUPPORT选项卡下。

3.单击Code Downloads & Errata

4.在搜索框Search中输入书名。

5.选择你要下载代码文件的图书。

6.从下拉菜单中选择你从何处购买该书。

7.单击Code Download下载代码文件

你还可以在Packt Publishing网站图书详情页,单击Code Files按钮下载代码文件。在Search搜索框中输入书名进行搜索可找到该书的图书详情页。请注意你需要登录网站。

代码下载下来之后,请确保用以下解压工具的最新版本进行解压或抽取文件:

本书的代码包在GitHub上也存储了一份:https://github.com/PacktPublishing/Machine- Learning-for-the-Web。我们很多其他图书和视频的代码包也存储到了GitHub上:https://github.com/PacktPublishing/ 。将它们检出到本地。

我们还为你准备了一个PDF文件,该文件包含书中的所有屏幕截图/图表。这些彩图能弥补书中黑白图像的不足,有助于你理解本书内容。该文件的下载地址为http://www.packtpub.com/ sites/default/files/downloads/MachineLearningfortheWeb_ColorImages.pdf

即使我们竭尽所能来保证图书内容的正确性,错误也在所难免。如果你在我们出版的任何一本书中发现错误——可能是在文本或代码中——倘若你能告诉我们,我们将会非常感激。你的善举足以减少其他读者在阅读出错位置时的纠结和不快,帮助我们在后续版本中更正错误。如果你发现任何错误,请访问 http://www.packtpub.com/submit-errata ,选择相应书籍,单击“Errata Submission Form”链接,输入错误之处的具体信息。你提交的错误得到验证后,我们就会接受你的建议,该处错误信息将会上传到我们网站或是添加到已有勘误表的相应位置。

访问https://www.packtpub.com/books/content/support ,在搜索框中输入书名,可查看该书已有的勘误信息。这部分信息会在Errata部分显示。

所有媒体在互联网上都面临的一个问题就是侵权。对Packt来说,我们严格保护我们的版权和许可。如果你在网上发现针对我们出版物的任何形式的盗版产品,请立即告知我们地址或网站名称,以便我们进行补救。

请将盗版书籍的网址发送到copyright@packtpub.com。

如果你能这么做,就是在保护我们的作者,保护我们,只有这样,我们才能继续以优质内容回馈像你这样热心的读者。

你对本书有任何方面的问题,都可以通过questions@packtpub.com 邮箱联系我们,我们也将尽最大努力来帮你答疑解惑。


Andrea Isoni博士是一名数据科学家、物理学家,他在软件开发领域有着丰富的经验,在机器学习算法和技术方面,拥有广博的知识。此外,他还有多种语言的使用经验,如Python、C/C++、Java、JavaScript、C#、SQL、HTML。他还用过Hadoop框架。


杜春晓,英语语言文学学士,软件工程硕士。其他译著有《Python数据挖掘入门与实践》《Python数据分析实战》和《电子达人——我的第一本Raspberry Pi入门手册》等。新浪微博:@宜_生。


Chetan Khatri是一名数据科学研究员,他共有4年半的研究和开发经验。他在Nazara Technologies Pvt. Ltd公司担任数据和机器学习方面的首席工程师,主导在游戏和电信订阅业务从事数据科学实践。他曾在一家顶尖的数据公司和印度四大公司其中一家工作,管理数据科学实践平台和后者的资源团队。在这之前,他曾供职于R & D Lab和Eccella Corporation。他拥有印度喀奇大学(KSKV Kachchh University)的计算机科学硕士学位,辅修数据科学,是该学校的金牌得主。

他积极以多种方式为社会做贡献,其中包括为大二学生做讲座,在学术以及其他各种会议上介绍数据科学相关知识,还援助社区一个数据平台。他在学术研究和行业最佳实践两方面均有着相关的专业知识。他喜欢参加数据科学马拉松比赛。他参与发起了Python社区——PyKutch。他目前正在探究深层神经网络和增强学习,学习使用并行和分布式计算管理数据。

感谢喀奇大学计算机科学系主任Devji Chhanga教授,感谢他引领我走上数据科学研究的正确道路,并给予宝贵的指导意见。同样把感谢送给我亲爱的家人。

Pavan Kumar Kolluru是一名交叉学科工程师,他是大数据、数字图像和处理、遥感(高光谱数据和图像)方面的专家,精通Python、R和MATLAB编程。他的研究重点在于如何用机器学习技术、编制算法处理大数据。

他目前正在探索如何找到不同学科之间的联系,以降低数据处理过程在计算和自动化方面的难度。

作为一名数据(图像和信号)处理方面的专业人士和老师,他一直在处理多/高光谱数据,该项工作使得他在数据处理、信息抽取和分割方面积累了很多专业知识。他用到的高级处理技术有OOA、随机集和马尔可夫随机场。

作为一名程序员和教师,他专注于Python和R语言,他执教于企业和教育行业的兄弟会。他培训过多批学员,教他们使用Python和各种包(信号、图像和数据分析等)。

作为一名机器学习研究员/教练,他是分类(有监督和无监督)、建模和数据理解、回归以及数据降维方面的专家。他曾开发出一套大数据(图像或信号)方面的新型机器学习算法,作为他理科硕士阶段的研究成果,该算法将数据降维和分类纳入同一框架,这为他赢得了很高的分数。他培训过多家大型公司的员工,教他们用Hadoop和MapReduce分析大数据。他的大数据分析专业知识包括HDFS、Pig、Hive和Spark。

Dipanjan Sarkar是Intel公司的一名数据科学家。Intel是世界上最大的半导体公司,它的使命是让世界更加连通和更具效率。他主要从事分析、商业智能、应用开发和构建大规模的智能系统方面的工作。他从班加罗尔的印度信息技术学院(IIIT)获得信息技术硕士学位。他的专业领域包括软件工程、数据科学、机器学习和文本分析。

Dipanjan的兴趣包括学习新技术、数据科学和最近的深度学习以及了解具有颠覆性的初创企业动态。业余时间,他喜欢阅读、写作、玩游戏和看情景喜剧。他写过一本关于机器学习的书R Machine Learning by Example,该书由Packt Publishing出版。他还为Packt Publishing出版的几本机器学习和数据科学图书做过技术评审。


在技术行业,分析和挖掘商业数据的技能正变得越来越重要。公司若有线上业务,可开发利用线上产生的数据,以改进自身业务,或将数据出售给其他公司。重组或分析这些可能具有商业价值的海量信息,只有掌握专业知识的数据科学(或数据挖掘)专业人士才能做得到。数据科学采用机器学习技术将数据转化为模型,以便预测业务领域高度重视的特定实体的行为。这些算法和技术在当今以技术为主导的业务领域是必不可少的。本书讲解这些算法和技术,并介绍如何将其部署到真实的商业环境。你将学到最常用的机器学习技术,并有机会在一系列旨在提高商业智能的练习和应用中使用它们。从本书学到的技能,可用于实际工作。为了充分掌握书中所讨论的各个主题,我们希望你已熟悉Python编程语言、线性代数和统计方法。

本章作为入门章节,目的是让你熟悉Python机器学习的专业人士所使用的更为高级的库和工具,比如NumPy、pandas和matplotlib,帮你掌握必要技术知识,以便实现后续章节的各种技术。讲解本书所用库之前,我们先来阐明机器学习领域的主要概念,并通过一个实例,展示在真实场景中机器学习算法如何给出有用的预测信息。

本书讨论最常用的机器学习算法,并在练习中加以运用,从而使你熟悉它们。为了解释这些算法,帮你理解本书内容,我们先大体看下几个常用概念,后面会详细介绍。

首先,若要为机器学习下定义,一个较为贴切的定义是,机器学习是计算机科学的一个分支,从模式识别、人工智能和计算学习理论发展而来。我们也可以将机器学习看作是数据挖掘工具,侧重于用数据分析方法理解给定的数据。该学科的目的是,开发能够从先前观测的数据,通过可调整的参数(通常为由双精度数值组成的数组)进行学习的程序,为了改善预测结果,将参数设计为可自动调整的。计算机用这种方式可预测某种行为,概括(generalize)数据的内在结构,而不只是像常见的数据库系统那样对数值进行排序(或检索)。因此,机器学习跟计算统计[1]相关,也是尝试根据先前数据预测某种行为。机器学习方法常见的行业应用有垃圾邮件过滤器、搜索引擎、光学字符识别(OCR)和计算机视觉。既已给出该学科的定义,我们接下来更详细地介绍每种机器学习问题所用术语。

任何学习问题都始于一个包含n个样本个体的数据集,未知数据的特性(properties)根据数据集来预测。每个个体通常包含一个以上的数值,因此它是一个向量。向量的组成元素[2]叫作特征(feature)。例如,根据二手车的制造时间、颜色和能耗等车况信息预测其价格。二手车数据集中,每辆车i表示成一个特征向量x(i),对应i这辆车的颜色、能耗等车况信息。每辆车i还有一个与之对应的目标(或标签)变量y(i),即二手车的价格。一个训练样例(training example)由一对(x(i), y(i))组成。由N个数据点组成、用于学习的整个集合叫作训练集{(x(i), y(i)); i=1,,N}。符号x表示特征(输入)值空间,y为目标(输出)值空间。为解决问题选用的机器学习算法用数学模型来描述,模型包含一些参数,需在训练集上调试。训练完成后,模型的预测性能用另外两个数据集来评估:验证集和训练集。验证集用来从多个模型中选择能给出最佳结果的那个,测试集通常用来决定所选用模型的实际准确率(precision)[3]。通常,数据集的50%划作训练集,验证集和测试集则各使用25%的数据。

学习问题可分为两大类(本书对这两类均有大量介绍)。

无监督学习:给定的训练集只有作为输入的特征向量x,而未给出任何相对应的标签。该类学习的目标通常为,用聚类算法找出数据中的相似样例,或将数据从高维空间映射(project)到维数更少的空间(盲信号分离算法,比如主成分分析PCA)。因为每个训练样例通常没有目标值,所以无法直接用训练数据评估模型的错误率;这就需要使用其他方法,评估簇内元素的相似度以及簇间元素的差异程度。这是无监督和有监督学习的一个主要不同点。

本书第2章集中介绍无监督学习方法,第3章讨论最常用的有监督学习算法。第4章着手讲解Web挖掘技术,也可将其看作有监督和无监督方法。第5章讲解推荐系统,属于有监督学习范畴。第6章介绍Django Web框架。第7章详细介绍推荐系统(用到Django框架和第5章相关知识)的实现。我们以一个Django Web挖掘应用实例结束本书,实现该应用需使用从第4章学到的一些技术。学完本书,你应该能够理解不同的机器学习方法,并有能力将其部署到用Django实现的真实Web应用。

本章接下来我们将给出一个实例,展示机器学习如何用于实际业务问题,还将给出Python库(NumPy、pandas和matplotlib)教程。只有掌握这些库的用法,才能实现从后续章节学到的各种算法。

为了进一步解释机器学习可以拿真实数据做什么,我们一起来看下面这个例子(下面的代码可从作者的GitHub主页该书的文件夹下找到,地址为https://github.com/ai2010/machine_ learning_for_the_web/tree/master/chapter_1/。我们从UCI机器学习数据库(http://archive.ics.uci.edu/)下载因特网广告数据集(Internet Advertisements Data Sethttp://archive.ics.uci.edu/ml/ datasets/Internet+Advertisements)。这些Web广告从各种各样的网页收集而来,每个网页被转换为一个特征向量,其元素为数值类型。从ad.names文件,我们可以看到前三个特征表示网页中广告图像的尺寸,其他特征表示图像的URL或在文本中出现了哪些特定词语(共有1558个特征)。根据网页中是否有广告,标签的取值为ad或nonad。举个例子,一个网页在ad.data文件中是这么表示的:

125, 125, …, 1. 0, 1, 0, ad.

根据这些数据,一个经典的机器学习任务是找到一个模型,预测哪些网页是广告,哪些不是广告(分类)。首先来看一下包含全部特征向量和标签的ad.data文件,我们发现它包含一些用?表示的缺失值。我们可以用Python的pandas库将?转换为-1(pandas库详细教程见下节):

import pandas as pd
df = pd.read_csv('ad-dataset/ad.data',header=None)
df=df.replace({'?': np.nan})
df=df.replace({' ?': np.nan})
df=df.replace({' ?': np.nan})
df=df.replace({' ?': np.nan})
df=df.replace({' ?': np.nan})
df=df.fillna(-1)

读入ad.data文件中的数据,创建一个DataFrame对象,先把每个?替换为一个特殊的元素(replace函数),然后再替换为-1(fillna函数)。这样每个标签都被转换为数值类型元素(数据中的其他元素亦同):

adindices = df[df.columns[-1]]== 'ad.'
df.loc[adindices,df.columns[-1]]=1
nonadindices = df[df.columns[-1]]=='nonad.'
df.loc[nonadindices,df.columns[-1]]=0
df[df.columns[-1]]=df[df.columns[-1]].astype(float)
df.apply(lambda x: pd.to_numeric(x))

每个ad.标签转换为1,而nonad.则被替换为0。所有的列(特征)需要是浮点型的数值(用astype函数将标签转换为浮点型,在lambda函数中用to_numeric函数将df转换为数值型)。

我们使用scikit-learn库(见第3章)提供的支持向量机(Support Vector Machine,SVM)算法预测数据集中20%数据的标签。首先,将数据分为两部分:训练集(80%)和测试集(20%):

import numpy as np
dataset = df.values[:,:]
np.random.shuffle(dataset)
data = dataset[:,:-1]
labels = dataset[:,-1].astype(float)
ntrainrows = int(len(data)*.8)
train = data[:ntrainrows,:]
trainlabels = labels[:ntrainrows]
test = data[ntrainrows:,:]
testlabels = labels[ntrainrows:]

上述代码,执行切分数据集之前,用NumPy库(教程见下一节)封装的函数,打乱数据的顺序(random.shuffle函数),确保两个数据集的各行数据是随机选取的。切片操作中的-1,表示数组的最后一列不予考虑。

现在,我们用训练数据训练SVM模型:

from sklearn.svm import SVC
clf = SVC(gamma=0.001, C=100.)
clf.fit(train, trainlabels)

我们声明一个SVM模型,指定参数,并将模型赋给clf变量。调用fit函数,用训练数据拟合(fit)模型(更多内容见第3章)。预测20%测试数据的平均正确率(mean accuracy),用score函数来计算,代码如下:

score=clf.score(test,testlabels)
print 'score:',score

运行上述代码(完整代码见作者GitHub主页chapter_1文件夹),得到92%的正确率,也就是说测试集标签的预测结果中,92%的预测标签跟实际标签相同。这就是机器学习的威力所在:根据以往数据,我们能够推断一个网页是否包含广告。为了实现预测功能,我们做了必要的准备工作,用NumPy和pandas库准备和预处理数据,然后用scikit-learn库封装的SVM算法处理清洗过的数据。鉴于本书将大量使用NumPy和pandas(有时也会用到matplotlib)库,下面几节将分别介绍这些库的安装方法以及用它们处理(甚至创建)数据的方法。

安装和导入模块(库)

继续讨论这些库之前,我们先讲怎样在Python中安装模块。常用的模块安装方法是,在终端使用pip命令:

>>> sudo pip install modulename[5]

然后,通常使用下述语句导入模块:

import numpy as np

其中,numpy是包名,np指代numpy,做了这一步操作之后,该库中的所有函数X都可以用np.X而不必用numpy.X访问。本书从此往后假定所有的库(scipy、scikit-learn、pandas、scrapy和nltk等)都用上面这种方式来安装和导入。

大多数数据在我们拿到时,其形式很不实用,无法直接用机器学习算法处理。如上一个例子所见(上一节),数据中有些元素可能缺失,或某些列不是数值型,因此无法直接用机器学习技术处理。因而,机器学习专家通常花费大量时间清洗和准备数据,转换数据的形式,以便进一步分析或做可视化处理。本节教你用NumPy和pandas库,用Python语言创建、准备和处理数据。matplotlib小节,将介绍Python绘图基础知识。NumPy教程结合Python shell进行讲解,但是代码的IPython notebook版和纯Python脚本版,都已放到作者GitHub主页chapter_1文件夹。pandas和matplotlib两个库的讲解则用IPython notebook。

Numerical Python或NumPy是Python的一个开源扩展包,是数据分析和高性能科学计算的基础模块。该库问世后,用Python处理大规模、多维数组和矩阵不再是梦想。对于常用数值计算,它提供预先编译好的函数。更进一步来讲,它提供一个巨大的数学函数库来支持数组运算。

该库提供以下功能:

比起Python的标准运算,NumPy的主要优势在于数组运算速度快。例如,用传统的求和方法,求10000000个元素的和:

>>> def sum_trad():
>>> start = time.time()
>>> X = range(10000000)
>>> Y = range(10000000)
>>> Z = []
>>> for i in range(len(X)):
>>> Z.append(X[i] + Y[i])
>>> return time.time() - start

与NumPy函数对比:

>>> def sum_numpy():
>>> start = time.time()
>>> X = np.arange(10000000)
>>> Y = np.arange(10000000)
>>> Z=X+Y
>>> return time.time() - start
>>> print 'time sum:',sum_trad(),' time sum numpy:',sum_numpy()
time sum: 2.1142539978 time sum numpy: 0.0807049274445

两种方法所用时间分别为2.1142539978和0.0807049274445。

1.数组创建

数组对象是NumPy库提供的主要功能。数组相当于Python的列表(list),但数组所有元素的数值类型相同(通常为浮点型或整型)。借助array函数,可用列表定义一个数组对象,需为array函数传入两个参数:即将被转换为数组的列表、新生成的数组的类型:

>>> arr = np.array([2, 6, 5, 9], float)
>>> arr
array([ 2., 6., 5., 9.])
>>> type(arr)
< type 'numpy.ndarray'>

反之,可用如下代码将数组转换为列表:

>>> arr = np.array([1, 2, 3], float)
>>> arr.tolist()
[1.0, 2.0, 3.0]
>>> list(arr)
[1.0, 2.0, 3.0]

 

将数组赋给变量,新建数组,这样做不会在内存为数组创建一个新的副本,它只是将新定义的变量指向原数组对象。

若想用现有数组,创建一个新的数组对象,则要用copy函数:

>>> arr = np.array([1, 2, 3], float)
>>> arr1 = arr
>>> arr2 = arr.copy()
>>> arr[0] = 0
>>> arr
array([0., 2., 3.])
>>> arr1
array([0., 2., 3.])
>>> arr2
array([1., 2., 3.])

此外,还可以用同一个值填充数组,覆盖掉之前的值,得到一个元素全部相同的数组,例如:

>>> arr = np.array([10, 20, 33], float)
>>> arr
array([ 10., 20., 33.])
>>> arr.fill(1)
>>> arr
array([ 1., 1., 1.])

还可以用np的子模块random随机选取元素创建数组。例如,将要创建的数组的长度作为permutation函数的参数传入,该函数返回一个由整数组成的随机序列[6]

>>> np.random.permutation(3)
array([0, 1, 2])

另一种数组创建方法是用normal函数从一个正态分布中抽取一列数字:

>>> np.random.normal(0,1,5)
array([-0.66494912, 0.7198794 , -0.29025382, 0.24577752, 0.23736908])

参数0为正态分布的均值,1为标准差,5表示抽取5个数字创建数组。若使用均匀分布,random函数将返回0到1之间(不包含0和1)的数字:

>>> np.random.random(5)
array([ 0.48241564, 0.24382627, 0.25457204, 0.9775729 , 0.61793725])

NumPy还提供几个创建二维数组(矩阵)的函数。例如,identity函数创建单位矩阵,其维度用参数来指定:

>>> np.identity(5, dtype=float)
array([[ 1., 0., 0., 0., 0.],
 [ 0., 1., 0., 0., 0.],
 [ 0., 0., 1., 0., 0.],
 [ 0., 0., 0., 1., 0.],
 [ 0., 0., 0., 0., 1.]])

eye函数返回第k条对角线上元素为1的矩阵。

>>> np.eye(3, k=1, dtype=float)
array([[ 0., 1., 0.],
 [ 0., 0., 1.],
 [ 0., 0., 0.]])

创建新数组(1或2维)最常用的函数是zeros和ones,它们按照指定的维度创建数组,并分别用0或1填充。示例如下:

>>> np.ones((2,3), dtype=float)
array([[ 1., 1., 1.],
 [ 1., 1., 1.]])
>>> np.zeros(6, dtype=int)
array([0, 0, 0, 0, 0, 0])

而zeros_like和ones_like函数,创建的是跟现有数组元素的类型[7]和维度都相同的数组:

>>> arr = np.array([[13, 32, 31], [64, 25, 76]], float)
>>> np.zeros_like(arr)
array([[ 0., 0., 0.],
 [ 0., 0., 0.]])
>>> np.ones_like(arr)
array([[ 1., 1., 1.],
 [ 1., 1., 1.]])

另外一种创建二维数组的方法是,用vstack函数(垂直方向合并)合并一维数组:

>>> arr1 = np.array([1,3,2])
>>> arr2 = np.array([3,4,6])
>>> np.vstack([arr1,arr2])
array([[1, 3, 2],
 [3, 4, 6]])

二维数组也可以用random子模块按照某种分布进行创建。例如,随机从0到1的均匀分布中选取数字作为数组元素,创建一个2×3型数组,方法如下:

>>> np.random.rand(2,3)
array([[ 0.36152029, 0.10663414, 0.64622729],
 [ 0.49498724, 0.59443518, 0.31257493]])

另一种经常用来创建数组的分布是多元正态分布:

>>> np.random.multivariate_normal([10, 0], [[3, 1], [1, 4]], size=[5,])
array([[ 11.8696466 , -0.99505689],
 [ 10.50905208, 1.47187705],
 [ 9.55350138, 0.48654548],
 [ 10.35759256, -3.72591054],
 [ 11.31376171, 2.15576512]])

列表[10,0]是均值向量,[[3, 1], [1, 4]]是协方差矩阵,5是要抽取的元素数量。

表1. 1

方法

用途

tolist

将NumPy数组转换为Python列表的函数

copy

复制NumPy数组元素的函数

ones, zeros

创建用1或0填充的数组的函数

zeros_like, ones_like

该函数用来创建与作为参数的列表形状相同的二维数组

fill

将数组元素替换为某一特定元素的函数

identity

创建单位矩阵的函数

eye

该函数用来创建第k条对角线上元素为0的矩阵

vstack 

将数组合并为二维数组的函数

random子模块:random、permutation、normal、rand、multivariate_normal等

random子模块从某种分布抽取元素,创建数组

2.数组操作

访问列表元素、切片以及其他Python列表的所有常见操作,均能以相同或相似的方式作用于数组:

>>> arr = np.array([2., 6., 5., 5.])
>>> arr[:3]
array([ 2., 6., 5.])
>>> arr[3]
5.0
>>> arr[0] = 5.
>>> arr
array([ 5., 6., 5., 5.])

数组所包含的不同元素也可以获取到,用unique函数即可:

>>> np.unique(arr)
array([ 5., 6., 5.])

数组元素的排序也可以用sort函数。数组的索引用argsort函数获取:

>>>arr = np.array([2., 6., 5., 5.])
>>> np.sort(arr)
array([ 2., 5., 5., 6.])
>>> np.argsort(arr)
array([0, 2, 3, 1])

用shuffle函数也可以调整数组元素,使其随机排列:

>>> np.random.shuffle(arr)
>>> arr
array([ 2., 5., 6., 5.])

NumPy数组类似,也有一个内置函数array_equal,用来比较两个数组是否相等[8]

>>> np.array_equal(arr,np.array([1,3,2]))
False

然而,多维数组与列表操作不同。事实上,多维列表各维度用逗号分隔的形式依次指定(而列表用方括号[9])。例如,二维列表(矩阵)元素访问方法如下:

>>> matrix = np.array([[ 4., 5., 6.], [2, 3, 6]], float)
>>> matrix
array([[ 4., 5., 6.],
 [ 2., 3., 6.]])
>>> matrix[0,0]
4.0
>>> matrix[0,2]
6.0

对数组的各维进行切片操作使用英文冒号:,冒号前后为位于起始位置和结束位置的元素的索引:

>>> arr = np.array([[ 4., 5., 6.], [ 2., 3., 6.]], float)
>>> arr[1:2,2:3]
array([[ 6.]])

仅用冒号:,不用数字,表示冒号所在轴上的所有元素都在切片范围之内:

>>> arr[1,:]
array([2, 3, 6])
>>> arr[:,2]
array([ 6., 6.])
>>> arr[-1:,-2:]
array([[ 3., 6.]])

flatten函数可将多维数组变为一维数组:

>>> arr = np.array([[10, 29, 23], [24, 25, 46]], float)
>>> arr
array([[ 10., 29., 23.],
 [ 24., 25., 46.]])
>>> arr.flatten()
array([ 10., 29., 23., 24., 25., 46.])

我们还可以查看数组对象,获取相关信息。用shape属性,可得到数组的大小:

>>> arr.shape
(2, 3)

该例中,arr是一个2行3列的矩阵。dtype属性返回数组元素的类型:

>>> arr.dtype
dtype('float64')

数值类型float64用来存储双精度(8字节)实数(类似于Python的标准float类型)。其他数据类型有int64、int32和字符串。数组的数据类型可以转换。例如:

>>> int_arr = matrix.astype(np.int32)
>>> int_arr.dtype
dtype('int32')

len函数返回数组第一维的长度:

>>> arr = np.array([[ 4., 5., 6.], [ 2., 3., 6.]], float)
>>> len(arr)
2

关键字in,类似于它在Python for循环中的用法,可用来判断数组是否包含某个元素:

>>> arr = np.array([[ 4., 5., 6.], [ 2., 3., 6.]], float)
>>> 2 in arr
True
>>> 0 in arr
False

reshape函数可调整数组的维度。[10]例如,8行1列的矩阵可调整为4行2列的矩阵:

>>> arr = np.array(range(8), float)
>>> arr
array([ 0., 1., 2., 3., 4., 5., 6., 7.])
>>> arr = arr.reshape((4,2))
>>> arr
array([[ 0., 1.],
 [ 2., 3.],
 [ 4., 5.],
 [ 6., 7.]])
>>> arr.shape
(4, 2)

此外,还支持矩阵的转置运算;也就是说,用transpose函数可互换两个维度,创建一个新数组:

>>> arr = np.array(range(6), float).reshape((2, 3))
>>> arr
array([[ 0., 1., 2.],
 [ 3., 4., 5.]])
>>> arr.transpose()
array([[ 0., 3.],
 [ 1., 4.],
 [ 2., 5.]])

数组还可以用T属性实现转置:

>>> matrix = np.arange(15).reshape((3, 5))
>>> matrix
array([[ 0, 1, 2, 3, 4],
 [ 5, 6, 7, 8, 9],
 [10, 11, 12, 13, 14]])
>>>matrix .T
array([[ 0, 5, 10],
 [ 1, 6, 11],
 [ 2, 7, 12],
 [ 3, 8, 13],
 [ 4, 9, 14]])

另一种调整数组元素位置的方法是,用newaxis函数增加维度:

>>> arr = np.array([14, 32, 13], float)
>>> arr
array([ 14., 32., 13.])
>> arr[:,np.newaxis]
array([[ 14.],
 [ 32.],
 [ 13.]])
>>> arr[:,np.newaxis].shape
(3,1)
>>> arr[np.newaxis,:]
array([[ 14., 32., 13.]])
>>> arr[np.newaxis,:].shape
(1,3)

上述例子,两个新数组都是二维的。由newaxis生成的第二个数组长度为1。

NumPy数组的连接操作用concatenate函数,句法形式取决于数组的维度。多个一维数组可相继连接,将要连接的多个数组置于元组中作为参数传入即可:

>>> arr1 = np.array([10,22], float)
>>> arr2 = np.array([31,43,54,61], float)
>>> arr3 = np.array([71,82,29], float)
>>> np.concatenate((arr1, arr2, arr3))
array([ 10., 22., 31., 43., 54., 61., 71., 82., 29.])

多维数组必须指定沿哪条轴连接。否则,NumPy默认沿第一条轴连接:

>>> arr1 = np.array([[11, 12], [32, 42]], float)
>>> arr2 = np.array([[54, 26], [27,28]], float)
>>> np.concatenate((arr1,arr2))
array([[ 11., 12.],
 [ 32., 42.],
 [ 54., 26.],
 [ 27., 28.]])
>>> np.concatenate((arr1,arr2), axis=0)
array([[ 11., 12.],
 [ 32., 42.],
 [ 54., 26.],
 [ 27., 28.]])
>>> np.concatenate((arr1,arr2), axis=1)
array([[ 11., 12., 54., 26.],
 [ 32., 42., 27., 28.]])

将大量数据保存为二进制文件而不用原格式存储,这样的情况很常见。NumPy的tostring函数可将数组转化为二进制字符串。当然,这个过程是可逆的,fromstring函数可将二进制字符串还原为数组。例如:

>>> arr = np.array([10, 20, 30], float)
>>> str = arr.tostring()
>>> str
'\x00\x00\x00\x00\x00\x00$@\x00\x00\x00\x00\x00\x004@\x00\x00\x00\x00\
x00\x00>@'
>>> np.fromstring(str)
array([ 10., 20., 30.])

表1. 2

方法

用途

unique

从数组选取所有不同元素的函数

random、shuffle

调整数组元素位置,使其随机排列的函数

sort、argsort

sort按照升序排列数组元素
argsort返回数组元素升序排列时的索引列表[11]

array_equal

比较两个数组,如果相同,返回True(否则返回False)

flatten

将二维数组转换为一维数组

transpose

计算二维数组的转置

reshape

调整二维数组的元素,改变数组的形状

concatenate

沿现有的轴,连接多个数组[12]

fromstring 、tostring

二进制字符串和数组之间的互相转换

3.数组运算

NumPy数组显然支持常见的数学运算。例如:

>>> arr1 = np.array([1,2,3], float)
>>> arr2 = np.array([1,2,3], float)
>>> arr1 + arr2
array([2.,4., 6.])
>>> arr1-arr2
array([0., 0., 0.])
>>> arr1 * arr2
array([1, 4., 9.])
>>> arr2 / arr1
array([1., 1., 1.])
>>> arr1 % arr2
array([0., 0., 0.])
>>> arr2**arr1
array([1., 4., 27.])

既然上述运算都作用于元素级别,这就要求参与运算的数组大小相同。如果该条件不能满足,就会返回错误:

>>> arr1 = np.array([1,2,3], float)
>>> arr2 = np.array([1,2], float)
>>> arr1 + arr2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: shape mismatch: objects cannot be broadcast to a single shape

上述错误信息的意思是无法对对象进行广播(broadcast),因为大小不同的数组参与运算的唯一方法叫作广播。广播的意思是数组维度不同时,维度少的数组将多次重复自身,直到它跟另外一个数组维度相同。看下面的示例:

>>> arr1 = np.array([[1, 2], [3, 4], [5, 6]], float)
>>> arr2 = np.array([1, 2], float)
>>> arr1
array([[ 1., 2.],
 [ 3., 4.],
 [ 5., 6.]])
>>> arr2
array([1., 2.])
>>> arr1 + arr2
array([[ 2., 4.],
 [ 4., 6.],
 [ 6., 8.]])

arr2被广播成大小跟arr1相同的二维数组。因而,对于arr1的每一维,arr2都重复自身一次,相当于arr2变换为下面这个数组后再参加运算:

array([[1., 2.],[1., 2.],[1., 2.]])

如要明确指定数组的广播方式,可用newaxis常量[13]

>>> arr1 = np.zeros((2,2), float)
>>> arr2 = np.array([1., 2.], float)
>>> arr1
array([[ 0., 0.],[ 0., 0.]])
>>> arr2
array([1., 2.])
>>> arr1 + arr2
array([[-1., 3.],[-1., 3.]])
>>> arr1 + arr2[np.newaxis,:]
array([[1., 2.],[1., 2.]])
>>> arr1 + arr2[:,np.newaxis]
array([[1.,1.],[ 2., 2.]])

跟Python列表[14]不同的是,数组支持按条件查询,用布尔数组过滤元素就是一个典型的例子:

>>> arr = np.array([[1, 2], [5, 9]], float)
>>> arr >= 7
array([[ False, False],
[False, True]], dtype=bool)
>>> arr[arr >= 7]
array([ 9.])

可以用多个布尔表达式获取数组的子集:

>>> arr[np.logical_and(arr > 5, arr < 11)]
>>> arr
array([ 9.])

我们可以根据索引选取元素,用目标元素的索引构造一个数据类型为整型的数组,然后,将索引数组放到目标数组的后面,并用方括号括起来。例如:

>>> arr1 = np.array([1, 4, 5, 9], float)
>>> arr2 = np.array([0, 1, 1, 3, 1, 1, 1], int)
>>> arr1[arr2]
array([ 1., 4., 4., 9., 4., 4., 4.])

上述第3行代码,表示按照arr2指定的索引顺序,从数组arr1中选取相应的元素,也就是选取arr1的第0、第1、第1、第3、第1、第1和第1个元素。用列表存储目标元素的索引,可以达到同样的选取效果:

>>> arr = np.array([1, 4, 5, 9], float)
>>> arr[[0, 1, 1, 3, 1]]
array([ 1., 4., 4., 9., 4.])

多维数组的选取操作,需要使用多个一维度的索引数组,每个维度对应一个索引数组。索引数组放到Python列表中[15],再将Python列表置于多维数组后面的方括号之中。

第1个种子数组(selection array)存储矩阵元素的行号,第2个种子数组存储列号。例如:

>>> arr1 = np.array([[1, 2], [5, 13]], float)
>>> arr2 = np.array([1, 0, 0, 1], int)
>>> arr3 = np.array([1, 1, 0, 1], int)
>>> arr1[arr2,arr3]
array([ 13., 2., 1., 13.])

arr2的元素为arr1元素的行号,而arr3的元素则为arr1元素的列号,因此从arr1选取的第1个元素为位于第1行、第1列的13。

take函数支持以索引数组为参数,从调用它的数组选取元素,效果等同于上面的方括号选择法:

>>> arr1 = np.array([7, 6, 6, 9], float)
>>> arr2 = np.array([1, 0, 1, 3, 3, 1], int)
>>> arr1.take(arr2)
array([ 6., 7., 6., 9., 9., 6.])

用axis参数指定维度,take函数可从调用它的多维数组、沿指定维度选取一部分元素:

>>> arr1 = np.array([[10, 21], [62, 33]], float)
>>> arr2 = np.array([0, 0, 1], int)
>>> arr1.take(arr2, axis=0)
array([[ 10., 21.],
 [ 10., 21.],
 [ 62., 33.]])
>>> arr1.take(arr2, axis=1)
array([[ 10., 10., 21.],
 [ 62., 62., 33.]])

put函数为take函数的逆操作,它有两个参数:将元素放到什么位置(索引列表)、被投放的元素来自哪个数组。put函数将一个数组的元素放到调用该函数的另一个数组的指定位置:

>>> arr1 = np.array([2, 1, 6, 2, 1, 9], float)
>>> arr2 = np.array([3, 10, 2], float)
>>> arr1.put([1, 4], arr2)
>>> arr1
array([ 2., 3., 6., 2., 10., 9.])

本节最后,我们想提醒你注意,二维数组的乘法运算也是元素级的(但矩阵乘法不是):

>>> arr1 = np.array([[11,22], [23,14]], float)
>>> arr2 = np.array([[25,30], [13,33]], float)
>>> arr1 * arr2
array([[ 275., 660.],
 [ 299., 462.]])

表1. 3

方法

用途

take

以一个整数数组做参数表示索引,从另一个数组选取相应的元素

put

将一个数组给定位置的元素替换为另一个数组的元素

4.线性代数运算

矩阵之间最常用的运算是,矩阵与其转置矩阵的内积XTX。计算内积,用np.dot函数[16]

>>> X = np.arange(15).reshape((3, 5))
>>> X
array([[ 0, 1, 2, 3, 4],
 [ 5, 6, 7, 8, 9],
 [10, 11, 12, 13, 14]])
>>> X.T
array([[ 0, 5, 10],
 [ 1, 6, 11],
 [ 2, 6, 12],
 [ 3, 8, 13],
 [ 4, 9, 14]])
>>>np.dot(X .T, X)#X^T X
array([[ 2.584 , 1.8753, 0.8888],
 [ 1.8753, 6.6636, 0.3884],
 [ 0.8888, 0.3884, 3.9781]])

有几个函数可直接计算数组(矩阵或向量)不同类型的积(内积、外积、向量积)。

一维数组(向量)的内积与点积相同:

>>> arr1 = np.array([12, 43, 10], float)
>>> arr2 = np.array([21, 42, 14], float)
>>> np.outer(arr1, arr2)
array([[ 252., 504., 168.],
 [ 903., 1806., 602.],
 [ 210., 420., 140.]])
>>> np.inner(arr1, arr2)
2198.0
>>> np.cross(arr1, arr2)
array([ 182., 42., -399.])

NumPy的linalg子模块,实现了矩阵的多种线性代数运算。例如,计算矩阵的行列式的值:

>>> matrix = np.array([[74, 22, 10], [92, 31, 17], [21, 22, 12]], float)
>>> matrix
array([[ 74., 22., 10.],
 [ 92., 31., 17.],
 [ 21., 22., 12.]])
>>> np.linalg.det(matrix)
-2852.0000000000032

inv函数生成矩阵的逆矩阵[17]

>>> inv_matrix = np.linalg.inv(matrix)
>>> inv_matrix
array([[ 0.00070126, 0.01542777, -0.02244039],
 [ 0.26192146, -0.23772791, 0.11851332],
 [-0.48141655, 0.4088359 , -0.09467041]])
>>> np.dot(inv_matrix,matrix)
array([[ 1.00000000e+00, 2.22044605e-16, 4.77048956e-17],
 [ -2.22044605e-15, 1.00000000e+00, 0.00000000e+00],
 [ -3.33066907e-15, -4.44089210e-16, 1.00000000e+00]])

矩阵的特征值(eigenvalues)和特征向量(eigenvectors)计算方法很简单:

>>> vals, vecs = np.linalg.eig(matrix)
>>> vals
array([ 107.99587441, 11.33411853, -2.32999294])
>>> vecs
array([[-0.57891525, -0.21517959, 0.06319955],
 [-0.75804695, 0.17632618, -0.58635713],
 [-0.30036971, 0.96052424, 0.80758352]])

表1. 4

方法

用途

dot  

两个数组的点积

inner

两个多维数组的内积

linalg模块中的linalg.det、linalg.inv 和linalg.eig等函数

linalg模块包括多个线性代数运算方法,其中有求矩阵的行列式的值(det)、矩阵的逆(inv)以及矩阵的特征值和特征向量(eig)

5.统计和数学函数

NumPy提供一组计算数组元素统计信息的函数。聚合型运算,比如求和、均值、中位数和标准差,可通过访问数组的相应属性得到。例如,随机选取元素(服从某正态分布),创建一个数组,我们可以用以下两种方法计算数组元素的均值:

>>> arr = np.random.rand(8, 4)
>>> arr.mean()
0.45808075801881332
>>> np.mean(arr)
0.45808075801881332
>>> arr.sum()
14.658584256602026

所有这一类函数见表1.5。

表1.5

方法

用途

mean

各元素的均值。空数组,均值默认为NaN

std、var

计算数组的标准差(std)和方差(var)。可指定自由度参数(默认为数组的长度)

min、max

求数组最小值(min)和最大值(max)

argmin、argmax

返回最小(argmin)和最大(argmax)元素的索引

 

本书从此往后,我们都用如下语句导入pandas:

因此,下文所有代码,只要有pd,均指pandas。

Python的pandas模块功能强大,包含大量用于分析数据结构的函数。它依赖于NumPy库。pandas的设计初衷是降低数据分析操作的难度,提升速度。比起Python的标准函数,pandas函数性能更高,尤其擅长文件读写、数据库操作;pandas是数据处理的最佳选择。探索数据所包含的信息,主要方法有哪些以及如何用pandas进行操作,下面几节将给出答案。我们先讲解数据在pandas中的存储形式和数据的加载方法。

import pandas as pd

1.探索数据

我们先介绍pandas只有一维的数组类对象Series,以此引入pandas的数据库结构DataFrame。Series可以存储NumPy所有类型的数据,同时还存储数据的标签——索引。我们来看一个简单的例子:

obj对象由两类值组成,右侧为元素,左侧为元素对应的索引。给定元素数组的长度为N,索引则默认从0排到N-1。Series的元素数组和索引对象,可分别用values和index属性获取:

NumPy数组运算,索引会予以保留(例如标量乘法、用布尔数组过滤或用数学函数处理数组):

Python字典可转换为Series对象,但是字典的键被转换为索引:

也可以用单独的列表作为索引:

上述例子,最后一个索引g,没有对应的元素,因此pandas默认插入NaN(Not a Number,非数字)。

我们用缺失值或NAN来指代缺失的数据。用pandas的isnull和notnull函数,可找出缺失值:

现在,我们可以从一个CSV文件导入数据到DataFrame结构。DataFrame这种数据结构,有一组按顺序排列的列,每一列可以是不同的数据类型(数值、字符串、布尔值等)。DataFrame有两种索引(行、列索引)。我们也可以将DataFrame看成一个由Series对象组成的字典,每个Series里面,所有元素的索引相同(列的标题)。下面我们结合ad.data文件中的数据进行讲解,该数据可从http://archive.ics.uci.edu/ml/datasets/Internet+Advertisements下载。在前面机器学习的例子已介绍过。

在终端输入以下代码导入数据(该例中,数据文件的路径为data_example/ad-dataset/ ad.data[18]):

该文件没有标题行(故将header参数设置为none),因此使用数字作为各列的名称。在data对象上调用describe函数,可得到DataFrame的各种总描述性统计信息:

上面总结了几种定量信息。由此可见,该数据集总共有1554个数值类型的列(因为没有标题行,列的名称用数字表示)、3279行(对每一列调用count函数)。每一列都有一组统计指标(均值、标准差、最小值、最大值和分位数),这些统计数据有助于我们对DataFrame中数据的定量信息做出初步估计。

用columns属性可获取到所有列的名称:

所有列的名称为int64类型,下述命令返回所有列的实际数据类型:

前4列和标签列(最后1列)为object类型,其余为int64类型。列的访问方法有两种。第1种,指定列的名称,与指定字典的键相似:

用列表形式,指定多个列名称,可获取到多列:

另一种访问列的方法是点号句法,这只有在列名称也是Python变量(中间没有空格),没有重名的DataFrame属性或函数(比如count或sum),并且列名称还必须是字符串类型时,才能使用该方法(该例中,列名称为int64类型,因此不能用此方法)。

若想对DataFrame中的内容有一个大致的了解,可使用head()函数。它默认返回一列的前5个元素(或DataFrame的前5行):

当然也可以用tail()函数,它默认返回最后5个元素或5行。在head()或tail()函数中指定数字n,将返回所选列的前、后n个元素:

用Python的标准切片句法,也可以从DataFrame中获取到一定数量的行:

上述代码仅获取到DataFrame的前两行(当然还有标题)。

2.操作数据

行的选取方法有多种,比如指定索引或按条件选取:

或者,按多个条件选取数据:

上面返回的数据为特征1大于0且包含广告的网页。

ix方法通过指定索引来选择相应的行:

此外,也可以使用iloc函数:

ix和iloc的不同点在于,ix操作的是索引列标签的名称,而iloc操作的是索引的位置(因此它只能接收整数)。因此,上述例子,ix一直找到标签3出现为止(共4行),而iloc函数返回DataFrame的前3行。访问DataFrame内部数据,还有一个函数叫作loc,它查找索引列的标签名,返回相应的行[19]。例如:

注意该函数与Python的标准切片方法不同,因为结果包括起始和结束位置的行(该例中,输出结果包含索引为3的行)。

再来看其他操作。DataFrame对象的一整列可设置为同一个值:

也可以将指定的单元格设置为我们想要的值:

或将整行设置为一组值(该例使用随机数0或1和ad.标签)。

数组转换为Series对象后,可作为新的一行追加到DataFrame的末尾:

用loc函数可在DataFrame最后增加一行:

追加列则非常简单,将元素赋给DataFrame新的列即可:

上述例子,新增的列所有元素的值为test value。删除列,可用drop函数:

出于多种原因,数据集也许包含重复数据。pandas的duplicated方法可判断每一行是否是对其他行的重复:

drop_duplicates函数比duplicated函数更强大,它返回的DataFrame仅包含去重过后剩余的所有元素。例如,使用该函数,我们可以找出标签这一列两个不同的元素是:

我们还可以方便地将上述结果转换为列表:

我们可以把标签转换为数值,前面机器学习示例一节讲过这种方法:

标签列仍然为object类型:

然后,我们可以将标签列转换为浮点型:

前4列包含不同类型的数据(字符串、?和浮点型数字)。我们删除字符串类型的元素后,才能将各列元素转换为数值型。我们可以用replace函数将所有的?实例(缺失值)替换为NaN:

现在,我们可以用两种方法处理包含缺失值的行。方法一,用dropna方法直接删除包含缺失值的行:

方法二,包含缺失数据的行,除了直接将其删除(可能删除重要信息)外,也可为其填充数据。用fillna方法,向这些空的单元格填充一个常量,可满足大多数需求:

经过以上处理,所有列的各元素均为数值型,因此可用astype函数将其设置为float型。此外,我们还可以用lambda函数,将DataFrame的每一列转换为数值类型[20]

上述代码,每个x实例表示一列,to_numeric函数将每一类的元素转换为最相近的数据类型(该例为float)。

pandas教程的最后,我们想演示下如何拼接两个DataFrame对象,因为在真实应用场景,可能会用到这项操作。我们随机选取元素,再创建一个小型的DataFrame:

上述代码生成一个包括两行数据的新表格。我们可以用concat函数,将其合并到原DataFrame中,将data1的各行拼接到data的下面:

执行上述操作后,我们会发现datatot比data增加了两行(注意data的行数与刚开始的不同,因为我们后来删除了它含有NaN元素的行)。

matplotlib.pyplot库,类似于MATLAB,提供多种将数据绘制成图的方法。由于后续章节的一些数据分析结果要用它实现可视化,因此我们有必要用一个简短的例子,解释后面即将用到的所有matplotlib代码:

导入该库之后(导入为plt),初始化figure对象(fig),添加axis对象(ax)。每条线是通过ax.plot()命令绘制到ax对象之中,每条线称为句柄(handle)。然后,matplotlib.pyplot记录下面所有指令,并将其绘制到figure对象之中。该例中,用plt.show()命令直接在终端显示绿色折线,并用fig.savefig()函数将其保存为figure.png文件。运行结果见图1.1。

图1.1 简单图表示例

接下来这个例子讲解如何用一条命令绘制样式不同的多条曲线,我们用到了NumPy数组,见图1.2。

图1.2 多序列曲线图表示例

注意上述代码中的get_legend_handles_labels() 函数,返回存储在ax对象中的句柄列表和标签,我们需要将这两项返回结果传给legend函数完成绘图。符号“r--”“bs”和“g^”指的是数据点的形状和颜色(分别表示红色矩形、蓝色方形和绿色三角形)。linewidth参数用来设置线条的密度,markersize用来设置点的大小。

数据分析结果,另一种常用的可视化方法是散点图,通常用来显示一组数据两个变量的不同取值情况(我们用NumPy的random子模块生成这样的一组数据)。

上述代码,s选项表示数据点的大小,colors选项为每组数据点的颜色。我们直接将句柄(p1,p2,p3)传给legend函数,见图1.3。

图1.3 由随机分布的数据点构成的散点图

关于matplotlib库的更多细节,我们建议大家读一读网上的相关材料和教程,比如他们官方提供的这份教程:http://matplotlib.org/users/pyplot_tutorial.html 。

若想实现本书讲解的机器学习技术,有一些库是必要的。我们简要介绍后续最常用的几个库。

机器学习也并不是魔法,不是所有与数据相关的问题都能受益于它,因此在入门章节的最后,讲清楚机器学习技术的最佳应用场景非常有必要。

然而,如果仅用数学规则、计算或预先确定的模式就能做到准确预测,并且实现这些方法无须用机器驱动的学习技术,那么你就没必要使用高级机器学习技术(你也不应该使用)。

在本章中,我们介绍了基本的机器学习概念和术语,这些知识是后续章节的基础。我们还介绍了机器学习专业人士准备数据、处理数据和实现数据可视化最常用的几个库(NumPy、pandas和matplotlib)。此外,后面要用到的其他几个Python库,我们也一并做了简要介绍。

学完这一章,你应该大体了解了机器学习技术的实际用途。你应该熟悉了常用的数据处理方法,能够将数据转换为机器学习算法可以处理的格式。下一章,我们来向大家解释主要的无监督学习算法以及如何用sklearn库实现它们。

[1] 原文为“computational statics”,其中“statics”应为statistics。——译者注

[2] 分量——译者注

[3] 精度——译者注

[4] 还有一类叫作半监督学习,学习器从未标记样本自动学习。见周志华著的《机器学习》。

[5] 注意是在终端而不是Python shell里运行pip命令。该处代码应去掉前面的Python shell提示符。——译者注

[6] 整数的取值范围为大于等于0,小于指定的参数。——译者注

[7] 指数组内各元素的数值类型。——译者注

[8] 相等,指形状和元素是否均相等。此外,列表比较用cmp函数。——译者注

[9] >>>multilist = [[4, 5, 6], [2, 3, 6]]
   >>>multilist[0][0]
   4                   ——译者注

[10] 直译就是reshape函数可调整元素所在的维度。——译者注

[11] 返回结果的类型为NumPy数组。——译者注

[12] 原文为“Concatenate two -dimensional arrays into one matrix”,意思是拼接两个数组,形成一个矩阵,不够全面。因此,此处译文是根据SciPy.org文档对concatenate函数的描述翻译的。——译者注

[13] 代码中arr1+arr2输出结果有误。应为:

              ——译者注

[14] 要实现Python列表筛选操作,可使用内置函数filter。——译者注

[15] 指示例代码第4行中的[arr2, arr3]。——译者注

[16] np.dot(X.T, X)的输出结果附图有误,应为:

                      ——译者注

[17] np.dot(inv_matrix, matrix)的输出结果附图有误,应为:

                                   ——译者注

[18] 原书此处为“ad-data”,文件名实际为“ad.data”。——译者注

[19] loc方法,如果是切片操作,起始和结束标签所对应的行连同它们之间的行都能获取到。如果是loc[label],则只能获取到标签为label的行。label既可以是数字也可以是字符串。——译者注

[20] 若DataFrame存在非数值型元素,转换时会报错。pandas 0.17及以上版本,可使用df1 = df.apply(pd.to_numeric, errors='coerce')语句,或提前处理非数值型元素。——译者注

[21] logistic regression,大抵有几种译法:逻辑回归、逻辑斯蒂回归、逻辑斯蒂克回归、对数几率回归,还有干脆不译的。这里取对数几率回归。——译者注


相关图书

ChatGPT原理与应用开发
ChatGPT原理与应用开发
动手学机器学习
动手学机器学习
机器学习与数据挖掘
机器学习与数据挖掘
机器学习公式详解 第2版
机器学习公式详解 第2版
自然语言处理迁移学习实战
自然语言处理迁移学习实战
AI医学图像处理(基于Python语言的Dragonfly)
AI医学图像处理(基于Python语言的Dragonfly)

相关文章

相关课程