第2章 Pytorch基础


Pytorch是Facebook团队于2017年1月发布的一个深度学习框架,虽然晚于TensorFlow、Keras等框架,但自发布之日起,其关注度就在不断上升,目前在GitHub上的热度已超过Theano、Caffe、MXNet等框架。
Pytorch 1.0版本推出后,增加了很多新功能,对原有内容进行了优化,并整合了caffe2,使用更方便,也大大增强其生产性,所以其热度在迅速上升。
Pytorch采用python语言接口来实现编程,非常容易上手。它就像带GPU的Numpy,与Python一样都属于动态框架。PyTorch继承了Torch灵活、动态的编程环境和用户友好的界面,支持以快速和灵活的方式构建动态神经网络,还允许在训练过程中快速更改代码而不妨碍其性能,支持动态图形等尖端AI模型的能力,是快速实验的理想选择。本章主要介绍Pytorch的一些基础且常用的概念和模块,具体包括如下内容:
为何选择Pytorch
Pytorch环境的安装与配置
Numpy与Tensor
Tensor与Autograd
使用Numpy实现机器学习
使用Tensor及antograd实现机器学习
使用TensorFlow架构

2.1 为何选择Pytorch?

PyTorch是一个建立在Torch库之上的Python包,旨在加速深度学习应用。它提供一种类似NumPy的抽象方法来表征张量(或多维数组),它可以利用GPU来加速训练。由于 PyTorch 采用了动态计算图(dynamic computational graph)结构,且基于tape的autograd 系统的深度神经网络。其它很多框架,比如 TensorFlow(TensorFlow2.0也加入了动态网络的支持)、Caffe、CNTK、Theano 等,采用静态计算图。 使用 PyTorch,通过一种我们称之为Reverse-mode auto-differentiation(反向模式自动微分)的技术,你可以零延迟或零成本地任意改变你的网络的行为。
torch是Pytorch中的一个重要包,它包含了多维张量的数据结构以及基于其上的多种数学操作。
自2015 年谷歌开源 TensorFlow以来,深度学习框架之争越来越激烈,全球多个看重 AI 研究与应用的科技巨头均在加大这方面的投入。Pytorch从 2017 年年初发布以来,PyTorch 可谓是异军突起,短时间内取得了一系列成果,成为其中的明星框架。最近Pytorch进行了一些较大的版本更新,0.4版本把Varable与Tensor进行了合并,增加了Windows的支持。1.0版本增加了JIT(全称Justintimecompilation,即时编译,它弥补了研究与生产的部署的差距)、更快的分布式、C++扩展等。
PyTorch 1.0 稳定版已发布,PyTorch 1.0 从 Caffe2 和 ONNX 移植了模块化和产品导向的功能,并将它们和 PyTorch 已有的灵活、专注研究的特性相结合。PyTorch 1.0 中的技术已经让很多 Facebook 的产品和服务变得更强大,包括每天执行 60 亿次文本翻译。
PyTorch由4个主要包组成:
torch:类似于Numpy的通用数组库,可将张量类型转换为torch.cuda.TensorFloat,并在GPU上进行计算。
torch.autograd:用于构建计算图形并自动获取梯度的包。
torch.nn:具有共享层和损失函数的神经网络库。
torch.optim:具有通用优化算法(如SGD,Adam等)的优化包。

2.2 安装配置

安装Pytorch时,请核查当前环境是否有GPU,如果没有,则安装CPU版;如果有,则安装GPU版本的。

2.2.1 安装CPU版Pytorch

安装CPU版的Pytorch比较简单,Pytorch是基于Python开发,所以如果没有安装Python需要先安装,然后再安装Pytorch。具体步骤如下:
(1)下载Python
安装Python建议采用anaconda方式安装,先从Anaconda的官网:https://www.anaconda.com/distribution, 如图2-1 所示。

图2-1 下载Anaconda界面
下载Anaconda3的最新版本,如Anaconda3-5.0.1-Linux-x86_64.sh,建议使用3系列,3系列代表未来发展。另外,下载时根据自己环境,选择操作系统等。
(2)在命令行,执行如下命令,开始安装Python:

(3)接下来根据安装提示,直接按回车即可。其间会提示选择安装路径,如果没有特殊要求,可以按回车使用默认路径(~/ anaconda3),然后就开始安装。
(4)安装完成后,程序提示是否把anaconda3的binary路径加入到当前用户的.bashrc配置文件中,建议添加。添加以后,就可以使用python、ipython命令时自动使用Anaconda3的python环境。
(5)安装Pytorch
登录Pytorch官网:https://pytorch.org/,登录后,可看到图2-2 所示界面,然后选择对应项。

图2-2 Pytorch 安装界面

把第⑥项内容,复制到命令行,执行即可。

(6)验证安装是否成功
启动Python,然后执行如下命令,如果没有报错,说明安装成功!

2.2.2 安装GPU版Pytorch

安装GPU版本的Pytorch稍微复杂一点,除需要安装Python、Pytorch,还需要安装GPU的驱动(如英伟达的Nvidia)及cuda、cuDNN计算框架,主要步骤如下:
(1)安装NVIDIA驱动
下载地址:https://www.nvidia.cn/Download/index.aspx?lang=cn
登录可以看到图2-3的界面:

图2-3 NVIDIA的下载界面
选择产品类型、操作系统等,然后点击搜索按钮,进入下载界面。
安装完成后,在命令行输入:nvidia-smi 用来显示GPU卡的基本信息,如果出现图2-4,则说明安装成功。如果报错,则说明安装失败,请搜索其他安装驱动的方法。

图2-4 显示GPU卡的基本信息
(2)安装cuda
CUDA(Compute Unified Device Architecture),是英伟达公司推出的一种基于新的并行编程模型和指令集架构的通用计算架构,它能利用英伟达GPU的并行计算引擎,比CPU更高效的解决许多复杂计算任务。安装CUDA Driver时需与NVIDIA GPU Driver的版本一致,CUDA才能找到显卡。
(3)安装cuDNN
NVIDIA cuDNN是用于深度神经网络的GPU加速库。注册NVIDIA并下载cuDNN包:https://developer.nvidia.com/rdp/cudnn-archive。
(4)安装Python及Pytorch
这步与本书2.2.1小节 安装CPU版Pytorch相同,只是选择cuda时,不是None,而是对应cuda的版本号。如图2-5所示。

图2-5 安装GPU版Pytorch
(5)验证
验证Pytorch安装是否成功与本书2.2.1小节一样,如果想进一步验证Pytorch是否在使用GPU,可以运行以下一段测试GPU的程序test_gpu.py,如果成功的话,可以看到如图2-6的效果。

在命令行运行以下脚本:

如果可以看到如图2-6或图2-7的结果,说明安装GPU版Pytorch成功!

图2-6 运行test_gpu.py的结果
在命令行运行:nvidia-smi,可以看到如图2-7所示界面。

图2-7 含GPU进程的显卡信息

2.3 Jupyter Notebook环境配置

Jupyter Notebook是目前Python比较流行的开发、调试环境,此前被称为 IPython notebook,以网页的形式打开,可以在网页页面中直接编写代码和运行代码,代码的运行结果(包括图形)也会直接显示。如在编程过程中添加注释、目录、图像或公式等内容,Jupyter Notebook有以下特点:
编程时具有语法高亮、缩进、tab补全的功能。
可直接通过浏览器运行代码,同时在代码块下方展示运行结果。
以富媒体格式展示计算结果。富媒体格式包括:HTML,LaTeX,PNG,SVG等。
对代码编写说明文档或语句时,支持Markdown语法。
支持使用LaTeX编写数学性说明。
接下来介绍配置Jupyter Notebook的主要步骤。
(1)生成配置文件

将在当前用户目录下生成文件:.jupyter/jupyter_notebook_config.py
(2)生成当前用户登录jupyter密码
打开ipython, 创建一个密文密码

(3)修改配置文件

进行如下修改:

(4)启动jupyter notebook

在浏览器上,输入IP:port,即可看到如下类似界面。

接下来就可以在浏览器进行开发调试Pytorch、Python等任务了。

2.4 Numpy与Tensor

第1章我们介绍了Numpy,知道其存取数据非常方便,而且还拥有大量的函数,所以深得数据处理、机器学习者喜爱。这节我们将介绍Pytorch的Tensor,它可以是零维(又称为标量或一个数)、一维、二维及多维的数组。其自称为神经网络界的Numpy, 它与Numpy相似,它们共享内存,它们之间的转换非常方便和高效。不过它们也有不同之处,最大的区别就是Numpy 会把 ndarray 放在 CPU 中加速运算,而 由torch 产生的 tensor 会放在 GPU 中加速运算 (假设当前环境有GPU)。

2.4.1 Tensor概述

对tensor的操作很多,从接口的角度来划分,可以分为两类:
(1)torch.function,如torch.sum、torch.add等,
(2)tensor.function,如tensor.view、tensor.add等。
这些操作对大部分tensor都是等价的,如torch.add(x,y)与x.add(y)等价。实际使用中可以根据个人爱好选择。
如果从修改方式的角度,可以分为以下两类:
(1)不修改自身数据,如x.add(y),x的数据不变,返回一个新的tensor。
(2)修改自身数据,如x.add_(y)(运行符带下划线后缀),运算结果存在x中,x被修改。

运行结果
tensor([4, 6])
tensor([1, 2])
tensor([4, 6])

2.4.2 创建Tensor

新建tensor的方法很多,可以从列表或ndarray等类型进行构建,也可根据指定的形状构建。常见的构建tensor的方法,可参考表2-1。
表2-1 常见的新建tensor方法

下面举例说明。

【说明】注意torch.Tensor与torch.tensor的几点区别
①torch.Tensor是torch.empty和torch.tensor之间的一种混合,但是,当传入数据时,torch.Tensor使用全局默认dtype(FloatTensor),torch.tensor从数据中推断数据类型。
②torch.tensor(1)返回一个固定值1,而torch.Tensor(1)返回一个大小为1的张量,它是随机初始化的值。

运行结果
t1的值tensor([3.5731e-20]),t1的数据类型torch.FloatTensor
t2的值1,t2的数据类型torch.LongTensor

根据一定规则,自动生成tensor的一些例子。

2.4.3 修改Tensor形状

在处理数据、构建网络层等过程中,经常需要了解Tensor的形状、修改Tensor的形状。
与修改Numpy的形状类似,修改tenor的形状也有很多类似函数,具体可参考表2-2。 表2-2 为tensor常用修改形状的函数。

以下为一些实例

【说明】torch.view与torch.reshpae的异同
①reshape()可以由torch.reshape(),也可由torch.Tensor.reshape()调用。view()只可由torch.Tensor.view()来调用。
②对于一个将要被view的Tensor,新的size必须与原来的size与stride兼容。否则,在view之前必须调用contiguous()方法。
③同样也是返回与input数据量相同,但形状不同的tensor。若满足view的条件,则不会copy,若不满足,则会copy
④如果您只想重塑张量,请使用torch.reshape。 如果您还关注内存使用情况并希望确保两个张量共享相同的数据,请使用torch.view。

2.4.4 索引操作

Tensor的索引操作与Numpy类似,一般情况下索引结果与源数据共享内存。从tensor获取元素除了可以通过索引,也可借助一些函数,常用的选择函数,可参考表2-3。
表2-3 常用选择操作函数

以下为部分函数的实现代码:

2.4.5 广播机制

本书1.7小节介绍了Numpy的广播机制,是向量运算的重要技巧。Pytorch也支持广播规则,以下通过几个示例进行说明。

2.4.6 逐元素操作

与Numpy一样,tensor也有逐元素操作(element-wise),操作内容相似,但使用函数可能不尽相同。大部分数学运算都属于逐元操作,逐元素操作输入与输出的形状相同。,常见的逐元素操作,可参考表2-4。
表2-4常见逐元素操作

