
3-4 神经网络层
上一节运用自动微分实现一条简单线性回归线的求解,然而神经网络是多条回归线的组合,并且每一条回归线可能需再乘上非线性的Activation Function,假如使用自动微分函数逐一定义每条公式,层层串连,程序可能需要很多个循环才能完成。所以为了简化程序开发的复杂度,PyTorch直接建构了各种各样的神经层(Layer)函数,可以使用神经层组合神经网络的结构,我们只需要专注在算法的设计即可,轻松不少。
神经网络是多个神经层组合而成的,如下图,包括输入层(Input Layer)、隐藏层(Hidden Layer)及输出层(Output Layer),其中隐藏层可以有任意多层,广义来说,隐藏层大于或等于两层,即称为深度(Deep)学习。

图3.11 神经网络示意图
PyTorch提供十多类神经层,每一类别又有很多种神经层,都定义在torch.nn命名空间下,可参阅官网说明[2]。
首先介绍完全神经层(Linear Layers),参阅图3.11,即上一层神经层的每一个神经元(图中的圆圈)都连接到下一层神经层的每一个神经元,所以也称为完全连接层,又分为Linear、Bilinear、LazyLinear等,可参阅维基百科[3],接下来开始实操完全连接层。
范例1.进行试验,熟悉完全连接层的基本用法。
下列程序代码请参考【03_04_完全连接层.ipynb】。
(1)载入套件。

(2)产生随机随机数的输入数据,输出二维数据。

(3)建立神经层,Linear参数依次为:
输入神经元个数。
输出神经元个数。
是否产生偏差项(bias)。
设备:None、CPU(‘cpu’)或GPU(‘cuda’)。
数据类型。
Linear神经层的转换为y=xAT+b,y为输出,x为输入。

(4)神经层计算:未训练Linear就是执行矩阵内积,维度(128, 20) @ (20, 30)=(128, 30)。

执行结果:torch.Size([128, 30])。
再测试Bilinear神经层,Bilinear有两个输入神经元个数,转换为
(1)建立神经层。

(2)Bilinear神经层计算:未训练Linear就是执行矩阵内积,维度(128, 20) @ (20,40)+(128, 30) @ (20, 40)=(128, 40)。

执行结果:torch.Size([128, 40])。
范例2.引进完全连接层,估算简单线性回归的参数w、b。
下列程序代码请参考【03_05_简单线性回归_神经层.ipynb】。
(1)载入套件。

(2)产生随机数据,与上一节范例相同。

(3)定义模型函数:导入神经网络模型,简单的顺序型模型内含Linear神经层及扁平层(Flatten),扁平层参数设定哪些维度要转成一维,设定范围参数为(0, -1)可将所有维度转成一维。

测试扁平层如下,Flatten()预设范围参数为(1, -1),故结果为二维,通常第一维为笔数,第二维为分类的预测答案。

执行结果:torch.Size([32, 288])。
(4)定义训练函数。
定义模型:神经网络仅使用一层完全连接层,而且输入只有一个神经元,即x,输出也只有一个神经元,即y。偏差项(bias)默认值为True,除了一个神经元输出外,还会有一个偏差项,设定其实就等于y=wx+b。
定义损失函数:直接使用MSELoss函数取代MSE公式。

(5)使用循环,反复进行正向/反向传导的训练。
计算MSE:改为loss_fn(y_pred, y)。
梯度重置:改由model.zero_grad()取代w、b逐一设定。
权重更新:改用model.parameters取代w、b逐一更新。
model[0].weight、model[0].bias可取得权重、偏差项。


(6)执行训练。

执行结果:w=0.847481906414032, b=3.2166433334350586。
(7)以NumPy验证。

执行结果与答案非常相近。
w=0.8510051491073364, b=4.517198474698629。
(8)显示回归线。

执行结果。

(9)NumPy模型绘图验证。

执行结果非常相近。

(10)损失函数绘图。

执行结果:在第25个执行周期左右就已经收敛。

范例3.接着我们再进一步,引进优化器(Optimizer),操控学习率,参阅图3.9。
这里仅列出与范例2的差异,完整程序请参见程序【03_06_简单线性回归_神经层_优化器.ipynb】。
(1)定义训练函数。
定义优化器:之前为固定的学习率,改用Adam优化器,会采取动态衰减(Decay)的学习率,参见第8行。PyTorch提供多种优化器,第4章会详细说明。
梯度重置:改由优化器(Optimizer)控制,optimizer.zero_grad()取代model.zero_grad(),参见第19行。
权重更新:改用optimizer.step()取代,w、b逐一更新,参见第25行。

(2)训练结果与范例2相同,注意,若出现w或b=nan,损失等于无穷大(inf),表示梯度下降可能错过最小值,继续往下寻找,碰到这种情况,可调低学习率。

范例4.除了回归之外,也可以处理分类(Classification)的问题,数据集采用Scikit-Learn套件内建的鸢尾花(Iris)。
下列程序代码请参考【03_07_IRIS分类.ipynb】。
(1)载入套件。

(2)载入IRIS数据集:有花萼长/宽、花瓣长/宽共4个特征。

执行结果。

(3)数据分割成训练及测试数据,测试数据占20%。

(4)进行one-hot encoding转换:y变成3个变量,代表3个品种的概率。

也可以使用PyTorch函数。

(5)转成PyTorch Tensor。

(6)建立神经网络模型。
Linear(4, 3):4个输入特征;3个品种预测值。
Softmax激励函数会将预测值转为概率形式。

(7)定义损失函数、优化器:与之前相同。

(8)训练模型:第9~10行比较实际值与预测值相等的笔数,并转为百分比。

(9)绘制训练过程的损失及准确率趋势图。

执行结果:在第400个执行周期左右就已经收敛。

(10)模型评估:评估测试数据的准确度。

执行结果:准确率100%,不过,数据分割是随机抽样,每次结果均不相同。