书名:深度学习入门与TensorFlow实践
ISBN:978-7-115-57533-3
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
著 林炳清
责任编辑 谢晓芳
人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
网址 http://www.ptpress.com.cn
读者服务热线:(010)81055410
反盗版热线:(010)81055315
本书首先介绍深度学习方面的数学知识与Python基础知识,线性模型中的线性回归模型和logistic模型;然后讲述正向传播算法、反向传播算法及深度神经网络的完整训练流程,输出层的激活函数和隐藏层的常见激活函数,深度学习的过拟合和欠拟合,应对过拟合的方法,以及使用TensorFlow 2建立深度神经网络模型的步骤;接着介绍卷积神经网络及其两个重要的组成部分—卷积和池化,以及如何使用TensorFlow 2建立卷积神经网络;最后讨论如何从零开始实现循环神经网络,如何搭建深度学习框架,如何使用TensorFlow 2建立循环神经网络模型。
本书既可供从事人工智能方面研究的专业人士阅读,也可供计算机专业的师生阅读。
人工智能时代已经到来。现在,智能手机不仅可以自动给照片美颜,还能理解人们说的话;配备了摄像头、超声波传感器和雷达的汽车可以主动减速或制动,从而保护行人和汽车的安全;AlphaGo在围棋中战胜了人类最优秀的棋手。未来,汽车可以不需要人类干预并完全实现自动驾驶,快递、外卖等可以通过机器人配送,医院可以实现自动诊断。实现这一切的核心技术之一便是深度学习。本书就是一本以深度学习为主题的书,目的是让读者深入地理解深度学习的相关内容。
市面上关于深度学习的书通常通过大量的数学公式来讲述算法和原理,常常忽略实现深度学习时必需的细节。看完这类书,读者在理论方面明白了深度学习中的算法,但是容易陷入困惑,不知道如何实现和应用所学习的算法。另外,为了更好地描述深度学习在不同场景下的应用技巧,一些关于深度学习的书通常只简单描述深度学习的基本原理和算法,然后讲述如何使用深度学习框架搭建网络模型并解决实际问题。这种方式可以帮助读者快速掌握和应用深度学习,但是由于读者缺乏对深度学习的基本原理和算法的理解,因此这种方式容易让读者陷入困惑,最终限制读者应用深度学习处理实际问题的能力。
本书旨在填补理论和应用的鸿沟,帮助读者更好、更快地掌握深度学习的算法和原理,并能够应用深度学习解决实际问题。本书将系统地介绍深度学习的各种基本算法和原理,并讲述如何使用Python中的函数从零实现这些算法。结合理论和实际代码,初学者可以更好、更全面地理解深度学习中的算法,打好坚实的基础。另外,本书还将介绍较流行的深度学习框架—TensorFlow 2。通过学习此框架,读者可以进一步理解深度学习的算法,同时学习在实践中如何搭建、训练和应用深度学习模型。
本书面向希望深入理解深度学习的读者,特别是对实际应用深度学习感兴趣的本科生、研究生、工程师和科研人员。本书不要求读者具有深度学习的很多相关知识,不要求读者具有专业的计算机编程经验。阅读本书要求读者掌握“线性代数”“微积分”“概率论与数理统计”这3门课程的一些核心概念和方法。这3门课程都是理工科学生的必修基础课,相信大部分读者在大学已经学习过。
本书共13章。
第1章简要介绍深度学习。
第2章讲述线性代数、微积分和概率论的相关知识,并且介绍Python编程的基础知识。
第3章讨论线性模型中的线性回归模型和logistic模型,并且介绍梯度下降法的3种变体—随机梯度下降法、全数据梯度下降法和批量随机梯度下降法。
第4章主要介绍正向传播算法、反向传播算法及深度神经网络的完整训练流程。
第5章主要介绍应用于输出层的激活函数,以及应用于隐藏层的激活函数。
第6章介绍深度学习的过拟合和欠拟合,以及4种应对过拟合的方法—使用早停法、L__2惩罚法、丢弃法和增加观测点。
第7章讲述如何使用TensorFlow 2建立深度学习模型。
第8章详细介绍卷积神经网络的两个重要组成部分—卷积和池化,以及如何从零开始建立卷积神经网络。
第9章讨论如何使用TensorFlow 2建立卷积神经网络,以及卷积神经网络的一些建模技巧。
第10章介绍循环神经网络,并描述从零开始实现循环神经网络。
第11章介绍如何搭建一个深度学习框架,这有助于读者更加深入地理解深度学习的原理和实现过程。
第12章讨论两个改良的循环神经网络模型—长短期记忆模型和门控循环单元模型。
第13章讲述如何使用TensorFlow 2建立循环神经网络模型。
阅读完本书,你将学会深度学习中基础和重要的知识,包括正向传播算法、反向传播算法、梯度下降法、激活函数、正则化、卷积神经网络、循环神经网络等。然而,我们希望这不是终点,而是学习深度学习的新起点!
以本书为基础,你可以继续学习深度学习,如优化算法(Adam、批量规格化等)、联邦机器学习、迁移学习、生成对抗网络(Generative Adversarial Network,GAN)、无监督深度学习等。我们相信深度学习不仅可以帮助你取得学业和事业上的成功,更可以帮助你收获信心和快乐!
你也可以找一个自己的兴趣点,看看深度学习是否可以提高效率,并且尝试去实现它。深度学习与任何其他工具一样,用得越多,我们就会越了解它的特性,也可以越好地应用它。
本书由异步社区出品,社区(https://www.epubit.com/)为您提供后续服务。
作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。
当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,单击“提交勘误”,输入勘误信息,单击“提交”按钮即可(见下图)。本书的作者和编辑会对您提交的勘误进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。
我们的联系邮箱是contact@epubit.com.cn。
如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。
如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区投稿(直接访问www.epubit.com/contribute即可)。
如果您所在学校、培训机构或企业想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。
如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接通过邮件发送给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。
“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。
“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、人工智能、测试、前端、网络技术等。
异步社区
微信服务号
毫无疑问,人类是地球上最具智慧的生物。一个重要体现是,人类可以做很多其他动物或者机器不能做的精细动作或者事情,如阅读、开车。人工智能的一个目的是让机器可以实现人类的某些能力,从而提高机器的工作效率,扩大机器的应用范围。我们如何让机器具有人类的某些智慧或者能力呢?一个自然的方法是,我们手把手教机器如何做。如图1-1所示,首先制定一套完成某项任务的规则(或称为算法),然后通过编程实现算法,最后输入指定值,通过算法得到输出值,依据输出值做出相应的决策。假设我们想让机器判断图片中的图形是一个矩形还是圆形,可以事先“告诉”(通过编程实现“告诉”的过程)机器判断矩形和圆形的标准或者规则。例如,我们已知由4条首尾相连的边组成且4条边的夹角均为90°的图形是矩形。在图形只包括矩形和圆形的情况下,判断矩形和圆形的规则如算法1.1所示。
图1-1 算法方式实现人工智能
| 算法1.1 在图形只包括矩形和圆形的情况下,制定判断矩形和圆形的规则 | 
|---|
| 如果图形由4条首尾相连的边组成且4条边的夹角均为90°, | 
另一个方法是,给机器提供很多矩形和圆形,让机器从中学习矩形和圆形的特征,进而做出判断。我们首先测量并记录这些图形的一些属性(属性也称为特征),如边的数量、夹角度数等,得到数据(该数据称为训练数据),如表1-1所示。
表1-1 判断矩形和圆形的训练数据
| 图形编号 | 特征 | 标签 | |
| 边的数量 | 夹角度数 | ||
| 0 | 4 | 90° | 矩形 | 
| 1 | 0 | 0° | 圆形 | 
我们将该训练数据提供给一个机器学习算法,然后该算法通过学习得出一个关于图形特征和形状的模型。当碰到一个新的图形时,测量其特征,然后将特征代入训练好的模型,模型将会自动判断新的图形是矩形还是圆形。对于一般情况,机器学习方法实现人工智能的流程分为3个步骤(见图1-2)。
图1-2 机器学习方法实现人工智能的流程
(1)收集和预处理数据。
(2)根据数据特点,选择适合的机器学习算法,使用数据训练模型。
(3)给定输入值,使用训练好的模型得到输出值(通常为一个预测或者判断),做出相应的决策或者行动。
总体来说,第一种方法比较直接,对一些相对简单的情况,它可以取得很好的效果,而且编程实现较快。然而,第一种方法在实际应用中有两点不足。首先,这种方法难推广。例如,如果进一步希望算法可以判断4种图形,如圆形、椭圆形、矩形和正方形,那么我们需要从头开始设计算法,重新编程。其次,对于较难的问题,我们有时会很难设计有效的算法。例如,判断图1-3的矩形框中是否为眼睛。可以试着想想,如何采用算法教一个小宝宝判断图1-3的矩形框中是眼睛。
图1-3 数字图像处理领域常用的标准图片
第二种方法基于机器学习算法,它可以有效克服第一种方法的缺点。首先,机器学习方法更易于推广。例如,如果已经建立了一个判断矩形和圆形的算法,现在,我们希望可以拓展模型的功能,判断图形是否是圆形、椭圆形、矩形和正方形,那么我们只需要收集这些图形的图片,测量其特征,得到训练数据,然后训练机器学习模型。在这个过程中,训练模型的方法是类似的,整个过程只在收集数据的步骤中增加了工作量,其他步骤中没有增加工作量。机器学习方法可以处理更加复杂的问题。例如,对于判断图1-3的矩形框中是否为眼睛的问题,我们只需要收集图片不同部位的特征,选择合适的机器学习算法,然后训练模型。这个过程与我们教小宝宝的过程类似,我们会指着图片的不同部位,告诉小宝宝哪个部位是眼睛,哪个部位不是眼睛;小宝宝通过学习不同的人脸、不同的部位,慢慢地便可以学会判断哪个脸部器官是眼睛。基于机器学习的诸多优点,现在机器学习是实现人工智能最重要的方法。深度学习是机器学习的一个分支。深度学习是机器学习的一部分,与机器学习的其他分支学科,以及统计学、人工智能等学科都有着紧密的联系。深度学习、机器学习、人工智能、统计学之间的关系如图1-4所示。
图1-4 深度学习、机器学习、人工智能、统计学之间的关系
在实际应用中,深度学习在机器翻译、语音转文字、推荐系统、数据挖掘,以及其他相关领域都取得了非常好的效果。这些任务都具有同样的特征:输入数据到模型中,然后得到输出数据,如图1-5所示。
图1-5 任务同样的特征
图1-5可以概括不同的应用,具体示例如下。
这类任务在机器学习中统称为有监督学习。有监督学习使用的训练数据的每个观测点都是一对,由一个输入对象(通常记为 )和一个期望的输出值(通常记为
)和一个期望的输出值(通常记为 )组成。有监督学习算法使用训练数据训练模型。当输入一个新的
)组成。有监督学习算法使用训练数据训练模型。当输入一个新的 时,模型可以输出一个预测值。例如,我们希望建立一个模型,该模型可以通过电子邮件的一些信息,自动判断电子邮件是否为垃圾邮件。表1-2中的数据是某员工收集的电子邮件信息。他查看了电子邮箱中的4601封电子邮件,统计每一封电子邮件中常用单词出现的频率,并人工判断每封电子邮件是否为垃圾邮件。把所有的数据总结成表1-2。为了方便,这里只列出了两封电子邮件的输出数据和两封电子邮件中8个单词出现的频率。
时,模型可以输出一个预测值。例如,我们希望建立一个模型,该模型可以通过电子邮件的一些信息,自动判断电子邮件是否为垃圾邮件。表1-2中的数据是某员工收集的电子邮件信息。他查看了电子邮箱中的4601封电子邮件,统计每一封电子邮件中常用单词出现的频率,并人工判断每封电子邮件是否为垃圾邮件。把所有的数据总结成表1-2。为了方便,这里只列出了两封电子邮件的输出数据和两封电子邮件中8个单词出现的频率。
表1-2 判断垃圾邮件的数据
| 是否是垃圾邮件 | you | your | hp | free | hpl | ! | our | re | edu | … | 
|---|---|---|---|---|---|---|---|---|---|---|
| Yes | 2.26 | 1.38 | 0.02 | 0.52 | 0.01 | 0.51 | 0.51 | 0.13 | 0.01 | … | 
| No | 1.27 | 0.44 | 0.90 | 0.07 | 1.43 | 0.11 | 0.18 | 0.42 | 0.29 | … | 
在表1-2中,输入数据 表示每一封电子邮件包含的单词出现的频率;输出数据
表示每一封电子邮件包含的单词出现的频率;输出数据 表示每一行的“Yes”或者“No”。有监督学习的“监督”指的是期望的输出值
表示每一行的“Yes”或者“No”。有监督学习的“监督”指的是期望的输出值 。在这个例子中,期望的输出值帮助模型判断电子邮件是否是垃圾邮件。训练好模型之后,我们便可以使用模型判断刚收到的邮件是否为垃圾邮件了。
。在这个例子中,期望的输出值帮助模型判断电子邮件是否是垃圾邮件。训练好模型之后,我们便可以使用模型判断刚收到的邮件是否为垃圾邮件了。
深度学习是有监督学习的一种算法。虽然随着深度学习的发展,深度学习也可以完成一些看似不像有监督学习的任务,例如,生成对抗网络(Generative Adversarial Network)可以用来生成图片,但是其算法的实质还是有监督学习。
深度学习与传统机器学习算法最大的不同点在于深度学习可以实现自动特征学习。传统机器学习算法通常需要人工设计,计算数据特征,才能实现较好的预测效果;深度学习基于多层的网络结构,可以自主地学习输入数据的特征,最终得到很好的效果。深度学习与传统机器学习算法的区别如图1-6所示。
图1-6 深度学习与传统机器学习算法的区别
什么是特征学习呢?特征学习是表达输入数据的方式。例如,在判断垃圾邮件的例子中,其最初始的数据是电子邮件,把电子邮件的信息用单词出现的频率表示,并得到表1-2,这就是特征学习。在这个例子中,该过程是人工完成的。也就是说,为了使用传统机器学习算法,我们需要把电子邮件信息表达成表1-2所示的形式,然后把数据输入模型中。
另外,直接以电子邮件(可能需要一些简单处理)作为输入数据,深度学习模型便可以自动地学习电子邮件蕴含的信息作为特征,然后根据自动学习的特征判断电子邮件是否为垃圾邮件。
深度学习也称为深度神经网络模型。在很多书中,我们经常会看到这样的描述,深度学习通过模拟人类神经网络的工作方式实现人脸识别、翻译、下棋等任务。我们很难考证是否最初发明神经网络模型的科学家受到了人类神经网络的启发。最初的神经网络模型非常简单,其工作原理与人类神经网络看上去只有一点点相似之处。随着神经网络模型的发展,其结构越来越复杂,变得越来越像人类的神经网络。把深度神经网络模型当作人类神经网络的模拟可能是一个很好的比喻或者类比。然而,深度学习模型本质是一个数学模型,提高其预测效果的方法主要源于对机器学习或者统计学习的思考,而不是源于对人类神经网络的深入模拟。
很高兴你已经阅读到本书的这部分内容。这说明你对深度学习感兴趣,或者说明你已经意识到深度学习正在改变我们的社会,或者你认为深度学习在未来将更广泛、更深入地改变我们的社会。确实,现在以深度学习为代表的机器学习和人工智能,正在极大地改变我们的生活方式、社会环境和工业发展。例如,深度学习改变了我们的驾驶体验,决定了我们的阅读内容,影响了我们中午吃什么等。深度学习已经无处不在,渗透到我们生活的各个方面。
深度学习使机器更加智能。深度学习帮助机器完成以前只有人类才能完成的任务。特别是最近几年,深度学习取得了巨大进步,在商业和工业应用中取得了巨大的成功,如计算机视觉、语音识别、机器翻译等。深度学习帮助我们更好地理解我们所处的环境,更好地控制和改造我们所处的环境。深度学习也许可以媲美于蒸汽机、计算机等改变世界的工具。
蒸汽机的发明主要改造了人类从事体力劳动的方式,计算机的发明提高了我们从事脑力劳动的效率。然而,深度学习有可能带来不一样的变革,有可能使得很多的脑力劳动发生重大的变化。
现在,很多媒体和相关从业者在夸大深度学习的功能,使得这个行业处于比较狂热的状态,好像深度学习马上就可以让机器变得比人类更加聪明,世界马上要发生翻天覆地的变化。然而,深度学习不可能在短期让机器拥有与人类一样的智能。即使之后数年深度学习理论的发展变慢,只要现在的深度学习技术在各行各业广泛且深入地应用,深度学习就足以给我们的社会带来巨大的变化。
最大的变化可能是工作的置换。普通的教师、律师、程序员、医生、投资者等脑力劳动者的岗位空间可能会受到挤压。深度学习可以使脑力劳动者提高工作效率,其他人可能需要改变自己的工作方式。
慕课及其他类似或者改进的教学方法的出现,可能使得社会不需要那么多普通的教师年年上着同样的课程;制作精良的课程可以供成百上千的学生同时学习,提高学生的学习效率。制作智慧课程的老师可以给更多学生上课,创造更多的社会价值。普通教师可能需要把精力转换到教育的其他方面,例如,从“教书”转到“育人”。
对于医生,深度学习也将给他们带来机遇和挑战。现在,在某些领域,医学影像自动诊断系统的准确率已经超过具有丰富经验的影像科医生。未来,随着自动诊断或者辅助诊断系统的出现,医生的工作效率可以成倍提高。例如,现在患者去医院看病,医生需要花大量时间看患者的病历和检查结果。如果医院系统可以为医生总结需要的指标和信息,并以图片或者其他更有效的方式呈现给医生,或者看完病,系统可以自动地填写病历,那么医生的工作效率便可以极大提高。当然,工作效率的提高不会导致大量医生失业,医生可以有更多时间提供机器无法提供的人文关怀。
我们相信深度学习一定会改变我们的生活和工作方式。工作效率的提高虽然不一定意味着失业,但是一定意味着改变。我们需要从事机器不能做的事情,如创造性的工作、情感的关怀等。当然,有一个产业在这个变革中一定会收益,那就是深度学习。
我们因为深度学习的超强魅力才开始学习它。很多深度学习算法极具创造性。在应用领域,深度学习也极具创造性。例如,深度学习可以让机器像画家一样作画,像音乐家一样作曲,像有工作经验的医生一样读X光片,像摄影师一样修图。
深度学习是一个强大的数据挖掘和人工智能工具。数据科学家、统计学家、商业分析师、人工智能专家等的工具箱中都应该有深度学习的一席之地。
不同行业的从业人员学习深度学习,对其事业的发展有很大的帮助;即使只是简单地了解深度学习的原理,也可以更好地了解相关技术的发展趋势,做出更明智的决策。
学深度学习之后,你可以做什么?找一个你的兴趣点,看看深度学习是否可以提高效率,并且尝试去实现它。深度学习与任何其他工具一样,你用得越多,就会越了解它的特性,越好地应用它。
深度学习是有监督的机器学习算法。深度学习可以自动从数据中学习特征,提高预测准确率。深度学习将会改变我们的生活和工作方式,加深我们对世界的认识。更重要的是,深度学习还是一门非常有趣的学科。从第2章开始,我们将正式踏上深度学习之旅。
深度学习是一类结构复杂的数学模型。我们需要掌握一定的数学知识,特别是线性代数、微积分和概率论的相关知识,才能很好地学习和理解深度学习。幸运的是,如果只需要理解深度学习的基本原理和应用,我们不需要精通这些数学知识,而只需要掌握一些核心概念和方法即可。为了方便随时复习和查阅相关知识,本章将介绍深度学习中常用的一些关键知识点。本章还将介绍Python编程的基础知识,包括Anaconda、Jupyter Notebook。
数(scalar)是一个数字,通常指一个实数。例如,通常用自然数 表示数据中的观测点数。
表示数据中的观测点数。
把多个数字有序地放在一起可以构成向量。例如,如下形式的 表示一个向量。
表示一个向量。
上面左边的向量叫作行向量;右边的向量叫作列向量。我们用小写黑体字母表示一个向量。向量里面的数字叫作向量的元素。在上面的例子中, ,
,  , …,
, …,  是向量
是向量 的
的 个元素。向量的元素可以用对应的位置序号表示。例如,
个元素。向量的元素可以用对应的位置序号表示。例如, 的第一个元素是
的第一个元素是 ,第二个元素是
,第二个元素是 。
。
以二维的形式表示的多个数字可以构成矩阵。例如,把6个数字写成如下形式,用于表示一个3×2的矩阵。
我们用大写黑体字母表示一个矩阵。矩阵中行和列的数量称为矩阵的维度。在上面的例子中,矩阵 有3行、2列,因此,
有3行、2列,因此, 的维度为3×2。当我们要强调矩阵的维度时,也会把矩阵
的维度为3×2。当我们要强调矩阵的维度时,也会把矩阵 写成
写成 。矩阵元素可以用对应的行号和列号表示,例如,
。矩阵元素可以用对应的行号和列号表示,例如, 的第2行第1列的元素可以写成
的第2行第1列的元素可以写成 。矩阵第
。矩阵第 行可以用
行可以用 表示,矩阵第
表示,矩阵第 列可以用
列可以用 表示。有一类特殊的矩阵在运算中经常会用到,那就是单位矩阵(identity matrix)。首先,单位矩阵是一个方阵(方阵的行数和列数相等)。其次,单位矩阵左上角到右下角的对角线(即主对角线)上的元素都是1,其他位置的元素都是0。单位矩阵通常用
表示。有一类特殊的矩阵在运算中经常会用到,那就是单位矩阵(identity matrix)。首先,单位矩阵是一个方阵(方阵的行数和列数相等)。其次,单位矩阵左上角到右下角的对角线(即主对角线)上的元素都是1,其他位置的元素都是0。单位矩阵通常用 (
( 表示方阵的维度)表示。例如,
表示方阵的维度)表示。例如, 表示3×3的单位矩阵。
表示3×3的单位矩阵。
如果把多个数字写成大于二维的形式,就可以得到张量。例如,把24个数字写成图2-1的形式,用于表示一个3维的张量。我们用大写黑体字母表示张量。在图2-1中, 的维度为3×4×2。有时候,我们也会把
的维度为3×4×2。有时候,我们也会把 写成
写成 。张量的元素也可以用对应的维度表示,例如,
。张量的元素也可以用对应的维度表示,例如, 的(2,2,2)元素是14。特别地,矩阵是一个只有两个维度的张量。在图2-1中,张量
的(2,2,2)元素是14。特别地,矩阵是一个只有两个维度的张量。在图2-1中,张量 由两个3×4的矩阵组成。有时候,深度学习的数据需要用张量来表示。由谷歌开发的深度学习框架叫作TensorFlow,直译就是“张量流”。
由两个3×4的矩阵组成。有时候,深度学习的数据需要用张量来表示。由谷歌开发的深度学习框架叫作TensorFlow,直译就是“张量流”。
图2-1 张量
最常用到的矩阵运算是矩阵的转置(transpose)。矩阵 的转置记为
的转置记为 (符号
(符号 是英文Transpose的首字母)。直观来看,矩阵的转置是矩阵的翻转,即
是英文Transpose的首字母)。直观来看,矩阵的转置是矩阵的翻转,即 的第一列变为
的第一列变为 的第一行,
的第一行, 的第二列变为
的第二列变为 的第二行等。例如:
的第二行等。例如:
因此,我们也可以得到
矩阵相加要求两个矩阵维度相同,矩阵对应元素相加。假设矩阵 和
和 的维度都是
的维度都是 ,
, 意味着
意味着 ,
, 且
且 。例如:
。例如:
在深度学习中,我们还会用到一些在传统“线性代数”课程中不常用的加法运算。例如,矩阵 和行向量
和行向量 的加法运算(要求矩阵
的加法运算(要求矩阵 的列数等于向量
的列数等于向量 的元素个数),
的元素个数), 定义为
定义为 ,
, 且
且 。例如:
。例如:
矩阵相减要求两个矩阵维度相同,矩阵对应元素相减。假设矩阵 和
和 的维度都是
的维度都是 ,
, 意味着
意味着 ,
, 且
且 。例如:
。例如:
矩阵 与行向量
与行向量 的减法运算(要求矩阵
的减法运算(要求矩阵 的列数等于向量
的列数等于向量 的元素个数)
的元素个数) 定义为
定义为 ,
, 且
且 。例如:
。例如:
第一种矩阵乘法称为逐点相乘(element-wise product或者hadamard product),记为 。该乘法要求两个矩阵维度相同,矩阵对应元素相乘。假设矩阵
。该乘法要求两个矩阵维度相同,矩阵对应元素相乘。假设矩阵 和
和 的维度都是
的维度都是 ,
, 意味着
意味着 ,
, 且
且 。例如:
。例如:
第二种矩阵乘法是“线性代数”课程中介绍的矩阵乘法,记为 。该乘法要求矩阵
。该乘法要求矩阵 的列数等于矩阵
的列数等于矩阵 的行数。如果矩阵
的行数。如果矩阵 的维度为
的维度为 ,矩阵
,矩阵 的维度为
的维度为 ,那么
,那么 和
和 相乘的结果
相乘的结果 的维度为
的维度为 。
。 的
的 )元素是
)元素是 的第
的第 行与
行与 的第
的第 列对应元素相乘,然后求和的结果,即
列对应元素相乘,然后求和的结果,即 。例如:
。例如: 
点乘(dot product)是两个元素个数相同的向量中对应元素相乘,然后求和的结果,记为 。即
。即 ,这里,
,这里, 和
和 都为列向量。例如,
都为列向量。例如, ,
, ,那么
,那么 。第二种矩阵乘法
。第二种矩阵乘法 也可以理解为,
也可以理解为, 的
的 )元素是
)元素是 的第
的第 行与
行与 的第
的第 列点乘的结果,即
列点乘的结果,即 ,示例如图2-2所示。
,示例如图2-2所示。
图2-2 矩阵乘法的示例
我们可以通过范数(norm)衡量向量或者矩阵的大小。向量 的
的 范数定义为
范数定义为
这里要求 。特别地,在深度学习中,我们常用到
。特别地,在深度学习中,我们常用到 范数
范数 。对于矩阵,在深度学习中,最常用的范数是Frobenius范数。矩阵
。对于矩阵,在深度学习中,最常用的范数是Frobenius范数。矩阵 的Frobenius范数定义为矩阵的所有元素的平方和,再求平方根。
的Frobenius范数定义为矩阵的所有元素的平方和,再求平方根。
在深度学习中应用较多的微积分概念是导数。因此,本节主要介绍导数和求导法则。
函数 在
在 处的导数记为
处的导数记为 或者
或者 ,定义为
,定义为
 为函数
