时序Transformer代码复现心得

时序Transformer代码复现心得

Vaswani A. Attention is all you need[J]. Advances in Neural Information Processing Systems, 2017.

代码部分

def forecast(self, x_enc, x_mark_enc, x_dec, x_mark_dec):

进预测模块,此时他四种信息都用到了

# Embedding
enc_out = self.enc_embedding(x_enc, x_mark_enc)#b,seq,dmodel
#encout: b, seq, dmodel
  • 先进embedding
x = self.value_embedding(
        x) + self.temporal_embedding(x_mark) + self.position_embedding(x)
return self.dropout(x)

三种嵌入方法,这是第一种TokenEmbedding

class TokenEmbedding(nn.Module):
self.tokenConv = nn.Conv1d(in_channels=c_in, out_channels=d_model,
                           kernel_size=3, padding=padding, padding_mode='circular', bias=False)##循环填充方式对时序数据比较有用
#B,seq,N
x = self.tokenConv(x.permute(0, 2, 1)).transpose(1, 2)
#B,seq,dmodel
#通过卷积N-dmodel,有维度变换,N必须在中间
return x

用的1D卷积做的,把特征N映射到demodel,并且用到了循环填充方式

class TimeFeatureEmbedding(nn.Module):

第二种时间信息嵌入,根据时间步长,弄一个线性层

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)#也是一个线性层

第三种pos位置嵌入

class PositionalEmbedding(nn.Module):
    def __init__(self, d_model, max_len=5000):
        super(PositionalEmbedding, self).__init__()
        # Compute the positional encodings once in log space.
        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)]#传过去batch和seq

在训练过程中,位置编码的参数不会更新。这是因为位置编码是固定的,不需要学习。

到此嵌入部分就完了,此时维度#encout: b, seq, dmodel、接下来进encoder

enc_out, attns = self.encoder(enc_out, attn_mask=None)#attns:none,none
# b, seq, dmodel
class EncoderLayer(nn.Module):
new_x, attn = self.attention(
    x, x, x,#自注意力qkv
    attn_mask=attn_mask,
    tau=tau, delta=delta
)
x = x + self.dropout(new_x)#残差连接

先进注意力

B, L, _ = queries.shape#B,seq,dmodel
        _, S, _ = keys.shape
        H = self.n_heads
#其实L和S是一个数
        queries = self.query_projection(queries).view(B, L, H, -1)#B, L, H, dmodel/h
        keys = self.key_projection(keys).view(B, S, H, -1)#一样的计算方法
        values = self.value_projection(values).view(B, S, H, -1)#H 表示头的数量-1 表示自动计算该维度

normal

def forward(self, queries, keys, values, attn_mask, tau=None, delta=None):
    B, L, H, E = queries.shape#B,seq,head ,64
    _, S, _, D = values.shape
    scale = self.scale or 1. / sqrt(E)#注意力权重的缩放因子

    scores = torch.einsum("blhe,bshe->bhls", queries, keys)#张量乘法
    #blhe 和 bshe 分别代表 queries 和 keys 张量的维度标签

用的fullattention

dec_out = self.dec_embedding(x_dec, x_mark_dec)
# b,label+pred,dmodel

来到dec_embedding,维度会变成b,label+pred,dmodel

和encoder一样的嵌入流程,区别只是输入数据不一样

这里省略直接到decoder

# b,label+pred,dmodel
dec_out = self.decoder(dec_out, enc_out, x_mask=None, cross_mask=None)
# b,label+ored,N
class DecoderLayer(nn.Module):
def forward(self, x, cross, x_mask=None, cross_mask=None, tau=None, delta=None):

x是dec_out,cross是enc_out要在decoder做交叉注意力

x = x + self.dropout(self.self_attention(#残差连接
    x, x, x,
    attn_mask=x_mask,
    tau=tau, delta=None
)[0])#自注意力
x = self.norm1(x)

不过要先做一次自注意力,也是全连接

if self.mask_flag:
    if attn_mask is None:
        attn_mask = TriangularCausalMask(B, L, device=queries.device)

    scores.masked_fill_(attn_mask.mask, -np.inf)

和encoder不同的是,decoder的注意力计算创建了一个下三角的掩码,确保在序列中的位置只能关注它之前的位置,而不能关注它之后的位置

x = x + self.dropout(self.cross_attention(
    x, cross, cross,
    attn_mask=cross_mask,
    tau=tau, delta=delta
)[0])#必不可少,decoder提供Q

之后再执行一次交叉注意力,也是fullattention,不过没用到掩码

y = x = self.norm2(x)
y = self.dropout(self.activation(self.conv1(y.transpose(-1, 1))))
y = self.dropout(self.conv2(y).transpose(-1, 1))

return self.norm3(x + y)

自注意力和交叉注意力完了之后就是很多个线性层

直接跟着归一化和预测头

if self.norm is not None:
    x = self.norm(x)

if self.projection is not None:
    x = self.projection(x)
return x

总结

和encoder不同的是,decoder的注意力计算创建了一个下三角的掩码,确保在序列中的位置只能关注它之前的位置,而不能关注它之后的位置

只有decoder的自注意力用到了掩码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懒羊羊吃辣条

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值