【说明】这些操作均创建新的tensor,如果需要就地操作,可以使用这些方法的下划线版本,例如abs_。
以下为部分逐元素操作代码实例。

2.4.7 归并操作

归并操作顾名思义,就是对输入进行归并或合计等操作,这类操作的输入输出形状一般不相同,而且往往是输入大于输出形状。归并操作可以对整个tensor,也可以沿着某个维度进行归并。常见的归并操作可参考表2-5。
表2-5 常见的归并操作

【说明】
归并操作一般涉及一个dim参数,指定沿哪个维进行归并。另一个参数是keepdim,说明输出结果中是否保留维度1,缺省情况是False,即不保留。
以下为归并操作的部分代码

2.4.8 比较操作

比较操作一般进行逐元素比较,有些是按指定方向比较。常用的比较函数可参考表2-6。
表2-6 常用的比较函数

以下是部分函数的代码实现。

2.4.9 矩阵操作

机器学习和深度学习中存在大量的矩阵运算,用的比较多的有两种,一种是逐元素乘法,另外一种是点积乘法。Pytorch中常用的矩阵函数可参考表2-7。
表2-7 常用矩阵函数

【说明】
①torch的dot与Numpy的dot有点不同,torch中dot对两个为1D张量进行点积运算,Numpy中的dot无此限制。
②mm是对2D的矩阵进行点积,bmm对含batch的3D进行点积运算。
③转置运算会导致存储空间不连续,需要调用contiguous方法转为连续。

2.4.10 Pytorch与Numpy比较

Pytorch与Numpy有很多类似的地方,并且有很多相同的操作函数名称,或虽然函数名称不同但含义相同;当然也有一些虽然函数名称相同,但含义不尽相同。对此,有时很容易混淆,下面我们把一些主要的区别进行汇总,具体可参考表2-8。
表2-8 Pytorch与Numpy函数对照表

2.5 Tensor与Autograd

在神经网络中,一个重要内容就是进行参数学习,而参数学习离不开求导,Pytorch是如何进行求导的呢?
现在大部分深度学习架构都有自动求导的功能,Pytorch也不列外,torch.autograd包就是用来自动求导的。autograd包为张量上所有的操作提供了自动求导功能,而torch.Tensor和torch.Function为autograd上的两个核心类,他们相互连接并生成一个有向非循环图。接下来我们先简单介绍tensor如何实现自动求导,然后介绍计算图,最后用代码实现这些功能。

2.5.1 自动求导要点

autograd包为对tensor进行自动求导,为实现对tensor自动求导,需考虑如下事项:
(1)创建叶子节点(leaf node)的tensor,使用requires_grad参数指定是否记录对其的操作,以便之后利用backward()方法进行梯度求解。requires_grad参数缺省值为False,如果要对其求导需设置为True,与之有依赖关系的节点自动变为True。
(2)可利用requires_grad_()方法修改tensor的requires_grad属性。可以调用.detach()或with torch.no_grad():将不再计算张量的梯度,跟踪张量的历史记录。这点在评估模型、测试模型阶段常常使用。
(3)通过运算创建的tensor(即非叶子节点),会自动被赋于grad_fn属性。该属性表示梯度函数。叶子节点的grad_fn为None。
(4)最后得到的tensor执行backward()函数,此时自动计算各变在量的梯度,并将累加结果保存grad属性中。计算完成后,非叶子节点的梯度自动释放。
(5)backward()函数接受参数,该参数应和调用backward()函数的Tensor的维度相同,或者是可broadcast的维度。如果求导的tensor为标量(即一个数字),backward中参数可省略。
(6)反向传播的中间缓存会被清空,如果需要进行多次反向传播,需要指定backward中的参数retain_graph=True。多次反向传播时,梯度是累加的。
(7)非叶子节点的梯度backward调用后即被清空。
(8)可以通过用torch.no_grad()包裹代码块来阻止autograd去跟踪那些标记为.requesgrad=True的张量的历史记录。这步在测试阶段经常使用。
整个过程中,Pytorch采用计算图的形式进行组织,该计算图为动态图,它的计算图在每次前向传播时,将重新构建。其他深度学习架构,如TensorFlow、Keras一般为静态图。接下来我们介绍计算图,用图的形式来描述就更直观了,该计算图为有向无环图(DAG)。

2.5.2计算图

计算图是一种有向无环图像,用图形方式表示算子与变量之间的关系,直观高效。如图2-8所示,圆形表示变量,矩阵表示算子。如表达式:z=wx+b,可写成两个表示式:y=wx,则z=y+b,其中x、w、b为变量,是用户创建的变量,不依赖于其他变量,故又称为叶子节点。为计算各叶子节点的梯度,需要把对应的张量参数requires_grad属性设置为True,这样就可自动跟踪其历史记录。y、z是计算得到的变量,非叶子节点,z为根节点。mul和add是算子(或操作或函数)。由这些变量及算子,就构成一个完整的计算过程(或前向传播过程)。

图2-8 正向传播计算图
我们的目标是更新各叶子节点的梯度,根据复合函数导数的链式法则,不难算出各叶子节点的梯度。

Pytorch调用backward(),将自动计算各节点的梯度,这是一个反向传播过程,这个过程可用图2-9表示。在反向传播过程中,autograd沿着图2-9,从当前根节点z反向溯源,利用导数链式法则,计算所有叶子节点的梯度,其梯度值将累加到grad属性中。对非叶子节点的计算操作(或function)记录在grad_fn属性中,叶子节点的grad_fn值为None。

图2-9 梯度反向传播计算图
下面我们用代码实现这个计算图。

2.5.3 标量反向传播

假设x、w、b都是标量,z=wx+b,对标量z调用backward(),我们无需对backward()传入参数。以下是实现自动求导的主要步骤:
(1)定义叶子节点及算子节点

运行结果
x,w,b的require_grad属性分别为:False,True,True
(2)查看叶子节点、非叶子节点的其他属性

(3)自动求导,实现梯度方向传播,即梯度的反向传播。

2.5.4 非标量反向传播

2.5.3小节我们介绍了当目标张量为标量时,调用backward()无需传入参数。目标张量一般是标量,如我们经常使用的损失值Loss,一般都是一个标量。但也有非标量的情况,后面我们介绍的Deep Dream的目标值就是一个含多个元素的张量。如何对非标量进行反向传播呢?Pytorch有个简单的规定,不让张量(tensor)对张量求导,只允许标量对张量求导,因此,如果目标张量对一个非标量调用backward(),需要传入一个gradient参数,该参数也是张量,而且需要与调用backward()的张量形状相同。为什么要传入一个张量gradient?
传入这个参数就是为了把张量对张量求导转换为标量对张量求导。这有点拗口,我们举一个例子来说,假设目标值为loss=(y_1,y_2,…,y_m)传入的参数为v=(v_1,v_2,…,v_m),那么就可把对loss的求导,转换为对loss*v^T标量的求导。即把原来∂loss/∂x得到雅可比矩阵(Jacobian)乘以张量v^T,便可得到我们需要的梯度矩阵。
backward函数的格式为:

上面说的可能有点抽象,下面我们通过一个实例进行说明。
(1)定义叶子叶子节点及计算节点

(2)手工计算y对x的梯度
我们先手工计算一下y对x的梯度,为了验证Pytorch的backward的结果是否正确。
y对x的梯度是一个雅可比矩阵,各项的值,我们可通过以下方法进行计算。

(3)调用backward获取y对x的梯度

这个结果与我们手工运算的不符,显然这个结果是错误的,错在哪里呢?这个结果的计算过程是:

由此,错在v的取值错误,通过这种方式得的到并不是y对x的梯度。这里我们可以分成两步的计算。首先让v=(1,0)得到y_1对x的梯度,然后使v=(0,1),得到y_2对x的梯度。这里因需要重复使用backward(),需要使参数retain_graph=True,具体代码如下:

运行结果
tensor([[4., 3.],[2., 6.]])
这个结果与手工运行的式(2.5)结果一致。

2.6 使用Numpy实现机器学习

前面我们介绍了Numpy、Tensor的基础内容,对如何用Numpy、Tensor操作数组有了一定认识。为了加深大家对Pytorch是如何进行完成机器学习、深度学习,本章剩余章节将分别用Numpy、Tensor、autograd、nn及optimal实现同一个机器学习任务,比较他们之间的异同及各自优缺点,从而加深对Pytorch的理解。
首先,我们用最原始的Numpy实现有关回归的一个机器学习任务,不用Pytorch中的包或类。这种方法代码可能多一点,但每一步都是透明的,有利于理解每步的工作原理。
主要步骤包括:
首先,是给出一个数组x,然后基于表达式:y=3x^2+2,加上一些噪音数据到达另一组数据y。
然后,构建一个机器学习模型,学习表达式y=wx^2+b的两个参数w,b。利用数组x,y的数据为训练数据
最后,采用梯度梯度下降法,通过多次迭代,学习到w、b的值。
以下为具体步骤:
(1)导入需要的库

(2)生成输入数据x及目标数据y
设置随机数种子,生成同一个份数据,以便用多种方法进行比较。

(3)查看x,y数据分布情况

图2-10 Numpy实现的源数据
(4)初始化权重参数

(5)训练模型
定义损失函数,假设批量大小为100:

用代码实现上面这些表达式:

(6)可视化结果

运行结果:

图2-11 可视化Numpy学习结果

[[2.95859544]] [[2.10178594]]
从结果看来,学习效果还是比较理想的。

2.7 使用Tensor及antograd实现机器学习

2.6节可以说是纯手工完成一个机器学习任务,数据用Numpy表示,梯度及学习是自己定义并构建学习模型。这种方法适合于比较简单的情况,如果稍微复杂一些,代码量将几何级增加。是否有更方便的方法呢?这节我们将使用Pytorch的自动求导的一个包antograd,利用这个包及对应的Tensor,便可利用自动反向传播来求梯度,无需手工计算梯度。以下是具体实现代码。
(1)导入需要的库

(2)生成训练数据,并可视化数据分布情况

图2-12 可视化输入数据
(3)初始化权重参数

(4)训练模型

(5)可视化训练结果

运行结果:

图2-13 使用 antograd的结果
tensor([[2.9645]], requires_grad=True) tensor([[2.1146]], requires_grad=True)
这个结果与使用Numpy机器学习的差不多。

2.8 使用TensorFlow架构

2.6小节我们用Numpy实现了回归分析,2.7 我们用Pytorch的autograd及Tensor实现了这个任务。这节我们用深度学习的另一个框架TensorFlow实现该回归分析任务,大家可比较一下,使用不同架构之间的一些区别。为便于比较,这里使用TensorFlow的静态图(TensorFlow2.0 新增核心功能Eager Execution,并把Eager Execution变为 TensorFlow 默认的执行模式。这意味着 TensorFlow 如同 PyTorch 那样,由编写静态计算图全面转向了动态计算图)。
(1)导入库及生成训练数据

(2)初始化参数

(3)实现前向传播及损失函数

(4)训练模型

(5)可视化结果

最后五次输出结果:
损失值、权重、偏移量分别为0.0094,[2.73642],[2.1918662]
损失值、权重、偏移量分别为0.0065,[2.8078585],[2.1653984]
损失值、权重、偏移量分别为0.0050,[2.8592768],[2.1463478]
损失值、权重、偏移量分别为0.0042,[2.896286],[2.132636]
损失值、权重、偏移量分别为0.0038,[2.922923],[2.1227665]

图2-14 使用Tensorflow的结果
迭代2000次后,损失值达到0.0038,权重和偏移量分别2.92、2.12,与目标值3、2是比较接近了,当然如果增加迭代次数,精度将进一步提升。大家可以尝试一下。
TensorFlow使用静态图,其特点是先构造图形(如果不显式说明,TensorFlow会自动构建一个缺省图形),然后启动session,开始执行相关程序,这个时候程序才开始运行,前面都是铺垫,所以也没有运行结果。而Pytorch的动态图,动态最关键一点就是它是交互式的,而且执行每个命令马上就可看到结果,这对训练、发现问题、纠正问题非常方便,其构图是一个叠加过程或动态过程,期间我们可以随时添加内容。这些特征对于训练和调式过程无疑是非常有帮助的,这或许也是Pytorch为何在高校、科研院所深得大家喜爱的重要原因。

