深度学习与TensorFlow实战

978-7-115-47884-9
作者: 李建军 王希铭 潘勉 许硕贵 孔德兴 张真诚 徐国卿
译者:
编辑: 张涛

图书目录:

详情

首先简述了神经网络的发展历史,介绍了TensorFlow,书中以一个简单的一元线性回归房价预测模型演示了TensorFlow的工作机制; 三是简单例举了几个以TensorFlow为基础的开源项目。介绍了深度神经网络的外延:机器学习。 书中以机器学习的三个要素:任务(Task)、性能(Performance)、经验(Experience)为核心,阐述了机器学习建立模型的原理。

图书摘要

版权信息

书名:深度学习与TensorFlow实战

ISBN:978-7-115-47884-9

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

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

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

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

著    李建军 王希铭 潘 勉 等

责任编辑 张 涛

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

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

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

读者服务热线:(010)81055410

反盗版热线:(010)81055315


本书主要讲解深度学习和TensorFlow的实战知识,全书分为10章,主要内容如下:第1章为深度学习概述,包括深度学习的基础知识、深度学习的生产力实现——TensorFlow、数据模型、TensorFlow项目介绍、TensorFlow工作环境的安装与运行;第2章为机器学习概述,讲解机器学习的定义、任务、性能、经验、学习算法、线性回归实例和TensorFlow的完整运行脚本;第3章介绍从生物神经元到感知器的内容,讲解基于MCP神经元实现布尔逻辑、感知器、使用感知器做分类等;第4章介绍人工神经网络,讲述的内容包括从感知器到多层感知器、带有权值的MCP神经元——感知器、反向传播神经网络、使用人工神经网络分类mnist;第5章介绍Logistic回归与Softmax回归;第6章介绍卷积神经网络,讲述感知器模式识别、卷积操作、卷积神经网络的结构、使用TensorFlow实现卷积神经网络的实例;第7章介绍循环神经网络,包括循环神经网络的特征、有限状态机、从MCP神经网络到循环神经网络等;第8章介绍LSTM循环神经网络,包括梯度弥散现象、长短期记忆网络、通过TensorFlow实现一个简单的LSTM;第9章深入讨论TensorFlow,讲解机器学习框架、计算图、神经网络与计算图、TensorFlow中的数据流图、使用GPU、数据可视化工具TensorBoard等;第10章为TensorFlow案例实践,包括构建TensorFlow的图片分类系统、准备代码和训练集、构造模型计算图、训练模型、评估模型的性能、多GPU训练等。

本书旨在帮助具有较少数学基础并期望在深度学习上有所作为的学习者,希望为他们提供一个快速上手深度学习的实战教程。本书适合阅读的读者包括相关专业的本科生或研究生,以及不具有机器学习或统计知识背景但想要快速补充深度学习知识,以便在实际产品或平台中应用的软件工程师。

人工智能、机器学习、深度学习是当今流行的前沿技术,人工智能是给机器赋予人的智能,机器学习是实现人工智能的一种方法,深度学习是实现机器学习的一种技术。

如果把人工智能比喻成一个人,机器学习就是一个会感知、会思考、会学习的大脑,而深度学习就是掌握了一种“超能力”或者“特异功能”的“超级大脑”。我们把人工智能用在语音识别领域,就能让机器更会“听”;把它用在计算机视觉领域,就能让机器更会“看”。也许若干年后,可能会出现一种全新的工具,它可以代替深度学习实现人工智能,人们要做的就是,在简单环境下用深度学习算法解决实际问题。

TensorFlow是由Google公司开发、通过C++工程化封装、以C++和Python为API的一种深度学习框架;TensorFlow也是一个以Graph为计算框架、以Session为运行环境、以Operation为运算单元的机器学习框架,这种框架保证了它的灵活性和易实现性。TensorFlow提供了完整的开源代码,这就意味着无论个人开发者或者研究机构都可以编写自己的数学模型和算法,并对TensorFlow架构进行补充和完善,从而形成一个活跃的深度学习社区。TensorFlow是当前最为流行的深度学习开发框架之一,其发展前景极好。本书旨在帮助具有一般数学基础并期望在深度学习上有所作为的学习者快速上手深度学习。

(1)可以在无较强数学基础、无丰富编程经验的条件下开始学习深度学习并实践操作。

本书每章都列出了必备的数学知识,方便没有足够数学基础知识的读者学习。书中还讲解了必要的Python编写规范,以方便缺乏Python编程知识的读者能够尽快熟悉开发技术,并上手操作TensorFlow。

(2)本书对深度学习的解读简单、生动,入门容易。

本书以深度学习(神经网络)的发展史为脉络,从神经网络这一学科最原始的概念出发,逐层深入各方面知识点,通过整理、提炼大量外文专著和原始文献思想,以简单、生动、凝练的方式书写,同时还使用了大量的图片和例子辅助说明,目的是让读者尽快进入实践角色。

(3)实例丰富,实践性强,让读者学以致用。

本书每章基本上配有代码实例。通过实例的演练,读者可以对学习的知识进行应用,强化学习效果。在本书最后一章,我们将前面9章的知识点连接起来,形成一个综合的项目实战案例,让读者可以体会到深度学习的强大之处。

另外,我们尽可能将所有章节的知识点加以封装和结构化,读者在学习时只需要对知识概念进行掌握,并在头脑里建立起一个系统化概念,就可以掌握深度学习的整个知识体系。以后,我们会完成一个关于深度学习原理及项目实践的系列教程,持续地分享TensorFlow开源社区的最新进展,以及我们自身的工程项目经验总结。

本书在编写过程中得到了杭州电子科技大学图形图像所、多媒体技术实验室的部分老师和同学的大力协助,特此表示感谢!他们为(排名不分先后):羊丹、王菊仙、黄思铭、赵凌志、郑志新、王睿之、陈君博、李珂冉、李玉菊、虞杰、黄凯等。

由于编写时间仓促,书中难免存在不足之处,希望广大读者阅读后给予反馈,以便我们修订完善。本书编辑邮箱为zhangtao@ptpress.com.cn。

李建军

于杭州


