Skip to content

4B SeasonalTrend 线性头

Abstract

这一篇是 04-Level4-encoder分解与线性预测总览 里的 4B 子块。

它只讲一件事:

seasonal 和 trend 怎样各自从 seq_len 线性映射到 pred_len,再相加得到最终预测。

1. 当前层第一性

这一层存在的第一性是:

把分解后的两路序列各自做最简单的线性外推,而不是再套复杂编码器。

换句话说,DLinear 的预测能力在这里最集中地体现出来:

  • seasonal 一条线性头
  • trend 一条线性头
  • 两路结果相加

2. 上下文

父节点:

下一步:

当前入口接口:

python
seasonal_output = self.Linear_Seasonal(seasonal_init)
trend_output = self.Linear_Trend(trend_init)

当前出口接口:

python
x = seasonal_output + trend_output
return x.permute(0, 2, 1)

3. 顺序图

4. 抽象树

5. 完整代码

位置:

python
def encoder(self, x):
    seasonal_init, trend_init = self.decompsition(x)
    seasonal_init, trend_init = seasonal_init.permute(0, 2, 1), trend_init.permute(
        0, 2, 1
    )
    if self.individual:
        seasonal_output = torch.zeros(
            [seasonal_init.size(0), seasonal_init.size(1), self.pred_len],
            dtype=seasonal_init.dtype,
        ).to(seasonal_init.device)
        trend_output = torch.zeros(
            [trend_init.size(0), trend_init.size(1), self.pred_len],
            dtype=trend_init.dtype,
        ).to(trend_init.device)
        for i in range(self.channels):
            seasonal_output[:, i, :] = self.Linear_Seasonal[i](
                seasonal_init[:, i, :]
            )
            trend_output[:, i, :] = self.Linear_Trend[i](trend_init[:, i, :])
    else:
        seasonal_output = self.Linear_Seasonal(seasonal_init)
        trend_output = self.Linear_Trend(trend_init)
    x = seasonal_output + trend_output
    return x.permute(0, 2, 1)

6. 中文注释版完整代码

python
def encoder(self, x):
    seasonal_init, trend_init = self.decompsition(x)

    # 线性层要吃最后一维 = seq_len
    seasonal_init = seasonal_init.permute(0, 2, 1)
    trend_init = trend_init.permute(0, 2, 1)

    if self.individual:
        # 每个变量一套独立线性层
        seasonal_output = torch.zeros(...)
        trend_output = torch.zeros(...)
        for i in range(self.channels):
            seasonal_output[:, i, :] = self.Linear_Seasonal[i](seasonal_init[:, i, :])
            trend_output[:, i, :] = self.Linear_Trend[i](trend_init[:, i, :])
    else:
        # 所有变量共享一套 seasonal / trend 线性层
        seasonal_output = self.Linear_Seasonal(seasonal_init)
        trend_output = self.Linear_Trend(trend_init)

    # 两路预测相加
    x = seasonal_output + trend_output

    # 回到 [B, pred_len, C]
    return x.permute(0, 2, 1)

7. 固定 toy 例子

沿用上一层固定结果:

text
seasonal_init =
[
  [-1/3, -1/3],
  [0,    0],
  [0,    0],
  [1/3,  1/3],
]

trend_init =
[
  [4/3, 31/3],
  [2,   11],
  [3,   12],
  [11/3,38/3],
]

固定:

  • seq_len = 4
  • pred_len = 2
  • individual = False

8. 代码块 1:为什么先 permute

代码:

python
seasonal_init = seasonal_init.permute(0, 2, 1)
trend_init = trend_init.permute(0, 2, 1)

8.1 输入/输出语义

输入:

  • (B, seq_len, C)

输出:

  • (B, C, seq_len)

因为:

  • nn.Linear(self.seq_len, self.pred_len) 作用在最后一维
  • 所以必须让“时间长度 seq_len”成为最后一维

