【长时间序列预测】Autoformer 代码详解之[2]模型部件之时间序列分解

1. 时间序列分解

        采用 移动平均 来平滑周期波动 和 强调 长期趋势。

        输入长度L的时间序列:X\in \mathbb{R}^{L\times d}

【长时间序列预测】Autoformer 代码详解之[2]模型部件之时间序列分解_第1张图片

2. 实验分析

【长时间序列预测】Autoformer 代码详解之[2]模型部件之时间序列分解_第2张图片

        表2:利用MSE度量,在ETT数据集上进行分解的消融研究。 其中 Ours 本文提出的分解架构引入其他模型。 Seq 采用两个模型分别来预测 预处理分解的  seasonal 和 trend-cyclical 部分。 Promotion(提升)  是 和 Origin 比, 预处理分解(Sep)和本文的分解架构(Ours)的   MSE 提升了多少。说明了 本文的方法提升的MSE 更多。

上表表面:上面的分解方法引入到其他模型 是有效的,预测的长度越长,效果越明显。作者说,这验证了 该分解方法可以泛化到其他模型,释放其它依赖学习机制的能力(?不太懂啥意思),缓解复杂模式 带来的 干扰( distraction)。此外,该分解方法超出了 预处理分解,尽管预处理分解使用了更大的模型和更多的参数。特别是,预处理分解 甚至可能带来负面影响,因为它忽略了组件(components)在长期未来的相互作用,如Transformer[36]  predict-720,Informer[42]  predict-336。

3. 序列分解分析

        如图4所示,如果没有我们的序列分解块,预测模型将无法捕捉  seasonal 部分的增长趋势和峰值。通过增加序列分解块, Autoformer 可以逐步地聚合和细化序列中的  trend-cyclical 部分。这种设计还便于学习  seasonal 部分,特别是高峰和低谷部分。这验证了我们提出的渐进分解架构的必要性。

【长时间序列预测】Autoformer 代码详解之[2]模型部件之时间序列分解_第3张图片

        图4:最后的解码器层学习到的  seasonal X_{de}^{M} 和  trend-cyclical \tau_{de}^{M}。我们在解码器中从左到右,逐渐增加了 分解模块。此例来自 ETT 数据集 在输入 96  预测720设置下。为了清晰起见,我们在原始数据中附加了线性增长。

3. pytorch 实现:

class moving_avg(nn.Module):
    """
    Moving average block to highlight the trend of time series
    """
    def __init__(self, kernel_size, stride):
        super(moving_avg, self).__init__()
        self.kernel_size = kernel_size
        self.avg = nn.AvgPool1d(kernel_size=kernel_size, stride=stride, padding=0)

    def forward(self, x):
        # padding on the both ends of time series  假设输入x shape: 32, 96, 21  kernel_size = 25
        front = x[:, 0:1, :].repeat(1, (self.kernel_size - 1) // 2, 1)   # 32, 1, 21 -> 32, 12, 21  取的x 的第一个值进行填充
        end = x[:, -1:, :].repeat(1, (self.kernel_size - 1) // 2, 1) # 32, 1, 21 -> 32, 12, 21 取的x 的最后一个值进行填充
        x = torch.cat([front, x, end], dim=1)  # 24 + 96 = 120, (32, 120, 21)
        x = self.avg(x.permute(0, 2, 1)) # (32, 21, 120)  # 换位置是因为 AvgPool1d 是在最后一维操作,我们要pool 的是 序列那个维度,故需要把该维度放到最后
        x = x.permute(0, 2, 1) # pool 完毕换回来
        return x


class series_decomp(nn.Module):
    """
    Series decomposition block
    """
    def __init__(self, kernel_size):
        super(series_decomp, self).__init__()
        self.moving_avg = moving_avg(kernel_size, stride=1)

    def forward(self, x):
        moving_mean = self.moving_avg(x)
        res = x - moving_mean 
        return res, moving_mean

 上面公式中所谓的 Padding 实现是代码中的 x = torch.cat([front, x, end], dim=1),front 是把原来输入的第一个复制多次(根据移动平均的长度),end 是把序列的最后一个进行复制多次(同front 次),然后把 它们cat 到一起,然后使用 AvgPool1d 后,结果的序列长度也就同X 一样,不会发生变化。

【长时间序列预测】Autoformer 代码详解之[2]模型部件之时间序列分解_第4张图片

 上图,假设 x 输入长度为 4, kernel_size = 25, 上图就是 Padding 操作,为了保持AvgPool1d 后长度不变。

然后在上面使用 1D 平均pool ,一次移动一步。


感觉这种只取最后一个元素和第一个元素的 Padding 方式并不是一个好办法,应该可以优化该部分。

如果 x 的长度很长,这种方式或许还好,但是如果x 较短,可能问题就比较大了。


4. 移动平均解释

6.2 移动平均 | 预测: 方法与实践

时间邻近的情况下,观测值也很可能接近。由此,平均值消除了数据中的一些随机性,从而我们可以得到较为平滑的趋势周期项

年份 电力销售量 (亿瓦时) 5-MA
1989 2354.34
1990 2379.71
1991 2318.52 2381.53
1992 2468.99 2424.56
1993 2386.09 2463.76
1994 2569.47 2552.60
1995 2575.72 2627.70
1996 2762.72 2750.62
1997 2844.50 2858.35
1998 3000.70 3014.70
1999 3108.10 3077.30
2000 3357.50 3144.52
2001 3075.70 3188.70
2002 3180.60 3202.32
2003 3221.60 3216.94
2004 3176.20 3307.30
2005 3430.60 3398.75
2006 3527.48 3485.43
2007 3637.89
2008 3655.00

        表中的最前面 2 年和最后面 2 年都没有值,因为我们在两端都没有观测值。之后我们会运用更复杂的方法来估计趋势-周期项,它可以求出端点的估计值。

        显然,最简单的移动平均法前后数据的结果是有缺失的。

趋势-周期项(红色)比原始数据更平滑,捕捉了时间序列去除了微小波动后的主要变化。移动平均的阶数决定了趋势-周期项的平滑程度。一般情况下,阶数越大曲线越平滑。

【长时间序列预测】Autoformer 代码详解之[2]模型部件之时间序列分解_第5张图片

其他知识,请参考:预测: 方法与实践

计划更新:[3]模型整体架构分析,[4]模型部件之自相关层

你可能感兴趣的:(时间序列预测以及异常检测,时间序列预测)