本书由异步社区出品,社区(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、测试、前端、网络技术等。

异步社区

微信服务号


虽然各种机器威胁论不绝于耳,但制造出能像人类一样思考、能够解放人力的机器,一直以来都是科学家的一大梦想,他们将这个宽泛的领域统称为“人工智能”。

那么,人工智能是伪命题、伪科学吗?如何定义人工智能?到现在,人工智能到底发展到哪一步了?有没有可供参考的、可供实施人工智能的工具?这就是本书所要讲的内容。阅读完本书,你就能够对这些问题形成一个初步的认识。

关于人工智能的最早定义,还需要从计算机之父——阿兰·图灵说起。

看过电影《模仿游戏》的读者一定会知道,图灵设计了这样一个机器:它通过机械运动来模拟一个计算过程,并认为人类的智慧就存在于由它模拟的机器计算过程之中。于是,他在纸上设计了一个机器——图灵机。图灵机是计算机理论的基础,在计算机基础上,诞生了最初的人工智能理论。

图灵机模拟了人类进行计算的过程。假如我们要计算两个3位数的加法:456+789,则需要拿出一张草稿纸和一支笔并在纸上按照加法规则运算:从个位到百位逐位相加,超过10还需要注意进位,最后得出一个计算结果。

图灵机将这个过程简化为一个基本的计算问题:草稿纸被模拟成一条由格子组成的无限长的纸带;笔被模拟成一个可以读、可以擦写的读写头;10以内的运算法则为读写头运行的程序;进位则被模拟成读写头的内部状态。首先设定好纸带上的初始信息和读写头的内部状态,以及程序规则,然后图灵机开始运行。

图灵机在每一时刻读入一个纸带信息,根据当前的内部状态查找相应的程序,从而给出下一时刻的内部状态并把信息涂写到纸带的格子上,如图1-1所示。

图灵机的模型得到了科学家的认可,这促使他开始认真思考机器是否能够具备人的智能。他逐渐意识到首先需要定义一个评价标准来定义一台机器是否具备智能。于是,图灵在1950年发表了“机器能思考吗”一文,提出了一个称为“图灵测试”的标准。

假设现有两间密闭的屋子,一个屋子里面放置了一台人工智能机器,另一个屋子里有一个人,屋外有一个测试者。测试者只能通过一根导线与屋子里面的计算机交流(如联网聊天)。如果测试者在有限的时间内无法判断出哪间屋子里关的是人,哪间屋子里关的是机器,那么就称这台人工智能机器通过了图灵测试(事实上,当初只要求超过30%的测试者不能确定出人和机器),如图1-2所示。

图1-1 图灵机

图1-2 图灵测试

从图灵机和图灵测试这两个方面可以发现,图灵将人工智能等同于符号计算,并试图通过一个“黑箱模型”隐藏智能的本质,从外部对其性质进行判别。图灵的工作具有开创性,但也存在重大缺陷。他虽然大大推动了计算机事业的发展,但图灵测试和图灵机没有从细节上说清楚人工智能以及人工智能的实现方式,毕竟人类是有血有肉、有感情的生物,用纯粹的符号运算概括似乎有些过于简化了。

1956年是人工智能元年。这一年夏天,在美国新罕步什尔州的汉诺威小镇,美丽的常春藤名校达特茅斯学院里群星闪耀。约翰·麦卡锡(John McCarthy)、马文·明斯基(Marvin Minsky)、克劳德·香农(Claude Shannon)、艾伦·纽厄尔(Allen Newell)、赫伯特·西蒙(Herbert Simon)等众多领域的科学家聚在一起,目标是“精确、全面地描述人类的学习和其他智能,并制造机器来模拟”。这次“达特茅斯会议”被公认为人工智能这一学科的起源。

达特茅斯会议之后,人工智能获得了不错的发展,陆续进行了不少出色的工作,如用计算机代替人类证明数学定理、击败人类选手获得美国跳棋州冠军等,但其实连我们渴求的强人工智能的门槛都还摸不到。人工智能学科也经历了几番大起大落。每一次人工智能的崛起都是因为某种先进的技术发明,而每一次人工智能遇到的瓶颈都是因为人们对于人工智能技术的期望太高,超出了它在技术上能达到的水准。有时,政府、基金会等甚至会撤资,从而导致研究人员没有足够的资金去从事相关研究。

从图灵测试的设计中,可以看出科学家将人工智能机器看成一个黑箱,用各种方法与这个黑箱进行沟通,并得到黑箱的反馈。可以用3种方式对待这个黑箱:

(1)完全不管黑箱中的内容,只关心人与黑箱之间的交流,以期通过图灵测试。

(2)仔细审视黑箱中的内容,调整黑箱中的内容来通过图灵测试。

(3)观察和模仿自然界的各种生命活动(将它们看成黑箱),以期模拟出一个黑箱。

这3种对待人工智能(黑箱)的方式,产生了三大人工智能学派。

符号学派不考虑黑箱如何构造,而是强调它的智能行为和表现。只要黑箱的表现符合一定标准(如满足图灵测试)或者表现出一定的生物智能行为,就认为实现了人工智能。因此,可以将这种观点概括为“物理符号系统假说”(physical symbolic system hypothesis)。符号学派将人工智能看成一个软件而非硬件,并重点关注人工智能黑箱与外部交互的部分。

行为学派的出发点与符号学派不同,他们将关注点聚焦在低等生命。如果我们观察许多动物的行为,可以发现某种“智慧”的表象。例如,单个蚂蚁结构简单、智力低下,但一窝蚂蚁能构筑庞杂、舒适的巢穴,并非常明智地选择生存策略;又如,燕群飞翔时似乎总能保持完美的队形,这是我们人类目前所不能理解的“群体行为智力”。行为学派受到了这种低等生物集群智慧的启发,认为人工智能可以通过设计低等的智慧并“涌现”出“人工智能”。

人类的智慧活动主要来自大脑的活动,而大脑是由亿万的神经元通过复杂的连接构成的。一个构建人工智能很自然的想法是:能不能通过模拟人脑的结构来实现类似大脑的智力呢?如果把智力活动比作软件,那么支撑这些活动的神经网络就是相应的硬件。对比符号学派,我们发现主张人工智能的科学家实际上在强调高级的人工智能活动是从大量神经网络的连接中自发出现的。因此,他们又称为连接学派。

本书所述内容的核心是深度学习(又称深度神经网络),它正是连接学派的一个分支。下面会具体介绍神经网络,其发展史如图1-3所示。其中列出了从1943年到2015年神经网络发展史中的里程碑。

图1-3 神经网络的发展史

故事要从1943年开始说起。Warren McCulloch和Walter Pitts构建了称为MCP人工神经元的模型,当时希望能够用计算机来模拟人的神经元反应过程。该模型将神经元简化为3个过程:输入信号线性加权、求和、非线性激活(通过阈值),如图1-4所示。

可以通过这个简单的模型模拟一些常见的函数,并进行自学习优化。这个模型是不完美的,它只是简单地模拟了一个神经元的行为,而人类大脑中的神经元总数达到了500亿个之多,离产生人工智能还相距遥远。

图1-4 MCP人工神经元模型

1957年,Rosenblatt对MCP神经元加以改良,发明了感知器(Perceptron)算法,这一算法将神经元活动抽象为一个数学模型,它可以对多维数据进行分类,并且能够使用梯度下降法从训练样本中自动学习更新权值。这一创新将神经网络理论用于人工智能向前推进了一大步,也激起了人工智能的第一波发展浪潮。

然而,到了1969年,美国数学家Marvin Minsky和Seymour Papert合著了一部关于感知器的著作(英文书名称为《Perceptrons》),针对感知器的局限性做了严谨的分析。

在该书中,他们证明了感知器本质上是一种线性模型,只能处理线性分类问题。例如,感知器在分析布尔函数中的异或(XOR)问题时,不能对异或分类问题进行线性分类。这实质上在理论上锁死了感知器继续发展的上限,也在某种程度上成为导致神经网络研究陷入近20年停滞的一个标志。

关于MCP神经元感知器,将在第3章中详细论述。

1986~1998年是神经网络的第二次发展高潮。

到了1986年,感知器不能解决非线性问题的缺陷被Hinton弥补,他提出了适用于多层感知器(Multi-Layer Perceptron,MLP)的反向传播(Back-Propagation,BP)算法,并引入了sigmoid非线性激活函数进行非线性映射,从而有效解决了非线性分类中的参数学习问题。

多层感知器是对感知器模型的发展,它解决了单个感知器不能够解决复杂任务的问题。如果假设感知器为一个神经元细胞,那么多个感知器组成的多层网络则可以视为一个神经元细胞组成的神经网络。感知器只有一个输入层和一个输出层,而多层感知器将前一层感知器的输出作为下一层感知器的输入,从而组成包含一个输入层、多个隐层和一个输出层的神经网络,依靠多个隐层可以处理复杂很多的任务,并且能够解决异或分类的问题(见图1-5)。然而,Marvin Minsky在其《Perceptrons》中认为,世界上没人发现可以将MLP训练得足够好,因此神经网络是没有前途的。而Hinton所提出的BP算法正好成功地解决了MLP网络参数的训练难题。

1989年,Robert Nielsen证明了多层感知器的万能逼近定理,即对于任何闭区间内的一个连续函数f(·),都可以用含有一个隐层的BP网络来逼近。用通俗的语言来讲,单个感知器只能模拟一个简单的线性方程式,即模拟出简单的直线、平面及超平面(比样本空间维度少一维的平面,可用于对样本空间进行划分),在感知器的输出层引入非线性函数,则可以将直线、平面及超平面变为曲线、曲面和超曲面;如果由多个非线性感知器组成一个神经网络,实际上,它就是曲线、曲面和超曲面的组合,这样就可以拟合一切连续函数了,如图1-6所示。

图1-5 多层感知器示意图

图1-6 神经网络可以拟合一切连续函数

按照这个思路发展下去,多层感知器组成的人工神经网络应当具有逼近一切连续函数的能力。既然连接主义者认为人工智能的实现方式在于那个黑箱内部的连接方式,那么只要实现任意函数拟合,也就能够实现任意的连接方式。同样在1989年,Yann LeCun发明了卷积神经网络——LeNet(见图1-7),并将其用于美国邮政系统的手写数字识别,取得了非常好的成绩,这意味着神经网络拟合的“人工智能”已经可以看懂人类写数字了!那么距离它实现强人工智能似乎只是时间问题了。

图1-7 LeNet结构图(来自Yann LeCun的论文)

然而,事情却没有那么顺利。首先,神经网络是一种连接方式,而这种连接方式并非实体,而是存在于计算机编写的代码中,并运行于内存中。尽管这个神经网络可以任意扩展下去,但计算机的计算能力跟不上。其次,神经网络的增加需要海量的数据进行训练,而在那个互联网还没有普及、传感器成本极高的时代,数据是极其稀缺的,这导致我们无法找到充足的数据来“喂养”我们的模型。最后,算法本身就存在严重缺陷,1991年指出BP算法存在梯度消失问题,即误差梯度在从后向前传递的过程中会逐渐变小。对于一个层数比较多的神经网络,误差梯度传到前层以后几乎为0,这导致模型参数无法训练。

前面已经提到,为了提高“智力”,多层感知器必须依靠增加神经网络的深度以逼近更复杂的连续函数。而对于一个使用BP算法的深度神经网络,其参数因为梯度消失问题而几乎无法学习,这实际上又给神经网络的未来带来了很大的不确定性。由此,对神经网络的探索在20世纪末又再次陷入沉寂,许多神经网络的学者不是投稿被拒,就是另寻他路。

由于种种原因,神经网络的发展再一次跌入低谷,但Hinton及其他几位科学家坚持了下来。事情的转机发生在2006年,困扰科学界多时的梯度消失终于有了合适的解决方案。为了让科学界重新接受神经网络,他们用“深度学习”来替换已经让人心生厌烦的“神经网络”一词,神经网络的“新称谓”——深度学习,就此登上历史舞台,并开启了第三次神经网络发展的高潮。

2006年是深度学习元年,Hinton提出了网络训练中梯度消失问题的解决方案:无监督预训练对参数进行初始化+有监督训练微调。其主要思想是:首先通过无监督方法学习到训练数据的结构(如自动编码器),然后在这一结构上进行有监督训练参数微调。

2011年,有人提出了ReLU微调,该激活函数能够有效地抑制梯度消失问题。

2012年,为了证明深度学习的潜力,Hinton课题组首次参加了ImageNet图像识别比赛(一个识别图像中物体分类的大赛)。他们通过构建CNN网络AlexNet一举夺魁,而且AlexNet的分类正确性大大超过了SVM模型(一种经典的统计学习方法)。曾经被冷落的神经网络,自此开启了“逆袭”之旅。

2015年,Hinton、Yann LeCun、Bengio论证了局部极值问题对于深度学习的影响,结论是损失函数的局部极值问题对于深度神经网络的影响可以忽略。这一论断消除了笼罩在神经网络上局部极值最优化问题的阴霾。具体原因是深度网络虽然局部极值非常多,但是通过深度学习的批量梯度下降(batch gradient descent)优化方法很难陷入局部极小值,就算陷进去,也非常接近于全局最小值。

神经网络发展的第三次浪潮不仅是因为算法上的革命,还受到20世纪90年代计算机硬件发展的影响。得益于摩尔定律,计算机相比过去快了数十倍、数百倍,这使得处理大规模数据集和深度神经网络成为可能。但这还不够,CPU开始逼近单核算力的上限,计算能力开始主要通过数个CPU并行计算来增长,但大型数据集的规模和神经网络层数越来越多,主流的CPU并行计算也不能满足对计算能力的要求,GPU并行计算模式开始崭露头角。

最先意识到这一点的是Abdel-rahman Mohamed和George Dalh,他们与Hinton合作发现了利用多个高端显卡有效训练并模拟神经网络的架构方法。由于不同的模型和不同的训练集需要的计算量不同,因此利用GPU代替CPU做并行计算到底能有多少效率的提升很难定量地说明,但是使用GPU后效率的提高是显而易见的。以往需要数周训练的一个模型,在使用GPU后,训练时间可以压缩到几天甚至是几个小时。这也就意味着深度学习可以从实验室走出来,开始朝着商业化应用的方向发展了。

另一位深度学习生产化应用的先驱Andrew Ng对分布式计算有着深刻的认识,他逐渐意识到利用大量训练数据与快速的计算能力结合产生商业化应用的发展潜力被严重低估。在当时,“大数据”的发展如火如荼,其概念也已经深入人心。但如何对大量的训练数据进行计算并挖掘其中有用的数据而产生价值,却是一个亟待解决的问题。而这个问题在深度学习的语境下可以理解为:在一个计算能力强大的GPU分布式计算平台上,使用大量的训练数据对一个深度神经网络模型进行训练,可以很好地减少传统模型过拟合的问题,从而让模型的预测、识别精度再上一个台阶(例如,识别人类语音、识别图片中的物体,甚至自动驾驶汽车等),接近甚至超过人类的水平。

于是Google技术专家Jeff Dean很快与Andrew Ng取得联系,并借助Google的资源一同创建了谷歌大脑(Google Brain)实验室。他们使用深度神经网络模型训练了YouTube上存储的大量视频数据,让模型试着去学习如何辨别一只“猫”,这个模型的识别精度已经接近了人类的最好水平。

从上文中我们可以发现,Hinton、Yann LeCun、Bengio、Andrew Ng对神经网络的第三次崛起(即深度学习)做出了非常大的贡献,可以说他们是开宗立派式的人物。而神经网络的发展,也从少量数据集、依靠较弱的CPU计算性能和简单的参数算法,朝着大量数据集、GPU并行计算,以及更好和更灵巧的算法方向发展,我们可以将深度学习总结为:

深度学习=海量训练数据+并行计算+多层神经网络算法

现在认真审视一下1.5节中提到的深度学习的“伪公式”:深度学习=海量训练数据+并行计算+多层神经网络算法。

(1)因为深度学习的结构,使它具有较传统模型更强的表达能力,从而也就需要更多的数据来避免过拟合的发生,以保证训练的模型在新的数据上也能有可以接受的表现。为了能够进行深度学习,你必须有非常多的训练数据,这个“非常多”并不是指PC里面GB级的数据,而是云服务器中TB级、PB级的数据。因此,深度学习的商业化应用诞生在Google、Amazon、Microsoft这样拥有海量数据的“大公司”也就合情合理了。

(2)为了能够在可接受的时间内对这些庞大的数据进行计算,需要设计一个全新的计算架构(当然,也可以用一台普通的PC来计算这些数据,但仅仅等待就有可能花去非常长的时间)。前面提到GPU并行计算的性能远远大于CPU并行计算(这并不是说GPU强于CPU,而是说GPU更适合处理琐碎的深度学习计算),因此设计一个基于GPU并行计算的软件平台则是当务之急。

(3)人工神经网络是一个能够学习、能够总结归纳的系统,也就是说,它能够通过已知数据来学习和归纳总结(看完此书以后,你将会理解我说的这种基于数据的学习)。人工神经网络通过已有数据和设定目标之间的联系,能够自我迭代训练,从而产生一个在具体问题(取决于你设定的目标)上的“类人”判断系统。

TensorFlow正是在这样的背景下产生的。一方面,Google的云服务器中拥有大量的优质训练数据;另一方面,它还是一个喜欢新鲜事物、追求创新的公司。2011年,Google开发了一个深度学习基础架构DistBelief。2016年,Google在其之上加以改进,开源了现在称为TensorFlow的深度学习架构。其命名来源于这一架构本身的运行原理:Tensor(张量)意味着N阶张量,Flow(流)意味着以张量数据为基础的数据流图(DataFlow)的计算。TensorFlow可以简单地表述为:张量从数据流图的一端流动到另一端的计算过程。前文所述的深度神经网络则可以通过这个数据流图进行模拟。这样,TensorFlow就可以将复杂的数据结构通过深度神经网络进行训练,并实现某种程度上的“人工智能”。

现在我们知道了TensorFlow由“Tensor”和“Flow”两部分组成。为了更深入地了解TensorFlow,我们分别了解“Tensor”和“Flow”的内涵。

Tensor指的是张量,即多维数组。可以用“阶”来定义数组的维度。例如,0阶数组是一个常值张量;1阶数组是一个向量;2阶数组是一个矩阵,依次类推,见表1-1。

表1-1 张量

阶数

表示的量

数组的表示方法

空间想象

0

常量

s=1

一个点

1

向量

s=[1, 2, 3]

一条线

2

矩阵

s=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

一个平面

3

3阶张量

t=[[[1], [2], [3]], [[4], [5], [6]], [[7], [8], [9]]

一个立方体

n

n阶张量

……

……

这样做的好处是什么呢?一个n阶张量可以存储一个大批量的数据。例如,现在有一张像素为28×28的图片,我们可以用一个28×28的二阶张量来表示它(见图1-8)。

这对于黑白图片是可行的。假设这张图片是彩色的,有3个颜色通道(channel),则可以增加一个维度,用一个3阶张量来表示它(见图1-9)。

图1-8 用28×28的二阶张量表示28×28像素的图片

图1-9 用3阶张量表示彩色图片

那么,如何把100张这样的图片打包成一批(batch)呢?可以用一个4阶张量,将这100个图片都包含进去(见图1-10)。

图1-10 用4阶张量表示100张彩色图片

这样就可以将100张图片的所有信息包含在一个张量当中了。原则上,只要内存足够大,就能够放足够多的数据进去。那么,这样做的好处是什么呢?张量化处理可以很明晰地将大量数据打包成一批一批的训练文件,同时还不用花太多的时间像程序员那样定义各种各样的变量,设计如何更好地使用、回收内存,以及如何更好地循环等工作(这是一件既烦琐又容易出错的工作),TensorFlow已经将这部分工作封装好并以简单的方式呈现给你了。

Flow指的是流动,即Tensor在数据流图中的流动。在TensorFlow中,一切的计算都是以矢量的计算方式从模型的一端流向模型的另一端。

这样描述有一些抽象,我们举一个例子:假如现在有两个矢量T1(tensor-1)和T2(tensor-2),对它们做一个矢量乘法(叉乘),并将结果放在矢量output_tensor中,那么在TensorFlow中定行就如图1-11所示。

图1-11 矢量相乘示意图

假如现在有10000个T1T2进行10000次计算,就表现为Tensor从一端向另一端的数据“流动”,而整个计算过程也展现为一个“数据流图”。

事实上,在TensorFlow中,神经网络模型也表现为一个数据流图,如对一个简单的拥有两个隐层的神经网络而言,其模型见图1-12。

图1-12 拥有两个隐层的神经网络

TensorFlow会将它表现为一个数据流图的形式,它以矢量数据流的形式进行计算,这是一种所见即所得的计算架构。

现在你是不是开始对TensorFlow有一些了解了?下面看一下这种数据流图到底有什么作用。

假设你是一名地产商,掌握着10万套房子的面积和房价信息(为了简化问题,假设房产的价格大致与面积呈线性相关),在你的数据库中有表1-2所示的一组数据。

表1-2 房屋面积与房价信息

序号

面积/m2

房价/万元

1

60

240

2

65

255

3

70

280

4

89

355

5

120

480

100000

150

600

由于假设房屋面积与房价大致呈线性相关,因此大致可以画成如图1-13所示的图。

图1-13 房屋面积与房价的线性关系

现在你希望利用TensorFlow,并通过目前已经掌握的数据,来训练得到一个只要输入房屋面积就可以大致得出对应房价的模型。显然,可以建立一个简单的一元线性方程来拟合这段数据。这个一元线性方程就是一个简单的模型。这里的wb就是模型的参数,我们通过输入训练数据来对参数进行学习。

显然,一开始我们并不知道wb的值应该是多少,那么就随机赋予它们一个值,继而产生一个初步的模型。当输入一个面积x时,就可以通过这个初步的模型产生一个临时的预测值。然后怎么办呢?我们希望这个模型的预测值与面积对应的真实价格y尽可能接近,因此可以设计这样一个损失函数,如果函数L的值越小,就说明预测值越接近真实值,模型的预测也就越准确。

设计好了该模型,就可以着手搭建一个TensorFlow计算流图:首先将房屋面积数据用矢量打包好,由于只有面积一个维度的数据需要考虑,因此建立一个0阶的常值矢量即可,假定为x;同样,将参数(这里也就是一元线性方程的斜率和截距)也使用常值矢量表示;房价y也是一个一维数据,建立一个常值矢量y;最后,建立一个函数L,显然这也是一个常值函数。

生成的计算流图如图1-14所示。

图1-14 房屋面积和房价的计算流图

然后,使用一个称为梯度下降的算法来对wb进行参数微调(这是一个经典的算法,将会在后面章节中详述。简单地说,就是微调参数wb,使函数L不断地变小。我们将房屋面积的仗量值从计算流图的一端(x)流向另一端(函数L),根据函数L的梯度修正wb,直到L收敛为止。最终的结果是预测矢量非常接近于真实房价y,模型也就训练完毕了,如图1-15所示。

图1-15 最终的预测矢量

现在再考虑一下计算效率问题。假设计算机一次可以完成100个房价的计算,那么如果只是采用单个房价矢量输入这样的“小水管”,显然就太浪费时间和计算资源了。我们可以将这100 000个数据分割成1000×100的数据集,即100个房价样本组成一个批次(batch),这样,输入1000个批次,就能完成计算(而之前必须输入100 000次,效率提升了100倍)。

如何操作呢?我们只需要增加每个矢量的维度,将批次考虑进去,此时的矢量乘法变成了二维的矩阵叉乘,最终将函数L修改为,即100个样本所产生的总值(也可以求其平均值)。

这样,我们用TensorFlow训练就完成了一个房屋面积-房价的预测模型,而这个计算流图本身就是TensorFlow。相信你已经领会到了TensorFlow的核心理念。

在正式学习深度神经网络和TensorFlow之前,首先看一下已有的成果。

图1-16 对黄瓜进行分类

图1-17 从高处拍摄的照片中寻找海牛

图1-18 医学扫描图

图1-19 火车动态追踪

图1-20 音乐创作示意图

图1-21 AlphaGo

TensorFlow有以下两种运行模式。

仅用CPU计算的配置比较简单,但计算模型速度很慢(这一点会在第9章详细介绍)。虽然使用CPU+GPU计算的配置较为烦琐,而且还需要显卡(GPU)支持CUDA开发套件,但计算效率会提升很多(例如,训练一个模型从几小时降到十几分钟)。

TensorFlow的官方文档中提供了非常详细的安装方法,下面介绍其中两种。

这里介绍一种基于Virtualenv的安装方法。Virtualenv可以搭建一个虚拟安装环境,在这个虚拟环境下安装不同版本TensorFlow所需要的依赖包,避免不同版本之间的混淆。具体安装步骤如下。

1)打开Terminal,安装pip和Virtualenv。

$ sudo apt-get install python-pip python-dev python-virtualenv 

2)创建虚拟环境。

$ virtualenv --no-site-packages ~/tensorflow

~/tensorflow指存放虚拟环境的位置。--no-site-packages指不使用系统自带的pip包。

3)激活虚拟环境(以后开启TensorFlow都要先激活虚拟环境)。

