做项目涉及到了时间序列的概率(区间)预测。说到时序概率预测,那最经典的无非是概率循环神经网络DeepAR了。DeepAR 是 Amazon 于 2017 年提出的基于深度学习的时间序列预测方法,目前已集成到 Amazon SageMaker 和 GluonTS 中。前者是 AWS 的机器学习云平台,后者是 Amazon 开源的时序预测工具库。DeepAR有的优势是:输出概率分布,比起点预测,容错率高,在有风险,误差比较大的的领域尤其重要。
想要快速使用DeepAR,可以直接参考awslabs/gluonts: Probabilistic time series modeling in Python (github.com)
当自己根据原理观摩大佬写的DeepAR算法的时候,也记录下了自己的心得和理解,在这里分享出来。
比起LSTM,DeepAR改进的地方主要是输出的概率分布了,而概率分布,主要由高斯分布和负二项分布来实现。这篇文章主要是高斯分布来实现。
训练模型和预测模型如下,预测思路为多步递归预测(将预测值作为协变量),而模型架构主要采用编码器-译码器(Encoder-Decoder)架构,及使用LSTM做编码器提取时序特征,再将LSTM隐藏层输出传入线性层中计算出概率分布的中值和数学期望(译码):
为已有的上一个时间段的目标数据,
是协变量,目的是预测下一个时间段的目标数据
。在这里我们使用高斯分布,我们将两个参数inputs进networks的时候,DeepAR采用的network是LSTM,我们可以自己随便换成其他模型:
self.input_embed = nn.Linear(1, embedding_size)
self.encoder = nn.LSTM(embedding_size+input_size, hidden_size, \
num_layers, bias=True, batch_first=True)
if likelihood == "g":
self.likelihood_layer = Gaussian(hidden_size, 1)
elif likelihood == "nb":
self.likelihood_layer = NegativeBinomial(hidden_size, 1)
self.likelihood = likelihood
#高斯函数的损失函数,用于模型的训练,不作为构建
def gaussian_likelihood_loss(z, mu, sigma):
negative_likelihood = torch.log(sigma + 1) + (z - mu) ** 2 / (2 * sigma ** 2) + 6
return negative_likelihood.mean()
#高斯神经网络,用于生成mu和sigma
class Gaussian(nn.Module):
def __init__(self, hidden_size, output_size):
super(Gaussian, self).__init__()
self.mu_layer = nn.Linear(hidden_size, output_size)
self.sigma_layer = nn.Linear(hidden_size, output_size)
def forward(self, h):
_, hidden_size = h.size()
sigma_t = torch.log(1 + torch.exp(self.sigma_layer(h))) + 1e-6
sigma_t = sigma_t.squeeze(0)
mu_t = self.mu_layer(h).squeeze(0)
return mu_t, sigma_t
值得注意的是,前面我们提到DeepAR是基于递归预测思路的,所以预测的过程中不能像训练时那样直接给decoder输入上一时刻的真实值,只能通过抽样来得到一个估计值(一般是区间的中值) ,将估计值输入到下一时刻的LSTM当中不断迭代以得到预测结果(但会累积误差)。
估计的具体步骤如下:
for s in range(seq_len + output_horizon): #seq_len + output_horizon是训练集和测试集的长度和
if s < seq_len: #训练时不断传入真实值
ynext = y[:, s].view(-1, 1)
yembed = self.input_embed(ynext).view(num_ts, -1)
x = X[:, s, :].view(num_ts, -1)
else: #进入测试阶段
yembed = self.input_embed(ynext).view(num_ts, -1) #训练时迭代上次传入的z
x = Xf[:, s-seq_len, :].view(num_ts, -1) #Xf为测试集的协变量
x = torch.cat([x, yembed], dim=1) # num_ts, num_features + embedding
inp = x.unsqueeze(1)
if h is None and c is None:
out, (h, c) = self.encoder(inp) # h size (num_layers, num_ts, hidden_size)
else:
out, (h, c) = self.encoder(inp, (h, c))
模型基于训练出的高斯分布进行预测的时候,DeepAR 产生一定数量的多步预测结果(你可以自己选择抽样数量,100,1000,2000都ok),我们可以根据模型效果和实际需求设置输出不同的分布区间,例如P10,P50和P90三个值。这里的P10指的是置信度,即10%的可能性会小于P10这个值。
#result为每次抽取ypredict的集合,采用numpy的quantile进行百分比切割
p50 = np.quantile(result, 0.5, axis=1)
p90 = np.quantile(result, 0.9, axis=1)
p10 = np.quantile(result, 0.1, axis=1)
最后,DeepAR已经是17-18年的产物了,目前预测的性能也许并不能和sota模型相提并论,但它提供的最大价值在于概率预测的思路,我们可以尝试将基于LSTM模型的编码器(局限所在)转换成目前更加主流强大的时序预测模型,我相信一定会有更加优异的预测效果!!
bishe可联系QQ:2663505372
代码解析来自Github大佬:jingw2/demand_forecast (github.com)
AWS DeepAR网址:https://arxiv.org/abs/1704.04110
概率自回归预测—DeepAR模型浅析:概率自回归预测——DeepAR模型浅析 - 知乎