为函数 )在
)在 处切线的斜率,表示函数
处切线的斜率,表示函数 )在
)在 处的变化率。在图2-3中,实线表示
处的变化率。在图2-3中,实线表示 的曲线,虚线表示
的曲线,虚线表示 )在
)在 处的切线。
处的切线。
图2-3 函数 及其在
及其在 处的导数
处的导数
如果函数 具有两个变量,可以把
具有两个变量,可以把 看成固定常数,对
看成固定常数,对 求导数,称为
求导数,称为 关于
关于 的偏导数,记为
的偏导数,记为 ;也可以把
;也可以把 看成固定常数,对
看成固定常数,对 求导数,称为
求导数,称为 关于
关于 的偏导数,记为
的偏导数,记为 。
。
根据同样的方式,我们可以把偏导数的定义推广到有多个变量的函数情形。 关于
关于 的偏导数为,固定
的偏导数为,固定 和
和 ,函数对
,函数对 求导,记为
求导,记为
主要求导法则如下。
 ,则
,则 ,则
,则该计算方法也可以推广到任意有限个函数相乘的情形。例如,若 ,则
,则
 ,则
,则 ,
, ,则复合函数
,则复合函数 关于
关于 的导数为
的导数为在解决现实问题时,深度学习需要处理带有不确定性特征的数据。例如,建立一个天气预报的模型。未来的天气情况本身是很多因素在未知机制作用下的结果,这些影响因素有些是已知并且可以测量的(但是,测量过程可能具有不确定性),有些却是未知的或者不能够测量的。对于深度学习,在建模过程中需要考虑这些不确定的影响因素才能更好地做出预测。在这里,我们将简要介绍深度学习中常用的概率论方面的概念。
直观上,随机变量(random variable)是一个取值不确定的变量。该变量的结果可能有多种,而现实中,我们不能确定哪个结果会出现。通常,随机变量记为大写字母 ,而观测到的随机变量的值记为小写字母
,而观测到的随机变量的值记为小写字母 。例如,考虑24小时后某只股票的价格。在当前的这个时刻看来,24小时后某只股票的价格可能上涨,可能下跌或者不涨不跌。在当前,我们可以把24小时后的股价看作随机变量,记为
。例如,考虑24小时后某只股票的价格。在当前的这个时刻看来,24小时后某只股票的价格可能上涨,可能下跌或者不涨不跌。在当前,我们可以把24小时后的股价看作随机变量,记为 。24小时后,我们观测到该股票的收盘价。这时,收盘价是观测到的随机变量
。24小时后,我们观测到该股票的收盘价。这时,收盘价是观测到的随机变量 的值,记为
的值,记为 。
。
随机变量可以分成两类,即离散型随机变量和连续型随机变量。离散型随机变量可能的结果的数量是有限的,或者是可数的。例如,掷一个骰子,可能的结果只有6种,即得到1,2,3,4,5,6。连续型随机变量可能取到实数轴中某些区间的所有值。
随机变量的分布用来描述随机变量出现某种结果的可能性。随机变量的累积分布函数(Cumulative Distribution Function, CDF)记为 ),定义为
),定义为
离散型随机变量可以用分布函数表示,定义为
连续型随机变量也可以用概率密度函数(Probability Density Function, PDF)表示,记为 ),且
),且 )满足
)满足
对于离散型随机变量, )与
)与 )(PMF)等价地表示随机变量
)(PMF)等价地表示随机变量 的分布信息。如果已知随机变量
的分布信息。如果已知随机变量 的累积分布函数
的累积分布函数 ),则可以得到随机变量
),则可以得到随机变量 的分布函数
的分布函数 ,这里
,这里 是一个很小的正数;如果已知随机变量
是一个很小的正数;如果已知随机变量 的分布函数
的分布函数 ),则可以得到随机变量
),则可以得到随机变量 的累积分布函数
的累积分布函数 。
。
对于连续型随机变量, )与
)与 )(PDF)等价地表示随机变量
)(PDF)等价地表示随机变量 的分布信息。如果已知随机变量
的分布信息。如果已知随机变量 的累积分布函数
的累积分布函数 ),则可以得到随机变量
),则可以得到随机变量 的概率密度函数
的概率密度函数 ;如果已知随机变量
;如果已知随机变量 的概率密度函数
的概率密度函数 ),则可以得到随机变量
),则可以得到随机变量 的累积分布函数,
的累积分布函数, 。
。
常见的概率分布如下。
 与