$ source ~/tensorflow/bin/activate

命令行左侧会出现一个表示TensorFlow的标志$,说明已经进入虚拟环境了。这样,你就可以随意地下载pip安装包,而不用担心pip安装的依赖之间出现版本冲突问题。

如何安装GPU版的TensorFlow

如果想使用GPU版的TensorFlow,那么必须先配置好CUDA和CUDNN两个依赖:

要安装CUDA,可以在nvidia网站找到参考资料。

要安装CUDNN,可以在nvidia网站找到参考资料。

安装TensorFlow的命令如下。

(tensorflow)$ pip install --upgrade tensorflow # 这是使用CPU计算的版本
(tensorflow)$ pip install --upgrade tensorflow-gpu # 这是使用GPU计算的版本

pip会安装一系列运行TensorFlow所依赖的库。

如果下载的速度有点慢,那么可以使用Ctrl+C组合键终止安装,然后使用国内的pip镜像安装,例如:

(tensorflow)$ pip install --upgrade tensorflow -i https://pypi.douban.com/simple/

4)确认是否安装成功。

进入Python环境,然后使用import tensorflow查看TensorFlow是否安装成功。如果没有报出什么错误,就说明安装成功了。

(tensorflow)$ python
>>import tensorflow
>>

5)退出虚拟环境。