2.9 小结

本章主要介绍Pytorch的基础知识,这些内容是后续章节的重要支撑。首先介绍了Pytorch的安装配置,然后介绍了Pytorch的重要数据结构Tensor。Tensor类似于Numpy的数据结构,但Tensor提供GPU加速及自动求导等技术。最后分别用Numpy、Tensor、autograd、TensorFlow等技术分别实现同一个机器学习任务。

第3章 Pytorch神经网络工具箱

前面我们介绍了Pytorch的数据结构及自动求导机制,充分运行这些技术可以大大提高我们的开发效率。这章将介绍Pytorch的另一利器:神经网络工具箱。利用这个工具箱,设计一个神经网络就像搭积木一样,可以极大简化我们构建模型的任务。
本章主要讨论如何使用Pytorch神经网络工具箱来构建网络,我们可以学习如下内容:
 介绍神经网络核心组件
 如何构建一个神经网络
 详细介绍如何构建一个神经网络
 如何使用nn模块中Module及functional
 如何选择优化器
 动态修改学习率参数

3.1 神经网络核心组件

神经网络看起来很复杂,节点很多,层数多,参数更多。但核心部分或组件不多,把这些组件确定后,这个神经网络基本就确定了。这些核心组件包括:
(1)层:神经网络的基本结构,将输入张量转换为输出张量。
(2)模型:层构成的网络。
(3)损失函数:参数学习的目标函数,通过最小化损失函数来学习各种参数。
(4)优化器:如何是损失函数最小,这就涉及到优化器。
当然这些核心组件不是独立的,它们之间、以及它们与神经网络其他组件之间有密切关系。为便于大家理解,我们把这些关键组件及相互关系,用图3-1表示。

 

图3-1 神经网络关键组件及相互关系示意图
多个层链接在一起构成一个模型或网络,输入数据通过这个模型转换为预测值,然后损失函数把预测值与真实值进行比较,得到损失值(损失值可以是距离、概率值等),该损失值用于衡量预测值与目标结果的匹配或相似程度,优化器利用损失值更新权重参数,从而使损失值越来越小。这是一个循环过程,损失值达到一个阀值或循环次数到达指定次数,循环结束。
接下来利用Pytorch的nn工具箱,构建一个神经网络实例。nn中对这些组件都有现成包或类,可以直接使用,非常方便。

3.2实现神经网络实例

使用Pytorch构建神经网络使用的主要工具(或类)及相互关系,如图3-2所示。

图3-2 Pytorch实现神经网络主要工具及相互关系
从图3-2可知,构建网络层可以基于Module类或函数(nn.functional)。nn中的大多数层(layer)在functional中都有与之对应的函数。nn.functional中函数与nn.Module中的layer的主要区别是后者继承Module类,会自动提取可学习的参数。而nn.functional更像是纯函数。两者功能相同,性能也没有很大区别,那么如何选择呢?像卷积层、全连接层、dropout层等因含有可学习参数,一般使用nn.Module,而激活函数、池化层不含可学习参数,可以使用nn.functional中对应的函数。下面我们通过实例来说明如何使用nn构建一个网络模型。

3.2.1背景说明

这节将利用神经网络完成对手写数字进行识别的实例,来说明如何借助nn工具箱来实现一个神经网络,并对神经网络有个直观了解。在这个基础上,后续我们将对nn的各模块进行详细介绍。实例环境使用Pytorch1.0+,GPU或CPU,源数据集为MNIST。
主要步骤:
(1)利用Pytorch内置函数mnist下载数据
(2)利用torchvision对数据进行预处理,调用torch.utils建立一个数据迭代器
(3)可视化源数据
(4)利用nn工具箱构建神经网络模型
(5)实例化模型,并定义损失函数及优化器
(6)训练模型
(7)可视化结果
神经网络的结构如下:

3-3 神经网络结构图
使用两个隐含层,每层激活函数为Relu,最后使用torch.max(out,1)找出张量out最大值对应索引作为预测值。

3.2.2准备数据

(1)导人必要的模块

(2)定义一些超参数

(3)下载数据并对数据进行预处理

【说明】
①transforms.Compose可以把一些转换函数组合在一起;
②Normalize([0.5], [0.5])对张量进行归一化,这里两个0.5分别表示对张量进行归一化的全局平均值和方差。因图像是灰色的只有一个通道,如果有多个通道,需要有多个数字,如三个通道,应该是Normalize([m1,m2,m3], [n1,n2,n3])
③download参数控制是否需要下载,如果./data目录下已有MNIST,可选择False。
④用DataLoader得到生成器,这可节省内存。
⑤torchvision及data的使用第4章将详细介绍。

3.2.3可视化源数据

图3-4 MNIST源数据示例

3.2.4 构建模型

数据预处理之后,我们开始构建网络,创建模型。
(1)构建网络

(2)实例化网络

3.2.5 训练模型

训练模型,这里使用for循环,进行迭代。其中包括对训练数据的训练模型,然后用测试数据的验证模型。
(1)训练模型

最后5次迭代的结果
epoch: 15, Train Loss: 0.0047, Train Acc: 0.9995, Test Loss: 0.0543, Test Acc: 0.9839
epoch: 16, Train Loss: 0.0048, Train Acc: 0.9997, Test Loss: 0.0532, Test Acc: 0.9839
epoch: 17, Train Loss: 0.0049, Train Acc: 0.9996, Test Loss: 0.0544, Test Acc: 0.9839
epoch: 18, Train Loss: 0.0049, Train Acc: 0.9995, Test Loss: 0.0535, Test Acc: 0.9839
epoch: 19, Train Loss: 0.0049, Train Acc: 0.9996, Test Loss: 0.0536, Test Acc: 0.9836

这个神经网络的结构比较简单,只用了两层,也没有使用dropout层,迭代20次,测试准确率达到98%左右,效果还可以。不过,还是有提升空间,如果采用cnn,dropout等层,应该还可以提升模型性能。
(2)可视化训练及测试损失值

图3-5 MNIST数据集训练的损失值

3.3 如何构建神经网络?

上节我们用nn工具箱,搭建一个神经网络。步骤好像不少,但关键就是选择网络层,构建网络,然后选择损失和优化器。在nn工具箱中,可以直接引用的网络很多,有全连接层、卷积层、循环层、正则化层、激活层等等。假设这些层都定义好了,接下来就是如何组织或构建这些层?

3.3.1 构建网络层

在3.2小节实例中,我们采用torch.nn.Sequential()来构建网络层,这个有点类似Keras的models.Sequential(),使用起来就像搭积木一样,非常方便。不过,这种方法每层的编码是默认的数字,不易区分。
如果要对每层定义一个名称,我们可以采用Sequential的一种改进方法,在Sequential的基础上,通过add_module()添加每一层,并且为每一层增加一个单独的名字。
此外,还可以在Sequential基础上,通过字典的形式添加每一层,并且设置单独的层名称。
以下是采用字典方式构建网络的一个示例代码:

3.3.2 前向传播

定义好每层后,最后还需要通过前向传播的方式把这些串起来。这就是涉及如何定义forward函数的问题。forward函数的任务需要把输入层、网络层、输出层链接起来,实现信息的前向传导。该函数的参数一般为输入数据,返回值为输出数据。
在forward函数中,有些层来自nn.Module,也可以使用nn.functional定义。来自nn.Module的需要实例化,而使用nn.functional定义的可以直接使用。

3.3.3 反向传播

前向传播函数定义好以后,接下来就是梯度的反向传播。在第二章,介绍了实现梯度反向传播的方法。这里关键是利用复合函数的链式法则。深度学习中涉及很多函数,如果要自己手工实现反向传播,比较费时。好在Pytorch提供了自动反向传播的功能,使用nn工具箱,我们无需自己编写反向传播,直接让损失函数(loss)调用backward()即可,非常方便和高效!
在反向传播过程中,优化器是一个重要角色。优化方法很多,3.2节采用SGD优化器。此外,我们还可以选择其他优化器,3.7小节将介绍各种优化器的优缺点。

3.3.4 训练模型

层、模型、损失函数和优化器等都定义或创建好,接下来就是训练模型。训练模型时需要注意使模型处于训练模式,即调用model.train()。调用model.train()会把所有的module设置为训练模式。如果是测试或验证阶段,需要使模型处于验证阶段,即调用model.eval()。调用model.eval()会把所有的training属性设置为False。
缺省情况下梯度是累加的,需要手工把梯度初始化或清零,调用optimizer.zero_grad()即可。训练过程中,正向传播生成网络的输出,计算输出和实际值之间的损失值。 调用loss.backward()自动生成梯度,然后使用optimizer.step()执行优化器,把梯度传播回每个网络。
如果希望用GPU训练,需要把模型、训练数据、测试数据发送到GPU上,即调用.to(device)。如果需要使用多GPU进行处理,可使模型或相关数据引用nn.DataParallel。nn.DataParallel的具体使用在第4章将详细介绍。

3.4 nn.Module

前面我们使用autograd及Tensor实现机器学习实例时,需要做不少设置,如对叶子节点的参数requires_grad设置为True,然后调用backward,再从grad属性中提取梯度。对于大规模的网络,autograd太过于底层和繁琐。为了简单、有效解决这个问题,nn是一个有效工具。它是专门为深度学习设计的一个模块,而nn.Module是nn的一个核心数据结构。nn.Module可以是神经网络的某个层(layer),也可以是包含多层的神经网络。在实际使用中,最常见的做法是继承nn.Module,生成自己的网络/层,如3.2小节实例中,我们定义的Net类就采用这种方法(class Net(torch.nn.Module))。nn中已实现了绝大多数层,包括全连接层、损失层、激活层、卷积层、循环层等等,这些层都是nn.Module的子类,能够自动检测到自己的Parameter,并将其作为学习参数,且针对GPU运行进行了CuDNN优化。

3.5 nn.functional

nn中的层,一类是继承了nn.Module,其命名一般为nn.Xxx(第一个是大写),如nn.Linear、nn.Conv2d、nn.CrossEntropyLoss等。另一类是nn.functional中的函数,其名称一般为nn.funtional.xxx,如nn.funtional.linear、nn.funtional.conv2d、nn.funtional.cross_entropy等。从功能来说两者相当,基于nn.Mudle能实现的层,使用nn.funtional也可实现,反之亦然,而且性能方面两者也没有太大差异。不过在具体使用时,两者还是有区别,主要区别如下:
(1)nn.Xxx继承于nn.Module,nn.Xxx 需要先实例化并传入参数,然后以函数调用的方式调用实例化的对象并传入输入数据。它能够很好的与nn.Sequential结合使用,而nn.functional.xxx无法与nn.Sequential结合使用。
(2)nn.Xxx不需要自己定义和管理weight、bias参数;而nn.functional.xxx需要你自己定义weight、bias,每次调用的时候都需要手动传入weight、bias等参数, 不利于代码复用。
(3)dropout操作在训练和测试阶段是有区别的,使用nn.Xxx方式定义dropout,在调用model.eval()之后,自动实现状态的转换,而使用nn.functional.xxx却无此功能。
总的来说,两种功能都是相同的,但PyTorch官方推荐:具有学习参数的(例如,conv2d, linear, batch_norm)采用nn.Xxx方式。没有学习参数的(例如,maxpool, loss func, activation func)等根据个人选择使用nn.functional.xxx或者nn.Xxx方式。3.2小节中使用激活层,我们采用F.relu来实现,即nn.functional.xxx方式。

3.6 优化器