与 ,
, 。
。 通常表示“成功”,
通常表示“成功”, 表示成功的概率;
表示成功的概率; 通常表示“失败”,
通常表示“失败”, 表示失败的概率。伯努利分布的分布函数为
表示失败的概率。伯努利分布的分布函数为 ,
, ,可以用表2-1表示。
,可以用表2-1表示。表2-1 伯努利分布的分布函数
| 
 | 0 | 1 | 
|---|---|---|
| 
 | 
 | 
 | 
 个不同的取值,即{1,2,
个不同的取值,即{1,2, ,
, }。第
}。第 个取值出现的概率为
个取值出现的概率为 ,
, ,且要求
,且要求 。多项分布的分布函数可以用表2-2表示。
。多项分布的分布函数可以用表2-2表示。表2-2 多项分布的分布函数
| 
 | 1 | 2 | 
 | 
 | 
|---|---|---|---|---|
| 
 | 
 | 
 | 
 | 
 | 
 。正态分布的概率密度函数为
。正态分布的概率密度函数为正态分布的两个参数 和
和 分别决定了正态分布的位置与分散程度,
分别决定了正态分布的位置与分散程度, 和
和 。图2-4的两条曲线分别表示均值为0、标准差为1和2的正态分布的概率密度函数。函数
。图2-4的两条曲线分别表示均值为0、标准差为1和2的正态分布的概率密度函数。函数 )最高点在
)最高点在 处,
处, )在最高点处的值由
)在最高点处的值由 决定。
决定。 越大,
越大, )在最高点处的值越小。随着
)在最高点处的值越小。随着 增大,
增大, )变小。
)变小。 越大,
越大, )变小的速度越慢,意味着随机变量
)变小的速度越慢,意味着随机变量 分散程度越大。
分散程度越大。
图2-4 正态分布的概率密度函数
很多情况下,我们会关心当A事件发生时,B事件发生的概率。例如,在“概率论”这门课程中,同学们可能会关心,每周学习概率论5小时(A事件),期末成绩是 (B事件)的概率。这就是条件概率。条件概率记为
(B事件)的概率。这就是条件概率。条件概率记为 ,定义为
,定义为
 表示随机变量