(tensorflow)$ deactivate

基于Mac OS安装TensorFlow的步骤如下。

1)打开Terminal,安装pip和Virtualenv。

$ sudo easy_install pip
$ sudo pip install --upgrade virtualenv

2)创建虚拟环境。

$ virtualenv --no-site-packages ~/tensorflow

~/tensorflow指存放虚拟环境的位置。--no-site-packages指不使用系统自带的pip包。

3)激活虚拟环境(以后开启TensorFlow都要先激活虚拟环境)。

$ source ~/tensorflow/bin/activate

然后命令行左侧会出现一个(TensorFlow)的标志$,说明已经进入虚拟环境了。这样你就可以随意下载pip安装包,而不用担心pip安装的依赖之间出现版本冲突问题。

如果想使用GPU版的TensorFlow,还必须先配置好CUDA和CUDNN两个依赖。

通过以下命令安装TensorFlow。

(tensorflow)$ pip install --upgrade tensorflow # 这是使用CPU计算的版本
(tensorflow)$ pip install --upgrade tensorflow-gpu # 这是使用GPU计算的版本

pip会安装一系列运行TensorFlow所依赖的库。

如果下载的速度有点慢,可以使用Ctrl+C组合键终止安装,然后使用国内的pip镜像安装,例如:

(tensorflow)$ pip install --upgrade tensorflow -i https://pypi.douban.com/simple/