Pytoch常用的优化方法都封装在torch.optim里面,其设计很灵活,可以扩展为自定义的优化方法。所有的优化方法都是继承了基类optim.Optimizer。并实现了自己的优化步骤。
最常用的优化算法就是梯度下降法及其各种变种,后续章节我们将介绍各种算法的原理,这类优化算法使用参数的梯度值更新参数。
3.2小节使用的随机梯度下降法(SGD)就是最普通的优化器,一般SGD并说没有加速效果, 3.2小节使用的SGD包含动量参数Momentum,它是SGD的改良版。
我们结合3.2小结内容,说明使用优化器的一般步骤为:
(1)建立优化器实例
导入optim模块,实例化SGD优化器,这里使用动量参数momentum(该值一般在(0,1)之间),是SGD的改进版,效果一般比不使用动量规则的要好。

以下步骤在训练模型的for循环中。
(2)向前传播
把输入数据传入神经网络Net实例化对象model中,自动执行forward函数,得到out输出值,然后用out与标记label计算损失值loss。

(3)清空梯度
缺省情况梯度是累加的,在梯度反向传播前,先需把梯度清零。

(4)反向传播
基于损失值,把梯度进行反向传播。

(5)更新参数
基于当前梯度(存储在参数的.grad属性中)更新参数。

3.7 动态修改学习率参数

修改参数的方式可以通过修改参数optimizer.params_groups或新建optimizer。新建optimizer比较简单,optimizer十分轻量级,所以开销很小。但是新的优化器会初始化动量等状态信息,这对于使用动量的优化器(momentum参数的sgd)可能会造成收敛中的震荡。所以,这里我们采用直接修改参数optimizer.params_groups。
optimizer.param_groups:长度1的list,optimizer.param_groups[0]:长度为6的字典,包括权重参数,lr,momentum等参数。

以下是3.2小节中动态修改学习率参数代码

3.8 优化器比较

Pytorch中的优化器很多,各种优化器一般都有其适应的场景,不过,像自适应优化器在深度学习中比较受欢迎,除了性能较好,鲁棒性性、泛化能力也更强。这里我们通过一个简单实例进行说明。
(1) 导入需要的模块

(2)生成数据

(3)构建神经网络

(4)使用多种优化器

(5)训练模型

(6)可视化结果

图3-6 多种优化器性能比较

3.9 小结

本章我们首先介绍了神经网络的核心组件,即层、模型、损失函数及优化器。然后,从一个完整实例开始,看Pytorch是如何使用其包、模块等来搭建、训练、评估、优化神经网络。最后详细剖析了Pytorch的工具箱nn以及基于nn的一些常用类或模块等,并用相关实例演示这些模块的功能。这章介绍了神经网络工具箱,下一章将介绍Pytorch的另一个强大工具箱,即数据处理工具箱。

第10章 Pandas基础


Python有了NumPy的Pandas,用Python处理数据就像使用Exel或SQL一样简单方便。
Pandas是基于NumPy的Python 库,它被广泛用于快速分析数据,以及数据清洗和准备等工作。可以把 Pandas 看作是 Python版的Excel或Table。Pandas 有两种数据结构:
Series和DataFrame,Pandas经过几个版本的更新,目前已经成为数据清洗、处理和分析的不二选择。
本章主要介绍Pandas的两个数据结构:
Serial简介
DataFrame简介

10.1 问题:Pandas有哪些优势?

科学计算方面NumPy是优势,但NumPy中没有标签,数据清理、数据处理就不是其强项了。而DataFrame有标签,就像SQL中的表一样,所以在数据处理方面DataFrame就更胜一筹了,具体包含以下几方面:
(1)读取数据方面
Pandas提供强大的IO读取工具,csv格式、Excel文件、数据库等都可以非常简便地读取,对于大数据,pandas也支持大文件的分块读取。
(2)在数据清洗方面
面对数据集,我们遇到最多的情况就是存在缺失值,Pandas把各种类型数据类型的缺失值统一称为NaN,Pandas提供许多方便快捷的方法来处理这些缺失值NaN。
(3)分析建模阶段
在分析建模阶段,Pandas自动且明确的数据对齐特性,非常方便地使新的对象可以正确地与一组标签对齐,由此,Pandas就可以非常方便地将数据集进行拆分-重组操作。
(4)结果可视化方面
结果展示方面,我们都知道Matplotlib是个数据视图化的好工具,Pandas与Matplotlib搭配,不用复杂的代码,就可以生成多种多样的数据视图。

10.2 Pandas数据结构

Pandas中两个最常用的对象是Series和DataFrame。使用pandas前,需导入以下内容:

Pandas主要采用Series和DataFrame两种数据结构。Series是一种类似一维数据的数据结构,由数据(values)及索引(indexs)组成,而DataFrame是一个表格型的数据结构,它有一组序列,每列的数据可以为不同类型(NumPy数据组中数据要求为相同类型),它既有行索引,也有列索引。

图10-1 DataFrame结构

10.3 Series

上章节我们介绍了多维数组(ndarray),当然,它也包括一维数组,Series类似一维数组,为啥还要介绍Series呢?或Series有哪些特点?
Series一个最大特点就是可以使用标签索引,序列及ndarray也有索引,但都是位置索引或整数索引,这种索引有很多局限性,如根据某个有意义标签找对应值,切片时采用类似[2:3]的方法,只能取索引为2这个元素等等,无法精确定位。
Series的标签索引(它位置索引自然保留)使用起来就方便多了,且定位也更精确,不会产生歧义。以下通过实例来说明。
(1)使用Series

0 1
1 3
2 6
3 -1
4 2
5 8
dtype: int64
(2)使用Series的索引

a 1
c 3
d 6
e -1
b 2
g 8
dtype: int64
(3)根据索引找对应值

10.4 DataFrame

DataFrame除了索引有位置索引也有标签索引,而且其数据组织方式与MySQL的表极为相似,除了形式相似,很多操作也类似,这就给操作DataFrame带来极大方便。这些是DataFrame特色的一小部分,它还有比数据库表更强大的功能,如强大统计、可视化等等。
DataFrame有几个要素:index、columns、values等,columns就像数据库表的列表,index是索引,values就是值。

图10-2 DataFrame结果

10.4.1 生成DataFrame

生成DataFrame有很多,比较常用的有导入等长列表、字典、numpy数组、数据文件等。

10.4.2 获取数据

获取DataFrame结构中数据可以采用obj[]操作、obj.iloc[]、obj.loc[]等命令。
(1)使用obj[]来获取列或行

(2)使用obj.loc[] 或obj.iloc[]获取行或列数据。
loc通过行标签获取行数据,iloc通过行号获取行数据。
loc 在index的标签上进行索引,范围包括start和end.
iloc 在index的位置上进行索引,不包括end.
这两者的主要区别可参考如下示例:

【说明】
除使用iloc及loc外,早期版本还有ix格式。pandas0.20.0及以上版本,ix已经丢弃,请尽量使用loc和iloc;

10.4.3 修改数据

我们可以像操作数据库表一样操作DataFrame,删除数据、插入数据、修改字段名、索引名、修改数据等,以下通过一些实例来说明。

图10-3 数据结构

10.4.4 汇总统计

Pandas有一组常用的统计方法,可以根据不同轴方向进行统计,当然也可按不同的列或行进行统计,非常方便。
常用的统计方法有:
表10-1 Pandas统计方法

以下通过实例来说明这些方法的使用
(1)把csv数据导入pandas

(2)查看df的统计信息

【说明】
var:表示方差: σ^2=∑▒〖(X-μ)〗^2/N (10.1)
即各项-均值的平方求和后再除以N 。
std:表示标准差,是var的平方根。

10.4.5选择部分列

这里选择学生代码、课程代码、课程名称、程程成绩,注册日期等字段

10.4.6删除重复数据

如果有重复数据(对df1的所有列),则删除最后一条记录。

10.4.7补充缺省值

(1)用指定值补充NaN值
这里要求把stat_date的缺省值(NaN)改为'2018-09-01'

(2)可视化,并在图形上标准数据

结果为:

导入一些库及支持中文的库

画图

运行结果


图10-4 可视化结果

10.4.8从MySQL中获取数据

(1)从MySQL数据库中获取学生基本信息表
Python连接MySQL数据库是通过pymysql这个桥梁,这个Python的第三方库,需要安装,安装命令如下:

以下是从数据库获取学习具体代码:

(2)查看df_info前3行数据

(3)选择前两个字段

(4)df2 与df_info1 根据字段stud_code 进行内关联

(5)对df3 根据字段stud_code,sub_code进行分组,并求平均每个同学各科的平均成绩。

【备注】
如果需要合计各同学的成绩,可用如下语句。

(6)选择数学分析课程,并根据成绩进行降序。

(7)取前5名

注:DataFrame数据结构的函数或方法有很多,大家可以通过df.[Tab键]方式查看,具体命令的使用方法,如df.count(),可以在Ipython命令行下输入:?df.count() 查看具体使用,退出帮助界面,按q即可。

10.4.9把pandas数据写入excel

把pandas数据写入excel中的sheet中

10.4.10 应用函数及映射

我们知道数据库中有很多函数可用作用于表中元素,DataFrame也可将函数(内置或自定义)应用到各列或行上,而且非常方便和简洁,具体可用通过DataFrame的apply,使或applymap或map,也可以作用到元素级。以下通过实例说明具体使用。

10.4.11 时间序列

pandas最基本的时间序列类型就是以时间戳(时间点)(通常以python字符串或datetime对象表示)为索引的Series:

索引为日期的DataFrame数据的索引、选取以及子集构造

10.4.12 数据离散化

如何离散化连续性数据?在一般开发语言中,可以通过控制语句来实现,但如果分类较多时,这种方法不但繁琐,效率也比较低。在Pandas中是否有更好方法?如果有,又该如何实现呢?
pandas有现成方法,如cut或qcut等,不需要编写代码,至于如何使用还是通过实例来说明。

现在需要对age字段进行离散化, 划分为(20,30],(30,40],(40,50].

10.4.13 交叉表

我们平常看到的数据格式大多像数据库中的表,如购买图书的基本信息:
表10-2 客户购买图书信息

这样的数据比较规范,比较适合于一般的统计分析。但如果我们想查看客户购买各种书的统计信息,就需要把以上数据转换为如下格式:
表10-3 客户购买图书的对应关系

我们观察一下不难发现,把表10-3中书代码列旋转为行就得到表2数据。如何实现行列的互换呢?编码能实现,但比较麻烦,还好,pandas提供了现成的方法或函数,如stack、unstack、pivot_table函数等。以下以pivot_table为例说明具体实现。

实现行列互换,把书代码列转换为行或索引

10.5 后续思考

(1)生成一个类似下例的DataFrame,C,D两列为随机数,可以与下表中数据不一致。

(2)求第(1)题的DataFrame中,C列的平均值,最大值。
(3)从第(1)题的dataframe中得到如下结果:

10.6 小结

本章介绍了Pandas的两个数据类型:Series和DataFrame。Series 是一种一维的数据类型,其中的每个元素都有各自的标签。DataFrame 是一个二维的、表格型的数据结构。Pandas 的Dataframe可以储存许多不同类型的数据,并且每个轴都有标签,可以把它当作一个Series 的字典。Pandas在数据清理、数据处理、数据可视化等方面有比较明显优势。

《Python从入门到人工智能》完整目录

目 录
图像

前言 2
第一部分 Python基础 18
第1章 Python安装配置 18
1.1 问题:Python能带来哪些优势? 18
1.2安装Python 18
1.2.1在Linux系统上安装 19
1.2.2在Windows系统上安装 20
1.2.3 在macOS系统上安装 20
1.3 配置开发环境 20
1.3.1 自带开发环境IDLE 20
1.3.2 安装配置Pycharm 22
1.3.3 Linux下配置Jupyter book 22
1.3.4 Windows下配置Jupyter book 23
1.4 试运行Python 24
1.5 后续思考 28
1.6 小结 28
第2章 变量和数据类型 29
2.1 问题:Python是如何定义变量? 29
2.2 变量 29
2.2.1 给变量赋值 29
2.2.2变量的命名规则 30
2.2.3多重赋值 30
2.3 字符串 31
2.3.1 字符串的多种表示 31
2.3.2 字符串的长度 32
2.3.3 拼接字符串 32
2.3.4 字符串常用方法 32
2.3.5 打印字符串 33
2.4 数字与运算符 34
2.4.1 算术运算符 34
2.4.2 关系运算符 35
2.4.3 逻辑运算符 35
2.4.4 赋值运算符 35
2.5数据类型转换 36
2.5.1 将整数和浮点数转换为字符串 36
2.5.2 把整数转换为浮点数 36
2.5.3 把浮点数转换为整数 36
2.5.4 把字符串转换为数字 37
2.5.5 使用input函数 37
2.6 注释 38
2.7 后续思考 38
2.8小结 38
第3章 列表和元组 39
3.1问题:如何存取更多数据? 39
3.2 列表概述 39
3.3 如何访问列表元素 39
3.3.1 获取一个元素 40
3.3.2 获取连续多个元素 41
3.3.3 遍历列表 41
3.3.4 访问列表经常出现的一个问题 42
3.4 对列表进行增、删、改 42
3.4.1 添加新元素到列表 42
3.4.2 从列表中删除元素 43
3.4.3 修改列表中的元素 44
3.5 统计分析列表 44
3.5.1 求列表最大(小)值 44
3.5.2 求列表总和或平均值 45
3.5.3 求列表元素出现次数及对应索引 45
3.5.4 求列表元素总数 45
3.6 组织列表 45
3.6.1 使用sort()函数 46
3.6.2 使用sorted()函数 46
3.6.3 使用reverse()函数 47
3.7 生成列表 47
3.7.1range()函数 47
3.7.2用range()创建列表 48
3.8 元组 48
3.8.1 定义元组 48
3.8.2 查看元组中元素 49
3.8.3 使用tuple()生成元组 49
3.8.4 元组特殊用途 50
3.8.5 元组解包 50
3.9后续思考 50
3.10小结 51
第4章 if语句与循环语句 52
4.1 问题:Python控制语句有何特点? 52
4.2 if语句 52
4.2.1 if语句格式 52
4.2.2 if语句 53
4.2.3 使用and连接条件语句 53
4.2.4 元素是否在列表中 54
4.2.5 缩进易出现的问题 55
4.3 循环语句 55
4.3.1 for循环 56
4.3.2 while 循环 56
4.3.3 嵌套循环 57
4.3.4 break跳出循环 57
4.3.5 continue加快循环 58
4.3.6 列表推导式 58
4.3.7 后续思考 59
4.4 后续思考 59
4.5 小结 59
第5章 字典和集合 60
5.1 问题:当索引不好用时 60
5.2 一个简单字典实例 60
5.3 创建和维护字典 60
5.3.1 创建字典 61
5.3.2 添加键-值对 61
5.3.3 修改字典中值 61
5.3.4 删除字典中的键-值对 62
5.4 遍历字典 62
5.4.1 遍历字典所有的键-值对 62
5.4.2 遍历字典中所有的键 63
5.4.3 遍历字典中所有的值 63
5.5 集合 63
5.5.1 创建集合 64
5.5.2 集合的添加和删除 64
5.6 列表、元组、字典和集合的异同 65
5.7迭代器和生成器 65
5.7.1 迭代器 66
5.7.2 生成器 67
5.8 后续思考 67
5.9小结 68
第6章 函数 69
6.1 问题:如何实现代码共享 69
6.2 创建和调用函数 69
6.3 传递参数 71
6.3.1 形参与实参 71
6.3.2 位置参数 71
6.3.3 关键字参数 72
6.3.4 默认值 73
6.4 返回值 73
6.5 传递任意数量的参数 74
6.5.1 传递任意数量的实参 75
6.5.3 传递任务数量的关键字实参 76
6.6 lambda函数 76
6.7 生成器函数 77
6.8 把函数放在模块中 78
6.8.1 导入整个模块 78
6.8.2 导入需要的函数 80
6.8.3 导入所有函数 80
6.8.4 主程序 81
6.9 后续思考 82
6.10 小结 82
第7章 面向对象编程 83
7.1 问题:如何实现不重复造轮子? 83
7.2 类与实例 83
7.2.1 创建类 83
7.2.2 创建类的实例 85
7.2.3 访问属性 85
7.2.4 访问限制 86
7.2.5 内置装饰器 @property 87
7.3 继承 88
7.3.1 使用super方法 88
7.3.2 重写父类方法 89
7.4 把类放在模块中 89
7.4.1 导入类 89
7.4.2 在一模块中导入另一个模块 89
7.5 标准库 90
7.5.1 datetime 91
7.5.2 math 92
7.5.3 random 92
7.5.4 os 93
7.5.5 sys 94
7.5.6 time 94
7.6 包 94
7.6.1 创建包 95
7.6.2 使用包 95
7.7 实例1:使用类和包 96
7.7.1 概述 96
7.7.2 实例功能介绍 96
7.7.3 代码实现 96
7.8 实例2:银行ATM机系统 98
7.8.1 实例概况 98
7.8.2实例功能介绍 99
7.8.3实现代码说明 99
7.8.4 后续思考 101
7.9 小结 101
第8章 文件与异常 103
8.1 问题:Python如何获取文件数据? 103
8.2基本文件操作 105
8.2.1 读取文件 105
8.2.2读取文件使用with语句 105
8.2.3 逐行读取文件 106
8.2.4 读取文件所有内容 107
8.2.5写入文件 108
8.2.6 中文乱码处理 109
8.3 目录操作 111
8.3.1 os简介 111
8.3.2 查看环境变量 111
8.3.3 判断是否为文件或目录 111
8.3.4 判断是否为文件或目录 112
8.3.5 join目录 112
8.4异常处理 112
8.4.1 如何使你的程序更可靠? 113
8.4.2 捕获异常 113
8.4.3 捕获多种异常 114
8.4.4 捕获所有异常 115
8.4.5 清理操作 116
8.4.6 try else finally return之间的关系 117
8.4.7 后续思考 118
8.5小结 118
第9章 NumPy基础 119
9.1 问题:为何说Numpy是打开人工智能的一把钥匙? 119
9.2 生成Numpy数组 120
9.2.1 从已有数据中创建数组 121
9.2.2 利用 random 模块生成数组 121
9.2.3 创建特定形状的多维数组 123
9.2.4 利用 arange、linspace 函数生成数组 124
9.3 获取元素 125
9.4 Numpy的算术运算 127
9.4.1对应元素相乘 127
9.4.2 点积运算 128
9.5 数组变形 129
9.5.1 更改数组的形状 129
9.5.2 合并数组 132
9.6 通用函数 134
9.7 广播机制 136
9.8 后续思考 138
9.8 小结 138
第10章 Pandas基础
10.1 问题:Pandas有哪些优势? 139
10.2 Pandas数据结构 139
10.3 Series 140
10.4 DataFrame 141
10.4.1 生成DataFrame 142
10.4.2 获取数据 142
10.4.3 修改数据 143
10.4.4 汇总统计 144
10.4.5选择部分列 145
10.4.6删除重复数据 145
10.4.7补充缺省值 146
10.4.8从MySQL中获取数据 148
10.4.9把pandas数据写入excel 150
10.4.10 应用函数及映射 151
10.4.11 时间序列 151
10.4.12 数据离散化 152
10.4.13 交叉表 153
10.5 后续思考 154
10.6 小结 155
第11章 数据可视化 156
11.1 问题:为何选择matplotlib? 156
11.2 可视化工具matplotlib 156
11.2.1 简单示例 156
11.2.2 添加标签和修改粗细 157
11.2.3 添加中文标注 158
11.2.4 让图像更美观、更丰富 159
11.2.6 改变图形形状 160
11.2.7 隐藏坐标轴 161
11.3 绘制多个子图 161
11.4 seaborn简介 163
11.4.1 查看单变量的分布规律 163
11.4.2 查看多变量之间的关系 164
11.5 图像处理与显示 165
11.6 pyecharts简介 165
11.6.1 pyecharts安装 166
11.6.2 降水量和蒸发量柱状图 166
11.6.3 从上海出发的航线图 167
11.7 实例:词云图 168
11.7.1 实例概况 169
11.7.2 代码实现 169
11.7.3 后续思考 171
11.8 小结 171
第二部分 人工智能基础 172
第12章 机器学习基础 172
12.1 问题:机器学习如何学习? 172
12.2 机器学习常用算法 172
12.2.1 监督学习 172
12.2.2 无监督学习 174
12.2.3半监督学习 175
12.3 机器学习一般流程 175
12.3.1 明确目标 176
12.3.2收集数据 176
12.3.3 数据探索 176
12.3.4 数据预处理 176
12.3.5 选择模型 177
12.3.6 定义损失函数 177
12.3.7评估模型 178
12.3.8性能评估指标 178
12.3.8 自动调参 179
12.4 机器学习常用技巧 179
12.4.1正则化 180
12.4.2 数据增强 181
12.5 实例1:机器学习如何学习的? 181
12.5.1 实例概述 181
12.5.2 查看数据分布情况 181
12.5.3 利用迭代方法求出参数 182
12.5.4 可视化模型 183
12.6 实例2:用Scikit-learn实现电信客户流失预测 184
12.6 .1 Scikit-learn简介 184
12.6.2实例概述 185
12.6.3 明确目标 185
12.6.4 导入数据 185
12.6.5 探索数据 188
12.6.6 数据预处理 190
12.6.7 选择模型 191
12.6.8 评估模型 191
12.6.9 模型解释与应用 192
12.6.10 后续思考 193
12.7 小结 193
第13章 神经网络 193
13.1 问题:神经网络能替换传统机器学习吗? 194
13.2 单层神经网络 195
13.3 多层神经网络 198
13.3.1 多层神经网络架构简介 198
13.3.2 各层之间的信息传输 198
13.4 输出层 199
13.4.1 回归问题 199
13.4.2二分类 199
13.4.3多分类 200
13.4.4 多标签分类 201
13.5 损失函数 201
13.5.1 均方误差 201
13.5.2交叉熵误差 202
13.6 正向传播 203
13.6.1 定义输入层 203
13.6.2 实现从输入层到隐含层 204
13.6.3 实现从隐含层到输出层 204
13.6.4 根据输出层计算损失值 205
13.7 误差反向传播 205
13.7.1 链式法则 205
13.7.2 加法的反向传播 206
13.7.3 乘法的反向传播 206
13.7.4 混合运算的反向传播 207
13.7.5 Python实现神经网络 208
13.8实例:用Python实现手写数字的识别 209
13.8.1 实例简介 209
13.8.2 数据说明 209
13.8.3 神经网络架构 210
13.8.4 准备数据 210
13.8.5 初始化参数 212
13.8.6 构建神经网络 213
13.8.7 定义损失函数 213
13.8.8 误差反向传播 214
13.8.9 梯度更新 214
13.8.10 训练模型 215
13.8.11 可视化结果 217
13.8.12 后续思考 218
13.9 小结 218
第14章 用PyTorch实现神经网络 219
14.1 为何选择PyTorch? 219
14.2 安装配置 220
14.2.1 安装CPU版PyTorch 220
14.2.2 安装GPU版PyTorch 221
14.3 Tensor简介 224
14.3.1 Tenor的基本操作 224
14.3.2 如何创建Tensor? 225
14.3.3 PyTorch与Numpy比较 225
14.4 autograd机制 226
14.4.1 autograd简介 226
14.4.2 使用antograd实现回归问题 227
14.5 构建神经网络的常用工具 229
14.5.1 神经网络构建工具箱nn 229
14.5.2 优化算法工具optim 230
14.6 数据处理工具 231
14.6.1 utils.data简介 232
14.6.2 torchvision简介 234
14.6.3 transforms 234
14.6.4 ImageFolder 235
14.7 实例1:用PyTorch实现手写数字识别 237
14.7.1背景说明 237
14.7.2导入模块 237
14.7.3加载及预处理数据 238
14.7.4可视化源数据 239
14.7.5 构建模型 239
14.7.6 定义损失函数 240
14.7.7 训练模型 240
14.7.8 可视化训练及测试损失值 242
14.8 实例2:用PyTorch实现回归问题 243
14.8.1 导入需要的库 243
14.8.2 生成数据 243
14.8.3 构建网络 244
14.8.4 定义一些超参数 244
14.8.5 定义损失函数 245
14.8.6 训练模型 245
14.8.7 可视化结果 245
14.9 小结 246
第15章 卷积神经网络 247
15.1 问题:传统神经网络有哪些不足? 247
15.2 卷积神经网络 247
15.2.1 卷积层 248
15.2.2 池化层 250
15.2.3 Flatten层 251
15.2.4 PyTorch构建卷积神经网络 251
15.3 实例:用PyTorch图像识别 253
15.3.1 概述 253
15.3.2 数据集说明 253
15.3.3 加载数据 254
15.3.4 构建网络 256
15.3.5 训练模型 258
15.3.6 获取图片真实标签 259
15.3.7 获取对应图片的预测标签 259
15.3.8测试模型 260
15.3.9后续思考 260
15.4 小结 260
第16章 提升模型性能的几种技巧 261
16.1 问题:为什么有些模型尝试了很多方法仍然效果不佳? 261
16.2 找到合适的学习率 261
16.3 正则化 262
16.3.1 权重衰减 262
16.3.2 归一化处理 263
16.3.3 Dropout 264
16.4 合理的初始化 265
16.4.1 全面初始化为零或某个数 265
16.4.2 初始化的随机数 266
16.4.3 Xavier 初始化 266
16.4.4 He初始化 266
16.5 选择合适的优化器 267
16.5.1 传统SGD和mini-batch SGD 267
16.5.2 带动量的SGD 267
16.5.3 AdaGrad算法 268
16.5.4 RMSProp算法 268
16.5.5 Adam算法 268
16.5.6 选择优化器的一般方法 268
16.6 GPU加速 269
16.6.1 单GPU加速 269
16.6.2 多GPU加速 270
16.7 后续思考 271
16.8 小结 271
第17章 Keras入门 272
17.1 问题:为何选择Keras架构? 272
17.2 Keras简介 274
17.2.1 Keras的安装 274
17.2.2 Keras特点 275
17.3 Keras常用概念 275
17.3.1 符号计算 276
17.3.2张量 276
17.3.3 数据格式(data_format) 276
17.3.4模型 277
17.3.5 批量大小(batch-size) 277
17.4 Keras常用层 277
17.4.1全连接层(Dense) 277
17.4.2 Dropout层 278
17.4.3卷积层(Conv2D) 278
17.4.4最大池化层(MaxPooling2D) 279
17.4.5 Flatten层 279
17.4.6 全局平均池化层 279
17.5 神经网络核心组件 279
17.5.1 层 280
17.5.2 模型 281
17.5.3优化器 281
17.5.4目标函数 281
17.6 keras的开发流程 282
17.7 实例-Keras程序的开发流程 283
17.7.1 构造数据 283
17.7.2 构造模型 283
17.7.3编译模型 284
17.7.4训练模型 284
17.7.5测试模型 285
17.7.6 保存模型 285
17.7.7 后续思考 285
17.8 小结 285
第18章 用Keras实现图像识别 286
18.1实例1:用自定义模型识别手写数字 286
18.1.1导入数据集,并初步探索数据集 286
18.1.2 数据预处理 287
18.1.3 定义模型结构 289
18.1.5 编译模型 290
18.1.6 训练模型 290
18.1.7 模型评估 290
18.2 实例2:用预训练模型识别图像 294
18.2.1 Keras中基于ImageNet的预训练模型 294
18.2.2 使用VGG16预训练模型实现图像识别 295
18.2.5 后续思考 298
18.3 小结 298
第19 章 Keras实现迁移学习 299
19.1 问题:如何发挥小数据的潜力? 299
19.2 迁移学习简介 299
19.3 迁移学习常用方法 300
19.3.1将卷积神经网络作为特征抽取器 300
19.3.2微调卷积部分 301
19.4实例:用keras实现迁移学习 302
19.4.1 下载数据 302
19.4.2 冻结不更新的层 303
19.4.3 导入需要的模块 303
19.4.4 添加一些层 303
19.4.5 编译损失函数 305
19.4.6 图像增强 305
19.4.7 训练模型 307
19.4.8 可视化训练过程 307
19.4.9 完整代码 308
19.4.10 后续思考 311
19.5小结 311
第20章 keras实现风格迁移 312
20.1 问题:如何捕捉图像风格? 313
20.2 通道与风格 314
20.3 内容损失与风格损失 316
20.3.1内容损失 316
20.3.2风格损失 316
20.4 格拉姆矩阵简介 318
20.4.1 Gram矩阵的定义 318
20.4.2 用Keras实现Gram矩阵 319
20.5 实例:Kreras实现风格迁移 319
20.5.1 加载数据 319
20.5.2 查看图像 320
20.5.3 预处理数据 321
20.5.4定义损失函数 322
20.5.5 选择优化器 325
20.5.6 完整代码 325
20.5.7 后续思考 331
20.6 小结 332