8.2 toy 张量逐步演变

text
seasonal_init(channel 1) = [-1/3, 0, 0, 1/3]
seasonal_init(channel 2) = [-1/3, 0, 0, 1/3]

trend_init(channel 1) = [4/3, 2, 3, 11/3]
trend_init(channel 2) = [31/3, 11, 12, 38/3]

9. 代码块 2:seasonal 线性映射

代码:

python
seasonal_output = self.Linear_Seasonal(seasonal_init)

9.1 输入/输出语义

输入:

  • 每个变量一条长度为 seq_len 的 seasonal 向量

输出:

  • 每个变量一条长度为 pred_len 的 seasonal 预测向量

9.2 toy 可算过程

假设 Linear_Seasonal 的 toy 权重矩阵是:

text
Ws =
[ [1, 0, 0, 0],
 [0, 0, 0, 1] ]

这表示:

  • 第一个未来步只看历史第一个位置
  • 第二个未来步只看历史最后一个位置

对 seasonal 第一个变量:

text
[-1/3, 0, 0, 1/3]

做线性映射:

text
future_1 = 1*(-1/3) + 0 + 0 + 0 = -1/3
future_2 = 0 + 0 + 0 + 1*(1/3) = 1/3

所以:

text
seasonal_output(channel 1) = [-1/3, 1/3]

10. 代码块 3:trend 线性映射

代码:

python
trend_output = self.Linear_Trend(trend_init)

10.1 输入/输出语义

输入:

  • 每个变量一条长度为 seq_len 的 trend 向量

输出:

  • 每个变量一条长度为 pred_len 的 trend 预测向量

10.2 toy 可算过程

假设 Linear_Trend 的 toy 权重矩阵是:

text
Wt =
[ [0, 1, 0, 0],
 [0, 0, 1, 0] ]

对 trend 第一个变量:

text
[4/3, 2, 3, 11/3]

做线性映射:

text
future_1 = 2
future_2 = 3

所以:

text
trend_output(channel 1) = [2, 3]

11. 代码块 4:两路相加

代码:

python
x = seasonal_output + trend_output

11.1 输入/输出语义

输入:

  • seasonal_output
  • trend_output

输出:

  • 最终预测的隐藏结果 (B, C, pred_len)

11.2 toy 张量逐步演变

第一个变量:

text
seasonal_output = [-1/3, 1/3]
trend_output    = [2, 3]

相加后 = [5/3, 10/3]

第二个变量同理。

12. 代码块 5:回到时间维

代码:

python
return x.permute(0, 2, 1)

12.1 输入/输出语义

输入:

  • (B, C, pred_len)

输出:

  • (B, pred_len, C)

为什么要这么做:

  • TFB 和 forecasting 主链都把时间维放在中间:(B, 时间, 变量)

12.2 toy 张量逐步演变

最终 toy 输出可写成:

text
[
  [5/3, 32/3],
  [10/3, 35/3],
]
shape = (1, 2, 2)

这意味着:

  • 未来第 1 步:两个变量分别预测为 5/3, 32/3
  • 未来第 2 步:两个变量分别预测为 10/3, 35/3

13. 当前真实例子里的参数在这层控制什么

  • seq_len = 96
    • 两条线性头输入维度。
  • pred_len = 24
    • 两条线性头输出维度。
  • enc_in = 7
    • 一共有 7 条变量通道被并行预测。
  • individual = False
    • 7 个变量共享一套 seasonal / trend 线性头。

14. 当前层最该固定什么

  1. DLinear 的预测核心就是:
    • seasonal 一路线性预测
    • trend 一路线性预测
    • 两路相加
  2. 它没有复杂 decoder;预测能力主要来自分解 + 线性外推。
  3. individual=False 时,所有变量共享同一套线性头。

15. 下一步

收束回整条链:

补查 torch 算子:

*记录并在线阅读我的笔记*