首先,打开虚拟环境和Python环境:

$ source ~/tensorflow/bin/activate
(tensorflow)$ python

然后,尝试编写一个HelloWorld程序:

>> import tensorflow as tf
>> hello = tf.constant('Hello, TensorFlow!')
>> sess = tf.Session()
>> print(sess.run(hello))

由于TensorFlow是一个执行计算图的软件,因此编写一个HelloWorld程序会显得比较麻烦,但用它来做计算非常便利。下面就可以开始深度学习之旅了!


机器学习可以理解成“让机器自己给自己编程的过程”。如果编程是一种逻辑思维的自动化实现,那么机器学习就是让这个实现自动化的过程也自动化。编写程序是软件开发中的一个瓶颈,因为我们没有足够多、足够好的开发者。如果让数据而不是人在开发中起更大的作用,那么我们就可以设计出更多针对不同场景、不同任务情况下的程序,如图2-1所示。

图2-1 传统编程与机器学习示意图

更加具体地分析,我们说的学习到底是什么意思呢?Mitchell提供了一个简洁的定义:“对于某类任务T和性能P,一个计算机程序被认为可以从经验E中学习。通过经验E的改进后,它在任务T上的性能度量有所提高。”如果把机器学习模型想象成一个黑箱模型,那么它主要就是通过任务T、经验E和性能P这3个接口与外界进行交流。

我们可以通过画一张形象的图来帮助理解它们之间的关系,如图2-2所示。

我们可以通过一个国际象棋的程序来更好地理解机器学习算法和传统算法之间的差异。

从任务和性能度量上来说,机器学习和传统编程并没有什么本质区别,任务都是设计一个可以下国际象棋的程序,性能则可以通过对弈的胜负来度量。它们最大的区别就在于是根据人的规则来优化程序还是根据对弈的经验来自己优化程序。

从传统程序设计的角度讲,一个国际象棋游戏就是设计一个能不断推演各种下法,并找到一个可以在N步之内击败对方的程序。具体地说,就是通过对游戏规则的理解,设置合适的剪枝策略,在计算未来若干步的情况后,在对自己最有利的位置落子。如果从机器学习的角度来设计,那么我们会通过过去已有的对弈棋谱或程序不断尝试下棋并从失败中吸取教训的方法取得一个程序,而给程序设定的目标就如图2-3所示:①最终获得胜利;②每落一步子的获胜概率最大。

图2-2 黑箱模型

图2-3 国际象棋程序示意图

机器学习是实现人工智能的一种途径。换言之,可以用机器学习作为手段,解决人工智能中的问题。从划分类别的角度来说,机器学习可以分为监督学习、无监督学习和增强学习。监督学习是目前最成功也是最成熟的领域,由于篇幅有限,本书只对监督学习进行介绍,下文提到的机器学习都可以狭义地理解为监督学习。

一些常用的任务有以下几种。

输入信息原本属于某个类型,机器学习模型要将输入的信息输出成对应的分类结果(离散的类)。例如,物体分类就是将图片输出成这张照片所属的分类,如猫和狗。

对于一个输入信息,机器学习模型需要预测其对应的结果。例如,输入一套商品房的位置、户型、面积,预测它的房价,如图2-4所示。与分类不同,回归的结果是一些连续的值,而不是一个个离散的类别。

图2-4 输入商品房信息预测其房价

扫描印刷体并转成电子文档,转换声音为文字等。

将一系列语言符号转换成另一种语言符号。

无人车通过雷达、摄像头等技术感知环境,并完成合理的驾驶操作。

……

假如我们将房子的面积当成自变量,将房屋的价格当成因变量,那么机器学习可以通过线性回归学习得到一个根据房子面积来预测其房价的模型。机器学习模型就是回归曲线,图中已有的样本就是经验数据。

为了评估机器学习算法的性能,针对特定的任务T,使用量化指标对其进行考核。下面介绍几个衡量模型性能的常用指标。

1.混淆矩阵

例如,对于一个分类任务,可以用混淆矩阵来可视化地评价算法性能。它以预测的类为列,以实际的类为行,很好地评价了模型中的误判情况。如果想用一个模型来辨别猫、狗和兔子之间的不同,那么混淆矩阵就能帮助我们评价算法,并指明未来努力的方向。假设有一个包含27个动物的样本,其中包含8只猫,6只狗,13只兔子,运行预测算法后的结果见表2-1。

表2-1 预测算法后的混淆矩阵

在这个混淆矩阵中,实际上,一共有8只猫,而只有5只猫被正确识别,另外3只猫被识别成狗。从混淆矩阵,可以看出算法在识别猫、狗之间存在瓶颈,但是它已经能很好地识别猫和兔子之间的不同了。在混淆矩阵中,所有正确的预测都位于对角线上,那么可视化地探索误差就成了一件很简单的事。

