Appearance
4B DataEmbedding
Abstract
这一篇是:
04-Level4-short_forecast五段总览里4B DataEmbedding这个子块的下钻文档。只讲:
DataEmbedding怎样把“数值张量 + 时间特征张量”变成(B, L, d_model)的隐藏表示。
1. 上下文
上一层:
下一层:
这一层的入口代码是:
python
enc_out = self.enc_embedding(x_enc, x_mark_enc)
dec_out = self.dec_embedding(x_dec, x_mark_dec)这一层的输出是:
python
x_embed.shape = (B, L, d_model)2. 当前层第一性
这一层存在的第一性是:
把原始数值序列和时间特征序列,变成统一
d_model维的隐藏表示,供后续 attention 使用。
3. 本层入口参数与输出含义
3.1 输入
x- 数值输入,形状
(B, L, C)
- 数值输入,形状
x_mark- 时间特征输入,形状
(B, L, T)
- 时间特征输入,形状
c_in- 数值输入通道数
C
- 数值输入通道数
d_model- embedding 输出隐藏维
embed- 当前真实例子是
timeF
- 当前真实例子是
freq- 当前真实例子是
h
- 当前真实例子是
3.2 输出
x_embed- 统一隐藏表示,形状
(B, L, d_model)
- 统一隐藏表示,形状
4. 顺序图
5. 抽象树
6. 当前真实例子与 toy 例子
6.1 真实运行例子
当前真实例子里:
enc_in = dec_in = 7d_model = 32embed = "timeF"freq = "h"
所以真实分支不是 TemporalEmbedding(fixed),而是:
python
TimeFeatureEmbedding(d_model=32, embed_type="timeF", freq="h")6.2 固定 toy 例子
B = 1L = 4c_in = 2d_model = 4freq = "h"embed = "timeF"
python
x = [
[1, 10],
[2, 11],
[3, 12],
[4, 13],
] # (1, 4, 2)
x_mark = [
[0.10, 0.20, 0.30, 0.40],
[0.20, 0.20, 0.30, 0.50],
[0.30, 0.20, 0.30, 0.60],
[0.40, 0.20, 0.30, 0.70],
] # (1, 4, 4)7. 代码块 1:DataEmbedding.forward(...)
位置:
完整代码:
python
class DataEmbedding(nn.Module):
def __init__(self, c_in, d_model, embed_type="fixed", freq="h", dropout=0.1):
super(DataEmbedding, 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) + self.position_embedding(x)
else:
x = (
self.value_embedding(x)
+ self.temporal_embedding(x_mark)
+ self.position_embedding(x)
)
return self.dropout(x)7.1 toy 张量演变图
text
输入:
x = (1, 4, 2)
x_mark = (1, 4, 4)
步骤 1: value_embedding(x)
-> value = (1, 4, 4)
步骤 2: temporal_embedding(x_mark)
-> temporal = (1, 4, 4)
步骤 3: position_embedding(x)
-> position = (1, 4, 4)
步骤 4: 三路逐元素相加
-> x_embed = value + temporal + position = (1, 4, 4)
步骤 5: dropout
-> 输出仍是 (1, 4, 4)7.2 这一段的 input / output 语义
- 输入
x- 原始数值时间序列
- 输入
x_mark- 当前时间步的连续时间特征
- 输出
x_embed- 已融合数值信息、时间特征和位置信息的统一隐藏表示
8. 代码块 2:TokenEmbedding.forward(...)
完整代码:
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 x8.1 toy 张量演变图
text
输入 x:
[
[1, 10],
[2, 11],
[3, 12],
[4, 13],
] (1, 4, 2)
步骤 1: permute -> (B, C, L)
[
[1, 2, 3, 4],
[10, 11, 12, 13],
] (1, 2, 4)
步骤 2: Conv1d(kernel_size=3, in_channels=2, out_channels=4)
中间位置会看到长度为 3 的局部窗口
例如位置 2 周围:
通道1窗口 [1, 2, 3]
通道2窗口 [10, 11, 12]
为了理解卷积核,固定第 1 个输出通道的 toy 卷积核:
通道1权重 = [1, 0, -1]
通道2权重 = [0.5, 0, -0.5]
再看位置 1(用 circular padding,左边会补最后一个时间步):
通道1窗口 = [4, 1, 2]
通道2窗口 = [13, 10, 11]
则该输出通道在位置 1 的值为:
1*4 + 0*1 + (-1)*2 + 0.5*13 + 0*10 + (-0.5)*11
= (4 - 2) + (6.5 - 5.5)
= 3
其余输出通道也是同样道理,只是卷积核不同。
所以卷积后得到 4 个输出通道
-> (1, 4, 4)
步骤 3: transpose 回来
value =
[
[v11, v12, v13, v14],
[v21, v22, v23, v24],
[v31, v32, v33, v34],
[v41, v42, v43, v44],
] (1, 4, 4)8.2 这一段的 input / output 语义
- 输入
x- 原始数值序列
- 输出
value- 已包含局部数值模式的隐藏表示
9. 代码块 3:TimeFeatureEmbedding.forward(...)
完整代码:
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)9.1 toy 张量演变图
text
输入 x_mark:
[
[0.10, 0.20, 0.30, 0.40],
[0.20, 0.20, 0.30, 0.50],
[0.30, 0.20, 0.30, 0.60],
[0.40, 0.20, 0.30, 0.70],
] (1, 4, 4)
步骤 1: 当前 freq="h",所以每个时间步有 4 个连续特征
t1 = [0.10, 0.20, 0.30, 0.40]
t2 = [0.20, 0.20, 0.30, 0.50]
t3 = [0.30, 0.20, 0.30, 0.60]
t4 = [0.40, 0.20, 0.30, 0.70]
步骤 2: Linear(4, 4)
每一行都乘同一组权重
-> temporal =
[
[t11, t12, t13, t14],
[t21, t22, t23, t24],
[t31, t32, t33, t34],
[t41, t42, t43, t44],
] (1, 4, 4)9.2 这一段的 input / output 语义
- 输入
x_mark- 连续时间特征
- 输出
temporal- 线性映射到
d_model维后的时间特征表示
- 线性映射到
10. 代码块 4:PositionalEmbedding.forward(...)
完整代码:
python
class PositionalEmbedding(nn.Module):
def __init__(self, d_model, max_len=5000):
super(PositionalEmbedding, self).__init__()
pe = torch.zeros(max_len, d_model).float()
pe.require_grad = False
position = torch.arange(0, max_len).float().unsqueeze(1)
div_term = (
torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model)
).exp()
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
pe = pe.unsqueeze(0)
self.register_buffer("pe", pe)
def forward(self, x):
return self.pe[:, : x.size(1)]10.1 toy 张量演变图
text
当前序列长度 L = 4
所以只取位置 0, 1, 2, 3 的位置编码
位置 0:
[sin(0*a), cos(0*a), sin(0*b), cos(0*b)]
= [0, 1, 0, 1]
位置 1:
[sin(1*a), cos(1*a), sin(1*b), cos(1*b)]
位置 2:
[sin(2*a), cos(2*a), sin(2*b), cos(2*b)]
位置 3:
[sin(3*a), cos(3*a), sin(3*b), cos(3*b)]
position = (1, 4, 4)10.2 这一段的 input / output 语义
- 输入
x- 这里只是借
x.size(1)读出当前序列长度
- 这里只是借
- 输出
position- 表示“这是第几个时间步”的固定位置向量
11. 代码块 5:三路相加
对应代码:
python
x = (
self.value_embedding(x)
+ self.temporal_embedding(x_mark)
+ self.position_embedding(x)
)11.1 toy 张量演变图
text
value =
[
[v11, v12, v13, v14],
[v21, v22, v23, v24],
[v31, v32, v33, v34],
[v41, v42, v43, v44],
]
temporal =
[
[t11, t12, t13, t14],
[t21, t22, t23, t24],
[t31, t32, t33, t34],
[t41, t42, t43, t44],
]
position =
[
[p11, p12, p13, p14],
[p21, p22, p23, p24],
[p31, p32, p33, p34],
[p41, p42, p43, p44],
]
逐元素相加后:
x_embed =
[
[v11+t11+p11, v12+t12+p12, v13+t13+p13, v14+t14+p14],
[v21+t21+p21, v22+t22+p22, v23+t23+p23, v24+t24+p24],
[v31+t31+p31, v32+t32+p32, v33+t33+p33, v34+t34+p34],
[v41+t41+p41, v42+t42+p42, v43+t43+p43, v44+t44+p44],
] (1, 4, 4)11.2 这一段的 input / output 语义
- 输入
value- 数值局部模式表示
- 输入
temporal- 时间特征表示
- 输入
position- 位置表示
- 输出
x_embed- 三种信息对齐后得到的统一隐藏表示
12. 当前层真正要固定什么
- 当前真实例子里,时间分支走的是
TimeFeatureEmbedding TokenEmbedding负责数值局部模式TimeFeatureEmbedding负责连续时间特征线性投影PositionalEmbedding负责位置编码- 三路都对齐到
d_model,再逐元素相加
13. 下一步
继续看: