2.4 PyTorch包的结构
本节首先从PyTorch包的结构出发,简单介绍一下PyTorch模块中子模块的作用,方便后续对子模块的功能进行更深入的学习。下面首先从PyTorch 1.3.0的文档出发,介绍每个子模块的功能和作用。
2.4.1 PyTorch的主要模块
PyTorch主要包括以下16个模块。
1.torch模块
torch模块本身包含了PyTorch经常使用的一些激活函数,比如Sigmoid(torch.sigmoid)、ReLU(torch.relu)和Tanh(torch.tanh),以及PyTorch张量的一些操作,比如矩阵的乘法(torch.mm)、张量元素的选择(torch.select)。需要注意的是,这些操作的对象大多数都是张量,因此,传入的参数需要是PyTorch的张量,否则会报错(一般报类型错误,即TypeError)。另外,还有一类函数能够产生一定形状的张量,比如torch.zeros产生元素全为0的张量,torch.randn产生元素服从标准正态分布的张量等。
2.torch.Tensor模块
torch.Tensor模块定义了torch中的张量类型,其中的张量有不同的数值类型,如单精度、双精度浮点、整数类型等,而且张量有一定的维数和形状。同时,张量的类中也包含着一系列的方法,返回新的张量或者更改当前的张量。torch.Storage则负责torch.Tensor底层的数据存储,即前面提到的为一个张量分配连续的一维内存地址(用于存储相同类型的一系列元素,数目则为张量的总元素数目)。这里需要提到一点,如果张量的某个类方法会返回张量,按照PyTorch中的命名规则,如果张量方法后缀带下画线,则该方法会修改张量本身的数据,反之则会返回新的张量。比如,Tensor.add方法会让当前张量和输入参数张量做加法,返回新的张量,而Tensor.add_方法会改变当前张量的值,新的值为旧的值和输入参数之和。
3.torch.sparse模块
torch.sparse模块定义了稀疏张量,其中构造的稀疏张量采用的是COO格式(Coordinate),主要方法是用一个长整型定义非零元素的位置,用浮点数张量定义对应非零元素的值。稀疏张量之间可以做元素加、减、乘、除运算和矩阵乘法。
4.torch.cuda模块
torch.cuda模块定义了与CUDA运算相关的一系列函数,包括但不限于检查系统的CUDA是否可用,当前进程对应的GPU序号(在多GPU情况下),清除GPU上的缓存,设置GPU的计算流(Stream),同步GPU上执行的所有核函数(Kernel)等。
5.torch.nn模块
torch.nn是一个非常重要的模块,是PyTorch神经网络模块化的核心。这个模块定义了一系列模块,包括卷积层nn.ConvNd(N=1,2,3)和线性层(全连接层)nn.Linear等。当构建深度学习模型的时候,可以通过继承nn.Module类并重写forward方法来实现一个新的神经网络(后续会提到如何通过组合神经网络模块来构建深度学习模型)。另外,torch.nn中也定义了一系列的损失函数,包括平方损失函数(torch.nn.MSELoss)、交叉熵损失函数(torch.nn.CrossEntropyLoss)等。一般来说,torch.nn里定义的神经网络模块都含有参数,可以对这些参数使用优化器进行训练。
6.torch.nn.functional函数模块
torch.nn.functional是PyTorch的函数模块,定义了一些核神经网络相关的函数,包括卷积函数和池化函数等,这些函数也是深度学习模型构建的基础。需要指出的是,torch.nn中定义的模块一般会调用torch.nn.functional里的函数,比如,nn.ConvNd模块(N=1,2,3)会调用torch.nn.functional.convNd函数(N=1,2,3)。另外,torch.nn.functional里面还定义了一些不常用的激活函数,包括torch.nn.functional.relu6和torch.nn.functional.elu等。
7.torch.nn.init模块
torch.nn.init模块定义了神经网络权重的初始化。前面已经介绍过,如果初始的神经网络权重取值不合适,就会导致后续的优化过程收敛很慢,甚至不熟练。这个模块中的函数就是为了解决神经网络权重的初始化问题,其中使用了很多初始化方法,包括均匀初始化torch.nn.init.uniform_和正态分布归一化torch.nn.init.normal_等。在前面提到过,在PyTorch中函数或者方法如果以下画线结尾,则这个方法会直接改变作用张量的值。因此,这些方法会直接改变传入张量的值,同时会返回改变后的张量。
8.torch.optim模块
torch.optim模块定义了一系列的优化器,包括但不限于前一章介绍的优化器,如torch.optim.SGD(随机梯度下降算法)、torch.optim.Adagrad(AdaGrad算法)、torch.optim.RMSprop(RMSProp算法)和torch.optim.Adam(Adam算法)等。当然,这个模块还包含学习率衰减的算法的子模块,即torch.optim.lr_scheduler,这个子模块中包含了诸如学习率阶梯下降算法torch.optim.lr_scheduler.StepLR和余弦退火算法torch.optim.lr_scheduler.CosineAnnealingLR等学习率衰减算法。
9.torch.autograd模块
torch.autograd模块是PyTorch的自动微分算法模块,定义了一系列的自动微分函数,包括torch.autograd.backward函数,主要用于在求得损失函数之后进行反向梯度传播,torch.autograd.grad函数用于一个标量张量(即只有一个分量的张量)对另一个张量求导,以及在代码中设置不参与求导的部分。另外,这个模块还内置了数值梯度功能和检查自动微分引擎是否输出正确结果的功能。
10.torch.distributed模块
torch.distributed是PyTorch的分布式计算模块,主要功能是提供PyTorch并行运行环境,其主要支持的后端有MPI、Gloo和NCCL三种。PyTorch的分布式工作原理主要是启动多个并行的进程,每个进程都拥有一个模型的备份,然后输入不同的训练数据到多个并行的进程,计算损失函数,每个进程独立地做反向传播,最后对所有进程权重张量的梯度做归约(Reduce)。用到后端的部分主要是数据的广播(Broadcast)和数据的收集(Gather),其中,前者是把数据从一个节点(进程)传播到另一个节点(进程),比如归约后梯度张量的传播,后者则是把数据从其他节点(进程)转移到当前节点(进程),比如把梯度张量从其他节点转移到某个特定的节点,然后对所有的张量求平均。PyTorch的分布式计算模块不但提供了后端的一个包装,还提供了一些启动方式来启动多个进程,包括但不限于通过网络(TCP)、通过环境变量、通过共享文件等。
11.torch.distributions模块
torch.distributions模块提供了一系列类,使得PyTorch能够对不同的分布进行采样,并且生成概率采样过程的计算图。在一些应用过程中,比如强化学习(Reinforcement Learning),经常会使用一个深度学习模型来模拟在不同环境条件下采取的策略(Policy),其最后的输出是不同动作的概率。当深度学习模型输出概率之后,需要根据概率对策略进行采样来模拟当前的策略概率分布,最后用梯度下降方法来让最优策略的概率最大(这个算法称为策略梯度算法,Policy Gradient)。实际上,因为采样的输出结果是离散的,无法直接求导,所以不能使用反向传播的方法来优化网络。torch.distributions模块的存在目的就是为了解决这个问题。我们可以结合torch.distributions.Categorical进行采样,然后使用对数求导技巧来规避这个问题。当然,除了服从多项式分布的torch.distributions.Categorical类,PyTorch还支持其他的分布(包括连续分布和离散分布),比如torch.distributions.Normal类支持连续的正态分布的采样,可以用于连续的强化学习的策略。
12.torch.hub模块
torch.hub提供了一系列预训练的模型供用户使用。比如,可以通过torch.hub.list函数来获取某个模型镜像站点的模型信息。通过torch.hub.load来载入预训练的模型,载入后的模型可以保存到本地,并可以看到这些模型对应类支持的方法。更多torch.hub支持的模型可以参考PyTorch官网中的相关页面。
13.torch.jit模块
torch.jit是PyTorch的即时编译器(Just-In-Time Compiler,JIT)模块。这个模块存在的意义是把PyTorch的动态图转换成可以优化和序列化的静态图,其主要工作原理是通过输入预先定义好的张量,追踪整个动态图的构建过程,得到最终构建出来的动态图,然后转换为静态图(通过中间表示,即Intermediate Representation,来描述最后得到的图)。通过JIT得到的静态图可以被保存,并且被PyTorch其他的前端(如C++语言的前端)支持。另外,JIT也可以用来生成其他格式的神经网络描述文件,如前文叙述的ONNX。需要注意的一点是,torch.jit支持两种模式,即脚本模式(ScriptModule)和追踪模式(Tracing)。前者和后者都能构建静态图,区别在于前者支持控制流,后者不支持,但是前者支持的神经网络模块比后者少,比如脚本模式不支持torch.nn.GRU(详细的描述可以参考PyTorch官方提供的JIT相关的文档)。
14.torch.multiprocessing模块
torch.multiprocessing定义了PyTorch中的多进程API。通过使用这个模块,可以启动不同的进程,每个进程运行不同的深度学习模型,并且能够在进程间共享张量(通过共享内存的方式)。共享的张量可以在CPU上,也可以在GPU上,多进程API还提供了与Python原生的多进程API(即multiprocessing库)相同的一系列函数,包括锁(Lock)和队列(Queue)等。
15.torch.random模块
torch.random提供了一系列的方法来保存和设置随机数生成器的状态,包括使用get_rng_state函数获取当前随机数生成器状态,set_rng_state函数设置当前随机数生成器状态,并且可以使用manual_seed函数来设置随机种子,也可使用initial_seed函数来得到程序初始的随机种子。因为神经网络的训练是一个随机的过程,包括数据的输入、权重的初始化都具有一定的随机性。设置一个统一的随机种子可以有效地帮助我们测试不同结构神经网络的表现,有助于调试神经网络的结构。
16.torch.onnx模块
torch.onnx定义了PyTorch导出和载入ONNX格式的深度学习模型描述文件。前面已经介绍过,ONNX格式的存在是为了方便不同深度学习框架之间交换模型。引入这个模块可以方便PyTorch导出模型给其他深度学习框架使用,或者让PyTorch可以载入其他深度学习框架构建的深度学习模型。
2.4.2 PyTorch的辅助工具模块
torch.utils提供了一系列的工具来帮助神经网络的训练、测试和结构优化。这个模块主要包含以下6个子模块。
1.torch.utils.bottleneck模块
torch.utils.bottleneck可以用来检查深度学习模型中模块的运行时间,从而可以找到导致性能瓶颈的那些模块,通过优化那些模块的运行时间,从而优化整个深度学习模型的性能。
2.torch.utils.checkpoint模块
torch.utils.checkpoint可以用来节约深度学习使用的内存。通过前面的介绍我们知道,因为要进行梯度反向传播,在构建计算图的时候需要保存中间的数据,而这些数据大大增加了深度学习的内存消耗。为了减少内存消耗,让迷你批次的大小得到提高,从而提升深度学习模型的性能和优化时的稳定性,我们可以通过这个模块记录中间数据的计算过程,然后丢弃这些中间数据,等需要用到的时候再重新计算这些数据。这个模块设计的核心思想是以计算时间换内存空间,当使用得当的时候,深度学习模型的性能可以有很大的提升。
3.torch.utils.cpp_extension模块
torch.utils.cpp_extension定义了PyTorch的C++扩展,其主要包含两个类:CppExtension定义了使用C++来编写的扩展模块的源代码相关信息,CUDAExtension则定义了C++/CUDA编写的扩展模块的源代码相关信息。在某些情况下,用户可能需要使用C++实现某些张量运算和神经网络结构(比如PyTorch没有类似功能的模块或者PyTorch类似功能的模块性能比较低),PyTorch的C++扩展模块就提供了一个方法能够让Python来调用使用C++/CUDA编写的深度学习扩展模块。在底层上,这个扩展模块使用了pybind11,保持了接口的轻量性并使得PyTorch易于被扩展。在后续章节会介绍如何使用C++/CUDA来编写PyTorch的扩展。
4.torch.utils.data模块
torch.utils.data引入了数据集(Dataset)和数据载入器(DataLoader)的概念,前者代表包含了所有数据的数据集,通过索引能够得到某一条特定的数据,后者通过对数据集的包装,可以对数据集进行随机排列(Shuffle)和采样(Sample),得到一系列打乱数据顺序的迷你批次。
5.torch.utils.dlpacl模块
torch.utils.dlpack定义了PyTorch张量和DLPack张量存储格式之间的转换,用于不同框架之间张量数据的交换。
6.torch.utils.tensorboard模块
torch.utils.tensorboard是PyTorch对TensorBoard数据可视化工具的支持。TensorBoard原来是TensorFlow自带的数据可视化工具,能够显示深度学习模型在训练过程中损失函数、张量权重的直方图,以及模型训练过程中输出的文本、图像和视频等。TensorBoard的功能非常强大,而且是基于可交互的动态网页设计的,使用者可以通过预先提供的一系列功能来输出特定的训练过程的细节(如某一神经网络层的权重的直方图,以及训练过程中某一段时间的损失函数等)。PyTorch支持TensorBoard可视化之后,在PyTorch的训练过程中,可以很方便地观察中间输出的张量,也可以方便地调试深度学习模型。