2.混淆表

在预测分析中,混淆表有助于更细致地分析模型对每一种类别的分类情况。它有两行两列,以阴阳性为横轴,若预测结果是目标类,则为阳性,反之为阴性;以真假为纵轴,结果正确为真,反之为假。比如,以上面的混淆矩阵为例,针对猫这个类,可以得到表2-2。

表2-2 混淆矩阵中“猫”的混淆表

猫 的 分 类

非猫的分类

5个真阳性(猫被正确分类为猫)

3个假阴性(猫被错误地分类为其他动物)

两个假阳性(其他动物被错误地分类为猫)

17个真阴性(其他动物被正确地分类为非猫)

根据混淆表,可以计算出几个常用的指标来评价模型。

准确率大体上评价了模型的准确情况。

精准率又称查准率,表明了分类器检测其为目标的准确性。

召回率又称查全率,表明了分类器能否把所有的目标都检测出来。

可以用一个简单的例子来说明准确率、精准率和召回率如图2-5所示。

图2-5 准确率、精准率、召回率示意图

假设我们将白色小球定义为阳性样本,黑色小球定义为阴性样本。用一个分类器将它们筛分到属于阳性分类的小筐和阴性分类的小筐。如果白色小球被分入阳性分类筐,认定为真阳性;如果黑色小球被误分入阳性小筐,认定为假阳性;同样,若黑色小球被分入阴性分类小筐,认定为真阴性;若白色小球被误分入阴性分类小筐,认定为假阴性。就可以通过计算小球的数量来得到准确率、精准率和召回率。可以发现,这3个性能指标反映了不同的分类性能。准确率反映了筛分器对两类小球的分类性能,如果两种小球有一个分不清,准确率则不会很高;精准率反映了筛分器对某一分类(如阳性)的分类性能,如果阳性小筐中流入了很多黑色小球,精准率则不会很高;召回率反映了阳性样本总体的分类情况,如果阳性小球很多被误分到阴性小筐中,召回率则不会很高。

3.误差

拥有了一些基本的评价准则后,我们首先要将注意力转到模型误差的来源上。总的来说,模型的误差(error)分为偏差(bias)和方差(variance)两大类。

可以画一张打靶图来可视化偏差和方差的区别。靶心是一个预测正确的模型,点离靶心越近,预测结果就越好。假设重复模型预测多次,因为向前传播中存在许多随机因素,所以在靶子上会得到许多独立的命中。有时候,可以得到很好的结果,既能很好地正确预测,又能有很好地聚集情况,但有时候又充满了异常值。可以绘制4种不同的情况,分别表示高低偏差和方差的组合情况,如图2-6所示。

图2-6 高低偏差和方差的组合示意图

处理偏差和方差的根本在于处理过拟合与欠拟合。随着模型中的参数越来越多,模型的复杂度不断上升,偏差越来越小,但方差不可避免地增大。因此,为了降低整体的误差,需要权衡误差与偏差,得到一个最优的复杂度。

如图2-7所示。如果模型复杂度(model complexity)超过了最优复杂度,那么这个模型实际上是过拟合的,它太过相信我们所拥有的数据,却忘了用来训练的样本往往是真实数据中一个很小的子集或者包含一定的噪声数据,不能很好地反映全部数据的真实分布。通过对样本所有信息的拟合,虽然降低了偏差,却导致了较大的方差,这样模型的泛化能力便大大下降,因为模型主观臆想了一些并不存在的关系。如果模型的复杂度小于最优复杂度,那么模型不能很好地洞察数据之间的关系,这样就导致最后结果的偏差过大。

我们根据二次函数再加上一些小的随机数生成一系列的数据点,现在需要拟合这些数据点以得到一个模型。通过观察数据点的分布,我们知道使用一个二次函数去拟合会得到更好的结果。如果减小模型的复杂度,可能导致模型的误差过大,不能发现数据点之间实际的关系。如果为了拟合现有的数据点,不断增加函数的次数,使模型能够精准地通过每一个已知的数据点,最终却会导致最后的模型太过复杂而把一个二次函数拟合成了奇奇怪怪的曲线。造成这个现象的主要原因是,我们对于已知的数据太过自信,而忽略了已知数据本来就自带的偏差。这样模型的泛化能力(这个在后面讲)就很差了,就是说,在这个例子中,如果我们得到更多的数据点,二次模型的误差可能会比高次模型的误差更小(如图2-8所示)。

图2-7 偏差与方差随模型复杂度变化的示意图

图2-8 欠拟合、过拟合与刚刚好的示意图

4.泛化能力

形象地说,学习算法(模型)就好比大小不一的水杯,我们用经验之水来灌满它。局部拟合就好比中间出现了漏洞,导致怎么都灌不满。如果水杯很大(模型规模大、参数多),我们就要用很多的水(经验数据)灌满,这样刚刚好,模型的泛化能力才强(见图2-9a);如果模型太大,水量不足,吃不饱,就会产生过拟合,模型的泛化能力降低(见图2-9b);如果杯子太小,水太多,溢出来了,就会产生欠拟合,即模型不足以学习到整个样本空间(经验数据)中的知识(见图2-9c)。

图2-9 刚刚好、过拟合、欠拟合及其对应的泛化能力示意图

基于给定的数据集是否有标签,可以将机器学习算法归类为有监督学习和无监督学习。对于大多数机器学习算法,经验就是遍历整个数据集。

数据集就是学习样本的集合,每个样本中含有许多特征,这些特征代表了这个样本的所有信息。例如,对于一张像素为28×28的黑白照片,如果在每个像素点上用0代表白、用1代表黑,那么这张照片的特征(feature)数就是一个由28×28=784个特征组成的向量(或者一阶矢量),每个特征的取值范围是0或1,每张照片代表一个样本(sample)(见图2-10)。如果你有1000张这样的照片,你的样本空间就是这1000张照片的集合(dataset),也就是含有784个特征的1000个不同样本的集合。

图2-10 28×28像素黑白照片的特征表示

这里,算法(Algorithm)是指输入样本空间到输出样本空间的映射。学习算法(Learning Algorithm)就是为了得到正确的映射关系,我们必须从经验中学习。算法可以当成一个黑盒子,一端是输入的样本空间,另一端是样本的输出空间。如果说上一节讨论的是黑箱与外界的接口,本节就讲述如何把黑箱打开,看看里面到底有什么东西。

用公式表示,学习算法=表示+评价+优化。

所有的机器学习算法都由3部分组成,分别是:表示(Representation)、评价(Evaluation)、优化(Optimization)。

表2-3列举了一些常见的机器学习算法的表示、评价和优化。

表2-3 常见的机器学习算法的表示、评价和优化

表示R

评价E

优化O

基于实例的方法

准确/错误比率

组合优化

近邻法

精准率和召回率

贪心搜索

支持向量机

平方误差

线性规划

超平面方法

似然

分支界限法

朴素贝叶斯

后验概率

连续优化

Logistic回归

信息增益

无约束

决策树方法

K-L距离

梯度下降

神经网络

成本/效用

拟牛顿法

贝叶斯网络

利润

狭义地讲,机器学习就是给定一些训练样本(xi, yi)(其中,xi是输入,yi是需要预测的目标),学习一个输入到输出的决策函数f(·),这一决策函数由模型来表示。模型属于输入空间到输出空间映射的集合,这个集合就是假设空间。机器学习的示意图如图2-11所示。

图2-11 机器学习的示意图

这里,是模型的输出,为决策函数的参数,表示样本x对应的特征表示。因为x不一定是数值型的输入,所以需要通过x转换为数值型的输入。如果假设x是已经处理好的标量或向量,也可以直接写为

