PyTorch与TensorFlow/Keras一样,提供两种模型结构:Sequential model和Functional API。
Sequential model:顺序型模型使用torch.nn.Sequential函数包覆各项神经层,适用于简单的结构,执行时神经层一层接一层地执行。
Functional API:使用torch.nn.functional函数,可以设计成较复杂的模型结构,包括多个输入层或多个输出层,也允许分叉及合并。PyTorch并未正式命名为Functional API,笔者为方便学习,自行采用TensorFlow/Keras的专门术语。
下列程序代码请参考【04_09_Sequential_vs_Functional.ipynb】。
(1)torch.nn.Sequential包含各种的神经层,简洁的写法如下。
(2)可以为每一神经层命名,使用字典(OrderedDict)数据结构,设定名称及神经层种类,逐一配对。
(3)注意,上一层的输出神经元个数要等于下一层的输入神经元个数,PyTorch必须同时设定输入与输出个数,而TensorFlow/Keras在第一层设定输入与输出个数,在第二层只要设定输出个数即可,因为输入可从上一层的输出得知,这点TensorFlow/Keras就略胜一筹,不要小看这一点,进阶神经层如CNN、RNN接到Linear,要算Linear的输入个数,就要伤脑筋了。
(4)可显示模型结构如下。
执行结果如下。
使用torch.nn.functional函数,可以设计成较复杂的模型结构,如包括多个输入层或输出层,也允许分叉及合并。相关函数与torch.nn几乎可以一一对照,只是torch.nn.functional函数名称均使用小写,可参阅PyTorch官网torch.nn.functional的说明 [10] 。
下列程序代码请参考【04_09_Sequential_vs_Functional.ipynb】后半部。
范例1.先看一个简单的范例,看看torch.nn.functional.linear的用法。
第2行:torch.nn.functional通常会被取别名为F。
第6行:torch.nn.functional下的函数必须输入张量的值,而非张量的维度。
F.linear的参数有两个:输入张量和权重,其中权重是初始值,训练过程会不断更新。
范例2.将上述顺序型模型改写为Functional API。
在第二层之后的完全连接层指定权重并不合理,因此,通常还是以nn.Linear取代F.linear。
F.relu(x):表示relu接在x的下一层。
范例3.使用类别定义模型,这是一般Functional API定义模型的方式。
类别至少要包含两个方法(Method):init和forward。
init函数内声明要使用的神经层对象。
forward函数内定义神经层的串连。
扁平层函数为torch.flatten(x, 1),命名空间不是torch.nn.functional,第2、第3个参数是打扁的起讫的维度,第3个参数预设为-1,表示最后一个维度。
使用的指令如下。
model=Net()
可显示模型结构如下。
执行结果如下。
范例4.使用类别定义模型,进行手写阿拉伯数字辨识(MNIST)。
下列程序代码请参考【04_10_手写阿拉伯数字辨识_专家模式.ipynb】。
以下只说明差异的程序代码。
定义模型。
训练模型:使用CrossEntropyLoss(),模型不可加SoftMax层。
结果与顺序模型差异不大。
范例5.使用另一种损失函数—Negative Log Likelihood Loss,进行手写阿拉伯数字辨识(MNIST),函数介绍可参阅官网说明 [11] 。
下列程序代码请参考【04_11_手写阿拉伯数字辨识_专家模式_NLL_LOSS.ipynb】。
以下只说明差异的程序代码。
定义模型:使用F.log_softmax取代F.softmax(第17行),可避免损失为负值。
训练模型:使用F.nll_loss取代CrossEntropyLoss(),才可加SoftMax层(第19行)。
评分:使用F.nll_loss(output, target, reduction='sum').item(),计算多笔测试数据的损失和(第13行)。