11.3 神经网络模型

人工神经网络是基于简单的数学模型的预测方法。它们允许响应变量与其预测变量之间存在复杂的非线性关系。

神经网络架构

神经网络可以被理解为是一种分层的“神经元”网络结构。预测变量(或输入)构成底层,响应变量(或输出)构成顶层。还可能存在包含“隐藏神经元”的中间层。

最简单的网络不包含中间的隐藏层,等价于线性回归。图11.11 展示了等价于线性回归,包含四个预测变量的神经网络。这些预测变量对应的系数称为“权重”。响应变量由输入项的线性组合得到。在神经网络框架中,通过使用“学习算法”最小化诸如 MSE 等“损失函数”从而确定权重大小。在这个简单的案例中,我们可以使用线性回归,这是一种更有效的训练模型的方法。

与线性回归等价的简单神经网络。

图 11.11: 与线性回归等价的简单神经网络。

如果我们增加一个包含隐藏神经元的中间层,神经网络就成为非线性形式。图11.12 展示了一个简单的例子。

包含4个输入及1个隐藏层的神经网络,其中隐藏层中包含3个隐藏神经元。

图 11.12: 包含4个输入及1个隐藏层的神经网络,其中隐藏层中包含3个隐藏神经元。

这是一个多层前馈网络,其中每层节点都接收来自先前层的输入。每层节点的输出是下一层的输入。每一节点接受输入后会对它们进行加权的线性组合。在输出之前,用一个非线性函数对结果进行修改。例如,对图11.11 中第 \(j\) 个隐藏神经元的输入进行线性组合 \[ z_j = b_j + \sum_{i=1}^4 w_{i,j} x_i. \] 在隐藏层,使用非线性函数(如 sigmoid )对其进行修改, \[ s(z) = \frac{1}{1+e^{-z}}, \] 为下一层提供输入。这往往会降低极端输入值的影响,从而使得神经网络对异常值具有稳健性。

权重 \(b_1,b_2,b_3\)\(w_{1,1},\dots,w_{4,3}\) 都是从数据中“训练学习”得到的。通常需要对权重大小进行限制以防其过大。限制权重的参数称为“衰减参数”,通常设置为0.1。

权重最初取随机值,然后根据观察到的数据进行更新。因此,根据神经网络产生的预测中存在随机性因素。基于此,通常选取不同的随机起始点进行多次训练,并对得到的结果进行平均。

在神经网络中,必须提前确定隐藏层的个数以及每个隐藏层中的节点数。在本章后面我们将介绍如何通过交叉验证确定这些值。

神经网络自回归

类似于我们在线性自回归模型(第 8 章)中使用滞后值作为自变量一样,对于时间序列数据,时间序列的滞后值也可以作为神经网络的输入。我们称之为神经网络自回归或 NNAR 模型。

在本书中,我们只考虑包含一个隐藏层的前馈网络的情况,使用符号 NNAR(\(p,k\)) 来表示隐藏层中有 \(p\) 期滞后输入和 \(k\) 个节点。例如, NNAR(9,5) 模型是一个使用滞后9期观测值 \((y_{t-1},y_{t-2},\dots,y_{t-9})\) 作为输入,预测 \(y_t\) 的神经网络,且该神经网络中隐藏层有5个神经元。 NNAR(\(p,0\)) 模型相当于 ARIMA(\(p,0,0\)) 模型,但这里不包含为确保平稳性而对参数的限制。

对于季节性数据,可以考虑增加同一季节的最后观测值作为输入。例如, NNAR(3,1,2)\(_{12}\) 模型的输入为 \(y_{t-1}\) , \(y_{t-2}\) , \(y_{t-3}\) , \(y_{t-12}\) ,隐藏层中有两个神经元。更一般地, NNAR(\(p,P,k\))\(_m\) 模型的输入为 \((y_{t-1},y_{t-2},\dots,y_{t-p},y_{t-m},y_{t-2m},y_{t-Pm})\) ,隐藏层有 \(k\) 个神经元。 NNAR(\(p,P,0\))\(_m\) 模型相当于 ARIMA(\(p,0,0\))(\(P\),0,0)\(_m\) 模型,但不包含为确保平稳性而对参数的限制。

函数nnetar()可用于拟合 NNAR(\(p,P,k\))\(_m\) 模型。如果没有指定 \(p\)\(P\) 的取值,函数会自动进行选择。对于非季节性时间序列,默认取值为线性 AR(\(p\)) 模型的最佳滞后阶数(根据 AIC 准则)。对于季节性时间序列,默认取值为 \(P=1\)\(p\) 是从季节调整数据得到的最佳线性模型中选择的。如果没有指定 \(k\) 的取值,则默认将其设置为 \(k=(p+P+1)/2\) (四舍五入到最接近的整数值)。

