Skip to content

Layer 2A — DataEmbedding_wo_pos 精读

forecast() 主链([[02-Layer1-forecast主链]])调用两次:enc_embedding(x_enc, x_mark_enc)dec_embedding(seasonal_init, x_mark_dec)


1. 在父层中的位置

forecast()
  ├─ self.enc_embedding(x_enc, x_mark_enc)           ← DataEmbedding_wo_pos
  └─ self.dec_embedding(seasonal_init, x_mark_dec)   ← DataEmbedding_wo_pos

与 Informer 的 DataEmbedding 唯一区别:不加 PositionalEmbedding


2. I/O 接口定义

以 encoder embedding 为例(toy):

shape含义
输入 x(2, 12, 5)encoder 窗口
输入 x_mark(2, 12, 4)时间特征(月/日/时/分)
输出(2, 12, 8)嵌入后的 token 序列

decoder embedding 同理:(2,10,5)+(2,10,4)(2,10,8)


3. 顺序图(具体层)


4. 语义分组图(索引层)


5. 逐步精读

5.0 完整原始代码

python
class DataEmbedding_wo_pos(nn.Module):
    def __init__(self, c_in, d_model, embed_type="fixed", freq="h", dropout=0.1):
        super(DataEmbedding_wo_pos, self).__init__()

        self.value_embedding = TokenEmbedding(c_in=c_in, d_model=d_model)
        self.position_embedding = PositionalEmbedding(d_model=d_model)
        self.temporal_embedding = (
            TemporalEmbedding(d_model=d_model, embed_type=embed_type, freq=freq)
            if embed_type != "timeF"
            else TimeFeatureEmbedding(d_model=d_model, embed_type=embed_type, freq=freq)
        )
        self.dropout = nn.Dropout(p=dropout)

    def forward(self, x, x_mark):
        if x_mark is None:
            x = self.value_embedding(x)
        else:
            x = self.value_embedding(x) + self.temporal_embedding(x_mark)
        return self.dropout(x)
⚠️ position_embedding__init__ 中定义但 forward 从未使用

DataEmbedding_wo_pos 名字中的 "wo_pos" 即 "without position"。虽然 __init__ 里初始化了 self.position_embedding,但 forward() 里完全没有调用它(对比 DataEmbeddingforward 会加 self.position_embedding(x))。
这个冗余的 position_embedding 属性占用了少量显存,但不影响正确性。


5.1 TokenEmbedding:值嵌入

python
class TokenEmbedding(nn.Module):
    def __init__(self, c_in, d_model):
        super(TokenEmbedding, self).__init__()
        padding = 1 if torch.__version__ >= "1.5.0" else 2
        self.tokenConv = nn.Conv1d(
            in_channels=c_in,
            out_channels=d_model,
            kernel_size=3,
            padding=padding,
            padding_mode="circular",
            bias=False,
        )

    def forward(self, x):
        x = self.tokenConv(x.permute(0, 2, 1)).transpose(1, 2)
        return x

Conv1d 要求输入 (B, C_in, L),原始 x(B, L, C_in)=(2,12,5)permute(0,2,1)(2,5,12) → Conv1d(5→8, k=3, pad=1, circular) → (2,8,12)transpose(1,2)(2,12,8)

为什么必须 transpose:

Conv1d 需要: (B, Channels, Length)
                   ↑           ↑
                输入通道数    序列长度

原始 x: (2, 12, 5)   ← C_in=5 在最后一维,不对
permute(0,2,1): (2, 5, 12)  ← Channels=5 ✓ Length=12 ✓
Conv1d 输出: (2, 8, 12)
transpose(1,2): (2, 12, 8)  ← 还原到 (B, L, d_model) 格式

toy 数值 — 输出通道 k=0,时间位置 t=0 的完整计算:

t=0 的 Conv1d 窗口覆盖 j=−1,0,+1 三个位置;circular padding 令 j=−1 循环至 t=11:

t(实际位置)x[0,t,0]x[0,t,1]x[0,t,2]x[0,t,3]x[0,t,4]
11(j=−1,circular pad)0.20.5−0.30.8−0.1
0 (j=0)1.0−0.50.4−0.20.7
1 (j=+1)0.30.6−0.80.1−0.4

设权重 W[k=0],shape (c_in=5, kernel_size=3)

cj=−1j=0j=+1
00.10.2−0.1
10.3−0.20.4
2−0.10.30.2
30.2−0.30.1
40.40.1−0.2
TokenEmb[0,0,0]=c=04j{1,0,1}W0,c,j+1x[0,circ(0+j),c]

按 c 分组展开:

=(0.1×0.2+0.2×1.00.1×0.3)c=0 = 0.19+(0.3×0.50.2×(0.5)+0.4×0.6)c=1 = 0.49+(0.1×(0.3)+0.3×0.4+0.2×(0.8))c=2 = 0.01+(0.2×0.80.3×(0.2)+0.1×0.1)c=3 = 0.23+(0.4×(0.1)+0.1×0.70.2×(0.4))c=4 = 0.11=1.01

完整输出 (2,12,8):B=2 个样本、L=12 个时间步、k=0..7 共 8 个输出通道各用独立权重做相同计算,并行生成所有 2×12×8=192 个输出值。


5.2 TemporalEmbedding:时间嵌入

TFB 的 embed_type="timeF",因此使用 TimeFeatureEmbedding(而非 TemporalEmbedding):

python
class TimeFeatureEmbedding(nn.Module):
    def __init__(self, d_model, embed_type="timeF", freq="h"):
        super(TimeFeatureEmbedding, self).__init__()
        freq_map = {"h": 4, "t": 5, "s": 6, "m": 1, "a": 1, "w": 2, "d": 3, "b": 3}
        d_inp = freq_map[freq]
        self.embed = nn.Linear(d_inp, d_model, bias=False)

    def forward(self, x):
        return self.embed(x)

freq="h"d_inp=4(对应小时频率的 4 个时间特征)。x_mark (2,12,4)Linear(4,8)(2,12,8)


5.3 双路嵌入结构总览

论文/原理描述代码实现关键原因
值嵌入:原始时序 → token 向量TokenEmbedding Conv1d(c_in→d_model, k=3, circular)时序值用 Conv1d 捕捉局部相关
时间嵌入:日历特征 → 时间向量TimeFeatureEmbedding Linear(4→8)freq="h",4维时间特征线性投影
无位置编码(Auto-Correlation 不需要)position_embedding 仅在 __init__ 定义,forward 从不调用AutoCorrelation 通过 FFT lag 捕获周期,位置编码反而引入冗余
两路相加value_embedding(x) + temporal_embedding(x_mark)向量空间叠加,保持 d_model 维度

5.4 相加与 Dropout

python
x = self.value_embedding(x) + self.temporal_embedding(x_mark)
return self.dropout(x)

两路 (2,12,8) 逐元素相加 → (2,12,8)。Dropout(p=0.1) 随机置零 10% 元素(训练时),测试时无效果。

toy 数值(batch=0, t=0, feature=0):设 token_emb[0,0,0]=0.43time_emb[0,0,0]=-0.12,则 output[0,0,0]=0.31

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