前面几节我们学习了深度学习模型的基本原理与实现,下面我们继续学习深度学习计算的各个组成部分,为后续学习复杂模型打下基础。 在学习模型构造之前,我们先了解一下什么是计算图。 计算图(ComputationalGraph) 计算图是用来描述运算的有向无环图,有两个主要元素: 节点(Node):节点表示数据,如向量、矩阵、张量。 边(Edge):边表示运算,如加减乘除卷积等。 用计算图表示:y(x1w)(x2w),如下图所示: 计算图 其中,x1,x2,w,y分别为节点,,为节点之间的操作,即边。故yab,其中ax1w,bx2w。 计算图与梯度求导 求上图y对w的导数,根据复合函数的求导法则,推理如下: 对应到计算图中,就是根节点y到叶子节点w有两条路径yaw和ybw。根节点依次对每条路径的子节点求导,直到叶子节点w,最后把每条路径的导数相加即可,其实就是我们前面讲过的反向传播求偏导。 总结为y对w求导,就是从计算图中找到所有y到w的路径,然后把各个路径的导数进行求和。 代码演示如下: PyTorch动态计算图演示 计算图又分为静态计算图(StaticComputationalGraph)和动态计算图(DynamicComputationalGraph): 动态图就是运算和搭建同时进行,也就是可以先计算前面的节点的值,再根据这些值搭建后面的计算图。 静态图是先搭建图,然后再输入数据进行运算,是先定义后运行的方式,之后再次运行的时候就不再需要重新构建计算图,所以速度会比动态图更快,但是不灵活。 PyTorch因其动态图的特色吸引了很多使用者,而Tensorflow早期是用的静态图导致开发很不友好,后来也改成了动态图模式。 tensorflow静态计算图演示 动态图和静态图对比 模型构造 PyTorch是基于动态图的模型搭建方式,我们可以随机的在网络中添加或者删除网络层。PyTorch为我们提供了非常方便的nn工具箱,我们搭建模型只需要定义一个继承自nn。module的类并实现其init和forward方法就可。 其中nn。Module类是nn模块中提供的一个模型构造类,是所有神经网络模块的基础类。我们需要继承它来定义我们的自己的网络结构。init方法中动态绑定成员变量,定义我们的网络结构和相关参数;forword方法中决定数据流经这些成员变量的顺序,即网络的前向传播方式。 PyTorch中nn工具箱的结构示意图 一般来说,我们构建网络模型时用到的卷积层、全连接层、Dropout层等含有可学习参数的层都是继承nn。Module,而激活函数、池化层等函数类型的层继承于nn。functional。 1。继承Module类来构造模型 下面定义的MLP类中无须定义反向传播函数。系统将通过自动求梯度而自动生成反向传播所需的backward函数。 我们可以实例化MLP类得到模型变量net。下的代码初始化net并传输数据X做次前向计算。其中,net(X)会调用MLP继承Module类的call函数,这个函数将调用MLP类定义的forward函数来完成前向计算。 继承Module类的基础模型 这并没有将Module类命名为Layer(层)或者Model(模型)之类的名字,这是因为该类是一个可供自由组建的部件。它的子类既可以是一个层(如PyTorch提供的Linear类),又可以是个模型(如这定义的MLP类),或者是模型的一个部分。我们下面来展示它的灵活性。 2。Module的子类 PyTorch还提供了许多继承自Module的类,如:Sequential、ModuleList和ModuleDict等。 2。1Sequential类 当模型的前向计算为简单的串联各个网络层的时候,可以通过Sequential类以更加简单的方式来定义模型。Sequential可以接收一个子模块的有序字典(OrderedDict)或者一系列的子模块作为参数来逐一的添加Module的子类的实例。在前向传播计算的时候,可以将这些实例按照添加的顺序逐一计算,向前传播。这里实现一个MySequential类,其机制和Sequential类似。 举例如下: Sequential类模型 2。2ModuleList类 ModuleList类接收一个子模块的列表作为输入,也可以类似List那样进行append和extend操作。类似于我们建立一个list,list内部中的每一个元素代表一个网络层。 举例如下: ModuleList类模型 ModuleList不同于一般的Python的list,加入到ModuleList里面的所有模块的参数会被自动添加到整个网络中。 2。3ModuleDict类 ModuleDict类接收一个子模块的字典作为输入,然后按照类似于字典的形式进行添加访问操作,举例如下: ModuleDict类模型 ModuleDict和ModuleList类似的是,ModuleDict实例仅仅是存放了一些模块的字典,并没有定义forward函数,前向传播的方式需要我们自己定义。同样,ModuleDict也与Python的Dict有所不同,ModuleDict里的所有模块的参数会被自动添加到整个网络结构的内部。 3。构造复杂的模型 上面介绍的Sequential使用简单,但灵活性不足。通常我们还是自定义类,继承nn。Module,去完成更复杂的模型定义和控制。下面的我们尝试构建一个复杂点的网络来总结上面的内容,该网络中包含不被迭代的参数,即常数参数,还多次调用相同的层。 复杂模型构建 总结 PyTorch是基于动态图的模型搭建方式。 Module类是PyTorch中所有神经网络模块的基类,也是个可供自由构建的模块。它的子类既可以是个层(如PyTorch提供的Linear类),又可以是一个模型(如这里是定义的MLP类),或者是模型的一个部分。 Sequential、ModuleList、ModuleDict类都继承自Module类。 Sequential内的模块需要按照顺序排列,要保证相邻层的输入输出大小相匹配,内部forward功能已经实现。与Sequential不同,ModuleList和ModuleDict并没有定义一个完整的网络,它们只是将不同的模块存放在一起,这些模块之间没有联系也没有顺序(所以不用保证相邻层的输入输出维度匹配),需要自己定义forward函数。 虽然Sequential等类可以使模型构造更加简单,但直接继承Module类可以极大地拓展模型构造的灵活性。