由上一小节可知,表示实际上就是纷繁复杂的机器学习模型,本书的重心就在于探讨神经网络这个模型。

我们需要一个针对学习算法的评价函数,用评价函数来评价学习算法所得到模型的优劣。注意,机器学习算法内部使用的评价函数和前面所指的性能P是两个概念,这里的评价E是用于优化机器学习模型内部的参数。下面通过分析整个参数优化的流程来区分评价E和性能P,如图2-12所示。

首先,把数据集合分为训练集、交叉验证集和测试集。训练集用于训练模型,交叉验证集用于调节超参数(机器学习模型中的框架参数,如训练迭代次数、每一次参数调整的程度等),测试集用于最后整体模型质量的检验。然后,初始化参数并开始训练,根据评价E产生的训练误差调整参数。最后,得到一个较优的模型用来测试性能P。

图2-12 参数优化流程

在这个训练的流程中,2.1.3节中提到的性能P指的就是最后测试集的结果,而学习算法中的评价E就是评价模型参数好坏的方法。虽然这两种方法可能是相似的,但目的有很大的区别,性能强调的是这个学习算法在整个任务T上的表现,评价E强调的是学习算法中参数的好坏。

因此我们还要建立一些准则来衡量决策函数的好坏,也就是评价E。在很多机器学习算法中,一般定义一个损失函数Ly, f(x, )),然后在所有的训练样本上评价决策函数的风险。

可以这样理解这个公式。

现在我们有了表示R(定义了各种模型参数)、评价E(基于模型参数,样本产生的结果与其真实值的差异程度),显然,还需要一个通过调整参数将这个差异程度变小的方法,也就是优化O。如果所有样本的经验丰富,风险降低到最小并不再发生显著变化(例如,收敛),我们就得到了一个良好的预测模型。

因此用对参数求经验风险来逐渐逼近理想的期望风险最小值,就是我们常说的经验风险最小化原则(Empirical Risk Minimization)。这样,我们的目标就变成了找到一个参数使得经验风险最小。

我们希望将这个差异最小化,也就是尽量将预测值贴近真实值,就需要不断地向这个过程里放入样本输入值x,并判断预测值和真实值的差异。通过不断地输入训练样本数据集,就得到了损失函数的“经验”风险。如果经验风险(也就是损失函数的值)不是最小,那么就改进决策函数中的参数θ,直到经验风险收敛,如图2-13所示。

图2-13 经验风险最小化流程图

改进决策函数中参数的方法,就是优化O的实质,也可以称为参数学习算法。

参数学习算法就是如何从训练集的样本中,自动学习决策函数的参数。不同机器学习算法的区别在于决策函数和学习算法的差异,相同的决策函数可以有不同的学习算法。下面介绍一种常用的参数学习算法:梯度下降法(Gradient Descent Method)。梯度下降法也叫最速下降法,如果一个实值函数f(x)在点a处可微且有定义,那么函数f (x)在a点沿着梯度相反的方向下降最快。梯度下降法经常用来求解无约束优化的极值问题。其迭代公式为:

其中,是梯度方向上的搜索步长。

为一个足够小的数值时,f (ak+1)≤f (ak),我们可以从一个初始值x0开始,并通过迭代公式得到x0, x1, x2,…,xn,最终xn收敛到期望的极值。

因此在机器学习问题中,可以通过这种方法学习到最优参数,使得风险函数最小化。

如果用梯度下降法进行参数学习:

也称学习率。

此时的决策函数已经能够较好地模拟训练数据集中xiyi的关系,我们将它作为一个机器学习的模型,用另一组测试数据集进行测试。一般来说,将训练数据集中的一部分数据提取出来,作为测试数据集。将正确预测的结果数除以整个测试数据集的样本数,就能够评价模型与模型之间的好坏。

当然,真正的测试数据集乃是真实的世界,但目前我们的计算机算力之弱、已掌握并存储的训练数据之少,还很难得到一个可以统筹真实世界的模型。但在一些专一、细分的领域(从识别手写数字到下围棋)机器学习已经能够得到一个比人脑识别更好的结果。

以机器学习中最为基础的算法——线性回归(Linear Regression)为例,本节介绍机器学习的整个流程(T+E+P→R+E+O)。

回归是监督学习的一个重要问题,回归用于预测输入变量(自变量)和输出变量(因变量)之间的关系,特别是当输入变量的值发生变化时,输出变量的值随之发生变化。回归模型正是表示从输入变量到输出变量之间的映射函数。

线性回归的任务就是函数拟合:选择一条函数曲线使其很好地拟合已知数据且很好地预测未知数据。具体地说,线性回归的任务是:在一个由x轴和y轴组成的二维平面上,给定一些已知的xiyi的映射关系。机器学习要探索给定的这种关系(也就是经验E),学习其规律,并可以成功地预测一个未知xk对应的映射值yk

首先,要给定训练数据集T(经验E):

其中,是输入,是对应的输出,

在这组数据中,代表一个样本。T是样本的总集,也就是数据集,我们将这个数据集作为经验E。也可以用另一种方法来表示。机器学习是将样本空间中的向量(vector)作为输入,映射到输出空间中的常量(scalar)

由于线性回归的任务是拟合xy的映射,因此前面所给定的数据集可以用如下方式表达:把每个样本视为拥有一个维度特征的一个向量,对应输出空间中拥有一个维度特征的一个常量y,每个样本可以表示为,整个数据集则是由n个样本组成的向量集合。这样表达的好处是可以更深刻地反映出样本空间向输出空间的映射关系,并且每个样本可以直观地表示多个维度的特征,理解这一点非常重要。在这个例子中,输入空间实质上是由一维特征组成的向量集,在x轴上表达为x轴上的取点。而其输出空间是x对应的y值,我们将经验E展开到二维平面上则表现出xy映射所产生的点集。

在了解了最基本的情况后,还可以对数据集的不同情况做如下深入探讨。

回到这个给定的数据集T,可以将其表示为样本组成的点集:,这里n代表第n个样本。

也可以将输入空间和输出空间分开表示:y可以是一个连续量,也可以是一个离散量。也可以用一个图表来表示,见表2-4。

表2-4 输入空间与输出空间

x

y

x1

y 1

x 2

y 2

x n

y n

