Skip to content

Level2 数据进入 DLinear

Abstract

这一篇对应 00-DLinear总览与Level树Level 2

它只讲一件事:

batch 四元组怎样经过 _process(...),变成 DLinear 的真正输入。

1. 当前层第一性

这一层存在的第一性是:

把框架里的统一 batch 四元组,翻译成 DLinear.forward(...) 的四输入。

同时要钉死一个关键事实:

虽然 DLinear 也走统一四输入接口,但 forecasting 路径里真正决定预测的是 x_enc

也就是说,这一层的意义不是“DLinear 真需要一个 decoder”,而是:

  • TFB 给了统一的接口壳
  • DLinear 接受这个壳
  • 但它内部只强依赖其中一部分输入

2. 上下文

父节点:

下一层:

当前入口接口:

python
_process(input, target, input_mark, target_mark)

当前出口接口:

python
DLinear.forward(x_enc, x_mark_enc, x_dec, x_mark_dec)

3. 当前真实例子里的 batch 是什么

当前真实运行例子里:

  • input.shape = (4, 96, 7)
  • target.shape = (4, 72, 7)
  • input_mark.shape = (4, 96, 4)
  • target_mark.shape = (4, 72, 4)

这些张量的语义:

  • input
    • encoder 侧历史数值窗口。
  • target
    • decoder 侧监督窗口,长度 = label_len + pred_len = 48 + 24 = 72
  • input_mark
    • input 这 96 个时间步对应的时间特征。
  • target_mark
    • target 这 72 个时间步对应的时间特征。

4. 顺序图

5. 抽象树

6. _process(...) 完整代码

位置:

python
def _process(self, input, target, input_mark, target_mark):
    dec_input = torch.zeros_like(target[:, -self.config.horizon :, :]).float()
    dec_input = (
        torch.cat([target[:, : self.config.label_len, :], dec_input], dim=1)
        .float()
        .to(input.device)
    )
    output = self.model(input, input_mark, dec_input, target_mark)

    return {"output": output}

7. 中文注释版完整代码

python
def _process(self, input, target, input_mark, target_mark):
    # 第一步:先造未来 pred_len 段的零占位
    dec_input = torch.zeros_like(target[:, -self.config.horizon :, :]).float()

    # 第二步:把 target 前半段的 label_len 段历史尾部拼到前面
    dec_input = (
        torch.cat([target[:, : self.config.label_len, :], dec_input], dim=1)
        .float()
        .to(input.device)
    )

    # 第三步:统一调用底层模型 forward
    output = self.model(input, input_mark, dec_input, target_mark)

    return {"output": output}

8. 固定 toy 例子

固定 toy 参数:

  • B = 1
  • label_len = 2
  • pred_len = horizon = 2
  • enc_in = 2

固定 toy 输入:

text
input =
[
  [1, 10],
  [2, 11],
  [3, 12],
  [4, 13],
]
shape = (1, 4, 2)

target =
[
  [3, 12],
  [4, 13],
  [5, 14],
  [6, 15],
]
shape = (1, 4, 2)

9. 代码块 1:先造未来零占位

代码:

python
dec_input = torch.zeros_like(target[:, -self.config.horizon :, :]).float()

9.1 输入/输出语义

输入:

  • target[:, -pred_len:, :]
    • 未来监督区的形状模板。

输出:

  • dec_input
    • 一个和未来监督区同 shape 的零占位张量。

9.2 toy 张量逐步演变

先取 target 最后 pred_len = 2 段:

text
[
  [5, 14],
  [6, 15],
]

torch.zeros_like(...) 后变成:

text
dec_input =
[
  [0, 0],
  [0, 0],
]

这一段在总体里的作用:

先给 decoder 输入留出“未来位置”,但这些位置暂时没有真实值,所以先用 0 占位。

虽然 DLinear 最后不真正依赖这个 decoder 语义,但统一接口层还是要这么造。

10. 代码块 2:拼上历史尾部

代码:

python
torch.cat([target[:, : self.config.label_len, :], dec_input], dim=1)

10.1 输入/输出语义

输入:

  • target[:, :label_len, :]
    • decoder 历史段。
  • dec_input
    • 未来零占位段。

输出:

  • x_dec
    • 长度 = label_len + pred_len 的 decoder 输入。

10.2 toy 张量逐步演变

先取 targetlabel_len = 2 段:

text
[
  [3, 12],
  [4, 13],
]

再和零占位拼接:

text
x_dec =
[
  [3, 12],
  [4, 13],
  [0,  0],
  [0,  0],
]

这一段在总体里的作用:

把“历史尾部 + 未来空位”拼成一个统一 decoder 输入。

这对 Informer 这种真正有 decoder 的模型是核心语义; 对 DLinear 来说,这一步更多是为了服从统一接口。

11. 代码块 3:四输入进入 DLinear

代码:

python
output = self.model(input, input_mark, dec_input, target_mark)

11.1 输入/输出语义

传进去后,对应关系是:

  • x_enc = input
    • 历史数值窗口。
  • x_mark_enc = input_mark
    • encoder 侧时间特征。
  • x_dec = dec_input
    • decoder 侧历史尾部 + 零占位。
  • x_mark_dec = target_mark
    • decoder 侧时间特征。

输出:

  • output
    • DLinear 的预测张量,形状 (B, pred_len, C)

11.2 最关键的解释

这里必须固定一个事实:

DLinear 虽然接受四输入,但 forecasting 主链里真正参与计算主体的是 x_enc

所以你读 DLinear 时,不能把注意力放在:

  • x_mark_enc
  • x_dec
  • x_mark_dec

而应该把注意力放在:

  • x_enc 是怎样被分解成 seasonal / trend
  • seasonal / trend 怎样各自外推到未来 pred_len

12. 这一层最该固定什么

  1. DLinear 仍然走统一 _process(...) 接口。
  2. 所以 x_dec 仍然会被构造。
  3. 但 DLinear 的 forecasting 主体几乎只依赖 x_enc
  4. 这一层的根本作用,是把框架 batch 翻译成模型可接受的统一四输入。

13. 下一步

继续看:

补查 torch 算子:

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