使用神经网络进行预测的时候,计算会迭代进行。比如向前预测一步时,我们只需要将历史数据作为输入。向前预测两步时,需要将历史数据和向前一步预测值作为神经网络的输入。这样的迭代会继续向后进行,直到所有需要的预测值都被计算出来。

案例:太阳黑子

太阳的表面含有磁区,这些磁区看起来像黑色的点。它们会影响无线电波的传播,因此电信公司喜欢预测太阳黑子的活动,以便为将来的困难做好准备。太阳黑子的一个周期为9到14年。在图11.13 中展示了 NNAR(10,6) 模型未来30年的预测值。我们进行了lambda=0的Box-Cox 变换,以确保预测值始终为正数值。

fit <- nnetar(sunspotarea, lambda=0)
autoplot(forecast(fit,h=30)) +
  xlab("时间") + ylab("太阳黑子活动") +
  theme(text = element_text(family = "STHeiti"))
包含10个滞后输入和1个隐藏层的神经网络预测,该隐藏包含6个神经元。

图 11.13: 包含10个滞后输入和1个隐藏层的神经网络预测,该隐藏包含6个神经元。

在这里,最后10个观察值被用作预测变量,且隐藏层中包含6个神经元。该神经网络模型很好地拟合了数据中的周期性。我们还可以看到,模型捕捉到了周期的不对称性,其中每个周期里,太阳黑子活动增加时的曲线比减少时更陡峭。这是 NNAR 模型和线性 AR 模型之间的一个区别 —— 线性 AR 模型可以对周期性进行建模,但模型周期性变动总是对称的。

预测区间

与本书中所讨论的大多数方法不同,神经网络不是基于明确定义的随机模型,因此也不能直接得到预测值对应的预测区间。但是,我们仍然可以使用模拟来得到预测区间,在模拟过程中,通过 bootstrap 残差项生成未来的样本路径。

上面拟合太阳黑子活动的模型可以表示为 \[ y_t = f(\boldsymbol{y}_{t-1}) + \varepsilon_t \] 其中 \(\boldsymbol{y}_{t-1} = (y_{t-1},y_{t-2},\dots,y_{t-10})'\) 是一个包含序列滞后项的向量, \(f\) 是一个在隐藏层中包含6个隐藏节点的神经网络。假定误差序列 \(\{\varepsilon_t\}\) 是同方差的(而且也可能服从正态分布)。

通过从正态分布中随机生成 \(\varepsilon_t\) 的值,或者从历史值中重抽样,我们可以模拟该模型的未来样本路径。因此,如果 \(\varepsilon^*_{T+1}\) 是从 \(T+1\) 时刻的误差项的分布中随机抽取的,那么 \[ y^*_{T+1} = f(\boldsymbol{y}_{T}) + \varepsilon^*_{T+1} \]\(y_{T+1}\) 的分布中的一个随机实现。令 \(\boldsymbol{y}_{T+1}^* = (y^*_{T+1}, y_{T}, \dots, y_{T-6})'\) , 我们可以重复这个过程从而得到 \[ y^*_{T+2} = f(\boldsymbol{y}^*_{T+1}) + \varepsilon^*_{T+2}. \] 通过这种方式,我们可以迭代地模拟未来的样本路径。通过反复模拟样本路径,我们得到了基于拟合神经网络的所有未来值的分布信息。

这是对太阳黑子数据的9种可能的未来样本路径的模拟,每个样本路径都是观测数据未来30年的。

sim <- ts(matrix(0, nrow=30L, ncol=9L),
          start=end(sunspotarea)[1L]+1L)
for(i in seq(9))
  sim[,i] <- simulate(fit, nsim=30L)
autoplot(sunspotarea) + autolayer(sim) +
  xlab("时间") + ylab("太阳黑子活动") +
  theme(text = element_text(family = "STHeiti"))
年度太阳黑子数据的未来样本路径。

图 11.14: 年度太阳黑子数据的未来样本路径。

如果这样重复几百或几千次,我们就可以很好地了解预测值分布的相关信息。这就是函数forecast()为 NNAR 模型生成预测区间的过程:

fcast <- forecast(fit, PI=TRUE, h=30)
autoplot(fcast) + 
  xlab("时间") + ylab("太阳黑子活动") +
  theme(text = element_text(family = "STHeiti"))
年度太阳黑子数据的预测区间。预测区间由模拟的未来样本路径计算得到。

图 11.15: 年度太阳黑子数据的预测区间。预测区间由模拟的未来样本路径计算得到。

因为这种计算速度有些慢,默认选项是PI=FALSE,即默认不会计算预测区间。forecast()中的npaths参数控制了模拟的次数(默认值为1000)。默认情况下,误差项从正态分布中抽取。bootstrap参数允许对误差进行 bootstrap抽取 (即,从历史误差中随机抽取)。