表示随机变量 等于
等于 时,
时, 等于
等于 的概率;
的概率; 表示
表示 和
和 同时发生的概率;
同时发生的概率; )表示
)表示 发生的概率。
发生的概率。
当我们写Python代码或者使用其他人写的代码时,通常会使用特定的Python版本,如Python 2或者Python 3。我们也有可能使用某些特定版本的Python模块或者包。例如,在一个项目中使用Python 2,NumPy 1.13.0,而在另一个项目中使用Python 3,NumPy 1.16.1。然而,在同一台计算机中,同时安装不同版本的Python和不同版本的包不是一件容易的事情。Anaconda可以帮助我们实现这一点。Anaconda是一个免费开源的包和环境的管理器,支持在同一台计算机上安装不同版本的Python和包,并允许在不同的版本之间切换。Anaconda的主要作用如下。
Anaconda支持多种系统,包括Windows、macOS和Linux。用户可以从Anaconda官网下载适合自己系统的安装程序。我们建议下载Python 3的安装程序。虽然现在同时存在Python 2和Python 3,但是我们相信Python 3会吸引越来越多的用户,而Python 2可能会慢慢地失去维护和更新。本书使用的Python版本的Python 3。如果计算机操作系统是64位的,则最好选择64位的Anaconda安装程序。
在Windows系统中,只需要双击扩展名为.exe的文件(如Anaconda3-2018.12-Windows-x86_ 64.exe),然后按照操作提示就可以完成安装。完成安装后,打开Anaconda Prompt,弹出图2-5所示的提示界面。
图2-5 Anaconda提示界面
在符号“>”后面输入以下命令,界面会显示Anaconda自带的Python和所有包的版本号。
conda list安装文件自带的某些包可能不是最新版本的,因此我们可以在符号“>”后输入以下代码更新所有包。
conda upgrade --all在运行上述命令的过程中,可能需要输入“y”,然后按Enter键,Anaconda才会更新所有不是最新版本的包。
包的管理包括以下几个方面。
Anaconda通过环境实现在同一台计算机中安装不同版本的Python和包。基本原理是,Anaconda首先创立环境,然后在该环境中安装特定版本的Python和包。不同环境是完全隔离的。刚打开Anaconda Prompt时,可以看到光标所在行的最左边括号里写着base,表示当前环境为Anaconda自带的base环境。
要创建环境,使用命令conda create -n env_name package_names。例如,创建一个叫作py37的环境,安装Python 3,同时安装pandas和NumPy,命令如下。
conda create -n py37 python=3 pandas numpy这里-n后面的py37是新环境的名字。python=3表示安装Python 3。如果要在新环境中安装Python 2,则使用以下命令。
conda create -n py37 python=2我们还可以指定更加具体的Python版本,例如:
conda create -n py37 python=3.5要列出已创建的所有环境,使用命令conda env list。
进入或者退出环境在不同系统平台略有不同。在Windows系统中,用conda activate env_name进入环境,用conda deactivate退出环境。例如,要进入环境py37,使用命令conda activate py37,如图2-6所示。这时可以看到光标所在行的最左边括号里写着py37,表示现在处于环境py37中。
图2-6 进入环境
在环境中安装Python包和上述介绍的包安装方法一样。例如,在py37中安装NumPy,只需要进入py37,然后输入conda install numpy。
要删除环境,使用命令conda env remove -n env_name。例如,输入conda env remove -n py37,将删除刚刚创建的环境py37。
Jupyter Notebook是一个Web应用程序,用于创建和共享数据分析文档。我们可以使用Jupyter Notebook编辑代码,运行代码,查看结果,可视化数据,编辑说明文字、公式等。另外,Jupyter Notebook还是一款便捷的数据分析工具,可以用于清理数据、统计建模、构建和训练机器学习模型、可视化数据与分析大数据等。
安装Jupyter Notebook最简单的方式是使用Anaconda的conda命令,具体步骤如下。
(1)打开Anaconda Prompt。
(2)使用conda activate env_name命令进入一个已经创建的环境(例如,要进入环境py37,在符号“>”后输入conda activate py37)。
(3)输入conda install jupyter notebook,Anaconda会自动在环境中安装Jupyter Notebook。
在Anaconda Prompt中输入jupyter notebook,按Enter键便可以打开Jupyter Notebook,如图2-7所示。
图2-7 在Anaconda中打开Jupyter Notebook
浏览器将打开图2-8所示的网页。
图2-8 Jupyter Notebook在浏览器中打开的网页
现在可以打开Jupyter Notebook文档(图2-8所示的文件夹中有一个Jupyter Notebook文档howmework1.ipynb。Jupyter Notebook文档的扩展名是.ipynb),或者新建一个Jupyter Notebook文档。单击网页右侧的New下拉列表,显示新建文件的类型,包括Python 3、Text File等,如图2-9所示。在New下拉列表中,选择Python 3将创建一个在Python 3中操作的Jupyter Notebook文档。
图2-9 新建Jupyter Notebook文档
现在,一个新的Jupyter Notebook文件已经创建,如图2-10所示。
图2-10 新建的Jupyter Notebook文档
新建的Jupyter Notebook文档的文件名都会以Untitled开头。第一个新建文档的名称是Untitled.ipynb,若再新建一个Jupyter Notebook文档,则文件名是Untitled1.ipynb,依次类推。在图2-10所示的界面中,选择File→rename命令,重命名Jupyter Notebook文档,如图2-11所示。
图2-11 重命名Jupyter Notebook文档
当完成Jupyter Notebook文件的编辑后,单击File菜单下的第一个按钮,保存文档。完成编辑后,直接关闭网页,或者在Anaconda中连按两次Ctrl+C组合键完全关闭Jupyter Notebook。
代码框(code cell)是在Jupyter Notebook上编写代码的地方。在代码框(见图2-12)中,我们可以编写任何Python代码,包括赋值、函数、类、画图等。我们在代码框中载入numpy模块,计算2+3,并赋值给变量a,最后用函数print()输出结果。
在Jupyter Notebook中,无论是代码框还是标记框,都有两种模式:一种是命令模式,另一种是编辑模式。命令模式左边有蓝色竖线,编辑模式左边有绿色竖线。图2-12所示的代码框处于命令模式。当一个代码框处于命令模式时,双击鼠标或者按Enter键,进入编辑模式,左边竖线变成绿色,如图2-13所示。
图2-12 Jupyter Notebook的代码框(命令模式)
图2-13 Jupyter Notebook的编辑模式
这时,在代码框中编辑代码。当代码框处于编辑模式时,按Esc键,进入命令模式。按Shift+Enter组合键或者按Ctrl+Enter组合键,运行代码框中的代码或者编译标记框中的文字等内容。按Shift+Enter组合键和按Ctrl+Enter组合键的区别在于:按Shift+Enter组合键运行代码或者编译标记框,然后自动进入下面一个代码框或者标记框;而按Ctrl+Enter组合键只运行代码或者编译标记框。
标记框(markdown cell)用于编辑文字说明,包括普通中英文、标题、数学公式等。Jupyter Notebook采用的是Markdown的语法。下面将介绍常用的一些语法,涉及标题、强调、列表(有序列表和无序列表)、链接、图片、表格和数学公式。
在标记框中,标题以“#”开始,Jupyter Notebook支持6级标题。
# Header 1
## Header 2
### Header 3
#### Header 4
##### Header 5
###### Header 6上面的输入编译后的结果如下。
标记框可以给文字加粗,将文字变为斜体或者在文字中间加横线 。
Jupyter Notebook是一个很强大的工具,可以用单个下画线`_`或者`*`把文字变成_斜体_  或者 *斜体*
也可以用两条下画线`__`或者`**`把文字变成__粗体__或者**粗体**   
还可以用`~~`在文字中间加~~横线~~上面的输入编译后的结果如下。
Jupyter Notebook是一个很强大的工具,可以用单条下画线_或者*把文字变成斜体或者斜体  
也可以用两条下画线__或者**把文字变成粗体或者粗体
还可以用~~在文字中间加横线    标记框可以用于添加无序的项目符号。
- 无序号的列表,可以用减号-
* 或者星号*
+ 或者加号+开头
+ 注意,符号-、*或者+之后需要有一个空格上面的输入编译后的结果如下。
□ 无序号的列表,可以用减号-
□ 或者星号*
□ 或者加号+开头
□ 注意,符号-、*或者+之后需要有一个空格标记框可以用于添加有序的项目符号。
1. 有序号的列表,可以以数字加.表示
1. 具体的数字是什么并不重要
1. Jupyter Notebook会自动按顺序变成1,2,3...上面的输入编译后的结果如下。
1. 有序号的列表,可以以数字加.表示
2. 具体的数字是什么并不重要
3. Jupyter Notebook会自动按顺序变成1,2,3...在Jupyter Notebook中,创建链接只需要把文字放在中括号内,把网址放在紧接着的圆括号内。例如:
[异步社区]( https://www.epubit.com/)编译结果如下。
异步社区在标记框内要插入图片,采用如下两种方式。例如,我们要插入的图片是insert_figs.png,该图片保存在文件夹figs中。第一种方式是感叹号+中括号(中括号内写图片的标题)+小括号(小括号内写图片的路径和名称);第二种方式是用尖括号,尖括号内部写img+src(src为图片的路径和名称)+图片的宽度。
      
<img src="figs/insert_figs.png" width=400>表格用竖线“|”分隔相邻列,用多个连字符“-”分隔列名和其他行。例如:
| 第一列  | 第二列  | 第三列 |
| ------ |--------| -----|
| 第一行     | 1    |   2 |
| 第二行     | 3    |   4 |
| 第三行     | 5    |   6 |编译结果如表2-3所示。
表2-3 Jupyter Notebook生成的表格
| 第一列 | 第二列 | 第三列 | 
|---|---|---|
| 第一行 | 1 | 2 | 
| 第二行 | 3 | 4 | 
| 第三行 | 5 | 6 | 
夹在文字中的数学公式使用单个符号“$”括起来,例如,$\beta$的编译结果为 。单独一行且居中的公式使用符号“$$”括起来,例如:
。单独一行且居中的公式使用符号“$$”括起来,例如:
$$
\mathbf{y}=\mathbf{X}\boldsymbol{\beta}+\boldsymbol{\epsilon}
$$编译结果为
下面简要介绍本书用到的Python功能,包括基本数据结构、控制结构、函数、NumPy、Pandas、画图工具。
Python常用的数据类型有整型(int)、浮点型(float)、字符串(string)和布尔型(bool)。函数type( )可以返回数据类型。
# 查看数据类型
type(3)       # 返回 'int'
type(3.0)     # 返回 'float'
type('three') # 返回 'str'
type(True)    # 返回 'bool'
type(None)    # 返回 'NoneType'
# 转换数据类型
float(3)
int(3.8)
str(33)常用的Python基本运算有加(+)、减(−)、乘(*)、除(/)、乘方(**)、求余(%)。
12 + 5   # 加 (返回 17)
12 - 5   # 减 (返回 7)
12 * 5   # 乘 (返回 60)
12 / 5   # 除 (返回 2.4)
12 ** 5  # 乘方 (返回 248832)
12 % 5   # 求余 (返回 2)Python的比较运算包括大于(>)、大于或等于(>=)、小于(<)、小于或等于(<=)、不等于(!=)、相等(==)。Python的布尔运算包括与(and)、或(or)、非(not)。
#比较
6 < 4    # 返回 False
6 <= 4   # 返回 False
6 > 4    # 返回 True
6 >= 4   # 返回 True
6 != 4   # 返回 True
4 == 4   # 返回 True
# 布尔运算
6 > 4 and 2 > 3  # 返回 False
6 > 4 or 6 < 4   # 返回 True
not False;       # 返回 True每次打开Python时,Python只会载入少量必需的模块,以实现一些基本功能。如果要拓展Python的功能,就需要载入额外的模块或者包。这样做的好处是,安装和打开Python不会占用太多的计算资源和内存空间,而我们可以随时安装和加载所需要的模块或者包,使Python可以很好地满足数据分析的需求。
Python模块和包是两个不同的概念,其区别在于,模块是以.py结尾的一个Python文件,包是包含多个模块的文件夹(这个文件中还有一些其他文件,如__init__.py,使得文件夹中的模块成为一个整体)。在Python中加载模块或者包有如下方式。
import math                       # 加载math模块
math.sqrt(100)                    # 使用方式是模块名.函数名
from math import sqrt             # 从math模块加载函数sqrt
sqrt(100)                         # 这时不需要写模块名
from math import cos, floor       # 从math模块加载函数cos和floor
cos(10)
floor(2.8)
# 从os模块中载入所有的函数(不推荐,这样容易发生函数混淆)
from os import *
import numpy as np                # 加载numpy模块,并命名为np
np.sqrt(100)列表(list)是Python中最常用的数据结构,可以容纳任何数据类型,如浮点型、字符串、布尔型等。列表的数据项不需要具有相同的类型。当创建一个列表时,不同元素间用逗号隔开,并放在方括号中,如下所示。
num = [3, 2, 1, 4, 2015]
string = ["lin", "yi"]
mixed_num_string = ["lin", 2015, "yi", 8]列表是有序的,其索引从0开始,依次递增1。例如,列表mixed_num_string中各个元素的索引如表2-4所示。
表2-4 列表中元素的索引
| 列表元素 | “lin” | 2015 | “yi” | 8 | 
| 索引 | 0 | 1 | 2 | 3 | 
用列表索引可以访问列表元素。
mixed_num_string[2]   # 返回索引为2的元素
mixed_num_string[0:2] # 返回索引为0与1的元素,注意,不包括索引为2的元素
mixed_num_string[-1]  # 返回列表最后一个元素
mixed_num_string[-2:] # 返回列表最后两个元素函数append()、函数extend()、函数pop()是3个常用的编辑列表元素的函数。
append():在列表后面增加一个元素。extend():合并两个列表。pop():移除列表最后一个元素。关于以上3个函数的示例代码如下。
num[1] = 200              # 把索引为1的元素初始化为200
num
[3, 200, 1, 4, 2015]
num.append(1024)
num
[3, 200, 1, 4, 2015, 1024]
num.append(string)        # 把列表string当成一个元素添加到列表num中
num
[3, 200, 1, 4, 2015, 1024, ['lin', 'yi']]
num.pop()                 # 从列表num中移除最后一个元素
num
[3, 200, 1, 4, 2015, 1024]
num.extend(string)        # 合并列表num和列表string
num
[3, 200, 1, 4, 2015, 1024, 'lin', 'yi']函数len()可以返回列表的元素个数。在列表中,需要注意,赋值只是给列表起了一个新的名字。例如:
len(num)               # 返回num的元素个数
8
string2 = string       # 相当于给列表string起了一个新的名字
string2[1] = "hh"      # 更改string2的元素也会更改string的元素
string
['lin', 'hh']
string3 = string[:]    # 复制列表string,并把复制的列表赋值给string3
string3[1] = "hello"   # 更改string3的元素不会更改string的元素
string
['lin', 'hh']Python的元组(tuple)与列表类似,不同之处在于元组的元素不能修改,元组使用小括号。元组的创建很简单,只需要在小括号中添加元素,元素间使用逗号隔开(括号也可以省略)。例如:
tup1 = ('physics', 'chemistry', 1997, 2000)
tup2 = (1, 2, 3, 4, 5 )
tup3 = "a", "b", "c", "d"      # 省略括号元组使用索引访问元素的方式和列表相同。
tup1[2]                        # 返回 1997
tup1[0:2]                      # 返回 ('physics', 'chemistry')以下修改元组元素的操作是非法的。
tup1[0] = 100                  # 非法的操作字典(dictionary)的元素由成对的键(key)和值(value)组成。字典也称作关联数组或哈希表,基本语法如下。
dict = {'yi': 2015, 'lin': 1984, 'luo': 1985}键与值用冒号隔开,每对键和值用逗号分隔,整体放在大括号“{}”中。键必须唯一,但值则不必。字典是无序的数据结构,因此不能通过索引访问字典元素。在字典里,使用键访问值。
dict = {'yi': 2015, 'lin': 1984, 'luo': 1985}
dict['yi']     # returns 2015
len(dict)      # 返回 3
dict.keys()    # 返回 ['
yi', 'lin', 'luo']dict.values()  # 返回 [2015, 1984, 1985]
# 返回 [('yi', 2015), ('lin', 1984),('luo', 1985)]
dict.items()Python的集合(set)与数学中集合的概念类似,是指由不同元素组成的合集。下面的代码可以从列表中创建一个集合。
s_list = [3,1,1,4,5,5,6,6]
s = set(s_list)
s
{1, 3, 4, 5, 6}函数add()可以给集合添加新的元素。
s.add(7)
s
{1, 3, 4, 5, 6, 7}Python的控制结构可以控制代码的运行顺序。在控制结构中,缩进是非常重要的,用于标记代码的结构。缩进可以通过4个空格或者1个制表符(tab)实现。
for循环的结构如下。
1  for each_item in list:
2      do something to 
3      each_item需要注意的是,在第1行中,最后一定有一个冒号“: ”;冒号后面属于for循环的行都要有4个空格的缩进。在for循环中,each_item首先等于list的第一个元素,执行冒号后面的代码;然后,each_item等于list的第二个元素,执行冒号后面的代码;直到each_item等于list的最后一个元素,执行冒号后面的代码;最后,结束for循环。以下代码对列表中所有元素求和。
a = [1,2,3,4,5,6]
sum_a = 0
for each in a:
    sum_a += each
    print(each)
sum_a
1
2
3
4
5
6
21while循环的结构如下。
while statement:
    do something
    do more在while循环中,statement是一个判断,结果为True或者False。如果statement的结果为True,则执行冒号后面的代码;如果statement的结果为False,则结束循环。语句while statement后面有冒号,属于while循环的代码需要4个空格的缩进。以下代码使用while循环对列表中所有元素求和。
a = [1,2,3,4,5,6]
sum_a = 0
i = 0
while i<len(a):
    sum_a += a[i]
    print(a[i])
    i += 1
sum_a
1
2
3
4
5
6
21if语句的结构如下。
1  if statement1:
2      do first_job
3  elif statement2:
4      do second_job
5  else:
6      do third_job如果statement1的结果为True,则执行第2行代码;如果statement1的结果为False,则运行statement2。如果statement2的结果为True,则执行第4行的代码;如果statement2的结果为False, 则执行第6行的代码。在Python中,elif表示else if。以下代码可以把列表a中的奇数和偶数分别保存在不同列表中。
a = [1,2,3,4,5,6]
odd_num = []
even_num = []
for each in a:
    if each % 2 != 0 :
        odd_num.append(each)
    else:
        even_num.append(each)
print("Odd numbers in list a: " + str(odd_num))
print("Even numbers in list a: " + str(even_num))
Odd numbers in list a: [1, 3, 5]
Even numbers in list a: [2, 4, 6]函数能提高代码的模块性和重复利用率。Python提供了许多内建函数,如len()、print()等。我们也可以自定义函数。函数定义的规则如下。
函数代码块以关键词def开头,后接函数名称和圆括号( ),传入函数的参数放在圆括号内,函数内容以冒号起始,并且缩进,若有返回值,使用return返回结果,结束函数。
函数的结构如下。
def fun_name(arg1, arg2):
    do something
    return result1, result2下面创建一个函数,函数名为odd_even。函数的任务是对于输入的列表,输出列表中的奇数和偶数。
def odd_even(ls):
    odd_num = []
    even_num = []
    for each in ls:
        if each % 2 != 0 :
            odd_num.append(each)
        else:
            even_num.append(each)
    return odd_num, even_num
a1 = [3,4,5,6,10,11,2,3]
odd_num_a1, even_num_a1 = odd_even(a1)
print("Odd numbers in list a1: " + str(odd_num_a1))
print("Even numbers in list a1: " + str(even_num_a1))
Odd numbers in list a1: [3, 5, 11, 3]
Even numbers in list a1: [4, 6, 10, 2]
a2 = [1,1,1,2,2,2]
odd_num_a2, even_num_a2 = odd_even(a2)
print("Odd numbers in list a2: " + str(odd_num_a2))
print("Even numbers in list a2: " + str(even_num_a2))
Odd numbers in list a2: [1, 1, 1]
Even numbers in list a2: [2, 2, 2]Python是非常强大的计算机语言,可以完成很多复杂的任务。不过,Python本身对数学运算的支持并不是很好。在使用标准的Python时,进行矩阵和向量的运算都需要使用循环语句,实现过程比较复杂,计算速度较慢。NumPy库可以实现快速的数学运算,特别是矩阵运算。NumPy库提供了大量矩阵运算函数。NumPy库的内部运算通过C语言而不是Python实现,使得它具有快速运算能力。NumPy库包含两种基本数据类型——数组(array)和矩阵(matrix)。数组和矩阵的运算稍有不同,在这里,我们将着重介绍数组的使用方法。在使用NumPy库前,需要加载NumPy库。
import numpy as np使用函数np.array()创建一个数组。
a = np.array([2,3,4])
a
array([2, 3, 4])上面创建的数组a相当于一个向量。我们可以使用函数a.shape()输出a的维度。a的维度为(3,0),表示a是一个长度为3的数组。
a.shape
(3,)
b = np.array([[1,2,3],[4,5,6]])
b
array([[1, 2, 3],
       [4, 5, 6]])
b.shape  
(2, 3)上面创建的数组b的维度是(2,3),表示b是一个2行3列的数组。数组的维度可以使用函数reshape()变换,示例如下。
# 返回一个数组, array([0,1,2,3,4,5,6,7,8,9,10,11])
c = np.arange(12)   
d = c.reshape(3,4)  # 返回一个维度为(3,4)的数组
d.reshape(2,6)      # 返回一个维度为(2,6)的数组通过输入列表,利用函数np.array()可以创建数组。NumPy库也可以产生经常用到的特殊数组。例如,函数np.arange()可以产生在某个区间内步长相等的数组,函数np.zeros()可以产生元素全是0的数组,函数np.ones()可以产生元素全是1的数组,函数np.eye()可以产生对角线元素是1而其他元素都是0的二维数组。示例如下。
np.arange(4)      # 返回元素是0,1,2,3的数组
np.arange(4,8)    # 返回元素是4,5,6,7的数组
np.zeros(3)       # 返回元素是0,0,0的数组
np.zeros((2,3))   # 返回一个维度是(2,3)、元素都是0的数组
np.ones(2)        # 返回元素是1,1的数组
np.ones((2,3))    # 返回一个维度是(2,3)、元素都是1的数组
np.eye(3)         # 返回一个对角线元素是1、其他元素都是0且维度为(3,3)的数组两个维度相同的NumPy库数组的加(+)、减(−)、乘(*)、除(/)表示两个数组中相同位置元素的加(+)、减(−)、乘(*)、除(/)。
a = np.array([[1,2,3],[4,5,6]])
b = np.array([[4,5,6],[1,2,3]])
a + b
a - b
a * b
a / b当数组维度不同时,我们也可以进行数组的运算。例如,a + 2表示a的所有元素加2,a + np.array([[3,2,1]])表示a的每一行加上维度为(1,3)的数组np.array([[3, 2,1]]),a + np.array([[3],[2]])表示a的每一列加上维度为(2,1)的数组np.array ([[3],[2]]。然而,在计算a + np.array([[3,2]])时,程序会报错。
a + 2
a + np.array([[3,2,1]])
a + np.array([[3],[2]])有的运算针对数组的每个元素分别进行。例如,函数np.sqrt()对数组的每个元素求平方根,函数np.log()对数组的每个元素求自然对数。
np.sqrt(a)
np.log(a)在NumPy库中,矩阵相乘(不是逐点相乘)有3种实现方式,即使用函数np.dot()、函数np.matmul()和二元运算符“@”。3种方式可以得到同样的结果。示例代码如下。
a = np.arange(1,7).reshape(2,3)
b = np.arange(7,13).reshape(3,2)
a @ b
np.dot(a, b)
np.matmul(a, b)我们可以很容易访问数组的行、列或者单独的元素。冒号“:”表示某一行或者某一列的所有元素。
a = np.arange(12).reshape(3,4)  # 创建维度为(3, 4)的数组
a[0,:]                          # 数组的第一行
a[0]                            # 数组的第一行
a[0:2]                          # 数组的前两行
a[:,0]                          # 数组的第一列
a[0:2, 0:2]                     # 数组的前两行、前两列
a[1,1]                          # 数组的第2行、第2列Pandas是基于NumPy库的一个开源Python包,广泛应用于数据分析、数据清洗和准备等工作。Pandas的数据结构主要有两种,即序列(series)和数据表(dataframe)。
import pandas as pdPandas的序列是一维数组,与NumPy的数组相似。与NumPy的数组不同的是,序列能为数据自定义标签,也就是索引(index),然后通过索引访问数组中的数据。例如,一个序列可以表示一个班级学生的数学成绩,索引是学生的名字。Pandas的数据表是二维数据表,数据以表格的形式存储,分成若干行和若干列。通过数据表,用户能够方便地处理数据。例如,用一个数据表表示一个班级中学生的多维信息,第一列表示数学成绩,第二列表示性别。在数据表中,同一列的数据类型是一样的,不同列的数据类型可以不一样。
# 当代码太长时,我们可以使用反斜杠(\)把代码写在两行或者多行
math_score = pd.Series([80, 20, 50, 60, 70], \
index = ["lin", "luo", "pan", "li", "wang"]) 
math_score["lin"]              # 返回lin同学的数学成绩
math_score[["lin", "luo"]];    # 返回lin和luo两位同学的数学成绩
class_info = pd.DataFrame({  \
             "math_score" : [80, 90, 50, 60, 70], \
             "gender" : ["male", "female", "female", "female",\ "male"]}, \
             index = ["lin", "luo", "pan", "li", "wang"])
class_info上述代码所创建的数据表如表2-5所示。
表2-5 创建的数据表
| index | math_score | gender | 
|---|---|---|
| lin | 80 | male | 
| luo | 90 | female | 
| pan | 50 | female | 
| li | 60 | female | 
| wang | 70 | male | 
列表中的['lin', 'luo', 'pan', 'li', 'wang']可以看成行的名字,['math_ score', 'gender']可以看成列的名字。在数据表中,我们可以通过行或者列的名字,或者索引来访问数据表的元素。
class_info["math_score"]             # 返回数据表中math_score对应的列
class_info[0:2]                      # 返回前两行使用.loc可以方便地通过行或者列的名字访问数据表。使用.iloc可以方便地通过索引访问数据表。示例如下。
class_info.loc[["lin", "luo"]]       # 返回数据表的前两行
# 返回数据表前两行中gender列的元素
class_info.loc[["lin", "luo"], "gender"]  
class_info.iloc[2:4]                 # 返回数据表的第3、4行
class_info.iloc[:,1];                # 返回数据表的第2行通过观察数据表及一些统计量,我们可以了解数据表数据的特征。示例如下。
class_info.head()                    # 返回前6行
class_info.tail()                    # 返回后6行
class_info.describe()                # 返回均值及一些分位数在Python中,Matplotlib库是最流行的画图工具之一。Matplotlib库可以很容易地实现数据图形化,兼具灵活性和易操作性。使用Matplotlib库前,需要载入Matplotlib库的pyplot。
# 在Jupyter Notebook中,以百分号(%)开头的命令叫作魔法命令
# 下面的魔法命令可以使图片分辨率更高
%config InlineBackend.figure_format = 'retina' 
import matplotlib.pyplot as plt      # 载入Matplotlib的pyplot函数plt.plot()用于绘制散点图和线图。下面代码用于绘制 0~7的正弦函数曲线。其中,函数
0~7的正弦函数曲线。其中,函数plt.plot()可以自动识别第一个参数是 轴坐标,第二个参数是
轴坐标,第二个参数是 轴坐标,函数
轴坐标,函数plt.show()用于绘制图形。
x = np.arange(0, 7, 0.1)
y = np.sin(x)
plt.plot(x, y, color="c", linestyle="-", linewidth=1, \
         marker = "o")
plt.show()通过Matplotlib库绘制的正弦函数曲线如图2-14所示。
函数plt.plot()还可以设置很多参数,这些参数可以改变线图或者散点图的呈现结果。常用的参数有color(颜色)、linestyle(线条类型)、marker(点类型)、linewidth(线条宽度)。linewidth值越大,线条越宽。
Matplotlib库常用的表示颜色的字符如表2-7所示。
图2-14 通过Matplotlib库绘制的正弦函数曲线
表2-6 Matplotlib库常用的表示颜色的字符
| 表示颜色的字符 | 颜色 | 
|---|---|
| b | 蓝色 | 
| c | 青色 | 
| g | 绿色 | 
| k | 黑色 | 
| r | 红色 | 
| m | 洋红色 | 
| y | 黄色 | 
| w | 白色 | 
Matplotlib库常用的表示线条类型的字符如表2-7所示。
表2-7 Matplotlib库常用的表示线条类型的字符
| 表示线条类型的字符 | 线条类型 | 
|---|---|
| - | 实线 | 
| -- | 虚线 | 
| – | 破折线 | 
| -. | 点画线 | 
Matplotlib库常用的表示点类型的字符如表2-8所示。
表2-8 Matplotlib库常用的表示点类型的字符
| 表示点类型的字符 | 点类型 | 
|---|---|
| o | 圆圈 | 
| h | 六边形 | 
| . | 点 | 
| * | 星号 | 
| D | 菱形 | 
| _ | 水平线 | 
| s | 正方形 | 
| 8 | 八边形 | 
为了把多条线或者不同的散点图画在同一幅图中,并且用函数plt.legend()在图上加上图例,需要在函数plt.plot()中加入参数label,label参数的值会显示在图例中。以下示例代码的运行结果如图2-15所示。
x = np.arange(0, 7, 0.1);
y = np.sin(x)
z = np.cos(x)
plt.plot(x, y, color="c", linestyle="-", linewidth=1,\
         marker = "o", label="sin")
plt.plot(x, z, color="m", linestyle="--", linewidth=2, \
         marker = "D", label="cos")
plt.legend()
plt.show()图2-15 通过Matplotlib库添加图例
我们也可以使用函数plt.xlabel()和plt.ylabel()为横坐标轴与纵坐标轴添加名称。以下示例代码的运行结果如图2-16所示。
plt.plot(x, y, color="c", linestyle="-", linewidth=1,\
         marker = "o", label="sin")
plt.plot(x, z, color="m", linestyle="--", linewidth=2,\ 
         marker = "D", label="cos")
plt.legend()
plt.xlabel("x")
plt.ylabel("y")
plt.show()图2-16 利用函数plt.xlabel()和plt.ylabel()添加坐标轴名称
函数plt.scatter()用于绘制散点图,如图2-17所示。
x = np.random.normal(size=100)  # 产生100个服从标准正态分布的随机数
y = 2*x + np.random.normal(size=100) 
plt.scatter(x, y)
plt.xlabel("x")
plt.ylabel("y")
plt.show()图2-17 利用函数plt.scatter()绘制散点图
本章介绍的线性代数、微积分、概率论和Python相关知识可以帮助你轻松理解与应用深度学习。
在学习Anaconda、Jupyter Notebook或者Python时,你一定要自己输入命令或者代码,思考并且观察输出结果。这样,你的编程技巧一定可以快速提升。