第4章 Pytorch数据处理工具箱


在3.2节我们利用Pytorch的torchvision、data等包,下载及预处理MNIST数据集。数据下载和预处理是机器学习、深度学习实际项目中耗时又重要的任务,尤其是数据预处理,关系到数据质量和模型性能,往往要占据项目的大部分时间。好在Pytorch为此提供了专门的数据下载、数据处理包,使用这些包,可极大提高我们的开发效率及数据质量。
本章将介绍以下内容:
 简单介绍Pytorch相关的数据处理工具箱
 utils.data简介
 torchvision简介
 tensorboardX简介及实例

4.1 数据处理工具箱概述

如果你看了第3章,应该对torchvision、data等数据处理包有了初步认识,但可能理解还不够深入,接下来我们将详细介绍。Pytorch涉及数据处理(数据装载、数据预处理、数据增强等)主要工具包及相互关系如图4-1所示。

图4-1 Pytorch主要数据处理工具
图4-1 的左边是torch.utils.data工具包,它包括以下三个类:
(1)Dataset:是一个抽象类,其它数据集需要继承这个类,并且覆写其中的两个方法(__getitem__、__len__)。
(2)DataLoader:定义一个新的迭代器,实现批量(batch)读取,打乱数据(shuffle)并提供并行加速等功能。
(3)random_split:把数据集随机拆分为给定长度的非重叠新数据集。
(4)*sampler:多种采样函数。
图4-1中间是Pytorch可视化处理工具(torchvision),Pytorch的一个视觉处理工具包,独立于Pytorch,需要另外安装,使用pip或conda安装即可:

它包括4个类,各类的主要功能如下:
(1)datasets:提供常用的数据集加载,设计上都是继承torch.utils.data.Dataset,主要包括MMIST、CIFAR10/100、ImageNet、COCO等。
(2)models:提供深度学习中各种经典的网络结构以及训练好的模型(如果选择pretrained=True),包括AlexNet, VGG系列、ResNet系列、Inception系列等。
(3)transforms:常用的数据预处理操作,主要包括对Tensor及PIL Image对象的操作。
(4)utils:含两个函数,一个是make_grid,它能将多张图片拼接在一个网格中;另一个是save_img,它能将Tensor保存成图片。

4.2 utils.data简介

utils.data包括Dataset和DataLoader。torch.utils.data.Dataset为抽象类。自定义数据集需要继承这个类,并实现两个函数。一个是__len__,另一个是__getitem__,前者提供数据的大小(size),后者通过给定索引获取数据和标签。 __getitem__一次只能获取一个数据,所以通过torch.utils.data.DataLoader来定义一个新的迭代器,实现batch读取。首先我们来定义一个简单的数据集,然后具体使用Dataset及DataLoader,以便有个直观认识。
(1) 导入需要的模块

(2)定义获取数据集的类
该类继承基类Dataset,自定义一个数据集及对应标签。

(3)获取数据集中数据

以上数据以tuple返回,每次只返回一个样本。实际上,Dateset只负责数据的抽取,一次调用__getitem__只返回一个样本。如果希望批量处理(batch),同时还要进行shuffle和并行加速等操作,可选择DataLoader。DataLoader的格式为:

主要参数说明:
 dataset: 加载的数据集;
 batch_size: 批大小;
 shuffle:是否将数据打乱;
 sampler:样本抽样
 num_workers:使用多进程加载的进程数,0代表不使用多进程;
 collate_fn:如何将多个样本数据拼接成一个batch,一般使用默认的拼接方式即可;
 pin_memory:是否将数据保存在pin memory区,pin memory中的数据转到GPU会快一些;
 drop_last:dataset 中的数据个数可能不是 batch_size的整数倍,drop_last为True会将多出来不足一个batch的数据丢弃。

运行结果
i: 0
data: tensor([[1, 2],
[3, 4]])
Label: tensor([0, 1])
i: 1
data: tensor([[2, 1],
[3, 4]])
Label: tensor([0, 1])
i: 2
data: tensor([[4, 5]])
Label: tensor([2])
从这个结果可以看出,这是批量读取。我们可以像使用迭代器一样使用它,如对它进行循环操作。不过它不是迭代器,我们可以通过iter命令转换为迭代器。

一般用data.Dataset处理同一个目录下的数据。如果数据在不同目录下,不同目录代表不同类别(这种情况比较普遍),使用data.Dataset来处理就不很方便。不过,可以使用Pytorch另一种可视化数据处理工具(即torchvision)就非常方便,不但可以自动获取标签,还提供很多数据预处理、数据增强等转换函数。

4.3 torchvision简介

torchvision有4个功能模块,model、datasets、transforms和utils。其中model后续章节将介绍,利用datasets下载一些经典数据集,3.2小节有实例,大家可以参考一下。这节我们将主要介绍如何使用datasets的ImageFolder处理自定义数据集,如何使用transforms对源数据进行预处理、增强等。下面我们重点介绍transforms及ImageFolder。

4.3.1 transforms

transforms提供了对PIL Image对象和Tensor对象的常用操作。
(1)对PIL Image的常见操作如下:
 Scale/Resize: 调整尺寸,长宽比保持不变;
 CenterCrop、RandomCrop、RandomSizedCrop:裁剪图片,CenterCrop和RandomCrop在crop时是固定size,RandomResizedCrop则是random size的crop;
 Pad: 填充;
 ToTensor: 把一个取值范围是[0,255]的PIL.Image 转换成 Tensor。形状为(H,W,C)的numpy.ndarray,转换成形状为[C,H,W],取值范围是[0,1.0]的torch.FloatTensor。
 RandomHorizontalFlip:图像随机水平翻转,翻转概率为0.5;
 RandomVerticalFlip: 图像随机垂直翻转;
 ColorJitter: 修改亮度、对比度和饱和度。
(2)对Tensor的常见操作如下:
 Normalize: 标准化,即减均值,除以标准差;
 ToPILImage:将Tensor转为PIL Image。
如果要对数据集进行多个操作,可通过Compose将这些操作像管道一样拼接起来,类似于nn.Sequential。以下为示例代码

还可以自己定义一个python lambda表达式,如将每个像素值加10,可表示为:transforms.Lambda(lambda x: x.add(10))。
更多内容可参考官网:
https://pytorch.org/docs/stable/torchvision/transforms.html

4.3.2 ImageFolder

当文件依据标签处于不同文件下时,如:
─── data
├── zhangliu
│ ├── 001.jpg
│ └── 002.jpg
├── wuhua
│ ├── 001.jpg
│ └── 002.jpg
.................
我们可以利用 torchvision.datasets.ImageFolder 来直接构造出 dataset,代码如下:

ImageFolder 会将目录中的文件夹名自动转化成序列,那么DataLoader载入时,标签自动就是整数序列了。
下面我们利用ImageFolder读取不同目录下图片数据,然后使用transorms进行图像预处理,预处理有多个,我们用compose把这些操作拼接在一起。然后使用DataLoader加载。
对处理后的数据用torchvision.utils中的save_image保存为一个png格式文件,然后用Image.open打开该png文件,详细代码如下:

tensor([2, 2, 0, 0, 0, 1, 2, 2])

图4-2 make_grid拼接在一起的图形
打开test01.png文件

图4-3 用Image查看png文件

4.4 可视化工具

Tensorboard是Google TensorFlow 的可视化工具,它可以记录训练数据、评估数据、网络结构、图像等,并且可以在web上展示,对于观察神经网路训练的过程非常有帮助。PyTorch可以采用tensorboard_logger, visdom等可视化工具,但这些方法比较复杂或不够友好。为解决这一问题,人们推出了可用于Pytorch可视化的新的更强大工具tensorboardX。

4.4.1 tensorboardX简介

tensorboardX功能很强大,支持scalar, image, figure, histogram, audio, text, graph, onnx_graph, embedding, pr_curve and videosummaries等可视化方式。
安装也比较方便,先安装tensorflow(CPU或GPU版),然后安装tensorboardX,在命令行运行以下命令即可。

使用tensorboardX的一般步骤为:
(1)导入tensorboardX,实例化SummaryWriter类,指明记录日志路径等信息。

【说明】
①如果是windows环境,log_dir注意路径解析,如
writer = SummaryWriter(log_dir=r'D:\myboard\test\logs')
②SummaryWriter的格式为

③如果不写log_dir,系统将在当前目录创建一个runs的目录。

(2)调用相应的API接口,接口一般格式为:

(3)启动tensorboard服务
cd到logs目录所在的同级目录,在命令行输入如下命令,logdir等式右边可以是相对路径或绝对路径。

(4)web展示
在浏览器输入

便可看到logs目录保存的各种图形,图4-4为示例图:

图4-4 tensorboardx示例图形
鼠标在图形上移动,还可以看到对应位置具体数据。
有关tensorboardX的更多内容,大家可参考其官网:
https://github.com/lanpa/tensorboardX

4.4.2用tensorboardX可视化神经网络

上节我们介绍了tensorboardX的主要内容,为帮助大家更好理解,这节我们将介绍几个实例。实例内容涉及如何使用tensorboardX可视化神经网络模型、可视化损失值、图像等。
(1)导入需要的模块

(2)构建神经网络

(3)把模型保存为graph

打开浏览器,便可看到图4-5:

图4-5 tensorboardx可视化计算图

4.4.3用tensorboardX可视化损失值

可视化损失值,使用add_scalar函数,这里利用一层全连接神经网络,训练一元二次函数的参数。

图4-6 可视化损失值与迭代步的关系

4.4.4用tensorboardX可视化特征图

利用tensorboardX对特征图进行可视化,不卷积层的特征图的抽取程度是不一样的。
x从cifair10数据集获取,具体请参考第6章代码pytorch-06。

图4-7 conv1的特征图

图4-8 conv2的特征图

4.5 小结

本章详细介绍了Pytorch有关数据下载、预处理方面的一些常用包,以及对计算结果进行可视化的工具tensorboardX,并通过一个实例详细说明如何使用tensorboardX。

《Python深度学习基于Pytorch》---目录--抢先看!

======后续将陆续加上具体内容=====
======本书使用环境:Python3.6+,Pytorch1.0+,Tensorflow1.5+,GPU或CPU(无需变更代码)
======可视化工具:Matplotlib,TensorboardX等
======本书将由工业机械出版社出版,三大部分,共16章,320页左右
======作者:吴茂贵、郁明敏、杨本法、李涛、张粤磊等

第一部分 基础篇
第1章 Numpy基础
1.1 生成Numpy数组
1.1.1 从已有数据中创建数组
1.1.2 利用 random 模块生成数组
1.1.3 创建特定形状的多维数组
1.1.4 利用 arange、linspace 函数生成数组
1.2 获取元素
1.3 Numpy的算术运算
1.3.1对应元素相乘
1.3.2 点积运算
1.4 数组变形
1.4.1 更改数组的形状
1.4.2 合并数组
1.5 批量处理
1.6 通用函数
1.7 广播
1.8 小结

第2章 Pytorch基础
2.1 为何选择Pytorch?
2.2 安装配置
2.2.1 CPU版Pytorch
2.2.2 GPU版Pytorch
2.3 Jupyter Notebook环境配置
2.4 Numpy与Tensor
2.4.1 Tensor概述
2.4.2 创建Tensor
2.4.3 修改Tensor形状
2.4.4 索引操作
2.4.5 广播机制
2.4.6 逐元素操作
2.4.7 归并操作
2.4.8 比较操作
2.4.9 矩阵操作
2.4.10 Pytorch与Numpy比较
2.5 Tensor与Autograd
2.5.1 自动求导要点
2.5.2计算图
2.5.3 标量反向传播
2.5.4 非标量反向传播
2.6 使用Numpy实现机器学习
2.7 使用Tensor及antograd实现机器学习
2.8 使用TensorFlow架构
2.9 小结
第3章 Pytorch实现神经网络工具箱
3.1 神经网络核心组件
3.2实现神经网络实例
3.2.1背景说明
3.2.2准备数据
3.2.3可视化源数据
3.2.4 构建模型
3.2.5 训练模型
3.3 如何构建神经网络?
3.3.1 构建网络层
3.3.2 前向传播
3.3.3 反向传播
3.3.4 训练模型
3.4 nn.Module
3.5 nn.functional
3.6 优化器
3.7 动态修改学习率参数
3.8 优化器比较
3.9 小结
第4章 Pytorch数据处理工具箱
4.1 数据处理工具箱概述
4.2 utils.data简介
4.3 torchvision简介
4.3.1 transforms
4.3.2 ImageFolder
4.4 可视化工具
4.4.1 tensorboardX简介
4.4.2用tensorboardX可视化神经网络
4.4.3用tensorboardX可视化损失值
4.4.4用tensorboardX可视化特征图
4.5 小结
第二部分 深度学习基础
第5 章 机器学习基础
5.1 机器学习的基本任务
5.1.1监督学习
5.1.2 无监督学习
5.1.3 半监督学习
5.1.4 强化学习
5.2 机器学习一般流程
5.2.1 明确目标
5.2.2收集数据
5.2.3 数据探索与预处理
5.2.4 选择模型
5.2.5 评估及优化模型
5.3 过拟合与欠拟合
5.3.1 权重正则化
5.3.2 dropout正则化
5.3.3 批量正则化
5.3.4权重初始化
5.4 选择合适激活函数
5.5 选择合适的损失函数
5.6 选择合适优化器
5.6.1传统梯度优化的不足
5.6.2动量算法
5.6.3 AdaGrad算法
5.6.4 RMSProp算法
5.6.5 Adam算法
5.7GPU加速
5.7.1 单GPU加速
5.7.2 多GPU加速
5.7.3使用GPU注意事项
5.8 小结
第6章 视觉处理基础
6.1卷积神经网络简介
6.2卷积层
6.2.1 卷积核
6.2.2步幅
6.2.3 填充
6.2.4 多通道上的卷积
6.2.5激活函数
6.2.6卷积函数
6.2.7转置卷积
6.3池化层
6.3.1局部池化
6.3.2全局池化
6.4现代经典网络
6.4.1 LeNet-5模型
6.4.2 AlexNet模型
6.4.3 VGG模型
6.4.4 GoogleNet模型
6.4.5 ResNet模型
6.4.6 胶囊网络简介
6.5 Pytorch实现cifar10多分类
6.5.1 数据集说明
6.5.2 加载数据
6.5.3 构建网络
6.5.4 训练模型
6.5.5 测试模型
6.5.6 采用全局平均池化
6.5.7像keras一样显示各层参数
6.6 模型集成提升性能
6.6.1 使用模型
6.6.2 集成方法
6.6.3 集成效果
6.7使用经典模型提升性能
6.8 小结
第7章 自然语言处理基础
7.1 循环神经网络基本结构
7.2前向传播与随时间反向传播
7.3 循环神经网络变种
7.3.1 LSTM
7.3.2 GRU
7.3.3 Bi-RNN
7.4 循环神经网络的Pytorch实现
7.4.1 RNN实现
7.4.2LSTM实现
7.4.3GRU实现
7.5文本数据处理
7.6词嵌入
7.6.1Word2Vec原理
7.6.2 CBOW模型
7.6.3 Skim-gram模型
7.7 Pytorch实现词性判别
7.7.1 词性判别主要步骤
7.7.2 数据预处理
7.7.3 构建网络
7.7.4 训练网络
7.7.5 测试模型
7.8循环神经网络应用场景
7.9 小结
第8章 生成式深度学习
8.1 用变分自编码器生成图像
8.1.1 自编码器
8.1.2变分自编码器
8.1.3用变分自编码器生成图像
8.2 GAN简介
8.2.1 GAN架构
8.2.2 GAN的损失函数
8.3用GAN生成图像
8.3.1判别器
8.3.2 生成器
8.3.3 训练模型
8.3.4 可视化结果
8.4 VAE与GAN的异同
8.5 Condition GAN
8.5.1 CGAN的架构
8.5.2 CGAN 生成器
8.5.3 CGAN 判别器
8.5.4 CGAN 损失函数
8.5.5 CGAN 可视化
8.5.6 查看指定标签的数据
8.5.7 可视化损失值
8.6 DCGAN
8.7 提升GAN训练效果的一些技巧
8.8 小结
第三部分 深度学习实战
第9章 人脸检测与识别
9.1 人脸识别一般流程
9.1.1图像采集
9.1.2 人脸检测
9.3特征提取
9.4人脸识别
9.4.1 人脸识别主要原理
9.4.2人脸识别发展
9.5 人脸检测与识别实例
9.5.1.验证检测代码
9.5.2.检测图像
9.5.3.检测后进行预处理
9.5.4.查看经检测后的图片
9.5.5.人脸识别
9.6 小结
第10章 迁移学习实例
10.1 迁移学习简介
10.2 特征提取
10.2.1 Pytorch提供的预处理模块
10.2.2 特征提取实例
10.3 数据增强
10.3.1 按比例缩放
10.3.2 裁剪
10.3.3翻转
10.3.4改变颜色
10.3.5组合多种增强方法
10.4 微调实例
10.4.1 数据预处理
10.4.2 加载预训练模型
10.4.3 修改分类器
10.4.4 选择损失函数及优化器
10.4.5 训练及验证模型
10.5 用预训练模型清除图像中的雾霾
10.5.1 导入需要的模块
10.5.2 查看原来的图像
10.5.3 定义一个神经网络
10.5.4 训练模型
10.5.5 查看处理后的图像
10.6 小结
第11章 神经网络机器翻译实例
11.1 Encode-Decoder模型原理
11.2 注意力框架
11.3 Pytorch实现注意力Decoder
11.3.1 构建Encoder
11.3.2 构建简单Decoder
11.3.3 构建注意力Decoder
11.4 用注意力机制实现中英文互译
11.4.1 导入需要的模块
11.4.2数据预处理
11.4.3构建模型
11.4.4训练模型
11.4.5随机采样,对模型进行测试
11.4.6可视化注意力
11.5 小结
第12章 实战生成式模型
12.1 Deep Dream模型
12.1.1 Deep Dream原理
12.1.2 DeepDream算法流程
12.1.3 用Pytorch实现Deep Dream
12.2 风格迁移
12.2.1 内容损失
12.2.2 风格损失
12.2.3 用Pytorch实现神经网络风格迁移
12.3 Pytorch实现图像修复
12.3.1 网络结构
12.3.2 损失函数
12.3.3 图像修复实例
12.4 Pytorch实现DiscoGAN
12.4.1 DiscoGAN架构
12.4.2 损失函数
12.4.3 DiscoGAN实现
12.4.4 用Pytorch实现从边框生成鞋子
第13章 Caffe2模型迁移实例
13.1 Caffe2简介
13.2 Caffe如何迁移到Caffe2
13.3 Pytorch如何迁移到caffe2
13.4 小结
第14章 AI新方向:对抗攻击
14.1对抗攻击简介
14.1.1白盒攻击与黑盒攻击
14.1.2无目标攻击与有目标攻击
14.2常见对抗样本生成方式
14.2.1快速梯度符号法
14.2.2快速梯度算法
14.3 Pytorch实现对抗攻击
14.3.1 实现无目标攻击
14.3.2 实现有目标攻击
14.4 对抗攻击和防御措施
14.4.1 对抗攻击
14.4.2 常见防御方法分类
14.5 总结
第15章 强化学习
15.1 强化学习简介
15.2Q Learning 原理
15.2.1 Q Learning主要流程
15.2.2 Q函数
15.2.3 贪婪策略
15.3 用Pytorch实现Q Learning
15.3.1 定义Q-Learing主函数
15.3.2执行Q-Learing
15.4 SARSA 算法
15.4.1 SARSA算法主要步骤
15.4.2 用Pytorch实现SARSA算法
15.5 小结
第16章 深度强化学习
16.1 DSN算法原理
16.1.1 Q-Learning方法的局限性
16.1.2 用DL处理RL需要解决的问题
16.1.3 用DQN解决方法
16.1.4 定义损失函数
16.1.5 DQN的经验回放机制
16.1.6 目标网络
16.1.7 网络模型
16.1.8 DQN算法
16.2 用Pytorch实现 DQN算法
16.3 小结
附录A:Pytorch0.4版本变更
A.1概述
A.2 合并Variable和Tensor
A.3 弃用volatile标签
A.4 dypes,devices以及numpy-style的构造函数
A.5 迁移实例比较
附录B:AI在各行业的最新应用
B.1 AI+电商
B.2 AI+金融
B.3 AI+医疗
B.4 AI+零售
B.5 AI+投行
B.6 AI+制造
B.7 AI+IT服务
B.8 AI+汽车
B.9 AI+公共安全

