TimeMixer 代码复现 第二篇

TimeMixer 的调用类图

首先给出 TimeMixer 的调用类图 ,接着我们继续看 forcast 的执行, 接第一篇

TimeMixer 调用图

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
输入: x_enc [B,T,N]  x_mark_enc [B,T,feat]
  |
  
┌──────────────────────────────────────────────────┐
 __multi_scale_process_inputs                     
  ┌─────────┐   ┌─────────┐   ┌─────────┐         
   原始尺度    尺度/2     尺度/4    ...   
  └─────────┘   └─────────┘   └─────────┘         
└──────────────────────────────────────────────────┘
  |
  
┌──────────────────────────────────────────────────┐
 normalize_layers 归一化                          
└──────────────────────────────────────────────────┘
  |
  
┌──────────────────────────────────────────────────┐
 pre_enc 预处理                                   
  ┌───────────────────┐     ┌───────────────────┐ 
   channel_independence=True      channel_independence=False  
  └─────────┬─────────┘     └──────────┬────────┘ 
                                                
                              ┌───────────────┐  
                               序列分解        
                               季节性 + 趋势   
                              └───────────────┘  
└────────────┼──────────────────────────┼──────────┘
                                       
                                       
┌────────────────────────────────────────────────┐
 enc_embedding 特征嵌入                         
 [value_embedding + temporal_embedding]         
└────────────────────────────────────────────────┘
  |
  
┌────────────────────────────────────────────────┐
 pdm_blocks 多尺度混合编码器                    
  ┌───────────────┐                             
   序列分解                                   
  └───────┬───────┘                             
                                               
  ┌───────▼───────┐     ┌───────────────┐       
   季节性分量          趋势性分量           
  └───────┬───────┘     └───────┬───────┘       
                                              
  ┌───────▼───────┐     ┌───────▼───────┐       
   自下而上混合        自上而下混合         
  └───────┬───────┘     └───────┬───────┘       
                                              
          └─────────┬───────────┘               
                                               
└────────────────────┼───────────────────────────┘
                     
                     
┌────────────────────────────────────────────────┐
 future_multi_mixing 解码器                     
 [多个预测器并行预测未来]                       
└────────────────────┬───────────────────────────┘
                     
                     
┌────────────────────────────────────────────────┐
 结果合并与反归一化                             
└────────────────────┬───────────────────────────┘
                     
                     
                   输出: dec_out [B,pred_len,N]

forcast $- >$ self.pdm_blocks

1
2
        for i in range(self.layer):
            enc_out_list = self.pdm_blocks[i](enc_out_list)

输入

  • self.layer = 2
  • enc_out_list
    • enc_out_list[0] [224,96,16]
    • enc_out_list[1] [224,48,16]
    • enc_out_list[2] [224,24,16]
    • enc_out_list[3] [224,12,16]

处理: self.pdm_blocks

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
ModuleList(
  (0-1): 2 x PastDecomposableMixing(
    (layer_norm): LayerNorm((16,), eps=1e-05, elementwise_affine=True)
    (dropout): Dropout(p=0.1, inplace=False)
    (decompsition): series_decomp(
      (moving_avg): moving_avg(
        (avg): AvgPool1d(kernel_size=(25,), stride=(1,), padding=(0,))
      )
    )
    (mixing_multi_scale_season): MultiScaleSeasonMixing(
      (down_sampling_layers): ModuleList(
        (0): Sequential(
          (0): Linear(in_features=96, out_features=48, bias=True)
          (1): GELU(approximate='none')
          (2): Linear(in_features=48, out_features=48, bias=True)
        )
        (1): Sequential(
          (0): Linear(in_features=48, out_features=24, bias=True)
          (1): GELU(approximate='none')
          (2): Linear(in_features=24, out_features=24, bias=True)
        )
        (2): Sequential(
          (0): Linear(in_features=24, out_features=12, bias=True)
          (1): GELU(approximate='none')
          (2): Linear(in_features=12, out_features=12, bias=True)
        )
      )
    )
    (mixing_multi_scale_trend): MultiScaleTrendMixing(
      (up_sampling_layers): ModuleList(
        (0): Sequential(
          (0): Linear(in_features=12, out_features=24, bias=True)
          (1): GELU(approximate='none')
          (2): Linear(in_features=24, out_features=24, bias=True)
        )
        (1): Sequential(
          (0): Linear(in_features=24, out_features=48, bias=True)
          (1): GELU(approximate='none')
          (2): Linear(in_features=48, out_features=48, bias=True)
        )
        (2): Sequential(
          (0): Linear(in_features=48, out_features=96, bias=True)
          (1): GELU(approximate='none')
          (2): Linear(in_features=96, out_features=96, bias=True)
        )
      )
    )
    (out_cross_layer): Sequential(
      (0): Linear(in_features=16, out_features=32, bias=True)
      (1): GELU(approximate='none')
      (2): Linear(in_features=32, out_features=16, bias=True)
    )
  )
)
  • 首先 self.pdm_blocks 堆叠了两个 ,因为 self.layer = 2

  • 一个 self.pdm_blocks 就是 self.pdm_blocks[0]

  • 它的结构是 self.pdm_blocks[0] = 一个趋势分解 + 多尺度季节性混合 + 趋势性混合 + 前馈(这里有一个遗留问题 就是为什么是 1 映射到 16 维呢? )

    • (layer_norm)
    • (dropout)
    • (decompsition) 名称: 类名 series_decomp
    • (moving_avg) 名称: 类名 moving_avg = (avg): AvgPool1d(kernel_size=(25,), stride=(1,), padding=(0,))
    • (mixing_multi_scale_season): MultiScaleSeasonMixing
      • 96 $->$ 48 (48 → 48)$->$ 24(24→24) $->$ 12(12→12)
    • (mixing_multi_scale_trend): MultiScaleTrendMixing
    • 12 $->$ 24 (24 → 24)$->$ 48 (48→48) $->$ 96 (96→96)
    • (out_cross_layer): Sequential
      • 16 -> 32 -> 16 (d_model $->$ d_ff $->$ d_model)
      • 类似 Transformer 的 FFN

self.pdm_blocks[0] = 一个趋势分解 + 多尺度季节性混合 + 趋势性混合 + 前馈(这里有一个遗留问题 就是为什么是 1 映射到 16 维呢? )

1
2
        for i in range(self.layer):
            enc_out_list = self.pdm_blocks[i](enc_out_list)

好了 这里看完了 , 输出

enc_out_list

  • enc_out_list[0] [224,96,16]
  • enc_out_list[1] [224,48,16]
  • enc_out_list[2] [224,24,16]
  • enc_out_list[3] [224,12,16]
1
2
# Future Multipredictor Mixing as decoder for future
dec_out_list = self.future_multi_mixing(B, enc_out_list, x_list)

输入:

  • B = 32(batch size)

  • enc_out_list

    • enc_out_list[0] [224,96,16]
    • enc_out_list[1] [224,48,16]
    • enc_out_list[2] [224,24,16]
    • enc_out_list[3] [224,12,16]
  • x_list

    • x_list[0] [224,96,1]
    • x_list[1] [224,48,1]
    • x_list[0] [224,96,1]
    • x_list[3] [224,12,1]

处理:

  • “Future Multipredictor Mixing”(未来多预测器混合)
  • 多尺度并行预测:对每个时间尺度的特征分别进行未来预测, 每个尺度使用独立的预测器(self.predict_layers[i]), 不同尺度的预测结果互相独立,捕获不同频率的模式
  • 特征投影变换:将高维特征映射到目标维度, 用projection_layer将特征维度转换为目标变量维度 , 处理形状从[B,T,d_model]到[B,pred_len,c_out]的转变
  • 步进

self.future_multi_mixing

完整代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def future_multi_mixing(self, B, enc_out_list, x_list):
    dec_out_list = []
    if self.channel_independence:
        x_list = x_list[0]
        for i, enc_out in zip(range(len(x_list)), enc_out_list):
            dec_out = self.predict_layers[i](enc_out.permute(0, 2, 1)).permute(
                0, 2, 1)  # align temporal dimension
            dec_out = self.projection_layer(dec_out)
            dec_out = dec_out.reshape(B, self.configs.c_out, self.pred_len).permute(0, 2, 1).contiguous()
            dec_out_list.append(dec_out)
  • 结构不难

  • 输入 B, enc_out_list, x_list

    点击查看输入的形状

    =================================== • B = 32(batch size) • enc_out_list ◦ enc_out_list[0] [224,96,16] ◦ enc_out_list[1] [224,48,16] ◦ enc_out_list[2] [224,24,16] ◦ enc_out_list[3] [224,12,16] • x_list ◦ x_list[0] [224,96,1] ◦ x_list[1] [224,48,1] ◦ x_list[0] [224,96,1] ◦ x_list[3] [224,12,1]

  • 中间执行 if 条件句

    • 关键层是 self.predict_layers ① args.down_sampling_layers=3 这个叫 下采样层下采样了三层 ②args.down_sampling_window=2 这个东西叫下采样倍率,$96->48->24->12$ ③然后 self.prediction 就是 $96 -> 720, 48->720, 24->720,12->720$

      self.predict_layers的 init 和具体的实例化

      =================================== self.predict_layers = torch.nn.ModuleList( [ torch.nn.Linear( configs.seq_len // (configs.down_sampling_window ** i), configs.pred_len, ) for i in range(configs.down_sampling_layers + 1) ] ) =================================== ModuleList( (0): Linear(in_features=96, out_features=720, bias=True) (1): Linear(in_features=48, out_features=720, bias=True) (2): Linear(in_features=24, out_features=720, bias=True) (3): Linear(in_features=12, out_features=720, bias=True) )

    • self.projection_layer

      • (具体地实例化) self.projection_layer = Linear(in_features=16, out_features=1, bias=True)
      • (初始化) self.projection_layer = nn.Linear(configs.d_model, 1, bias=True)
  • 输出 dec_out_list

  • 开始吧 逐步执行,看功能,要画类似 UNET 的计算流程图, TimesNet 的也是,要补

1
def future_multi_mixing(self, B, enc_out_list, x_list):

步进,是一个函数, 接收参数,

B = 32, enc_out_list, x_list分别是有四个元素的列表,去前面翻吧,不想写了

enc_out_list, [Tensor[224,96,16], [224,48,16], [224,24,16], [224,12,16]]

x_list, [Tensor[224,96,1], [224,48,1], [224,24,1], [224,12,1]]

1
dec_out_list = []

存储 输出列表

1
if self.channel_independence:

如果通道独立 , 执行,是的 这里 self.channel_independence=1

1
x_list = x_list[0]

取出 第一个元素 x_list,

输出 : x_list[0] .shape= [224,96,1]

1
for i, enc_out in zip(range(len(x_list)), enc_out_list):

for 循环遍历列表

输入 :

  • range(len(x_list)) = range(0,4) (遍历 4 次,i 的取值 0,1,2,3,现在第一次,i=0)
  • enc_out_list=List[Tensor(224,96,16),(224,48,16),(224,24,16),(224,12,16)]

输出 :

  • i=0
  • enc_out = Tensor(224,96,16)
1
dec_out = self.predict_layers[i](enc_out.permute(0, 2, 1)).permute(0, 2, 1) 
  • 输入 enc_out Tensor(224,96,16)
  • .permute(0, 2, 1) $->$ Tensor(224,16,96)
  • self.predict_layers[i] = Linear(in_features=96, out_features=720, bias=True) $->$ Tensor(224,16,720)
  • .permute(0, 2, 1) $->$ Tensor(224,720,16)
  • 输出 dec_out Tensor(224,720,16)
1
dec_out = self.projection_layer(dec_out)
  • 输入 dec_out Tensor(224,720,16)
  • self.projection_layer = Linear(in_features=16, out_features=1, bias=True) -> Tensor(224,720,1)
  • 输出 dec_out Tensor(224,720,1)
1
dec_out = dec_out.reshape(B, self.configs.c_out, self.pred_len).permute(0, 2, 1).contiguous()
  • 输入 dec_out Tensor(224,720,1)
  • .reshape(B, self.configs.c_out, self.pred_len) $->$ Tensor(32,7,720)
  • .permute(0, 2, 1).contiguous() $->$ [32,720,7]
  • 输出 dec_out [32,720,7]
1
dec_out_list.append(dec_out)

输入 dec_out Tensor (32,720,7)

添加进列表

输出 dec_out_list[0] tensor (32,720,7)

1
2
3
4
5
6
for i, enc_out in zip(range(len(x_list)), enc_out_list):
    dec_out = self.predict_layers[i](enc_out.permute(0, 2, 1)).permute(
        0, 2, 1)  # align temporal dimension
    dec_out = self.projection_layer(dec_out)
    dec_out = dec_out.reshape(B, self.configs.c_out, self.pred_len).permute(0, 2, 1).contiguous()
    dec_out_list.append(dec_out)

重复整个 for 循环,最终得到这个部分的返回值 dec_out_list,长度为 4 的列表,列表中每个元素的形状

  • dec_out_list[0] Tensor(32,720,7)
  • dec_out_list[1] Tensor(32,720,7)
  • dec_out_list[0] Tensor(32,720,7)
  • dec_out_list[3] Tensor(32,720,7)

继续 def forcast

1
dec_out = torch.stack(dec_out_list, dim=-1).sum(-1)
  • 输入 dec_out_list , 是一个长度为 4 的列表, 形状List [Tensor(32,720,7), Tensor(32,720,7), Tensor(32,720,7), Tensor(32,720,7)]
  • stack(dim=-1) Tensor(32,720,7,4) (增加维度堆叠)
  • .sum(-1) Tensor(32,720,7)
  • 输出 dec_out Tensor(32,720,7)
1
dec_out = self.normalize_layers[0](dec_out, 'denorm')
  • 输入 dec_out Tensor(32,720,7)
  • self.normalize_layers[0] = Normalize(),逆标准化,没啥意思,不会改变形状, self.normalize_layers是一个 4 层的标准化层,这里只需要进行一个标准化,所以只取 0
  • 输出 dec_out Tensor(32,720,7)
1
return dec_out

输出: dec_out 形状 [32,720,7] batchsize,sequence_length,feature_dim

返回 Model forward

dec_out dec_out 形状 [32,720,7]

整个模型 前向传播结束

👾 本站运行时间:
发表了59篇文章 · 总计11万6千字
使用 Hugo 构建
主题 StackJimmy 设计