在TensorFlow中,先使用numpy生成一段数组来模拟生成一段训练数据和测试数据。首先,打开文本编辑器(如Sublime Text),新建一个Python(.py)文档(“#”符号后面的文字是注释)。

import numpy
import TensorFlow as tf
# import的功能是导入Python的一个库numpy,它可以用来生成和处理各种格式的数组
# import TensorFlow as tf 导入了TensorFlow,"as tf" 是TensorFlow的简写,这样就可以使用tf来直接调用TensorFlow了,也可以使用import numpy as np来简写numpy
# 生成训练数据
train_X = numpy.asarray([3.3,4.4,5.5,6.71,6.93,4.168,9.779,6.182,7.59,2.167, 7.042,10.791,5.313,7.997,5.654,9.27,3.1])
# train_Y可以看成对应数据X的“真实”值
train_Y = numpy.asarray([1.7,2.76,2.09,3.19,1.694,1.573,3.366,2.596,2.53, 1.221,2.827,3.465,1.65,2.904,2.42,2.94,1.3])
# 生成测试数据
test_X = numpy.asarray([6.83, 4.668, 8.9, 7.91, 5.7, 8.7, 3.1, 2.1])

test_Y = numpy.asarray([1.84, 2.273, 3.2, 2.831, 2.92, 3.24, 1.35, 1.03])

作为线性回归学习的表示R,其映射方程为:

这里,也是一个参数向量(parameter vector),分别对应输入特征

参数向量w和常值b是这个模型的参数,w实质是将来自样本空间的特征分别与对应的系数相乘并求和,系数也可以看成决定不同特征对系统影响的权值,且的影响与呈正相关关系。

学习系统基于训练数据构建一个机器学习算法的模型,即决策函数。对新的输入,预测系统根据学习的模型确定相应的输出

联系上一节提到的决策函数,线性回归表示R的决策函数为 是第i个样本的经验E,wb是决策函数的参数。

x可以是一个n维的向量。wb也可以是一个n维的向量。此时的决策函数应该为,就是向量wx的点乘再加上常值b

在TensorFlow中,这样写这个公式:

# 这里直接使用TensorFlow的缩写tf来使用TensorFlow
# tf后面的"."代表调用TensorFlow下面的类库
# 可以在www.tensorflow.org下面的API列表中找到每个类库的说明文档
# pred是一个你自己命名的变量,这里的pred代表predition,即预测之意
pred = tf.add(tf.mul(x,W),b)

也可以用:

pred = tf.mul(x,W) + b

如果xW是一个矩阵,那么:

pred = tf.matmul(x,W) + b

对于一个给定的训练集,我们需要一个损失函数用于评价模型的拟合情况。由于线性回归的特殊性,损失函数的形式与性能P的评价函数很类似,区别在于评价函数的经验E来自于训练集:

结合上一节给出的代价函数形式,可以得到在一个样本上的损失函数:。联系上一节给出的决策函数的经验风险函数:

可以得到在给定的经验E上的风险函数:

式中,N是样本个数,这里使用1/2是为了方便后面的求导。显然,这样的处理不会对经验风险带来影响。

在TensorFlow中,我们这样表示均方损失函数:

# cost代表损失之意
cost = tf.reduce_sum(tf.pow(pred - Y, 2)) / (2 * n_samples)

我们使用梯度下降法调整参数wb,使经验风险最小化。

现在要调整wb,使得R( w,b)取得最小值。为了达到这个目标,可以对wb取一个随机初始值(随机初始化的目的是使w、b的对称性失效),然后不断迭代改变wb的值,使减小,直到最终收敛,取得一个wb,使得最小。

可以通过图2-14来理解梯度下降的原理。

图2-14 梯度下降的示意图

图2-14中,横轴为w,纵轴为b,竖轴为R(w, b),我们的目的是最小化这个竖轴的函数,即找到图像的最低点。假设从图中随机的一点开始出发,梯度下降所做的,就是环顾四周,找一个最陡峭的方向,然后沿着这个方向下降,重复上述的行为,直至无法继续下降。由梯度下降的步骤,我们知道选择梯度下降未必能保证全局最优,因为不恰当的初始点可能会让算法在局部最优停止。

总体上来说,梯度下降法就是利用梯度来为我们找到一个最陡的下降方向,然后向那个方向前行,因此梯度下降也叫作最速下降。下面对这个更新参数(下降)的方式进行深入探讨:

重复以下流程,直至收敛。

这个式子中有以下几个要点需要注意。

●  是对损失函数求偏导的部分。

● 代入上文定义的损失函数:

可得

根据梯度下降时训练样本的数量,可以把训练的过程分为批量梯度下降和随机梯度下降。如果每次迭代都考察训练集的所有样本,就称为批量梯度下降;如果每次迭代使用随机的单个训练样本,则称随机梯度下降。

在TensorFlow中,这样表示优化过程:

learning_rate = 0.01
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)

本章总结了机器学习的6个知识点,它们可以简化为如图2-15所示的流程。

图2-15 机器学习流程

直接把下面这段代码粘贴到文本中,例如,创建linear_regression.py的Python文本。然后在命令行下执行:

python linear_regression.py

# TensorFlow实现线性回归
# 作者: Aymeric Damien
# 来自开源项目:https://github.com/aymericdamien/TensorFlow-Examples/

from __future__ import print_function

import tensorflow as tf
import numpy
import matplotlib.pyplot as plt
rng = numpy.random

# 模型的超参数
# 学习率,即步长
learning_rate = 0.01
# 训练迭代次数
training_epochs = 1000
display_step = 50
# 模拟生成一段训练数据
Training Data
train_X = numpy.asarray([3.3,4.4,5.5,6.71,6.93,4.168,9.779,6.182,7.59,2.167,
             7.042,10.791,5.313,7.997,5.654,9.27,3.1])
train_Y = numpy.asarray([1.7,2.76,2.09,3.19,1.694,1.573,3.366,2.596,2.53,1.221,
            2.827,3.465,1.65,2.904,2.42,2.94,1.3])
n_samples = train_X.shape[0]

# 创建placeholder,这会在第9章中说明
X = tf.placeholder("float")
Y = tf.placeholder("float")

# 创建模型参数作为变量
W = tf.Variable(rng.randn(), name="weight")
b = tf.Variable(rng.randn(), name="bias")

# 创建决策函数
pred = tf.add(tf.mul(X, W), b)

# 创建损失函数
cost = tf.reduce_sum(tf.pow(pred-Y, 2))/(2*n_samples)

# 创建优化方法
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)

# 模型参数初始化,这会在第9章中说明
init = tf.initialize_all_variables()

# 创建计算图
with tf.Session() as sess:
  sess.run(init)

  # 创建迭代训练循环
  for epoch in range(training_epochs):
    for (x, y) in zip(train_X, train_Y):
      # feed_dict是将数据放入模型的方法
      sess.run(optimizer, feed_dict={X: x , Y: y})

    # 每隔50次显示一下损失函数的值
    if (epoch+1) % display_step == 0:
      c = sess.run(cost, feed_dict={X: train_X, Y:train_Y})
      print("Epoch:", '%04d' % (epoch+1), "cost=", "{:.9f}".format(c), \
        "W=", sess.run(W), "b=", sess.run(b))

  print("Optimization Finished!")
  # 训练结束后,显示一下最终的损失函数值
  training_cost = sess.run(cost, feed_dict={X: train_X, Y: train_Y})
  print("Training cost=", training_cost, "W=", sess.run(W), "b=", sess.run(b), '\n')

  # 用matplotlib库生成一幅图
  plt.plot(train_X, train_Y, 'ro', label='Original data')
  plt.plot(train_X, sess.run(W) * train_X + sess.run(b), label='Fitted line')
  plt.legend()
  plt.show()

  # 生成一段测试数据
  test_X = numpy.asarray([6.83, 4.668, 8.9, 7.91, 5.7, 8.7, 3.1, 2.1])
  test_Y = numpy.asarray([1.84, 2.273, 3.2, 2.831, 2.92, 3.24, 1.35, 1.03])

  print("Testing... (Mean square loss Comparison)")
  # 现在用训练好的参数根据test_x预测对应的y
  testing_cost = sess.run(
    tf.reduce_sum(tf.pow(pred - Y, 2)) / (2 * test_X.shape[0]),
    feed_dict={X: test_X, Y: test_Y}) # 使用的模型是一样的,此时参数已经不同了
  print("Testing cost=", testing_cost)
  # 比较一下训练的损失函数值和测试损失函数值
  print("Absolute mean square loss difference:", abs(
    training_cost - testing_cost))

  plt.plot(test_X, test_Y, 'bo', label='Testing data')
  plt.plot(train_X, sess.run(W) * train_X + sess.run(b), label='Fitted line')
  plt.legend()
  plt.show()

本章简述了机器学习的基本概念,为后续引入深度学习的知识创造了条件。本章要点如下。


相关图书

ChatGPT原理与应用开发
ChatGPT原理与应用开发
深度学习的数学——使用Python语言
深度学习的数学——使用Python语言
深度学习:从基础到实践(上、下册)
深度学习:从基础到实践(上、下册)
动手学深度学习(PyTorch版)
动手学深度学习(PyTorch版)
深度学习与医学图像处理
深度学习与医学图像处理
深度强化学习实战:用OpenAI Gym构建智能体
深度强化学习实战:用OpenAI Gym构建智能体

相关文章

相关课程