第26章 用预训练模型清除图像中的雾霾

这节我们介绍利用一个预训练模型清除图像中雾霾,使图像更清晰。

26.1 导入需要的模块

26.2 查看原来的图像

26.3 定义一个神经网络

这个神经网络主要由卷积层构成,该网络将构建在预训练模型之上。

26.4 训练模型

clean_photo/test_images/shanghai02.jpg done!

26.5 查看处理后的图像

处理后的图像与原图像拼接在一起,保存在clean_photo /results目录下。

虽非十分理想,但效果还是比较明显的!
本章数据集下载地址(提取码是:1nxs)
更多内容可参考:
https://github.com/TheFairBear/PyTorch-Image-Dehazing

第25章 Pytorch 如何高效使用GPU

深度学习涉及很多向量或多矩阵运算,如矩阵相乘、矩阵相加、矩阵-向量乘法等。深层模型的算法,如BP,Auto-Encoder,CNN等,都可以写成矩阵运算的形式,无须写成循环运算。然而,在单核CPU上执行时,矩阵运算会被展开成循环的形式,本质上还是串行执行。GPU(Graphic Process Units,图形处理器)的众核体系结构包含几千个流处理器,可将矩阵运算并行化执行,大幅缩短计算时间。随着NVIDIA、AMD等公司不断推进其GPU的大规模并行架构,面向通用计算的GPU已成为加速可并行应用程序的重要手段。得益于GPU众核(many-core)体系结构,程序在GPU系统上的运行速度相较于单核CPU往往提升几十倍乃至上千倍。
目前,GPU已经发展到了较为成熟的阶段。利用GPU来训练深度神经网络,可以充分发挥其数以千计计算核心的能力,在使用海量训练数据的场景下,所耗费的时间大幅缩短,占用的服务器也更少。如果对适当的深度神经网络进行合理优化,一块GPU卡相当于数十甚至上百台CPU服务器的计算能力,因此GPU已经成为业界在深度学习模型训练方面的首选解决方案。
如何使用GPU?现在很多深度学习工具都支持GPU运算,使用时只要简单配置即可。Pytorch支持GPU,可以通过to(device)函数来将数据从内存中转移到GPU显存,如果有多个GPU还可以定位到哪个或哪些GPU。Pytorch一般把GPU作用于张量(Tensor)或模型(包括torch.nn下面的一些网络模型以及自己创建的模型)等数据结构上。

25.1 单GPU加速

使用GPU之前,需要确保GPU是可以使用,可通过torch.cuda.is_available()的返回值来进行判断。返回True则具有能够使用的GPU。
通过torch.cuda.device_count()可以获得能够使用的GPU数量。
如何查看平台GPU的配置信息?在命令行输入命令nvidia-smi即可 (适合于Linux或Windows环境)。图5-13是GPU配置信息样例,从中可以看出共有2个GPU。

图5-13 GPU配置信息

把数据从内存转移到GPU,一般针对张量(我们需要的数据)和模型。
对张量(类型为FloatTensor或者是LongTensor等),一律直接使用方法.to(device)或.cuda()即可。

对于模型来说,也是同样的方式,使用.to(device)或.cuda来将网络放到GPU显存。

25.2 多GPU加速

这里我们介绍单主机多GPUs的情况,单机多GPUs主要采用的DataParallel函数,而不是DistributedParallel,后者一般用于多主机多GPUs,当然也可用于单机多GPU。
使用多卡训练的方式有很多,当然前提是我们的设备中存在两个及以上的GPU。
使用时直接用model传入torch.nn.DataParallel函数即可,如下代码:

这时,默认所有存在的显卡都会被使用。
如果你的电脑有很多显卡,但只想利用其中一部分,如只使用编号为0、1、3、4的四个GPU,那么可以采用以下方式:

或者

其中CUDA_VISIBLE_DEVICES 表示当前可以被Pytorch程序检测到的GPU。
下面为单机多GPU的实现代码。
(1)背景说明
这里使用波士顿房价数据为例,共506个样本,13个特征。数据划分成训练集和测试集,然后用data.DataLoader转换为可批加载的方式。采用nn.DataParallel并发机制,环境有2个GPU。当然,数据量很小,按理不宜用nn.DataParallel,这里只是为了说明使用方法。
(2)加载数据

(2)把数据转换为批处理加载方式
批次大小为128,打乱数据。

(3)定义网络

(4)把模型转换为多GPU并发处理格式

运行结果
Let's use 2 GPUs
DataParallel(
(module): Net1(
(layer1): Sequential(
(0): Linear(in_features=13, out_features=16, bias=True)
)
(layer2): Sequential(
(0): Linear(in_features=16, out_features=32, bias=True)
)
(layer3): Sequential(
(0): Linear(in_features=32, out_features=1, bias=True)
)
)
)
(5)选择优化器及损失函数

(6)模型训练,并可视化损失值。

运行的部分结果
In Model: input size torch.Size([64, 13]) output size torch.Size([64, 1])
In Model: input size torch.Size([64, 13]) output size torch.Size([64, 1])
Outside: input size torch.Size([128, 13]) output_size torch.Size([128, 1])
In Model: input size torch.Size([64, 13]) output size torch.Size([64, 1])
In Model: input size torch.Size([64, 13]) output size torch.Size([64, 1])
Outside: input size torch.Size([128, 13]) output_size torch.Size([128, 1])
从运行结果可以看出,一个批次数据(batch-size=128)拆分成两份,每份大小为64,分别放在不同的GPU上。此时用GPU监控也可发现,两个GPU都同时在使用。

(7)通过web查看损失值的变化情况

图5-16 并发运行训练损失值变化情况
图形中出现较大振幅,是由于采用批次处理,而且数据没有做任何预处理,对数据进行规范化应该更平滑一些,大家可以尝试一下。

单机多GPU也可使用DistributedParallel,它多用于分布式训练,但也可以用在单机多GPU的训练,配置比使用nn.DataParallel稍微麻烦一点,但是训练速度和效果更好一点。具体配置为:

单机运行时使用下面方法启动

25.3使用GPU注意事项

使用GPU可以提升我们训练的速度,如果使用不当,可能影响使用效率,具体使用时要注意以下几点:
(1)GPU的数量尽量为偶数,奇数的GPU有可能会出现异常中断的情况;
(2)GPU很快,但数据量较小时,效果可能没有单GPU好,甚至还不如CPU;
(3)如果内存不够大,使用多GPU训练的时候可通过设置pin_memory为False,当然使用精度稍微低一点的数据类型有时也效果。

第24章 轻松掌握深度学习的核心-误差反向传播法

24.1 链式法则

假设z=f(t)
t=g(y)
则,根据复合函数的求导规则(即链式法则),可得:

24.1.1 计算图的方向传播

反向传播的计算顺序是将信号E乘以节点的局部导数(∂y/∂x),然后将结果传递给下一个节点。

24.1.2 链式法则

如果某个函数为复合函数,则该复合函数的导数可以用构成复合函数的各个函数的导数的乘积表示。

24.1.3 链式法则和计算图

反向传播是上游传过来的导数乘以本节点导数。

24.2 反向传播

反向传播是上游传过来的导数乘以本节点对应输入的导数。

24.2.1 加法节点的反向传播

 

代码实现如下

24.2.2 乘法法节点的反向传播

24.2.3 激活函数层的反向传播

24.3 激活函数层的反向传播

24.3.1 ReLU激活函数

24.3.2 Sigmoid激活函数


根据导数的链式规则,上游的值乘以本节点输出对输入的导数.
代码实现

24.4 Affine/softmax层的反向传播


DY=np.array([[1,2,3],[4,5,6]])
dB=np.sum(DY,axis=0)
Affine层的代码实现

24.4.1 Softmax-with loss 层

输入一张手写5的图片,经过多层(这里假设为2层)神经网络转换后,对输出10个节点,在各个输出节点的得分或概率是不同的,其中对应标签为5的节点(转换为one
-hot后为[0,0,0,0,1,0,0,0,0],得分或概率最大。

我们看一下带softmax及loss的反向传播,如何计算梯度。以下为示意图。

代码实现

24.5 损失反向传播法的实现

24.5.1 神经网络学习的基本步骤

利用随机梯度下降法,求梯度并更新权重和偏置参数,整个过程是个循环过程。
步骤1
从训练数据中随机选择一部分数据
步骤2
构建网络,利用前向传播,求出输出值。然后利用输出值与目标值得到损失函数,利用损失函数,利用反向传播方法,求各参数的梯度。
步骤3
将权重参数沿梯度方向进行微小更新
步骤4
重复以上1、2、3步骤

24.5.2 神经网络学习的反向传播法的实现

神经网络结构图如下

下面用代码实现
1)概述
为定义和保存以上神经网络架构,需要先定义几个实例变量:
保存权重参数的字典型变量params。
保存各层的信息的顺序字典layers,这里的顺序是插入数据的先后顺序。
神经网络的最后一层lastlayer
除了以上三个实例变量,还需要定义一些方法
构造函数,以初始化变量和权重等
预测方法,根据神经网络各层的前向传播得到最后的输出值
损失函数,根据输出值与目标值,得到交叉熵作为衡量两个分布的距离。
评估指标,这里使用精度来衡量模型性能
最后就是计算梯度,这里使用反向传播方法的得到,具体是利用导数的链式法则,从后往前,获取各层的梯度作为前层梯度往前传递(往输入端)
当然,这里需要先定义好各层类,各类中各层的权重参数、包括前向传播结果,反向传播的梯度等。
2)定义各层类
①softmax 函数及Sigmoid类

②Affine类或称为sumweigt

③最后一层

3)定义损失函数

4)定义神经网络类