2.3.2 神经网络层的实现原理
2.3.1节中使用伪代码定义了一些卷积神经网络接口和模型构建过程,整个模型构建过程需要创建训练变量和构建连接过程。随着网络层数的增加,手动管理训练变量是一个烦琐的过程,因此2.3.1节中描述的接口在机器学习库中属于低级API。机器学习编程库大都提供了更高级用户友好的API,它将神经网络层抽象出一个基类,所有的神经网络层都继承基类实现。如MindSpore提供的mindspore.nn.Cell,PyTorch提供的torch.nn.Module。基于基类它们都提供了高阶API,如MindSpore提供的mindspore.nn.Conv2d、mindspore.nn.MaxPool2d、mindspore.dataset;PyTorch提供的torch.nn.Conv2d、torch.nn.MaxPool2d、torch.utils.data.Dataset。
图2.9描述了神经网络模型构建过程中的基本细节。神经网络层需要初始化训练参数、管理参数状态以及定义计算过程;神经网络模型需要实现对神经网络层和神经网络层参数管理的功能。在机器学习编程库中,承担此功能有MindSpore的Cell类和PyTorch的Module类。Cell和Module是模型抽象方法,也是所有网络的基类。现有模型抽象方案有两种:一种是抽象出两个方法,分别为Layer(负责单个神经网络层的参数构建和前向计算),Model(负责对神经网络层进行连接组合和神经网络层参数管理);另一种是将Layer和Model抽象成一个方法,该方法既能表示单层神经网络层,也能表示包含多个神经网络层堆叠的模型,Cell和Module就是这样实现的。
图2.9 神经网络模型构建细节
图2.10展示了设计神经网络基类抽象方法的通用表示。通常在构造器会选择使用Python中collections模块的OrderedDict方法存储初始化神经网络层和神经网络层参数。它的输出是有序的,相比与无序的Dict方法更适合深度学习这种模型堆叠的模式。参数和神经网络层的管理是在__setattr__方法中实现的,当检测到属性是属于神经网络层及神经网络层参数时就记录起来。神经网络模型比较重要的是计算连接过程,可以在__call__方法中重载,实现神经网络层时在这里定义计算过程。训练参数的返回接口是为了给优化器传送所有训练参数,这些参数是基类遍历了所有网络层后得到的。这里只列出了一些重要的方法,在自定义方法中,通常需要实现参数插入与删除方法、神经网络层插入与删除、神经网络模型信息等。
图2.10 神经网络基类抽象方法
神经网络接口层基类的实现,这里仅做了简化的描述,在实际实现时,执行计算的_call_方法并不会让用户直接重载,它往往在__call__方法之外,定义一个执行操作的方法(该方法对于神经网络模型是实现网络结构的连接,对于神经网络层则是实现计算过程),然后通过__call__方法调用;如MindSpore的Cell因为动态图和静态图的执行是不一样的,因此在__call__方法中定义动态图和计算图的计算执行,在construct方法中定义层或者模型的操作过程。